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/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 fe13fc496..8b4883e92 100644 --- a/src/engine/engine.cpp +++ b/src/engine/engine.cpp @@ -1023,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/fileOps.cpp b/src/engine/fileOps.cpp index b02579331..6eaeccecc 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()); 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/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 22e39af29..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))); @@ -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..35e0e69f6 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; } @@ -488,6 +494,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 +615,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 +964,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 +1099,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 +1231,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 +1359,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 +1415,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/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 bbc530a0d..60587fa46 100644 --- a/src/gui/gui.cpp +++ b/src/gui/gui.cpp @@ -4478,23 +4478,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); @@ -4509,8 +4509,8 @@ 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); } @@ -4560,7 +4560,6 @@ bool FurnaceGUI::loop() { } break; } - case DIV_SYSTEM_ARCADE: case DIV_SYSTEM_YM2151: if (ImGui::RadioButton("NTSC (3.58MHz)",flags==0)) { e->setSysFlags(i,0,restart); @@ -4704,23 +4703,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);