From 8db53faf720800d769fcbddbb564a24d33e34ccd Mon Sep 17 00:00:00 2001 From: tildearrow Date: Fri, 22 Apr 2022 04:23:52 -0500 Subject: [PATCH 01/46] new FM frequency calculation experiments --- src/engine/dispatch.h | 1 + src/engine/platform/genesis.cpp | 78 +++++++++--------------------- src/engine/platform/genesis.h | 3 -- src/engine/platform/genesisext.cpp | 66 +++++++++++-------------- 4 files changed, 51 insertions(+), 97 deletions(-) diff --git a/src/engine/dispatch.h b/src/engine/dispatch.h index c35ff919..b358c464 100644 --- a/src/engine/dispatch.h +++ b/src/engine/dispatch.h @@ -426,6 +426,7 @@ class DivDispatch { #define NOTE_PERIODIC(x) round(parent->calcBaseFreq(chipClock,CHIP_DIVIDER,x,true)) #define NOTE_PERIODIC_NOROUND(x) parent->calcBaseFreq(chipClock,CHIP_DIVIDER,x,true) #define NOTE_FREQUENCY(x) parent->calcBaseFreq(chipClock,CHIP_FREQBASE,x,false) +#define NOTE_FNUM_BLOCK(x,bits) ((((int)parent->calcBaseFreq(chipClock,CHIP_FREQBASE,(x)%12,false))&((1<calcFreq(chan[i].baseFreq,chan[i].pitch,false,octave(chan[i].baseFreq)); - if (chan[i].freq>262143) chan[i].freq=262143; - int freqt=toFreq(chan[i].freq)+chan[i].std.pitch.val; + chan[i].freq=((chan[i].baseFreq&0xf800)|parent->calcFreq(chan[i].baseFreq&0x7ff,chan[i].pitch,false))+chan[i].std.pitch.val; + if (chan[i].freq>65535) chan[i].freq=65535; + int freqt=chan[i].freq; immWrite(chanOffs[i]+ADDR_FREQH,freqt>>8); immWrite(chanOffs[i]+ADDR_FREQ,freqt&0xff); if (chan[i].furnaceDac && dacMode) { @@ -422,47 +422,6 @@ void DivPlatformGenesis::tick(bool sysTick) { } } -int DivPlatformGenesis::octave(int freq) { - if (freq>=82432) { - return 128; - } else if (freq>=41216) { - return 64; - } else if (freq>=20608) { - return 32; - } else if (freq>=10304) { - return 16; - } else if (freq>=5152) { - return 8; - } else if (freq>=2576) { - return 4; - } else if (freq>=1288) { - return 2; - } else { - return 1; - } - return 1; -} - -int DivPlatformGenesis::toFreq(int freq) { - if (freq>=82432) { - return 0x3800|((freq>>7)&0x7ff); - } else if (freq>=41216) { - return 0x3000|((freq>>6)&0x7ff); - } else if (freq>=20608) { - return 0x2800|((freq>>5)&0x7ff); - } else if (freq>=10304) { - return 0x2000|((freq>>4)&0x7ff); - } else if (freq>=5152) { - return 0x1800|((freq>>3)&0x7ff); - } else if (freq>=2576) { - return 0x1000|((freq>>2)&0x7ff); - } else if (freq>=1288) { - return 0x800|((freq>>1)&0x7ff); - } else { - return freq&0x7ff; - } -} - void DivPlatformGenesis::muteChannel(int ch, bool mute) { isMuted[ch]=mute; for (int j=0; j<4; j++) { @@ -509,7 +468,7 @@ int DivPlatformGenesis::dispatch(DivCommand c) { dacPos=0; dacPeriod=0; if (c.value!=DIV_NOTE_NULL) { - chan[c.chan].baseFreq=NOTE_FREQUENCY(c.value); + chan[c.chan].baseFreq=NOTE_FNUM_BLOCK(c.value,11); chan[c.chan].freqChanged=true; } chan[c.chan].furnaceDac=true; @@ -576,7 +535,7 @@ int DivPlatformGenesis::dispatch(DivCommand c) { chan[c.chan].insChanged=false; if (c.value!=DIV_NOTE_NULL) { - chan[c.chan].baseFreq=NOTE_FREQUENCY(c.value); + chan[c.chan].baseFreq=NOTE_FNUM_BLOCK(c.value,11); chan[c.chan].portaPause=false; chan[c.chan].note=c.value; chan[c.chan].freqChanged=true; @@ -656,26 +615,33 @@ int DivPlatformGenesis::dispatch(DivCommand c) { break; } case DIV_CMD_NOTE_PORTA: { - int destFreq=NOTE_FREQUENCY(c.value2); + int destFreq=NOTE_FNUM_BLOCK(c.value2,11); int newFreq; bool return2=false; if (destFreq>chan[c.chan].baseFreq) { - newFreq=chan[c.chan].baseFreq+c.value*octave(chan[c.chan].baseFreq); + newFreq=chan[c.chan].baseFreq+c.value; if (newFreq>=destFreq) { newFreq=destFreq; return2=true; } } else { - newFreq=chan[c.chan].baseFreq-c.value*octave(chan[c.chan].baseFreq); + newFreq=chan[c.chan].baseFreq-c.value; if (newFreq<=destFreq) { newFreq=destFreq; return2=true; } } + // check for octave boundary if (!chan[c.chan].portaPause) { - if (octave(chan[c.chan].baseFreq)!=octave(newFreq)) { - chan[c.chan].portaPause=true; - break; + if ((newFreq&0x7ff)>1288) { + newFreq=((newFreq&0x7ff)>>1)|((newFreq+0x800)&0xf800); + /*chan[c.chan].portaPause=true; + break;*/ + } + if ((newFreq&0x7ff)<644) { + newFreq=(newFreq&0x7ff)<<1|((newFreq-0x800)&0xf800); + /*chan[c.chan].portaPause=true; + break;*/ } } chan[c.chan].baseFreq=newFreq; @@ -699,7 +665,7 @@ int DivPlatformGenesis::dispatch(DivCommand c) { } break; case DIV_CMD_LEGATO: { - chan[c.chan].baseFreq=NOTE_FREQUENCY(c.value); + chan[c.chan].baseFreq=NOTE_FNUM_BLOCK(c.value,11); chan[c.chan].note=c.value; chan[c.chan].freqChanged=true; break; diff --git a/src/engine/platform/genesis.h b/src/engine/platform/genesis.h index 34db5a25..ce52c6df 100644 --- a/src/engine/platform/genesis.h +++ b/src/engine/platform/genesis.h @@ -93,9 +93,6 @@ class DivPlatformGenesis: public DivDispatch { short oldWrites[512]; short pendingWrites[512]; - int octave(int freq); - int toFreq(int freq); - friend void putDispatchChan(void*,int,int); void acquire_nuked(short* bufL, short* bufR, size_t start, size_t len); diff --git a/src/engine/platform/genesisext.cpp b/src/engine/platform/genesisext.cpp index f0369370..db51f5ea 100644 --- a/src/engine/platform/genesisext.cpp +++ b/src/engine/platform/genesisext.cpp @@ -72,7 +72,7 @@ int DivPlatformGenesisExt::dispatch(DivCommand c) { opChan[ch].insChanged=false; if (c.value!=DIV_NOTE_NULL) { - opChan[ch].baseFreq=NOTE_FREQUENCY(c.value); + opChan[ch].baseFreq=NOTE_FNUM_BLOCK(c.value,11); opChan[ch].portaPause=false; opChan[ch].freqChanged=true; } @@ -127,31 +127,46 @@ int DivPlatformGenesisExt::dispatch(DivCommand c) { break; } case DIV_CMD_NOTE_PORTA: { - int destFreq=NOTE_FREQUENCY(c.value2); + int destFreq=NOTE_FNUM_BLOCK(c.value2,11); int newFreq; bool return2=false; if (destFreq>opChan[ch].baseFreq) { - newFreq=opChan[ch].baseFreq+c.value*octave(opChan[ch].baseFreq); + newFreq=opChan[ch].baseFreq+c.value; if (newFreq>=destFreq) { newFreq=destFreq; return2=true; } } else { - newFreq=opChan[ch].baseFreq-c.value*octave(opChan[ch].baseFreq); + newFreq=opChan[ch].baseFreq-c.value; if (newFreq<=destFreq) { newFreq=destFreq; return2=true; } } if (!opChan[ch].portaPause) { - if (octave(opChan[ch].baseFreq)!=octave(newFreq)) { + opChan[ch].freqChanged=true; + if ((newFreq&0x7ff)>1288) { + newFreq=((newFreq&0x7ff)>>1)|((newFreq+0x800)&0xf800); opChan[ch].portaPause=true; - break; + opChan[ch].freqChanged=false; + return2=false; + if (ch==3) printf("%d: upper bound\n",ch); + //break; } + if ((newFreq&0x7ff)<644) { + newFreq=(newFreq&0x7ff)<<1|((newFreq-0x800)&0xf800); + opChan[ch].portaPause=true; + opChan[ch].freqChanged=false; + return2=false; + if (ch==3) printf("%d: lower bound\n",ch); + //break; + } + } else { + opChan[ch].portaPause=false; + opChan[ch].freqChanged=true; } + if (ch==3) printf("%d: writing %.4x to freq\n",ch,newFreq); opChan[ch].baseFreq=newFreq; - opChan[ch].portaPause=false; - opChan[ch].freqChanged=true; if (return2) return 2; break; } @@ -172,7 +187,7 @@ int DivPlatformGenesisExt::dispatch(DivCommand c) { } break; case DIV_CMD_LEGATO: { - opChan[ch].baseFreq=NOTE_FREQUENCY(c.value); + opChan[ch].baseFreq=NOTE_FNUM_BLOCK(c.value,11); opChan[ch].freqChanged=true; break; } @@ -289,35 +304,10 @@ void DivPlatformGenesisExt::tick(bool sysTick) { unsigned char writeMask=2; if (extMode) for (int i=0; i<4; i++) { if (opChan[i].freqChanged) { - opChan[i].freq=parent->calcFreq(opChan[i].baseFreq,opChan[i].pitch); - if (opChan[i].freq>262143) opChan[i].freq=262143; - if (opChan[i].freq>=82432) { - opChan[i].freqH=((opChan[i].freq>>15)&7)|0x38; - opChan[i].freqL=(opChan[i].freq>>7)&0xff; - } else if (opChan[i].freq>=41216) { - opChan[i].freqH=((opChan[i].freq>>14)&7)|0x30; - opChan[i].freqL=(opChan[i].freq>>6)&0xff; - } else if (opChan[i].freq>=20608) { - opChan[i].freqH=((opChan[i].freq>>13)&7)|0x28; - opChan[i].freqL=(opChan[i].freq>>5)&0xff; - } else if (opChan[i].freq>=10304) { - opChan[i].freqH=((opChan[i].freq>>12)&7)|0x20; - opChan[i].freqL=(opChan[i].freq>>4)&0xff; - } else if (opChan[i].freq>=5152) { - opChan[i].freqH=((opChan[i].freq>>11)&7)|0x18; - opChan[i].freqL=(opChan[i].freq>>3)&0xff; - } else if (opChan[i].freq>=2576) { - opChan[i].freqH=((opChan[i].freq>>10)&7)|0x10; - opChan[i].freqL=(opChan[i].freq>>2)&0xff; - } else if (opChan[i].freq>=1288) { - opChan[i].freqH=((opChan[i].freq>>9)&7)|0x08; - opChan[i].freqL=(opChan[i].freq>>1)&0xff; - } else { - opChan[i].freqH=(opChan[i].freq>>8)&7; - opChan[i].freqL=opChan[i].freq&0xff; - } - immWrite(opChanOffsH[i],opChan[i].freqH); - immWrite(opChanOffsL[i],opChan[i].freqL); + opChan[i].freq=(opChan[i].baseFreq&0xf800)|parent->calcFreq(opChan[i].baseFreq&0x7ff,opChan[i].pitch); + if (chan[i].freq>65535) chan[i].freq=65535; + immWrite(opChanOffsH[i],opChan[i].freq>>8); + immWrite(opChanOffsL[i],opChan[i].freq&0xff); } writeMask|=opChan[i].active<<(4+i); if (opChan[i].keyOn) { From 943e013cb469319fbbda2cc9540e39677ba773d7 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Fri, 22 Apr 2022 16:46:24 -0500 Subject: [PATCH 02/46] YM2612: new pitch slide formula finally nailed it (almost) --- src/engine/platform/genesis.cpp | 21 +++++++++++++-------- src/engine/platform/genesisext.cpp | 10 +++------- 2 files changed, 16 insertions(+), 15 deletions(-) diff --git a/src/engine/platform/genesis.cpp b/src/engine/platform/genesis.cpp index 80dac1d4..615747dc 100644 --- a/src/engine/platform/genesis.cpp +++ b/src/engine/platform/genesis.cpp @@ -632,21 +632,26 @@ int DivPlatformGenesis::dispatch(DivCommand c) { } } // check for octave boundary + // what the heck! if (!chan[c.chan].portaPause) { + chan[c.chan].freqChanged=true; if ((newFreq&0x7ff)>1288) { - newFreq=((newFreq&0x7ff)>>1)|((newFreq+0x800)&0xf800); - /*chan[c.chan].portaPause=true; - break;*/ + newFreq=(644)|((newFreq+0x800)&0xf800); + chan[c.chan].portaPause=true; + chan[c.chan].freqChanged=false; + return2=false; } if ((newFreq&0x7ff)<644) { - newFreq=(newFreq&0x7ff)<<1|((newFreq-0x800)&0xf800); - /*chan[c.chan].portaPause=true; - break;*/ + newFreq=(1287)|((newFreq-0x800)&0xf800); + chan[c.chan].portaPause=true; + chan[c.chan].freqChanged=false; + return2=false; } + } else { + chan[c.chan].portaPause=false; + chan[c.chan].freqChanged=true; } chan[c.chan].baseFreq=newFreq; - chan[c.chan].portaPause=false; - chan[c.chan].freqChanged=true; if (return2) { chan[c.chan].inPorta=false; return 2; diff --git a/src/engine/platform/genesisext.cpp b/src/engine/platform/genesisext.cpp index db51f5ea..2f8fba8c 100644 --- a/src/engine/platform/genesisext.cpp +++ b/src/engine/platform/genesisext.cpp @@ -143,29 +143,25 @@ int DivPlatformGenesisExt::dispatch(DivCommand c) { return2=true; } } + // what the heck! if (!opChan[ch].portaPause) { opChan[ch].freqChanged=true; if ((newFreq&0x7ff)>1288) { - newFreq=((newFreq&0x7ff)>>1)|((newFreq+0x800)&0xf800); + newFreq=(644)|((newFreq+0x800)&0xf800); opChan[ch].portaPause=true; opChan[ch].freqChanged=false; return2=false; - if (ch==3) printf("%d: upper bound\n",ch); - //break; } if ((newFreq&0x7ff)<644) { - newFreq=(newFreq&0x7ff)<<1|((newFreq-0x800)&0xf800); + newFreq=(1287)|((newFreq-0x800)&0xf800); opChan[ch].portaPause=true; opChan[ch].freqChanged=false; return2=false; - if (ch==3) printf("%d: lower bound\n",ch); - //break; } } else { opChan[ch].portaPause=false; opChan[ch].freqChanged=true; } - if (ch==3) printf("%d: writing %.4x to freq\n",ch,newFreq); opChan[ch].baseFreq=newFreq; if (return2) return 2; break; From dd80cb8b55c9adf7e7c2441cf4ca47105300d7d6 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Fri, 22 Apr 2022 17:42:42 -0500 Subject: [PATCH 03/46] what the heck --- src/engine/platform/genesis.cpp | 2 +- src/engine/platform/genesisext.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/engine/platform/genesis.cpp b/src/engine/platform/genesis.cpp index 615747dc..43aae3f1 100644 --- a/src/engine/platform/genesis.cpp +++ b/src/engine/platform/genesis.cpp @@ -394,7 +394,7 @@ void DivPlatformGenesis::tick(bool sysTick) { for (int i=0; i<6; i++) { if (i==2 && extMode) continue; if (chan[i].freqChanged) { - chan[i].freq=((chan[i].baseFreq&0xf800)|parent->calcFreq(chan[i].baseFreq&0x7ff,chan[i].pitch,false))+chan[i].std.pitch.val; + chan[i].freq=((chan[i].baseFreq&0xf800)|parent->calcFreq(chan[i].baseFreq&0x7ff,chan[i].pitch,false,4))+chan[i].std.pitch.val; if (chan[i].freq>65535) chan[i].freq=65535; int freqt=chan[i].freq; immWrite(chanOffs[i]+ADDR_FREQH,freqt>>8); diff --git a/src/engine/platform/genesisext.cpp b/src/engine/platform/genesisext.cpp index 2f8fba8c..bb33f401 100644 --- a/src/engine/platform/genesisext.cpp +++ b/src/engine/platform/genesisext.cpp @@ -300,7 +300,7 @@ void DivPlatformGenesisExt::tick(bool sysTick) { unsigned char writeMask=2; if (extMode) for (int i=0; i<4; i++) { if (opChan[i].freqChanged) { - opChan[i].freq=(opChan[i].baseFreq&0xf800)|parent->calcFreq(opChan[i].baseFreq&0x7ff,opChan[i].pitch); + opChan[i].freq=(opChan[i].baseFreq&0xf800)|parent->calcFreq(opChan[i].baseFreq&0x7ff,opChan[i].pitch,false,4); if (chan[i].freq>65535) chan[i].freq=65535; immWrite(opChanOffsH[i],opChan[i].freq>>8); immWrite(opChanOffsL[i],opChan[i].freq&0xff); From f6b34a5bb0535383385b3da4014c4bfc5da58eed Mon Sep 17 00:00:00 2001 From: tildearrow Date: Sat, 23 Apr 2022 04:25:22 -0500 Subject: [PATCH 04/46] huh? --- src/engine/platform/genesisext.cpp | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/engine/platform/genesisext.cpp b/src/engine/platform/genesisext.cpp index bb33f401..d8ae8b38 100644 --- a/src/engine/platform/genesisext.cpp +++ b/src/engine/platform/genesisext.cpp @@ -144,19 +144,18 @@ int DivPlatformGenesisExt::dispatch(DivCommand c) { } } // what the heck! + // TODO: rework! if (!opChan[ch].portaPause) { - opChan[ch].freqChanged=true; if ((newFreq&0x7ff)>1288) { newFreq=(644)|((newFreq+0x800)&0xf800); opChan[ch].portaPause=true; - opChan[ch].freqChanged=false; return2=false; - } - if ((newFreq&0x7ff)<644) { + } else if ((newFreq&0x7ff)<644) { newFreq=(1287)|((newFreq-0x800)&0xf800); opChan[ch].portaPause=true; - opChan[ch].freqChanged=false; return2=false; + } else { + opChan[ch].freqChanged=true; } } else { opChan[ch].portaPause=false; From f716ac262d87a96676c07c20845d2e603b13be0e Mon Sep 17 00:00:00 2001 From: James Alan Nguyen Date: Sat, 23 Apr 2022 19:52:09 +1000 Subject: [PATCH 05/46] #79: OPM import progress and start everything else --- src/engine/engine.h | 7 ++ src/engine/fileOpsIns.cpp | 150 ++++++++++++++++++++++++++++++++++++-- src/engine/safeReader.cpp | 48 +++++++++++- src/engine/safeReader.h | 3 + 4 files changed, 198 insertions(+), 10 deletions(-) diff --git a/src/engine/engine.h b/src/engine/engine.h index 515b9c59..c8afb86a 100644 --- a/src/engine/engine.h +++ b/src/engine/engine.h @@ -31,6 +31,7 @@ #include #include #include +#include #define addWarning(x) \ if (warnings.empty()) { \ @@ -288,9 +289,15 @@ class DivEngine { void loadVGI(SafeReader& reader, std::vector& ret, String& stripPath); void loadS3I(SafeReader& reader, std::vector& ret, String& stripPath); void loadSBI(SafeReader& reader, std::vector& ret, String& stripPath); + void loadOPLI(SafeReader& reader, std::vector& ret, String& stripPath); + void loadOPNI(SafeReader& reader, std::vector& ret, String& stripPath); + void loadPAT(SafeReader& reader, std::vector& ret, String& stripPath); + void loadY12(SafeReader& reader, std::vector& ret, String& stripPath); void loadBNK(SafeReader& reader, std::vector& ret, String& stripPath); void loadOPM(SafeReader& reader, std::vector& ret, String& stripPath); void loadFF(SafeReader& reader, std::vector& ret, String& stripPath); + void loadWOPL(SafeReader& reader, std::vector& ret, String& stripPath); + void loadWOPN(SafeReader& reader, std::vector& ret, String& stripPath); bool initAudioBackend(); bool deinitAudioBackend(); diff --git a/src/engine/fileOpsIns.cpp b/src/engine/fileOpsIns.cpp index f9eaab38..148c9d2e 100644 --- a/src/engine/fileOpsIns.cpp +++ b/src/engine/fileOpsIns.cpp @@ -30,9 +30,15 @@ enum DivInsFormats { DIV_INSFORMAT_BTI, DIV_INSFORMAT_S3I, DIV_INSFORMAT_SBI, + DIV_INSFORMAT_PAT, + DIV_INSFORMAT_Y12, + DIV_INSFORMAT_OPLI, + DIV_INSFORMAT_OPNI, DIV_INSFORMAT_BNK, DIV_INSFORMAT_OPM, DIV_INSFORMAT_FF, + DIV_INSFORMAT_WOPL, + DIV_INSFORMAT_WOPN, }; // Patch data structures @@ -670,6 +676,7 @@ void DivEngine::loadBNK(SafeReader& reader, std::vector& ret, St auto& ins = insList[i]; ins->type = DIV_INS_OPL; + ins->fm.ops = 2; timbre.mode = reader.readC(); timbre.percVoice = reader.readC(); @@ -810,16 +817,132 @@ void DivEngine::loadFF(SafeReader& reader, std::vector& ret, Str } void DivEngine::loadOPM(SafeReader& reader, std::vector& ret, String& stripPath) { - DivInstrument* ins[128]; - memset(ins,0,128*sizeof(void*)); + std::vector insList; + std::stringstream ss; + + int readCount = 0; + + bool patchNameRead = false, + lfoRead = false, + characteristicRead = false, + m1Read = false, + c1Read = false, + m2Read = false, + c2Read = false; + + auto completePatchRead = [&]() { + return patchNameRead && lfoRead && characteristicRead && m1Read && c1Read && m2Read && c2Read; + }; + auto resetPatchRead = [&]() { + patchNameRead = lfoRead = characteristicRead = m1Read = c1Read = m2Read = c2Read = false; + }; + auto readOpmOperator = [](SafeReader& reader, DivInstrumentFM::Operator& op) { + op.ar = atoi(reader.readString_Token().c_str()); + op.dr = atoi(reader.readString_Token().c_str()); + op.d2r = atoi(reader.readString_Token().c_str()); + op.rr = atoi(reader.readString_Token().c_str()); + op.sl = atoi(reader.readString_Token().c_str()); + op.tl = atoi(reader.readString_Token().c_str()); + op.ksl = atoi(reader.readString_Token().c_str()); + op.mult = atoi(reader.readString_Token().c_str()); + op.dt = atoi(reader.readString_Token().c_str()); + op.dt2 = atoi(reader.readString_Token().c_str()); + op.ssgEnv = atoi(reader.readString_Token().c_str()); + }; + + DivInstrument* newPatch = nullptr; try { - String line; - + reader.seek(0, SEEK_SET); + while (!reader.isEOF()) { + String token = reader.readString_Token(); + if (token.length() == 0) { + continue; + } + + if (token.compare(0,2,"//") == 0) { + if (!reader.isEOF()) { + reader.readString_Line(); + } + continue; + } + + // At this point we know any other line would be associated with patch params + if (newPatch == nullptr) { + newPatch = new DivInstrument; + newPatch->type = DIV_INS_FM; + newPatch->fm.ops = 4; + } + + // Read each line for their respective params. They may not be written in the same order but they + // must absolutely be properly grouped per patch! Line prefixes must be separated by a space! + + // Patch number + name + // "@:123 Name of patch" + if (token.length() >= 2) { + if (token[0] == '@') { + newPatch->name = reader.readString_Line(); + patchNameRead = true; + } else if (token.compare(0,3,"CH:") == 0) { + // CH: PAN FL CON AMS PMS SLOT NE + reader.readString_Token(); // skip PAN + newPatch->fm.fb = atoi(reader.readString_Token().c_str()); + newPatch->fm.alg = atoi(reader.readString_Token().c_str()); + newPatch->fm.ams = atoi(reader.readString_Token().c_str()); + newPatch->fm.ams2 = atoi(reader.readString_Token().c_str()); + reader.readString_Token(); // skip SLOT + reader.readString_Token(); // skip NE + characteristicRead = true; + } else if (token.compare(0,3,"C1:") == 0) { + // C1: AR D1R D2R RR D1L TL KS MUL DT1 DT2 AMS-EN + readOpmOperator(reader, newPatch->fm.op[2]); + c1Read = true; + } else if (token.compare(0,3,"C2:") == 0) { + // C2: AR D1R D2R RR D1L TL KS MUL DT1 DT2 AMS-EN + readOpmOperator(reader, newPatch->fm.op[3]); + c2Read = true; + } else if (token.compare(0,3,"M1:") == 0) { + // M1: AR D1R D2R RR D1L TL KS MUL DT1 DT2 AMS-EN + readOpmOperator(reader, newPatch->fm.op[0]); + m1Read = true; + } else if (token.compare(0,3,"M2:") == 0) { + // M2: AR D1R D2R RR D1L TL KS MUL DT1 DT2 AMS-EN + readOpmOperator(reader, newPatch->fm.op[1]); + m2Read = true; + } else if (token.compare(0,4,"LFO:") == 0) { + // LFO: LFRQ AMD PMD WF NFRQ + // Furnace patches do not store this as these are chip-global. + reader.readString_Line(); + lfoRead = true; + } else { + // other unsupported lines ignored. + reader.readString_Line(); + } + } + + if (completePatchRead()) { + insList.push_back(newPatch); + newPatch = nullptr; + ++readCount; + } + } + + if (newPatch != nullptr) { + addWarning("Last OPM patch read was incomplete and therefore not imported."); + logW("Last OPM patch read was incomplete and therefore not imported."); + delete newPatch; + } + + for (int i = 0; i < readCount; ++i) { + ret.push_back(insList[i]); + } } catch (EndOfFileException& e) { - lastError="premature end of file"; + lastError = "premature end of file"; logE("premature end of file"); - return; + for (int i = readCount; i >= 0; --i) { + delete insList[i]; + } + delete newPatch; } } @@ -947,12 +1070,24 @@ std::vector DivEngine::instrumentFromFile(const char* path) { format=DIV_INSFORMAT_S3I; } else if (extS==String(".sbi")) { format=DIV_INSFORMAT_SBI; + } else if (extS==String(".opli")) { + format=DIV_INSFORMAT_OPLI; + } else if (extS==String(".opni")) { + format=DIV_INSFORMAT_OPNI; + } else if (extS==String(".pat")) { + format=DIV_INSFORMAT_PAT; + } else if (extS==String(".y12")) { + format=DIV_INSFORMAT_Y12; } else if (extS==String(".bnk")) { format=DIV_INSFORMAT_BNK; } else if (extS==String(".opm")) { format=DIV_INSFORMAT_OPM; } else if (extS==String(".ff")) { format=DIV_INSFORMAT_FF; + } else if (extS==String(".wopl")) { + format=DIV_INSFORMAT_WOPL; + } else if (extS==String(".wopn")) { + format=DIV_INSFORMAT_WOPN; } } @@ -971,7 +1106,8 @@ std::vector DivEngine::instrumentFromFile(const char* path) { break; case DIV_INSFORMAT_BTI: // TODO break; - case DIV_INSFORMAT_OPM: // TODO + case DIV_INSFORMAT_OPM: + loadOPM(reader,ret,stripPath); break; case DIV_INSFORMAT_S3I: loadS3I(reader,ret,stripPath); diff --git a/src/engine/safeReader.cpp b/src/engine/safeReader.cpp index 7fa66e8b..e46827c6 100644 --- a/src/engine/safeReader.cpp +++ b/src/engine/safeReader.cpp @@ -139,12 +139,14 @@ double SafeReader::readD() { } String SafeReader::readString(size_t stlen) { - String ret; + String ret(stlen, ' '); #ifdef READ_DEBUG logD("SR: reading string len %d at %x",stlen,curSeek); #endif size_t curPos=0; - while (curPos= len; +} \ No newline at end of file diff --git a/src/engine/safeReader.h b/src/engine/safeReader.h index f674f592..9fd0282b 100644 --- a/src/engine/safeReader.h +++ b/src/engine/safeReader.h @@ -66,6 +66,9 @@ class SafeReader { double readD_BE(); String readString(); String readString(size_t len); + String readString_Line(); + String readString_Token(unsigned char delim=' '); + bool isEOF(); SafeReader(void* b, size_t l): buf((unsigned char*)b), From 3550ad512a83bdd1c420056838905098fdd3a2fa Mon Sep 17 00:00:00 2001 From: James Alan Nguyen Date: Sat, 23 Apr 2022 20:20:29 +1000 Subject: [PATCH 06/46] OPM import: Fix reset read procedural flow --- src/engine/fileOpsIns.cpp | 7 ++++--- src/gui/gui.cpp | 4 ++-- src/gui/settings.cpp | 1 + 3 files changed, 7 insertions(+), 5 deletions(-) diff --git a/src/engine/fileOpsIns.cpp b/src/engine/fileOpsIns.cpp index 148c9d2e..b74c7cba 100644 --- a/src/engine/fileOpsIns.cpp +++ b/src/engine/fileOpsIns.cpp @@ -829,12 +829,15 @@ void DivEngine::loadOPM(SafeReader& reader, std::vector& ret, St c1Read = false, m2Read = false, c2Read = false; + + DivInstrument* newPatch = nullptr; auto completePatchRead = [&]() { return patchNameRead && lfoRead && characteristicRead && m1Read && c1Read && m2Read && c2Read; }; auto resetPatchRead = [&]() { patchNameRead = lfoRead = characteristicRead = m1Read = c1Read = m2Read = c2Read = false; + newPatch = nullptr; }; auto readOpmOperator = [](SafeReader& reader, DivInstrumentFM::Operator& op) { op.ar = atoi(reader.readString_Token().c_str()); @@ -850,8 +853,6 @@ void DivEngine::loadOPM(SafeReader& reader, std::vector& ret, St op.ssgEnv = atoi(reader.readString_Token().c_str()); }; - DivInstrument* newPatch = nullptr; - try { reader.seek(0, SEEK_SET); while (!reader.isEOF()) { @@ -922,7 +923,7 @@ void DivEngine::loadOPM(SafeReader& reader, std::vector& ret, St if (completePatchRead()) { insList.push_back(newPatch); - newPatch = nullptr; + resetPatchRead(); ++readCount; } } diff --git a/src/gui/gui.cpp b/src/gui/gui.cpp index 009952a6..d3218b30 100644 --- a/src/gui/gui.cpp +++ b/src/gui/gui.cpp @@ -1290,9 +1290,9 @@ void FurnaceGUI::openFileDialog(FurnaceGUIFileDialogs type) { if (!dirExists(workingDirIns)) workingDirIns=getHomeDir(); hasOpened=fileDialog->openLoad( "Load Instrument", - {"compatible files", "*.fui *.dmp *.tfi *.vgi *.s3i *.sbi *.bnk *.ff", + {"compatible files", "*.fui *.dmp *.tfi *.vgi *.s3i *.sbi *.bnk *.ff *.opm", "all files", ".*"}, - "compatible files{.fui,.dmp,.tfi,.vgi,.s3i,.sbi,.bnk,.ff},.*", + "compatible files{.fui,.dmp,.tfi,.vgi,.s3i,.sbi,.bnk,.ff,.opm},.*", workingDirIns, dpiScale ); diff --git a/src/gui/settings.cpp b/src/gui/settings.cpp index f1cd27a3..f1565b8e 100644 --- a/src/gui/settings.cpp +++ b/src/gui/settings.cpp @@ -2489,6 +2489,7 @@ void FurnaceGUI::applyUISettings(bool updateFonts) { ImGuiFileDialog::Instance()->SetFileStyle(IGFD_FileStyleByExtension,".fti",uiColors[GUI_COLOR_FILE_INSTR],ICON_FA_FILE); ImGuiFileDialog::Instance()->SetFileStyle(IGFD_FileStyleByExtension,".bti",uiColors[GUI_COLOR_FILE_INSTR],ICON_FA_FILE); ImGuiFileDialog::Instance()->SetFileStyle(IGFD_FileStyleByExtension,".ff",uiColors[GUI_COLOR_FILE_INSTR],ICON_FA_FILE); + ImGuiFileDialog::Instance()->SetFileStyle(IGFD_FileStyleByExtension,".opm",uiColors[GUI_COLOR_FILE_INSTR],ICON_FA_FILE); if (updateFonts) { if (fileDialog!=NULL) delete fileDialog; From 8ad827478c20cd7858cb77f797231b802babf665 Mon Sep 17 00:00:00 2001 From: James Alan Nguyen Date: Sat, 23 Apr 2022 21:48:18 +1000 Subject: [PATCH 07/46] #79: OPM complete for now --- src/engine/fileOpsIns.cpp | 47 ++++++++++++++++++++++----------------- 1 file changed, 27 insertions(+), 20 deletions(-) diff --git a/src/engine/fileOpsIns.cpp b/src/engine/fileOpsIns.cpp index b74c7cba..a8b2b652 100644 --- a/src/engine/fileOpsIns.cpp +++ b/src/engine/fileOpsIns.cpp @@ -839,18 +839,23 @@ void DivEngine::loadOPM(SafeReader& reader, std::vector& ret, St patchNameRead = lfoRead = characteristicRead = m1Read = c1Read = m2Read = c2Read = false; newPatch = nullptr; }; - auto readOpmOperator = [](SafeReader& reader, DivInstrumentFM::Operator& op) { - op.ar = atoi(reader.readString_Token().c_str()); - op.dr = atoi(reader.readString_Token().c_str()); - op.d2r = atoi(reader.readString_Token().c_str()); - op.rr = atoi(reader.readString_Token().c_str()); - op.sl = atoi(reader.readString_Token().c_str()); - op.tl = atoi(reader.readString_Token().c_str()); - op.ksl = atoi(reader.readString_Token().c_str()); - op.mult = atoi(reader.readString_Token().c_str()); - op.dt = atoi(reader.readString_Token().c_str()); - op.dt2 = atoi(reader.readString_Token().c_str()); - op.ssgEnv = atoi(reader.readString_Token().c_str()); + auto readIntStrWithinRange = [](String& input, int limitLow, int limitHigh) { + int x = atoi(input.c_str()); + return (x>limitHigh) ? limitHigh : + (x& ret, St newPatch->fm.ops = 4; } - // Read each line for their respective params. They may not be written in the same order but they + // Read each line for their respective params. They may not be written in the same LINE order but they // must absolutely be properly grouped per patch! Line prefixes must be separated by a space! - // Patch number + name - // "@:123 Name of patch" if (token.length() >= 2) { if (token[0] == '@') { + // @:123 Name of patch + // Note: Fallback to bank filename and current patch number in _file_ order (not @n order) newPatch->name = reader.readString_Line(); + newPatch->name = newPatch->name.length() > 0 ? newPatch->name : fmt::format("{0}[{1}]", stripPath, readCount); + patchNameRead = true; } else if (token.compare(0,3,"CH:") == 0) { // CH: PAN FL CON AMS PMS SLOT NE reader.readString_Token(); // skip PAN - newPatch->fm.fb = atoi(reader.readString_Token().c_str()); - newPatch->fm.alg = atoi(reader.readString_Token().c_str()); - newPatch->fm.ams = atoi(reader.readString_Token().c_str()); - newPatch->fm.ams2 = atoi(reader.readString_Token().c_str()); + newPatch->fm.fb = readIntStrWithinRange(reader.readString_Token(), 0, 7); + newPatch->fm.alg = readIntStrWithinRange(reader.readString_Token(), 0, 7); + newPatch->fm.ams = readIntStrWithinRange(reader.readString_Token(), 0, 4); + newPatch->fm.fms = readIntStrWithinRange(reader.readString_Token(), 0, 7); reader.readString_Token(); // skip SLOT reader.readString_Token(); // skip NE characteristicRead = true; @@ -912,7 +919,7 @@ void DivEngine::loadOPM(SafeReader& reader, std::vector& ret, St m2Read = true; } else if (token.compare(0,4,"LFO:") == 0) { // LFO: LFRQ AMD PMD WF NFRQ - // Furnace patches do not store this as these are chip-global. + // Furnace patches do not store these as these are chip-global. reader.readString_Line(); lfoRead = true; } else { From 0f47a3ed7b049208183a6f84067b4a53898e3f47 Mon Sep 17 00:00:00 2001 From: James Alan Nguyen Date: Sat, 23 Apr 2022 23:40:58 +1000 Subject: [PATCH 08/46] Fix DT range --- src/engine/fileOpsIns.cpp | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/src/engine/fileOpsIns.cpp b/src/engine/fileOpsIns.cpp index a8b2b652..8b43f65e 100644 --- a/src/engine/fileOpsIns.cpp +++ b/src/engine/fileOpsIns.cpp @@ -640,6 +640,7 @@ void DivEngine::loadSBI(SafeReader& reader, std::vector& ret, St delete ins; } } + void DivEngine::loadBNK(SafeReader& reader, std::vector& ret, String& stripPath) { std::vector insList; std::vector instNames; @@ -853,7 +854,8 @@ void DivEngine::loadOPM(SafeReader& reader, std::vector& ret, St op.tl = readIntStrWithinRange(reader.readString_Token(), 0, 127); op.rs = readIntStrWithinRange(reader.readString_Token(), 0, 3);; op.mult = readIntStrWithinRange(reader.readString_Token(), 0, 15); - op.dt = readIntStrWithinRange(reader.readString_Token(), -3, 4); + op.dt = readIntStrWithinRange(reader.readString_Token(), 0, 7); + op.dt = (op.dt >= 4) ? (7 - op.dt) : (op.dt + 3); op.dt2 = readIntStrWithinRange(reader.readString_Token(), 0, 3); op.am = readIntStrWithinRange(reader.readString_Token(), 0, 1); }; @@ -881,7 +883,7 @@ void DivEngine::loadOPM(SafeReader& reader, std::vector& ret, St } // Read each line for their respective params. They may not be written in the same LINE order but they - // must absolutely be properly grouped per patch! Line prefixes must be separated by a space! + // must absolutely be properly grouped per patch! Line prefixes must be separated by a space! (see inline comments) if (token.length() >= 2) { if (token[0] == '@') { @@ -889,8 +891,8 @@ void DivEngine::loadOPM(SafeReader& reader, std::vector& ret, St // Note: Fallback to bank filename and current patch number in _file_ order (not @n order) newPatch->name = reader.readString_Line(); newPatch->name = newPatch->name.length() > 0 ? newPatch->name : fmt::format("{0}[{1}]", stripPath, readCount); - patchNameRead = true; + } else if (token.compare(0,3,"CH:") == 0) { // CH: PAN FL CON AMS PMS SLOT NE reader.readString_Token(); // skip PAN @@ -901,27 +903,33 @@ void DivEngine::loadOPM(SafeReader& reader, std::vector& ret, St reader.readString_Token(); // skip SLOT reader.readString_Token(); // skip NE characteristicRead = true; + } else if (token.compare(0,3,"C1:") == 0) { // C1: AR D1R D2R RR D1L TL KS MUL DT1 DT2 AMS-EN readOpmOperator(reader, newPatch->fm.op[2]); c1Read = true; + } else if (token.compare(0,3,"C2:") == 0) { // C2: AR D1R D2R RR D1L TL KS MUL DT1 DT2 AMS-EN readOpmOperator(reader, newPatch->fm.op[3]); c2Read = true; + } else if (token.compare(0,3,"M1:") == 0) { // M1: AR D1R D2R RR D1L TL KS MUL DT1 DT2 AMS-EN readOpmOperator(reader, newPatch->fm.op[0]); m1Read = true; + } else if (token.compare(0,3,"M2:") == 0) { // M2: AR D1R D2R RR D1L TL KS MUL DT1 DT2 AMS-EN readOpmOperator(reader, newPatch->fm.op[1]); m2Read = true; + } else if (token.compare(0,4,"LFO:") == 0) { // LFO: LFRQ AMD PMD WF NFRQ - // Furnace patches do not store these as these are chip-global. + // Furnace patches do not store these as they are chip-global. reader.readString_Line(); lfoRead = true; + } else { // other unsupported lines ignored. reader.readString_Line(); @@ -950,7 +958,9 @@ void DivEngine::loadOPM(SafeReader& reader, std::vector& ret, St for (int i = readCount; i >= 0; --i) { delete insList[i]; } - delete newPatch; + if (newPatch != nullptr) { + delete newPatch; + } } } From b8d9fab7455963f675f10ce223ad665a07841066 Mon Sep 17 00:00:00 2001 From: James Alan Nguyen Date: Sun, 24 Apr 2022 00:29:33 +1000 Subject: [PATCH 09/46] Start .y12 (Gens KMod/Kaneda) patch dump import --- src/engine/fileOpsIns.cpp | 48 ++++++++++++++++++++++++++++++++++++--- 1 file changed, 45 insertions(+), 3 deletions(-) diff --git a/src/engine/fileOpsIns.cpp b/src/engine/fileOpsIns.cpp index 8b43f65e..ddf9a1df 100644 --- a/src/engine/fileOpsIns.cpp +++ b/src/engine/fileOpsIns.cpp @@ -641,6 +641,45 @@ void DivEngine::loadSBI(SafeReader& reader, std::vector& ret, St } } +void DivEngine::loadY12(SafeReader& reader, std::vector& ret, String& stripPath) { + DivInstrument *ins = new DivInstrument; + + try { + reader.seek(0, SEEK_SET); + ins->type = DIV_INS_FM; + ins->fm.ops = 4; + ins->name = stripPath; + + for (int i = 0; i < 4; ++i) { + DivInstrumentFM::Operator& insOp = ins->fm.op[i]; + uint8_t tmp = reader.readC(); + insOp.mult = (tmp & 0xF); + insOp.dt = ((tmp >> 4) & 0x7); + insOp.tl = (reader.readC() & 0xF); + tmp = reader.readC(); + insOp.rs = ((tmp >> 6) & 0x3); + insOp.ar = (tmp & 0x1F); + tmp = reader.readC(); + insOp.dr = (tmp & 0x1F); + insOp.am = ((tmp >> 7) & 0x1); + insOp.d2r = (reader.readC() & 0x1F); + tmp = reader.readC(); + insOp.rr = (tmp & 0xF); + insOp.sl = ((tmp >> 4) & 0xF); + insOp.ssgEnv = reader.readC(); + reader.seek(9, SEEK_CUR); + } + ins->fm.alg = reader.readC(); + ins->fm.fb = reader.readC(); + reader.seek(14, SEEK_CUR); + ret.push_back(ins); + } catch (EndOfFileException& e) { + lastError = "premature end of file"; + logE("premature end of file"); + delete ins; + } +} + void DivEngine::loadBNK(SafeReader& reader, std::vector& ret, String& stripPath) { std::vector insList; std::vector instNames; @@ -1124,21 +1163,24 @@ std::vector DivEngine::instrumentFromFile(const char* path) { break; case DIV_INSFORMAT_BTI: // TODO break; - case DIV_INSFORMAT_OPM: - loadOPM(reader,ret,stripPath); - break; case DIV_INSFORMAT_S3I: loadS3I(reader,ret,stripPath); break; case DIV_INSFORMAT_SBI: loadSBI(reader,ret,stripPath); break; + case DIV_INSFORMAT_Y12: + loadY12(reader,ret,stripPath); + break; case DIV_INSFORMAT_BNK: loadBNK(reader, ret, stripPath); break; case DIV_INSFORMAT_FF: loadFF(reader,ret,stripPath); break; + case DIV_INSFORMAT_OPM: + loadOPM(reader, ret, stripPath); + break; } if (reader.tell() Date: Sun, 24 Apr 2022 00:45:19 +1000 Subject: [PATCH 10/46] #79: .y12 import done --- src/engine/fileOpsIns.cpp | 6 +++--- src/gui/gui.cpp | 4 ++-- src/gui/settings.cpp | 1 + 3 files changed, 6 insertions(+), 5 deletions(-) diff --git a/src/engine/fileOpsIns.cpp b/src/engine/fileOpsIns.cpp index ddf9a1df..602fbba5 100644 --- a/src/engine/fileOpsIns.cpp +++ b/src/engine/fileOpsIns.cpp @@ -650,12 +650,12 @@ void DivEngine::loadY12(SafeReader& reader, std::vector& ret, St ins->fm.ops = 4; ins->name = stripPath; - for (int i = 0; i < 4; ++i) { + for (int i : {0,1,2,3}) { DivInstrumentFM::Operator& insOp = ins->fm.op[i]; uint8_t tmp = reader.readC(); insOp.mult = (tmp & 0xF); insOp.dt = ((tmp >> 4) & 0x7); - insOp.tl = (reader.readC() & 0xF); + insOp.tl = (reader.readC() & 0x3F); tmp = reader.readC(); insOp.rs = ((tmp >> 6) & 0x3); insOp.ar = (tmp & 0x1F); @@ -671,7 +671,7 @@ void DivEngine::loadY12(SafeReader& reader, std::vector& ret, St } ins->fm.alg = reader.readC(); ins->fm.fb = reader.readC(); - reader.seek(14, SEEK_CUR); + reader.seek(62, SEEK_CUR); ret.push_back(ins); } catch (EndOfFileException& e) { lastError = "premature end of file"; diff --git a/src/gui/gui.cpp b/src/gui/gui.cpp index d3218b30..47b81f6e 100644 --- a/src/gui/gui.cpp +++ b/src/gui/gui.cpp @@ -1290,9 +1290,9 @@ void FurnaceGUI::openFileDialog(FurnaceGUIFileDialogs type) { if (!dirExists(workingDirIns)) workingDirIns=getHomeDir(); hasOpened=fileDialog->openLoad( "Load Instrument", - {"compatible files", "*.fui *.dmp *.tfi *.vgi *.s3i *.sbi *.bnk *.ff *.opm", + {"compatible files", "*.fui *.dmp *.tfi *.vgi *.s3i *.sbi *.y12 *.bnk *.ff *.opm", "all files", ".*"}, - "compatible files{.fui,.dmp,.tfi,.vgi,.s3i,.sbi,.bnk,.ff,.opm},.*", + "compatible files{.fui,.dmp,.tfi,.vgi,.s3i,.sbi,.y12,.bnk,.ff,.opm},.*", workingDirIns, dpiScale ); diff --git a/src/gui/settings.cpp b/src/gui/settings.cpp index f1565b8e..5ed9cf0a 100644 --- a/src/gui/settings.cpp +++ b/src/gui/settings.cpp @@ -2485,6 +2485,7 @@ void FurnaceGUI::applyUISettings(bool updateFonts) { ImGuiFileDialog::Instance()->SetFileStyle(IGFD_FileStyleByExtension,".vgi",uiColors[GUI_COLOR_FILE_INSTR],ICON_FA_FILE); ImGuiFileDialog::Instance()->SetFileStyle(IGFD_FileStyleByExtension,".s3i",uiColors[GUI_COLOR_FILE_INSTR],ICON_FA_FILE); ImGuiFileDialog::Instance()->SetFileStyle(IGFD_FileStyleByExtension,".sbi",uiColors[GUI_COLOR_FILE_INSTR],ICON_FA_FILE); + ImGuiFileDialog::Instance()->SetFileStyle(IGFD_FileStyleByExtension,".y12",uiColors[GUI_COLOR_FILE_INSTR],ICON_FA_FILE); ImGuiFileDialog::Instance()->SetFileStyle(IGFD_FileStyleByExtension,".bnk",uiColors[GUI_COLOR_FILE_INSTR],ICON_FA_FILE); ImGuiFileDialog::Instance()->SetFileStyle(IGFD_FileStyleByExtension,".fti",uiColors[GUI_COLOR_FILE_INSTR],ICON_FA_FILE); ImGuiFileDialog::Instance()->SetFileStyle(IGFD_FileStyleByExtension,".bti",uiColors[GUI_COLOR_FILE_INSTR],ICON_FA_FILE); From 6bd199923f042bb1d9882fec6869eab7bf2112ae Mon Sep 17 00:00:00 2001 From: James Alan Nguyen Date: Sun, 24 Apr 2022 02:37:25 +1000 Subject: [PATCH 11/46] OPLI progress... still figuring out correct readings --- src/engine/engine.h | 3 - src/engine/fileOpsIns.cpp | 126 +++++++++++++++++++++++++++++++++----- src/engine/safeReader.cpp | 2 +- src/gui/settings.cpp | 2 + 4 files changed, 114 insertions(+), 19 deletions(-) diff --git a/src/engine/engine.h b/src/engine/engine.h index c8afb86a..5e840362 100644 --- a/src/engine/engine.h +++ b/src/engine/engine.h @@ -291,13 +291,10 @@ class DivEngine { void loadSBI(SafeReader& reader, std::vector& ret, String& stripPath); void loadOPLI(SafeReader& reader, std::vector& ret, String& stripPath); void loadOPNI(SafeReader& reader, std::vector& ret, String& stripPath); - void loadPAT(SafeReader& reader, std::vector& ret, String& stripPath); void loadY12(SafeReader& reader, std::vector& ret, String& stripPath); void loadBNK(SafeReader& reader, std::vector& ret, String& stripPath); void loadOPM(SafeReader& reader, std::vector& ret, String& stripPath); void loadFF(SafeReader& reader, std::vector& ret, String& stripPath); - void loadWOPL(SafeReader& reader, std::vector& ret, String& stripPath); - void loadWOPN(SafeReader& reader, std::vector& ret, String& stripPath); bool initAudioBackend(); bool deinitAudioBackend(); diff --git a/src/engine/fileOpsIns.cpp b/src/engine/fileOpsIns.cpp index 602fbba5..87233ef6 100644 --- a/src/engine/fileOpsIns.cpp +++ b/src/engine/fileOpsIns.cpp @@ -30,15 +30,12 @@ enum DivInsFormats { DIV_INSFORMAT_BTI, DIV_INSFORMAT_S3I, DIV_INSFORMAT_SBI, - DIV_INSFORMAT_PAT, DIV_INSFORMAT_Y12, DIV_INSFORMAT_OPLI, DIV_INSFORMAT_OPNI, DIV_INSFORMAT_BNK, DIV_INSFORMAT_OPM, DIV_INSFORMAT_FF, - DIV_INSFORMAT_WOPL, - DIV_INSFORMAT_WOPN, }; // Patch data structures @@ -641,6 +638,105 @@ void DivEngine::loadSBI(SafeReader& reader, std::vector& ret, St } } +void DivEngine::loadOPLI(SafeReader& reader, std::vector& ret, String& stripPath) { + DivInstrument* ins = new DivInstrument; + + try { + reader.seek(0, SEEK_SET); + String header = reader.readString(11); + if (header.compare("WOPL3-INST") == 0) { + uint16_t version = reader.readS(); + bool isPerc = (reader.readC() == 1); + + ins->type = DIV_INS_OPL; + String insName = reader.readString(32); + insName = (insName.length() > 0) ? insName : stripPath; + ins->name = insName; + reader.seek(7, SEEK_CUR); // skip MIDI params + uint8_t instTypeFlags = reader.readC(); // [0EEEDCBA] - see WOPL/OPLI spec + + bool is_2op = ((instTypeFlags & 0x1) > 0); + bool is_4op = (((instTypeFlags>>1) & 0x1) > 0); + bool is_2x2op = (((instTypeFlags>>2) & 0x1) > 0); + bool is_rhythm = (((instTypeFlags>>4) & 0x7) > 0); + + auto readOpliOp = [](SafeReader& reader, DivInstrumentFM::Operator op) { + uint8_t characteristics = reader.readC(); + uint8_t keyScaleLevel = reader.readC(); + uint8_t attackDecay = reader.readC(); + uint8_t sustainRelease = reader.readC(); + uint8_t waveSelect = reader.readC(); + + op.mult = characteristics & 0xF; + op.ksr = ((characteristics >> 4) & 0x1); + op.sus = ((characteristics >> 5) & 0x1); + op.vib = ((characteristics >> 6) & 0x1); + op.am = ((characteristics >> 7) & 0x1); + op.tl = keyScaleLevel & 0x3F; + op.ksl = ((keyScaleLevel >> 6) & 0x3); + op.ar = ((attackDecay >> 4) & 0xF); + op.dr = attackDecay & 0xF; + op.rr = sustainRelease & 0xF; + op.sl = ((sustainRelease >> 4) & 0xF); + op.ws = waveSelect; + }; + + uint8_t feedConnect = reader.readC(); + uint8_t feedConnect2nd = reader.readC(); + + ins->fm.alg = (feedConnect & 0x1); + ins->fm.fb = ((feedConnect >> 1) & 0xF); + + if (is_4op) { + ins->fm.ops = 4; + ins->fm.alg = (feedConnect & 0x1) | ((feedConnect2nd & 0x1) << 1); + for (int i : {0,2,1,3}) { + readOpliOp(reader, ins->fm.op[i]); + } + } else { + ins->fm.ops = 2; + for (int i = 0; i < 2; ++i) { + readOpliOp(reader, ins->fm.op[i]); + } + if (is_rhythm) { + ins->fm.opllPreset = (uint8_t)(1<<4); + + } else if (is_2x2op) { + ins->name = fmt::format("{0} (1)", insName); + ret.push_back(ins); + + ins = new DivInstrument; + ins->type = DIV_INS_OPL; + ins->name = fmt::format("{0} (2)", insName); + for (int i = 0; i < 2; ++i) { + readOpliOp(reader, ins->fm.op[i]); + } + } + } + + // Skip rest of file + reader.seek(0, SEEK_END); + ret.push_back(ins); + } + } catch (EndOfFileException& e) { + lastError = "premature end of file"; + logE("premature end of file"); + delete ins; + } +} + +void DivEngine::loadOPNI(SafeReader& reader, std::vector& ret, String& stripPath) { + DivInstrument* ins = new DivInstrument; + + try { + reader.seek(0, SEEK_SET); + } catch (EndOfFileException& e) { + lastError = "premature end of file"; + logE("premature end of file"); + delete ins; + } +} + void DivEngine::loadY12(SafeReader& reader, std::vector& ret, String& stripPath) { DivInstrument *ins = new DivInstrument; @@ -653,18 +749,18 @@ void DivEngine::loadY12(SafeReader& reader, std::vector& ret, St for (int i : {0,1,2,3}) { DivInstrumentFM::Operator& insOp = ins->fm.op[i]; uint8_t tmp = reader.readC(); - insOp.mult = (tmp & 0xF); + insOp.mult = tmp & 0xF; insOp.dt = ((tmp >> 4) & 0x7); insOp.tl = (reader.readC() & 0x3F); tmp = reader.readC(); insOp.rs = ((tmp >> 6) & 0x3); - insOp.ar = (tmp & 0x1F); + insOp.ar = tmp & 0x1F; tmp = reader.readC(); - insOp.dr = (tmp & 0x1F); + insOp.dr = tmp & 0x1F; insOp.am = ((tmp >> 7) & 0x1); insOp.d2r = (reader.readC() & 0x1F); tmp = reader.readC(); - insOp.rr = (tmp & 0xF); + insOp.rr = tmp & 0xF; insOp.sl = ((tmp >> 4) & 0xF); insOp.ssgEnv = reader.readC(); reader.seek(9, SEEK_CUR); @@ -884,7 +980,7 @@ void DivEngine::loadOPM(SafeReader& reader, std::vector& ret, St return (x>limitHigh) ? limitHigh : (x DivEngine::instrumentFromFile(const char* path) { } else if (extS==String(".opli")) { format=DIV_INSFORMAT_OPLI; } else if (extS==String(".opni")) { - format=DIV_INSFORMAT_OPNI; - } else if (extS==String(".pat")) { - format=DIV_INSFORMAT_PAT; + format=DIV_INSFORMAT_OPNI;; } else if (extS==String(".y12")) { format=DIV_INSFORMAT_Y12; } else if (extS==String(".bnk")) { @@ -1141,10 +1235,6 @@ std::vector DivEngine::instrumentFromFile(const char* path) { format=DIV_INSFORMAT_OPM; } else if (extS==String(".ff")) { format=DIV_INSFORMAT_FF; - } else if (extS==String(".wopl")) { - format=DIV_INSFORMAT_WOPL; - } else if (extS==String(".wopn")) { - format=DIV_INSFORMAT_WOPN; } } @@ -1169,6 +1259,12 @@ std::vector DivEngine::instrumentFromFile(const char* path) { case DIV_INSFORMAT_SBI: loadSBI(reader,ret,stripPath); break; + case DIV_INSFORMAT_OPLI: + loadOPLI(reader,ret,stripPath); + break; + case DIV_INSFORMAT_OPNI: + loadOPNI(reader, ret, stripPath); + break; case DIV_INSFORMAT_Y12: loadY12(reader,ret,stripPath); break; diff --git a/src/engine/safeReader.cpp b/src/engine/safeReader.cpp index e46827c6..97031b2b 100644 --- a/src/engine/safeReader.cpp +++ b/src/engine/safeReader.cpp @@ -139,7 +139,7 @@ double SafeReader::readD() { } String SafeReader::readString(size_t stlen) { - String ret(stlen, ' '); + String ret; #ifdef READ_DEBUG logD("SR: reading string len %d at %x",stlen,curSeek); #endif diff --git a/src/gui/settings.cpp b/src/gui/settings.cpp index 5ed9cf0a..c7a23511 100644 --- a/src/gui/settings.cpp +++ b/src/gui/settings.cpp @@ -2485,6 +2485,8 @@ void FurnaceGUI::applyUISettings(bool updateFonts) { ImGuiFileDialog::Instance()->SetFileStyle(IGFD_FileStyleByExtension,".vgi",uiColors[GUI_COLOR_FILE_INSTR],ICON_FA_FILE); ImGuiFileDialog::Instance()->SetFileStyle(IGFD_FileStyleByExtension,".s3i",uiColors[GUI_COLOR_FILE_INSTR],ICON_FA_FILE); ImGuiFileDialog::Instance()->SetFileStyle(IGFD_FileStyleByExtension,".sbi",uiColors[GUI_COLOR_FILE_INSTR],ICON_FA_FILE); + ImGuiFileDialog::Instance()->SetFileStyle(IGFD_FileStyleByExtension,".opli",uiColors[GUI_COLOR_FILE_INSTR],ICON_FA_FILE); + ImGuiFileDialog::Instance()->SetFileStyle(IGFD_FileStyleByExtension,".opni",uiColors[GUI_COLOR_FILE_INSTR],ICON_FA_FILE); ImGuiFileDialog::Instance()->SetFileStyle(IGFD_FileStyleByExtension,".y12",uiColors[GUI_COLOR_FILE_INSTR],ICON_FA_FILE); ImGuiFileDialog::Instance()->SetFileStyle(IGFD_FileStyleByExtension,".bnk",uiColors[GUI_COLOR_FILE_INSTR],ICON_FA_FILE); ImGuiFileDialog::Instance()->SetFileStyle(IGFD_FileStyleByExtension,".fti",uiColors[GUI_COLOR_FILE_INSTR],ICON_FA_FILE); From bb0253d82fc3a8f90568adead3cd93625e747d95 Mon Sep 17 00:00:00 2001 From: James Alan Nguyen Date: Sun, 24 Apr 2022 03:15:20 +1000 Subject: [PATCH 12/46] Urgh why are your operators BACKWARDS FFS --- src/engine/fileOpsIns.cpp | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/engine/fileOpsIns.cpp b/src/engine/fileOpsIns.cpp index 87233ef6..d864bda5 100644 --- a/src/engine/fileOpsIns.cpp +++ b/src/engine/fileOpsIns.cpp @@ -655,12 +655,12 @@ void DivEngine::loadOPLI(SafeReader& reader, std::vector& ret, S reader.seek(7, SEEK_CUR); // skip MIDI params uint8_t instTypeFlags = reader.readC(); // [0EEEDCBA] - see WOPL/OPLI spec - bool is_2op = ((instTypeFlags & 0x1) > 0); - bool is_4op = (((instTypeFlags>>1) & 0x1) > 0); - bool is_2x2op = (((instTypeFlags>>2) & 0x1) > 0); + bool is_2op = ((instTypeFlags & 0x3) == 0); + bool is_4op = ((instTypeFlags & 0x1) == 1); + bool is_2x2op = (((instTypeFlags>>1) & 0x1) == 1); bool is_rhythm = (((instTypeFlags>>4) & 0x7) > 0); - auto readOpliOp = [](SafeReader& reader, DivInstrumentFM::Operator op) { + auto readOpliOp = [](SafeReader& reader, DivInstrumentFM::Operator& op) { uint8_t characteristics = reader.readC(); uint8_t keyScaleLevel = reader.readC(); uint8_t attackDecay = reader.readC(); @@ -687,15 +687,15 @@ void DivEngine::loadOPLI(SafeReader& reader, std::vector& ret, S ins->fm.alg = (feedConnect & 0x1); ins->fm.fb = ((feedConnect >> 1) & 0xF); - if (is_4op) { + if (is_4op && !is_2x2op) { ins->fm.ops = 4; ins->fm.alg = (feedConnect & 0x1) | ((feedConnect2nd & 0x1) << 1); - for (int i : {0,2,1,3}) { + for (int i : {2,0,3,1}) { readOpliOp(reader, ins->fm.op[i]); } } else { ins->fm.ops = 2; - for (int i = 0; i < 2; ++i) { + for (int i : {1,0}) { readOpliOp(reader, ins->fm.op[i]); } if (is_rhythm) { @@ -708,7 +708,7 @@ void DivEngine::loadOPLI(SafeReader& reader, std::vector& ret, S ins = new DivInstrument; ins->type = DIV_INS_OPL; ins->name = fmt::format("{0} (2)", insName); - for (int i = 0; i < 2; ++i) { + for (int i : {1,0}) { readOpliOp(reader, ins->fm.op[i]); } } From 3865e3eac60fe6d5acc03afda5af6a544ca24b52 Mon Sep 17 00:00:00 2001 From: James Alan Nguyen Date: Sun, 24 Apr 2022 03:49:01 +1000 Subject: [PATCH 13/46] #79: OPNI support added...... --- src/engine/fileOpsIns.cpp | 52 +++++++++++++++++++++++++++++++++++++++ src/gui/gui.cpp | 4 +-- 2 files changed, 54 insertions(+), 2 deletions(-) diff --git a/src/engine/fileOpsIns.cpp b/src/engine/fileOpsIns.cpp index d864bda5..d541a9df 100644 --- a/src/engine/fileOpsIns.cpp +++ b/src/engine/fileOpsIns.cpp @@ -702,6 +702,7 @@ void DivEngine::loadOPLI(SafeReader& reader, std::vector& ret, S ins->fm.opllPreset = (uint8_t)(1<<4); } else if (is_2x2op) { + // Note: Pair detuning offset not mappable. Use E5xx effect :P ins->name = fmt::format("{0} (1)", insName); ret.push_back(ins); @@ -730,6 +731,57 @@ void DivEngine::loadOPNI(SafeReader& reader, std::vector& ret, S try { reader.seek(0, SEEK_SET); + + String header = reader.readString(11); + if (header.compare("WOPN2-INST") == 0 || header.compare("WOPN2-IN2T") == 0) { // omfg + uint16_t version = reader.readS(); + if (!(version >= 2) || version > 0xF) { + // version 1 doesn't have a version field........ + reader.seek(-2, SEEK_CUR); + } + + bool is_perc = (reader.readC() == 0x1); + ins->type = DIV_INS_FM; + ins->fm.ops = 4; + + String insName = reader.readString(32); + ins->name = (insName.length() > 0) ? insName : stripPath; + reader.seek(3, SEEK_CUR); // skip MIDI params + uint8_t feedAlgo = reader.readC(); + ins->fm.alg = (feedAlgo & 0x7); + ins->fm.fb = ((feedAlgo>>3) & 0x7); + reader.readC(); // Skip global bank flags - see WOPN/OPNI spec + + auto readOpniOp = [](SafeReader& reader, DivInstrumentFM::Operator& op) { + uint8_t dtMul = reader.readC(); + uint8_t totalLevel = reader.readC(); + uint8_t arRateScale = reader.readC(); + uint8_t drAmpEnable = reader.readC(); + uint8_t d2r = reader.readC(); + uint8_t susRelease = reader.readC(); + uint8_t ssgEg = reader.readC(); + + op.mult = dtMul & 0xF; + op.dt = ((dtMul >> 4) & 0x7); + op.tl = totalLevel & 0x3F; + op.rs = ((arRateScale >> 6) & 0x3); + op.ar = arRateScale & 0x1F; + op.dr = drAmpEnable & 0x1F; + op.am = ((drAmpEnable >> 7) & 0x1); + op.d2r = d2r & 0x1F; + op.rr = susRelease & 0xF; + op.sl = ((susRelease >> 4) & 0xF); + op.ssgEnv = ssgEg; + }; + + for (int i : {0,1,2,3}) { + readOpniOp(reader, ins->fm.op[i]); + } + + // Skip rest of file + reader.seek(0, SEEK_END); + ret.push_back(ins); + } } catch (EndOfFileException& e) { lastError = "premature end of file"; logE("premature end of file"); diff --git a/src/gui/gui.cpp b/src/gui/gui.cpp index 47b81f6e..4d6f1089 100644 --- a/src/gui/gui.cpp +++ b/src/gui/gui.cpp @@ -1290,9 +1290,9 @@ void FurnaceGUI::openFileDialog(FurnaceGUIFileDialogs type) { if (!dirExists(workingDirIns)) workingDirIns=getHomeDir(); hasOpened=fileDialog->openLoad( "Load Instrument", - {"compatible files", "*.fui *.dmp *.tfi *.vgi *.s3i *.sbi *.y12 *.bnk *.ff *.opm", + {"compatible files", "*.fui *.dmp *.tfi *.vgi *.s3i *.sbi *.opli *.opni *.y12 *.bnk *.ff *.opm", "all files", ".*"}, - "compatible files{.fui,.dmp,.tfi,.vgi,.s3i,.sbi,.y12,.bnk,.ff,.opm},.*", + "compatible files{.fui,.dmp,.tfi,.vgi,.s3i,.sbi,.opli,.opni,.y12,.bnk,.ff,.opm},.*", workingDirIns, dpiScale ); From 6638941c9dfd0c3c98c15b971dfa2377cbfc8ce8 Mon Sep 17 00:00:00 2001 From: James Alan Nguyen Date: Sun, 24 Apr 2022 04:34:32 +1000 Subject: [PATCH 14/46] Address unused stuff and CI fix --- src/engine/engine.h | 1 - src/engine/fileOpsIns.cpp | 12 +++++++----- src/engine/safeReader.cpp | 5 +++++ src/engine/safeReader.h | 3 ++- 4 files changed, 14 insertions(+), 7 deletions(-) diff --git a/src/engine/engine.h b/src/engine/engine.h index 5e840362..b76e271e 100644 --- a/src/engine/engine.h +++ b/src/engine/engine.h @@ -31,7 +31,6 @@ #include #include #include -#include #define addWarning(x) \ if (warnings.empty()) { \ diff --git a/src/engine/fileOpsIns.cpp b/src/engine/fileOpsIns.cpp index d541a9df..d913d96d 100644 --- a/src/engine/fileOpsIns.cpp +++ b/src/engine/fileOpsIns.cpp @@ -646,7 +646,11 @@ void DivEngine::loadOPLI(SafeReader& reader, std::vector& ret, S String header = reader.readString(11); if (header.compare("WOPL3-INST") == 0) { uint16_t version = reader.readS(); - bool isPerc = (reader.readC() == 1); + if (version > 3) { + logW("Unknown OPLI version."); + } + + reader.readC(); // skip isPerc field ins->type = DIV_INS_OPL; String insName = reader.readString(32); @@ -655,7 +659,6 @@ void DivEngine::loadOPLI(SafeReader& reader, std::vector& ret, S reader.seek(7, SEEK_CUR); // skip MIDI params uint8_t instTypeFlags = reader.readC(); // [0EEEDCBA] - see WOPL/OPLI spec - bool is_2op = ((instTypeFlags & 0x3) == 0); bool is_4op = ((instTypeFlags & 0x1) == 1); bool is_2x2op = (((instTypeFlags>>1) & 0x1) == 1); bool is_rhythm = (((instTypeFlags>>4) & 0x7) > 0); @@ -740,7 +743,7 @@ void DivEngine::loadOPNI(SafeReader& reader, std::vector& ret, S reader.seek(-2, SEEK_CUR); } - bool is_perc = (reader.readC() == 0x1); + reader.readC(); // skip isPerc ins->type = DIV_INS_FM; ins->fm.ops = 4; @@ -1006,7 +1009,6 @@ void DivEngine::loadFF(SafeReader& reader, std::vector& ret, Str void DivEngine::loadOPM(SafeReader& reader, std::vector& ret, String& stripPath) { std::vector insList; - std::stringstream ss; int readCount = 0; @@ -1027,7 +1029,7 @@ void DivEngine::loadOPM(SafeReader& reader, std::vector& ret, St patchNameRead = lfoRead = characteristicRead = m1Read = c1Read = m2Read = c2Read = false; newPatch = nullptr; }; - auto readIntStrWithinRange = [](String& input, int limitLow, int limitHigh) { + auto readIntStrWithinRange = [](String&& input, int limitLow, int limitHigh) { int x = atoi(input.c_str()); return (x>limitHigh) ? limitHigh : (x= len; diff --git a/src/engine/safeReader.h b/src/engine/safeReader.h index 9fd0282b..4216a596 100644 --- a/src/engine/safeReader.h +++ b/src/engine/safeReader.h @@ -67,7 +67,8 @@ class SafeReader { String readString(); String readString(size_t len); String readString_Line(); - String readString_Token(unsigned char delim=' '); + String readString_Token(unsigned char delim); + String readString_Token(); bool isEOF(); SafeReader(void* b, size_t l): From 6493a0481c80bb3cd91e6121ba2a78375a1a3e3d Mon Sep 17 00:00:00 2001 From: tildearrow Date: Sat, 23 Apr 2022 13:39:00 -0500 Subject: [PATCH 15/46] i hate this --- src/gui/gui.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/src/gui/gui.cpp b/src/gui/gui.cpp index 009952a6..19f2e77e 100644 --- a/src/gui/gui.cpp +++ b/src/gui/gui.cpp @@ -17,7 +17,6 @@ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ -#include #define _USE_MATH_DEFINES #include "gui.h" #include "util.h" From 0e3dbdc1b28cfff246ed438315b17dc3716bcb9b Mon Sep 17 00:00:00 2001 From: tildearrow Date: Sat, 23 Apr 2022 16:52:31 -0500 Subject: [PATCH 16/46] this might fix it --- src/engine/platform/genesisext.cpp | 24 ++++++++++++------------ src/engine/platform/genesisext.h | 19 +++++++++++++++++-- 2 files changed, 29 insertions(+), 14 deletions(-) diff --git a/src/engine/platform/genesisext.cpp b/src/engine/platform/genesisext.cpp index d8ae8b38..eefec5d8 100644 --- a/src/engine/platform/genesisext.cpp +++ b/src/engine/platform/genesisext.cpp @@ -130,6 +130,9 @@ int DivPlatformGenesisExt::dispatch(DivCommand c) { int destFreq=NOTE_FNUM_BLOCK(c.value2,11); int newFreq; bool return2=false; + if (opChan[ch].portaPause) { + opChan[ch].baseFreq=opChan[ch].portaPauseFreq; + } if (destFreq>opChan[ch].baseFreq) { newFreq=opChan[ch].baseFreq+c.value; if (newFreq>=destFreq) { @@ -144,23 +147,20 @@ int DivPlatformGenesisExt::dispatch(DivCommand c) { } } // what the heck! - // TODO: rework! if (!opChan[ch].portaPause) { if ((newFreq&0x7ff)>1288) { - newFreq=(644)|((newFreq+0x800)&0xf800); + opChan[ch].portaPauseFreq=(644)|((newFreq+0x800)&0xf800); opChan[ch].portaPause=true; - return2=false; - } else if ((newFreq&0x7ff)<644) { - newFreq=(1287)|((newFreq-0x800)&0xf800); - opChan[ch].portaPause=true; - return2=false; - } else { - opChan[ch].freqChanged=true; + break; + } + if ((newFreq&0x7ff)<644) { + opChan[ch].portaPauseFreq=newFreq=(1287)|((newFreq-0x800)&0xf800); + opChan[ch].portaPause=true; + break; } - } else { - opChan[ch].portaPause=false; - opChan[ch].freqChanged=true; } + opChan[ch].portaPause=false; + opChan[ch].freqChanged=true; opChan[ch].baseFreq=newFreq; if (return2) return 2; break; diff --git a/src/engine/platform/genesisext.h b/src/engine/platform/genesisext.h index fc9eed89..4cdf9fc6 100644 --- a/src/engine/platform/genesisext.h +++ b/src/engine/platform/genesisext.h @@ -25,13 +25,28 @@ class DivPlatformGenesisExt: public DivPlatformGenesis { struct OpChannel { DivMacroInt std; unsigned char freqH, freqL; - int freq, baseFreq, pitch; + int freq, baseFreq, pitch, portaPauseFreq; unsigned char ins; signed char konCycles; bool active, insChanged, freqChanged, keyOn, keyOff, portaPause; int vol; unsigned char pan; - OpChannel(): freqH(0), freqL(0), freq(0), baseFreq(0), pitch(0), ins(-1), active(false), insChanged(true), freqChanged(false), keyOn(false), keyOff(false), portaPause(false), vol(0), pan(3) {} + OpChannel(): + freqH(0), + freqL(0), + freq(0), + baseFreq(0), + pitch(0), + portaPauseFreq(0), + ins(-1), + active(false), + insChanged(true), + freqChanged(false), + keyOn(false), + keyOff(false), + portaPause(false), + vol(0), + pan(3) {} }; OpChannel opChan[4]; bool isOpMuted[4]; From a7828d6ad63500ef44ba0ec2683540394982f506 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Sat, 23 Apr 2022 16:54:49 -0500 Subject: [PATCH 17/46] update contributing guidelines --- CONTRIBUTING.md | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index ad608e4c..eb25675d 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -49,12 +49,27 @@ the coding style is described here: - `size_t` are 32-bit or 64-bit, depending on architecture. - in float/double operations, always use decimal and `f` if single-precision. - e.g. `1.0f` or `1.0` instead of `1`. +- prefer `NULL` over `nullptr` or any other proprietary null. - don't use `auto` unless needed. +- use `String` for `std::string` (this is typedef'd in ta-utils.h). +- prefer using operator for String (std::string) comparisons (a==""). some files (particularly the ones in `src/engine/platform/sound` and `extern/`) don't follow this style. you don't have to follow this style. I will fix it after I accept your contribution. +additional guidelines: + +- in general **strongly** avoid breaking compatibility. + - do not touch loadFur/saveFur unless you know what you're doing! + - new fields must be at the end of each block to ensure forward compatibility + - likewise, the instrument read/write functions in DivInstrument have to be handled carefully + - any change to the format requires a version bump (see `src/engine/engine.h`). + - do not bump the version number under any circumstances! + - if you are making major changes to the playback routine, make sure to test with older songs to ensure nothing breaks. + - I will run a test suite to make sure this is the case. + - if something breaks, you might want to add a compatibility flag (this requires changing the format though). + ## Demo Songs just put your demo song in `demos/`! From dd9bb8327a0ab8588d75dbc098810753f75f9958 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Sat, 23 Apr 2022 18:02:43 -0500 Subject: [PATCH 18/46] and this finally does it the last thing to do is a compatibility flag --- src/engine/platform/genesis.cpp | 19 +++++++++---------- src/engine/platform/genesis.h | 3 ++- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/src/engine/platform/genesis.cpp b/src/engine/platform/genesis.cpp index 43aae3f1..61fffcb8 100644 --- a/src/engine/platform/genesis.cpp +++ b/src/engine/platform/genesis.cpp @@ -618,6 +618,9 @@ int DivPlatformGenesis::dispatch(DivCommand c) { int destFreq=NOTE_FNUM_BLOCK(c.value2,11); int newFreq; bool return2=false; + if (chan[c.chan].portaPause) { + chan[c.chan].baseFreq=chan[c.chan].portaPauseFreq; + } if (destFreq>chan[c.chan].baseFreq) { newFreq=chan[c.chan].baseFreq+c.value; if (newFreq>=destFreq) { @@ -634,23 +637,19 @@ int DivPlatformGenesis::dispatch(DivCommand c) { // check for octave boundary // what the heck! if (!chan[c.chan].portaPause) { - chan[c.chan].freqChanged=true; if ((newFreq&0x7ff)>1288) { - newFreq=(644)|((newFreq+0x800)&0xf800); + chan[c.chan].portaPauseFreq=(644)|((newFreq+0x800)&0xf800); chan[c.chan].portaPause=true; - chan[c.chan].freqChanged=false; - return2=false; + break; } if ((newFreq&0x7ff)<644) { - newFreq=(1287)|((newFreq-0x800)&0xf800); + chan[c.chan].portaPauseFreq=newFreq=(1287)|((newFreq-0x800)&0xf800); chan[c.chan].portaPause=true; - chan[c.chan].freqChanged=false; - return2=false; + break; } - } else { - chan[c.chan].portaPause=false; - chan[c.chan].freqChanged=true; } + chan[c.chan].portaPause=false; + chan[c.chan].freqChanged=true; chan[c.chan].baseFreq=newFreq; if (return2) { chan[c.chan].inPorta=false; diff --git a/src/engine/platform/genesis.h b/src/engine/platform/genesis.h index ce52c6df..b9430306 100644 --- a/src/engine/platform/genesis.h +++ b/src/engine/platform/genesis.h @@ -36,7 +36,7 @@ class DivPlatformGenesis: public DivDispatch { DivInstrumentFM state; DivMacroInt std; unsigned char freqH, freqL; - int freq, baseFreq, pitch, note; + int freq, baseFreq, pitch, portaPauseFreq, note; unsigned char ins; bool active, insChanged, freqChanged, keyOn, keyOff, portaPause, furnaceDac, inPorta, hardReset; int vol, outVol; @@ -47,6 +47,7 @@ class DivPlatformGenesis: public DivDispatch { freq(0), baseFreq(0), pitch(0), + portaPauseFreq(0), note(0), ins(-1), active(false), From 18f7dcc0b0fff37545c51f98080147aa4466a752 Mon Sep 17 00:00:00 2001 From: James Alan Nguyen Date: Sun, 24 Apr 2022 12:31:37 +1000 Subject: [PATCH 19/46] Address review comments --- src/engine/fileOpsIns.cpp | 131 +++++++++++++++++++++----------------- src/engine/safeReader.cpp | 13 ++-- src/engine/safeReader.h | 8 +-- 3 files changed, 82 insertions(+), 70 deletions(-) diff --git a/src/engine/fileOpsIns.cpp b/src/engine/fileOpsIns.cpp index d913d96d..796fd3ea 100644 --- a/src/engine/fileOpsIns.cpp +++ b/src/engine/fileOpsIns.cpp @@ -498,11 +498,11 @@ void DivEngine::loadS3I(SafeReader& reader, std::vector& ret, St // Skip more stuff we don't need reader.seek(21, SEEK_CUR); } else { - lastError = "S3I PCM samples currently not supported."; + lastError="S3I PCM samples currently not supported."; logE("S3I PCM samples currently not supported."); } ins->name = reader.readString(28); - ins->name = (ins->name.length() == 0) ? stripPath : ins->name; + ins->name = (ins->name.size() == 0) ? stripPath : ins->name; int s3i_signature = reader.readI(); @@ -511,7 +511,7 @@ void DivEngine::loadS3I(SafeReader& reader, std::vector& ret, St logW("S3I signature invalid."); }; } catch (EndOfFileException& e) { - lastError = "premature end of file"; + lastError="premature end of file"; logE("premature end of file"); delete ins; return; @@ -534,7 +534,7 @@ void DivEngine::loadSBI(SafeReader& reader, std::vector& ret, St // 32-byte null terminated instrument name String patchName = reader.readString(32); - patchName = (patchName.length() == 0) ? stripPath : patchName; + patchName = (patchName.size() == 0) ? stripPath : patchName; auto writeOp = [](sbi_t& sbi, DivInstrumentFM::Operator& opM, DivInstrumentFM::Operator& opC) { opM.mult = sbi.Mcharacteristics & 0xF; @@ -608,7 +608,7 @@ void DivEngine::loadSBI(SafeReader& reader, std::vector& ret, St if (is_6op) { // Freq Monster 801 6op SBIs use a 4+2op layout // Save the 4op portion before reading the 2op part - ins->name = fmt::format("{0} (4op)", ins->name); + ins->name = fmt::sprintf("%s (4op)", ins->name); ret.push_back(ins); readSbiOpData(sbi_op12, reader); @@ -618,7 +618,7 @@ void DivEngine::loadSBI(SafeReader& reader, std::vector& ret, St DivInstrumentFM::Operator& opC6 = ins->fm.op[1]; ins->type = DIV_INS_OPL; ins->fm.ops = 2; - ins->name = fmt::format("{0} (2op)", patchName); + ins->name = fmt::sprintf("%s (2op)", patchName); writeOp(sbi_op12, opM6, opC6); ins->fm.alg = (sbi_op12.FeedConnect & 0x1); ins->fm.fb = ((sbi_op12.FeedConnect >> 1) & 0x7); @@ -632,7 +632,7 @@ void DivEngine::loadSBI(SafeReader& reader, std::vector& ret, St } } catch (EndOfFileException& e) { - lastError = "premature end of file"; + lastError="premature end of file"; logE("premature end of file"); delete ins; } @@ -644,7 +644,7 @@ void DivEngine::loadOPLI(SafeReader& reader, std::vector& ret, S try { reader.seek(0, SEEK_SET); String header = reader.readString(11); - if (header.compare("WOPL3-INST") == 0) { + if (header == "WOPL3-INST") { uint16_t version = reader.readS(); if (version > 3) { logW("Unknown OPLI version."); @@ -654,7 +654,7 @@ void DivEngine::loadOPLI(SafeReader& reader, std::vector& ret, S ins->type = DIV_INS_OPL; String insName = reader.readString(32); - insName = (insName.length() > 0) ? insName : stripPath; + insName = (insName.size() > 0) ? insName : stripPath; ins->name = insName; reader.seek(7, SEEK_CUR); // skip MIDI params uint8_t instTypeFlags = reader.readC(); // [0EEEDCBA] - see WOPL/OPLI spec @@ -693,7 +693,7 @@ void DivEngine::loadOPLI(SafeReader& reader, std::vector& ret, S if (is_4op && !is_2x2op) { ins->fm.ops = 4; ins->fm.alg = (feedConnect & 0x1) | ((feedConnect2nd & 0x1) << 1); - for (int i : {2,0,3,1}) { + for (int i : {2,0,3,1}) { // omfg >_< readOpliOp(reader, ins->fm.op[i]); } } else { @@ -706,12 +706,12 @@ void DivEngine::loadOPLI(SafeReader& reader, std::vector& ret, S } else if (is_2x2op) { // Note: Pair detuning offset not mappable. Use E5xx effect :P - ins->name = fmt::format("{0} (1)", insName); + ins->name = fmt::sprintf("%s (1)", insName); ret.push_back(ins); ins = new DivInstrument; ins->type = DIV_INS_OPL; - ins->name = fmt::format("{0} (2)", insName); + ins->name = fmt::sprintf("%s (2)", insName); for (int i : {1,0}) { readOpliOp(reader, ins->fm.op[i]); } @@ -723,7 +723,7 @@ void DivEngine::loadOPLI(SafeReader& reader, std::vector& ret, S ret.push_back(ins); } } catch (EndOfFileException& e) { - lastError = "premature end of file"; + lastError="premature end of file"; logE("premature end of file"); delete ins; } @@ -736,7 +736,7 @@ void DivEngine::loadOPNI(SafeReader& reader, std::vector& ret, S reader.seek(0, SEEK_SET); String header = reader.readString(11); - if (header.compare("WOPN2-INST") == 0 || header.compare("WOPN2-IN2T") == 0) { // omfg + if (header == "WOPN2-INST" || header == "WOPN2-IN2T") { // omfg >_< uint16_t version = reader.readS(); if (!(version >= 2) || version > 0xF) { // version 1 doesn't have a version field........ @@ -748,7 +748,7 @@ void DivEngine::loadOPNI(SafeReader& reader, std::vector& ret, S ins->fm.ops = 4; String insName = reader.readString(32); - ins->name = (insName.length() > 0) ? insName : stripPath; + ins->name = (insName.size() > 0) ? insName : stripPath; reader.seek(3, SEEK_CUR); // skip MIDI params uint8_t feedAlgo = reader.readC(); ins->fm.alg = (feedAlgo & 0x7); @@ -777,7 +777,7 @@ void DivEngine::loadOPNI(SafeReader& reader, std::vector& ret, S op.ssgEnv = ssgEg; }; - for (int i : {0,1,2,3}) { + for (int i = 0; i < 4; ++i) { readOpniOp(reader, ins->fm.op[i]); } @@ -786,7 +786,7 @@ void DivEngine::loadOPNI(SafeReader& reader, std::vector& ret, S ret.push_back(ins); } } catch (EndOfFileException& e) { - lastError = "premature end of file"; + lastError="premature end of file"; logE("premature end of file"); delete ins; } @@ -801,7 +801,7 @@ void DivEngine::loadY12(SafeReader& reader, std::vector& ret, St ins->fm.ops = 4; ins->name = stripPath; - for (int i : {0,1,2,3}) { + for (int i; i < 4; ++i) { DivInstrumentFM::Operator& insOp = ins->fm.op[i]; uint8_t tmp = reader.readC(); insOp.mult = tmp & 0xF; @@ -825,7 +825,7 @@ void DivEngine::loadY12(SafeReader& reader, std::vector& ret, St reader.seek(62, SEEK_CUR); ret.push_back(ins); } catch (EndOfFileException& e) { - lastError = "premature end of file"; + lastError="premature end of file"; logE("premature end of file"); delete ins; } @@ -904,14 +904,14 @@ void DivEngine::loadBNK(SafeReader& reader, std::vector& ret, St ins->fm.op[0].ws = reader.readC(); ins->fm.op[1].ws = reader.readC(); - ins->name = instNames[i]->length() > 0 ? (*instNames[i]) : fmt::format("{0}[{1}]", stripPath, i); + ins->name = instNames[i]->length() > 0 ? (*instNames[i]) : fmt::sprintf("%s[%d]", stripPath, i); } reader.seek(0, SEEK_END); } catch (EndOfFileException& e) { - lastError = "premature end of file"; + lastError="premature end of file"; logE("premature end of file"); - for (int i = readCount; i >= 0; --i) { + for (int i = readCount - 1; i >= 0; --i) { delete insList[i]; } is_failed = true; @@ -919,7 +919,7 @@ void DivEngine::loadBNK(SafeReader& reader, std::vector& ret, St } else { // assume GEMS BNK for now. - lastError = "GEMS BNK currently not supported."; + lastError="GEMS BNK currently not supported."; logE("GEMS BNK currently not supported."); } @@ -994,9 +994,9 @@ void DivEngine::loadFF(SafeReader& reader, std::vector& ret, Str ++readCount; } } catch (EndOfFileException& e) { - lastError = "premature end of file"; + lastError="premature end of file"; logE("premature end of file"); - for (int i = readCount; i >= 0; --i) { + for (int i = readCount - 1; i >= 0; --i) { delete insList[i]; } return; @@ -1011,6 +1011,7 @@ void DivEngine::loadOPM(SafeReader& reader, std::vector& ret, St std::vector insList; int readCount = 0; + bool is_failed = false; bool patchNameRead = false, lfoRead = false, @@ -1020,52 +1021,55 @@ void DivEngine::loadOPM(SafeReader& reader, std::vector& ret, St m2Read = false, c2Read = false; - DivInstrument* newPatch = nullptr; + DivInstrument* newPatch = NULL; auto completePatchRead = [&]() { return patchNameRead && lfoRead && characteristicRead && m1Read && c1Read && m2Read && c2Read; }; auto resetPatchRead = [&]() { patchNameRead = lfoRead = characteristicRead = m1Read = c1Read = m2Read = c2Read = false; - newPatch = nullptr; + newPatch = NULL; }; auto readIntStrWithinRange = [](String&& input, int limitLow, int limitHigh) { - int x = atoi(input.c_str()); + int x = std::stoi(input.c_str()); + if (x > limitHigh || x < limitLow) { + throw std::invalid_argument(fmt::sprintf("%s is out of bounds of range [%d..%d]", input, limitLow, limitHigh)); + } return (x>limitHigh) ? limitHigh : (x= 4) ? (7 - op.dt) : (op.dt + 3); - op.dt2 = readIntStrWithinRange(reader.readString_Token(), 0, 3); - op.am = readIntStrWithinRange(reader.readString_Token(), 0, 1); + op.dt2 = readIntStrWithinRange(reader.readStringToken(), 0, 3); + op.am = readIntStrWithinRange(reader.readStringToken(), 0, 1); }; try { reader.seek(0, SEEK_SET); while (!reader.isEOF()) { - String token = reader.readString_Token(); - if (token.length() == 0) { + String token = reader.readStringToken(); + if (token.size() == 0) { continue; } if (token.compare(0,2,"//") == 0) { if (!reader.isEOF()) { - reader.readString_Line(); + reader.readStringLine(); } continue; } // At this point we know any other line would be associated with patch params - if (newPatch == nullptr) { + if (newPatch == NULL) { newPatch = new DivInstrument; newPatch->type = DIV_INS_FM; newPatch->fm.ops = 4; @@ -1074,23 +1078,23 @@ void DivEngine::loadOPM(SafeReader& reader, std::vector& ret, St // Read each line for their respective params. They may not be written in the same LINE order but they // must absolutely be properly grouped per patch! Line prefixes must be separated by a space! (see inline comments) - if (token.length() >= 2) { + if (token.size() >= 2) { if (token[0] == '@') { // @:123 Name of patch // Note: Fallback to bank filename and current patch number in _file_ order (not @n order) - newPatch->name = reader.readString_Line(); - newPatch->name = newPatch->name.length() > 0 ? newPatch->name : fmt::format("{0}[{1}]", stripPath, readCount); + newPatch->name = reader.readStringLine(); + newPatch->name = newPatch->name.size() > 0 ? newPatch->name : fmt::sprintf("%s[%d]", stripPath, readCount); patchNameRead = true; } else if (token.compare(0,3,"CH:") == 0) { // CH: PAN FL CON AMS PMS SLOT NE - reader.readString_Token(); // skip PAN - newPatch->fm.fb = readIntStrWithinRange(reader.readString_Token(), 0, 7); - newPatch->fm.alg = readIntStrWithinRange(reader.readString_Token(), 0, 7); - newPatch->fm.ams = readIntStrWithinRange(reader.readString_Token(), 0, 4); - newPatch->fm.fms = readIntStrWithinRange(reader.readString_Token(), 0, 7); - reader.readString_Token(); // skip SLOT - reader.readString_Token(); // skip NE + reader.readStringToken(); // skip PAN + newPatch->fm.fb = readIntStrWithinRange(reader.readStringToken(), 0, 7); + newPatch->fm.alg = readIntStrWithinRange(reader.readStringToken(), 0, 7); + newPatch->fm.ams = readIntStrWithinRange(reader.readStringToken(), 0, 4); + newPatch->fm.fms = readIntStrWithinRange(reader.readStringToken(), 0, 7); + reader.readStringToken(); // skip SLOT + reader.readStringToken(); // skip NE characteristicRead = true; } else if (token.compare(0,3,"C1:") == 0) { @@ -1116,12 +1120,12 @@ void DivEngine::loadOPM(SafeReader& reader, std::vector& ret, St } else if (token.compare(0,4,"LFO:") == 0) { // LFO: LFRQ AMD PMD WF NFRQ // Furnace patches do not store these as they are chip-global. - reader.readString_Line(); + reader.readStringLine(); lfoRead = true; } else { // other unsupported lines ignored. - reader.readString_Line(); + reader.readStringLine(); } } @@ -1132,7 +1136,7 @@ void DivEngine::loadOPM(SafeReader& reader, std::vector& ret, St } } - if (newPatch != nullptr) { + if (newPatch != NULL) { addWarning("Last OPM patch read was incomplete and therefore not imported."); logW("Last OPM patch read was incomplete and therefore not imported."); delete newPatch; @@ -1142,12 +1146,21 @@ void DivEngine::loadOPM(SafeReader& reader, std::vector& ret, St ret.push_back(insList[i]); } } catch (EndOfFileException& e) { - lastError = "premature end of file"; + lastError="premature end of file"; logE("premature end of file"); - for (int i = readCount; i >= 0; --i) { + is_failed = true; + } catch (std::invalid_argument& e) { + lastError=fmt::sprintf("Invalid value found in patch file. %s", e.what()); + logE("Invalid value found in patch file."); + logE(e.what()); + is_failed = true; + } + + if (is_failed) { + for (int i = readCount - 1; i >= 0; --i) { delete insList[i]; } - if (newPatch != nullptr) { + if (newPatch != NULL) { delete newPatch; } } diff --git a/src/engine/safeReader.cpp b/src/engine/safeReader.cpp index 9d0da1a9..eb9f63ae 100644 --- a/src/engine/safeReader.cpp +++ b/src/engine/safeReader.cpp @@ -165,7 +165,7 @@ String SafeReader::readString() { return ret; } -String SafeReader::readString_Line() { +String SafeReader::readStringLine() { String ret; unsigned char c; if (isEOF()) throw EndOfFileException(this, len); @@ -179,7 +179,7 @@ String SafeReader::readString_Line() { return ret; } -String SafeReader::readString_Token(unsigned char delim) { +String SafeReader::readStringToken(unsigned char delim) { String ret; unsigned char c; if (isEOF()) throw EndOfFileException(this, len); @@ -198,12 +198,11 @@ String SafeReader::readString_Token(unsigned char delim) { } return ret; } -String SafeReader::readString_Token() { - return readString_Token(' '); + +String SafeReader::readStringToken() { + return readStringToken(' '); } - - -bool SafeReader::isEOF() { +inline bool SafeReader::isEOF() { return curSeek >= len; } \ No newline at end of file diff --git a/src/engine/safeReader.h b/src/engine/safeReader.h index 4216a596..5f27383c 100644 --- a/src/engine/safeReader.h +++ b/src/engine/safeReader.h @@ -66,10 +66,10 @@ class SafeReader { double readD_BE(); String readString(); String readString(size_t len); - String readString_Line(); - String readString_Token(unsigned char delim); - String readString_Token(); - bool isEOF(); + String readStringLine(); + String readStringToken(unsigned char delim); + String readStringToken(); + inline bool isEOF(); SafeReader(void* b, size_t l): buf((unsigned char*)b), From 580cff9d32417684823a0f4537d6958ecdc6a37f Mon Sep 17 00:00:00 2001 From: James Alan Nguyen Date: Sun, 24 Apr 2022 12:37:49 +1000 Subject: [PATCH 20/46] Decided to EOF fail seeks beyond bounds --- src/engine/fileOpsIns.cpp | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/src/engine/fileOpsIns.cpp b/src/engine/fileOpsIns.cpp index 796fd3ea..ce03915b 100644 --- a/src/engine/fileOpsIns.cpp +++ b/src/engine/fileOpsIns.cpp @@ -818,11 +818,15 @@ void DivEngine::loadY12(SafeReader& reader, std::vector& ret, St insOp.rr = tmp & 0xF; insOp.sl = ((tmp >> 4) & 0xF); insOp.ssgEnv = reader.readC(); - reader.seek(9, SEEK_CUR); + if (!reader.seek(9, SEEK_CUR)) { + throw EndOfFileException(&reader, reader.tell() + 9); + } } ins->fm.alg = reader.readC(); ins->fm.fb = reader.readC(); - reader.seek(62, SEEK_CUR); + if (!reader.seek(62, SEEK_CUR)) { + throw EndOfFileException(&reader, reader.tell() + 62); + } ret.push_back(ins); } catch (EndOfFileException& e) { lastError="premature end of file"; @@ -858,7 +862,9 @@ void DivEngine::loadBNK(SafeReader& reader, std::vector& ret, St } // Seek to BNK data - reader.seek(data_offset, SEEK_SET); + if (!reader.seek(data_offset, SEEK_SET)) { + throw EndOfFileException(&reader, data_offset); + }; // Read until EOF for (int i = 0; i < readCount; ++i) { From 2a48adfde8a9d42cdd8ce23aa08b683ae7ade504 Mon Sep 17 00:00:00 2001 From: James Alan Nguyen Date: Sun, 24 Apr 2022 13:43:56 +1000 Subject: [PATCH 21/46] Inline error --- src/engine/safeReader.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/engine/safeReader.h b/src/engine/safeReader.h index 5f27383c..e95c90a0 100644 --- a/src/engine/safeReader.h +++ b/src/engine/safeReader.h @@ -69,7 +69,7 @@ class SafeReader { String readStringLine(); String readStringToken(unsigned char delim); String readStringToken(); - inline bool isEOF(); + bool isEOF(); SafeReader(void* b, size_t l): buf((unsigned char*)b), From 2b90bd6c66b82d4589b0bab2b01c1b3bd249b520 Mon Sep 17 00:00:00 2001 From: James Alan Nguyen Date: Sun, 24 Apr 2022 13:55:21 +1000 Subject: [PATCH 22/46] = 0 --- src/engine/fileOpsIns.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/engine/fileOpsIns.cpp b/src/engine/fileOpsIns.cpp index ce03915b..e080b779 100644 --- a/src/engine/fileOpsIns.cpp +++ b/src/engine/fileOpsIns.cpp @@ -801,7 +801,7 @@ void DivEngine::loadY12(SafeReader& reader, std::vector& ret, St ins->fm.ops = 4; ins->name = stripPath; - for (int i; i < 4; ++i) { + for (int i = 0; i < 4; ++i) { DivInstrumentFM::Operator& insOp = ins->fm.op[i]; uint8_t tmp = reader.readC(); insOp.mult = tmp & 0xF; From 798bc08431419fe811262be24b5d1d9016d9434a Mon Sep 17 00:00:00 2001 From: James Alan Nguyen Date: Sun, 24 Apr 2022 14:04:56 +1000 Subject: [PATCH 23/46] =?UTF-8?q?verdammte=20compilerschie=C3=9F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/engine/safeReader.cpp | 4 ---- src/engine/safeReader.h | 2 +- 2 files changed, 1 insertion(+), 5 deletions(-) diff --git a/src/engine/safeReader.cpp b/src/engine/safeReader.cpp index eb9f63ae..36d6097d 100644 --- a/src/engine/safeReader.cpp +++ b/src/engine/safeReader.cpp @@ -202,7 +202,3 @@ String SafeReader::readStringToken(unsigned char delim) { String SafeReader::readStringToken() { return readStringToken(' '); } - -inline bool SafeReader::isEOF() { - return curSeek >= len; -} \ No newline at end of file diff --git a/src/engine/safeReader.h b/src/engine/safeReader.h index e95c90a0..e21311f9 100644 --- a/src/engine/safeReader.h +++ b/src/engine/safeReader.h @@ -69,7 +69,7 @@ class SafeReader { String readStringLine(); String readStringToken(unsigned char delim); String readStringToken(); - bool isEOF(); + inline bool isEOF() { return curSeek >= len; }; SafeReader(void* b, size_t l): buf((unsigned char*)b), From 23be8d93367f0960338725fb26cf7f9e65437fcf Mon Sep 17 00:00:00 2001 From: tildearrow Date: Sun, 24 Apr 2022 14:40:07 -0500 Subject: [PATCH 24/46] the final piece of f-num/block work --- papers/format.md | 6 +++++- src/engine/engine.h | 4 ++-- src/engine/fileOps.cpp | 14 ++++++++++++-- src/engine/platform/genesisext.cpp | 20 ++++++++++++++------ src/engine/song.h | 4 +++- src/gui/compatFlags.cpp | 6 +++++- 6 files changed, 41 insertions(+), 13 deletions(-) diff --git a/papers/format.md b/papers/format.md index 7775e9ea..ef8ab7e5 100644 --- a/papers/format.md +++ b/papers/format.md @@ -29,6 +29,8 @@ furthermore, an `or reserved` indicates this field is always present, but is res the format versions are: +- 85: Furnace dev85 +- 84: Furnace dev84 - 83: Furnace dev83 - 82: Furnace dev82 - 81: Furnace dev81 @@ -269,7 +271,9 @@ size | description 1 | ExtCh channel state is shared (>=78) or reserved 1 | ignore DAC mode change outside of intended channel (>=83) or reserved 1 | E1xx and E2xx also take priority over Slide00 (>=83) or reserved - 23 | reserved + 1 | new Sega PCM (with macros and proper vol/pan) (>=84) or reserved + 1 | weird f-num/block-based chip pitch slides (>=85) or reserved + 21 | reserved ``` # instrument diff --git a/src/engine/engine.h b/src/engine/engine.h index 515b9c59..81e15f72 100644 --- a/src/engine/engine.h +++ b/src/engine/engine.h @@ -43,8 +43,8 @@ #define BUSY_BEGIN_SOFT softLocked=true; isBusy.lock(); #define BUSY_END isBusy.unlock(); softLocked=false; -#define DIV_VERSION "dev84" -#define DIV_ENGINE_VERSION 84 +#define DIV_VERSION "dev85" +#define DIV_ENGINE_VERSION 85 // for imports #define DIV_VERSION_MOD 0xff01 diff --git a/src/engine/fileOps.cpp b/src/engine/fileOps.cpp index aee7de71..48ef68bb 100644 --- a/src/engine/fileOps.cpp +++ b/src/engine/fileOps.cpp @@ -162,6 +162,7 @@ bool DivEngine::loadDMF(unsigned char* file, size_t len) { ds.gbInsAffectsEnvelope=true; ds.ignoreDACModeOutsideIntendedChannel=false; ds.e1e2AlsoTakePriority=true; + ds.fbPortaPause=true; // 1.1 compat flags if (ds.version>24) { @@ -993,6 +994,9 @@ bool DivEngine::loadFur(unsigned char* file, size_t len) { if (ds.version<84) { ds.newSegaPCM=false; } + if (ds.version<85) { + ds.fbPortaPause=true; + } ds.isDMF=false; reader.readS(); // reserved @@ -1342,7 +1346,12 @@ bool DivEngine::loadFur(unsigned char* file, size_t len) { } else { reader.readC(); } - for (int i=0; i<22; i++) { + if (ds.version>=85) { + ds.fbPortaPause=reader.readC(); + } else { + reader.readC(); + } + for (int i=0; i<21; i++) { reader.readC(); } } @@ -2283,7 +2292,8 @@ SafeWriter* DivEngine::saveFur(bool notPrimary) { w->writeC(song.ignoreDACModeOutsideIntendedChannel); w->writeC(song.e1e2AlsoTakePriority); w->writeC(song.newSegaPCM); - for (int i=0; i<22; i++) { + w->writeC(song.fbPortaPause); + for (int i=0; i<21; i++) { w->writeC(0); } diff --git a/src/engine/platform/genesisext.cpp b/src/engine/platform/genesisext.cpp index eefec5d8..479e4242 100644 --- a/src/engine/platform/genesisext.cpp +++ b/src/engine/platform/genesisext.cpp @@ -149,14 +149,22 @@ int DivPlatformGenesisExt::dispatch(DivCommand c) { // what the heck! if (!opChan[ch].portaPause) { if ((newFreq&0x7ff)>1288) { - opChan[ch].portaPauseFreq=(644)|((newFreq+0x800)&0xf800); - opChan[ch].portaPause=true; - break; + if (parent->song.fbPortaPause) { + opChan[ch].portaPauseFreq=(644)|((newFreq+0x800)&0xf800); + opChan[ch].portaPause=true; + break; + } else { + newFreq=(newFreq>>1)|((newFreq+0x800)&0xf800); + } } if ((newFreq&0x7ff)<644) { - opChan[ch].portaPauseFreq=newFreq=(1287)|((newFreq-0x800)&0xf800); - opChan[ch].portaPause=true; - break; + if (parent->song.fbPortaPause) { + opChan[ch].portaPauseFreq=newFreq=(1287)|((newFreq-0x800)&0xf800); + opChan[ch].portaPause=true; + break; + } else { + newFreq=(newFreq<<1)|((newFreq-0x800)&0xf800); + } } } opChan[ch].portaPause=false; diff --git a/src/engine/song.h b/src/engine/song.h index 20b17ae6..9b9187ac 100644 --- a/src/engine/song.h +++ b/src/engine/song.h @@ -324,6 +324,7 @@ struct DivSong { bool ignoreDACModeOutsideIntendedChannel; bool e1e2AlsoTakePriority; bool newSegaPCM; + bool fbPortaPause; DivOrders orders; std::vector ins; @@ -406,7 +407,8 @@ struct DivSong { sharedExtStat(true), ignoreDACModeOutsideIntendedChannel(false), e1e2AlsoTakePriority(false), - newSegaPCM(true) { + newSegaPCM(true), + fbPortaPause(false) { for (int i=0; i<32; i++) { system[i]=DIV_SYSTEM_NULL; systemVol[i]=64; diff --git a/src/gui/compatFlags.cpp b/src/gui/compatFlags.cpp index d2035ff7..8d1fd6d1 100644 --- a/src/gui/compatFlags.cpp +++ b/src/gui/compatFlags.cpp @@ -101,6 +101,10 @@ void FurnaceGUI::drawCompatFlags() { if (ImGui::IsItemHovered()) { ImGui::SetTooltip("simulates a bug in where portamento does not work after sliding."); } + ImGui::Checkbox("FM pitch slide octave boundary odd behavior",&e->song.fbPortaPause); + if (ImGui::IsItemHovered()) { + ImGui::SetTooltip("if this is on, a pitch slide that crosses the octave boundary will stop for one tick and then continue from the nearest octave boundary.\nfor .dmf compatibility."); + } ImGui::Checkbox("Apply Game Boy envelope on note-less instrument change",&e->song.gbInsAffectsEnvelope); if (ImGui::IsItemHovered()) { ImGui::SetTooltip("if this is on, an instrument change will also affect the envelope."); @@ -173,4 +177,4 @@ void FurnaceGUI::drawCompatFlags() { } if (ImGui::IsWindowFocused(ImGuiFocusedFlags_ChildWindows)) curWindow=GUI_WINDOW_COMPAT_FLAGS; ImGui::End(); -} \ No newline at end of file +} From e414056f2d82e355c68c7a20976e7dc2572b3284 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Sun, 24 Apr 2022 16:57:58 -0500 Subject: [PATCH 25/46] prepare for a clear option --- src/gui/gui.cpp | 31 +++++++++++++++++++++++++++++-- src/gui/gui.h | 1 + 2 files changed, 30 insertions(+), 2 deletions(-) diff --git a/src/gui/gui.cpp b/src/gui/gui.cpp index e87c1e8e..1a9e537c 100644 --- a/src/gui/gui.cpp +++ b/src/gui/gui.cpp @@ -2711,8 +2711,10 @@ bool FurnaceGUI::loop() { if (ImGui::MenuItem("redo",BIND_FOR(GUI_ACTION_REDO))) doRedo(); ImGui::Separator(); editOptions(true); - /*ImGui::Separator(); - ImGui::MenuItem("clear...");*/ + ImGui::Separator(); + if (ImGui::MenuItem("clear...")) { + showWarning("Are you sure you want to clear...",GUI_WARN_CLEAR); + } ImGui::EndMenu(); } if (ImGui::BeginMenu("settings")) { @@ -3370,6 +3372,31 @@ bool FurnaceGUI::loop() { ImGui::CloseCurrentPopup(); } break; + case GUI_WARN_CLEAR: + if (ImGui::Button("Song (orders and patterns)")) { + ImGui::CloseCurrentPopup(); + } + ImGui::SameLine(); + if (ImGui::Button("Pattern")) { + ImGui::CloseCurrentPopup(); + } + ImGui::SameLine(); + if (ImGui::Button("Instruments")) { + ImGui::CloseCurrentPopup(); + } + ImGui::SameLine(); + if (ImGui::Button("Wavetables")) { + ImGui::CloseCurrentPopup(); + } + ImGui::SameLine(); + if (ImGui::Button("Samples")) { + ImGui::CloseCurrentPopup(); + } + + if (ImGui::Button("Wait! What am I doing?")) { + ImGui::CloseCurrentPopup(); + } + break; case GUI_WARN_GENERIC: if (ImGui::Button("OK")) { ImGui::CloseCurrentPopup(); diff --git a/src/gui/gui.h b/src/gui/gui.h index 6c50a914..8c850fd0 100644 --- a/src/gui/gui.h +++ b/src/gui/gui.h @@ -264,6 +264,7 @@ enum FurnaceGUIWarnings { GUI_WARN_RESET_COLORS, GUI_WARN_RESET_KEYBINDS, GUI_WARN_CLOSE_SETTINGS, + GUI_WARN_CLEAR, GUI_WARN_GENERIC }; From 521b3116922766344071b0f0d0a7acf4e767da99 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Sun, 24 Apr 2022 17:39:18 -0500 Subject: [PATCH 26/46] GUI: implement clear --- src/engine/song.cpp | 36 ++++++++++++++++++++++++++++++++++++ src/engine/song.h | 21 +++++++++++++++++++++ src/gui/gui.cpp | 40 +++++++++++++++++++++++++++++++++++++++- 3 files changed, 96 insertions(+), 1 deletion(-) diff --git a/src/engine/song.cpp b/src/engine/song.cpp index 4d2aef8d..7a815eb1 100644 --- a/src/engine/song.cpp +++ b/src/engine/song.cpp @@ -19,21 +19,57 @@ #include "song.h" +void DivSong::clearSongData() { + for (int i=0; ilockEngine([this]() { + e->song.clearSongData(); + }); + e->setOrder(0); + curOrder=0; + oldOrder=0; + oldOrder1=0; + MARK_MODIFIED; ImGui::CloseCurrentPopup(); } ImGui::SameLine(); if (ImGui::Button("Pattern")) { + stop(); + e->lockEngine([this]() { + for (int i=0; igetTotalChannelCount(); i++) { + DivPattern* pat=e->song.pat[i].getPattern(e->song.orders.ord[i][curOrder],true); + memset(pat->data,-1,256*32*sizeof(short)); + for (int j=0; j<256; j++) { + pat->data[j][0]=0; + pat->data[j][1]=0; + } + } + }); + MARK_MODIFIED; ImGui::CloseCurrentPopup(); } ImGui::SameLine(); if (ImGui::Button("Instruments")) { + stop(); + e->lockEngine([this]() { + e->song.clearInstruments(); + }); + curIns=-1; + MARK_MODIFIED; ImGui::CloseCurrentPopup(); } ImGui::SameLine(); if (ImGui::Button("Wavetables")) { + stop(); + e->lockEngine([this]() { + e->song.clearWavetables(); + }); + curWave=0; + MARK_MODIFIED; ImGui::CloseCurrentPopup(); } ImGui::SameLine(); if (ImGui::Button("Samples")) { + stop(); + e->lockEngine([this]() { + e->song.clearSamples(); + }); + curSample=0; ImGui::CloseCurrentPopup(); } - if (ImGui::Button("Wait! What am I doing?")) { + if (ImGui::Button("Wait! What am I doing? Cancel!")) { ImGui::CloseCurrentPopup(); } break; From f99ba810feab7b3bed0b5e8b46174df2a3e0617c Mon Sep 17 00:00:00 2001 From: tildearrow Date: Sun, 24 Apr 2022 17:48:04 -0500 Subject: [PATCH 27/46] PC speaker: fix timer issue --- src/engine/platform/pcspkr.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/engine/platform/pcspkr.cpp b/src/engine/platform/pcspkr.cpp index b34d16f2..8a00c563 100644 --- a/src/engine/platform/pcspkr.cpp +++ b/src/engine/platform/pcspkr.cpp @@ -53,6 +53,7 @@ void DivPlatformPCSpeaker::acquire_unfilt(short* bufL, short* bufR, size_t start for (size_t i=start; ifreq) pos=freq; while (pos<0) { if (freq<1) { pos=1; @@ -71,6 +72,7 @@ void DivPlatformPCSpeaker::acquire_cone(short* bufL, short* bufR, size_t start, for (size_t i=start; ifreq) pos=freq; while (pos<0) { if (freq<1) { pos=1; @@ -95,6 +97,7 @@ void DivPlatformPCSpeaker::acquire_piezo(short* bufL, short* bufR, size_t start, for (size_t i=start; ifreq) pos=freq; while (pos<0) { if (freq<1) { pos=1; From bd68ef5cc3efdc439aa7f7418dc8028f65f6d106 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Sun, 24 Apr 2022 18:12:18 -0500 Subject: [PATCH 28/46] dev86 - SN duty macro phase reset fix --- papers/format.md | 4 +++- src/engine/engine.h | 4 ++-- src/engine/fileOps.cpp | 14 ++++++++++++-- src/engine/platform/sms.cpp | 10 ++++++---- src/engine/song.h | 4 +++- src/gui/compatFlags.cpp | 4 ++++ 6 files changed, 30 insertions(+), 10 deletions(-) diff --git a/papers/format.md b/papers/format.md index ef8ab7e5..207b232a 100644 --- a/papers/format.md +++ b/papers/format.md @@ -29,6 +29,7 @@ furthermore, an `or reserved` indicates this field is always present, but is res the format versions are: +- 86: Furnace dev86 - 85: Furnace dev85 - 84: Furnace dev84 - 83: Furnace dev83 @@ -273,7 +274,8 @@ size | description 1 | E1xx and E2xx also take priority over Slide00 (>=83) or reserved 1 | new Sega PCM (with macros and proper vol/pan) (>=84) or reserved 1 | weird f-num/block-based chip pitch slides (>=85) or reserved - 21 | reserved + 1 | SN duty macro always resets phase (>=86) or reserved + 20 | reserved ``` # instrument diff --git a/src/engine/engine.h b/src/engine/engine.h index 427215df..3bd1db3d 100644 --- a/src/engine/engine.h +++ b/src/engine/engine.h @@ -43,8 +43,8 @@ #define BUSY_BEGIN_SOFT softLocked=true; isBusy.lock(); #define BUSY_END isBusy.unlock(); softLocked=false; -#define DIV_VERSION "dev85" -#define DIV_ENGINE_VERSION 85 +#define DIV_VERSION "dev86" +#define DIV_ENGINE_VERSION 86 // for imports #define DIV_VERSION_MOD 0xff01 diff --git a/src/engine/fileOps.cpp b/src/engine/fileOps.cpp index 48ef68bb..38b33502 100644 --- a/src/engine/fileOps.cpp +++ b/src/engine/fileOps.cpp @@ -163,6 +163,7 @@ bool DivEngine::loadDMF(unsigned char* file, size_t len) { ds.ignoreDACModeOutsideIntendedChannel=false; ds.e1e2AlsoTakePriority=true; ds.fbPortaPause=true; + ds.snDutyReset=true; // 1.1 compat flags if (ds.version>24) { @@ -997,6 +998,9 @@ bool DivEngine::loadFur(unsigned char* file, size_t len) { if (ds.version<85) { ds.fbPortaPause=true; } + if (ds.version<86) { + ds.snDutyReset=true; + } ds.isDMF=false; reader.readS(); // reserved @@ -1351,7 +1355,12 @@ bool DivEngine::loadFur(unsigned char* file, size_t len) { } else { reader.readC(); } - for (int i=0; i<21; i++) { + if (ds.version>=86) { + ds.snDutyReset=reader.readC(); + } else { + reader.readC(); + } + for (int i=0; i<20; i++) { reader.readC(); } } @@ -2293,7 +2302,8 @@ SafeWriter* DivEngine::saveFur(bool notPrimary) { w->writeC(song.e1e2AlsoTakePriority); w->writeC(song.newSegaPCM); w->writeC(song.fbPortaPause); - for (int i=0; i<21; i++) { + w->writeC(song.snDutyReset); + for (int i=0; i<20; i++) { w->writeC(0); } diff --git a/src/engine/platform/sms.cpp b/src/engine/platform/sms.cpp index af339f6e..c32548b5 100644 --- a/src/engine/platform/sms.cpp +++ b/src/engine/platform/sms.cpp @@ -86,11 +86,13 @@ void DivPlatformSMS::tick(bool sysTick) { } if (i==3) { if (chan[i].std.duty.had) { - snNoiseMode=chan[i].std.duty.val; - if (chan[i].std.duty.val<2) { - chan[3].freqChanged=false; + if (chan[i].std.duty.val!=snNoiseMode || parent->song.snDutyReset) { + snNoiseMode=chan[i].std.duty.val; + if (chan[i].std.duty.val<2) { + chan[3].freqChanged=false; + } + updateSNMode=true; } - updateSNMode=true; } if (chan[i].std.phaseReset.had) { if (chan[i].std.phaseReset.val==1) { diff --git a/src/engine/song.h b/src/engine/song.h index 11264558..7c06a14a 100644 --- a/src/engine/song.h +++ b/src/engine/song.h @@ -325,6 +325,7 @@ struct DivSong { bool e1e2AlsoTakePriority; bool newSegaPCM; bool fbPortaPause; + bool snDutyReset; DivOrders orders; std::vector ins; @@ -429,7 +430,8 @@ struct DivSong { ignoreDACModeOutsideIntendedChannel(false), e1e2AlsoTakePriority(false), newSegaPCM(true), - fbPortaPause(false) { + fbPortaPause(false), + snDutyReset(false) { for (int i=0; i<32; i++) { system[i]=DIV_SYSTEM_NULL; systemVol[i]=64; diff --git a/src/gui/compatFlags.cpp b/src/gui/compatFlags.cpp index 8d1fd6d1..ccaeeb3f 100644 --- a/src/gui/compatFlags.cpp +++ b/src/gui/compatFlags.cpp @@ -117,6 +117,10 @@ void FurnaceGUI::drawCompatFlags() { if (ImGui::IsItemHovered()) { ImGui::SetTooltip("does this make any sense by now?"); } + ImGui::Checkbox("SN76489 duty macro always resets phase",&e->song.snDutyReset); + if (ImGui::IsItemHovered()) { + ImGui::SetTooltip("when enabled, duty macro will always reset phase, even if its value hasn't changed."); + } ImGui::Text("Loop modality:"); if (ImGui::RadioButton("Reset channels",e->song.loopModality==0)) { From 52e35fdf049ac9975f297341fcf363a85e57c1e6 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Sun, 24 Apr 2022 18:17:05 -0500 Subject: [PATCH 29/46] huh? TODO: fix this --- src/engine/platform/genesis.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/engine/platform/genesis.cpp b/src/engine/platform/genesis.cpp index 61fffcb8..0e8c5954 100644 --- a/src/engine/platform/genesis.cpp +++ b/src/engine/platform/genesis.cpp @@ -395,7 +395,7 @@ void DivPlatformGenesis::tick(bool sysTick) { if (i==2 && extMode) continue; if (chan[i].freqChanged) { chan[i].freq=((chan[i].baseFreq&0xf800)|parent->calcFreq(chan[i].baseFreq&0x7ff,chan[i].pitch,false,4))+chan[i].std.pitch.val; - if (chan[i].freq>65535) chan[i].freq=65535; + if (chan[i].freq>0x3fff) chan[i].freq=0x3fff; int freqt=chan[i].freq; immWrite(chanOffs[i]+ADDR_FREQH,freqt>>8); immWrite(chanOffs[i]+ADDR_FREQ,freqt&0xff); From c84ff399d9f649776c382a6603e1f5ac954d8cd9 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Sun, 24 Apr 2022 22:45:59 -0500 Subject: [PATCH 30/46] YM2612: fix negative octaves --- src/engine/dispatch.h | 3 ++- src/engine/engine.cpp | 11 +++++++++++ src/engine/engine.h | 3 +++ 3 files changed, 16 insertions(+), 1 deletion(-) diff --git a/src/engine/dispatch.h b/src/engine/dispatch.h index b358c464..bd3b19a6 100644 --- a/src/engine/dispatch.h +++ b/src/engine/dispatch.h @@ -426,7 +426,8 @@ class DivDispatch { #define NOTE_PERIODIC(x) round(parent->calcBaseFreq(chipClock,CHIP_DIVIDER,x,true)) #define NOTE_PERIODIC_NOROUND(x) parent->calcBaseFreq(chipClock,CHIP_DIVIDER,x,true) #define NOTE_FREQUENCY(x) parent->calcBaseFreq(chipClock,CHIP_FREQBASE,x,false) -#define NOTE_FNUM_BLOCK(x,bits) ((((int)parent->calcBaseFreq(chipClock,CHIP_FREQBASE,(x)%12,false))&((1<calcBaseFreqFNumBlock(chipClock,CHIP_FREQBASE,x,bits) #define COLOR_NTSC (315000000.0/88.0) #define COLOR_PAL (283.75*15625.0+25.0) diff --git a/src/engine/engine.cpp b/src/engine/engine.cpp index 905ba8aa..2ebbd2fd 100644 --- a/src/engine/engine.cpp +++ b/src/engine/engine.cpp @@ -887,6 +887,17 @@ double DivEngine::calcBaseFreq(double clock, double divider, int note, bool peri base*(divider/clock); } +unsigned short DivEngine::calcBaseFreqFNumBlock(double clock, double divider, int note, int bits) { + int bf=calcBaseFreq(clock,divider,note,false); + int block=note/12; + if (block<0) block=0; + if (block>7) block=7; + bf>>=block; + if (bf<0) bf=0; + if (bf>((1< Date: Mon, 25 Apr 2022 01:24:42 -0500 Subject: [PATCH 31/46] YM2612: fix several sample bugs --- src/engine/engine.cpp | 10 ++++- src/engine/platform/genesis.cpp | 69 +++++++++++++++++++++++------- src/engine/platform/genesisext.cpp | 14 +++++- 3 files changed, 74 insertions(+), 19 deletions(-) diff --git a/src/engine/engine.cpp b/src/engine/engine.cpp index 2ebbd2fd..3bfcb54c 100644 --- a/src/engine/engine.cpp +++ b/src/engine/engine.cpp @@ -894,7 +894,15 @@ unsigned short DivEngine::calcBaseFreqFNumBlock(double clock, double divider, in if (block>7) block=7; bf>>=block; if (bf<0) bf=0; - if (bf>((1<1288) { // apparently octave boundary + while (block<7) { + bf>>=1; + block++; + } + if (bf>((1<=rate) { DivSample* s=parent->getSample(dacSample); if (s->samples>0) { if (!isMuted[5]) { @@ -106,7 +106,7 @@ void DivPlatformGenesis::acquire_nuked(short* bufL, short* bufR, size_t start, s } } } - dacPeriod+=MAX(40,dacRate); + dacPeriod-=rate; } else { dacSample=-1; } @@ -156,8 +156,8 @@ void DivPlatformGenesis::acquire_ymfm(short* bufL, short* bufR, size_t start, si for (size_t h=start; h=rate) { DivSample* s=parent->getSample(dacSample); if (s->samples>0) { if (!isMuted[5]) { @@ -175,7 +175,7 @@ void DivPlatformGenesis::acquire_ymfm(short* bufL, short* bufR, size_t start, si } } } - dacPeriod+=MAX(40,dacRate); + dacPeriod-=rate; } else { dacSample=-1; } @@ -394,11 +394,20 @@ void DivPlatformGenesis::tick(bool sysTick) { for (int i=0; i<6; i++) { if (i==2 && extMode) continue; if (chan[i].freqChanged) { - chan[i].freq=((chan[i].baseFreq&0xf800)|parent->calcFreq(chan[i].baseFreq&0x7ff,chan[i].pitch,false,4))+chan[i].std.pitch.val; + int fNum=parent->calcFreq(chan[i].baseFreq&0x7ff,chan[i].pitch,false,4)+chan[i].std.pitch.val; + int block=(chan[i].baseFreq&0xf800)>>11; + if (fNum<0) fNum=0; + if (fNum>2047) { + while (block<7) { + fNum>>=1; + block++; + } + if (fNum>2047) fNum=2047; + } + chan[i].freq=(block<<11)|fNum; if (chan[i].freq>0x3fff) chan[i].freq=0x3fff; - int freqt=chan[i].freq; - immWrite(chanOffs[i]+ADDR_FREQH,freqt>>8); - immWrite(chanOffs[i]+ADDR_FREQ,freqt&0xff); + immWrite(chanOffs[i]+ADDR_FREQH,chan[i].freq>>8); + immWrite(chanOffs[i]+ADDR_FREQ,chan[i].freq&0xff); if (chan[i].furnaceDac && dacMode) { double off=1.0; if (dacSample>=0 && dacSamplesong.sampleLen) { @@ -406,12 +415,13 @@ void DivPlatformGenesis::tick(bool sysTick) { if (s->centerRate<1) { off=1.0; } else { - off=8363.0/(double)s->centerRate; + off=(double)s->centerRate/8363.0; } } - dacRate=(1280000*1.25*off)/MAX(1,chan[i].baseFreq); + chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,false,4)+chan[i].std.pitch.val;; + dacRate=chan[i].freq*off; if (dacRate<1) dacRate=1; - if (dumpWrites) addWrite(0xffff0001,1280000/dacRate); + if (dumpWrites) addWrite(0xffff0001,dacRate); } chan[i].freqChanged=false; } @@ -468,7 +478,7 @@ int DivPlatformGenesis::dispatch(DivCommand c) { dacPos=0; dacPeriod=0; if (c.value!=DIV_NOTE_NULL) { - chan[c.chan].baseFreq=NOTE_FNUM_BLOCK(c.value,11); + chan[c.chan].baseFreq=parent->calcBaseFreq(1,1,c.value,false); chan[c.chan].freqChanged=true; } chan[c.chan].furnaceDac=true; @@ -487,7 +497,7 @@ int DivPlatformGenesis::dispatch(DivCommand c) { } dacPos=0; dacPeriod=0; - dacRate=1280000/MAX(1,parent->getSample(dacSample)->rate); + dacRate=MAX(1,parent->getSample(dacSample)->rate); if (dumpWrites) addWrite(0xffff0001,parent->getSample(dacSample)->rate); chan[c.chan].furnaceDac=false; } @@ -615,6 +625,29 @@ int DivPlatformGenesis::dispatch(DivCommand c) { break; } case DIV_CMD_NOTE_PORTA: { + if (c.chan==5 && chan[c.chan].furnaceDac) { + int destFreq=parent->calcBaseFreq(1,1,c.value2,false); + bool return2=false; + if (destFreq>chan[c.chan].baseFreq) { + chan[c.chan].baseFreq+=c.value*16; + if (chan[c.chan].baseFreq>=destFreq) { + chan[c.chan].baseFreq=destFreq; + return2=true; + } + } else { + chan[c.chan].baseFreq-=c.value*16; + if (chan[c.chan].baseFreq<=destFreq) { + chan[c.chan].baseFreq=destFreq; + return2=true; + } + } + chan[c.chan].freqChanged=true; + if (return2) { + chan[c.chan].inPorta=false; + return 2; + } + break; + } int destFreq=NOTE_FNUM_BLOCK(c.value2,11); int newFreq; bool return2=false; @@ -669,7 +702,11 @@ int DivPlatformGenesis::dispatch(DivCommand c) { } break; case DIV_CMD_LEGATO: { - chan[c.chan].baseFreq=NOTE_FNUM_BLOCK(c.value,11); + if (c.chan==5 && chan[c.chan].furnaceDac) { + chan[c.chan].baseFreq=parent->calcBaseFreq(1,1,c.value,false); + } else { + chan[c.chan].baseFreq=NOTE_FNUM_BLOCK(c.value,11); + } chan[c.chan].note=c.value; chan[c.chan].freqChanged=true; break; diff --git a/src/engine/platform/genesisext.cpp b/src/engine/platform/genesisext.cpp index 479e4242..f146e2fc 100644 --- a/src/engine/platform/genesisext.cpp +++ b/src/engine/platform/genesisext.cpp @@ -307,8 +307,18 @@ void DivPlatformGenesisExt::tick(bool sysTick) { unsigned char writeMask=2; if (extMode) for (int i=0; i<4; i++) { if (opChan[i].freqChanged) { - opChan[i].freq=(opChan[i].baseFreq&0xf800)|parent->calcFreq(opChan[i].baseFreq&0x7ff,opChan[i].pitch,false,4); - if (chan[i].freq>65535) chan[i].freq=65535; + int fNum=parent->calcFreq(opChan[i].baseFreq&0x7ff,opChan[i].pitch,false,4)+opChan[i].std.pitch.val; + int block=(opChan[i].baseFreq&0xf800)>>11; + if (fNum<0) fNum=0; + if (fNum>2047) { + while (block<7) { + fNum>>=1; + block++; + } + if (fNum>2047) fNum=2047; + } + opChan[i].freq=(block<<11)|fNum; + if (opChan[i].freq>0x3fff) opChan[i].freq=0x3fff; immWrite(opChanOffsH[i],opChan[i].freq>>8); immWrite(opChanOffsL[i],opChan[i].freq&0xff); } From bcade6931a45163096c59c04f4f38223d8600f0a Mon Sep 17 00:00:00 2001 From: tildearrow Date: Mon, 25 Apr 2022 01:42:26 -0500 Subject: [PATCH 32/46] YM2612: allow for DAC rate higher than 32KHz issue #391 --- src/engine/platform/genesis.cpp | 26 ++++++++++++++++++++++---- src/engine/platform/genesis.h | 2 ++ 2 files changed, 24 insertions(+), 4 deletions(-) diff --git a/src/engine/platform/genesis.cpp b/src/engine/platform/genesis.cpp index 324a66e0..f2203fc3 100644 --- a/src/engine/platform/genesis.cpp +++ b/src/engine/platform/genesis.cpp @@ -86,14 +86,22 @@ void DivPlatformGenesis::acquire_nuked(short* bufL, short* bufR, size_t start, s static int os[2]; for (size_t h=start; h=rate) { + dacDelay-=rate; + dacReady=true; + } + } if (dacMode && dacSample!=-1) { dacPeriod+=dacRate; if (dacPeriod>=rate) { DivSample* s=parent->getSample(dacSample); if (s->samples>0) { if (!isMuted[5]) { - if (writes.size()<16) { + if (dacReady && writes.size()<16) { urgentWrite(0x2a,(unsigned char)s->data8[dacPos]+0x80); + dacReady=false; } } if (++dacPos>=s->samples) { @@ -106,7 +114,7 @@ void DivPlatformGenesis::acquire_nuked(short* bufL, short* bufR, size_t start, s } } } - dacPeriod-=rate; + while (dacPeriod>=rate) dacPeriod-=rate; } else { dacSample=-1; } @@ -155,14 +163,22 @@ void DivPlatformGenesis::acquire_ymfm(short* bufL, short* bufR, size_t start, si static int os[2]; for (size_t h=start; h=rate) { + dacDelay-=rate; + dacReady=true; + } + } if (dacMode && dacSample!=-1) { dacPeriod+=dacRate; if (dacPeriod>=rate) { DivSample* s=parent->getSample(dacSample); if (s->samples>0) { if (!isMuted[5]) { - if (writes.size()<16) { + if (dacReady && writes.size()<16) { urgentWrite(0x2a,(unsigned char)s->data8[dacPos]+0x80); + dacReady=false; } } if (++dacPos>=s->samples) { @@ -175,7 +191,7 @@ void DivPlatformGenesis::acquire_ymfm(short* bufL, short* bufR, size_t start, si } } } - dacPeriod-=rate; + while (dacPeriod>=rate) dacPeriod-=rate; } else { dacSample=-1; } @@ -858,6 +874,8 @@ void DivPlatformGenesis::reset() { dacPeriod=0; dacPos=0; dacRate=0; + dacDelay=0; + dacReady=true; dacSample=-1; sampleBank=0; lfoValue=8; diff --git a/src/engine/platform/genesis.h b/src/engine/platform/genesis.h index b9430306..aa7659fa 100644 --- a/src/engine/platform/genesis.h +++ b/src/engine/platform/genesis.h @@ -85,6 +85,8 @@ class DivPlatformGenesis: public DivDispatch { int dacRate; unsigned int dacPos; int dacSample; + int dacDelay; + bool dacReady; unsigned char sampleBank; unsigned char lfoValue; From ed6820ac46df214cef057ae666bfe975b145263d Mon Sep 17 00:00:00 2001 From: tildearrow Date: Mon, 25 Apr 2022 11:23:44 -0500 Subject: [PATCH 33/46] GUI: fix cursor going out of bounds on paste --- src/gui/editing.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/gui/editing.cpp b/src/gui/editing.cpp index 106ec61b..e5874f91 100644 --- a/src/gui/editing.cpp +++ b/src/gui/editing.cpp @@ -554,6 +554,7 @@ void FurnaceGUI::doPaste(PasteMode mode) { } if (settings.cursorPastePos) { cursor.y=j; + if (cursor.y>=e->song.patLen) cursor.y=e->song.patLen-1; updateScroll(cursor.y); } @@ -1003,4 +1004,4 @@ void FurnaceGUI::doRedo() { } redoHist.pop_back(); -} \ No newline at end of file +} From 17cf657f6acb3aa323c6267def47cec0bb6cacfd Mon Sep 17 00:00:00 2001 From: tildearrow Date: Mon, 25 Apr 2022 11:52:05 -0500 Subject: [PATCH 34/46] one more pitch calc fix --- src/engine/engine.cpp | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/engine/engine.cpp b/src/engine/engine.cpp index 3bfcb54c..c56eff9c 100644 --- a/src/engine/engine.cpp +++ b/src/engine/engine.cpp @@ -894,7 +894,12 @@ unsigned short DivEngine::calcBaseFreqFNumBlock(double clock, double divider, in if (block>7) block=7; bf>>=block; if (bf<0) bf=0; - if (bf>1288) { // apparently octave boundary + // octave boundaries + while (bf>0 && bf<644 && block>0) { + bf<<=1; + block--; + } + if (bf>1288) { while (block<7) { bf>>=1; block++; From 1b97d3912bcd7872da78ac8b47779ee54c821c01 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Mon, 25 Apr 2022 11:59:02 -0500 Subject: [PATCH 35/46] YM2612: hopefully the final pitch slide fix --- src/engine/platform/genesis.cpp | 6 +++--- src/engine/platform/genesisext.cpp | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/engine/platform/genesis.cpp b/src/engine/platform/genesis.cpp index f2203fc3..72dc9c0f 100644 --- a/src/engine/platform/genesis.cpp +++ b/src/engine/platform/genesis.cpp @@ -434,7 +434,7 @@ void DivPlatformGenesis::tick(bool sysTick) { off=(double)s->centerRate/8363.0; } } - chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,false,4)+chan[i].std.pitch.val;; + chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,false,4)+chan[i].std.pitch.val; dacRate=chan[i].freq*off; if (dacRate<1) dacRate=1; if (dumpWrites) addWrite(0xffff0001,dacRate); @@ -686,12 +686,12 @@ int DivPlatformGenesis::dispatch(DivCommand c) { // check for octave boundary // what the heck! if (!chan[c.chan].portaPause) { - if ((newFreq&0x7ff)>1288) { + if ((newFreq&0x7ff)>1288 && (newFreq&0xf800)<0x3800) { chan[c.chan].portaPauseFreq=(644)|((newFreq+0x800)&0xf800); chan[c.chan].portaPause=true; break; } - if ((newFreq&0x7ff)<644) { + if ((newFreq&0x7ff)<644 && (newFreq&0xf800)>0) { chan[c.chan].portaPauseFreq=newFreq=(1287)|((newFreq-0x800)&0xf800); chan[c.chan].portaPause=true; break; diff --git a/src/engine/platform/genesisext.cpp b/src/engine/platform/genesisext.cpp index f146e2fc..59b77587 100644 --- a/src/engine/platform/genesisext.cpp +++ b/src/engine/platform/genesisext.cpp @@ -148,7 +148,7 @@ int DivPlatformGenesisExt::dispatch(DivCommand c) { } // what the heck! if (!opChan[ch].portaPause) { - if ((newFreq&0x7ff)>1288) { + if ((newFreq&0x7ff)>1288 && (newFreq&0xf800)<0x3800) { if (parent->song.fbPortaPause) { opChan[ch].portaPauseFreq=(644)|((newFreq+0x800)&0xf800); opChan[ch].portaPause=true; @@ -157,7 +157,7 @@ int DivPlatformGenesisExt::dispatch(DivCommand c) { newFreq=(newFreq>>1)|((newFreq+0x800)&0xf800); } } - if ((newFreq&0x7ff)<644) { + if ((newFreq&0x7ff)<644 && (newFreq&0xf800)>0) { if (parent->song.fbPortaPause) { opChan[ch].portaPauseFreq=newFreq=(1287)|((newFreq-0x800)&0xf800); opChan[ch].portaPause=true; From 7b79c3225823be4a21508543c016d0bcbd632168 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Mon, 25 Apr 2022 14:58:58 -0500 Subject: [PATCH 36/46] increase range of arp macro to 120 --- src/gui/insEdit.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/gui/insEdit.cpp b/src/gui/insEdit.cpp index 7879d966..a3368e92 100644 --- a/src/gui/insEdit.cpp +++ b/src/gui/insEdit.cpp @@ -2836,7 +2836,7 @@ void FurnaceGUI::drawInsEdit() { if (volMax>0) { NORMAL_MACRO(ins->std.volMacro,volMin,volMax,"vol",volumeLabel,160,ins->std.volMacro.open,false,NULL,false,NULL,0,0,0,0,false,0,macroDummyMode,uiColors[GUI_COLOR_MACRO_VOLUME],mmlString[0],volMin,volMax,NULL,false); } - NORMAL_MACRO(ins->std.arpMacro,arpMacroScroll,arpMacroScroll+24,"arp","Arpeggio",160,ins->std.arpMacro.open,false,NULL,true,&arpMacroScroll,(arpMode?-60:-80),70,0,0,true,1,macroAbsoluteMode,uiColors[GUI_COLOR_MACRO_PITCH],mmlString[1],-92,94,(ins->std.arpMacro.mode?(¯oHoverNote):NULL),true); + NORMAL_MACRO(ins->std.arpMacro,arpMacroScroll,arpMacroScroll+24,"arp","Arpeggio",160,ins->std.arpMacro.open,false,NULL,true,&arpMacroScroll,(arpMode?-60:-120),120,0,0,true,1,macroAbsoluteMode,uiColors[GUI_COLOR_MACRO_PITCH],mmlString[1],-120,120,(ins->std.arpMacro.mode?(¯oHoverNote):NULL),true); if (dutyMax>0) { if (ins->type==DIV_INS_MIKEY) { NORMAL_MACRO(ins->std.dutyMacro,0,dutyMax,"duty",dutyLabel,160,ins->std.dutyMacro.open,true,mikeyFeedbackBits,false,NULL,0,0,0,0,false,0,macroDummyMode,uiColors[GUI_COLOR_MACRO_OTHER],mmlString[2],0,dutyMax,NULL,false); From f3f92b80352f4226a179b31860cee488ddbe911a Mon Sep 17 00:00:00 2001 From: tildearrow Date: Mon, 25 Apr 2022 15:07:36 -0500 Subject: [PATCH 37/46] GUI: deprecate compat rate --- src/gui/sampleEdit.cpp | 38 +++++++++++++++++++------------------- 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/src/gui/sampleEdit.cpp b/src/gui/sampleEdit.cpp index 802e10bc..53379c99 100644 --- a/src/gui/sampleEdit.cpp +++ b/src/gui/sampleEdit.cpp @@ -74,15 +74,6 @@ void FurnaceGUI::drawSampleEdit() { ImGui::EndCombo(); } - ImGui::TableNextColumn(); - ImGui::Text("Rate (Hz)"); - ImGui::SameLine(); - ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x); - if (ImGui::InputInt("##SampleRate",&sample->rate,10,200)) { MARK_MODIFIED - if (sample->rate<100) sample->rate=100; - if (sample->rate>96000) sample->rate=96000; - } - ImGui::TableNextColumn(); ImGui::Text("C-4 (Hz)"); ImGui::SameLine(); @@ -92,6 +83,15 @@ void FurnaceGUI::drawSampleEdit() { if (sample->centerRate>65535) sample->centerRate=65535; } + ImGui::TableNextColumn(); + ImGui::Text("Compat Rate"); + ImGui::SameLine(); + ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x); + if (ImGui::InputInt("##SampleRate",&sample->rate,10,200)) { MARK_MODIFIED + if (sample->rate<100) sample->rate=100; + if (sample->rate>96000) sample->rate=96000; + } + ImGui::TableNextColumn(); bool doLoop=(sample->loopStart>=0); if (ImGui::Checkbox("Loop",&doLoop)) { MARK_MODIFIED @@ -587,16 +587,6 @@ void FurnaceGUI::drawSampleEdit() { ImGui::EndCombo(); } - ImGui::TableNextColumn(); - ImGui::Text("Rate (Hz)"); - ImGui::SameLine(); - ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x); - if (ImGui::InputInt("##SampleRate",&sample->rate,10,200)) { MARK_MODIFIED - if (sample->rate<100) sample->rate=100; - if (sample->rate>96000) sample->rate=96000; - } - - ImGui::TableNextRow(); ImGui::TableNextColumn(); ImGui::Text("C-4 (Hz)"); ImGui::SameLine(); @@ -606,6 +596,16 @@ void FurnaceGUI::drawSampleEdit() { if (sample->centerRate>65535) sample->centerRate=65535; } + ImGui::TableNextRow(); + ImGui::TableNextColumn(); + ImGui::Text("Compat Rate"); + ImGui::SameLine(); + ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x); + if (ImGui::InputInt("##SampleRate",&sample->rate,10,200)) { MARK_MODIFIED + if (sample->rate<100) sample->rate=100; + if (sample->rate>96000) sample->rate=96000; + } + ImGui::TableNextColumn(); bool doLoop=(sample->loopStart>=0); if (ImGui::Checkbox("Loop",&doLoop)) { MARK_MODIFIED From 5821135b38c916ffdb088fedc06836e3476e5057 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Mon, 25 Apr 2022 15:54:31 -0500 Subject: [PATCH 38/46] don't return 1 on -help and -version fixes #385 --- src/main.cpp | 67 ++++++++++++++++++++++++++++---------------------- src/ta-utils.h | 10 ++++++-- 2 files changed, 46 insertions(+), 31 deletions(-) diff --git a/src/main.cpp b/src/main.cpp index 4a3e9d0e..5db37c30 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -60,7 +60,7 @@ bool displayEngineFailError=false; std::vector params; -bool pHelp(String) { +TAParamResult pHelp(String) { printf("usage: furnace [params] [filename]\n" "you may specify the following parameters:\n"); for (auto& i: params) { @@ -70,13 +70,13 @@ bool pHelp(String) { printf(" -%s: %s\n",i.name.c_str(),i.desc.c_str()); } } - return false; + return TA_PARAM_QUIT; } -bool pAudio(String val) { +TAParamResult pAudio(String val) { if (outName!="") { logE("can't use -audio and -output at the same time."); - return false; + return TA_PARAM_ERROR; } if (val=="jack") { e.setAudio(DIV_AUDIO_JACK); @@ -84,12 +84,12 @@ bool pAudio(String val) { e.setAudio(DIV_AUDIO_SDL); } else { logE("invalid value for audio engine! valid values are: jack, sdl."); - return false; + return TA_PARAM_ERROR; } - return true; + return TA_PARAM_SUCCESS; } -bool pView(String val) { +TAParamResult pView(String val) { if (val=="pattern") { e.setView(DIV_STATUS_PATTERN); } else if (val=="commands") { @@ -98,17 +98,17 @@ bool pView(String val) { e.setView(DIV_STATUS_NOTHING); } else { logE("invalid value for view type! valid values are: pattern, commands, nothing."); - return false; + return TA_PARAM_ERROR; } - return true; + return TA_PARAM_SUCCESS; } -bool pConsole(String val) { +TAParamResult pConsole(String val) { consoleMode=true; - return true; + return TA_PARAM_SUCCESS; } -bool pLogLevel(String val) { +TAParamResult pLogLevel(String val) { if (val=="trace") { logLevel=LOGLEVEL_TRACE; } else if (val=="debug") { @@ -121,12 +121,12 @@ bool pLogLevel(String val) { logLevel=LOGLEVEL_ERROR; } else { logE("invalid value for loglevel! valid values are: trace, debug, info, warning, error."); - return false; + return TA_PARAM_ERROR; } - return true; + return TA_PARAM_SUCCESS; } -bool pVersion(String) { +TAParamResult pVersion(String) { printf("Furnace version " DIV_VERSION ".\n\n"); printf("copyright (C) 2021-2022 tildearrow and contributors.\n"); printf("licensed under the GNU General Public License version 2 or later\n"); @@ -152,10 +152,10 @@ bool pVersion(String) { printf("- puNES by FHorse (GPLv2)\n"); printf("- reSID by Dag Lem (GPLv2)\n"); printf("- Stella by Stella Team (GPLv2)\n"); - return false; + return TA_PARAM_QUIT; } -bool pWarranty(String) { +TAParamResult pWarranty(String) { printf("This program is free software; you can redistribute it and/or\n" "modify it under the terms of the GNU General Public License\n" "as published by the Free Software Foundation; either version 2\n" @@ -169,10 +169,10 @@ bool pWarranty(String) { "You should have received a copy of the GNU General Public License\n" "along with this program; if not, write to the Free Software\n" "Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.\n"); - return false; + return TA_PARAM_QUIT; } -bool pLoops(String val) { +TAParamResult pLoops(String val) { try { int count=std::stoi(val); if (count<0) { @@ -182,12 +182,12 @@ bool pLoops(String val) { } } catch (std::exception& e) { logE("loop count shall be a number."); - return false; + return TA_PARAM_ERROR; } - return true; + return TA_PARAM_SUCCESS; } -bool pOutMode(String val) { +TAParamResult pOutMode(String val) { if (val=="one") { outMode=DIV_EXPORT_MODE_ONE; } else if (val=="persys") { @@ -196,21 +196,21 @@ bool pOutMode(String val) { outMode=DIV_EXPORT_MODE_MANY_CHAN; } else { logE("invalid value for outmode! valid values are: one, persys and perchan."); - return false; + return TA_PARAM_ERROR; } - return true; + return TA_PARAM_SUCCESS; } -bool pOutput(String val) { +TAParamResult pOutput(String val) { outName=val; e.setAudio(DIV_AUDIO_DUMMY); - return true; + return TA_PARAM_SUCCESS; } -bool pVGMOut(String val) { +TAParamResult pVGMOut(String val) { vgmOutName=val; e.setAudio(DIV_AUDIO_DUMMY); - return true; + return TA_PARAM_SUCCESS; } bool needsValue(String param) { @@ -283,7 +283,16 @@ int main(int argc, char** argv) { } for (size_t j=0; j Date: Mon, 25 Apr 2022 16:46:38 -0500 Subject: [PATCH 39/46] GUI: add two ImGui settings --- src/gui/gui.h | 4 ++++ src/gui/settings.cpp | 24 ++++++++++++++++++++++++ 2 files changed, 28 insertions(+) diff --git a/src/gui/gui.h b/src/gui/gui.h index 8c850fd0..98a86112 100644 --- a/src/gui/gui.h +++ b/src/gui/gui.h @@ -842,6 +842,8 @@ class FurnaceGUI { int notePreviewBehavior; int powerSave; int absorbInsInput; + int eventDelay; + int moveWindowTitle; unsigned int maxUndoSteps; String mainFontPath; String patFontPath; @@ -917,6 +919,8 @@ class FurnaceGUI { notePreviewBehavior(1), powerSave(1), absorbInsInput(0), + eventDelay(0), + moveWindowTitle(0), maxUndoSteps(100), mainFontPath(""), patFontPath(""), diff --git a/src/gui/settings.cpp b/src/gui/settings.cpp index c7a23511..61493722 100644 --- a/src/gui/settings.cpp +++ b/src/gui/settings.cpp @@ -313,6 +313,21 @@ void FurnaceGUI::drawSettings() { settings.sysFileDialog=sysFileDialogB; } + bool moveWindowTitleB=settings.moveWindowTitle; + if (ImGui::Checkbox("Only allow window movement when clicking on title bar",&moveWindowTitleB)) { + settings.moveWindowTitle=moveWindowTitleB; + applyUISettings(false); + } + + bool eventDelayB=settings.eventDelay; + if (ImGui::Checkbox("Enable event delay",&eventDelayB)) { + settings.eventDelay=eventDelayB; + applyUISettings(false); + } + if (ImGui::IsItemHovered()) { + ImGui::SetTooltip("may cause issues with high-polling-rate mice when previewing notes."); + } + bool powerSaveB=settings.powerSave; if (ImGui::Checkbox("Power-saving mode",&powerSaveB)) { settings.powerSave=powerSaveB; @@ -1651,6 +1666,8 @@ void FurnaceGUI::syncSettings() { settings.notePreviewBehavior=e->getConfInt("notePreviewBehavior",1); settings.powerSave=e->getConfInt("powerSave",POWER_SAVE_DEFAULT); settings.absorbInsInput=e->getConfInt("absorbInsInput",0); + settings.eventDelay=e->getConfInt("eventDelay",0); + settings.moveWindowTitle=e->getConfInt("moveWindowTitle",0); clampSetting(settings.mainFontSize,2,96); clampSetting(settings.patFontSize,2,96); @@ -1715,6 +1732,8 @@ void FurnaceGUI::syncSettings() { clampSetting(settings.notePreviewBehavior,0,3); clampSetting(settings.powerSave,0,1); clampSetting(settings.absorbInsInput,0,1); + clampSetting(settings.eventDelay,0,1); + clampSetting(settings.moveWindowTitle,0,1); // keybinds for (int i=0; isetConf("notePreviewBehavior",settings.notePreviewBehavior); e->setConf("powerSave",settings.powerSave); e->setConf("absorbInsInput",settings.absorbInsInput); + e->setConf("eventDelay",settings.eventDelay); + e->setConf("moveWindowTitle",settings.moveWindowTitle); // colors for (int i=0; i Date: Mon, 25 Apr 2022 17:55:48 -0500 Subject: [PATCH 40/46] dev87 - C64 changes issue #335 --- papers/format.md | 3 +++ src/engine/engine.h | 4 ++-- src/engine/fileOps.cpp | 30 ++++++++++++++++++++++++++---- src/engine/fileOpsIns.cpp | 10 ++++++++++ src/engine/instrument.cpp | 8 ++++++++ src/engine/platform/c64.cpp | 4 ++-- src/gui/insEdit.cpp | 12 ++++++------ 7 files changed, 57 insertions(+), 14 deletions(-) diff --git a/papers/format.md b/papers/format.md index 207b232a..083fd766 100644 --- a/papers/format.md +++ b/papers/format.md @@ -29,6 +29,7 @@ furthermore, an `or reserved` indicates this field is always present, but is res the format versions are: +- 87: Furnace dev87 - 86: Furnace dev86 - 85: Furnace dev85 - 84: Furnace dev84 @@ -423,9 +424,11 @@ size | description 1 | reserved (>=17) or duty macro height (>=15) or reserved 1 | reserved (>=17) or wave macro height (>=15) or reserved 4?? | volume macro + | - before version 87, if this is the C64 relative cutoff macro, its values were stored offset by 18. 4?? | arp macro | - before version 31, this macro's values were stored offset by 12. 4?? | duty macro + | - before version 87, if this is the C64 relative duty macro, its values were stored offset by 12. 4?? | wave macro 4?? | pitch macro (>=17) 4?? | extra 1 macro (>=17) diff --git a/src/engine/engine.h b/src/engine/engine.h index 57acf970..212bf422 100644 --- a/src/engine/engine.h +++ b/src/engine/engine.h @@ -43,8 +43,8 @@ #define BUSY_BEGIN_SOFT softLocked=true; isBusy.lock(); #define BUSY_END isBusy.unlock(); softLocked=false; -#define DIV_VERSION "dev86" -#define DIV_ENGINE_VERSION 86 +#define DIV_VERSION "dev87" +#define DIV_ENGINE_VERSION 87 // for imports #define DIV_VERSION_MOD 0xff01 diff --git a/src/engine/fileOps.cpp b/src/engine/fileOps.cpp index 38b33502..ca2f2d10 100644 --- a/src/engine/fileOps.cpp +++ b/src/engine/fileOps.cpp @@ -501,9 +501,9 @@ bool DivEngine::loadDMF(unsigned char* file, size_t len) { } else { ins->std.dutyMacro.val[j]=reader.readI(); } - if ((ds.system[0]==DIV_SYSTEM_C64_8580 || ds.system[0]==DIV_SYSTEM_C64_6581) && ins->std.dutyMacro.val[j]>24) { + /*if ((ds.system[0]==DIV_SYSTEM_C64_8580 || ds.system[0]==DIV_SYSTEM_C64_6581) && ins->std.dutyMacro.val[j]>24) { ins->std.dutyMacro.val[j]=24; - } + }*/ } if (ins->std.dutyMacro.len>0) { ins->std.dutyMacro.open=true; @@ -556,6 +556,16 @@ bool DivEngine::loadDMF(unsigned char* file, size_t len) { ins->c64.bp=reader.readC(); ins->c64.lp=reader.readC(); ins->c64.ch3off=reader.readC(); + + // weird storage + if (ins->c64.volIsCutoff) { + for (int j=0; jstd.volMacro.len; j++) { + ins->std.volMacro.val[j]-=18; + } + } + for (int j=0; jstd.dutyMacro.len; j++) { + ins->std.dutyMacro.val[j]-=12; + } } if (ds.system[0]==DIV_SYSTEM_GB && ds.version>0x11) { @@ -2609,7 +2619,13 @@ SafeWriter* DivEngine::saveDMF(unsigned char version) { } else { // STD if (sys!=DIV_SYSTEM_GB) { w->writeC(i->std.volMacro.len); - w->write(i->std.volMacro.val,4*i->std.volMacro.len); + if ((sys==DIV_SYSTEM_C64_6581 || sys==DIV_SYSTEM_C64_8580) && i->c64.volIsCutoff) { + for (int j=0; jstd.volMacro.len; j++) { + w->writeI(i->std.volMacro.val[j]+18); + } + } else { + w->write(i->std.volMacro.val,4*i->std.volMacro.len); + } if (i->std.volMacro.len>0) { w->writeC(i->std.volMacro.loop); } @@ -2629,7 +2645,13 @@ SafeWriter* DivEngine::saveDMF(unsigned char version) { w->writeC(i->std.arpMacro.mode); w->writeC(i->std.dutyMacro.len); - w->write(i->std.dutyMacro.val,4*i->std.dutyMacro.len); + if (sys==DIV_SYSTEM_C64_6581 || sys==DIV_SYSTEM_C64_8580) { + for (int j=0; jstd.dutyMacro.len; j++) { + w->writeI(i->std.dutyMacro.val[j]+12); + } + } else { + w->write(i->std.dutyMacro.val,4*i->std.dutyMacro.len); + } if (i->std.dutyMacro.len>0) { w->writeC(i->std.dutyMacro.loop); } diff --git a/src/engine/fileOpsIns.cpp b/src/engine/fileOpsIns.cpp index e080b779..1173d59a 100644 --- a/src/engine/fileOpsIns.cpp +++ b/src/engine/fileOpsIns.cpp @@ -345,6 +345,16 @@ void DivEngine::loadDMP(SafeReader& reader, std::vector& ret, St ins->c64.bp=reader.readC(); ins->c64.lp=reader.readC(); ins->c64.ch3off=reader.readC(); + + // weird storage + if (ins->c64.volIsCutoff) { + for (int j=0; jstd.volMacro.len; j++) { + ins->std.volMacro.val[j]-=18; + } + } + for (int j=0; jstd.dutyMacro.len; j++) { + ins->std.dutyMacro.val[j]-=12; + } } if (ins->type==DIV_INS_GB) { ins->gb.envVol=reader.readC(); diff --git a/src/engine/instrument.cpp b/src/engine/instrument.cpp index 273af9bf..d5061e0b 100644 --- a/src/engine/instrument.cpp +++ b/src/engine/instrument.cpp @@ -639,6 +639,14 @@ DivDataErrors DivInstrument::readInsData(SafeReader& reader, short version) { std.arpMacro.val[j]-=12; } } + if (type==DIV_INS_C64 && version<87) { + if (c64.volIsCutoff && !c64.filterIsAbs) for (int j=0; j=17) { reader.read(std.pitchMacro.val,4*std.pitchMacro.len); reader.read(std.ex1Macro.val,4*std.ex1Macro.len); diff --git a/src/engine/platform/c64.cpp b/src/engine/platform/c64.cpp index 604dcfe3..79cfb42b 100644 --- a/src/engine/platform/c64.cpp +++ b/src/engine/platform/c64.cpp @@ -131,7 +131,7 @@ void DivPlatformC64::tick(bool sysTick) { if (ins->c64.filterIsAbs) { filtCut=MIN(2047,chan[i].std.vol.val); } else { - filtCut-=((signed char)chan[i].std.vol.val-18)*7; + filtCut-=((signed char)chan[i].std.vol.val)*7; if (filtCut>2047) filtCut=2047; if (filtCut<0) filtCut=0; } @@ -161,7 +161,7 @@ void DivPlatformC64::tick(bool sysTick) { if (ins->c64.dutyIsAbs) { chan[i].duty=chan[i].std.duty.val; } else { - chan[i].duty-=((signed char)chan[i].std.duty.val-12)*4; + chan[i].duty-=((signed char)chan[i].std.duty.val)*4; } rWrite(i*7+2,chan[i].duty&0xff); rWrite(i*7+3,chan[i].duty>>8); diff --git a/src/gui/insEdit.cpp b/src/gui/insEdit.cpp index a3368e92..77781f9d 100644 --- a/src/gui/insEdit.cpp +++ b/src/gui/insEdit.cpp @@ -2687,8 +2687,8 @@ void FurnaceGUI::drawInsEdit() { if (ins->c64.filterIsAbs) { volMax=2047; } else { - volMin=-18; - volMax=18; + volMin=-64; + volMax=64; } } } @@ -2717,13 +2717,15 @@ void FurnaceGUI::drawInsEdit() { bool arpMode=ins->std.arpMacro.mode; const char* dutyLabel="Duty/Noise"; + int dutyMin=0; int dutyMax=3; if (ins->type==DIV_INS_C64) { dutyLabel="Duty"; if (ins->c64.dutyIsAbs) { dutyMax=4095; } else { - dutyMax=24; + dutyMin=-96; + dutyMax=96; } } if (ins->type==DIV_INS_FM) { @@ -2840,10 +2842,8 @@ void FurnaceGUI::drawInsEdit() { if (dutyMax>0) { if (ins->type==DIV_INS_MIKEY) { NORMAL_MACRO(ins->std.dutyMacro,0,dutyMax,"duty",dutyLabel,160,ins->std.dutyMacro.open,true,mikeyFeedbackBits,false,NULL,0,0,0,0,false,0,macroDummyMode,uiColors[GUI_COLOR_MACRO_OTHER],mmlString[2],0,dutyMax,NULL,false); - } else if (ins->type==DIV_INS_C64) { - NORMAL_MACRO(ins->std.dutyMacro,0,dutyMax,"duty",dutyLabel,160,ins->std.dutyMacro.open,false,NULL,false,NULL,0,0,0,0,false,0,macroDummyMode,uiColors[GUI_COLOR_MACRO_OTHER],mmlString[2],0,dutyMax,NULL,false); } else { - NORMAL_MACRO(ins->std.dutyMacro,0,dutyMax,"duty",dutyLabel,160,ins->std.dutyMacro.open,false,NULL,false,NULL,0,0,0,0,false,0,macroDummyMode,uiColors[GUI_COLOR_MACRO_OTHER],mmlString[2],0,dutyMax,NULL,false); + NORMAL_MACRO(ins->std.dutyMacro,dutyMin,dutyMax,"duty",dutyLabel,160,ins->std.dutyMacro.open,false,NULL,false,NULL,0,0,0,0,false,0,macroDummyMode,uiColors[GUI_COLOR_MACRO_OTHER],mmlString[2],dutyMin,dutyMax,NULL,false); } } if (waveMax>0) { From 3e1c2197ebf1f66f4396e57108f43d4f92a7cdfd Mon Sep 17 00:00:00 2001 From: tildearrow Date: Mon, 25 Apr 2022 18:04:13 -0500 Subject: [PATCH 41/46] OPL: fix #369 --- src/engine/platform/opl.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/engine/platform/opl.cpp b/src/engine/platform/opl.cpp index ef9adecf..fac157da 100644 --- a/src/engine/platform/opl.cpp +++ b/src/engine/platform/opl.cpp @@ -548,6 +548,9 @@ int DivPlatformOPL::dispatch(DivCommand c) { if (chan[c.chan].insChanged) { int ops=(slots[3][c.chan]!=255 && chan[c.chan].state.ops==4 && oplType==3)?4:2; chan[c.chan].fourOp=(ops==4); + if (chan[c.chan].fourOp) { + chan[c.chan+1].std.init(NULL); + } update4OpMask=true; for (int i=0; i Date: Mon, 25 Apr 2022 18:07:49 -0500 Subject: [PATCH 42/46] WonderSwan: fix noise phase reset --- src/engine/platform/swan.cpp | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/src/engine/platform/swan.cpp b/src/engine/platform/swan.cpp index b8f9a40d..86ad9b10 100644 --- a/src/engine/platform/swan.cpp +++ b/src/engine/platform/swan.cpp @@ -226,12 +226,14 @@ void DivPlatformSwan::tick(bool sysTick) { } } if (chan[3].std.duty.had) { - noise=chan[3].std.duty.val; - if (noise>0) { - rWrite(0x0e,((noise-1)&0x07)|0x18); - sndCtrl|=0x80; - } else { - sndCtrl&=~0x80; + if (noise!=chan[3].std.duty.val) { + noise=chan[3].std.duty.val; + if (noise>0) { + rWrite(0x0e,((noise-1)&0x07)|0x18); + sndCtrl|=0x80; + } else { + sndCtrl&=~0x80; + } } } rWrite(0x10,sndCtrl); From b3f1935f2b3f29f2188a6fa8a8f80a0522b9664d Mon Sep 17 00:00:00 2001 From: tildearrow Date: Mon, 25 Apr 2022 18:23:12 -0500 Subject: [PATCH 43/46] GUI: prepare for more UX improvements - add "temporary instrument" to DivEngine - prepare for two things: - instrument preview on file picker (at least built-in one) - instrument selector for banks (#364) --- src/engine/engine.cpp | 10 ++++++++++ src/engine/engine.h | 5 +++++ src/gui/gui.cpp | 2 +- 3 files changed, 16 insertions(+), 1 deletion(-) diff --git a/src/engine/engine.cpp b/src/engine/engine.cpp index c56eff9c..f77ab635 100644 --- a/src/engine/engine.cpp +++ b/src/engine/engine.cpp @@ -739,6 +739,7 @@ String DivEngine::getWarnings() { } DivInstrument* DivEngine::getIns(int index, DivInstrumentType fallbackType) { + if (index==-2 && tempIns!=NULL) return tempIns; if (index<0 || index>=song.insLen) { switch (fallbackType) { case DIV_INS_OPLL: @@ -1363,6 +1364,15 @@ int DivEngine::addInstrumentPtr(DivInstrument* which) { return song.insLen; } +void DivEngine::loadTempIns(DivInstrument* which) { + BUSY_BEGIN; + if (tempIns==NULL) { + tempIns=new DivInstrument; + } + memcpy(tempIns,which,sizeof(DivInstrument)); + BUSY_END; +} + void DivEngine::delInstrument(int index) { BUSY_BEGIN; saveLock.lock(); diff --git a/src/engine/engine.h b/src/engine/engine.h index 212bf422..5bbb1060 100644 --- a/src/engine/engine.h +++ b/src/engine/engine.h @@ -302,6 +302,7 @@ class DivEngine { public: DivSong song; + DivInstrument* tempIns; DivSystem sysOfChan[DIV_MAX_CHANS]; int dispatchOfChan[DIV_MAX_CHANS]; int dispatchChanOfChan[DIV_MAX_CHANS]; @@ -525,6 +526,9 @@ class DivEngine { // if the returned vector is empty then there was an error. std::vector instrumentFromFile(const char* path); + // load temporary instrument + void loadTempIns(DivInstrument* which); + // delete instrument void delInstrument(int index); @@ -798,6 +802,7 @@ class DivEngine { metroAmp(0.0f), metroVol(1.0f), totalProcessed(0), + tempIns(NULL), oscBuf{NULL,NULL}, oscSize(1), oscReadPos(0), diff --git a/src/gui/gui.cpp b/src/gui/gui.cpp index a3c41317..26e414e0 100644 --- a/src/gui/gui.cpp +++ b/src/gui/gui.cpp @@ -2419,7 +2419,7 @@ bool FurnaceGUI::loop() { } } - wantCaptureKeyboard=ImGui::GetIO().WantCaptureKeyboard; + wantCaptureKeyboard=ImGui::GetIO().WantTextInput; while (true) { midiLock.lock(); From 663e724111242d4734d76506091188b2545b4bfc Mon Sep 17 00:00:00 2001 From: tildearrow Date: Mon, 25 Apr 2022 18:41:24 -0500 Subject: [PATCH 44/46] I KNEW IT --- src/engine/engine.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/engine/engine.cpp b/src/engine/engine.cpp index f77ab635..4821df3a 100644 --- a/src/engine/engine.cpp +++ b/src/engine/engine.cpp @@ -1369,7 +1369,7 @@ void DivEngine::loadTempIns(DivInstrument* which) { if (tempIns==NULL) { tempIns=new DivInstrument; } - memcpy(tempIns,which,sizeof(DivInstrument)); + *tempIns=*which; BUSY_END; } From e8c9b645c53781cfe8863d70e14cb35944cabe48 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Mon, 25 Apr 2022 18:58:17 -0500 Subject: [PATCH 45/46] GUI: implement instrument preview in file dialog SADLY it's just for the built-in file dialog but i'll think of a solution for the external one --- extern/igfd/ImGuiFileDialog.cpp | 39 ++++++++++++++++++++++---------- extern/igfd/ImGuiFileDialog.h | 26 ++++++++++++++------- src/engine/engine.cpp | 6 +++-- src/engine/platform/amiga.h | 2 +- src/engine/platform/arcade.h | 2 +- src/engine/platform/ay.h | 3 ++- src/engine/platform/ay8930.h | 3 ++- src/engine/platform/bubsyswsg.h | 3 +-- src/engine/platform/c64.h | 4 ++-- src/engine/platform/fds.h | 4 ++-- src/engine/platform/gb.h | 4 ++-- src/engine/platform/genesis.h | 2 +- src/engine/platform/genesisext.h | 3 +-- src/engine/platform/lynx.h | 4 ++-- src/engine/platform/mmc5.h | 4 ++-- src/engine/platform/nes.h | 4 ++-- src/engine/platform/opl.h | 3 +-- src/engine/platform/opll.h | 3 +-- src/engine/platform/pce.h | 4 ++-- src/engine/platform/pcspkr.h | 4 ++-- src/engine/platform/pet.h | 3 +-- src/engine/platform/qsound.h | 3 +-- src/engine/platform/saa.h | 4 ++-- src/engine/platform/segapcm.h | 3 +-- src/engine/platform/sms.h | 3 +-- src/engine/platform/swan.h | 4 ++-- src/engine/platform/tia.h | 4 ++-- src/engine/platform/tx81z.h | 3 +-- src/engine/platform/vera.h | 4 ++-- src/engine/platform/vic20.h | 4 ++-- src/engine/platform/vrc6.h | 4 ++-- src/engine/platform/ym2610.h | 4 ++-- src/engine/platform/ym2610b.h | 4 ++-- src/engine/platform/ym2610bext.h | 3 +-- src/engine/platform/ym2610ext.h | 3 +-- src/gui/fileDialog.cpp | 4 ++-- src/gui/fileDialog.h | 4 +++- src/gui/gui.cpp | 10 +++++++- 38 files changed, 113 insertions(+), 85 deletions(-) diff --git a/extern/igfd/ImGuiFileDialog.cpp b/extern/igfd/ImGuiFileDialog.cpp index 3980bcd3..98d6c909 100644 --- a/extern/igfd/ImGuiFileDialog.cpp +++ b/extern/igfd/ImGuiFileDialog.cpp @@ -3295,7 +3295,8 @@ namespace IGFD const std::string& vFileName, const int& vCountSelectionMax, UserDatas vUserDatas, - ImGuiFileDialogFlags vFlags) + ImGuiFileDialogFlags vFlags, + SelectFun vSelectFun) { if (prFileDialogInternal.puShowDialog) // if already opened, quit return; @@ -3306,6 +3307,7 @@ namespace IGFD prFileDialogInternal.puDLGtitle = vTitle; prFileDialogInternal.puDLGuserDatas = vUserDatas; prFileDialogInternal.puDLGflags = vFlags; + prFileDialogInternal.puDLGselFun = vSelectFun; prFileDialogInternal.puDLGoptionsPane = nullptr; prFileDialogInternal.puDLGoptionsPaneWidth = 0.0f; prFileDialogInternal.puDLGmodal = false; @@ -3335,7 +3337,8 @@ namespace IGFD const std::string& vFilePathName, const int& vCountSelectionMax, UserDatas vUserDatas, - ImGuiFileDialogFlags vFlags) + ImGuiFileDialogFlags vFlags, + SelectFun vSelectFun) { if (prFileDialogInternal.puShowDialog) // if already opened, quit return; @@ -3348,6 +3351,7 @@ namespace IGFD prFileDialogInternal.puDLGoptionsPaneWidth = 0.0f; prFileDialogInternal.puDLGuserDatas = vUserDatas; prFileDialogInternal.puDLGflags = vFlags; + prFileDialogInternal.puDLGselFun = vSelectFun; prFileDialogInternal.puDLGmodal = false; auto ps = IGFD::Utils::ParsePathFileName(vFilePathName); @@ -3390,7 +3394,8 @@ namespace IGFD const float& vSidePaneWidth, const int& vCountSelectionMax, UserDatas vUserDatas, - ImGuiFileDialogFlags vFlags) + ImGuiFileDialogFlags vFlags, + SelectFun vSelectFun) { if (prFileDialogInternal.puShowDialog) // if already opened, quit return; @@ -3401,6 +3406,7 @@ namespace IGFD prFileDialogInternal.puDLGtitle = vTitle; prFileDialogInternal.puDLGuserDatas = vUserDatas; prFileDialogInternal.puDLGflags = vFlags; + prFileDialogInternal.puDLGselFun = vSelectFun; prFileDialogInternal.puDLGoptionsPane = vSidePane; prFileDialogInternal.puDLGoptionsPaneWidth = vSidePaneWidth; prFileDialogInternal.puDLGmodal = false; @@ -3435,7 +3441,8 @@ namespace IGFD const float& vSidePaneWidth, const int& vCountSelectionMax, UserDatas vUserDatas, - ImGuiFileDialogFlags vFlags) + ImGuiFileDialogFlags vFlags, + SelectFun vSelectFun) { if (prFileDialogInternal.puShowDialog) // if already opened, quit return; @@ -3448,6 +3455,7 @@ namespace IGFD prFileDialogInternal.puDLGoptionsPaneWidth = vSidePaneWidth; prFileDialogInternal.puDLGuserDatas = vUserDatas; prFileDialogInternal.puDLGflags = vFlags; + prFileDialogInternal.puDLGselFun = vSelectFun; prFileDialogInternal.puDLGmodal = false; auto ps = IGFD::Utils::ParsePathFileName(vFilePathName); @@ -3489,7 +3497,8 @@ namespace IGFD const std::string& vFileName, const int& vCountSelectionMax, UserDatas vUserDatas, - ImGuiFileDialogFlags vFlags) + ImGuiFileDialogFlags vFlags, + SelectFun vSelectFun) { if (prFileDialogInternal.puShowDialog) // if already opened, quit return; @@ -3497,7 +3506,7 @@ namespace IGFD OpenDialog( vKey, vTitle, vFilters, vPath, vFileName, - vCountSelectionMax, vUserDatas, vFlags); + vCountSelectionMax, vUserDatas, vFlags, vSelectFun); prFileDialogInternal.puDLGmodal = true; } @@ -3509,7 +3518,8 @@ namespace IGFD const std::string& vFilePathName, const int& vCountSelectionMax, UserDatas vUserDatas, - ImGuiFileDialogFlags vFlags) + ImGuiFileDialogFlags vFlags, + SelectFun vSelectFun) { if (prFileDialogInternal.puShowDialog) // if already opened, quit return; @@ -3517,7 +3527,7 @@ namespace IGFD OpenDialog( vKey, vTitle, vFilters, vFilePathName, - vCountSelectionMax, vUserDatas, vFlags); + vCountSelectionMax, vUserDatas, vFlags, vSelectFun); prFileDialogInternal.puDLGmodal = true; } @@ -3534,7 +3544,8 @@ namespace IGFD const float& vSidePaneWidth, const int& vCountSelectionMax, UserDatas vUserDatas, - ImGuiFileDialogFlags vFlags) + ImGuiFileDialogFlags vFlags, + SelectFun vSelectFun) { if (prFileDialogInternal.puShowDialog) // if already opened, quit return; @@ -3543,7 +3554,7 @@ namespace IGFD vKey, vTitle, vFilters, vPath, vFileName, vSidePane, vSidePaneWidth, - vCountSelectionMax, vUserDatas, vFlags); + vCountSelectionMax, vUserDatas, vFlags, vSelectFun); prFileDialogInternal.puDLGmodal = true; } @@ -3559,7 +3570,8 @@ namespace IGFD const float& vSidePaneWidth, const int& vCountSelectionMax, UserDatas vUserDatas, - ImGuiFileDialogFlags vFlags) + ImGuiFileDialogFlags vFlags, + SelectFun vSelectFun) { if (prFileDialogInternal.puShowDialog) // if already opened, quit return; @@ -3568,7 +3580,7 @@ namespace IGFD vKey, vTitle, vFilters, vFilePathName, vSidePane, vSidePaneWidth, - vCountSelectionMax, vUserDatas, vFlags); + vCountSelectionMax, vUserDatas, vFlags, vSelectFun); prFileDialogInternal.puDLGmodal = true; } @@ -3940,6 +3952,9 @@ namespace IGFD return 2; } else { fdi.SelectFileName(prFileDialogInternal, vInfos); + if (prFileDialogInternal.puDLGselFun!=NULL) { + prFileDialogInternal.puDLGselFun(GetFilePathName().c_str()); + } } } } diff --git a/extern/igfd/ImGuiFileDialog.h b/extern/igfd/ImGuiFileDialog.h index 2944bfbf..016bd9a1 100644 --- a/extern/igfd/ImGuiFileDialog.h +++ b/extern/igfd/ImGuiFileDialog.h @@ -1080,6 +1080,7 @@ namespace IGFD typedef void* UserDatas; typedef std::function PaneFun; // side pane function binding + typedef std::function SelectFun; // click on file function binding class FileDialogInternal { public: @@ -1103,6 +1104,7 @@ namespace IGFD ImGuiFileDialogFlags puDLGflags = ImGuiFileDialogFlags_None; UserDatas puDLGuserDatas = nullptr; PaneFun puDLGoptionsPane = nullptr; + SelectFun puDLGselFun = nullptr; float puDLGoptionsPaneWidth = 0.0f; bool puDLGmodal = false; bool puNeedToExitDialog = false; @@ -1155,7 +1157,8 @@ namespace IGFD const std::string& vFileName, // defaut file name const int& vCountSelectionMax = 1, // count selection max UserDatas vUserDatas = nullptr, // user datas (can be retrieved in pane) - ImGuiFileDialogFlags vFlags = 0); // ImGuiFileDialogFlags + ImGuiFileDialogFlags vFlags = 0, // ImGuiFileDialogFlags + SelectFun vSelectFun = nullptr); // function to be called on file click void OpenDialog( // open simple dialog (path and filename are obtained from filePathName) const std::string& vKey, // key dialog @@ -1164,7 +1167,8 @@ namespace IGFD const std::string& vFilePathName, // file path name (will be decompsoed in path and fileName) const int& vCountSelectionMax = 1, // count selection max UserDatas vUserDatas = nullptr, // user datas (can be retrieved in pane) - ImGuiFileDialogFlags vFlags = 0); // ImGuiFileDialogFlags + ImGuiFileDialogFlags vFlags = 0, // ImGuiFileDialogFlags + SelectFun vSelectFun = nullptr); // function to be called on file click // with pane void OpenDialog( // open dialog with custom right pane (path and fileName can be specified) @@ -1177,7 +1181,8 @@ namespace IGFD const float& vSidePaneWidth = 250.0f, // side pane width const int& vCountSelectionMax = 1, // count selection max UserDatas vUserDatas = nullptr, // user datas (can be retrieved in pane) - ImGuiFileDialogFlags vFlags = 0); // ImGuiFileDialogFlags + ImGuiFileDialogFlags vFlags = 0, // ImGuiFileDialogFlags + SelectFun vSelectFun = nullptr); // function to be called on file click void OpenDialog( // open dialog with custom right pane (path and filename are obtained from filePathName) const std::string& vKey, // key dialog @@ -1188,7 +1193,8 @@ namespace IGFD const float& vSidePaneWidth = 250.0f, // side pane width const int& vCountSelectionMax = 1, // count selection max UserDatas vUserDatas = nullptr, // user datas (can be retrieved in pane) - ImGuiFileDialogFlags vFlags = 0); // ImGuiFileDialogFlags + ImGuiFileDialogFlags vFlags = 0, // ImGuiFileDialogFlags + SelectFun vSelectFun = nullptr); // function to be called on file click // modal dialog void OpenModal( // open simple modal (path and fileName can be specified) @@ -1199,7 +1205,8 @@ namespace IGFD const std::string& vFileName, // defaut file name const int& vCountSelectionMax = 1, // count selection max UserDatas vUserDatas = nullptr, // user datas (can be retrieved in pane) - ImGuiFileDialogFlags vFlags = 0); // ImGuiFileDialogFlags + ImGuiFileDialogFlags vFlags = 0, // ImGuiFileDialogFlags + SelectFun vSelectFun = nullptr); // function to be called on file click void OpenModal( // open simple modal (path and fielname are obtained from filePathName) const std::string& vKey, // key dialog @@ -1208,7 +1215,8 @@ namespace IGFD const std::string& vFilePathName, // file path name (will be decompsoed in path and fileName) const int& vCountSelectionMax = 1, // count selection max UserDatas vUserDatas = nullptr, // user datas (can be retrieved in pane) - ImGuiFileDialogFlags vFlags = 0); // ImGuiFileDialogFlags + ImGuiFileDialogFlags vFlags = 0, // ImGuiFileDialogFlags + SelectFun vSelectFun = nullptr); // function to be called on file click // with pane void OpenModal( // open modal with custom right pane (path and filename are obtained from filePathName) @@ -1221,7 +1229,8 @@ namespace IGFD const float& vSidePaneWidth = 250.0f, // side pane width const int& vCountSelectionMax = 1, // count selection max UserDatas vUserDatas = nullptr, // user datas (can be retrieved in pane) - ImGuiFileDialogFlags vFlags = 0); // ImGuiFileDialogFlags + ImGuiFileDialogFlags vFlags = 0, // ImGuiFileDialogFlags + SelectFun vSelectFun = nullptr); // function to be called on file click void OpenModal( // open modal with custom right pane (path and fielname are obtained from filePathName) const std::string& vKey, // key dialog @@ -1232,7 +1241,8 @@ namespace IGFD const float& vSidePaneWidth = 250.0f, // side pane width const int& vCountSelectionMax = 1, // count selection max UserDatas vUserDatas = nullptr, // user datas (can be retrieved in pane) - ImGuiFileDialogFlags vFlags = 0); // ImGuiFileDialogFlags + ImGuiFileDialogFlags vFlags = 0, // ImGuiFileDialogFlags + SelectFun vSelectFun = nullptr); // function to be called on file click // Display / Close dialog form bool Display( // Display the dialog. return true if a result was obtained (Ok or not) diff --git a/src/engine/engine.cpp b/src/engine/engine.cpp index 4821df3a..80fbb5ca 100644 --- a/src/engine/engine.cpp +++ b/src/engine/engine.cpp @@ -739,7 +739,9 @@ String DivEngine::getWarnings() { } DivInstrument* DivEngine::getIns(int index, DivInstrumentType fallbackType) { - if (index==-2 && tempIns!=NULL) return tempIns; + if (index==-2 && tempIns!=NULL) { + return tempIns; + } if (index<0 || index>=song.insLen) { switch (fallbackType) { case DIV_INS_OPLL: @@ -2027,7 +2029,7 @@ void DivEngine::autoNoteOn(int ch, int ins, int note, int vol) { } do { - if ((ins<0 || ins>=song.insLen || getChannelType(finalChan)==4 || getPreferInsType(finalChan)==getIns(ins)->type || getIns(ins)->type==DIV_INS_AMIGA) && chan[finalChan].midiNote==-1) { + if ((ins==-1 || ins>=song.insLen || getChannelType(finalChan)==4 || getPreferInsType(finalChan)==getIns(ins)->type || getIns(ins)->type==DIV_INS_AMIGA) && chan[finalChan].midiNote==-1) { chan[finalChan].midiNote=note; pendingNotes.push(DivNoteEvent(finalChan,ins,note,vol,true)); break; diff --git a/src/engine/platform/amiga.h b/src/engine/platform/amiga.h index 07c44ee7..a4c1a7a4 100644 --- a/src/engine/platform/amiga.h +++ b/src/engine/platform/amiga.h @@ -34,7 +34,7 @@ class DivPlatformAmiga: public DivDispatch { int audSub; signed char audDat; int sample, wave; - unsigned char ins; + int ins; int busClock; int note; bool active, insChanged, freqChanged, keyOn, keyOff, inPorta, useWave, setPos, useV, useP; diff --git a/src/engine/platform/arcade.h b/src/engine/platform/arcade.h index 41af49e3..e051e8ab 100644 --- a/src/engine/platform/arcade.h +++ b/src/engine/platform/arcade.h @@ -37,7 +37,7 @@ class DivPlatformArcade: public DivDispatch { DivMacroInt std; unsigned char freqH, freqL; int freq, baseFreq, pitch, note; - unsigned char ins; + int ins; signed char konCycles; bool active, insChanged, freqChanged, keyOn, keyOff, inPorta, portaPause, furnacePCM, hardReset; int vol, outVol; diff --git a/src/engine/platform/ay.h b/src/engine/platform/ay.h index a78f21bc..ce06fc9f 100644 --- a/src/engine/platform/ay.h +++ b/src/engine/platform/ay.h @@ -33,7 +33,8 @@ class DivPlatformAY8910: public DivDispatch { struct Channel { unsigned char freqH, freqL; int freq, baseFreq, note, pitch; - unsigned char ins, psgMode, autoEnvNum, autoEnvDen; + int ins; + unsigned char psgMode, autoEnvNum, autoEnvDen; signed char konCycles; bool active, insChanged, freqChanged, keyOn, keyOff, portaPause, inPorta; int vol, outVol; diff --git a/src/engine/platform/ay8930.h b/src/engine/platform/ay8930.h index adbe0be8..61a186f4 100644 --- a/src/engine/platform/ay8930.h +++ b/src/engine/platform/ay8930.h @@ -29,7 +29,8 @@ class DivPlatformAY8930: public DivDispatch { struct Channel { unsigned char freqH, freqL; int freq, baseFreq, note, pitch; - unsigned char ins, psgMode, autoEnvNum, autoEnvDen, duty; + int ins; + unsigned char psgMode, autoEnvNum, autoEnvDen, duty; signed char konCycles; bool active, insChanged, freqChanged, keyOn, keyOff, portaPause, inPorta; int vol, outVol; diff --git a/src/engine/platform/bubsyswsg.h b/src/engine/platform/bubsyswsg.h index 2efcc4d9..0fbb2ac5 100644 --- a/src/engine/platform/bubsyswsg.h +++ b/src/engine/platform/bubsyswsg.h @@ -28,8 +28,7 @@ class DivPlatformBubSysWSG: public DivDispatch { struct Channel { - int freq, baseFreq, pitch, note; - unsigned char ins; + int freq, baseFreq, pitch, note, ins; bool active, insChanged, freqChanged, keyOn, keyOff, inPorta; signed char vol, outVol, wave; signed char waveROM[32] = {0}; // 4 bit PROM per channel on bubble system diff --git a/src/engine/platform/c64.h b/src/engine/platform/c64.h index f77e24b9..8df82051 100644 --- a/src/engine/platform/c64.h +++ b/src/engine/platform/c64.h @@ -26,8 +26,8 @@ class DivPlatformC64: public DivDispatch { struct Channel { - int freq, baseFreq, pitch, prevFreq, testWhen, note; - unsigned char ins, sweep, wave, attack, decay, sustain, release; + int freq, baseFreq, pitch, prevFreq, testWhen, note, ins; + unsigned char sweep, wave, attack, decay, sustain, release; short duty; bool active, insChanged, freqChanged, sweepChanged, keyOn, keyOff, inPorta, filter; bool resetMask, resetFilter, resetDuty, ring, sync; diff --git a/src/engine/platform/fds.h b/src/engine/platform/fds.h index 6d206f3e..bf949272 100644 --- a/src/engine/platform/fds.h +++ b/src/engine/platform/fds.h @@ -26,8 +26,8 @@ class DivPlatformFDS: public DivDispatch { struct Channel { - int freq, baseFreq, pitch, prevFreq, note, modFreq; - unsigned char ins, duty, sweep, modDepth, modPos; + int freq, baseFreq, pitch, prevFreq, note, modFreq, ins; + unsigned char duty, sweep, modDepth, modPos; bool active, insChanged, freqChanged, sweepChanged, keyOn, keyOff, inPorta, modOn; signed char vol, outVol, wave; signed char modTable[32]; diff --git a/src/engine/platform/gb.h b/src/engine/platform/gb.h index df7070ec..c79603ab 100644 --- a/src/engine/platform/gb.h +++ b/src/engine/platform/gb.h @@ -27,8 +27,8 @@ class DivPlatformGB: public DivDispatch { struct Channel { - int freq, baseFreq, pitch, note; - unsigned char ins, duty, sweep; + int freq, baseFreq, pitch, note, ins; + unsigned char duty, sweep; bool active, insChanged, freqChanged, sweepChanged, keyOn, keyOff, inPorta; signed char vol, outVol, wave; DivMacroInt std; diff --git a/src/engine/platform/genesis.h b/src/engine/platform/genesis.h index aa7659fa..31f11d18 100644 --- a/src/engine/platform/genesis.h +++ b/src/engine/platform/genesis.h @@ -37,7 +37,7 @@ class DivPlatformGenesis: public DivDispatch { DivMacroInt std; unsigned char freqH, freqL; int freq, baseFreq, pitch, portaPauseFreq, note; - unsigned char ins; + int ins; bool active, insChanged, freqChanged, keyOn, keyOff, portaPause, furnaceDac, inPorta, hardReset; int vol, outVol; unsigned char pan; diff --git a/src/engine/platform/genesisext.h b/src/engine/platform/genesisext.h index 4cdf9fc6..e3604a34 100644 --- a/src/engine/platform/genesisext.h +++ b/src/engine/platform/genesisext.h @@ -25,8 +25,7 @@ class DivPlatformGenesisExt: public DivPlatformGenesis { struct OpChannel { DivMacroInt std; unsigned char freqH, freqL; - int freq, baseFreq, pitch, portaPauseFreq; - unsigned char ins; + int freq, baseFreq, pitch, portaPauseFreq, ins; signed char konCycles; bool active, insChanged, freqChanged, keyOn, keyOff, portaPause; int vol; diff --git a/src/engine/platform/lynx.h b/src/engine/platform/lynx.h index 61f6e67d..f1f974ed 100644 --- a/src/engine/platform/lynx.h +++ b/src/engine/platform/lynx.h @@ -44,8 +44,8 @@ class DivPlatformLynx: public DivDispatch { DivMacroInt std; MikeyFreqDiv fd; MikeyDuty duty; - int baseFreq, pitch, note, actualNote, lfsr; - unsigned char ins, pan; + int baseFreq, pitch, note, actualNote, lfsr, ins; + unsigned char pan; bool active, insChanged, freqChanged, keyOn, keyOff, inPorta; signed char vol, outVol; Channel(): diff --git a/src/engine/platform/mmc5.h b/src/engine/platform/mmc5.h index 02ca06e8..879ebee1 100644 --- a/src/engine/platform/mmc5.h +++ b/src/engine/platform/mmc5.h @@ -25,8 +25,8 @@ class DivPlatformMMC5: public DivDispatch { struct Channel { - int freq, baseFreq, pitch, prevFreq, note; - unsigned char ins, duty, sweep; + int freq, baseFreq, pitch, prevFreq, note, ins; + unsigned char duty, sweep; bool active, insChanged, freqChanged, sweepChanged, keyOn, keyOff, inPorta, furnaceDac; signed char vol, outVol, wave; DivMacroInt std; diff --git a/src/engine/platform/nes.h b/src/engine/platform/nes.h index 2cf2a8a1..7650d39e 100644 --- a/src/engine/platform/nes.h +++ b/src/engine/platform/nes.h @@ -25,8 +25,8 @@ class DivPlatformNES: public DivDispatch { struct Channel { - int freq, baseFreq, pitch, prevFreq, note; - unsigned char ins, duty, sweep; + int freq, baseFreq, pitch, prevFreq, note, ins; + unsigned char duty, sweep; bool active, insChanged, freqChanged, sweepChanged, keyOn, keyOff, inPorta, furnaceDac; signed char vol, outVol, wave; DivMacroInt std; diff --git a/src/engine/platform/opl.h b/src/engine/platform/opl.h index 76979a54..ee3bcb36 100644 --- a/src/engine/platform/opl.h +++ b/src/engine/platform/opl.h @@ -30,8 +30,7 @@ class DivPlatformOPL: public DivDispatch { DivInstrumentFM state; DivMacroInt std; unsigned char freqH, freqL; - int freq, baseFreq, pitch, note; - unsigned char ins; + int freq, baseFreq, pitch, note, ins; bool active, insChanged, freqChanged, keyOn, keyOff, portaPause, furnaceDac, inPorta, fourOp; int vol, outVol; unsigned char pan; diff --git a/src/engine/platform/opll.h b/src/engine/platform/opll.h index 04f4f13a..79f905c6 100644 --- a/src/engine/platform/opll.h +++ b/src/engine/platform/opll.h @@ -33,8 +33,7 @@ class DivPlatformOPLL: public DivDispatch { DivInstrumentFM state; DivMacroInt std; unsigned char freqH, freqL; - int freq, baseFreq, pitch, note; - unsigned char ins; + int freq, baseFreq, pitch, note, ins; bool active, insChanged, freqChanged, keyOn, keyOff, portaPause, furnaceDac, inPorta; int vol, outVol; unsigned char pan; diff --git a/src/engine/platform/pce.h b/src/engine/platform/pce.h index 57a4b033..deb85fbb 100644 --- a/src/engine/platform/pce.h +++ b/src/engine/platform/pce.h @@ -31,8 +31,8 @@ class DivPlatformPCE: public DivDispatch { int freq, baseFreq, pitch, note; int dacPeriod, dacRate; unsigned int dacPos; - int dacSample; - unsigned char ins, pan; + int dacSample, ins; + unsigned char pan; bool active, insChanged, freqChanged, keyOn, keyOff, inPorta, noise, pcm, furnaceDac; signed char vol, outVol, wave; DivMacroInt std; diff --git a/src/engine/platform/pcspkr.h b/src/engine/platform/pcspkr.h index 68e6bf1a..f8813f3f 100644 --- a/src/engine/platform/pcspkr.h +++ b/src/engine/platform/pcspkr.h @@ -25,8 +25,8 @@ class DivPlatformPCSpeaker: public DivDispatch { struct Channel { - int freq, baseFreq, pitch, note; - unsigned char ins, duty, sweep; + int freq, baseFreq, pitch, note, ins; + unsigned char duty, sweep; bool active, insChanged, freqChanged, sweepChanged, keyOn, keyOff, inPorta, furnaceDac; signed char vol, outVol, wave; DivMacroInt std; diff --git a/src/engine/platform/pet.h b/src/engine/platform/pet.h index 7647a87f..b4b75316 100644 --- a/src/engine/platform/pet.h +++ b/src/engine/platform/pet.h @@ -25,8 +25,7 @@ class DivPlatformPET: public DivDispatch { struct Channel { - int freq, baseFreq, pitch, note; - unsigned char ins; + int freq, baseFreq, pitch, note, ins; bool active, insChanged, freqChanged, keyOn, keyOff, inPorta; int vol, outVol, wave; unsigned char sreg; diff --git a/src/engine/platform/qsound.h b/src/engine/platform/qsound.h index 62b1d647..aa07a3b4 100644 --- a/src/engine/platform/qsound.h +++ b/src/engine/platform/qsound.h @@ -30,8 +30,7 @@ class DivPlatformQSound: public DivDispatch { int freq, baseFreq, pitch; unsigned short audLen; unsigned int audPos; - int sample, wave; - unsigned char ins; + int sample, wave, ins; int note; int panning; bool active, insChanged, freqChanged, keyOn, keyOff, inPorta, useWave; diff --git a/src/engine/platform/saa.h b/src/engine/platform/saa.h index 0542ff51..30909293 100644 --- a/src/engine/platform/saa.h +++ b/src/engine/platform/saa.h @@ -35,8 +35,8 @@ class DivPlatformSAA1099: public DivDispatch { protected: struct Channel { unsigned char freqH, freqL; - int freq, baseFreq, pitch, note; - unsigned char ins, psgMode; + int freq, baseFreq, pitch, note, ins; + unsigned char psgMode; signed char konCycles; bool active, insChanged, freqChanged, keyOn, keyOff, portaPause, inPorta; int vol, outVol; diff --git a/src/engine/platform/segapcm.h b/src/engine/platform/segapcm.h index 4b05c160..3a68a34b 100644 --- a/src/engine/platform/segapcm.h +++ b/src/engine/platform/segapcm.h @@ -29,8 +29,7 @@ class DivPlatformSegaPCM: public DivDispatch { struct Channel { DivMacroInt std; unsigned char freqH, freqL; - int freq, baseFreq, pitch, note; - unsigned char ins; + int freq, baseFreq, pitch, note, ins; signed char konCycles; bool active, insChanged, freqChanged, keyOn, keyOff, inPorta, portaPause, furnacePCM; int vol, outVol; diff --git a/src/engine/platform/sms.h b/src/engine/platform/sms.h index 0aeb9e81..66fdda85 100644 --- a/src/engine/platform/sms.h +++ b/src/engine/platform/sms.h @@ -26,8 +26,7 @@ class DivPlatformSMS: public DivDispatch { struct Channel { - int freq, baseFreq, pitch, note, actualNote; - unsigned char ins; + int freq, baseFreq, pitch, note, actualNote, ins; bool active, insChanged, freqChanged, keyOn, keyOff, inPorta; signed char vol, outVol; DivMacroInt std; diff --git a/src/engine/platform/swan.h b/src/engine/platform/swan.h index 0f1643c5..fdad150b 100644 --- a/src/engine/platform/swan.h +++ b/src/engine/platform/swan.h @@ -28,8 +28,8 @@ class DivPlatformSwan: public DivDispatch { struct Channel { - int freq, baseFreq, pitch, note; - unsigned char ins, pan; + int freq, baseFreq, pitch, note, ins; + unsigned char pan; bool active, insChanged, freqChanged, keyOn, keyOff, inPorta; int vol, outVol, wave; DivMacroInt std; diff --git a/src/engine/platform/tia.h b/src/engine/platform/tia.h index 3ec3c7f3..602cc6f1 100644 --- a/src/engine/platform/tia.h +++ b/src/engine/platform/tia.h @@ -27,8 +27,8 @@ class DivPlatformTIA: public DivDispatch { protected: struct Channel { - int freq, baseFreq, pitch, note; - unsigned char ins, shape; + int freq, baseFreq, pitch, note, ins; + unsigned char shape; signed char konCycles; bool active, insChanged, freqChanged, keyOn, keyOff, portaPause, inPorta; int vol, outVol; diff --git a/src/engine/platform/tx81z.h b/src/engine/platform/tx81z.h index c61b4bfe..d9261ebd 100644 --- a/src/engine/platform/tx81z.h +++ b/src/engine/platform/tx81z.h @@ -35,8 +35,7 @@ class DivPlatformTX81Z: public DivDispatch { DivInstrumentFM state; DivMacroInt std; unsigned char freqH, freqL; - int freq, baseFreq, pitch, note; - unsigned char ins; + int freq, baseFreq, pitch, note, ins; signed char konCycles; bool active, insChanged, freqChanged, keyOn, keyOff, inPorta, portaPause, furnacePCM, hardReset; int vol, outVol; diff --git a/src/engine/platform/vera.h b/src/engine/platform/vera.h index ec6dcde1..64dee5c7 100644 --- a/src/engine/platform/vera.h +++ b/src/engine/platform/vera.h @@ -29,8 +29,8 @@ struct VERA_PCM; class DivPlatformVERA: public DivDispatch { protected: struct Channel { - int freq, baseFreq, pitch, note; - unsigned char ins, pan; + int freq, baseFreq, pitch, note, ins; + unsigned char pan; bool active, freqChanged, inPorta; int vol, outVol; unsigned accum; diff --git a/src/engine/platform/vic20.h b/src/engine/platform/vic20.h index 7948c5ef..8c15c35d 100644 --- a/src/engine/platform/vic20.h +++ b/src/engine/platform/vic20.h @@ -27,8 +27,8 @@ class DivPlatformVIC20: public DivDispatch { struct Channel { - int freq, baseFreq, pitch, note; - unsigned char ins, pan; + int freq, baseFreq, pitch, note, ins; + unsigned char pan; bool active, insChanged, freqChanged, keyOn, keyOff, inPorta; int vol, outVol, wave, waveWriteCycle; DivMacroInt std; diff --git a/src/engine/platform/vrc6.h b/src/engine/platform/vrc6.h index d28265a4..a44e9d33 100644 --- a/src/engine/platform/vrc6.h +++ b/src/engine/platform/vrc6.h @@ -31,8 +31,8 @@ class DivPlatformVRC6: public DivDispatch { int freq, baseFreq, pitch, note; int dacPeriod, dacRate, dacOut; unsigned int dacPos; - int dacSample; - unsigned char ins, duty; + int dacSample, ins; + unsigned char duty; bool active, insChanged, freqChanged, keyOn, keyOff, inPorta, pcm, furnaceDac; signed char vol, outVol; DivMacroInt std; diff --git a/src/engine/platform/ym2610.h b/src/engine/platform/ym2610.h index e75e24d7..af947199 100644 --- a/src/engine/platform/ym2610.h +++ b/src/engine/platform/ym2610.h @@ -43,8 +43,8 @@ class DivPlatformYM2610: public DivDispatch { struct Channel { DivInstrumentFM state; unsigned char freqH, freqL; - int freq, baseFreq, pitch, note; - unsigned char ins, psgMode, autoEnvNum, autoEnvDen; + int freq, baseFreq, pitch, note, ins; + unsigned char psgMode, autoEnvNum, autoEnvDen; signed char konCycles; bool active, insChanged, freqChanged, keyOn, keyOff, portaPause, inPorta, furnacePCM, hardReset; int vol, outVol; diff --git a/src/engine/platform/ym2610b.h b/src/engine/platform/ym2610b.h index 6d46ecb2..d2363ed1 100644 --- a/src/engine/platform/ym2610b.h +++ b/src/engine/platform/ym2610b.h @@ -35,8 +35,8 @@ class DivPlatformYM2610B: public DivDispatch { struct Channel { DivInstrumentFM state; unsigned char freqH, freqL; - int freq, baseFreq, pitch, note; - unsigned char ins, psgMode, autoEnvNum, autoEnvDen; + int freq, baseFreq, pitch, note, ins; + unsigned char psgMode, autoEnvNum, autoEnvDen; signed char konCycles; bool active, insChanged, freqChanged, keyOn, keyOff, portaPause, inPorta, furnacePCM, hardReset; int vol, outVol; diff --git a/src/engine/platform/ym2610bext.h b/src/engine/platform/ym2610bext.h index e908916e..c2cd4fb3 100644 --- a/src/engine/platform/ym2610bext.h +++ b/src/engine/platform/ym2610bext.h @@ -25,8 +25,7 @@ class DivPlatformYM2610BExt: public DivPlatformYM2610B { struct OpChannel { DivMacroInt std; unsigned char freqH, freqL; - int freq, baseFreq, pitch; - unsigned char ins; + int freq, baseFreq, pitch, ins; signed char konCycles; bool active, insChanged, freqChanged, keyOn, keyOff, portaPause; int vol; diff --git a/src/engine/platform/ym2610ext.h b/src/engine/platform/ym2610ext.h index 9fa44d0b..b8922912 100644 --- a/src/engine/platform/ym2610ext.h +++ b/src/engine/platform/ym2610ext.h @@ -25,8 +25,7 @@ class DivPlatformYM2610Ext: public DivPlatformYM2610 { struct OpChannel { DivMacroInt std; unsigned char freqH, freqL; - int freq, baseFreq, pitch; - unsigned char ins; + int freq, baseFreq, pitch, ins; signed char konCycles; bool active, insChanged, freqChanged, keyOn, keyOff, portaPause; int vol; diff --git a/src/gui/fileDialog.cpp b/src/gui/fileDialog.cpp index 7bfafb4a..3b8fdd46 100644 --- a/src/gui/fileDialog.cpp +++ b/src/gui/fileDialog.cpp @@ -4,7 +4,7 @@ #include "../../extern/pfd-fixed/portable-file-dialogs.h" -bool FurnaceGUIFileDialog::openLoad(String header, std::vector filter, const char* noSysFilter, String path, double dpiScale) { +bool FurnaceGUIFileDialog::openLoad(String header, std::vector filter, const char* noSysFilter, String path, double dpiScale, FileDialogSelectCallback clickCallback) { if (opened) return false; saving=false; curPath=path; @@ -13,7 +13,7 @@ bool FurnaceGUIFileDialog::openLoad(String header, std::vector filter, c dialogO=new pfd::open_file(header,path,filter); } else { ImGuiFileDialog::Instance()->DpiScale=dpiScale; - ImGuiFileDialog::Instance()->OpenModal("FileDialog",header,noSysFilter,path); + ImGuiFileDialog::Instance()->OpenModal("FileDialog",header,noSysFilter,path,1,nullptr,0,clickCallback); } opened=true; return true; diff --git a/src/gui/fileDialog.h b/src/gui/fileDialog.h index 0dcd82a6..6c5f2faa 100644 --- a/src/gui/fileDialog.h +++ b/src/gui/fileDialog.h @@ -7,6 +7,8 @@ namespace pfd { class save_file; } +typedef std::function FileDialogSelectCallback; + class FurnaceGUIFileDialog { bool sysDialog; bool opened; @@ -16,7 +18,7 @@ class FurnaceGUIFileDialog { pfd::open_file* dialogO; pfd::save_file* dialogS; public: - bool openLoad(String header, std::vector filter, const char* noSysFilter, String path, double dpiScale); + bool openLoad(String header, std::vector filter, const char* noSysFilter, String path, double dpiScale, FileDialogSelectCallback clickCallback=NULL); bool openSave(String header, std::vector filter, const char* noSysFilter, String path, double dpiScale); bool accepted(); void close(); diff --git a/src/gui/gui.cpp b/src/gui/gui.cpp index 26e414e0..4aa37b81 100644 --- a/src/gui/gui.cpp +++ b/src/gui/gui.cpp @@ -1293,7 +1293,15 @@ void FurnaceGUI::openFileDialog(FurnaceGUIFileDialogs type) { "all files", ".*"}, "compatible files{.fui,.dmp,.tfi,.vgi,.s3i,.sbi,.opli,.opni,.y12,.bnk,.ff,.opm},.*", workingDirIns, - dpiScale + dpiScale, + [this](const char* path) { + std::vector instruments=e->instrumentFromFile(path); + if (!instruments.empty()) { + e->loadTempIns(instruments[0]); + curIns=-2; + } + for (DivInstrument* i: instruments) delete i; + } ); break; case GUI_FILE_INS_SAVE: From 584e97f313b4337bfc7fc1389d390386aaf72d51 Mon Sep 17 00:00:00 2001 From: cam900 Date: Tue, 26 Apr 2022 09:20:41 +0900 Subject: [PATCH 46/46] Missing header --- src/gui/fileDialog.h | 1 + 1 file changed, 1 insertion(+) diff --git a/src/gui/fileDialog.h b/src/gui/fileDialog.h index 6c5f2faa..8d3ff787 100644 --- a/src/gui/fileDialog.h +++ b/src/gui/fileDialog.h @@ -1,5 +1,6 @@ #include "../ta-utils.h" #include "imgui.h" +#include #include namespace pfd {