diff --git a/src/engine/dispatch.h b/src/engine/dispatch.h index 86b2e229e..fb9be5800 100644 --- a/src/engine/dispatch.h +++ b/src/engine/dispatch.h @@ -58,10 +58,14 @@ enum DivDispatchCmds { // these will be used in ROM export. // do NOT implement! DIV_CMD_HINT_VIBRATO, // (speed, depth) + DIV_CMD_HINT_VIBRATO_RANGE, // (range) DIV_CMD_HINT_VIBRATO_SHAPE, // (shape) DIV_CMD_HINT_PITCH, // (pitch) DIV_CMD_HINT_ARPEGGIO, // (note1, note2) + DIV_CMD_HINT_VOLUME, // (vol) DIV_CMD_HINT_VOL_SLIDE, // (amount, oneTick) + DIV_CMD_HINT_PORTA, // (target, speed) + DIV_CMD_HINT_LEGATO, // (note) DIV_CMD_SAMPLE_MODE, // (enabled) DIV_CMD_SAMPLE_FREQ, // (frequency) diff --git a/src/engine/playback.cpp b/src/engine/playback.cpp index b089b6f3e..8fd20d5f9 100644 --- a/src/engine/playback.cpp +++ b/src/engine/playback.cpp @@ -58,10 +58,14 @@ const char* cmdName[]={ "PRE_NOTE", "HINT_VIBRATO", + "HINT_VIBRATO_RANGE", "HINT_VIBRATO_SHAPE", "HINT_PITCH", "HINT_ARPEGGIO", "HINT_VOL_SLIDE", + "HINT_VOLUME", + "HINT_PORTA", + "HINT_LEGATO", "SAMPLE_MODE", "SAMPLE_FREQ", @@ -344,6 +348,7 @@ void DivEngine::processRow(int i, bool afterDelay) { logV("forcing volume"); chan[i].volume=chan[i].volMax; dispatchCmd(DivCommand(DIV_CMD_VOLUME,i,chan[i].volume>>8)); + dispatchCmd(DivCommand(DIV_CMD_HINT_VOLUME,i,chan[i].volume>>8)); } } } @@ -356,11 +361,13 @@ void DivEngine::processRow(int i, bool afterDelay) { if (chan[i].stopOnOff) { chan[i].portaNote=-1; chan[i].portaSpeed=-1; + dispatchCmd(DivCommand(DIV_CMD_HINT_PORTA,i,chan[i].portaNote,chan[i].portaSpeed)); chan[i].stopOnOff=false; } if (disCont[dispatchOfChan[i]].dispatch->keyOffAffectsPorta(dispatchChanOfChan[i])) { chan[i].portaNote=-1; chan[i].portaSpeed=-1; + dispatchCmd(DivCommand(DIV_CMD_HINT_PORTA,i,chan[i].portaNote,chan[i].portaSpeed)); /*if (i==2 && sysOfChan[i]==DIV_SYSTEM_SMS) { chan[i+1].portaNote=-1; chan[i+1].portaSpeed=-1; @@ -377,11 +384,13 @@ void DivEngine::processRow(int i, bool afterDelay) { if (chan[i].stopOnOff) { chan[i].portaNote=-1; chan[i].portaSpeed=-1; + dispatchCmd(DivCommand(DIV_CMD_HINT_PORTA,i,chan[i].portaNote,chan[i].portaSpeed)); chan[i].stopOnOff=false; } if (disCont[dispatchOfChan[i]].dispatch->keyOffAffectsPorta(dispatchChanOfChan[i])) { chan[i].portaNote=-1; chan[i].portaSpeed=-1; + dispatchCmd(DivCommand(DIV_CMD_HINT_PORTA,i,chan[i].portaNote,chan[i].portaSpeed)); /*if (i==2 && sysOfChan[i]==DIV_SYSTEM_SMS) { chan[i+1].portaNote=-1; chan[i+1].portaSpeed=-1; @@ -398,6 +407,7 @@ void DivEngine::processRow(int i, bool afterDelay) { if (!chan[i].keyOn) { if (disCont[dispatchOfChan[i]].dispatch->keyOffAffectsArp(dispatchChanOfChan[i])) { chan[i].arp=0; + dispatchCmd(DivCommand(DIV_CMD_HINT_ARPEGGIO,i,chan[i].arp)); } } chan[i].doNote=true; @@ -414,6 +424,7 @@ void DivEngine::processRow(int i, bool afterDelay) { } chan[i].volume=pat->data[whatRow][3]<<8; dispatchCmd(DivCommand(DIV_CMD_VOLUME,i,chan[i].volume>>8)); + dispatchCmd(DivCommand(DIV_CMD_HINT_VOLUME,i,chan[i].volume>>8)); } } @@ -458,11 +469,13 @@ void DivEngine::processRow(int i, bool afterDelay) { if (effectVal==0) { chan[i].portaNote=-1; chan[i].portaSpeed=-1; + dispatchCmd(DivCommand(DIV_CMD_HINT_PORTA,i,chan[i].portaNote,chan[i].portaSpeed)); chan[i].inPorta=false; if (!song.arpNonPorta) dispatchCmd(DivCommand(DIV_CMD_PRE_PORTA,i,false,0)); } else { chan[i].portaNote=song.limitSlides?0x60:255; chan[i].portaSpeed=effectVal; + dispatchCmd(DivCommand(DIV_CMD_HINT_PORTA,i,chan[i].portaNote,chan[i].portaSpeed)); chan[i].portaStop=true; chan[i].nowYouCanStop=false; chan[i].stopOnOff=false; @@ -478,11 +491,13 @@ void DivEngine::processRow(int i, bool afterDelay) { if (effectVal==0) { chan[i].portaNote=-1; chan[i].portaSpeed=-1; + dispatchCmd(DivCommand(DIV_CMD_HINT_PORTA,i,chan[i].portaNote,chan[i].portaSpeed)); chan[i].inPorta=false; if (!song.arpNonPorta) dispatchCmd(DivCommand(DIV_CMD_PRE_PORTA,i,false,0)); } else { chan[i].portaNote=song.limitSlides?disCont[dispatchOfChan[i]].dispatch->getPortaFloor(dispatchChanOfChan[i]):-60; chan[i].portaSpeed=effectVal; + dispatchCmd(DivCommand(DIV_CMD_HINT_PORTA,i,chan[i].portaNote,chan[i].portaSpeed)); chan[i].portaStop=true; chan[i].nowYouCanStop=false; chan[i].stopOnOff=false; @@ -496,6 +511,7 @@ void DivEngine::processRow(int i, bool afterDelay) { if (effectVal==0) { chan[i].portaNote=-1; chan[i].portaSpeed=-1; + dispatchCmd(DivCommand(DIV_CMD_HINT_PORTA,i,chan[i].portaNote,chan[i].portaSpeed)); chan[i].inPorta=false; dispatchCmd(DivCommand(DIV_CMD_PRE_PORTA,i,false,0)); } else { @@ -509,6 +525,7 @@ void DivEngine::processRow(int i, bool afterDelay) { chan[i].inPorta=true; chan[i].wasShorthandPorta=false; } + dispatchCmd(DivCommand(DIV_CMD_HINT_PORTA,i,chan[i].portaNote,chan[i].portaSpeed)); chan[i].portaStop=true; if (chan[i].keyOn) chan[i].doNote=false; chan[i].stopOnOff=song.stopPortaOnNoteOff; // what?! @@ -520,6 +537,7 @@ void DivEngine::processRow(int i, bool afterDelay) { case 0x04: // vibrato chan[i].vibratoDepth=effectVal&15; chan[i].vibratoRate=effectVal>>4; + dispatchCmd(DivCommand(DIV_CMD_HINT_VIBRATO,i,chan[i].vibratoDepth,chan[i].vibratoRate)); dispatchCmd(DivCommand(DIV_CMD_PITCH,i,chan[i].pitch+(((chan[i].vibratoDepth*vibTable[chan[i].vibratoPos]*chan[i].vibratoFine)>>4)/15))); break; case 0x07: // tremolo @@ -543,12 +561,14 @@ void DivEngine::processRow(int i, bool afterDelay) { } else { chan[i].volSpeed=0; } + dispatchCmd(DivCommand(DIV_CMD_HINT_VOL_SLIDE,i,chan[i].volSpeed)); break; case 0x00: // arpeggio chan[i].arp=effectVal; if (chan[i].arp==0 && song.arp0Reset) { chan[i].resetArp=true; } + dispatchCmd(DivCommand(DIV_CMD_HINT_ARPEGGIO,i,chan[i].arp)); break; case 0x0c: // retrigger if (effectVal!=0) { @@ -580,6 +600,7 @@ void DivEngine::processRow(int i, bool afterDelay) { case 0xe1: // portamento up chan[i].portaNote=chan[i].note+(effectVal&15); chan[i].portaSpeed=(effectVal>>4)*4; + dispatchCmd(DivCommand(DIV_CMD_HINT_PORTA,i,chan[i].portaNote,chan[i].portaSpeed)); chan[i].portaStop=true; chan[i].nowYouCanStop=false; chan[i].stopOnOff=song.stopPortaOnNoteOff; // what?! @@ -598,6 +619,7 @@ void DivEngine::processRow(int i, bool afterDelay) { case 0xe2: // portamento down chan[i].portaNote=chan[i].note-(effectVal&15); chan[i].portaSpeed=(effectVal>>4)*4; + dispatchCmd(DivCommand(DIV_CMD_HINT_PORTA,i,chan[i].portaNote,chan[i].portaSpeed)); chan[i].portaStop=true; chan[i].nowYouCanStop=false; chan[i].stopOnOff=song.stopPortaOnNoteOff; // what?! @@ -615,9 +637,11 @@ void DivEngine::processRow(int i, bool afterDelay) { break; case 0xe3: // vibrato direction chan[i].vibratoDir=effectVal; + dispatchCmd(DivCommand(DIV_CMD_HINT_VIBRATO_SHAPE,i,chan[i].vibratoDir)); break; case 0xe4: // vibrato fine chan[i].vibratoFine=effectVal; + dispatchCmd(DivCommand(DIV_CMD_HINT_VIBRATO_RANGE,i,chan[i].vibratoFine)); break; case 0xe5: // pitch chan[i].pitch=effectVal-0x80; @@ -628,6 +652,7 @@ void DivEngine::processRow(int i, bool afterDelay) { } //chan[i].pitch+=globalPitch; dispatchCmd(DivCommand(DIV_CMD_PITCH,i,chan[i].pitch+(((chan[i].vibratoDepth*vibTable[chan[i].vibratoPos]*chan[i].vibratoFine)>>4)/15))); + dispatchCmd(DivCommand(DIV_CMD_HINT_PITCH,i,chan[i].pitch)); break; case 0xea: // legato mode chan[i].legato=effectVal; @@ -677,17 +702,21 @@ void DivEngine::processRow(int i, bool afterDelay) { break; case 0xf3: // fine volume ramp up chan[i].volSpeed=effectVal; + dispatchCmd(DivCommand(DIV_CMD_HINT_VOL_SLIDE,i,chan[i].volSpeed)); break; case 0xf4: // fine volume ramp down chan[i].volSpeed=-effectVal; + dispatchCmd(DivCommand(DIV_CMD_HINT_VOL_SLIDE,i,chan[i].volSpeed)); break; case 0xf8: // single volume ramp up chan[i].volume=MIN(chan[i].volume+effectVal*256,chan[i].volMax); dispatchCmd(DivCommand(DIV_CMD_VOLUME,i,chan[i].volume>>8)); + dispatchCmd(DivCommand(DIV_CMD_HINT_VOLUME,i,chan[i].volume>>8)); break; case 0xf9: // single volume ramp down chan[i].volume=MAX(chan[i].volume-effectVal*256,0); dispatchCmd(DivCommand(DIV_CMD_VOLUME,i,chan[i].volume>>8)); + dispatchCmd(DivCommand(DIV_CMD_HINT_VOLUME,i,chan[i].volume>>8)); break; case 0xfa: // fast volume ramp if (effectVal!=0) { @@ -699,6 +728,7 @@ void DivEngine::processRow(int i, bool afterDelay) { } else { chan[i].volSpeed=0; } + dispatchCmd(DivCommand(DIV_CMD_HINT_VOL_SLIDE,i,chan[i].volSpeed)); break; case 0xff: // stop song @@ -729,15 +759,18 @@ void DivEngine::processRow(int i, bool afterDelay) { dispatchCmd(DivCommand(DIV_CMD_PITCH,i,chan[i].pitch+(((chan[i].vibratoDepth*vibTable[chan[i].vibratoPos]*chan[i].vibratoFine)>>4)/15))); if (chan[i].legato) { dispatchCmd(DivCommand(DIV_CMD_LEGATO,i,chan[i].note)); + dispatchCmd(DivCommand(DIV_CMD_HINT_LEGATO,i,chan[i].note)); } else { if (chan[i].inPorta && chan[i].keyOn && !chan[i].shorthandPorta) { if (song.e1e2StopOnSameNote && chan[i].wasShorthandPorta) { chan[i].portaSpeed=-1; + dispatchCmd(DivCommand(DIV_CMD_HINT_PORTA,i,chan[i].portaNote,chan[i].portaSpeed)); if (!song.brokenShortcutSlides) dispatchCmd(DivCommand(DIV_CMD_PRE_PORTA,i,false,0)); chan[i].wasShorthandPorta=false; chan[i].inPorta=false; } else { chan[i].portaNote=chan[i].note; + dispatchCmd(DivCommand(DIV_CMD_HINT_PORTA,i,chan[i].portaNote,chan[i].portaSpeed)); } } else if (!chan[i].noteOnInhibit) { dispatchCmd(DivCommand(DIV_CMD_NOTE_ON,i,chan[i].note,chan[i].volume>>8)); @@ -748,12 +781,14 @@ void DivEngine::processRow(int i, bool afterDelay) { if (!chan[i].keyOn && chan[i].scheduledSlideReset) { chan[i].portaNote=-1; chan[i].portaSpeed=-1; + dispatchCmd(DivCommand(DIV_CMD_HINT_PORTA,i,chan[i].portaNote,chan[i].portaSpeed)); chan[i].scheduledSlideReset=false; chan[i].inPorta=false; } if (!chan[i].keyOn && chan[i].volume>chan[i].volMax) { chan[i].volume=chan[i].volMax; dispatchCmd(DivCommand(DIV_CMD_VOLUME,i,chan[i].volume>>8)); + dispatchCmd(DivCommand(DIV_CMD_HINT_VOLUME,i,chan[i].volume>>8)); } chan[i].keyOn=true; chan[i].keyOff=false; @@ -993,15 +1028,19 @@ bool DivEngine::nextTick(bool noAccum, bool inhibitLowLat) { if (chan[i].volume>chan[i].volMax) { chan[i].volume=chan[i].volMax; chan[i].volSpeed=0; + dispatchCmd(DivCommand(DIV_CMD_HINT_VOLUME,i,chan[i].volume>>8)); dispatchCmd(DivCommand(DIV_CMD_VOLUME,i,chan[i].volume>>8)); + dispatchCmd(DivCommand(DIV_CMD_HINT_VOL_SLIDE,i,0)); } else if (chan[i].volume<0) { chan[i].volSpeed=0; + dispatchCmd(DivCommand(DIV_CMD_HINT_VOL_SLIDE,i,0)); if (song.legacyVolumeSlides) { chan[i].volume=chan[i].volMax+1; } else { chan[i].volume=0; } dispatchCmd(DivCommand(DIV_CMD_VOLUME,i,chan[i].volume>>8)); + dispatchCmd(DivCommand(DIV_CMD_HINT_VOLUME,i,chan[i].volume>>8)); } else { dispatchCmd(DivCommand(DIV_CMD_VOLUME,i,chan[i].volume>>8)); } @@ -1030,10 +1069,12 @@ bool DivEngine::nextTick(bool noAccum, bool inhibitLowLat) { if ((chan[i].keyOn || chan[i].keyOff) && chan[i].portaSpeed>0) { if (dispatchCmd(DivCommand(DIV_CMD_NOTE_PORTA,i,chan[i].portaSpeed*(song.linearPitch==2?song.pitchSlideSpeed:1),chan[i].portaNote))==2 && chan[i].portaStop && song.targetResetsSlides) { chan[i].portaSpeed=0; + dispatchCmd(DivCommand(DIV_CMD_HINT_PORTA,i,chan[i].portaNote,chan[i].portaSpeed)); chan[i].oldNote=chan[i].note; chan[i].note=chan[i].portaNote; chan[i].inPorta=false; dispatchCmd(DivCommand(DIV_CMD_LEGATO,i,chan[i].note)); + dispatchCmd(DivCommand(DIV_CMD_HINT_LEGATO,i,chan[i].note)); } } } @@ -1047,11 +1088,13 @@ bool DivEngine::nextTick(bool noAccum, bool inhibitLowLat) { if (chan[i].stopOnOff) { chan[i].portaNote=-1; chan[i].portaSpeed=-1; + dispatchCmd(DivCommand(DIV_CMD_HINT_PORTA,i,chan[i].portaNote,chan[i].portaSpeed)); chan[i].stopOnOff=false; } if (disCont[dispatchOfChan[i]].dispatch->keyOffAffectsPorta(dispatchChanOfChan[i])) { chan[i].portaNote=-1; chan[i].portaSpeed=-1; + dispatchCmd(DivCommand(DIV_CMD_HINT_PORTA,i,chan[i].portaNote,chan[i].portaSpeed)); /*if (i==2 && sysOfChan[i]==DIV_SYSTEM_SMS) { chan[i+1].portaNote=-1; chan[i+1].portaSpeed=-1; @@ -1065,6 +1108,7 @@ bool DivEngine::nextTick(bool noAccum, bool inhibitLowLat) { } if (chan[i].resetArp) { dispatchCmd(DivCommand(DIV_CMD_LEGATO,i,chan[i].note)); + dispatchCmd(DivCommand(DIV_CMD_HINT_LEGATO,i,chan[i].note)); chan[i].resetArp=false; } if (song.rowResetsArpPos && firstTick) {