diff --git a/src/engine/dispatch.h b/src/engine/dispatch.h index f9815449..0b400547 100644 --- a/src/engine/dispatch.h +++ b/src/engine/dispatch.h @@ -15,15 +15,23 @@ enum DivDispatchCmds { struct DivCommand { DivDispatchCmds cmd; - unsigned char chan, value; - DivCommand(DivDispatchCmds c, unsigned char ch, unsigned char val): + unsigned char chan; + int value, value2; + DivCommand(DivDispatchCmds c, unsigned char ch, int val, int val2): cmd(c), chan(ch), - value(val) {} + value(val), + value2(val2) {} + DivCommand(DivDispatchCmds c, unsigned char ch, int val): + cmd(c), + chan(ch), + value(val), + value2(0) {} DivCommand(DivDispatchCmds c, unsigned char ch): cmd(c), chan(ch), - value(0) {} + value(0), + value2(0) {} }; struct DivDelayedCommand { diff --git a/src/engine/engine.cpp b/src/engine/engine.cpp index cfe62682..016ba75a 100644 --- a/src/engine/engine.cpp +++ b/src/engine/engine.cpp @@ -4,6 +4,7 @@ #include "../audio/sdl.h" #include "platform/genesis.h" #include "platform/dummy.h" +#include #include void process(void* u, float** in, float** out, int inChans, int outChans, unsigned int size) { @@ -662,6 +663,10 @@ bool DivEngine::init() { bbOut[0]=new short[got.bufsize]; bbOut[1]=new short[got.bufsize]; + for (int i=0; i<60; i++) { + vibTable[i]=127*sin(((double)i/60.0)*(2*M_PI)); + } + switch (song.system) { case DIV_SYSTEM_GENESIS: dispatch=new DivPlatformGenesis; diff --git a/src/engine/engine.h b/src/engine/engine.h index b72ca532..babe2b7f 100644 --- a/src/engine/engine.h +++ b/src/engine/engine.h @@ -9,8 +9,9 @@ struct DivChannelState { std::vector delayed; int note, pitch, portaSpeed, portaNote; int volume, volSpeed; - int vibratoDepth, vibratoRate; - int tremoloDepth, tremoloRate; + int vibratoDepth, vibratoRate, vibratoPos; + int tremoloDepth, tremoloRate, tremoloPos; + bool doNote; DivChannelState(): note(-1), @@ -21,8 +22,11 @@ struct DivChannelState { volSpeed(0), vibratoDepth(0), vibratoRate(0), + vibratoPos(0), tremoloDepth(0), - tremoloRate(0) {} + tremoloRate(0), + tremoloPos(0), + doNote(false) {} }; class DivEngine { @@ -36,6 +40,8 @@ class DivEngine { int changeOrd, changePos; DivChannelState chan[17]; + short vibTable[60]; + blip_buffer_t* bb[2]; short temp[2], prevSample[2]; short* bbOut[2]; diff --git a/src/engine/platform/genesis.cpp b/src/engine/platform/genesis.cpp index 3ed84d5d..2821ec15 100644 --- a/src/engine/platform/genesis.cpp +++ b/src/engine/platform/genesis.cpp @@ -160,7 +160,8 @@ int DivPlatformGenesis::dispatch(DivCommand c) { rWrite(chanOffs[c.chan]+0xb0,(ins->fm.alg&7)|(ins->fm.fb<<3)); rWrite(chanOffs[c.chan]+0xb4,(chan[c.chan].pan<<6)|(ins->fm.fms&7)|((ins->fm.ams&3)<<4)); } - chan[c.chan].freq=644.0f*pow(2.0f,((float)c.value/12.0f)); + chan[c.chan].baseFreq=644.0f*pow(2.0f,((float)c.value/12.0f)); + chan[c.chan].freq=(chan[c.chan].baseFreq*(2048+chan[c.chan].pitch))>>11; chan[c.chan].freqChanged=true; chan[c.chan].keyOn=true; chan[c.chan].active=true; @@ -206,6 +207,33 @@ int DivPlatformGenesis::dispatch(DivCommand c) { rWrite(chanOffs[c.chan]+0xb4,(chan[c.chan].pan<<6)|(ins->fm.fms&7)|((ins->fm.ams&3)<<4)); break; } + case DIV_CMD_PITCH: { + chan[c.chan].pitch=c.value; + chan[c.chan].freq=(chan[c.chan].baseFreq*(2048+chan[c.chan].pitch))>>11; + chan[c.chan].freqChanged=true; + break; + } + case DIV_CMD_NOTE_PORTA: { + int destFreq=644.0f*pow(2.0f,((float)c.value2/12.0f)); + bool return2=false; + if (destFreq>chan[c.chan].baseFreq) { + chan[c.chan].baseFreq=(chan[c.chan].baseFreq*(960+c.value))/960; + if (chan[c.chan].baseFreq>=destFreq) { + chan[c.chan].baseFreq=destFreq; + return2=true; + } + } else { + chan[c.chan].baseFreq=(chan[c.chan].baseFreq*(960-c.value))/960; + if (chan[c.chan].baseFreq<=destFreq) { + chan[c.chan].baseFreq=destFreq; + return2=true; + } + } + chan[c.chan].freq=(chan[c.chan].baseFreq*(2048+chan[c.chan].pitch))>>11; + chan[c.chan].freqChanged=true; + if (return2) return 2; + break; + } case DIV_CMD_SAMPLE_MODE: { if (c.chan==5) { dacMode=c.value; diff --git a/src/engine/platform/genesis.h b/src/engine/platform/genesis.h index c932f924..f8d07988 100644 --- a/src/engine/platform/genesis.h +++ b/src/engine/platform/genesis.h @@ -5,13 +5,13 @@ class DivPlatformGenesis: public DivDispatch { struct Channel { unsigned char freqH, freqL; - int freq; + int freq, baseFreq, pitch; unsigned char ins; signed char konCycles; bool active, insChanged, freqChanged, keyOn, keyOff; signed char vol; unsigned char pan; - Channel(): freqH(0), freqL(0), freq(0), ins(0), active(false), insChanged(true), freqChanged(false), keyOn(false), keyOff(false), vol(0), pan(3) {} + Channel(): freqH(0), freqL(0), freq(0), baseFreq(0), pitch(0), ins(0), active(false), insChanged(true), freqChanged(false), keyOn(false), keyOff(false), vol(0), pan(3) {} }; Channel chan[10]; struct QueuedWrite { diff --git a/src/engine/playback.cpp b/src/engine/playback.cpp index 685dd90e..1330c349 100644 --- a/src/engine/playback.cpp +++ b/src/engine/playback.cpp @@ -107,7 +107,7 @@ void DivEngine::nextRow() { dispatch->dispatch(DivCommand(DIV_CMD_NOTE_OFF,i)); } else if (!(pat->data[curRow][0]==0 && pat->data[curRow][1]==0)) { chan[i].note=pat->data[curRow][0]+pat->data[curRow][1]*12; - dispatch->dispatch(DivCommand(DIV_CMD_NOTE_ON,i,chan[i].note)); + chan[i].doNote=true; } // volume @@ -118,8 +118,8 @@ void DivEngine::nextRow() { // effects for (int j=0; jeffectRows; j++) { - unsigned char effect=pat->data[curRow][4+(j<<1)]; - unsigned char effectVal=pat->data[curRow][5+(j<<1)]; + short effect=pat->data[curRow][4+(j<<1)]; + short effectVal=pat->data[curRow][5+(j<<1)]; if (effectVal==-1) effectVal=0; @@ -147,7 +147,7 @@ void DivEngine::nextRow() { chan[i].portaNote=-1; chan[i].portaSpeed=-1; } else { - chan[i].portaNote=0x7f; + chan[i].portaNote=0x60; chan[i].portaSpeed=effectVal; } break; @@ -167,11 +167,13 @@ void DivEngine::nextRow() { } else { chan[i].portaNote=chan[i].note; chan[i].portaSpeed=effectVal; + chan[i].doNote=false; } break; case 0x04: // vibrato chan[i].vibratoDepth=effectVal&15; chan[i].vibratoRate=effectVal>>4; + dispatch->dispatch(DivCommand(DIV_CMD_PITCH,i,(chan[i].vibratoDepth*vibTable[chan[i].vibratoPos])>>4)); break; case 0x0a: // volume ramp if (effectVal!=0) { @@ -187,17 +189,24 @@ void DivEngine::nextRow() { case 0xe1: // portamento up chan[i].portaNote=chan[i].note+(effectVal&15); - chan[i].portaSpeed=effectVal>>4; + chan[i].portaSpeed=(effectVal>>4)*3; break; case 0xe2: // portamento down chan[i].portaNote=chan[i].note-(effectVal&15); - chan[i].portaSpeed=effectVal>>4; + chan[i].portaSpeed=(effectVal>>4)*3; break; case 0xe5: // pitch chan[i].pitch=effectVal-0x80; break; } } + + if (chan[i].doNote) { + chan[i].vibratoPos=0; + dispatch->dispatch(DivCommand(DIV_CMD_PITCH,i,(chan[i].vibratoDepth*vibTable[chan[i].vibratoPos])>>2)); + dispatch->dispatch(DivCommand(DIV_CMD_NOTE_ON,i,chan[i].note)); + chan[i].doNote=false; + } } } @@ -228,6 +237,16 @@ void DivEngine::nextTick() { if (chan[i].volume<0) chan[i].volume=0; dispatch->dispatch(DivCommand(DIV_CMD_VOLUME,i,chan[i].volume>>8)); } + if (chan[i].vibratoDepth>0) { + chan[i].vibratoPos+=chan[i].vibratoRate; + if (chan[i].vibratoPos>=60) chan[i].vibratoPos-=60; + dispatch->dispatch(DivCommand(DIV_CMD_PITCH,i,(chan[i].vibratoDepth*vibTable[chan[i].vibratoPos])>>4)); + } + if (chan[i].portaSpeed>0) { + if (dispatch->dispatch(DivCommand(DIV_CMD_NOTE_PORTA,i,chan[i].portaSpeed,chan[i].portaNote))==2) { + chan[i].portaSpeed=0; + } + } } // system tick