diff --git a/CMakeLists.txt b/CMakeLists.txt index 171529a98..d4305e28d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -310,6 +310,7 @@ src/engine/platform/ay8930.cpp src/engine/platform/tia.cpp src/engine/platform/saa.cpp src/engine/platform/amiga.cpp +src/engine/platform/segapcm.cpp src/engine/platform/qsound.cpp src/engine/platform/dummy.cpp src/engine/platform/lynx.cpp diff --git a/demos/m7 vibe.fur b/demos/m7 vibe.fur new file mode 100644 index 000000000..e9870ad2c Binary files /dev/null and b/demos/m7 vibe.fur differ diff --git a/extern/igfd/ImGuiFileDialog.cpp b/extern/igfd/ImGuiFileDialog.cpp index 3300d4e0b..5be457930 100644 --- a/extern/igfd/ImGuiFileDialog.cpp +++ b/extern/igfd/ImGuiFileDialog.cpp @@ -1105,7 +1105,7 @@ namespace IGFD bool needToApllyNewFilter = false; - ImGui::PushItemWidth(FILTER_COMBO_WIDTH); + ImGui::PushItemWidth(FILTER_COMBO_WIDTH*FileDialog::Instance()->DpiScale); if (ImGui::BeginCombo("##Filters", prSelectedFilter.filter.c_str(), ImGuiComboFlags_None)) { intptr_t i = 0; @@ -3279,7 +3279,7 @@ namespace IGFD //// FILE DIALOG CONSTRUCTOR / DESTRUCTOR /////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////// - IGFD::FileDialog::FileDialog() : BookMarkFeature(), KeyExplorerFeature(), ThumbnailFeature() {} + IGFD::FileDialog::FileDialog() : BookMarkFeature(), KeyExplorerFeature(), ThumbnailFeature() {DpiScale=1.0f;} IGFD::FileDialog::~FileDialog() = default; ////////////////////////////////////////////////////////////////////////////////////////////////// @@ -3827,8 +3827,9 @@ namespace IGFD // Input file fields float width = ImGui::GetContentRegionAvail().x; + // fix this! fix this! fix this! if (!fdFile.puDLGDirectoryMode) - width -= FILTER_COMBO_WIDTH; + width -= FILTER_COMBO_WIDTH*DpiScale; ImGui::PushItemWidth(width); ImGui::InputText("##FileName", fdFile.puFileNameBuffer, MAX_FILE_DIALOG_NAME_BUFFER); if (ImGui::GetItemID() == ImGui::GetActiveID()) diff --git a/extern/igfd/ImGuiFileDialog.h b/extern/igfd/ImGuiFileDialog.h index 0b69f8306..2944bfbf4 100644 --- a/extern/igfd/ImGuiFileDialog.h +++ b/extern/igfd/ImGuiFileDialog.h @@ -1133,6 +1133,7 @@ namespace IGFD public: bool puAnyWindowsHovered = false; // not remember why haha :) todo : to check if we can remove + double DpiScale; public: static FileDialog* Instance() // Singleton for easier accces form anywhere but only one dialog at a time diff --git a/src/engine/dispatch.h b/src/engine/dispatch.h index f2887fc22..300b0033b 100644 --- a/src/engine/dispatch.h +++ b/src/engine/dispatch.h @@ -225,7 +225,7 @@ class DivDispatch { /** * get the bit depth of the register pool of this dispatch. - * If the result is 16, it should be casted to unsigned short + * If the result is 16, it should be casted to unsigned short. * @return the depth. Default value is 8 */ virtual int getRegisterPoolDepth(); diff --git a/src/engine/dispatchContainer.cpp b/src/engine/dispatchContainer.cpp index a44e049c8..801a3d84f 100644 --- a/src/engine/dispatchContainer.cpp +++ b/src/engine/dispatchContainer.cpp @@ -34,6 +34,7 @@ #include "platform/tia.h" #include "platform/saa.h" #include "platform/amiga.h" +#include "platform/segapcm.h" #include "platform/qsound.h" #include "platform/dummy.h" #include "platform/lynx.h" @@ -140,12 +141,11 @@ void DivDispatchContainer::init(DivSystem sys, DivEngine* eng, int chanCount, do bbInLen=32768; switch (sys) { - case DIV_SYSTEM_GENESIS: case DIV_SYSTEM_YM2612: dispatch=new DivPlatformGenesis; ((DivPlatformGenesis*)dispatch)->setYMFM(eng->getConfInt("ym2612Core",0)); break; - case DIV_SYSTEM_GENESIS_EXT: + case DIV_SYSTEM_YM2612_EXT: dispatch=new DivPlatformGenesisExt; ((DivPlatformGenesisExt*)dispatch)->setYMFM(eng->getConfInt("ym2612Core",0)); break; @@ -169,7 +169,6 @@ void DivDispatchContainer::init(DivSystem sys, DivEngine* eng, int chanCount, do dispatch=new DivPlatformC64; ((DivPlatformC64*)dispatch)->setChipModel(false); break; - case DIV_SYSTEM_ARCADE: case DIV_SYSTEM_YM2151: dispatch=new DivPlatformArcade; ((DivPlatformArcade*)dispatch)->setYMFM(eng->getConfInt("arcadeCore",0)==0); @@ -207,6 +206,10 @@ void DivDispatchContainer::init(DivSystem sys, DivEngine* eng, int chanCount, do case DIV_SYSTEM_QSOUND: dispatch=new DivPlatformQSound; break; + case DIV_SYSTEM_SEGAPCM: + case DIV_SYSTEM_SEGAPCM_COMPAT: + dispatch=new DivPlatformSegaPCM; + break; default: logW("this system is not supported yet! using dummy platform.\n"); dispatch=new DivPlatformDummy; diff --git a/src/engine/engine.cpp b/src/engine/engine.cpp index 5093af2e9..8b4883e92 100644 --- a/src/engine/engine.cpp +++ b/src/engine/engine.cpp @@ -437,11 +437,11 @@ void DivEngine::notifyWaveChange(int wave) { // ADPCM code attribution: https://wiki.neogeodev.org/index.php?title=ADPCM_codecs static short adSteps[49]={ - 16, 17, 19, 21, 23, 25, 28, 31, 34, 37, - 41, 45, 50, 55, 60, 66, 73, 80, 88, 97, - 107, 118, 130, 143, 157, 173, 190, 209, 230, 253, - 279, 307, 337, 371, 408, 449, 494, 544, 598, 658, - 724, 796, 876, 963, 1060, 1166, 1282, 1411, 1552 + 16, 17, 19, 21, 23, 25, 28, 31, 34, 37, + 41, 45, 50, 55, 60, 66, 73, 80, 88, 97, + 107, 118, 130, 143, 157, 173, 190, 209, 230, 253, + 279, 307, 337, 371, 408, 449, 494, 544, 598, 658, + 724, 796, 876, 963, 1060, 1166, 1282, 1411, 1552 }; static int adStepSeek[16]={ @@ -606,9 +606,10 @@ void DivEngine::renderSamples() { memPos=0; for (int i=0; irendLength; - if(length > 65536-16) - length = 65536-16; + int length = s->rendLength; + if (length > 65536-16) { + length = 65536-16; + } if ((memPos&0xff0000)!=((memPos+length)&0xff0000)) { memPos=(memPos+0xffff)&0xff0000; } @@ -617,16 +618,14 @@ void DivEngine::renderSamples() { break; } if (memPos+length>=16777216) { - for(unsigned int i=0; i<16777216-(memPos+length); i++) - { - qsoundMem[(memPos + i) ^ 0x8000] = s->rendData[i] >> ((s->depth == 16) ? 8 : 0); - } + for (unsigned int i=0; i<16777216-(memPos+length); i++) { + qsoundMem[(memPos + i) ^ 0x8000] = s->rendData[i] >> ((s->depth == 16) ? 8 : 0); + } logW("out of QSound PCM memory for sample %d!\n",i); } else { - for(int i=0; irendData[i] >> ((s->depth == 16) ? 8 : 0); - } + for (int i=0; irendData[i] >> ((s->depth == 16) ? 8 : 0); + } } s->rendOffQsound=memPos ^ 0x8000; memPos+=length+16; @@ -1024,11 +1023,11 @@ int DivEngine::getEffectiveSampleRate(int rate) { switch (song.system[0]) { case DIV_SYSTEM_YMU759: return 8000; - case DIV_SYSTEM_GENESIS: case DIV_SYSTEM_GENESIS_EXT: + case DIV_SYSTEM_YM2612: case DIV_SYSTEM_YM2612_EXT: return 1278409/(1280000/rate); case DIV_SYSTEM_PCE: return 1789773/(1789773/rate); - case DIV_SYSTEM_ARCADE: + case DIV_SYSTEM_SEGAPCM: case DIV_SYSTEM_SEGAPCM_COMPAT: return (31250*MIN(255,(rate*255/31250)))/255; case DIV_SYSTEM_YM2610: case DIV_SYSTEM_YM2610_EXT: case DIV_SYSTEM_YM2610_FULL: case DIV_SYSTEM_YM2610_FULL_EXT: return 18518; diff --git a/src/engine/engine.h b/src/engine/engine.h index bb83fa510..6e7eefcf5 100644 --- a/src/engine/engine.h +++ b/src/engine/engine.h @@ -365,6 +365,9 @@ class DivEngine { // get preferred instrument type DivInstrumentType getPreferInsType(int ch); + // get song system name + const char* getSongSystemName(); + // get sys name const char* getSystemName(DivSystem sys); @@ -684,7 +687,7 @@ class DivEngine { adpcmMemLen(0), adpcmBMem(NULL), adpcmBMemLen(0), - qsoundMem(NULL), - qsoundMemLen(0) {} + qsoundMem(NULL), + qsoundMemLen(0) {} }; #endif diff --git a/src/engine/fileOps.cpp b/src/engine/fileOps.cpp index b02579331..743d55866 100644 --- a/src/engine/fileOps.cpp +++ b/src/engine/fileOps.cpp @@ -649,7 +649,24 @@ bool DivEngine::loadDMF(unsigned char* file, size_t len) { } } - // handle special systems + // handle compound systems + if (ds.system[0]==DIV_SYSTEM_GENESIS) { + ds.systemLen=2; + ds.system[0]=DIV_SYSTEM_YM2612; + ds.system[1]=DIV_SYSTEM_SMS; + ds.systemVol[1]=24; + } + if (ds.system[0]==DIV_SYSTEM_GENESIS_EXT) { + ds.systemLen=2; + ds.system[0]=DIV_SYSTEM_YM2612_EXT; + ds.system[1]=DIV_SYSTEM_SMS; + ds.systemVol[1]=24; + } + if (ds.system[0]==DIV_SYSTEM_ARCADE) { + ds.systemLen=2; + ds.system[0]=DIV_SYSTEM_YM2151; + ds.system[1]=DIV_SYSTEM_SEGAPCM_COMPAT; + } if (ds.system[0]==DIV_SYSTEM_SMS_OPLL) { ds.systemLen=2; ds.system[0]=DIV_SYSTEM_SMS; @@ -789,6 +806,42 @@ bool DivEngine::loadFur(unsigned char* file, size_t len) { ds.systemFlags[i]=reader.readI(); } + // handle compound systems + for (int i=0; i<32; i++) { + if (ds.system[i]==DIV_SYSTEM_GENESIS || + ds.system[i]==DIV_SYSTEM_GENESIS_EXT || + ds.system[i]==DIV_SYSTEM_ARCADE) { + for (int j=31; j>i; j--) { + ds.system[j]=ds.system[j-1]; + ds.systemVol[j]=ds.systemVol[j-1]; + ds.systemPan[j]=ds.systemPan[j-1]; + } + if (++ds.systemLen>32) ds.systemLen=32; + + if (ds.system[i]==DIV_SYSTEM_GENESIS) { + ds.system[i]=DIV_SYSTEM_YM2612; + if (i<31) { + ds.system[i+1]=DIV_SYSTEM_SMS; + ds.systemVol[i+1]=(((ds.systemVol[i]&127)*3)>>3)|(ds.systemVol[i]&128); + } + } + if (ds.system[i]==DIV_SYSTEM_GENESIS_EXT) { + ds.system[i]=DIV_SYSTEM_YM2612_EXT; + if (i<31) { + ds.system[i+1]=DIV_SYSTEM_SMS; + ds.systemVol[i+1]=(((ds.systemVol[i]&127)*3)>>3)|(ds.systemVol[i]&128); + } + } + if (ds.system[i]==DIV_SYSTEM_ARCADE) { + ds.system[i]=DIV_SYSTEM_YM2151; + if (i<31) { + ds.system[i+1]=DIV_SYSTEM_SEGAPCM_COMPAT; + } + } + i++; + } + } + ds.name=reader.readString(); ds.author=reader.readString(); logI("%s by %s\n",ds.name.c_str(),ds.author.c_str()); @@ -1394,15 +1447,28 @@ SafeWriter* DivEngine::saveDMF(unsigned char version) { lastError="invalid version to save in! this is a bug!"; return NULL; } + // check whether system is compound + bool isFlat=false; + if (song.systemLen==2) { + if (song.system[0]==DIV_SYSTEM_YM2612 && song.system[1]==DIV_SYSTEM_SMS) { + isFlat=true; + } + if (song.system[0]==DIV_SYSTEM_YM2612_EXT && song.system[1]==DIV_SYSTEM_SMS) { + isFlat=true; + } + if (song.system[0]==DIV_SYSTEM_YM2151 && song.system[1]==DIV_SYSTEM_SEGAPCM_COMPAT) { + isFlat=true; + } + if (song.system[0]==DIV_SYSTEM_SMS && song.system[1]==DIV_SYSTEM_OPLL) { + isFlat=true; + } + } // fail if more than one system - // TODO: fix this mess for the flattening in 0.6 - if (!(song.system[0]==DIV_SYSTEM_SMS && song.system[1]==DIV_SYSTEM_OPLL)) { - if (song.systemLen!=1) { + if (!isFlat && song.systemLen!=1) { logE("cannot save multiple systems in this format!\n"); lastError="multiple systems not possible on .dmf"; return NULL; } - } // fail if this is an YMU759 song if (song.system[0]==DIV_SYSTEM_YMU759) { logE("cannot save YMU759 song!\n"); @@ -1416,7 +1482,7 @@ SafeWriter* DivEngine::saveDMF(unsigned char version) { return NULL; } // fail if the system is Furnace-exclusive - if (systemToFile(song.system[0])&0x80) { + if (!isFlat && systemToFile(song.system[0])&0x80) { logE("cannot save Furnace-exclusive system song!\n"); lastError="this system is not possible on .dmf"; return NULL; @@ -1432,7 +1498,16 @@ SafeWriter* DivEngine::saveDMF(unsigned char version) { // version w->writeC(version); DivSystem sys=DIV_SYSTEM_NULL; - if (song.system[0]==DIV_SYSTEM_SMS && song.system[1]==DIV_SYSTEM_OPLL) { + if (song.system[0]==DIV_SYSTEM_YM2612 && song.system[1]==DIV_SYSTEM_SMS) { + w->writeC(systemToFile(DIV_SYSTEM_GENESIS)); + sys=DIV_SYSTEM_GENESIS; + } else if (song.system[0]==DIV_SYSTEM_YM2612_EXT && song.system[1]==DIV_SYSTEM_SMS) { + w->writeC(systemToFile(DIV_SYSTEM_GENESIS_EXT)); + sys=DIV_SYSTEM_GENESIS_EXT; + } else if (song.system[0]==DIV_SYSTEM_YM2151 && song.system[1]==DIV_SYSTEM_SEGAPCM_COMPAT) { + w->writeC(systemToFile(DIV_SYSTEM_ARCADE)); + sys=DIV_SYSTEM_ARCADE; + } else if (song.system[0]==DIV_SYSTEM_SMS && song.system[1]==DIV_SYSTEM_OPLL) { w->writeC(systemToFile(DIV_SYSTEM_SMS_OPLL)); sys=DIV_SYSTEM_SMS_OPLL; } else { diff --git a/src/engine/platform/arcade.cpp b/src/engine/platform/arcade.cpp index ed665e576..15a255178 100644 --- a/src/engine/platform/arcade.cpp +++ b/src/engine/platform/arcade.cpp @@ -130,9 +130,6 @@ const char* DivPlatformArcade::getEffectName(unsigned char effect) { case 0x1f: return "1Fxx: Set PM depth (0 to 7F)"; break; - case 0x20: - return "20xx: Set PCM frequency"; - break; } return NULL; } @@ -158,43 +155,6 @@ void DivPlatformArcade::acquire_nuked(short* bufL, short* bufR, size_t start, si OPM_Clock(&fm,NULL,NULL,NULL,NULL); OPM_Clock(&fm,NULL,NULL,NULL,NULL); OPM_Clock(&fm,o,NULL,NULL,NULL); - - pcmCycles+=31250; - if (pcmCycles>=rate) { - pcmCycles-=rate; - - // do a PCM cycle - pcmL=0; pcmR=0; - for (int i=8; i<13; i++) { - if (chan[i].pcm.sample>=0) { - DivSample* s=parent->song.sample[chan[i].pcm.sample]; - if (s->rendLength<=0) { - chan[i].pcm.sample=-1; - continue; - } - if (!isMuted[i]) { - if (s->depth==8) { - pcmL+=(s->rendData[chan[i].pcm.pos>>8]*chan[i].chVolL); - pcmR+=(s->rendData[chan[i].pcm.pos>>8]*chan[i].chVolR); - } else { - pcmL+=(s->rendData[chan[i].pcm.pos>>8]*chan[i].chVolL)>>8; - pcmR+=(s->rendData[chan[i].pcm.pos>>8]*chan[i].chVolR)>>8; - } - } - chan[i].pcm.pos+=chan[i].pcm.freq; - if (chan[i].pcm.pos>=(s->rendLength<<8)) { - if (s->loopStart>=0 && s->loopStart<=(int)s->rendLength) { - chan[i].pcm.pos=s->loopStart<<8; - } else { - chan[i].pcm.sample=-1; - } - } - } - } - } - - o[0]+=pcmL; - o[1]+=pcmR; if (o[0]<-32768) o[0]=-32768; if (o[0]>32767) o[0]=32767; @@ -225,45 +185,11 @@ void DivPlatformArcade::acquire_ymfm(short* bufL, short* bufR, size_t start, siz fm_ymfm->generate(&out_ymfm); - pcmCycles+=31250; - if (pcmCycles>=rate) { - pcmCycles-=rate; - - // do a PCM cycle - pcmL=0; pcmR=0; - for (int i=8; i<13; i++) { - if (chan[i].pcm.sample>=0 && chan[i].pcm.samplesong.sampleLen) { - DivSample* s=parent->song.sample[chan[i].pcm.sample]; - if (s->rendLength<=0) { - chan[i].pcm.sample=-1; - continue; - } - if (!isMuted[i]) { - if (s->depth==8) { - pcmL+=(s->rendData[chan[i].pcm.pos>>8]*chan[i].chVolL); - pcmR+=(s->rendData[chan[i].pcm.pos>>8]*chan[i].chVolR); - } else { - pcmL+=(s->rendData[chan[i].pcm.pos>>8]*chan[i].chVolL)>>8; - pcmR+=(s->rendData[chan[i].pcm.pos>>8]*chan[i].chVolR)>>8; - } - } - chan[i].pcm.pos+=chan[i].pcm.freq; - if (chan[i].pcm.pos>=(s->rendLength<<8)) { - if (s->loopStart>=0 && s->loopStart<=(int)s->rendLength) { - chan[i].pcm.pos=s->loopStart<<8; - } else { - chan[i].pcm.sample=-1; - } - } - } - } - } - - os[0]=out_ymfm.data[0]+pcmL; + os[0]=out_ymfm.data[0]; if (os[0]<-32768) os[0]=-32768; if (os[0]>32767) os[0]=32767; - os[1]=out_ymfm.data[1]+pcmR; + os[1]=out_ymfm.data[1]; if (os[1]<-32768) os[1]=-32768; if (os[1]>32767) os[1]=32767; @@ -464,106 +390,21 @@ void DivPlatformArcade::tick() { chan[i].keyOn=false; } } - - for (int i=8; i<13; i++) { - if (chan[i].freqChanged) { - chan[i].freq=chan[i].baseFreq+(chan[i].pitch>>1)-64; - if (chan[i].furnacePCM) { - double off=1.0; - if (chan[i].pcm.sample>=0 && chan[i].pcm.samplesong.sampleLen) { - DivSample* s=parent->song.sample[chan[i].pcm.sample]; - off=(double)s->centerRate/8363.0; - } - chan[i].pcm.freq=MIN(255,((off*parent->song.tuning*pow(2.0,double(chan[i].freq+256)/(64.0*12.0)))*255)/31250); - if (dumpWrites && i>=8) { - addWrite(0x10007+((i-8)<<3),chan[i].pcm.freq); - } - } - chan[i].freqChanged=false; - } - } } void DivPlatformArcade::muteChannel(int ch, bool mute) { isMuted[ch]=mute; - if (ch<8) { - if (isMuted[ch]) { - rWrite(chanOffs[ch]+ADDR_LR_FB_ALG,(chan[ch].state.alg&7)|(chan[ch].state.fb<<3)); - } else { - rWrite(chanOffs[ch]+ADDR_LR_FB_ALG,(chan[ch].state.alg&7)|(chan[ch].state.fb<<3)|((chan[ch].chVolL&1)<<6)|((chan[ch].chVolR&1)<<7)); - } + if (isMuted[ch]) { + rWrite(chanOffs[ch]+ADDR_LR_FB_ALG,(chan[ch].state.alg&7)|(chan[ch].state.fb<<3)); + } else { + rWrite(chanOffs[ch]+ADDR_LR_FB_ALG,(chan[ch].state.alg&7)|(chan[ch].state.fb<<3)|((chan[ch].chVolL&1)<<6)|((chan[ch].chVolR&1)<<7)); } } int DivPlatformArcade::dispatch(DivCommand c) { - int pcmChan=c.chan-8; switch (c.cmd) { case DIV_CMD_NOTE_ON: { DivInstrument* ins=parent->getIns(chan[c.chan].ins); - if (c.chan>7) { - if (skipRegisterWrites) break; - if (ins->type==DIV_INS_AMIGA) { - chan[c.chan].pcm.sample=ins->amiga.initSample; - if (chan[c.chan].pcm.sample<0 || chan[c.chan].pcm.sample>=parent->song.sampleLen) { - chan[c.chan].pcm.sample=-1; - if (dumpWrites) { - addWrite(0x10086+(pcmChan<<3),3); - } - break; - } - chan[c.chan].pcm.pos=0; - chan[c.chan].baseFreq=(c.value<<6); - chan[c.chan].freqChanged=true; - chan[c.chan].furnacePCM=true; - if (dumpWrites) { // Sega PCM writes - DivSample* s=parent->song.sample[chan[c.chan].pcm.sample]; - addWrite(0x10086+(pcmChan<<3),3+((s->rendOffP>>16)<<3)); - addWrite(0x10084+(pcmChan<<3),(s->rendOffP)&0xff); - addWrite(0x10085+(pcmChan<<3),(s->rendOffP>>8)&0xff); - addWrite(0x10006+(pcmChan<<3),MIN(255,((s->rendOffP&0xffff)+s->rendLength-1)>>8)); - if (s->loopStart<0 || s->loopStart>=(int)s->rendLength) { - addWrite(0x10086+(pcmChan<<3),2+((s->rendOffP>>16)<<3)); - } else { - int loopPos=(s->rendOffP&0xffff)+s->loopStart+s->loopOffP; - addWrite(0x10004+(pcmChan<<3),loopPos&0xff); - addWrite(0x10005+(pcmChan<<3),(loopPos>>8)&0xff); - addWrite(0x10086+(pcmChan<<3),((s->rendOffP>>16)<<3)); - } - } - } else { - if (c.value!=DIV_NOTE_NULL) { - chan[c.chan].note=c.value; - } - chan[c.chan].pcm.sample=12*sampleBank+chan[c.chan].note%12; - if (chan[c.chan].pcm.sample>=parent->song.sampleLen) { - chan[c.chan].pcm.sample=-1; - if (dumpWrites) { - addWrite(0x10086+(pcmChan<<3),3); - } - break; - } - chan[c.chan].pcm.pos=0; - chan[c.chan].pcm.freq=MIN(255,(parent->song.sample[chan[c.chan].pcm.sample]->rate*255)/31250); - chan[c.chan].furnacePCM=false; - if (dumpWrites) { // Sega PCM writes - DivSample* s=parent->song.sample[chan[c.chan].pcm.sample]; - addWrite(0x10086+(pcmChan<<3),3+((s->rendOffP>>16)<<3)); - addWrite(0x10084+(pcmChan<<3),(s->rendOffP)&0xff); - addWrite(0x10085+(pcmChan<<3),(s->rendOffP>>8)&0xff); - addWrite(0x10006+(pcmChan<<3),MIN(255,((s->rendOffP&0xffff)+s->rendLength-1)>>8)); - if (s->loopStart<0 || s->loopStart>=(int)s->rendLength) { - addWrite(0x10086+(pcmChan<<3),2+((s->rendOffP>>16)<<3)); - } else { - int loopPos=(s->rendOffP&0xffff)+s->loopStart+s->loopOffP; - addWrite(0x10004+(pcmChan<<3),loopPos&0xff); - addWrite(0x10005+(pcmChan<<3),(loopPos>>8)&0xff); - addWrite(0x10086+(pcmChan<<3),((s->rendOffP>>16)<<3)); - } - addWrite(0x10007+(pcmChan<<3),chan[c.chan].pcm.freq); - } - } - break; - } if (chan[c.chan].insChanged) { chan[c.chan].state=ins->fm; @@ -614,12 +455,6 @@ int DivPlatformArcade::dispatch(DivCommand c) { break; } case DIV_CMD_NOTE_OFF: - if (c.chan>7) { - chan[c.chan].pcm.sample=-1; - if (dumpWrites) { - addWrite(0x10086+(pcmChan<<3),3); - } - } chan[c.chan].keyOff=true; chan[c.chan].keyOn=false; chan[c.chan].active=false; @@ -638,15 +473,6 @@ int DivPlatformArcade::dispatch(DivCommand c) { if (!chan[c.chan].std.hasVol) { chan[c.chan].outVol=c.value; } - if (c.chan>7) { - chan[c.chan].chVolL=c.value; - chan[c.chan].chVolR=c.value; - if (dumpWrites) { - addWrite(0x10002+(pcmChan<<3),chan[c.chan].chVolL); - addWrite(0x10003+(pcmChan<<3),chan[c.chan].chVolR); - } - break; - } for (int i=0; i<4; i++) { unsigned short baseAddr=chanOffs[c.chan]|opOffs[i]; DivInstrumentFM::Operator& op=chan[c.chan].state.op[i]; @@ -669,22 +495,12 @@ int DivPlatformArcade::dispatch(DivCommand c) { chan[c.chan].ins=c.value; break; case DIV_CMD_PANNING: { - // TODO - if (c.chan>7) { - chan[c.chan].chVolL=(c.value>>4)|(((c.value>>4)>>1)<<4); - chan[c.chan].chVolR=(c.value&15)|(((c.value&15)>>1)<<4); - if (dumpWrites) { - addWrite(0x10002+(pcmChan<<3),chan[c.chan].chVolL); - addWrite(0x10003+(pcmChan<<3),chan[c.chan].chVolR); - } + chan[c.chan].chVolL=((c.value>>4)==1); + chan[c.chan].chVolR=((c.value&15)==1); + if (isMuted[c.chan]) { + rWrite(chanOffs[c.chan]+ADDR_LR_FB_ALG,(chan[c.chan].state.alg&7)|(chan[c.chan].state.fb<<3)); } else { - chan[c.chan].chVolL=((c.value>>4)==1); - chan[c.chan].chVolR=((c.value&15)==1); - if (isMuted[c.chan]) { - rWrite(chanOffs[c.chan]+ADDR_LR_FB_ALG,(chan[c.chan].state.alg&7)|(chan[c.chan].state.fb<<3)); - } else { - rWrite(chanOffs[c.chan]+ADDR_LR_FB_ALG,(chan[c.chan].state.alg&7)|(chan[c.chan].state.fb<<3)|((chan[c.chan].chVolL&1)<<6)|((chan[c.chan].chVolR&1)<<7)); - } + rWrite(chanOffs[c.chan]+ADDR_LR_FB_ALG,(chan[c.chan].state.alg&7)|(chan[c.chan].state.fb<<3)|((chan[c.chan].chVolL&1)<<6)|((chan[c.chan].chVolR&1)<<7)); } break; } @@ -724,17 +540,14 @@ int DivPlatformArcade::dispatch(DivCommand c) { break; } case DIV_CMD_FM_LFO: { - if (c.chan>7) break; rWrite(0x18,c.value); break; } case DIV_CMD_FM_LFO_WAVE: { - if (c.chan>7) break; rWrite(0x1b,c.value&3); break; } case DIV_CMD_FM_FB: { - if (c.chan>7) break; chan[c.chan].state.fb=c.value&7; if (isMuted[c.chan]) { rWrite(chanOffs[c.chan]+ADDR_LR_FB_ALG,(chan[c.chan].state.alg&7)|(chan[c.chan].state.fb<<3)); @@ -744,7 +557,6 @@ int DivPlatformArcade::dispatch(DivCommand c) { break; } case DIV_CMD_FM_MULT: { - if (c.chan>7) break; unsigned short baseAddr=chanOffs[c.chan]|opOffs[orderedOps[c.value]]; DivInstrumentFM::Operator& op=chan[c.chan].state.op[orderedOps[c.value]]; op.mult=c.value2&15; @@ -752,7 +564,6 @@ int DivPlatformArcade::dispatch(DivCommand c) { break; } case DIV_CMD_FM_TL: { - if (c.chan>7) break; unsigned short baseAddr=chanOffs[c.chan]|opOffs[orderedOps[c.value]]; DivInstrumentFM::Operator& op=chan[c.chan].state.op[orderedOps[c.value]]; op.tl=c.value2; @@ -764,7 +575,6 @@ int DivPlatformArcade::dispatch(DivCommand c) { break; } case DIV_CMD_FM_AR: { - if (c.chan>7) break; if (c.value<0) { for (int i=0; i<4; i++) { DivInstrumentFM::Operator& op=chan[c.chan].state.op[i]; @@ -803,12 +613,6 @@ int DivPlatformArcade::dispatch(DivCommand c) { } break; } - case DIV_CMD_SAMPLE_BANK: - sampleBank=c.value; - if (sampleBank>(parent->song.sample.size()/12)) { - sampleBank=parent->song.sample.size()/12; - } - break; case DIV_ALWAYS_SET_VOLUME: return 0; break; @@ -820,12 +624,6 @@ int DivPlatformArcade::dispatch(DivCommand c) { break; case DIV_CMD_PRE_NOTE: break; - case DIV_CMD_SAMPLE_FREQ: - chan[c.chan].pcm.freq=c.value; - if (dumpWrites) { - addWrite(0x10007+(pcmChan<<3),chan[c.chan].pcm.freq); - } - break; default: //printf("WARNING: unimplemented command %d\n",c.cmd); break; @@ -860,9 +658,6 @@ void DivPlatformArcade::forceIns() { chan[i].freqChanged=true; } } - for (int i=8; i<13; i++) { - chan[i].insChanged=true; - } immWrite(0x19,amDepth); immWrite(0x19,0x80|pmDepth); } @@ -907,7 +702,7 @@ void DivPlatformArcade::reset() { if (dumpWrites) { addWrite(0xffffffff,0); } - for (int i=0; i<13; i++) { + for (int i=0; i<8; i++) { chan[i]=DivPlatformArcade::Channel(); chan[i].vol=0x7f; chan[i].outVol=0x7f; @@ -922,7 +717,6 @@ void DivPlatformArcade::reset() { pcmCycles=0; pcmL=0; pcmR=0; - sampleBank=0; delay=0; amDepth=0x7f; pmDepth=0x7f; @@ -931,13 +725,6 @@ void DivPlatformArcade::reset() { immWrite(0x19,amDepth); immWrite(0x19,0x80|pmDepth); //rWrite(0x1b,0x00); - if (dumpWrites) { - for (int i=0; i<5; i++) { - addWrite(0x10086+(i<<3),3); - addWrite(0x10002+(i<<3),0x7f); - addWrite(0x10003+(i<<3),0x7f); - } - } extMode=false; } @@ -972,14 +759,14 @@ int DivPlatformArcade::init(DivEngine* p, int channels, int sugRate, unsigned in parent=p; dumpWrites=false; skipRegisterWrites=false; - for (int i=0; i<13; i++) { + for (int i=0; i<8; i++) { isMuted[i]=false; } setFlags(flags); if (useYMFM) fm_ymfm=new ymfm::ym2151(iface); reset(); - return 13; + return 8; } void DivPlatformArcade::quit() { diff --git a/src/engine/platform/arcade.h b/src/engine/platform/arcade.h index f43b54cce..a305eefdb 100644 --- a/src/engine/platform/arcade.h +++ b/src/engine/platform/arcade.h @@ -42,17 +42,9 @@ class DivPlatformArcade: public DivDispatch { bool active, insChanged, freqChanged, keyOn, keyOff, inPorta, portaPause, furnacePCM; int vol, outVol; unsigned char chVolL, chVolR; - - struct PCMChannel { - int sample; - unsigned int pos; // <<8 - unsigned short len; - unsigned char freq; - PCMChannel(): sample(-1), pos(0), len(0), freq(0) {} - } pcm; Channel(): freqH(0), freqL(0), freq(0), baseFreq(0), pitch(0), note(0), ins(-1), active(false), insChanged(true), freqChanged(false), keyOn(false), keyOff(false), inPorta(false), portaPause(false), furnacePCM(false), vol(0), outVol(0), chVolL(127), chVolR(127) {} }; - Channel chan[13]; + Channel chan[8]; struct QueuedWrite { unsigned short addr; unsigned char val; @@ -63,7 +55,6 @@ class DivPlatformArcade: public DivDispatch { opm_t fm; int delay, baseFreqOff; int pcmL, pcmR, pcmCycles; - unsigned char sampleBank; unsigned char lastBusy; unsigned char amDepth, pmDepth; @@ -75,7 +66,7 @@ class DivPlatformArcade: public DivDispatch { bool extMode, useYMFM; - bool isMuted[13]; + bool isMuted[8]; short oldWrites[256]; short pendingWrites[256]; diff --git a/src/engine/platform/lynx.h b/src/engine/platform/lynx.h index 2fa6cf08a..8ad2bf2ce 100644 --- a/src/engine/platform/lynx.h +++ b/src/engine/platform/lynx.h @@ -1,3 +1,22 @@ +/** + * Furnace Tracker - multi-system chiptune tracker + * Copyright (C) 2021-2022 tildearrow and contributors + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + #ifndef _LYNX_H #define _LYNX_H diff --git a/src/engine/platform/qsound.cpp b/src/engine/platform/qsound.cpp index 47e6bdb50..02067ffd8 100644 --- a/src/engine/platform/qsound.cpp +++ b/src/engine/platform/qsound.cpp @@ -30,219 +30,219 @@ #define immWrite(a,v) {qsound_write_data(&chip,a,v); if(dumpWrites) addWrite(a,v);} const char* regCheatSheetQSound[]={ - "Ch15_Bank", "00", - "Ch00_Start", "01", - "Ch00_Freq", "02", - "Ch00_Phase", "03", - "Ch00_Loop", "04", - "Ch00_End", "05", - "Ch00_Volume", "06", - "Ch00_Bank", "08", - "Ch01_Start", "09", - "Ch01_Freq", "0A", - "Ch01_Phase", "0B", - "Ch01_Loop", "0C", - "Ch01_End", "0D", - "Ch01_Volume", "0E", - "Ch01_Bank", "10", - "Ch02_Start", "11", - "Ch02_Freq", "12", - "Ch02_Phase", "13", - "Ch02_Loop", "14", - "Ch02_End", "15", - "Ch02_Volume", "16", - "Ch02_Bank", "18", - "Ch03_Start", "19", - "Ch03_Freq", "1A", - "Ch03_Phase", "1B", - "Ch03_Loop", "1C", - "Ch03_End", "1D", - "Ch03_Volume", "1E", - "Ch03_Bank", "20", - "Ch04_Start", "21", - "Ch04_Freq", "22", - "Ch04_Phase", "23", - "Ch04_Loop", "24", - "Ch04_End", "25", - "Ch04_Volume", "26", - "Ch04_Bank", "28", - "Ch05_Start", "29", - "Ch05_Freq", "2A", - "Ch05_Phase", "2B", - "Ch05_Loop", "2C", - "Ch05_End", "2D", - "Ch05_Volume", "2E", - "Ch05_Bank", "30", - "Ch06_Start", "31", - "Ch06_Freq", "32", - "Ch06_Phase", "33", - "Ch06_Loop", "34", - "Ch06_End", "35", - "Ch06_Volume", "36", - "Ch06_Bank", "38", - "Ch07_Start", "39", - "Ch07_Freq", "3A", - "Ch07_Phase", "3B", - "Ch07_Loop", "3C", - "Ch07_End", "3D", - "Ch07_Volume", "3E", - "Ch07_Bank", "40", - "Ch08_Start", "41", - "Ch08_Freq", "42", - "Ch08_Phase", "43", - "Ch08_Loop", "44", - "Ch08_End", "45", - "Ch08_Volume", "46", - "Ch08_Bank", "48", - "Ch09_Start", "49", - "Ch09_Freq", "4A", - "Ch09_Phase", "4B", - "Ch09_Loop", "4C", - "Ch09_End", "4D", - "Ch09_Volume", "4E", - "Ch09_Bank", "50", - "Ch10_Start", "51", - "Ch10_Freq", "52", - "Ch10_Phase", "53", - "Ch10_Loop", "54", - "Ch10_End", "55", - "Ch10_Volume", "56", - "Ch10_Bank", "58", - "Ch11_Start", "59", - "Ch11_Freq", "5A", - "Ch11_Phase", "5B", - "Ch11_Loop", "5C", - "Ch11_End", "5D", - "Ch11_Volume", "5E", - "Ch11_Bank", "60", - "Ch12_Start", "61", - "Ch12_Freq", "62", - "Ch12_Phase", "63", - "Ch12_Loop", "64", - "Ch12_End", "65", - "Ch12_Volume", "66", - "Ch12_Bank", "68", - "Ch13_Start", "69", - "Ch13_Freq", "6A", - "Ch13_Phase", "6B", - "Ch13_Loop", "6C", - "Ch13_End", "6D", - "Ch13_Volume", "6E", - "Ch13_Bank", "70", - "Ch14_Start", "71", - "Ch14_Freq", "72", - "Ch14_Phase", "73", - "Ch14_Loop", "74", - "Ch14_End", "75", - "Ch14_Volume", "76", - "Ch14_Bank", "78", - "Ch15_Start", "79", - "Ch15_Freq", "7A", - "Ch15_Phase", "7B", - "Ch15_Loop", "7C", - "Ch15_End", "7D", - "Ch15_Volume", "7E", - "Ch00_Panning", "80", - "Ch01_Panning", "81", - "Ch02_Panning", "82", - "Ch03_Panning", "83", - "Ch04_Panning", "84", - "Ch05_Panning", "85", - "Ch06_Panning", "86", - "Ch07_Panning", "87", - "Ch08_Panning", "88", - "Ch09_Panning", "89", - "Ch10_Panning", "8A", - "Ch11_Panning", "8B", - "Ch12_Panning", "8C", - "Ch13_Panning", "8D", - "Ch14_Panning", "8E", - "Ch15_Panning", "8F", - "Adpcm0_Panning","90", - "Adpcm1_Panning","91", - "Adpcm2_Panning","92", - "Echo_Feedback","93", - "Ch00_Echo", "BA", - "Ch01_Echo", "BB", - "Ch02_Echo", "BC", - "Ch03_Echo", "BD", - "Ch04_Echo", "BE", - "Ch05_Echo", "BF", - "Ch06_Echo", "C0", - "Ch07_Echo", "C1", - "Ch08_Echo", "C2", - "Ch09_Echo", "C3", - "Ch10_Echo", "C4", - "Ch11_Echo", "C5", - "Ch12_Echo", "C6", - "Ch13_Echo", "C7", - "Ch14_Echo", "C8", - "Ch15_Echo", "C9", - "Adpcm0_Start", "CA", - "Adpcm0_End", "CB", - "Adpcm0_Bank", "CC", - "Adpcm0_Volume","CD", - "Adpcm1_Start", "CE", - "Adpcm1_End", "CF", - "Adpcm1_Bank", "D0", - "Adpcm1_Volume","D1", - "Adpcm2_Start", "D2", - "Adpcm2_End", "D3", - "Adpcm2_Bank", "D4", - "Adpcm2_Volume","D5", - "Adpcm0_KeyOn", "D6", - "Adpcm1_KeyOn", "D7", - "Adpcm2_KeyOn", "D8", - "Echo_Delay", "D9", - "L_Wet_Filter", "DA", - "L_Dry_Filter", "DB", - "R_Wet_Filter", "DC", - "R_Dry_Filter", "DD", - "L_Wet_Delay", "DE", - "L_Dry_Delay", "DF", - "R_Wet_Delay", "E0", - "R_Dry_Delay", "E1", - "Delay_Flag", "E2", - "Mode_Select", "E3", //valid: 0000,0288,0039,061A,004F - "L_Wet_Volume", "E4", - "L_Dry_Volume", "E5", - "R_Wet_Volume", "E6", - "R_Dry_Volume", "E7", - NULL + "Ch15_Bank", "00", + "Ch00_Start", "01", + "Ch00_Freq", "02", + "Ch00_Phase", "03", + "Ch00_Loop", "04", + "Ch00_End", "05", + "Ch00_Volume", "06", + "Ch00_Bank", "08", + "Ch01_Start", "09", + "Ch01_Freq", "0A", + "Ch01_Phase", "0B", + "Ch01_Loop", "0C", + "Ch01_End", "0D", + "Ch01_Volume", "0E", + "Ch01_Bank", "10", + "Ch02_Start", "11", + "Ch02_Freq", "12", + "Ch02_Phase", "13", + "Ch02_Loop", "14", + "Ch02_End", "15", + "Ch02_Volume", "16", + "Ch02_Bank", "18", + "Ch03_Start", "19", + "Ch03_Freq", "1A", + "Ch03_Phase", "1B", + "Ch03_Loop", "1C", + "Ch03_End", "1D", + "Ch03_Volume", "1E", + "Ch03_Bank", "20", + "Ch04_Start", "21", + "Ch04_Freq", "22", + "Ch04_Phase", "23", + "Ch04_Loop", "24", + "Ch04_End", "25", + "Ch04_Volume", "26", + "Ch04_Bank", "28", + "Ch05_Start", "29", + "Ch05_Freq", "2A", + "Ch05_Phase", "2B", + "Ch05_Loop", "2C", + "Ch05_End", "2D", + "Ch05_Volume", "2E", + "Ch05_Bank", "30", + "Ch06_Start", "31", + "Ch06_Freq", "32", + "Ch06_Phase", "33", + "Ch06_Loop", "34", + "Ch06_End", "35", + "Ch06_Volume", "36", + "Ch06_Bank", "38", + "Ch07_Start", "39", + "Ch07_Freq", "3A", + "Ch07_Phase", "3B", + "Ch07_Loop", "3C", + "Ch07_End", "3D", + "Ch07_Volume", "3E", + "Ch07_Bank", "40", + "Ch08_Start", "41", + "Ch08_Freq", "42", + "Ch08_Phase", "43", + "Ch08_Loop", "44", + "Ch08_End", "45", + "Ch08_Volume", "46", + "Ch08_Bank", "48", + "Ch09_Start", "49", + "Ch09_Freq", "4A", + "Ch09_Phase", "4B", + "Ch09_Loop", "4C", + "Ch09_End", "4D", + "Ch09_Volume", "4E", + "Ch09_Bank", "50", + "Ch10_Start", "51", + "Ch10_Freq", "52", + "Ch10_Phase", "53", + "Ch10_Loop", "54", + "Ch10_End", "55", + "Ch10_Volume", "56", + "Ch10_Bank", "58", + "Ch11_Start", "59", + "Ch11_Freq", "5A", + "Ch11_Phase", "5B", + "Ch11_Loop", "5C", + "Ch11_End", "5D", + "Ch11_Volume", "5E", + "Ch11_Bank", "60", + "Ch12_Start", "61", + "Ch12_Freq", "62", + "Ch12_Phase", "63", + "Ch12_Loop", "64", + "Ch12_End", "65", + "Ch12_Volume", "66", + "Ch12_Bank", "68", + "Ch13_Start", "69", + "Ch13_Freq", "6A", + "Ch13_Phase", "6B", + "Ch13_Loop", "6C", + "Ch13_End", "6D", + "Ch13_Volume", "6E", + "Ch13_Bank", "70", + "Ch14_Start", "71", + "Ch14_Freq", "72", + "Ch14_Phase", "73", + "Ch14_Loop", "74", + "Ch14_End", "75", + "Ch14_Volume", "76", + "Ch14_Bank", "78", + "Ch15_Start", "79", + "Ch15_Freq", "7A", + "Ch15_Phase", "7B", + "Ch15_Loop", "7C", + "Ch15_End", "7D", + "Ch15_Volume", "7E", + "Ch00_Panning", "80", + "Ch01_Panning", "81", + "Ch02_Panning", "82", + "Ch03_Panning", "83", + "Ch04_Panning", "84", + "Ch05_Panning", "85", + "Ch06_Panning", "86", + "Ch07_Panning", "87", + "Ch08_Panning", "88", + "Ch09_Panning", "89", + "Ch10_Panning", "8A", + "Ch11_Panning", "8B", + "Ch12_Panning", "8C", + "Ch13_Panning", "8D", + "Ch14_Panning", "8E", + "Ch15_Panning", "8F", + "Adpcm0_Panning","90", + "Adpcm1_Panning","91", + "Adpcm2_Panning","92", + "Echo_Feedback","93", + "Ch00_Echo", "BA", + "Ch01_Echo", "BB", + "Ch02_Echo", "BC", + "Ch03_Echo", "BD", + "Ch04_Echo", "BE", + "Ch05_Echo", "BF", + "Ch06_Echo", "C0", + "Ch07_Echo", "C1", + "Ch08_Echo", "C2", + "Ch09_Echo", "C3", + "Ch10_Echo", "C4", + "Ch11_Echo", "C5", + "Ch12_Echo", "C6", + "Ch13_Echo", "C7", + "Ch14_Echo", "C8", + "Ch15_Echo", "C9", + "Adpcm0_Start", "CA", + "Adpcm0_End", "CB", + "Adpcm0_Bank", "CC", + "Adpcm0_Volume","CD", + "Adpcm1_Start", "CE", + "Adpcm1_End", "CF", + "Adpcm1_Bank", "D0", + "Adpcm1_Volume","D1", + "Adpcm2_Start", "D2", + "Adpcm2_End", "D3", + "Adpcm2_Bank", "D4", + "Adpcm2_Volume","D5", + "Adpcm0_KeyOn", "D6", + "Adpcm1_KeyOn", "D7", + "Adpcm2_KeyOn", "D8", + "Echo_Delay", "D9", + "L_Wet_Filter", "DA", + "L_Dry_Filter", "DB", + "R_Wet_Filter", "DC", + "R_Dry_Filter", "DD", + "L_Wet_Delay", "DE", + "L_Dry_Delay", "DF", + "R_Wet_Delay", "E0", + "R_Dry_Delay", "E1", + "Delay_Flag", "E2", + "Mode_Select", "E3", //valid: 0000,0288,0039,061A,004F + "L_Wet_Volume", "E4", + "L_Dry_Volume", "E5", + "R_Wet_Volume", "E6", + "R_Dry_Volume", "E7", + NULL }; enum q1_register_name { - Q1V_BANK = 0, - Q1V_START = 1, - Q1V_FREQ = 2, - Q1V_PHASE = 3, - Q1V_LOOP = 4, - Q1V_END = 5, - Q1V_VOL = 6, - Q1V_REG_COUNT = 7, + Q1V_BANK = 0, + Q1V_START = 1, + Q1V_FREQ = 2, + Q1V_PHASE = 3, + Q1V_LOOP = 4, + Q1V_END = 5, + Q1V_VOL = 6, + Q1V_REG_COUNT = 7, - Q1_PAN = 0x80, - Q1_ECHO = 0xba, + Q1_PAN = 0x80, + Q1_ECHO = 0xba, - Q1A_PAN = 0x90, - Q1A_START = 0xca, - Q1A_END = 0xcb, - Q1A_BANK = 0xcc, - Q1A_VOL = 0xcd, + Q1A_PAN = 0x90, + Q1A_START = 0xca, + Q1A_END = 0xcb, + Q1A_BANK = 0xcc, + Q1A_VOL = 0xcd, - Q1A_KEYON = 0xd6, + Q1A_KEYON = 0xd6, - Q1_ECHO_FEEDBACK = 0x93, - Q1_ECHO_LENGTH = 0xd9, + Q1_ECHO_FEEDBACK = 0x93, + Q1_ECHO_LENGTH = 0xd9, }; const unsigned char q1_reg_map[Q1V_REG_COUNT][16] = { - {0x78,0x00,0x08,0x10,0x18,0x20,0x28,0x30,0x38,0x40,0x48,0x50,0x58,0x60,0x68,0x70}, - {0x01,0x09,0x11,0x19,0x21,0x29,0x31,0x39,0x41,0x49,0x51,0x59,0x61,0x69,0x71,0x79}, - {0x02,0x0a,0x12,0x1a,0x22,0x2a,0x32,0x3a,0x42,0x4a,0x52,0x5a,0x62,0x6a,0x72,0x7a}, - {0x03,0x0b,0x13,0x1b,0x23,0x2b,0x33,0x3b,0x43,0x4b,0x53,0x5b,0x63,0x6b,0x73,0x7b}, - {0x04,0x0c,0x14,0x1c,0x24,0x2c,0x34,0x3c,0x44,0x4c,0x54,0x5c,0x64,0x6c,0x74,0x7c}, - {0x05,0x0d,0x15,0x1d,0x25,0x2d,0x35,0x3d,0x45,0x4d,0x55,0x5d,0x65,0x6d,0x75,0x7d}, - {0x06,0x0e,0x16,0x1e,0x26,0x2e,0x36,0x3e,0x46,0x4e,0x56,0x5e,0x66,0x6e,0x76,0x7e}, + {0x78,0x00,0x08,0x10,0x18,0x20,0x28,0x30,0x38,0x40,0x48,0x50,0x58,0x60,0x68,0x70}, + {0x01,0x09,0x11,0x19,0x21,0x29,0x31,0x39,0x41,0x49,0x51,0x59,0x61,0x69,0x71,0x79}, + {0x02,0x0a,0x12,0x1a,0x22,0x2a,0x32,0x3a,0x42,0x4a,0x52,0x5a,0x62,0x6a,0x72,0x7a}, + {0x03,0x0b,0x13,0x1b,0x23,0x2b,0x33,0x3b,0x43,0x4b,0x53,0x5b,0x63,0x6b,0x73,0x7b}, + {0x04,0x0c,0x14,0x1c,0x24,0x2c,0x34,0x3c,0x44,0x4c,0x54,0x5c,0x64,0x6c,0x74,0x7c}, + {0x05,0x0d,0x15,0x1d,0x25,0x2d,0x35,0x3d,0x45,0x4d,0x55,0x5d,0x65,0x6d,0x75,0x7d}, + {0x06,0x0e,0x16,0x1e,0x26,0x2e,0x36,0x3e,0x46,0x4e,0x56,0x5e,0x66,0x6e,0x76,0x7e}, }; const char** DivPlatformQSound::getRegisterSheet() { @@ -258,8 +258,9 @@ const char* DivPlatformQSound::getEffectName(unsigned char effect) { return "11xx: Set channel echo level (00 to FF)"; break; default: - if((effect & 0xf0) == 0x30) - return "3xxx: Set echo delay buffer length (000 to AA5)"; + if ((effect & 0xf0) == 0x30) { + return "3xxx: Set echo delay buffer length (000 to AA5)"; + } } return NULL; } @@ -267,7 +268,7 @@ void DivPlatformQSound::acquire(short* bufL, short* bufR, size_t start, size_t l chip.rom_data = parent->qsoundMem; chip.rom_mask = 0xffffff; for (size_t h=start; h>8; - // Check if enabled and write volume - if(chan[i].active) - { - rWrite(q1_reg_map[Q1V_VOL][i], chan[i].outVol << 5); - //logW("ch %d vol=%04x (hadVol)!\n",i,chan[i].outVol << 5); - } + chan[i].outVol=((chan[i].vol&0xff)*MIN(255,chan[i].std.vol<<2))>>8; + // Check if enabled and write volume + if (chan[i].active) { + rWrite(q1_reg_map[Q1V_VOL][i], chan[i].outVol << 4); + } } - uint16_t qsound_bank = 0; - uint16_t qsound_addr = 0; - uint16_t qsound_loop = 0; - uint16_t qsound_end = 0; + uint16_t qsound_bank = 0; + uint16_t qsound_addr = 0; + uint16_t qsound_loop = 0; + uint16_t qsound_end = 0; double off=1.0; if (chan[i].sample>=0 && chan[i].samplesong.sampleLen) { DivSample* s=parent->song.sample[chan[i].sample]; @@ -297,22 +296,20 @@ void DivPlatformQSound::tick() { } else { off=(double)s->centerRate/24038.0/16.0; } - qsound_bank = 0x8000 | (s->rendOffQsound >> 16); - qsound_addr = s->rendOffQsound & 0xffff; + qsound_bank = 0x8000 | (s->rendOffQsound >> 16); + qsound_addr = s->rendOffQsound & 0xffff; - int length = s->length; - if(length > 65536 - 16) - length = 65536 - 16; - if(s->loopStart == -1 || s->loopStart >= length) - { - qsound_end = s->rendOffQsound + length + 15; - qsound_loop = 15; - } - else - { - qsound_end = s->rendOffQsound + length; - qsound_loop = length - s->loopStart; - } + int length = s->length; + if (length > 65536 - 16) { + length = 65536 - 16; + } + if (s->loopStart == -1 || s->loopStart >= length) { + qsound_end = s->rendOffQsound + length + 15; + qsound_loop = 15; + } else { + qsound_end = s->rendOffQsound + length; + qsound_loop = length - s->loopStart; + } } if (chan[i].std.hadArp) { if (!chan[i].inPorta) { @@ -333,29 +330,26 @@ void DivPlatformQSound::tick() { //DivInstrument* ins=parent->getIns(chan[i].ins); chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,false); if (chan[i].freq>0xffff) chan[i].freq=0xffff; - //if (chan[i].note>0x5d) chan[i].freq=0x01; //???? if (chan[i].keyOn) { - rWrite(q1_reg_map[Q1V_BANK][i], qsound_bank); - rWrite(q1_reg_map[Q1V_END][i], qsound_end); - rWrite(q1_reg_map[Q1V_LOOP][i], qsound_loop); - rWrite(q1_reg_map[Q1V_START][i], qsound_addr); - rWrite(q1_reg_map[Q1V_PHASE][i], 0x8000); - //logW("ch %d bank=%04x, addr=%04x, end=%04x, loop=%04x!\n",i,qsound_bank,qsound_addr,qsound_end,qsound_loop); - // Write sample address. Enable volume - if (!chan[i].std.hadVol) { - rWrite(q1_reg_map[Q1V_VOL][i], chan[i].vol << 5); - //logW("ch %d vol=%04x (!hadVol)!\n",i,chan[i].vol << 5); - } + rWrite(q1_reg_map[Q1V_BANK][i], qsound_bank); + rWrite(q1_reg_map[Q1V_END][i], qsound_end); + rWrite(q1_reg_map[Q1V_LOOP][i], qsound_loop); + rWrite(q1_reg_map[Q1V_START][i], qsound_addr); + rWrite(q1_reg_map[Q1V_PHASE][i], 0x8000); + //logW("ch %d bank=%04x, addr=%04x, end=%04x, loop=%04x!\n",i,qsound_bank,qsound_addr,qsound_end,qsound_loop); + // Write sample address. Enable volume + if (!chan[i].std.hadVol) { + rWrite(q1_reg_map[Q1V_VOL][i], chan[i].vol << 4); + } } if (chan[i].keyOff) { - rWrite(q1_reg_map[Q1V_VOL][i], 0); - rWrite(q1_reg_map[Q1V_FREQ][i], 0); - // Disable volume + // Disable volume + rWrite(q1_reg_map[Q1V_VOL][i], 0); + rWrite(q1_reg_map[Q1V_FREQ][i], 0); + } else if (chan[i].active) { + //logW("ch %d frequency set to %04x, off=%f, note=%d, %04x!\n",i,chan[i].freq,off,chan[i].note,QS_NOTE_FREQUENCY(chan[i].note)); + rWrite(q1_reg_map[Q1V_FREQ][i], chan[i].freq); } - else if (chan[i].active) { - //logW("ch %d frequency set to %04x, off=%f, note=%d, %04x!\n",i,chan[i].freq,off,chan[i].note,QS_NOTE_FREQUENCY(chan[i].note)); - rWrite(q1_reg_map[Q1V_FREQ][i], chan[i].freq); - } if (chan[i].keyOn) chan[i].keyOn=false; if (chan[i].keyOff) chan[i].keyOff=false; chan[i].freqChanged=false; @@ -411,13 +405,11 @@ int DivPlatformQSound::dispatch(DivCommand c) { if (chan[c.chan].vol!=c.value) { chan[c.chan].vol=c.value; if (!chan[c.chan].std.hasVol) { - // Check if enabled and write volume + // Check if enabled and write volume chan[c.chan].outVol=c.value; - if(chan[c.chan].active && c.chan < 16) - { - rWrite(q1_reg_map[Q1V_VOL][c.chan], chan[c.chan].outVol << 5); - //logW("ch %d vol=%04x (cmd vol)!\n",c.chan,chan[c.chan].outVol << 5); - } + if (chan[c.chan].active && c.chan < 16) { + rWrite(q1_reg_map[Q1V_VOL][c.chan], chan[c.chan].outVol << 4); + } } } break; @@ -428,16 +420,16 @@ int DivPlatformQSound::dispatch(DivCommand c) { return chan[c.chan].outVol; break; case DIV_CMD_PANNING: - immWrite(Q1_PAN+c.chan, c.value + 0x110); + immWrite(Q1_PAN+c.chan, c.value + 0x110); break; case DIV_CMD_QSOUND_ECHO_LEVEL: - immWrite(Q1_ECHO+c.chan, c.value << 7); + immWrite(Q1_ECHO+c.chan, c.value << 7); break; case DIV_CMD_QSOUND_ECHO_FEEDBACK: - immWrite(Q1_ECHO_FEEDBACK, c.value << 6); + immWrite(Q1_ECHO_FEEDBACK, c.value << 6); break; - case DIV_CMD_QSOUND_ECHO_DELAY: - immWrite(Q1_ECHO_LENGTH, (c.value > 2725 ? 0xfff : 0xfff - (2725 - c.value))); + case DIV_CMD_QSOUND_ECHO_DELAY: + immWrite(Q1_ECHO_LENGTH, (c.value > 2725 ? 0xfff : 0xfff - (2725 - c.value))); break; case DIV_CMD_PITCH: chan[c.chan].pitch=c.value; @@ -509,10 +501,11 @@ int DivPlatformQSound::dispatch(DivCommand c) { } void DivPlatformQSound::muteChannel(int ch, bool mute) { - if(mute) - chip.mute_mask |= (1 << ch); - else - chip.mute_mask &= ~(1 << ch); + if (mute) { + chip.mute_mask|=(1<> 12) & 255; - if(echoDelay < 0) - echoDelay = 0; - if(echoDelay > 2725) - echoDelay = 2725; + if(echoDelay < 0) { + echoDelay = 0; + } + if(echoDelay > 2725) { + echoDelay = 2725; + } //rate=chipClock/CHIP_DIVIDER; } @@ -586,21 +583,21 @@ void DivPlatformQSound::poke(std::vector& wlist) { } unsigned char* DivPlatformQSound::getRegisterPool() { - unsigned short* regPoolPtr = regPool; - for(int i=0; i<256; i++) - { - uint16_t data = qsound_read_data(&chip, i); - *regPoolPtr++ = data; - } - return (unsigned char*)regPool; + unsigned short* regPoolPtr = regPool; + for(int i=0; i<256; i++) + { + uint16_t data = qsound_read_data(&chip, i); + *regPoolPtr++ = data; + } + return (unsigned char*)regPool; } int DivPlatformQSound::getRegisterPoolSize() { - return 256; + return 256; } int DivPlatformQSound::getRegisterPoolDepth() { - return 16; + return 16; } int DivPlatformQSound::init(DivEngine* p, int channels, int sugRate, unsigned int flags) { @@ -608,9 +605,9 @@ int DivPlatformQSound::init(DivEngine* p, int channels, int sugRate, unsigned in dumpWrites=false; skipRegisterWrites=false; -// for (int i=0; i<16; i++) { -// isMuted[i]=false; -// } + //for (int i=0; i<16; i++) { + // isMuted[i]=false; + //} setFlags(flags); chipClock=60000000; diff --git a/src/engine/platform/qsound.h b/src/engine/platform/qsound.h index 8d90aff0a..16420055d 100644 --- a/src/engine/platform/qsound.h +++ b/src/engine/platform/qsound.h @@ -33,7 +33,7 @@ class DivPlatformQSound: public DivDispatch { int sample, wave; unsigned char ins; int note; - int panning; + int panning; bool active, insChanged, freqChanged, keyOn, keyOff, inPorta, useWave; int vol, outVol; DivMacroInt std; @@ -46,7 +46,7 @@ class DivPlatformQSound: public DivDispatch { sample(-1), ins(-1), note(0), - panning(0x10), + panning(0x10), active(false), insChanged(true), freqChanged(false), diff --git a/src/engine/platform/segapcm.cpp b/src/engine/platform/segapcm.cpp new file mode 100644 index 000000000..6a5276c5d --- /dev/null +++ b/src/engine/platform/segapcm.cpp @@ -0,0 +1,406 @@ +/** + * Furnace Tracker - multi-system chiptune tracker + * Copyright (C) 2021-2022 tildearrow and contributors + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include "segapcm.h" +#include "../engine.h" +#include +#include + +//#define rWrite(a,v) if (!skipRegisterWrites) {pendingWrites[a]=v;} +//#define immWrite(a,v) if (!skipRegisterWrites) {writes.emplace(a,v); if (dumpWrites) {addWrite(a,v);} } + +const char* DivPlatformSegaPCM::getEffectName(unsigned char effect) { + switch (effect) { + case 0x20: + return "20xx: Set PCM frequency"; + break; + } + return NULL; +} + +void DivPlatformSegaPCM::acquire(short* bufL, short* bufR, size_t start, size_t len) { + static int os[2]; + + for (size_t h=start; h=0 && chan[i].pcm.samplesong.sampleLen) { + DivSample* s=parent->song.sample[chan[i].pcm.sample]; + if (s->rendLength<=0) { + chan[i].pcm.sample=-1; + continue; + } + if (!isMuted[i]) { + if (s->depth==8) { + pcmL+=(s->rendData[chan[i].pcm.pos>>8]*chan[i].chVolL); + pcmR+=(s->rendData[chan[i].pcm.pos>>8]*chan[i].chVolR); + } else { + pcmL+=(s->rendData[chan[i].pcm.pos>>8]*chan[i].chVolL)>>8; + pcmR+=(s->rendData[chan[i].pcm.pos>>8]*chan[i].chVolR)>>8; + } + } + chan[i].pcm.pos+=chan[i].pcm.freq; + if (chan[i].pcm.pos>=(s->rendLength<<8)) { + if (s->loopStart>=0 && s->loopStart<=(int)s->rendLength) { + chan[i].pcm.pos=s->loopStart<<8; + } else { + chan[i].pcm.sample=-1; + } + } + } + } + + os[0]=pcmL; + if (os[0]<-32768) os[0]=-32768; + if (os[0]>32767) os[0]=32767; + + os[1]=pcmR; + if (os[1]<-32768) os[1]=-32768; + if (os[1]>32767) os[1]=32767; + + bufL[h]=os[0]; + bufR[h]=os[1]; + } +} + +void DivPlatformSegaPCM::tick() { + for (int i=0; i<16; i++) { + chan[i].std.next(); + + if (chan[i].std.hadVol) { + chan[i].outVol=(chan[i].vol*MIN(127,chan[i].std.vol))/127; + } + + if (chan[i].std.hadArp) { + if (!chan[i].inPorta) { + if (chan[i].std.arpMode) { + chan[i].baseFreq=(chan[i].std.arp<<6)+baseFreqOff; + } else { + chan[i].baseFreq=((chan[i].note+(signed char)chan[i].std.arp)<<6)+baseFreqOff; + } + } + chan[i].freqChanged=true; + } else { + if (chan[i].std.arpMode && chan[i].std.finishedArp) { + chan[i].baseFreq=(chan[i].note<<6)+baseFreqOff; + chan[i].freqChanged=true; + } + } + /*if (chan[i].keyOn || chan[i].keyOff) { + chan[i].keyOff=false; + }*/ + } + + for (int i=0; i<16; i++) { + if (chan[i].freqChanged) { + chan[i].freq=chan[i].baseFreq+(chan[i].pitch>>1)-64; + if (chan[i].furnacePCM) { + double off=1.0; + if (chan[i].pcm.sample>=0 && chan[i].pcm.samplesong.sampleLen) { + DivSample* s=parent->song.sample[chan[i].pcm.sample]; + off=(double)s->centerRate/8363.0; + } + chan[i].pcm.freq=MIN(255,((off*parent->song.tuning*pow(2.0,double(chan[i].freq+256)/(64.0*12.0)))*255)/31250); + if (dumpWrites && i>=8) { + addWrite(0x10007+((i-8)<<3),chan[i].pcm.freq); + } + } + chan[i].freqChanged=false; + } + } +} + +void DivPlatformSegaPCM::muteChannel(int ch, bool mute) { + isMuted[ch]=mute; +} + +int DivPlatformSegaPCM::dispatch(DivCommand c) { + switch (c.cmd) { + case DIV_CMD_NOTE_ON: { + DivInstrument* ins=parent->getIns(chan[c.chan].ins); + if (skipRegisterWrites) break; + if (ins->type==DIV_INS_AMIGA) { + chan[c.chan].pcm.sample=ins->amiga.initSample; + if (chan[c.chan].pcm.sample<0 || chan[c.chan].pcm.sample>=parent->song.sampleLen) { + chan[c.chan].pcm.sample=-1; + if (dumpWrites) { + addWrite(0x10086+(c.chan<<3),3); + } + break; + } + chan[c.chan].pcm.pos=0; + chan[c.chan].baseFreq=(c.value<<6); + chan[c.chan].freqChanged=true; + chan[c.chan].furnacePCM=true; + if (dumpWrites) { // Sega PCM writes + DivSample* s=parent->song.sample[chan[c.chan].pcm.sample]; + addWrite(0x10086+(c.chan<<3),3+((s->rendOffP>>16)<<3)); + addWrite(0x10084+(c.chan<<3),(s->rendOffP)&0xff); + addWrite(0x10085+(c.chan<<3),(s->rendOffP>>8)&0xff); + addWrite(0x10006+(c.chan<<3),MIN(255,((s->rendOffP&0xffff)+s->rendLength-1)>>8)); + if (s->loopStart<0 || s->loopStart>=(int)s->rendLength) { + addWrite(0x10086+(c.chan<<3),2+((s->rendOffP>>16)<<3)); + } else { + int loopPos=(s->rendOffP&0xffff)+s->loopStart+s->loopOffP; + addWrite(0x10004+(c.chan<<3),loopPos&0xff); + addWrite(0x10005+(c.chan<<3),(loopPos>>8)&0xff); + addWrite(0x10086+(c.chan<<3),((s->rendOffP>>16)<<3)); + } + } + } else { + if (c.value!=DIV_NOTE_NULL) { + chan[c.chan].note=c.value; + } + chan[c.chan].pcm.sample=12*sampleBank+chan[c.chan].note%12; + if (chan[c.chan].pcm.sample>=parent->song.sampleLen) { + chan[c.chan].pcm.sample=-1; + if (dumpWrites) { + addWrite(0x10086+(c.chan<<3),3); + } + break; + } + chan[c.chan].pcm.pos=0; + chan[c.chan].pcm.freq=MIN(255,(parent->song.sample[chan[c.chan].pcm.sample]->rate*255)/31250); + chan[c.chan].furnacePCM=false; + if (dumpWrites) { // Sega PCM writes + DivSample* s=parent->song.sample[chan[c.chan].pcm.sample]; + addWrite(0x10086+(c.chan<<3),3+((s->rendOffP>>16)<<3)); + addWrite(0x10084+(c.chan<<3),(s->rendOffP)&0xff); + addWrite(0x10085+(c.chan<<3),(s->rendOffP>>8)&0xff); + addWrite(0x10006+(c.chan<<3),MIN(255,((s->rendOffP&0xffff)+s->rendLength-1)>>8)); + if (s->loopStart<0 || s->loopStart>=(int)s->rendLength) { + addWrite(0x10086+(c.chan<<3),2+((s->rendOffP>>16)<<3)); + } else { + int loopPos=(s->rendOffP&0xffff)+s->loopStart+s->loopOffP; + addWrite(0x10004+(c.chan<<3),loopPos&0xff); + addWrite(0x10005+(c.chan<<3),(loopPos>>8)&0xff); + addWrite(0x10086+(c.chan<<3),((s->rendOffP>>16)<<3)); + } + addWrite(0x10007+(c.chan<<3),chan[c.chan].pcm.freq); + } + } + break; + } + case DIV_CMD_NOTE_OFF: + chan[c.chan].pcm.sample=-1; + if (dumpWrites) { + addWrite(0x10086+(c.chan<<3),3); + } + chan[c.chan].keyOff=true; + chan[c.chan].keyOn=false; + chan[c.chan].active=false; + break; + case DIV_CMD_NOTE_OFF_ENV: + chan[c.chan].keyOff=true; + chan[c.chan].keyOn=false; + chan[c.chan].active=false; + chan[c.chan].std.release(); + break; + case DIV_CMD_ENV_RELEASE: + chan[c.chan].std.release(); + break; + case DIV_CMD_VOLUME: { + chan[c.chan].vol=c.value; + if (!chan[c.chan].std.hasVol) { + chan[c.chan].outVol=c.value; + } + chan[c.chan].chVolL=c.value; + chan[c.chan].chVolR=c.value; + if (dumpWrites) { + addWrite(0x10002+(c.chan<<3),chan[c.chan].chVolL); + addWrite(0x10003+(c.chan<<3),chan[c.chan].chVolR); + } + break; + } + case DIV_CMD_GET_VOLUME: { + return chan[c.chan].vol; + break; + } + case DIV_CMD_INSTRUMENT: + if (chan[c.chan].ins!=c.value || c.value2==1) { + chan[c.chan].insChanged=true; + } + chan[c.chan].ins=c.value; + break; + case DIV_CMD_PANNING: { + chan[c.chan].chVolL=(c.value>>4)|(((c.value>>4)>>1)<<4); + chan[c.chan].chVolR=(c.value&15)|(((c.value&15)>>1)<<4); + if (dumpWrites) { + addWrite(0x10002+(c.chan<<3),chan[c.chan].chVolL); + addWrite(0x10003+(c.chan<<3),chan[c.chan].chVolR); + } + break; + } + case DIV_CMD_PITCH: { + chan[c.chan].pitch=c.value; + chan[c.chan].freqChanged=true; + break; + } + case DIV_CMD_NOTE_PORTA: { + int destFreq=(c.value2<<6)+baseFreqOff; + int newFreq; + bool return2=false; + if (destFreq>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; + if (newFreq<=destFreq) { + newFreq=destFreq; + return2=true; + } + } + chan[c.chan].baseFreq=newFreq; + chan[c.chan].freqChanged=true; + if (return2) { + chan[c.chan].inPorta=false; + return 2; + } + break; + } + case DIV_CMD_LEGATO: { + chan[c.chan].baseFreq=(c.value<<6)+baseFreqOff; + chan[c.chan].freqChanged=true; + break; + } + case DIV_CMD_SAMPLE_BANK: + sampleBank=c.value; + if (sampleBank>(parent->song.sample.size()/12)) { + sampleBank=parent->song.sample.size()/12; + } + break; + case DIV_ALWAYS_SET_VOLUME: + return 0; + break; + case DIV_CMD_GET_VOLMAX: + return 127; + break; + case DIV_CMD_PRE_PORTA: + chan[c.chan].inPorta=c.value; + break; + case DIV_CMD_PRE_NOTE: + break; + case DIV_CMD_SAMPLE_FREQ: + chan[c.chan].pcm.freq=c.value; + if (dumpWrites) { + addWrite(0x10007+(c.chan<<3),chan[c.chan].pcm.freq); + } + break; + default: + //printf("WARNING: unimplemented command %d\n",c.cmd); + break; + } + return 1; +} + +void DivPlatformSegaPCM::forceIns() { + for (int i=0; i<16; i++) { + chan[i].insChanged=true; + } +} + +void DivPlatformSegaPCM::notifyInsChange(int ins) { + for (int i=0; i<16; i++) { + if (chan[i].ins==ins) { + chan[i].insChanged=true; + } + } +} + +void* DivPlatformSegaPCM::getChanState(int ch) { + return &chan[ch]; +} + +unsigned char* DivPlatformSegaPCM::getRegisterPool() { + return regPool; +} + +int DivPlatformSegaPCM::getRegisterPoolSize() { + return 256; +} + +void DivPlatformSegaPCM::poke(unsigned int addr, unsigned short val) { + //immWrite(addr,val); +} + +void DivPlatformSegaPCM::poke(std::vector& wlist) { + //for (DivRegWrite& i: wlist) immWrite(i.addr,i.val); +} + +void DivPlatformSegaPCM::reset() { + while (!writes.empty()) writes.pop(); + memset(regPool,0,256); + for (int i=0; i<16; i++) { + chan[i]=DivPlatformSegaPCM::Channel(); + chan[i].vol=0x7f; + chan[i].outVol=0x7f; + } + + lastBusy=60; + pcmCycles=0; + pcmL=0; + pcmR=0; + sampleBank=0; + delay=0; + amDepth=0x7f; + pmDepth=0x7f; + + if (dumpWrites) { + for (int i=0; i<16; i++) { + addWrite(0x10086+(i<<3),3); + addWrite(0x10002+(i<<3),0x7f); + addWrite(0x10003+(i<<3),0x7f); + } + } + + extMode=false; +} + +void DivPlatformSegaPCM::setFlags(unsigned int flags) { + chipClock=8000000.0; + rate=31250; +} + +bool DivPlatformSegaPCM::isStereo() { + return true; +} + +int DivPlatformSegaPCM::init(DivEngine* p, int channels, int sugRate, unsigned int flags) { + parent=p; + dumpWrites=false; + skipRegisterWrites=false; + for (int i=0; i<16; i++) { + isMuted[i]=false; + } + setFlags(flags); + reset(); + + return 16; +} + +void DivPlatformSegaPCM::quit() { +} + +DivPlatformSegaPCM::~DivPlatformSegaPCM() { +} diff --git a/src/engine/platform/segapcm.h b/src/engine/platform/segapcm.h new file mode 100644 index 000000000..b3c84cd3b --- /dev/null +++ b/src/engine/platform/segapcm.h @@ -0,0 +1,93 @@ +/** + * Furnace Tracker - multi-system chiptune tracker + * Copyright (C) 2021-2022 tildearrow and contributors + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#ifndef _SEGAPCM_H +#define _SEGAPCM_H +#include "../dispatch.h" +#include "../instrument.h" +#include +#include "../macroInt.h" + +class DivPlatformSegaPCM: public DivDispatch { + protected: + struct Channel { + DivMacroInt std; + unsigned char freqH, freqL; + int freq, baseFreq, pitch, note; + unsigned char ins; + signed char konCycles; + bool active, insChanged, freqChanged, keyOn, keyOff, inPorta, portaPause, furnacePCM; + int vol, outVol; + unsigned char chVolL, chVolR; + + struct PCMChannel { + int sample; + unsigned int pos; // <<8 + unsigned short len; + unsigned char freq; + PCMChannel(): sample(-1), pos(0), len(0), freq(0) {} + } pcm; + Channel(): freqH(0), freqL(0), freq(0), baseFreq(0), pitch(0), note(0), ins(-1), active(false), insChanged(true), freqChanged(false), keyOn(false), keyOff(false), inPorta(false), portaPause(false), furnacePCM(false), vol(0), outVol(0), chVolL(127), chVolR(127) {} + }; + Channel chan[16]; + struct QueuedWrite { + unsigned short addr; + unsigned char val; + bool addrOrVal; + QueuedWrite(unsigned short a, unsigned char v): addr(a), val(v), addrOrVal(false) {} + }; + std::queue writes; + int delay, baseFreqOff; + int pcmL, pcmR, pcmCycles; + unsigned char sampleBank; + unsigned char lastBusy; + unsigned char amDepth, pmDepth; + + unsigned char regPool[256]; + + bool extMode, useYMFM; + + bool isMuted[16]; + + short oldWrites[256]; + short pendingWrites[256]; + + friend void putDispatchChan(void*,int,int); + + public: + void acquire(short* bufL, short* bufR, size_t start, size_t len); + int dispatch(DivCommand c); + void* getChanState(int chan); + unsigned char* getRegisterPool(); + int getRegisterPoolSize(); + void reset(); + void forceIns(); + void tick(); + void muteChannel(int ch, bool mute); + void notifyInsChange(int ins); + void setFlags(unsigned int flags); + bool isStereo(); + void poke(unsigned int addr, unsigned short val); + void poke(std::vector& wlist); + const char* getEffectName(unsigned char effect); + int init(DivEngine* parent, int channels, int sugRate, unsigned int flags); + void quit(); + ~DivPlatformSegaPCM(); +}; +#endif diff --git a/src/engine/playback.cpp b/src/engine/playback.cpp index 81220f644..e5a4d9206 100644 --- a/src/engine/playback.cpp +++ b/src/engine/playback.cpp @@ -18,6 +18,7 @@ */ #include "blip_buf.h" +#include "song.h" #include "wavetable.h" #define _USE_MATH_DEFINES #include "dispatch.h" @@ -147,9 +148,8 @@ int DivEngine::dispatchCmd(DivCommand c) { bool DivEngine::perSystemEffect(int ch, unsigned char effect, unsigned char effectVal) { switch (sysOfChan[ch]) { - case DIV_SYSTEM_GENESIS: - case DIV_SYSTEM_GENESIS_EXT: case DIV_SYSTEM_YM2612: + case DIV_SYSTEM_YM2612_EXT: switch (effect) { case 0x17: // DAC enable dispatchCmd(DivCommand(DIV_CMD_SAMPLE_MODE,ch,(effectVal>0))); @@ -241,10 +241,10 @@ bool DivEngine::perSystemEffect(int ch, unsigned char effect, unsigned char effe dispatchCmd(DivCommand(DIV_CMD_QSOUND_ECHO_LEVEL,ch,effectVal)); break; default: - if ((effect & 0xf0)==0x30) { - dispatchCmd(DivCommand(DIV_CMD_QSOUND_ECHO_DELAY,ch,((effect & 0x0f) << 8) | effectVal)); + if ((effect&0xf0)==0x30) { + dispatchCmd(DivCommand(DIV_CMD_QSOUND_ECHO_DELAY,ch,((effect & 0x0f) << 8) | effectVal)); } else { - return false; + return false; } break; } @@ -257,16 +257,14 @@ bool DivEngine::perSystemEffect(int ch, unsigned char effect, unsigned char effe bool DivEngine::perSystemPostEffect(int ch, unsigned char effect, unsigned char effectVal) { switch (sysOfChan[ch]) { - case DIV_SYSTEM_GENESIS: - case DIV_SYSTEM_GENESIS_EXT: case DIV_SYSTEM_YM2612: - case DIV_SYSTEM_ARCADE: + case DIV_SYSTEM_YM2612_EXT: case DIV_SYSTEM_YM2151: case DIV_SYSTEM_YM2610: case DIV_SYSTEM_YM2610_EXT: switch (effect) { case 0x10: // LFO or noise mode - if (sysOfChan[ch]==DIV_SYSTEM_ARCADE || sysOfChan[ch]==DIV_SYSTEM_YM2151) { + if (sysOfChan[ch]==DIV_SYSTEM_YM2151) { dispatchCmd(DivCommand(DIV_CMD_STD_NOISE_FREQ,ch,effectVal)); } else { dispatchCmd(DivCommand(DIV_CMD_FM_LFO,ch,effectVal)); @@ -293,12 +291,12 @@ bool DivEngine::perSystemPostEffect(int ch, unsigned char effect, unsigned char } break; case 0x17: // arcade LFO - if (sysOfChan[ch]==DIV_SYSTEM_ARCADE || sysOfChan[ch]==DIV_SYSTEM_YM2151) { + if (sysOfChan[ch]==DIV_SYSTEM_YM2151) { dispatchCmd(DivCommand(DIV_CMD_FM_LFO,ch,effectVal)); } break; case 0x18: // EXT or LFO waveform - if (sysOfChan[ch]==DIV_SYSTEM_ARCADE || sysOfChan[ch]==DIV_SYSTEM_YM2151) { + if (sysOfChan[ch]==DIV_SYSTEM_YM2151) { dispatchCmd(DivCommand(DIV_CMD_FM_LFO_WAVE,ch,effectVal)); } else { dispatchCmd(DivCommand(DIV_CMD_FM_EXTCH,ch,effectVal)); @@ -325,10 +323,8 @@ bool DivEngine::perSystemPostEffect(int ch, unsigned char effect, unsigned char case 0x1f: // UNOFFICIAL: Arcade PM depth dispatchCmd(DivCommand(DIV_CMD_FM_PM_DEPTH,ch,effectVal&127)); break; - case 0x20: // PCM frequency or Neo Geo PSG mode - if (sysOfChan[ch]==DIV_SYSTEM_ARCADE || sysOfChan[ch]==DIV_SYSTEM_YM2151) { - dispatchCmd(DivCommand(DIV_CMD_SAMPLE_FREQ,ch,effectVal)); - } else if (sysOfChan[ch]==DIV_SYSTEM_YM2610 || sysOfChan[ch]==DIV_SYSTEM_YM2610_EXT) { + case 0x20: // Neo Geo PSG mode + if (sysOfChan[ch]==DIV_SYSTEM_YM2610 || sysOfChan[ch]==DIV_SYSTEM_YM2610_EXT) { dispatchCmd(DivCommand(DIV_CMD_STD_NOISE_MODE,ch,effectVal)); } break; @@ -453,6 +449,8 @@ bool DivEngine::perSystemPostEffect(int ch, unsigned char effect, unsigned char case 0x29: // auto-envelope dispatchCmd(DivCommand(DIV_CMD_AY_AUTO_ENVELOPE,ch,effectVal)); break; + default: + return false; } break; case DIV_SYSTEM_SAA1099: @@ -466,6 +464,8 @@ bool DivEngine::perSystemPostEffect(int ch, unsigned char effect, unsigned char case 0x12: // setup envelope dispatchCmd(DivCommand(DIV_CMD_SAA_ENVELOPE,ch,effectVal)); break; + default: + return false; } break; case DIV_SYSTEM_TIA: @@ -473,6 +473,18 @@ bool DivEngine::perSystemPostEffect(int ch, unsigned char effect, unsigned char case 0x10: // select waveform dispatchCmd(DivCommand(DIV_CMD_WAVE,ch,effectVal)); break; + default: + return false; + } + break; + case DIV_SYSTEM_SEGAPCM: + case DIV_SYSTEM_SEGAPCM_COMPAT: + switch (effect) { + case 0x20: // PCM frequency + dispatchCmd(DivCommand(DIV_CMD_SAMPLE_FREQ,ch,effectVal)); + break; + default: + return false; } break; default: @@ -755,7 +767,7 @@ void DivEngine::processRow(int i, bool afterDelay) { break; case 0xe5: // pitch chan[i].pitch=effectVal-0x80; - if (sysOfChan[i]==DIV_SYSTEM_ARCADE || sysOfChan[i]==DIV_SYSTEM_YM2151) { // YM2151 pitch oddity + if (sysOfChan[i]==DIV_SYSTEM_YM2151) { // YM2151 pitch oddity chan[i].pitch*=2; if (chan[i].pitch<-128) chan[i].pitch=-128; if (chan[i].pitch>127) chan[i].pitch=127; diff --git a/src/engine/song.h b/src/engine/song.h index 724ba4121..384f8674c 100644 --- a/src/engine/song.h +++ b/src/engine/song.h @@ -34,16 +34,16 @@ enum DivSystem { DIV_SYSTEM_NULL=0, DIV_SYSTEM_YMU759, - DIV_SYSTEM_GENESIS, - DIV_SYSTEM_GENESIS_EXT, + DIV_SYSTEM_GENESIS, // ** COMPOUND SYSTEM - DO NOT USE! ** + DIV_SYSTEM_GENESIS_EXT, // ** COMPOUND SYSTEM - DO NOT USE! ** DIV_SYSTEM_SMS, - DIV_SYSTEM_SMS_OPLL, + DIV_SYSTEM_SMS_OPLL, // ** COMPOUND SYSTEM - DO NOT USE! ** DIV_SYSTEM_GB, DIV_SYSTEM_PCE, DIV_SYSTEM_NES, DIV_SYSTEM_C64_6581, DIV_SYSTEM_C64_8580, - DIV_SYSTEM_ARCADE, + DIV_SYSTEM_ARCADE, // ** COMPOUND SYSTEM - DO NOT USE! ** DIV_SYSTEM_YM2610, DIV_SYSTEM_YM2610_EXT, @@ -88,7 +88,8 @@ enum DivSystem { DIV_SYSTEM_YM2610_FULL_EXT, DIV_SYSTEM_OPLL_DRUMS, DIV_SYSTEM_LYNX, - DIV_SYSTEM_QSOUND + DIV_SYSTEM_QSOUND, + DIV_SYSTEM_SEGAPCM_COMPAT }; struct DivSong { @@ -278,7 +279,7 @@ struct DivSong { DivSong(): version(0), isDMF(false), - systemLen(1), + systemLen(2), name(""), author(""), carrier(""), @@ -331,7 +332,8 @@ struct DivSong { chanShow[i]=true; chanCollapse[i]=false; } - system[0]=DIV_SYSTEM_GENESIS; + system[0]=DIV_SYSTEM_YM2612; + system[1]=DIV_SYSTEM_SMS; } }; diff --git a/src/engine/sysDef.cpp b/src/engine/sysDef.cpp index 4e7bb3bcd..6b1351622 100644 --- a/src/engine/sysDef.cpp +++ b/src/engine/sysDef.cpp @@ -131,6 +131,8 @@ DivSystem DivEngine::systemFromFile(unsigned char val) { return DIV_SYSTEM_OPLL_DRUMS; case 0xa8: return DIV_SYSTEM_LYNX; + case 0xa9: + return DIV_SYSTEM_SEGAPCM_COMPAT; case 0xe0: return DIV_SYSTEM_QSOUND; } @@ -248,6 +250,8 @@ unsigned char DivEngine::systemToFile(DivSystem val) { return 0xa7; case DIV_SYSTEM_LYNX: return 0xa8; + case DIV_SYSTEM_SEGAPCM_COMPAT: + return 0xa9; case DIV_SYSTEM_QSOUND: return 0xe0; @@ -364,6 +368,8 @@ int DivEngine::getChannelCount(DivSystem sys) { return 11; case DIV_SYSTEM_LYNX: return 4; + case DIV_SYSTEM_SEGAPCM_COMPAT: + return 5; case DIV_SYSTEM_QSOUND: return 19; } @@ -374,7 +380,132 @@ int DivEngine::getTotalChannelCount() { return chans; } -// TODO: replace with a better strategy to determine name +const char* DivEngine::getSongSystemName() { + switch (song.systemLen) { + case 0: + return "help! what's going on!"; + case 1: + if (song.system[0]==DIV_SYSTEM_AY8910) { + switch (song.systemFlags[0]&0x3f) { + case 0: // AY-3-8910, 1.79MHz + case 1: // AY-3-8910, 1.77MHz + case 2: // AY-3-8910, 1.75MHz + return "ZX Spectrum"; + case 3: // AY-3-8910, 2MHz + return "Fujitsu Micro-7"; + case 4: // AY-3-8910, 1.5MHz + return "Vectrex"; + case 5: // AY-3-8910, 1MHz + return "Amstrad CPC"; + case 6: // AY-3-8910, 0.somethingMhz + return "Intellivision"; + case 8: // AY-3-8910, 0.somethingMhz + return "Intellivision (PAL)"; + + case 0x10: // YM2149, 1.79MHz + return "MSX"; + case 0x13: // YM2149, 2MHz + return "Atari ST"; + case 0x26: // 5B NTSC + return "Sunsoft 5B standalone"; + case 0x28: // 5B PAL + return "Sunsoft 5B standalone (PAL)"; + + default: + if ((song.systemFlags[0]&0x30)==0x00) { + return "AY-3-8910"; + } else if ((song.systemFlags[0]&0x30)==0x10) { + return "Yamaha YM2149"; + } else if ((song.systemFlags[0]&0x30)==0x20) { + return "Overclocked Sunsoft 5B"; + } + } + } else if (song.system[0]==DIV_SYSTEM_SMS) { + switch (song.systemFlags[0]&0x0f) { + case 0: case 1: + return "Sega Master System"; + case 6: + return "BBC Micro"; + } + } else if (song.system[0]==DIV_SYSTEM_YM2612) { + switch (song.systemFlags[0]&3) { + case 2: + return "FM Towns"; + } + } else if (song.system[0]==DIV_SYSTEM_YM2151) { + switch (song.systemFlags[0]&3) { + case 2: + return "Sharp X68000"; + } + } else if (song.system[0]==DIV_SYSTEM_SAA1099) { + switch (song.systemFlags[0]&3) { + case 0: + return "SAM Coupé"; + } + } + return getSystemName(song.system[0]); + case 2: + if (song.system[0]==DIV_SYSTEM_YM2612 && song.system[1]==DIV_SYSTEM_SMS) { + return "Sega Genesis/Mega Drive"; + } + if (song.system[0]==DIV_SYSTEM_YM2612_EXT && song.system[1]==DIV_SYSTEM_SMS) { + return "Sega Genesis Extended Channel 3"; + } + + if (song.system[0]==DIV_SYSTEM_C64_6581 && song.system[1]==DIV_SYSTEM_C64_6581) { + return "Commodore 64 with dual 6581"; + } + if (song.system[0]==DIV_SYSTEM_C64_8580 && song.system[1]==DIV_SYSTEM_C64_8580) { + return "Commodore 64 with dual 8580"; + } + + if (song.system[0]==DIV_SYSTEM_YM2151 && song.system[1]==DIV_SYSTEM_SEGAPCM_COMPAT) { + return "YM2151 + SegaPCM Arcade (compatibility)"; + } + if (song.system[0]==DIV_SYSTEM_YM2151 && song.system[1]==DIV_SYSTEM_SEGAPCM) { + return "YM2151 + SegaPCM Arcade"; + } + + if (song.system[0]==DIV_SYSTEM_SAA1099 && song.system[1]==DIV_SYSTEM_SAA1099) { + return "Creative Music System"; + } + + if (song.system[0]==DIV_SYSTEM_GB && song.system[1]==DIV_SYSTEM_AY8910) { + return "Game Boy with AY expansion"; + } + + if (song.system[0]==DIV_SYSTEM_NES && song.system[1]==DIV_SYSTEM_VRC6) { + return "NES + Konami VRC6"; + } + if (song.system[0]==DIV_SYSTEM_NES && song.system[1]==DIV_SYSTEM_VRC7) { + return "NES + Konami VRC7"; + } + if (song.system[0]==DIV_SYSTEM_NES && song.system[1]==DIV_SYSTEM_OPLL) { + return "NES + Yamaha OPLL"; + } + if (song.system[0]==DIV_SYSTEM_NES && song.system[1]==DIV_SYSTEM_FDS) { + return "Famicom Disk System"; + } + if (song.system[0]==DIV_SYSTEM_NES && song.system[1]==DIV_SYSTEM_N163) { + return "NES + Namco 163"; + } + if (song.system[0]==DIV_SYSTEM_NES && song.system[1]==DIV_SYSTEM_MMC5) { + return "NES + MMC5"; + } + if (song.system[0]==DIV_SYSTEM_NES && song.system[1]==DIV_SYSTEM_AY8910) { + return "NES + Sunsoft 5B"; + } + + if (song.system[0]==DIV_SYSTEM_AY8910 && song.system[1]==DIV_SYSTEM_AY8910) { + return "Bally Midway MCR"; + } + break; + case 3: + break; + } + return "multi-system"; +} + const char* DivEngine::getSystemName(DivSystem sys) { switch (sys) { case DIV_SYSTEM_NULL: @@ -384,7 +515,7 @@ const char* DivEngine::getSystemName(DivSystem sys) { case DIV_SYSTEM_GENESIS: return "Sega Genesis/Mega Drive"; case DIV_SYSTEM_SMS: - return "Sega Master System"; + return "TI SN76489"; case DIV_SYSTEM_SMS_OPLL: return "Sega Master System + FM Expansion"; case DIV_SYSTEM_GB: @@ -431,7 +562,7 @@ const char* DivEngine::getSystemName(DivSystem sys) { case DIV_SYSTEM_OPLL: return "Yamaha OPLL"; case DIV_SYSTEM_FDS: - return "Famicom Disk System"; + return "Famicom Disk System (chip)"; case DIV_SYSTEM_MMC5: return "MMC5"; case DIV_SYSTEM_N163: @@ -443,7 +574,7 @@ const char* DivEngine::getSystemName(DivSystem sys) { case DIV_SYSTEM_OPL: return "Yamaha OPL"; case DIV_SYSTEM_OPL2: - return "Adlib Music Synthesizer Card"; + return "Yamaha OPL2"; case DIV_SYSTEM_OPL3: return "Yamaha OPL3"; case DIV_SYSTEM_MULTIPCM: @@ -457,7 +588,7 @@ const char* DivEngine::getSystemName(DivSystem sys) { case DIV_SYSTEM_SWAN: return "WonderSwan"; case DIV_SYSTEM_SAA1099: - return "SAM Coupé"; + return "Philips SAA1099"; case DIV_SYSTEM_OPZ: return "Yamaha TX81Z/YS200"; case DIV_SYSTEM_POKEMINI: @@ -488,6 +619,8 @@ const char* DivEngine::getSystemName(DivSystem sys) { return "Yamaha OPLL with drums"; case DIV_SYSTEM_LYNX: return "Atari Lynx"; + case DIV_SYSTEM_SEGAPCM_COMPAT: + return "SegaPCM (compatible 5-channel mode)"; case DIV_SYSTEM_QSOUND: return "Capcom QSound"; } @@ -607,6 +740,8 @@ const char* DivEngine::getSystemChips(DivSystem sys) { return "Yamaha YM2413 with drums"; case DIV_SYSTEM_LYNX: return "Mikey"; + case DIV_SYSTEM_SEGAPCM_COMPAT: + return "SegaPCM (compatible 5-channel mode)"; case DIV_SYSTEM_QSOUND: return "Capcom DL-1425"; } @@ -954,6 +1089,7 @@ const char* DivEngine::getChannelName(int chan) { break; case DIV_SYSTEM_MULTIPCM: case DIV_SYSTEM_SEGAPCM: + case DIV_SYSTEM_SEGAPCM_COMPAT: return chanNames[28][dispatchChanOfChan[chan]]; break; case DIV_SYSTEM_PCSPKR: @@ -1088,6 +1224,7 @@ const char* DivEngine::getChannelShortName(int chan) { break; case DIV_SYSTEM_MULTIPCM: case DIV_SYSTEM_SEGAPCM: + case DIV_SYSTEM_SEGAPCM_COMPAT: return chanShortNames[28][dispatchChanOfChan[chan]]; break; case DIV_SYSTEM_PCSPKR: @@ -1219,6 +1356,7 @@ int DivEngine::getChannelType(int chan) { break; case DIV_SYSTEM_MULTIPCM: case DIV_SYSTEM_SEGAPCM: + case DIV_SYSTEM_SEGAPCM_COMPAT: case DIV_SYSTEM_QSOUND: return chanTypes[28][dispatchChanOfChan[chan]]; break; @@ -1346,6 +1484,7 @@ DivInstrumentType DivEngine::getPreferInsType(int chan) { break; case DIV_SYSTEM_MULTIPCM: case DIV_SYSTEM_SEGAPCM: + case DIV_SYSTEM_SEGAPCM_COMPAT: case DIV_SYSTEM_QSOUND: return chanPrefType[28][dispatchChanOfChan[chan]]; break; @@ -1401,21 +1540,21 @@ DivInstrumentType DivEngine::getPreferInsType(int chan) { bool DivEngine::isVGMExportable(DivSystem which) { switch (which) { - case DIV_SYSTEM_GENESIS: - case DIV_SYSTEM_GENESIS_EXT: case DIV_SYSTEM_SMS: case DIV_SYSTEM_GB: case DIV_SYSTEM_PCE: case DIV_SYSTEM_NES: - case DIV_SYSTEM_ARCADE: case DIV_SYSTEM_YM2151: case DIV_SYSTEM_YM2612: + case DIV_SYSTEM_YM2612_EXT: case DIV_SYSTEM_YM2610: case DIV_SYSTEM_YM2610_EXT: case DIV_SYSTEM_AY8910: case DIV_SYSTEM_AY8930: case DIV_SYSTEM_SAA1099: case DIV_SYSTEM_QSOUND: + case DIV_SYSTEM_SEGAPCM: + case DIV_SYSTEM_SEGAPCM_COMPAT: return true; default: return false; diff --git a/src/engine/vgmOps.cpp b/src/engine/vgmOps.cpp index 8518c5ef9..0d5eb521c 100644 --- a/src/engine/vgmOps.cpp +++ b/src/engine/vgmOps.cpp @@ -975,10 +975,11 @@ SafeWriter* DivEngine::saveVGM(bool* sysToExport, bool loop) { } if (writeQSound && qsoundMemLen>0) { - // always write a whole bank - unsigned int blockSize=(qsoundMemLen + 0xffff) & ~0xffff; - if(blockSize > 0x1000000) - blockSize = 0x1000000; + // always write a whole bank + unsigned int blockSize=(qsoundMemLen+0xffff)&(~0xffff); + if (blockSize > 0x1000000) { + blockSize = 0x1000000; + } w->writeC(0x67); w->writeC(0x66); w->writeC(0x8F); diff --git a/src/gui/debug.cpp b/src/gui/debug.cpp index 08febdc0c..8b06f6dec 100644 --- a/src/gui/debug.cpp +++ b/src/gui/debug.cpp @@ -220,12 +220,6 @@ void putDispatchChan(void* data, int chanNum, int type) { ImGui::Text("- outVol: %.2x",ch->outVol); ImGui::Text("- chVolL: %.2x",ch->chVolL); ImGui::Text("- chVolR: %.2x",ch->chVolR); - ImGui::Text("* PCM:"); - ImGui::Text(" - sample: %d",ch->pcm.sample); - ImGui::Text(" - pos: %d",ch->pcm.pos>>8); - ImGui::Text(" - subPos: %d",ch->pcm.pos&0xff); - ImGui::Text(" - len: %d",ch->pcm.len); - ImGui::Text(" - freq: %.2x",ch->pcm.freq); ImGui::TextColored(ch->active?colorOn:colorOff,">> Active"); ImGui::TextColored(ch->insChanged?colorOn:colorOff,">> InsChanged"); ImGui::TextColored(ch->freqChanged?colorOn:colorOff,">> FreqChanged"); diff --git a/src/gui/gui.cpp b/src/gui/gui.cpp index 80c6fc7c6..94c371982 100644 --- a/src/gui/gui.cpp +++ b/src/gui/gui.cpp @@ -422,12 +422,10 @@ void FurnaceGUI::setFileName(String name) { } void FurnaceGUI::updateWindowTitle() { - String type=getSystemName(e->song.system[0]); - if (e->song.systemLen>1) type="multi-system"; if (e->song.name.empty()) { - SDL_SetWindowTitle(sdlWin,fmt::sprintf("Furnace (%s)",type).c_str()); + SDL_SetWindowTitle(sdlWin,fmt::sprintf("Furnace (%s)",e->getSongSystemName()).c_str()); } else { - SDL_SetWindowTitle(sdlWin,fmt::sprintf("%s - Furnace (%s)",e->song.name,type).c_str()); + SDL_SetWindowTitle(sdlWin,fmt::sprintf("%s - Furnace (%s)",e->song.name,e->getSongSystemName()).c_str()); } } @@ -1507,9 +1505,12 @@ const char* aboutLine[]={ "", "-- program --", "tildearrow", + "laoo", + "superctr", "", - "-- graphics --", + "-- graphics/UI design --", "tildearrow", + "BlastBrothers", "", "-- documentation --", "tildearrow", @@ -2096,9 +2097,9 @@ void FurnaceGUI::drawRegView() { for (int i=0; isong.systemLen; i++) { ImGui::Text("%d. %s",i+1,getSystemName(e->song.system[i])); int size=0; - int depth=8; + int depth=8; unsigned char* regPool=e->getRegisterPool(i,size,depth); - unsigned short* regPoolW=(unsigned short*) regPool; + unsigned short* regPoolW=(unsigned short*)regPool; if (regPool==NULL) { ImGui::Text("- no register pool available"); } else { @@ -2117,12 +2118,13 @@ void FurnaceGUI::drawRegView() { for (int j=0; j<16; j++) { ImGui::TableNextColumn(); if (i*16+j>=size) continue; - if(depth == 8) - ImGui::Text("%.2x",regPool[i*16+j]); - else if(depth == 16) - ImGui::Text("%.4x",regPoolW[i*16+j]); - else - ImGui::Text("??"); + if (depth == 8) { + ImGui::Text("%.2x",regPool[i*16+j]); + } else if (depth == 16) { + ImGui::Text("%.4x",regPoolW[i*16+j]); + } else { + ImGui::Text("??"); + } } } ImGui::EndTable(); @@ -3912,6 +3914,7 @@ bool dirExists(String what) { void FurnaceGUI::openFileDialog(FurnaceGUIFileDialogs type) { if (!dirExists(workingDir)) workingDir=getHomeDir(); + ImGuiFileDialog::Instance()->DpiScale=dpiScale; switch (type) { case GUI_FILE_OPEN: ImGuiFileDialog::Instance()->OpenModal("FileDialog","Open File","compatible files{.fur,.dmf},.*",workingDir); @@ -4473,23 +4476,23 @@ bool FurnaceGUI::loop() { } ImGui::Separator(); if (ImGui::BeginMenu("add system...")) { - sysAddOption(DIV_SYSTEM_GENESIS); - sysAddOption(DIV_SYSTEM_GENESIS_EXT); + sysAddOption(DIV_SYSTEM_YM2612); + sysAddOption(DIV_SYSTEM_YM2612_EXT); sysAddOption(DIV_SYSTEM_SMS); sysAddOption(DIV_SYSTEM_GB); sysAddOption(DIV_SYSTEM_PCE); sysAddOption(DIV_SYSTEM_NES); sysAddOption(DIV_SYSTEM_C64_8580); sysAddOption(DIV_SYSTEM_C64_6581); - sysAddOption(DIV_SYSTEM_ARCADE); + sysAddOption(DIV_SYSTEM_YM2151); + sysAddOption(DIV_SYSTEM_SEGAPCM); + sysAddOption(DIV_SYSTEM_SEGAPCM_COMPAT); sysAddOption(DIV_SYSTEM_YM2610); sysAddOption(DIV_SYSTEM_YM2610_EXT); sysAddOption(DIV_SYSTEM_YM2610_FULL); sysAddOption(DIV_SYSTEM_YM2610_FULL_EXT); sysAddOption(DIV_SYSTEM_AY8910); sysAddOption(DIV_SYSTEM_AMIGA); - sysAddOption(DIV_SYSTEM_YM2151); - sysAddOption(DIV_SYSTEM_YM2612); sysAddOption(DIV_SYSTEM_TIA); sysAddOption(DIV_SYSTEM_SAA1099); sysAddOption(DIV_SYSTEM_AY8930); @@ -4504,23 +4507,28 @@ bool FurnaceGUI::loop() { bool restart=settings.restartOnFlagChange; bool sysPal=flags&1; switch (e->song.system[i]) { - case DIV_SYSTEM_GENESIS: - case DIV_SYSTEM_GENESIS_EXT: { + case DIV_SYSTEM_YM2612: + case DIV_SYSTEM_YM2612_EXT: { if (ImGui::RadioButton("NTSC (7.67MHz)",(flags&3)==0)) { e->setSysFlags(i,(flags&0x80000000)|0,restart); + updateWindowTitle(); } if (ImGui::RadioButton("PAL (7.61MHz)",(flags&3)==1)) { e->setSysFlags(i,(flags&0x80000000)|1,restart); + updateWindowTitle(); } if (ImGui::RadioButton("FM Towns (8MHz)",(flags&3)==2)) { e->setSysFlags(i,(flags&0x80000000)|2,restart); + updateWindowTitle(); } if (ImGui::RadioButton("AtGames Genesis (6.13MHz)",(flags&3)==3)) { e->setSysFlags(i,(flags&0x80000000)|3,restart); + updateWindowTitle(); } bool ladder=flags&0x80000000; if (ImGui::Checkbox("Enable DAC distortion",&ladder)) { e->setSysFlags(i,(flags&(~0x80000000))|(ladder?0x80000000:0),restart); + updateWindowTitle(); } break; } @@ -4528,22 +4536,28 @@ bool FurnaceGUI::loop() { ImGui::Text("Clock rate:"); if (ImGui::RadioButton("NTSC (3.58MHz)",(flags&3)==0)) { e->setSysFlags(i,(flags&(~3))|0,restart); + updateWindowTitle(); } if (ImGui::RadioButton("PAL (3.55MHz)",(flags&3)==1)) { e->setSysFlags(i,(flags&(~3))|1,restart); + updateWindowTitle(); } if (ImGui::RadioButton("BBC Micro (4MHz)",(flags&3)==2)) { e->setSysFlags(i,(flags&(~3))|2,restart); + updateWindowTitle(); } ImGui::Text("Chip type:"); if (ImGui::RadioButton("Sega VDP/Master System",((flags>>2)&3)==0)) { e->setSysFlags(i,(flags&(~12))|0,restart); + updateWindowTitle(); } if (ImGui::RadioButton("TI SN76489",((flags>>2)&3)==1)) { e->setSysFlags(i,(flags&(~12))|4,restart); + updateWindowTitle(); } if (ImGui::RadioButton("TI SN76489 with Atari-like short noise",((flags>>2)&3)==2)) { e->setSysFlags(i,(flags&(~12))|8,restart); + updateWindowTitle(); } /*if (ImGui::RadioButton("Game Gear",(flags>>2)==3)) { e->setSysFlags(i,(flags&3)|12); @@ -4552,30 +4566,36 @@ bool FurnaceGUI::loop() { bool noPhaseReset=flags&16; if (ImGui::Checkbox("Disable noise period change phase reset",&noPhaseReset)) { e->setSysFlags(i,(flags&(~16))|(noPhaseReset<<4),restart); + updateWindowTitle(); } break; } - case DIV_SYSTEM_ARCADE: case DIV_SYSTEM_YM2151: if (ImGui::RadioButton("NTSC (3.58MHz)",flags==0)) { e->setSysFlags(i,0,restart); + updateWindowTitle(); } if (ImGui::RadioButton("PAL (3.55MHz)",flags==1)) { e->setSysFlags(i,1,restart); + updateWindowTitle(); } if (ImGui::RadioButton("X68000 (4MHz)",flags==2)) { e->setSysFlags(i,2,restart); + updateWindowTitle(); } break; case DIV_SYSTEM_NES: if (ImGui::RadioButton("NTSC (1.79MHz)",flags==0)) { e->setSysFlags(i,0,restart); + updateWindowTitle(); } if (ImGui::RadioButton("PAL (1.67MHz)",flags==1)) { e->setSysFlags(i,1,restart); + updateWindowTitle(); } if (ImGui::RadioButton("Dendy (1.77MHz)",flags==2)) { e->setSysFlags(i,2,restart); + updateWindowTitle(); } break; case DIV_SYSTEM_AY8910: @@ -4583,47 +4603,60 @@ bool FurnaceGUI::loop() { ImGui::Text("Clock rate:"); if (ImGui::RadioButton("1.79MHz (ZX Spectrum/MSX NTSC)",(flags&15)==0)) { e->setSysFlags(i,(flags&(~15))|0,restart); + updateWindowTitle(); } if (ImGui::RadioButton("1.77MHz (ZX Spectrum/MSX PAL)",(flags&15)==1)) { e->setSysFlags(i,(flags&(~15))|1,restart); + updateWindowTitle(); } if (ImGui::RadioButton("1.75MHz (ZX Spectrum)",(flags&15)==2)) { e->setSysFlags(i,(flags&(~15))|2,restart); + updateWindowTitle(); } if (ImGui::RadioButton("2MHz (Atari ST)",(flags&15)==3)) { e->setSysFlags(i,(flags&(~15))|3,restart); + updateWindowTitle(); } if (ImGui::RadioButton("1.5MHz (Vectrex)",(flags&15)==4)) { e->setSysFlags(i,(flags&(~15))|4,restart); + updateWindowTitle(); } if (ImGui::RadioButton("1MHz (Amstrad CPC)",(flags&15)==5)) { e->setSysFlags(i,(flags&(~15))|5,restart); + updateWindowTitle(); } if (ImGui::RadioButton("0.89MHz (Sunsoft 5B)",(flags&15)==6)) { e->setSysFlags(i,(flags&(~15))|6,restart); + updateWindowTitle(); } if (ImGui::RadioButton("1.67MHz (?)",(flags&15)==7)) { e->setSysFlags(i,(flags&(~15))|7,restart); + updateWindowTitle(); } if (ImGui::RadioButton("0.83MHz (Sunsoft 5B on PAL)",(flags&15)==8)) { e->setSysFlags(i,(flags&(~15))|8,restart); + updateWindowTitle(); } if (e->song.system[i]==DIV_SYSTEM_AY8910) { ImGui::Text("Chip type:"); if (ImGui::RadioButton("AY-3-8910",(flags&0x30)==0)) { e->setSysFlags(i,(flags&(~0x30))|0,restart); + updateWindowTitle(); } if (ImGui::RadioButton("YM2149(F)",(flags&0x30)==16)) { e->setSysFlags(i,(flags&(~0x30))|16,restart); + updateWindowTitle(); } if (ImGui::RadioButton("Sunsoft 5B",(flags&0x30)==32)) { e->setSysFlags(i,(flags&(~0x30))|32,restart); + updateWindowTitle(); } } bool stereo=flags&0x40; ImGui::BeginDisabled((flags&0x30)==32); if (ImGui::Checkbox("Stereo##_AY_STEREO",&stereo)) { e->setSysFlags(i,(flags&(~0x40))|(stereo?0x40:0),restart); + updateWindowTitle(); } ImGui::EndDisabled(); break; @@ -4631,12 +4664,15 @@ bool FurnaceGUI::loop() { case DIV_SYSTEM_SAA1099: if (ImGui::RadioButton("SAM Coupé (8MHz)",flags==0)) { e->setSysFlags(i,0,restart); + updateWindowTitle(); } if (ImGui::RadioButton("NTSC (7.15MHz)",flags==1)) { e->setSysFlags(i,1,restart); + updateWindowTitle(); } if (ImGui::RadioButton("PAL (7.09MHz)",flags==2)) { e->setSysFlags(i,2,restart); + updateWindowTitle(); } break; case DIV_SYSTEM_AMIGA: { @@ -4646,6 +4682,7 @@ bool FurnaceGUI::loop() { if (stereoSep<0) stereoSep=0; if (stereoSep>127) stereoSep=127; e->setSysFlags(i,(flags&1)|((stereoSep&127)<<8),restart); + updateWindowTitle(); } /* TODO LATER: I want 0.5 out already if (ImGui::RadioButton("Amiga 500 (OCS)",(flags&2)==0)) { @@ -4657,6 +4694,7 @@ bool FurnaceGUI::loop() { sysPal=flags&1; if (ImGui::Checkbox("PAL",&sysPal)) { e->setSysFlags(i,(flags&2)|sysPal,restart); + updateWindowTitle(); } break; } @@ -4667,6 +4705,7 @@ bool FurnaceGUI::loop() { if (echoBufSize<0) echoBufSize=0; if (echoBufSize>2725) echoBufSize=2725; e->setSysFlags(i,(flags & ~4095) | ((2725 - echoBufSize) & 4095),restart); + updateWindowTitle(); } ImGui::Text("Echo feedback:"); int echoFeedback=(flags>>12)&255; @@ -4674,6 +4713,7 @@ bool FurnaceGUI::loop() { if (echoFeedback<0) echoFeedback=0; if (echoFeedback>255) echoFeedback=255; e->setSysFlags(i,(flags & ~(255 << 12)) | ((echoFeedback & 255) << 12),restart); + updateWindowTitle(); } break; } @@ -4688,6 +4728,7 @@ bool FurnaceGUI::loop() { default: if (ImGui::Checkbox("PAL",&sysPal)) { e->setSysFlags(i,sysPal,restart); + updateWindowTitle(); } break; } @@ -4699,23 +4740,23 @@ bool FurnaceGUI::loop() { if (ImGui::BeginMenu("change system...")) { for (int i=0; isong.systemLen; i++) { if (ImGui::BeginMenu(fmt::sprintf("%d. %s##_SYSC%d",i+1,getSystemName(e->song.system[i]),i).c_str())) { - sysChangeOption(i,DIV_SYSTEM_GENESIS); - sysChangeOption(i,DIV_SYSTEM_GENESIS_EXT); + sysChangeOption(i,DIV_SYSTEM_YM2612); + sysChangeOption(i,DIV_SYSTEM_YM2612_EXT); sysChangeOption(i,DIV_SYSTEM_SMS); sysChangeOption(i,DIV_SYSTEM_GB); sysChangeOption(i,DIV_SYSTEM_PCE); sysChangeOption(i,DIV_SYSTEM_NES); sysChangeOption(i,DIV_SYSTEM_C64_8580); sysChangeOption(i,DIV_SYSTEM_C64_6581); - sysChangeOption(i,DIV_SYSTEM_ARCADE); + sysChangeOption(i,DIV_SYSTEM_YM2151); + sysChangeOption(i,DIV_SYSTEM_SEGAPCM); + sysChangeOption(i,DIV_SYSTEM_SEGAPCM_COMPAT); sysChangeOption(i,DIV_SYSTEM_YM2610); sysChangeOption(i,DIV_SYSTEM_YM2610_EXT); sysChangeOption(i,DIV_SYSTEM_YM2610_FULL); sysChangeOption(i,DIV_SYSTEM_YM2610_FULL_EXT); sysChangeOption(i,DIV_SYSTEM_AY8910); sysChangeOption(i,DIV_SYSTEM_AMIGA); - sysChangeOption(i,DIV_SYSTEM_YM2151); - sysChangeOption(i,DIV_SYSTEM_YM2612); sysChangeOption(i,DIV_SYSTEM_TIA); sysChangeOption(i,DIV_SYSTEM_SAA1099); sysChangeOption(i,DIV_SYSTEM_AY8930); diff --git a/src/gui/insEdit.cpp b/src/gui/insEdit.cpp index 3ec50d629..33ce458bb 100644 --- a/src/gui/insEdit.cpp +++ b/src/gui/insEdit.cpp @@ -24,6 +24,7 @@ #include "guiConst.h" #include "intConst.h" #include +#include #include "plot_nolerp.h" const char* insTypes[24]={ @@ -786,29 +787,103 @@ void FurnaceGUI::drawInsEdit() { //56.0 controls vert scaling; default 96 drawFMEnv(op.tl,op.ar,op.dr,op.d2r,op.rr,op.sl,ImVec2(ImGui::GetContentRegionAvail().x,56.0*dpiScale)); //P(ImGui::SliderScalar(FM_NAME(FM_AR),ImGuiDataType_U8,&op.ar,&_ZERO,&_THIRTY_ONE)); - P(ImGui::SliderScalar(FM_NAME(FM_AR),ImGuiDataType_U8,&op.ar,&_THIRTY_ONE,&_ZERO)); - P(ImGui::SliderScalar(FM_NAME(FM_DR),ImGuiDataType_U8,&op.dr,&_THIRTY_ONE,&_ZERO)); - P(ImGui::SliderScalar(FM_NAME(FM_SL),ImGuiDataType_U8,&op.sl,&_FIFTEEN,&_ZERO)); - P(ImGui::SliderScalar(FM_NAME(FM_D2R),ImGuiDataType_U8,&op.d2r,&_THIRTY_ONE,&_ZERO)); - P(ImGui::SliderScalar(FM_NAME(FM_RR),ImGuiDataType_U8,&op.rr,&_FIFTEEN,&_ZERO)); - P(ImGui::SliderScalar(FM_NAME(FM_TL),ImGuiDataType_U8,&op.tl,&_ONE_HUNDRED_TWENTY_SEVEN,&_ZERO)); + if (ImGui::BeginTable("opParams",2,ImGuiTableFlags_SizingStretchProp)) { + ImGui::TableSetupColumn("c0",ImGuiTableColumnFlags_WidthStretch,0.0); \ + ImGui::TableSetupColumn("c1",ImGuiTableColumnFlags_WidthFixed,0.0); \ - ImGui::Separator(); + ImGui::TableNextRow(); + ImGui::TableNextColumn(); + ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x); + P(ImGui::SliderScalar("##AR",ImGuiDataType_U8,&op.ar,&_THIRTY_ONE,&_ZERO)); + ImGui::TableNextColumn(); + ImGui::Text("%s",FM_NAME(FM_AR)); - P(ImGui::SliderScalar(FM_NAME(FM_RS),ImGuiDataType_U8,&op.rs,&_ZERO,&_THREE)); - P(ImGui::SliderScalar(FM_NAME(FM_MULT),ImGuiDataType_U8,&op.mult,&_ZERO,&_FIFTEEN)); - - int detune=(op.dt&7)-3; - if (ImGui::SliderInt(FM_NAME(FM_DT),&detune,-3,3)) { PARAMETER - op.dt=detune+3; + ImGui::TableNextRow(); + ImGui::TableNextColumn(); + ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x); + P(ImGui::SliderScalar("##DR",ImGuiDataType_U8,&op.dr,&_THIRTY_ONE,&_ZERO)); + ImGui::TableNextColumn(); + ImGui::Text("%s",FM_NAME(FM_DR)); + + ImGui::TableNextRow(); + ImGui::TableNextColumn(); + ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x); + P(ImGui::SliderScalar("##SL",ImGuiDataType_U8,&op.sl,&_FIFTEEN,&_ZERO)); + ImGui::TableNextColumn(); + ImGui::Text("%s",FM_NAME(FM_SL)); + + ImGui::TableNextRow(); + ImGui::TableNextColumn(); + ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x); + P(ImGui::SliderScalar("##D2R",ImGuiDataType_U8,&op.d2r,&_THIRTY_ONE,&_ZERO)); + ImGui::TableNextColumn(); + ImGui::Text("%s",FM_NAME(FM_D2R)); + + ImGui::TableNextRow(); + ImGui::TableNextColumn(); + ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x); + P(ImGui::SliderScalar("##RR",ImGuiDataType_U8,&op.rr,&_FIFTEEN,&_ZERO)); + ImGui::TableNextColumn(); + ImGui::Text("%s",FM_NAME(FM_RR)); + + ImGui::TableNextRow(); + ImGui::TableNextColumn(); + ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x); + P(ImGui::SliderScalar("##TL",ImGuiDataType_U8,&op.tl,&_ONE_HUNDRED_TWENTY_SEVEN,&_ZERO)); + ImGui::TableNextColumn(); + ImGui::Text("%s",FM_NAME(FM_TL)); + + ImGui::TableNextRow(); + ImGui::TableNextColumn(); + ImGui::Separator(); + ImGui::TableNextColumn(); + ImGui::Separator(); + + ImGui::TableNextRow(); + ImGui::TableNextColumn(); + ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x); + P(ImGui::SliderScalar("##RS",ImGuiDataType_U8,&op.rs,&_ZERO,&_THREE)); + ImGui::TableNextColumn(); + ImGui::Text("%s",FM_NAME(FM_RS)); + + ImGui::TableNextRow(); + ImGui::TableNextColumn(); + ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x); + P(ImGui::SliderScalar(FM_NAME(FM_MULT),ImGuiDataType_U8,&op.mult,&_ZERO,&_FIFTEEN)); + ImGui::TableNextColumn(); + ImGui::Text("%s",FM_NAME(FM_MULT)); + + int detune=(op.dt&7)-3; + ImGui::TableNextRow(); + ImGui::TableNextColumn(); + ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x); + if (ImGui::SliderInt("##DT",&detune,-3,3)) { PARAMETER + op.dt=detune+3; + } + ImGui::TableNextColumn(); + ImGui::Text("%s",FM_NAME(FM_DT)); + + ImGui::TableNextRow(); + ImGui::TableNextColumn(); + ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x); + P(ImGui::SliderScalar("##DT2",ImGuiDataType_U8,&op.dt2,&_ZERO,&_THREE)); + if (ImGui::IsItemHovered()) { + ImGui::SetTooltip("Only for Arcade system"); + } + ImGui::TableNextColumn(); + ImGui::Text("%s",FM_NAME(FM_DT2)); + + ImGui::TableNextRow(); + ImGui::TableNextColumn(); + ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x); + if (ImGui::SliderScalar("##SSG",ImGuiDataType_U8,&ssgEnv,&_ZERO,&_SEVEN,ssgEnvTypes[ssgEnv])) { PARAMETER + op.ssgEnv=(op.ssgEnv&8)|(ssgEnv&7); + } + ImGui::TableNextColumn(); + ImGui::Text("%s",FM_NAME(FM_SSG)); + + ImGui::EndTable(); } - P(ImGui::SliderScalar(FM_NAME(FM_DT2),ImGuiDataType_U8,&op.dt2,&_ZERO,&_THREE)); - if (ImGui::IsItemHovered()) { - ImGui::SetTooltip("Only for Arcade system"); - } - if (ImGui::SliderScalar(FM_NAME(FM_SSG),ImGuiDataType_U8,&ssgEnv,&_ZERO,&_SEVEN,ssgEnvTypes[ssgEnv])) { PARAMETER - op.ssgEnv=(op.ssgEnv&8)|(ssgEnv&7); - } ImGui::PopID(); } ImGui::EndTable();