diff --git a/src/engine/engine.cpp b/src/engine/engine.cpp index 38185aaa8..17926e0ec 100644 --- a/src/engine/engine.cpp +++ b/src/engine/engine.cpp @@ -4470,6 +4470,9 @@ bool DivEngine::init() { for (int i=0; i<64; i++) { vibTable[i]=127*sin(((double)i/64.0)*(2*M_PI)); } + for (int i=0; i<128; i++) { + tremTable[i]=255*0.5*(1.0-cos(((double)i/128.0)*(2*M_PI))); + } for (int i=0; i<4096; i++) { reversePitchTable[i]=round(1024.0*pow(2.0,(2048.0-(double)i)/(12.0*128.0))); pitchTable[i]=round(1024.0*pow(2.0,((double)i-2048.0)/(12.0*128.0))); diff --git a/src/engine/engine.h b/src/engine/engine.h index d0f6f98b9..fc552cacc 100644 --- a/src/engine/engine.h +++ b/src/engine/engine.h @@ -413,6 +413,7 @@ class DivEngine { } sPreview; short vibTable[64]; + short tremTable[128]; int reversePitchTable[4096]; int pitchTable[4096]; char c163NameCS[1024]; @@ -1158,6 +1159,7 @@ class DivEngine { memset(dispatchOfChan,0,DIV_MAX_CHANS*sizeof(int)); memset(sysOfChan,0,DIV_MAX_CHANS*sizeof(int)); memset(vibTable,0,64*sizeof(short)); + memset(tremTable,0,128*sizeof(short)); memset(reversePitchTable,0,4096*sizeof(int)); memset(pitchTable,0,4096*sizeof(int)); memset(sysDefs,0,256*sizeof(void*)); diff --git a/src/engine/playback.cpp b/src/engine/playback.cpp index 485a020dd..abcb8dc56 100644 --- a/src/engine/playback.cpp +++ b/src/engine/playback.cpp @@ -689,6 +689,13 @@ void DivEngine::processRow(int i, bool afterDelay) { // - then a volume slide down starts to the low boundary, and then when this is reached a volume slide up begins // - this process repeats until 0700 or 0Axy are found // - note that a volume value does not stop tremolo - instead it glitches this whole thing up + if (chan[i].tremoloDepth==0) { + chan[i].tremoloPos=0; + } + chan[i].tremoloDepth=effectVal&15; + chan[i].tremoloRate=effectVal>>4; + // tremolo and vol slides are incompatiblw + chan[i].volSpeed=0; break; case 0x0a: // volume ramp // TODO: non-0x-or-x0 value should be treated as 00 @@ -698,6 +705,9 @@ void DivEngine::processRow(int i, bool afterDelay) { } else { chan[i].volSpeed=(effectVal>>4)*64; } + // tremolo and vol slides are incompatible + chan[i].tremoloDepth=0; + chan[i].tremoloRate=0; } else { chan[i].volSpeed=0; } @@ -838,10 +848,16 @@ void DivEngine::processRow(int i, bool afterDelay) { if (!song.arpNonPorta) dispatchCmd(DivCommand(DIV_CMD_PRE_PORTA,i,false,0)); break; case 0xf3: // fine volume ramp up + // tremolo and vol slides are incompatible + chan[i].tremoloDepth=0; + chan[i].tremoloRate=0; chan[i].volSpeed=effectVal; dispatchCmd(DivCommand(DIV_CMD_HINT_VOL_SLIDE,i,chan[i].volSpeed)); break; case 0xf4: // fine volume ramp down + // tremolo and vol slides are incompatible + chan[i].tremoloDepth=0; + chan[i].tremoloRate=0; chan[i].volSpeed=-effectVal; dispatchCmd(DivCommand(DIV_CMD_HINT_VOL_SLIDE,i,chan[i].volSpeed)); break; @@ -868,6 +884,9 @@ void DivEngine::processRow(int i, bool afterDelay) { } else { chan[i].volSpeed=(effectVal>>4)*256; } + // tremolo and vol slides are incompatible + chan[i].tremoloDepth=0; + chan[i].tremoloRate=0; } else { chan[i].volSpeed=0; } @@ -1239,6 +1258,10 @@ bool DivEngine::nextTick(bool noAccum, bool inhibitLowLat) { } else { dispatchCmd(DivCommand(DIV_CMD_VOLUME,i,chan[i].volume>>8)); } + } else if (chan[i].tremoloDepth>0) { + chan[i].tremoloPos+=chan[i].tremoloRate; + chan[i].tremoloPos&=127; + dispatchCmd(DivCommand(DIV_CMD_VOLUME,i,MAX(0,chan[i].volume-(tremTable[chan[i].tremoloPos]*chan[i].tremoloDepth))>>8)); } } if (chan[i].vibratoDepth>0) {