diff --git a/src/engine/engine.h b/src/engine/engine.h index 52d2127ac..95559e393 100644 --- a/src/engine/engine.h +++ b/src/engine/engine.h @@ -104,7 +104,7 @@ struct DivChannelState { int delayOrder, delayRow, retrigSpeed, retrigTick; int vibratoDepth, vibratoRate, vibratoPos, vibratoPosGiant, vibratoDir, vibratoFine; int tremoloDepth, tremoloRate, tremoloPos; - unsigned char arp, arpStage, arpTicks, panL, panR, panRL, panRR, lastVibrato, lastPorta, cutType; + unsigned char arp, arpStage, arpTicks, panL, panR, panRL, panRR, lastVibrato, lastPorta, cutType, lastArp, lastVibrato2, lastPorta2; bool doNote, legato, portaStop, keyOn, keyOff, nowYouCanStop, stopOnOff, releasing; bool arpYield, delayLocked, inPorta, scheduledSlideReset, shorthandPorta, wasShorthandPorta, noteOnInhibit, resetArp; bool wentThroughNote, goneThroughNote; @@ -150,6 +150,9 @@ struct DivChannelState { lastVibrato(0), lastPorta(0), cutType(0), + lastArp(0), + lastVibrato2(0), + lastPorta2(0), doNote(false), legato(false), portaStop(false), diff --git a/src/engine/fileOps/tfm.cpp b/src/engine/fileOps/tfm.cpp index 7d9a8687e..b23fa18a3 100644 --- a/src/engine/fileOps/tfm.cpp +++ b/src/engine/fileOps/tfm.cpp @@ -134,6 +134,14 @@ public: return ret; } + String readString(size_t l) { + String ret; + ret.reserve(l); + while (l--) { + ret += readC(); + } + return ret; + } void skip(size_t l) { // quick and dirty while (l--) { @@ -157,12 +165,13 @@ bool DivEngine::loadTFM(unsigned char* file, size_t len) { DivSong ds; ds.systemName="Sega Genesis/Mega Drive or TurboSound FM"; ds.subsong[0]->hz=50; - ds.systemLen = 1; + ds.systemLen=1; + ds.resetEffectsOnNewNote=true; ds.system[0]=DIV_SYSTEM_YM2612; unsigned char magic[8]={0}; - reader.readNoRLE(magic, 8); + reader.readNoRLE(magic,8); if (memcmp(magic,DIV_TFM_MAGIC,8)!=0) throw InvalidHeaderException(); unsigned char speedEven=reader.readCNoRLE(); @@ -197,24 +206,17 @@ bool DivEngine::loadTFM(unsigned char* file, size_t len) { // TODO: use this for something, number of saves (void)reader.readSNoRLE(); - unsigned char buffer[384]; - // author logD("parsing author"); - reader.read(buffer,64); - ds.author=String((const char*)buffer,strnlen((const char*)buffer,64)); - memset(buffer, 0, 64); + ds.author=reader.readString(64); // name logD("parsing name"); - reader.read(buffer,64); - ds.name=String((const char*)buffer,strnlen((const char*)buffer,64)); - memset(buffer, 0, 64); + ds.name=reader.readString(64); // notes logD("parsing notes"); - reader.read(buffer,384); - String notes((const char*)buffer,strnlen((const char*)buffer,384)); + String notes=reader.readString(384); // fix \r\n to \n for (auto& c : notes) { @@ -398,9 +400,12 @@ bool DivEngine::loadTFM(unsigned char* file, size_t len) { pat->data[k][5]=effectVal[k]; break; case 1: - // pitch slide up + // note slide up case 2: - // pitch slide down + // note slide down + pat->data[k][4]=0xF0|effectNum[k]; + pat->data[k][5]=effectVal[k]; + break; case 3: // portamento case 4: diff --git a/src/engine/playback.cpp b/src/engine/playback.cpp index 1e7d823c2..9364f1a89 100644 --- a/src/engine/playback.cpp +++ b/src/engine/playback.cpp @@ -740,6 +740,7 @@ void DivEngine::processRow(int i, bool afterDelay) { } break; case 0x03: // portamento + chan[i].lastPorta2=effectVal; if (effectVal==0) { chan[i].portaNote=-1; chan[i].portaSpeed=-1; @@ -768,6 +769,7 @@ void DivEngine::processRow(int i, bool afterDelay) { } break; case 0x04: // vibrato + chan[i].lastVibrato2=effectVal; if (effectVal) chan[i].lastVibrato=effectVal; chan[i].vibratoDepth=effectVal&15; chan[i].vibratoRate=effectVal>>4; @@ -775,6 +777,7 @@ 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))); break; case 0x05: // vol slide + vibrato + chan[i].lastVibrato2=effectVal; if (effectVal==0) { chan[i].vibratoDepth=0; chan[i].vibratoRate=0; @@ -782,6 +785,7 @@ void DivEngine::processRow(int i, bool afterDelay) { chan[i].vibratoDepth=chan[i].lastVibrato&15; chan[i].vibratoRate=chan[i].lastVibrato>>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))); // TODO: non-0x-or-x0 value should be treated as 00 @@ -800,6 +804,7 @@ void DivEngine::processRow(int i, bool afterDelay) { dispatchCmd(DivCommand(DIV_CMD_HINT_VOL_SLIDE,i,chan[i].volSpeed)); break; case 0x06: // vol slide + porta + chan[i].lastPorta2=effectVal; if (effectVal==0 || chan[i].lastPorta==0) { chan[i].portaNote=-1; chan[i].portaSpeed=-1; @@ -870,6 +875,7 @@ void DivEngine::processRow(int i, bool afterDelay) { dispatchCmd(DivCommand(DIV_CMD_HINT_VOL_SLIDE,i,chan[i].volSpeed)); break; case 0x00: // arpeggio + chan[i].lastArp=effectVal; chan[i].arp=effectVal; if (chan[i].arp==0 && song.arp0Reset) { chan[i].resetArp=true; @@ -1110,6 +1116,33 @@ void DivEngine::processRow(int i, bool afterDelay) { } } + if (song.resetEffectsOnNewNote) { + if (chan[i].lastArp) { + chan[i].lastArp=0; + } else { + chan[i].arp=0; + dispatchCmd(DivCommand(DIV_CMD_HINT_ARPEGGIO,i,chan[i].arp)); + } + + if (chan[i].lastVibrato2) { + chan[i].lastVibrato2=0; + } else { + chan[i].vibratoDepth=0; + chan[i].vibratoRate=0; + dispatchCmd(DivCommand(DIV_CMD_HINT_VIBRATO,i,chan[i].vibratoDepth,chan[i].vibratoRate)); + } + + if (chan[i].lastPorta2) { + chan[i].lastPorta2=0; + } else { + chan[i].portaSpeed=-1; + chan[i].portaNote=-1; + dispatchCmd(DivCommand(DIV_CMD_HINT_PORTA,i,CLAMP(chan[i].portaNote,-128,127),MAX(chan[i].portaSpeed,0))); + chan[i].inPorta=false; + dispatchCmd(DivCommand(DIV_CMD_PRE_PORTA,i,false,0)); + } + } + if (panChanged) { dispatchCmd(DivCommand(DIV_CMD_PANNING,i,chan[i].panL,chan[i].panR)); } @@ -1146,7 +1179,7 @@ void DivEngine::processRow(int i, bool afterDelay) { dispatchCmd(DivCommand(DIV_CMD_NOTE_ON,i,chan[i].note,chan[i].volume>>8)); chan[i].releasing=false; if (song.resetArpPhaseOnNewNote) { - chan[i].arpStage=-1; + chan[i].arpStage=-1; } chan[i].goneThroughNote=true; chan[i].wentThroughNote=true; diff --git a/src/engine/song.h b/src/engine/song.h index f9983ba9e..0d99cc747 100644 --- a/src/engine/song.h +++ b/src/engine/song.h @@ -331,6 +331,7 @@ struct DivSong { bool resetArpPhaseOnNewNote; bool ceilVolumeScaling; bool oldAlwaysSetVolume; + bool resetEffectsOnNewNote; std::vector ins; std::vector wave; @@ -454,7 +455,8 @@ struct DivSong { oldDPCM(false), resetArpPhaseOnNewNote(false), ceilVolumeScaling(false), - oldAlwaysSetVolume(false) { + oldAlwaysSetVolume(false), + resetEffectsOnNewNote(false) { for (int i=0; i