force update

This commit is contained in:
BlastBrothers 2022-02-23 11:51:02 -05:00
parent 9237a0f6fa
commit 1c33fe0edb
23 changed files with 1286 additions and 646 deletions

View File

@ -310,6 +310,7 @@ src/engine/platform/ay8930.cpp
src/engine/platform/tia.cpp src/engine/platform/tia.cpp
src/engine/platform/saa.cpp src/engine/platform/saa.cpp
src/engine/platform/amiga.cpp src/engine/platform/amiga.cpp
src/engine/platform/segapcm.cpp
src/engine/platform/qsound.cpp src/engine/platform/qsound.cpp
src/engine/platform/dummy.cpp src/engine/platform/dummy.cpp
src/engine/platform/lynx.cpp src/engine/platform/lynx.cpp

BIN
demos/m7 vibe.fur Normal file

Binary file not shown.

View File

@ -1105,7 +1105,7 @@ namespace IGFD
bool needToApllyNewFilter = false; 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)) if (ImGui::BeginCombo("##Filters", prSelectedFilter.filter.c_str(), ImGuiComboFlags_None))
{ {
intptr_t i = 0; intptr_t i = 0;
@ -3279,7 +3279,7 @@ namespace IGFD
//// FILE DIALOG CONSTRUCTOR / DESTRUCTOR /////////////////////////////////////////// //// FILE DIALOG CONSTRUCTOR / DESTRUCTOR ///////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////////////
IGFD::FileDialog::FileDialog() : BookMarkFeature(), KeyExplorerFeature(), ThumbnailFeature() {} IGFD::FileDialog::FileDialog() : BookMarkFeature(), KeyExplorerFeature(), ThumbnailFeature() {DpiScale=1.0f;}
IGFD::FileDialog::~FileDialog() = default; IGFD::FileDialog::~FileDialog() = default;
////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////////////////
@ -3827,8 +3827,9 @@ namespace IGFD
// Input file fields // Input file fields
float width = ImGui::GetContentRegionAvail().x; float width = ImGui::GetContentRegionAvail().x;
// fix this! fix this! fix this!
if (!fdFile.puDLGDirectoryMode) if (!fdFile.puDLGDirectoryMode)
width -= FILTER_COMBO_WIDTH; width -= FILTER_COMBO_WIDTH*DpiScale;
ImGui::PushItemWidth(width); ImGui::PushItemWidth(width);
ImGui::InputText("##FileName", fdFile.puFileNameBuffer, MAX_FILE_DIALOG_NAME_BUFFER); ImGui::InputText("##FileName", fdFile.puFileNameBuffer, MAX_FILE_DIALOG_NAME_BUFFER);
if (ImGui::GetItemID() == ImGui::GetActiveID()) if (ImGui::GetItemID() == ImGui::GetActiveID())

View File

@ -1133,6 +1133,7 @@ namespace IGFD
public: public:
bool puAnyWindowsHovered = false; // not remember why haha :) todo : to check if we can remove bool puAnyWindowsHovered = false; // not remember why haha :) todo : to check if we can remove
double DpiScale;
public: public:
static FileDialog* Instance() // Singleton for easier accces form anywhere but only one dialog at a time static FileDialog* Instance() // Singleton for easier accces form anywhere but only one dialog at a time

View File

@ -225,7 +225,7 @@ class DivDispatch {
/** /**
* get the bit depth of the register pool of this dispatch. * 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 * @return the depth. Default value is 8
*/ */
virtual int getRegisterPoolDepth(); virtual int getRegisterPoolDepth();

View File

@ -34,6 +34,7 @@
#include "platform/tia.h" #include "platform/tia.h"
#include "platform/saa.h" #include "platform/saa.h"
#include "platform/amiga.h" #include "platform/amiga.h"
#include "platform/segapcm.h"
#include "platform/qsound.h" #include "platform/qsound.h"
#include "platform/dummy.h" #include "platform/dummy.h"
#include "platform/lynx.h" #include "platform/lynx.h"
@ -140,12 +141,11 @@ void DivDispatchContainer::init(DivSystem sys, DivEngine* eng, int chanCount, do
bbInLen=32768; bbInLen=32768;
switch (sys) { switch (sys) {
case DIV_SYSTEM_GENESIS:
case DIV_SYSTEM_YM2612: case DIV_SYSTEM_YM2612:
dispatch=new DivPlatformGenesis; dispatch=new DivPlatformGenesis;
((DivPlatformGenesis*)dispatch)->setYMFM(eng->getConfInt("ym2612Core",0)); ((DivPlatformGenesis*)dispatch)->setYMFM(eng->getConfInt("ym2612Core",0));
break; break;
case DIV_SYSTEM_GENESIS_EXT: case DIV_SYSTEM_YM2612_EXT:
dispatch=new DivPlatformGenesisExt; dispatch=new DivPlatformGenesisExt;
((DivPlatformGenesisExt*)dispatch)->setYMFM(eng->getConfInt("ym2612Core",0)); ((DivPlatformGenesisExt*)dispatch)->setYMFM(eng->getConfInt("ym2612Core",0));
break; break;
@ -169,7 +169,6 @@ void DivDispatchContainer::init(DivSystem sys, DivEngine* eng, int chanCount, do
dispatch=new DivPlatformC64; dispatch=new DivPlatformC64;
((DivPlatformC64*)dispatch)->setChipModel(false); ((DivPlatformC64*)dispatch)->setChipModel(false);
break; break;
case DIV_SYSTEM_ARCADE:
case DIV_SYSTEM_YM2151: case DIV_SYSTEM_YM2151:
dispatch=new DivPlatformArcade; dispatch=new DivPlatformArcade;
((DivPlatformArcade*)dispatch)->setYMFM(eng->getConfInt("arcadeCore",0)==0); ((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: case DIV_SYSTEM_QSOUND:
dispatch=new DivPlatformQSound; dispatch=new DivPlatformQSound;
break; break;
case DIV_SYSTEM_SEGAPCM:
case DIV_SYSTEM_SEGAPCM_COMPAT:
dispatch=new DivPlatformSegaPCM;
break;
default: default:
logW("this system is not supported yet! using dummy platform.\n"); logW("this system is not supported yet! using dummy platform.\n");
dispatch=new DivPlatformDummy; dispatch=new DivPlatformDummy;

View File

@ -437,11 +437,11 @@ void DivEngine::notifyWaveChange(int wave) {
// ADPCM code attribution: https://wiki.neogeodev.org/index.php?title=ADPCM_codecs // ADPCM code attribution: https://wiki.neogeodev.org/index.php?title=ADPCM_codecs
static short adSteps[49]={ static short adSteps[49]={
16, 17, 19, 21, 23, 25, 28, 31, 34, 37, 16, 17, 19, 21, 23, 25, 28, 31, 34, 37,
41, 45, 50, 55, 60, 66, 73, 80, 88, 97, 41, 45, 50, 55, 60, 66, 73, 80, 88, 97,
107, 118, 130, 143, 157, 173, 190, 209, 230, 253, 107, 118, 130, 143, 157, 173, 190, 209, 230, 253,
279, 307, 337, 371, 408, 449, 494, 544, 598, 658, 279, 307, 337, 371, 408, 449, 494, 544, 598, 658,
724, 796, 876, 963, 1060, 1166, 1282, 1411, 1552 724, 796, 876, 963, 1060, 1166, 1282, 1411, 1552
}; };
static int adStepSeek[16]={ static int adStepSeek[16]={
@ -606,9 +606,10 @@ void DivEngine::renderSamples() {
memPos=0; memPos=0;
for (int i=0; i<song.sampleLen; i++) { for (int i=0; i<song.sampleLen; i++) {
DivSample* s=song.sample[i]; DivSample* s=song.sample[i];
int length = s->rendLength; int length = s->rendLength;
if(length > 65536-16) if (length > 65536-16) {
length = 65536-16; length = 65536-16;
}
if ((memPos&0xff0000)!=((memPos+length)&0xff0000)) { if ((memPos&0xff0000)!=((memPos+length)&0xff0000)) {
memPos=(memPos+0xffff)&0xff0000; memPos=(memPos+0xffff)&0xff0000;
} }
@ -617,16 +618,14 @@ void DivEngine::renderSamples() {
break; break;
} }
if (memPos+length>=16777216) { if (memPos+length>=16777216) {
for(unsigned int i=0; i<16777216-(memPos+length); i++) for (unsigned int i=0; i<16777216-(memPos+length); i++) {
{ qsoundMem[(memPos + i) ^ 0x8000] = s->rendData[i] >> ((s->depth == 16) ? 8 : 0);
qsoundMem[(memPos + i) ^ 0x8000] = s->rendData[i] >> ((s->depth == 16) ? 8 : 0); }
}
logW("out of QSound PCM memory for sample %d!\n",i); logW("out of QSound PCM memory for sample %d!\n",i);
} else { } else {
for(int i=0; i<length; i++) for (int i=0; i<length; i++) {
{ qsoundMem[(memPos + i) ^ 0x8000] = s->rendData[i] >> ((s->depth == 16) ? 8 : 0);
qsoundMem[(memPos + i) ^ 0x8000] = s->rendData[i] >> ((s->depth == 16) ? 8 : 0); }
}
} }
s->rendOffQsound=memPos ^ 0x8000; s->rendOffQsound=memPos ^ 0x8000;
memPos+=length+16; memPos+=length+16;
@ -1024,11 +1023,11 @@ int DivEngine::getEffectiveSampleRate(int rate) {
switch (song.system[0]) { switch (song.system[0]) {
case DIV_SYSTEM_YMU759: case DIV_SYSTEM_YMU759:
return 8000; return 8000;
case DIV_SYSTEM_GENESIS: case DIV_SYSTEM_GENESIS_EXT: case DIV_SYSTEM_YM2612: case DIV_SYSTEM_YM2612_EXT:
return 1278409/(1280000/rate); return 1278409/(1280000/rate);
case DIV_SYSTEM_PCE: case DIV_SYSTEM_PCE:
return 1789773/(1789773/rate); 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; 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: case DIV_SYSTEM_YM2610: case DIV_SYSTEM_YM2610_EXT: case DIV_SYSTEM_YM2610_FULL: case DIV_SYSTEM_YM2610_FULL_EXT:
return 18518; return 18518;

View File

@ -365,6 +365,9 @@ class DivEngine {
// get preferred instrument type // get preferred instrument type
DivInstrumentType getPreferInsType(int ch); DivInstrumentType getPreferInsType(int ch);
// get song system name
const char* getSongSystemName();
// get sys name // get sys name
const char* getSystemName(DivSystem sys); const char* getSystemName(DivSystem sys);
@ -684,7 +687,7 @@ class DivEngine {
adpcmMemLen(0), adpcmMemLen(0),
adpcmBMem(NULL), adpcmBMem(NULL),
adpcmBMemLen(0), adpcmBMemLen(0),
qsoundMem(NULL), qsoundMem(NULL),
qsoundMemLen(0) {} qsoundMemLen(0) {}
}; };
#endif #endif

View File

@ -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) { if (ds.system[0]==DIV_SYSTEM_SMS_OPLL) {
ds.systemLen=2; ds.systemLen=2;
ds.system[0]=DIV_SYSTEM_SMS; ds.system[0]=DIV_SYSTEM_SMS;
@ -789,6 +806,42 @@ bool DivEngine::loadFur(unsigned char* file, size_t len) {
ds.systemFlags[i]=reader.readI(); 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.name=reader.readString();
ds.author=reader.readString(); ds.author=reader.readString();
logI("%s by %s\n",ds.name.c_str(),ds.author.c_str()); 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!"; lastError="invalid version to save in! this is a bug!";
return NULL; 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 // fail if more than one system
// TODO: fix this mess for the flattening in 0.6 if (!isFlat && song.systemLen!=1) {
if (!(song.system[0]==DIV_SYSTEM_SMS && song.system[1]==DIV_SYSTEM_OPLL)) {
if (song.systemLen!=1) {
logE("cannot save multiple systems in this format!\n"); logE("cannot save multiple systems in this format!\n");
lastError="multiple systems not possible on .dmf"; lastError="multiple systems not possible on .dmf";
return NULL; return NULL;
} }
}
// fail if this is an YMU759 song // fail if this is an YMU759 song
if (song.system[0]==DIV_SYSTEM_YMU759) { if (song.system[0]==DIV_SYSTEM_YMU759) {
logE("cannot save YMU759 song!\n"); logE("cannot save YMU759 song!\n");
@ -1416,7 +1482,7 @@ SafeWriter* DivEngine::saveDMF(unsigned char version) {
return NULL; return NULL;
} }
// fail if the system is Furnace-exclusive // 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"); logE("cannot save Furnace-exclusive system song!\n");
lastError="this system is not possible on .dmf"; lastError="this system is not possible on .dmf";
return NULL; return NULL;
@ -1432,7 +1498,16 @@ SafeWriter* DivEngine::saveDMF(unsigned char version) {
// version // version
w->writeC(version); w->writeC(version);
DivSystem sys=DIV_SYSTEM_NULL; 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)); w->writeC(systemToFile(DIV_SYSTEM_SMS_OPLL));
sys=DIV_SYSTEM_SMS_OPLL; sys=DIV_SYSTEM_SMS_OPLL;
} else { } else {

View File

@ -130,9 +130,6 @@ const char* DivPlatformArcade::getEffectName(unsigned char effect) {
case 0x1f: case 0x1f:
return "1Fxx: Set PM depth (0 to 7F)"; return "1Fxx: Set PM depth (0 to 7F)";
break; break;
case 0x20:
return "20xx: Set PCM frequency";
break;
} }
return NULL; 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,NULL,NULL,NULL,NULL); OPM_Clock(&fm,NULL,NULL,NULL,NULL);
OPM_Clock(&fm,o,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]<-32768) o[0]=-32768;
if (o[0]>32767) o[0]=32767; 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); fm_ymfm->generate(&out_ymfm);
pcmCycles+=31250; os[0]=out_ymfm.data[0];
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.sample<parent->song.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;
if (os[0]<-32768) os[0]=-32768; if (os[0]<-32768) os[0]=-32768;
if (os[0]>32767) os[0]=32767; 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]<-32768) os[1]=-32768;
if (os[1]>32767) os[1]=32767; if (os[1]>32767) os[1]=32767;
@ -464,106 +390,21 @@ void DivPlatformArcade::tick() {
chan[i].keyOn=false; 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.sample<parent->song.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) { void DivPlatformArcade::muteChannel(int ch, bool mute) {
isMuted[ch]=mute; isMuted[ch]=mute;
if (ch<8) { if (isMuted[ch]) {
if (isMuted[ch]) { rWrite(chanOffs[ch]+ADDR_LR_FB_ALG,(chan[ch].state.alg&7)|(chan[ch].state.fb<<3));
rWrite(chanOffs[ch]+ADDR_LR_FB_ALG,(chan[ch].state.alg&7)|(chan[ch].state.fb<<3)); } else {
} 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));
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 DivPlatformArcade::dispatch(DivCommand c) {
int pcmChan=c.chan-8;
switch (c.cmd) { switch (c.cmd) {
case DIV_CMD_NOTE_ON: { case DIV_CMD_NOTE_ON: {
DivInstrument* ins=parent->getIns(chan[c.chan].ins); 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) { if (chan[c.chan].insChanged) {
chan[c.chan].state=ins->fm; chan[c.chan].state=ins->fm;
@ -614,12 +455,6 @@ int DivPlatformArcade::dispatch(DivCommand c) {
break; break;
} }
case DIV_CMD_NOTE_OFF: 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].keyOff=true;
chan[c.chan].keyOn=false; chan[c.chan].keyOn=false;
chan[c.chan].active=false; chan[c.chan].active=false;
@ -638,15 +473,6 @@ int DivPlatformArcade::dispatch(DivCommand c) {
if (!chan[c.chan].std.hasVol) { if (!chan[c.chan].std.hasVol) {
chan[c.chan].outVol=c.value; 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++) { for (int i=0; i<4; i++) {
unsigned short baseAddr=chanOffs[c.chan]|opOffs[i]; unsigned short baseAddr=chanOffs[c.chan]|opOffs[i];
DivInstrumentFM::Operator& op=chan[c.chan].state.op[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; chan[c.chan].ins=c.value;
break; break;
case DIV_CMD_PANNING: { case DIV_CMD_PANNING: {
// TODO chan[c.chan].chVolL=((c.value>>4)==1);
if (c.chan>7) { chan[c.chan].chVolR=((c.value&15)==1);
chan[c.chan].chVolL=(c.value>>4)|(((c.value>>4)>>1)<<4); if (isMuted[c.chan]) {
chan[c.chan].chVolR=(c.value&15)|(((c.value&15)>>1)<<4); rWrite(chanOffs[c.chan]+ADDR_LR_FB_ALG,(chan[c.chan].state.alg&7)|(chan[c.chan].state.fb<<3));
if (dumpWrites) {
addWrite(0x10002+(pcmChan<<3),chan[c.chan].chVolL);
addWrite(0x10003+(pcmChan<<3),chan[c.chan].chVolR);
}
} else { } else {
chan[c.chan].chVolL=((c.value>>4)==1); 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));
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));
}
} }
break; break;
} }
@ -724,17 +540,14 @@ int DivPlatformArcade::dispatch(DivCommand c) {
break; break;
} }
case DIV_CMD_FM_LFO: { case DIV_CMD_FM_LFO: {
if (c.chan>7) break;
rWrite(0x18,c.value); rWrite(0x18,c.value);
break; break;
} }
case DIV_CMD_FM_LFO_WAVE: { case DIV_CMD_FM_LFO_WAVE: {
if (c.chan>7) break;
rWrite(0x1b,c.value&3); rWrite(0x1b,c.value&3);
break; break;
} }
case DIV_CMD_FM_FB: { case DIV_CMD_FM_FB: {
if (c.chan>7) break;
chan[c.chan].state.fb=c.value&7; chan[c.chan].state.fb=c.value&7;
if (isMuted[c.chan]) { if (isMuted[c.chan]) {
rWrite(chanOffs[c.chan]+ADDR_LR_FB_ALG,(chan[c.chan].state.alg&7)|(chan[c.chan].state.fb<<3)); 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; break;
} }
case DIV_CMD_FM_MULT: { case DIV_CMD_FM_MULT: {
if (c.chan>7) break;
unsigned short baseAddr=chanOffs[c.chan]|opOffs[orderedOps[c.value]]; unsigned short baseAddr=chanOffs[c.chan]|opOffs[orderedOps[c.value]];
DivInstrumentFM::Operator& op=chan[c.chan].state.op[orderedOps[c.value]]; DivInstrumentFM::Operator& op=chan[c.chan].state.op[orderedOps[c.value]];
op.mult=c.value2&15; op.mult=c.value2&15;
@ -752,7 +564,6 @@ int DivPlatformArcade::dispatch(DivCommand c) {
break; break;
} }
case DIV_CMD_FM_TL: { case DIV_CMD_FM_TL: {
if (c.chan>7) break;
unsigned short baseAddr=chanOffs[c.chan]|opOffs[orderedOps[c.value]]; unsigned short baseAddr=chanOffs[c.chan]|opOffs[orderedOps[c.value]];
DivInstrumentFM::Operator& op=chan[c.chan].state.op[orderedOps[c.value]]; DivInstrumentFM::Operator& op=chan[c.chan].state.op[orderedOps[c.value]];
op.tl=c.value2; op.tl=c.value2;
@ -764,7 +575,6 @@ int DivPlatformArcade::dispatch(DivCommand c) {
break; break;
} }
case DIV_CMD_FM_AR: { case DIV_CMD_FM_AR: {
if (c.chan>7) break;
if (c.value<0) { if (c.value<0) {
for (int i=0; i<4; i++) { for (int i=0; i<4; i++) {
DivInstrumentFM::Operator& op=chan[c.chan].state.op[i]; DivInstrumentFM::Operator& op=chan[c.chan].state.op[i];
@ -803,12 +613,6 @@ int DivPlatformArcade::dispatch(DivCommand c) {
} }
break; 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: case DIV_ALWAYS_SET_VOLUME:
return 0; return 0;
break; break;
@ -820,12 +624,6 @@ int DivPlatformArcade::dispatch(DivCommand c) {
break; break;
case DIV_CMD_PRE_NOTE: case DIV_CMD_PRE_NOTE:
break; 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: default:
//printf("WARNING: unimplemented command %d\n",c.cmd); //printf("WARNING: unimplemented command %d\n",c.cmd);
break; break;
@ -860,9 +658,6 @@ void DivPlatformArcade::forceIns() {
chan[i].freqChanged=true; chan[i].freqChanged=true;
} }
} }
for (int i=8; i<13; i++) {
chan[i].insChanged=true;
}
immWrite(0x19,amDepth); immWrite(0x19,amDepth);
immWrite(0x19,0x80|pmDepth); immWrite(0x19,0x80|pmDepth);
} }
@ -907,7 +702,7 @@ void DivPlatformArcade::reset() {
if (dumpWrites) { if (dumpWrites) {
addWrite(0xffffffff,0); addWrite(0xffffffff,0);
} }
for (int i=0; i<13; i++) { for (int i=0; i<8; i++) {
chan[i]=DivPlatformArcade::Channel(); chan[i]=DivPlatformArcade::Channel();
chan[i].vol=0x7f; chan[i].vol=0x7f;
chan[i].outVol=0x7f; chan[i].outVol=0x7f;
@ -922,7 +717,6 @@ void DivPlatformArcade::reset() {
pcmCycles=0; pcmCycles=0;
pcmL=0; pcmL=0;
pcmR=0; pcmR=0;
sampleBank=0;
delay=0; delay=0;
amDepth=0x7f; amDepth=0x7f;
pmDepth=0x7f; pmDepth=0x7f;
@ -931,13 +725,6 @@ void DivPlatformArcade::reset() {
immWrite(0x19,amDepth); immWrite(0x19,amDepth);
immWrite(0x19,0x80|pmDepth); immWrite(0x19,0x80|pmDepth);
//rWrite(0x1b,0x00); //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; extMode=false;
} }
@ -972,14 +759,14 @@ int DivPlatformArcade::init(DivEngine* p, int channels, int sugRate, unsigned in
parent=p; parent=p;
dumpWrites=false; dumpWrites=false;
skipRegisterWrites=false; skipRegisterWrites=false;
for (int i=0; i<13; i++) { for (int i=0; i<8; i++) {
isMuted[i]=false; isMuted[i]=false;
} }
setFlags(flags); setFlags(flags);
if (useYMFM) fm_ymfm=new ymfm::ym2151(iface); if (useYMFM) fm_ymfm=new ymfm::ym2151(iface);
reset(); reset();
return 13; return 8;
} }
void DivPlatformArcade::quit() { void DivPlatformArcade::quit() {

View File

@ -42,17 +42,9 @@ class DivPlatformArcade: public DivDispatch {
bool active, insChanged, freqChanged, keyOn, keyOff, inPorta, portaPause, furnacePCM; bool active, insChanged, freqChanged, keyOn, keyOff, inPorta, portaPause, furnacePCM;
int vol, outVol; int vol, outVol;
unsigned char chVolL, chVolR; 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(): 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 { struct QueuedWrite {
unsigned short addr; unsigned short addr;
unsigned char val; unsigned char val;
@ -63,7 +55,6 @@ class DivPlatformArcade: public DivDispatch {
opm_t fm; opm_t fm;
int delay, baseFreqOff; int delay, baseFreqOff;
int pcmL, pcmR, pcmCycles; int pcmL, pcmR, pcmCycles;
unsigned char sampleBank;
unsigned char lastBusy; unsigned char lastBusy;
unsigned char amDepth, pmDepth; unsigned char amDepth, pmDepth;
@ -75,7 +66,7 @@ class DivPlatformArcade: public DivDispatch {
bool extMode, useYMFM; bool extMode, useYMFM;
bool isMuted[13]; bool isMuted[8];
short oldWrites[256]; short oldWrites[256];
short pendingWrites[256]; short pendingWrites[256];

View File

@ -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 #ifndef _LYNX_H
#define _LYNX_H #define _LYNX_H

View File

@ -30,219 +30,219 @@
#define immWrite(a,v) {qsound_write_data(&chip,a,v); if(dumpWrites) addWrite(a,v);} #define immWrite(a,v) {qsound_write_data(&chip,a,v); if(dumpWrites) addWrite(a,v);}
const char* regCheatSheetQSound[]={ const char* regCheatSheetQSound[]={
"Ch15_Bank", "00", "Ch15_Bank", "00",
"Ch00_Start", "01", "Ch00_Start", "01",
"Ch00_Freq", "02", "Ch00_Freq", "02",
"Ch00_Phase", "03", "Ch00_Phase", "03",
"Ch00_Loop", "04", "Ch00_Loop", "04",
"Ch00_End", "05", "Ch00_End", "05",
"Ch00_Volume", "06", "Ch00_Volume", "06",
"Ch00_Bank", "08", "Ch00_Bank", "08",
"Ch01_Start", "09", "Ch01_Start", "09",
"Ch01_Freq", "0A", "Ch01_Freq", "0A",
"Ch01_Phase", "0B", "Ch01_Phase", "0B",
"Ch01_Loop", "0C", "Ch01_Loop", "0C",
"Ch01_End", "0D", "Ch01_End", "0D",
"Ch01_Volume", "0E", "Ch01_Volume", "0E",
"Ch01_Bank", "10", "Ch01_Bank", "10",
"Ch02_Start", "11", "Ch02_Start", "11",
"Ch02_Freq", "12", "Ch02_Freq", "12",
"Ch02_Phase", "13", "Ch02_Phase", "13",
"Ch02_Loop", "14", "Ch02_Loop", "14",
"Ch02_End", "15", "Ch02_End", "15",
"Ch02_Volume", "16", "Ch02_Volume", "16",
"Ch02_Bank", "18", "Ch02_Bank", "18",
"Ch03_Start", "19", "Ch03_Start", "19",
"Ch03_Freq", "1A", "Ch03_Freq", "1A",
"Ch03_Phase", "1B", "Ch03_Phase", "1B",
"Ch03_Loop", "1C", "Ch03_Loop", "1C",
"Ch03_End", "1D", "Ch03_End", "1D",
"Ch03_Volume", "1E", "Ch03_Volume", "1E",
"Ch03_Bank", "20", "Ch03_Bank", "20",
"Ch04_Start", "21", "Ch04_Start", "21",
"Ch04_Freq", "22", "Ch04_Freq", "22",
"Ch04_Phase", "23", "Ch04_Phase", "23",
"Ch04_Loop", "24", "Ch04_Loop", "24",
"Ch04_End", "25", "Ch04_End", "25",
"Ch04_Volume", "26", "Ch04_Volume", "26",
"Ch04_Bank", "28", "Ch04_Bank", "28",
"Ch05_Start", "29", "Ch05_Start", "29",
"Ch05_Freq", "2A", "Ch05_Freq", "2A",
"Ch05_Phase", "2B", "Ch05_Phase", "2B",
"Ch05_Loop", "2C", "Ch05_Loop", "2C",
"Ch05_End", "2D", "Ch05_End", "2D",
"Ch05_Volume", "2E", "Ch05_Volume", "2E",
"Ch05_Bank", "30", "Ch05_Bank", "30",
"Ch06_Start", "31", "Ch06_Start", "31",
"Ch06_Freq", "32", "Ch06_Freq", "32",
"Ch06_Phase", "33", "Ch06_Phase", "33",
"Ch06_Loop", "34", "Ch06_Loop", "34",
"Ch06_End", "35", "Ch06_End", "35",
"Ch06_Volume", "36", "Ch06_Volume", "36",
"Ch06_Bank", "38", "Ch06_Bank", "38",
"Ch07_Start", "39", "Ch07_Start", "39",
"Ch07_Freq", "3A", "Ch07_Freq", "3A",
"Ch07_Phase", "3B", "Ch07_Phase", "3B",
"Ch07_Loop", "3C", "Ch07_Loop", "3C",
"Ch07_End", "3D", "Ch07_End", "3D",
"Ch07_Volume", "3E", "Ch07_Volume", "3E",
"Ch07_Bank", "40", "Ch07_Bank", "40",
"Ch08_Start", "41", "Ch08_Start", "41",
"Ch08_Freq", "42", "Ch08_Freq", "42",
"Ch08_Phase", "43", "Ch08_Phase", "43",
"Ch08_Loop", "44", "Ch08_Loop", "44",
"Ch08_End", "45", "Ch08_End", "45",
"Ch08_Volume", "46", "Ch08_Volume", "46",
"Ch08_Bank", "48", "Ch08_Bank", "48",
"Ch09_Start", "49", "Ch09_Start", "49",
"Ch09_Freq", "4A", "Ch09_Freq", "4A",
"Ch09_Phase", "4B", "Ch09_Phase", "4B",
"Ch09_Loop", "4C", "Ch09_Loop", "4C",
"Ch09_End", "4D", "Ch09_End", "4D",
"Ch09_Volume", "4E", "Ch09_Volume", "4E",
"Ch09_Bank", "50", "Ch09_Bank", "50",
"Ch10_Start", "51", "Ch10_Start", "51",
"Ch10_Freq", "52", "Ch10_Freq", "52",
"Ch10_Phase", "53", "Ch10_Phase", "53",
"Ch10_Loop", "54", "Ch10_Loop", "54",
"Ch10_End", "55", "Ch10_End", "55",
"Ch10_Volume", "56", "Ch10_Volume", "56",
"Ch10_Bank", "58", "Ch10_Bank", "58",
"Ch11_Start", "59", "Ch11_Start", "59",
"Ch11_Freq", "5A", "Ch11_Freq", "5A",
"Ch11_Phase", "5B", "Ch11_Phase", "5B",
"Ch11_Loop", "5C", "Ch11_Loop", "5C",
"Ch11_End", "5D", "Ch11_End", "5D",
"Ch11_Volume", "5E", "Ch11_Volume", "5E",
"Ch11_Bank", "60", "Ch11_Bank", "60",
"Ch12_Start", "61", "Ch12_Start", "61",
"Ch12_Freq", "62", "Ch12_Freq", "62",
"Ch12_Phase", "63", "Ch12_Phase", "63",
"Ch12_Loop", "64", "Ch12_Loop", "64",
"Ch12_End", "65", "Ch12_End", "65",
"Ch12_Volume", "66", "Ch12_Volume", "66",
"Ch12_Bank", "68", "Ch12_Bank", "68",
"Ch13_Start", "69", "Ch13_Start", "69",
"Ch13_Freq", "6A", "Ch13_Freq", "6A",
"Ch13_Phase", "6B", "Ch13_Phase", "6B",
"Ch13_Loop", "6C", "Ch13_Loop", "6C",
"Ch13_End", "6D", "Ch13_End", "6D",
"Ch13_Volume", "6E", "Ch13_Volume", "6E",
"Ch13_Bank", "70", "Ch13_Bank", "70",
"Ch14_Start", "71", "Ch14_Start", "71",
"Ch14_Freq", "72", "Ch14_Freq", "72",
"Ch14_Phase", "73", "Ch14_Phase", "73",
"Ch14_Loop", "74", "Ch14_Loop", "74",
"Ch14_End", "75", "Ch14_End", "75",
"Ch14_Volume", "76", "Ch14_Volume", "76",
"Ch14_Bank", "78", "Ch14_Bank", "78",
"Ch15_Start", "79", "Ch15_Start", "79",
"Ch15_Freq", "7A", "Ch15_Freq", "7A",
"Ch15_Phase", "7B", "Ch15_Phase", "7B",
"Ch15_Loop", "7C", "Ch15_Loop", "7C",
"Ch15_End", "7D", "Ch15_End", "7D",
"Ch15_Volume", "7E", "Ch15_Volume", "7E",
"Ch00_Panning", "80", "Ch00_Panning", "80",
"Ch01_Panning", "81", "Ch01_Panning", "81",
"Ch02_Panning", "82", "Ch02_Panning", "82",
"Ch03_Panning", "83", "Ch03_Panning", "83",
"Ch04_Panning", "84", "Ch04_Panning", "84",
"Ch05_Panning", "85", "Ch05_Panning", "85",
"Ch06_Panning", "86", "Ch06_Panning", "86",
"Ch07_Panning", "87", "Ch07_Panning", "87",
"Ch08_Panning", "88", "Ch08_Panning", "88",
"Ch09_Panning", "89", "Ch09_Panning", "89",
"Ch10_Panning", "8A", "Ch10_Panning", "8A",
"Ch11_Panning", "8B", "Ch11_Panning", "8B",
"Ch12_Panning", "8C", "Ch12_Panning", "8C",
"Ch13_Panning", "8D", "Ch13_Panning", "8D",
"Ch14_Panning", "8E", "Ch14_Panning", "8E",
"Ch15_Panning", "8F", "Ch15_Panning", "8F",
"Adpcm0_Panning","90", "Adpcm0_Panning","90",
"Adpcm1_Panning","91", "Adpcm1_Panning","91",
"Adpcm2_Panning","92", "Adpcm2_Panning","92",
"Echo_Feedback","93", "Echo_Feedback","93",
"Ch00_Echo", "BA", "Ch00_Echo", "BA",
"Ch01_Echo", "BB", "Ch01_Echo", "BB",
"Ch02_Echo", "BC", "Ch02_Echo", "BC",
"Ch03_Echo", "BD", "Ch03_Echo", "BD",
"Ch04_Echo", "BE", "Ch04_Echo", "BE",
"Ch05_Echo", "BF", "Ch05_Echo", "BF",
"Ch06_Echo", "C0", "Ch06_Echo", "C0",
"Ch07_Echo", "C1", "Ch07_Echo", "C1",
"Ch08_Echo", "C2", "Ch08_Echo", "C2",
"Ch09_Echo", "C3", "Ch09_Echo", "C3",
"Ch10_Echo", "C4", "Ch10_Echo", "C4",
"Ch11_Echo", "C5", "Ch11_Echo", "C5",
"Ch12_Echo", "C6", "Ch12_Echo", "C6",
"Ch13_Echo", "C7", "Ch13_Echo", "C7",
"Ch14_Echo", "C8", "Ch14_Echo", "C8",
"Ch15_Echo", "C9", "Ch15_Echo", "C9",
"Adpcm0_Start", "CA", "Adpcm0_Start", "CA",
"Adpcm0_End", "CB", "Adpcm0_End", "CB",
"Adpcm0_Bank", "CC", "Adpcm0_Bank", "CC",
"Adpcm0_Volume","CD", "Adpcm0_Volume","CD",
"Adpcm1_Start", "CE", "Adpcm1_Start", "CE",
"Adpcm1_End", "CF", "Adpcm1_End", "CF",
"Adpcm1_Bank", "D0", "Adpcm1_Bank", "D0",
"Adpcm1_Volume","D1", "Adpcm1_Volume","D1",
"Adpcm2_Start", "D2", "Adpcm2_Start", "D2",
"Adpcm2_End", "D3", "Adpcm2_End", "D3",
"Adpcm2_Bank", "D4", "Adpcm2_Bank", "D4",
"Adpcm2_Volume","D5", "Adpcm2_Volume","D5",
"Adpcm0_KeyOn", "D6", "Adpcm0_KeyOn", "D6",
"Adpcm1_KeyOn", "D7", "Adpcm1_KeyOn", "D7",
"Adpcm2_KeyOn", "D8", "Adpcm2_KeyOn", "D8",
"Echo_Delay", "D9", "Echo_Delay", "D9",
"L_Wet_Filter", "DA", "L_Wet_Filter", "DA",
"L_Dry_Filter", "DB", "L_Dry_Filter", "DB",
"R_Wet_Filter", "DC", "R_Wet_Filter", "DC",
"R_Dry_Filter", "DD", "R_Dry_Filter", "DD",
"L_Wet_Delay", "DE", "L_Wet_Delay", "DE",
"L_Dry_Delay", "DF", "L_Dry_Delay", "DF",
"R_Wet_Delay", "E0", "R_Wet_Delay", "E0",
"R_Dry_Delay", "E1", "R_Dry_Delay", "E1",
"Delay_Flag", "E2", "Delay_Flag", "E2",
"Mode_Select", "E3", //valid: 0000,0288,0039,061A,004F "Mode_Select", "E3", //valid: 0000,0288,0039,061A,004F
"L_Wet_Volume", "E4", "L_Wet_Volume", "E4",
"L_Dry_Volume", "E5", "L_Dry_Volume", "E5",
"R_Wet_Volume", "E6", "R_Wet_Volume", "E6",
"R_Dry_Volume", "E7", "R_Dry_Volume", "E7",
NULL NULL
}; };
enum q1_register_name { enum q1_register_name {
Q1V_BANK = 0, Q1V_BANK = 0,
Q1V_START = 1, Q1V_START = 1,
Q1V_FREQ = 2, Q1V_FREQ = 2,
Q1V_PHASE = 3, Q1V_PHASE = 3,
Q1V_LOOP = 4, Q1V_LOOP = 4,
Q1V_END = 5, Q1V_END = 5,
Q1V_VOL = 6, Q1V_VOL = 6,
Q1V_REG_COUNT = 7, Q1V_REG_COUNT = 7,
Q1_PAN = 0x80, Q1_PAN = 0x80,
Q1_ECHO = 0xba, Q1_ECHO = 0xba,
Q1A_PAN = 0x90, Q1A_PAN = 0x90,
Q1A_START = 0xca, Q1A_START = 0xca,
Q1A_END = 0xcb, Q1A_END = 0xcb,
Q1A_BANK = 0xcc, Q1A_BANK = 0xcc,
Q1A_VOL = 0xcd, Q1A_VOL = 0xcd,
Q1A_KEYON = 0xd6, Q1A_KEYON = 0xd6,
Q1_ECHO_FEEDBACK = 0x93, Q1_ECHO_FEEDBACK = 0x93,
Q1_ECHO_LENGTH = 0xd9, Q1_ECHO_LENGTH = 0xd9,
}; };
const unsigned char q1_reg_map[Q1V_REG_COUNT][16] = { 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}, {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}, {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}, {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}, {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}, {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}, {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}, {0x06,0x0e,0x16,0x1e,0x26,0x2e,0x36,0x3e,0x46,0x4e,0x56,0x5e,0x66,0x6e,0x76,0x7e},
}; };
const char** DivPlatformQSound::getRegisterSheet() { const char** DivPlatformQSound::getRegisterSheet() {
@ -258,8 +258,9 @@ const char* DivPlatformQSound::getEffectName(unsigned char effect) {
return "11xx: Set channel echo level (00 to FF)"; return "11xx: Set channel echo level (00 to FF)";
break; break;
default: default:
if((effect & 0xf0) == 0x30) if ((effect & 0xf0) == 0x30) {
return "3xxx: Set echo delay buffer length (000 to AA5)"; return "3xxx: Set echo delay buffer length (000 to AA5)";
}
} }
return NULL; 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_data = parent->qsoundMem;
chip.rom_mask = 0xffffff; chip.rom_mask = 0xffffff;
for (size_t h=start; h<start+len; h++) { for (size_t h=start; h<start+len; h++) {
qsound_update(&chip); qsound_update(&chip);
bufL[h]=chip.out[0]; bufL[h]=chip.out[0];
bufR[h]=chip.out[1]; bufR[h]=chip.out[1];
} }
@ -277,18 +278,16 @@ void DivPlatformQSound::tick() {
for (int i=0; i<16; i++) { for (int i=0; i<16; i++) {
chan[i].std.next(); chan[i].std.next();
if (chan[i].std.hadVol) { if (chan[i].std.hadVol) {
chan[i].outVol=((chan[i].vol%256)*MIN(255,chan[i].std.vol << 2))>>8; chan[i].outVol=((chan[i].vol&0xff)*MIN(255,chan[i].std.vol<<2))>>8;
// Check if enabled and write volume // Check if enabled and write volume
if(chan[i].active) if (chan[i].active) {
{ rWrite(q1_reg_map[Q1V_VOL][i], chan[i].outVol << 4);
rWrite(q1_reg_map[Q1V_VOL][i], chan[i].outVol << 5); }
//logW("ch %d vol=%04x (hadVol)!\n",i,chan[i].outVol << 5);
}
} }
uint16_t qsound_bank = 0; uint16_t qsound_bank = 0;
uint16_t qsound_addr = 0; uint16_t qsound_addr = 0;
uint16_t qsound_loop = 0; uint16_t qsound_loop = 0;
uint16_t qsound_end = 0; uint16_t qsound_end = 0;
double off=1.0; double off=1.0;
if (chan[i].sample>=0 && chan[i].sample<parent->song.sampleLen) { if (chan[i].sample>=0 && chan[i].sample<parent->song.sampleLen) {
DivSample* s=parent->song.sample[chan[i].sample]; DivSample* s=parent->song.sample[chan[i].sample];
@ -297,22 +296,20 @@ void DivPlatformQSound::tick() {
} else { } else {
off=(double)s->centerRate/24038.0/16.0; off=(double)s->centerRate/24038.0/16.0;
} }
qsound_bank = 0x8000 | (s->rendOffQsound >> 16); qsound_bank = 0x8000 | (s->rendOffQsound >> 16);
qsound_addr = s->rendOffQsound & 0xffff; qsound_addr = s->rendOffQsound & 0xffff;
int length = s->length; int length = s->length;
if(length > 65536 - 16) if (length > 65536 - 16) {
length = 65536 - 16; length = 65536 - 16;
if(s->loopStart == -1 || s->loopStart >= length) }
{ if (s->loopStart == -1 || s->loopStart >= length) {
qsound_end = s->rendOffQsound + length + 15; qsound_end = s->rendOffQsound + length + 15;
qsound_loop = 15; qsound_loop = 15;
} } else {
else qsound_end = s->rendOffQsound + length;
{ qsound_loop = length - s->loopStart;
qsound_end = s->rendOffQsound + length; }
qsound_loop = length - s->loopStart;
}
} }
if (chan[i].std.hadArp) { if (chan[i].std.hadArp) {
if (!chan[i].inPorta) { if (!chan[i].inPorta) {
@ -333,29 +330,26 @@ void DivPlatformQSound::tick() {
//DivInstrument* ins=parent->getIns(chan[i].ins); //DivInstrument* ins=parent->getIns(chan[i].ins);
chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,false); chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,false);
if (chan[i].freq>0xffff) chan[i].freq=0xffff; if (chan[i].freq>0xffff) chan[i].freq=0xffff;
//if (chan[i].note>0x5d) chan[i].freq=0x01; //????
if (chan[i].keyOn) { if (chan[i].keyOn) {
rWrite(q1_reg_map[Q1V_BANK][i], qsound_bank); rWrite(q1_reg_map[Q1V_BANK][i], qsound_bank);
rWrite(q1_reg_map[Q1V_END][i], qsound_end); rWrite(q1_reg_map[Q1V_END][i], qsound_end);
rWrite(q1_reg_map[Q1V_LOOP][i], qsound_loop); rWrite(q1_reg_map[Q1V_LOOP][i], qsound_loop);
rWrite(q1_reg_map[Q1V_START][i], qsound_addr); rWrite(q1_reg_map[Q1V_START][i], qsound_addr);
rWrite(q1_reg_map[Q1V_PHASE][i], 0x8000); 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); //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 // Write sample address. Enable volume
if (!chan[i].std.hadVol) { if (!chan[i].std.hadVol) {
rWrite(q1_reg_map[Q1V_VOL][i], chan[i].vol << 5); rWrite(q1_reg_map[Q1V_VOL][i], chan[i].vol << 4);
//logW("ch %d vol=%04x (!hadVol)!\n",i,chan[i].vol << 5); }
}
} }
if (chan[i].keyOff) { if (chan[i].keyOff) {
rWrite(q1_reg_map[Q1V_VOL][i], 0); // Disable volume
rWrite(q1_reg_map[Q1V_FREQ][i], 0); rWrite(q1_reg_map[Q1V_VOL][i], 0);
// Disable volume 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].keyOn) chan[i].keyOn=false;
if (chan[i].keyOff) chan[i].keyOff=false; if (chan[i].keyOff) chan[i].keyOff=false;
chan[i].freqChanged=false; chan[i].freqChanged=false;
@ -411,13 +405,11 @@ int DivPlatformQSound::dispatch(DivCommand c) {
if (chan[c.chan].vol!=c.value) { if (chan[c.chan].vol!=c.value) {
chan[c.chan].vol=c.value; chan[c.chan].vol=c.value;
if (!chan[c.chan].std.hasVol) { if (!chan[c.chan].std.hasVol) {
// Check if enabled and write volume // Check if enabled and write volume
chan[c.chan].outVol=c.value; chan[c.chan].outVol=c.value;
if(chan[c.chan].active && c.chan < 16) if (chan[c.chan].active && c.chan < 16) {
{ rWrite(q1_reg_map[Q1V_VOL][c.chan], chan[c.chan].outVol << 4);
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);
}
} }
} }
break; break;
@ -428,16 +420,16 @@ int DivPlatformQSound::dispatch(DivCommand c) {
return chan[c.chan].outVol; return chan[c.chan].outVol;
break; break;
case DIV_CMD_PANNING: case DIV_CMD_PANNING:
immWrite(Q1_PAN+c.chan, c.value + 0x110); immWrite(Q1_PAN+c.chan, c.value + 0x110);
break; break;
case DIV_CMD_QSOUND_ECHO_LEVEL: case DIV_CMD_QSOUND_ECHO_LEVEL:
immWrite(Q1_ECHO+c.chan, c.value << 7); immWrite(Q1_ECHO+c.chan, c.value << 7);
break; break;
case DIV_CMD_QSOUND_ECHO_FEEDBACK: case DIV_CMD_QSOUND_ECHO_FEEDBACK:
immWrite(Q1_ECHO_FEEDBACK, c.value << 6); immWrite(Q1_ECHO_FEEDBACK, c.value << 6);
break; break;
case DIV_CMD_QSOUND_ECHO_DELAY: case DIV_CMD_QSOUND_ECHO_DELAY:
immWrite(Q1_ECHO_LENGTH, (c.value > 2725 ? 0xfff : 0xfff - (2725 - c.value))); immWrite(Q1_ECHO_LENGTH, (c.value > 2725 ? 0xfff : 0xfff - (2725 - c.value)));
break; break;
case DIV_CMD_PITCH: case DIV_CMD_PITCH:
chan[c.chan].pitch=c.value; chan[c.chan].pitch=c.value;
@ -509,10 +501,11 @@ int DivPlatformQSound::dispatch(DivCommand c) {
} }
void DivPlatformQSound::muteChannel(int ch, bool mute) { void DivPlatformQSound::muteChannel(int ch, bool mute) {
if(mute) if (mute) {
chip.mute_mask |= (1 << ch); chip.mute_mask|=(1<<ch);
else } else {
chip.mute_mask &= ~(1 << ch); chip.mute_mask&=~(1<<ch);
}
} }
void DivPlatformQSound::forceIns() { void DivPlatformQSound::forceIns() {
@ -532,8 +525,9 @@ void DivPlatformQSound::reset() {
chan[i]=DivPlatformQSound::Channel(); chan[i]=DivPlatformQSound::Channel();
} }
qsound_reset(&chip); qsound_reset(&chip);
while(!chip.ready_flag) while(!chip.ready_flag) {
qsound_update(&chip); qsound_update(&chip);
}
immWrite(Q1_ECHO_LENGTH, 0xfff - (2725 - echoDelay)); immWrite(Q1_ECHO_LENGTH, 0xfff - (2725 - echoDelay));
immWrite(Q1_ECHO_FEEDBACK, echoFeedback << 6); immWrite(Q1_ECHO_FEEDBACK, echoFeedback << 6);
@ -557,6 +551,7 @@ void DivPlatformQSound::notifyInsChange(int ins) {
void DivPlatformQSound::notifyWaveChange(int wave) { void DivPlatformQSound::notifyWaveChange(int wave) {
// TODO when wavetables are added // TODO when wavetables are added
// TODO they probably won't be added unless the samples reside in RAM
} }
void DivPlatformQSound::notifyInsDeletion(void* ins) { void DivPlatformQSound::notifyInsDeletion(void* ins) {
@ -569,10 +564,12 @@ void DivPlatformQSound::setFlags(unsigned int flags) {
echoDelay = 2725 - (flags & 0xfff); echoDelay = 2725 - (flags & 0xfff);
echoFeedback = (flags >> 12) & 255; echoFeedback = (flags >> 12) & 255;
if(echoDelay < 0) if(echoDelay < 0) {
echoDelay = 0; echoDelay = 0;
if(echoDelay > 2725) }
echoDelay = 2725; if(echoDelay > 2725) {
echoDelay = 2725;
}
//rate=chipClock/CHIP_DIVIDER; //rate=chipClock/CHIP_DIVIDER;
} }
@ -586,21 +583,21 @@ void DivPlatformQSound::poke(std::vector<DivRegWrite>& wlist) {
} }
unsigned char* DivPlatformQSound::getRegisterPool() { unsigned char* DivPlatformQSound::getRegisterPool() {
unsigned short* regPoolPtr = regPool; unsigned short* regPoolPtr = regPool;
for(int i=0; i<256; i++) for(int i=0; i<256; i++)
{ {
uint16_t data = qsound_read_data(&chip, i); uint16_t data = qsound_read_data(&chip, i);
*regPoolPtr++ = data; *regPoolPtr++ = data;
} }
return (unsigned char*)regPool; return (unsigned char*)regPool;
} }
int DivPlatformQSound::getRegisterPoolSize() { int DivPlatformQSound::getRegisterPoolSize() {
return 256; return 256;
} }
int DivPlatformQSound::getRegisterPoolDepth() { int DivPlatformQSound::getRegisterPoolDepth() {
return 16; return 16;
} }
int DivPlatformQSound::init(DivEngine* p, int channels, int sugRate, unsigned int flags) { 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; dumpWrites=false;
skipRegisterWrites=false; skipRegisterWrites=false;
// for (int i=0; i<16; i++) { //for (int i=0; i<16; i++) {
// isMuted[i]=false; // isMuted[i]=false;
// } //}
setFlags(flags); setFlags(flags);
chipClock=60000000; chipClock=60000000;

View File

@ -33,7 +33,7 @@ class DivPlatformQSound: public DivDispatch {
int sample, wave; int sample, wave;
unsigned char ins; unsigned char ins;
int note; int note;
int panning; int panning;
bool active, insChanged, freqChanged, keyOn, keyOff, inPorta, useWave; bool active, insChanged, freqChanged, keyOn, keyOff, inPorta, useWave;
int vol, outVol; int vol, outVol;
DivMacroInt std; DivMacroInt std;
@ -46,7 +46,7 @@ class DivPlatformQSound: public DivDispatch {
sample(-1), sample(-1),
ins(-1), ins(-1),
note(0), note(0),
panning(0x10), panning(0x10),
active(false), active(false),
insChanged(true), insChanged(true),
freqChanged(false), freqChanged(false),

View File

@ -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 <string.h>
#include <math.h>
//#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<start+len; h++) {
os[0]=0; os[1]=0;
// do a PCM cycle
pcmL=0; pcmR=0;
for (int i=0; i<16; i++) {
if (chan[i].pcm.sample>=0 && chan[i].pcm.sample<parent->song.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.sample<parent->song.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<DivRegWrite>& 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() {
}

View File

@ -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 <queue>
#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<QueuedWrite> 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<DivRegWrite>& wlist);
const char* getEffectName(unsigned char effect);
int init(DivEngine* parent, int channels, int sugRate, unsigned int flags);
void quit();
~DivPlatformSegaPCM();
};
#endif

View File

@ -18,6 +18,7 @@
*/ */
#include "blip_buf.h" #include "blip_buf.h"
#include "song.h"
#include "wavetable.h" #include "wavetable.h"
#define _USE_MATH_DEFINES #define _USE_MATH_DEFINES
#include "dispatch.h" #include "dispatch.h"
@ -147,9 +148,8 @@ int DivEngine::dispatchCmd(DivCommand c) {
bool DivEngine::perSystemEffect(int ch, unsigned char effect, unsigned char effectVal) { bool DivEngine::perSystemEffect(int ch, unsigned char effect, unsigned char effectVal) {
switch (sysOfChan[ch]) { switch (sysOfChan[ch]) {
case DIV_SYSTEM_GENESIS:
case DIV_SYSTEM_GENESIS_EXT:
case DIV_SYSTEM_YM2612: case DIV_SYSTEM_YM2612:
case DIV_SYSTEM_YM2612_EXT:
switch (effect) { switch (effect) {
case 0x17: // DAC enable case 0x17: // DAC enable
dispatchCmd(DivCommand(DIV_CMD_SAMPLE_MODE,ch,(effectVal>0))); 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)); dispatchCmd(DivCommand(DIV_CMD_QSOUND_ECHO_LEVEL,ch,effectVal));
break; break;
default: default:
if ((effect & 0xf0)==0x30) { if ((effect&0xf0)==0x30) {
dispatchCmd(DivCommand(DIV_CMD_QSOUND_ECHO_DELAY,ch,((effect & 0x0f) << 8) | effectVal)); dispatchCmd(DivCommand(DIV_CMD_QSOUND_ECHO_DELAY,ch,((effect & 0x0f) << 8) | effectVal));
} else { } else {
return false; return false;
} }
break; 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) { bool DivEngine::perSystemPostEffect(int ch, unsigned char effect, unsigned char effectVal) {
switch (sysOfChan[ch]) { switch (sysOfChan[ch]) {
case DIV_SYSTEM_GENESIS:
case DIV_SYSTEM_GENESIS_EXT:
case DIV_SYSTEM_YM2612: case DIV_SYSTEM_YM2612:
case DIV_SYSTEM_ARCADE: case DIV_SYSTEM_YM2612_EXT:
case DIV_SYSTEM_YM2151: case DIV_SYSTEM_YM2151:
case DIV_SYSTEM_YM2610: case DIV_SYSTEM_YM2610:
case DIV_SYSTEM_YM2610_EXT: case DIV_SYSTEM_YM2610_EXT:
switch (effect) { switch (effect) {
case 0x10: // LFO or noise mode 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)); dispatchCmd(DivCommand(DIV_CMD_STD_NOISE_FREQ,ch,effectVal));
} else { } else {
dispatchCmd(DivCommand(DIV_CMD_FM_LFO,ch,effectVal)); dispatchCmd(DivCommand(DIV_CMD_FM_LFO,ch,effectVal));
@ -293,12 +291,12 @@ bool DivEngine::perSystemPostEffect(int ch, unsigned char effect, unsigned char
} }
break; break;
case 0x17: // arcade LFO 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)); dispatchCmd(DivCommand(DIV_CMD_FM_LFO,ch,effectVal));
} }
break; break;
case 0x18: // EXT or LFO waveform 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)); dispatchCmd(DivCommand(DIV_CMD_FM_LFO_WAVE,ch,effectVal));
} else { } else {
dispatchCmd(DivCommand(DIV_CMD_FM_EXTCH,ch,effectVal)); 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 case 0x1f: // UNOFFICIAL: Arcade PM depth
dispatchCmd(DivCommand(DIV_CMD_FM_PM_DEPTH,ch,effectVal&127)); dispatchCmd(DivCommand(DIV_CMD_FM_PM_DEPTH,ch,effectVal&127));
break; break;
case 0x20: // PCM frequency or Neo Geo PSG mode case 0x20: // Neo Geo PSG mode
if (sysOfChan[ch]==DIV_SYSTEM_ARCADE || sysOfChan[ch]==DIV_SYSTEM_YM2151) { if (sysOfChan[ch]==DIV_SYSTEM_YM2610 || sysOfChan[ch]==DIV_SYSTEM_YM2610_EXT) {
dispatchCmd(DivCommand(DIV_CMD_SAMPLE_FREQ,ch,effectVal));
} else if (sysOfChan[ch]==DIV_SYSTEM_YM2610 || sysOfChan[ch]==DIV_SYSTEM_YM2610_EXT) {
dispatchCmd(DivCommand(DIV_CMD_STD_NOISE_MODE,ch,effectVal)); dispatchCmd(DivCommand(DIV_CMD_STD_NOISE_MODE,ch,effectVal));
} }
break; break;
@ -453,6 +449,8 @@ bool DivEngine::perSystemPostEffect(int ch, unsigned char effect, unsigned char
case 0x29: // auto-envelope case 0x29: // auto-envelope
dispatchCmd(DivCommand(DIV_CMD_AY_AUTO_ENVELOPE,ch,effectVal)); dispatchCmd(DivCommand(DIV_CMD_AY_AUTO_ENVELOPE,ch,effectVal));
break; break;
default:
return false;
} }
break; break;
case DIV_SYSTEM_SAA1099: case DIV_SYSTEM_SAA1099:
@ -466,6 +464,8 @@ bool DivEngine::perSystemPostEffect(int ch, unsigned char effect, unsigned char
case 0x12: // setup envelope case 0x12: // setup envelope
dispatchCmd(DivCommand(DIV_CMD_SAA_ENVELOPE,ch,effectVal)); dispatchCmd(DivCommand(DIV_CMD_SAA_ENVELOPE,ch,effectVal));
break; break;
default:
return false;
} }
break; break;
case DIV_SYSTEM_TIA: case DIV_SYSTEM_TIA:
@ -473,6 +473,18 @@ bool DivEngine::perSystemPostEffect(int ch, unsigned char effect, unsigned char
case 0x10: // select waveform case 0x10: // select waveform
dispatchCmd(DivCommand(DIV_CMD_WAVE,ch,effectVal)); dispatchCmd(DivCommand(DIV_CMD_WAVE,ch,effectVal));
break; 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; break;
default: default:
@ -755,7 +767,7 @@ void DivEngine::processRow(int i, bool afterDelay) {
break; break;
case 0xe5: // pitch case 0xe5: // pitch
chan[i].pitch=effectVal-0x80; 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; chan[i].pitch*=2;
if (chan[i].pitch<-128) chan[i].pitch=-128; if (chan[i].pitch<-128) chan[i].pitch=-128;
if (chan[i].pitch>127) chan[i].pitch=127; if (chan[i].pitch>127) chan[i].pitch=127;

View File

@ -34,16 +34,16 @@
enum DivSystem { enum DivSystem {
DIV_SYSTEM_NULL=0, DIV_SYSTEM_NULL=0,
DIV_SYSTEM_YMU759, DIV_SYSTEM_YMU759,
DIV_SYSTEM_GENESIS, DIV_SYSTEM_GENESIS, // ** COMPOUND SYSTEM - DO NOT USE! **
DIV_SYSTEM_GENESIS_EXT, DIV_SYSTEM_GENESIS_EXT, // ** COMPOUND SYSTEM - DO NOT USE! **
DIV_SYSTEM_SMS, DIV_SYSTEM_SMS,
DIV_SYSTEM_SMS_OPLL, DIV_SYSTEM_SMS_OPLL, // ** COMPOUND SYSTEM - DO NOT USE! **
DIV_SYSTEM_GB, DIV_SYSTEM_GB,
DIV_SYSTEM_PCE, DIV_SYSTEM_PCE,
DIV_SYSTEM_NES, DIV_SYSTEM_NES,
DIV_SYSTEM_C64_6581, DIV_SYSTEM_C64_6581,
DIV_SYSTEM_C64_8580, DIV_SYSTEM_C64_8580,
DIV_SYSTEM_ARCADE, DIV_SYSTEM_ARCADE, // ** COMPOUND SYSTEM - DO NOT USE! **
DIV_SYSTEM_YM2610, DIV_SYSTEM_YM2610,
DIV_SYSTEM_YM2610_EXT, DIV_SYSTEM_YM2610_EXT,
@ -88,7 +88,8 @@ enum DivSystem {
DIV_SYSTEM_YM2610_FULL_EXT, DIV_SYSTEM_YM2610_FULL_EXT,
DIV_SYSTEM_OPLL_DRUMS, DIV_SYSTEM_OPLL_DRUMS,
DIV_SYSTEM_LYNX, DIV_SYSTEM_LYNX,
DIV_SYSTEM_QSOUND DIV_SYSTEM_QSOUND,
DIV_SYSTEM_SEGAPCM_COMPAT
}; };
struct DivSong { struct DivSong {
@ -278,7 +279,7 @@ struct DivSong {
DivSong(): DivSong():
version(0), version(0),
isDMF(false), isDMF(false),
systemLen(1), systemLen(2),
name(""), name(""),
author(""), author(""),
carrier(""), carrier(""),
@ -331,7 +332,8 @@ struct DivSong {
chanShow[i]=true; chanShow[i]=true;
chanCollapse[i]=false; chanCollapse[i]=false;
} }
system[0]=DIV_SYSTEM_GENESIS; system[0]=DIV_SYSTEM_YM2612;
system[1]=DIV_SYSTEM_SMS;
} }
}; };

View File

@ -131,6 +131,8 @@ DivSystem DivEngine::systemFromFile(unsigned char val) {
return DIV_SYSTEM_OPLL_DRUMS; return DIV_SYSTEM_OPLL_DRUMS;
case 0xa8: case 0xa8:
return DIV_SYSTEM_LYNX; return DIV_SYSTEM_LYNX;
case 0xa9:
return DIV_SYSTEM_SEGAPCM_COMPAT;
case 0xe0: case 0xe0:
return DIV_SYSTEM_QSOUND; return DIV_SYSTEM_QSOUND;
} }
@ -248,6 +250,8 @@ unsigned char DivEngine::systemToFile(DivSystem val) {
return 0xa7; return 0xa7;
case DIV_SYSTEM_LYNX: case DIV_SYSTEM_LYNX:
return 0xa8; return 0xa8;
case DIV_SYSTEM_SEGAPCM_COMPAT:
return 0xa9;
case DIV_SYSTEM_QSOUND: case DIV_SYSTEM_QSOUND:
return 0xe0; return 0xe0;
@ -364,6 +368,8 @@ int DivEngine::getChannelCount(DivSystem sys) {
return 11; return 11;
case DIV_SYSTEM_LYNX: case DIV_SYSTEM_LYNX:
return 4; return 4;
case DIV_SYSTEM_SEGAPCM_COMPAT:
return 5;
case DIV_SYSTEM_QSOUND: case DIV_SYSTEM_QSOUND:
return 19; return 19;
} }
@ -374,7 +380,132 @@ int DivEngine::getTotalChannelCount() {
return chans; 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) { const char* DivEngine::getSystemName(DivSystem sys) {
switch (sys) { switch (sys) {
case DIV_SYSTEM_NULL: case DIV_SYSTEM_NULL:
@ -384,7 +515,7 @@ const char* DivEngine::getSystemName(DivSystem sys) {
case DIV_SYSTEM_GENESIS: case DIV_SYSTEM_GENESIS:
return "Sega Genesis/Mega Drive"; return "Sega Genesis/Mega Drive";
case DIV_SYSTEM_SMS: case DIV_SYSTEM_SMS:
return "Sega Master System"; return "TI SN76489";
case DIV_SYSTEM_SMS_OPLL: case DIV_SYSTEM_SMS_OPLL:
return "Sega Master System + FM Expansion"; return "Sega Master System + FM Expansion";
case DIV_SYSTEM_GB: case DIV_SYSTEM_GB:
@ -431,7 +562,7 @@ const char* DivEngine::getSystemName(DivSystem sys) {
case DIV_SYSTEM_OPLL: case DIV_SYSTEM_OPLL:
return "Yamaha OPLL"; return "Yamaha OPLL";
case DIV_SYSTEM_FDS: case DIV_SYSTEM_FDS:
return "Famicom Disk System"; return "Famicom Disk System (chip)";
case DIV_SYSTEM_MMC5: case DIV_SYSTEM_MMC5:
return "MMC5"; return "MMC5";
case DIV_SYSTEM_N163: case DIV_SYSTEM_N163:
@ -443,7 +574,7 @@ const char* DivEngine::getSystemName(DivSystem sys) {
case DIV_SYSTEM_OPL: case DIV_SYSTEM_OPL:
return "Yamaha OPL"; return "Yamaha OPL";
case DIV_SYSTEM_OPL2: case DIV_SYSTEM_OPL2:
return "Adlib Music Synthesizer Card"; return "Yamaha OPL2";
case DIV_SYSTEM_OPL3: case DIV_SYSTEM_OPL3:
return "Yamaha OPL3"; return "Yamaha OPL3";
case DIV_SYSTEM_MULTIPCM: case DIV_SYSTEM_MULTIPCM:
@ -457,7 +588,7 @@ const char* DivEngine::getSystemName(DivSystem sys) {
case DIV_SYSTEM_SWAN: case DIV_SYSTEM_SWAN:
return "WonderSwan"; return "WonderSwan";
case DIV_SYSTEM_SAA1099: case DIV_SYSTEM_SAA1099:
return "SAM Coupé"; return "Philips SAA1099";
case DIV_SYSTEM_OPZ: case DIV_SYSTEM_OPZ:
return "Yamaha TX81Z/YS200"; return "Yamaha TX81Z/YS200";
case DIV_SYSTEM_POKEMINI: case DIV_SYSTEM_POKEMINI:
@ -488,6 +619,8 @@ const char* DivEngine::getSystemName(DivSystem sys) {
return "Yamaha OPLL with drums"; return "Yamaha OPLL with drums";
case DIV_SYSTEM_LYNX: case DIV_SYSTEM_LYNX:
return "Atari Lynx"; return "Atari Lynx";
case DIV_SYSTEM_SEGAPCM_COMPAT:
return "SegaPCM (compatible 5-channel mode)";
case DIV_SYSTEM_QSOUND: case DIV_SYSTEM_QSOUND:
return "Capcom QSound"; return "Capcom QSound";
} }
@ -607,6 +740,8 @@ const char* DivEngine::getSystemChips(DivSystem sys) {
return "Yamaha YM2413 with drums"; return "Yamaha YM2413 with drums";
case DIV_SYSTEM_LYNX: case DIV_SYSTEM_LYNX:
return "Mikey"; return "Mikey";
case DIV_SYSTEM_SEGAPCM_COMPAT:
return "SegaPCM (compatible 5-channel mode)";
case DIV_SYSTEM_QSOUND: case DIV_SYSTEM_QSOUND:
return "Capcom DL-1425"; return "Capcom DL-1425";
} }
@ -954,6 +1089,7 @@ const char* DivEngine::getChannelName(int chan) {
break; break;
case DIV_SYSTEM_MULTIPCM: case DIV_SYSTEM_MULTIPCM:
case DIV_SYSTEM_SEGAPCM: case DIV_SYSTEM_SEGAPCM:
case DIV_SYSTEM_SEGAPCM_COMPAT:
return chanNames[28][dispatchChanOfChan[chan]]; return chanNames[28][dispatchChanOfChan[chan]];
break; break;
case DIV_SYSTEM_PCSPKR: case DIV_SYSTEM_PCSPKR:
@ -1088,6 +1224,7 @@ const char* DivEngine::getChannelShortName(int chan) {
break; break;
case DIV_SYSTEM_MULTIPCM: case DIV_SYSTEM_MULTIPCM:
case DIV_SYSTEM_SEGAPCM: case DIV_SYSTEM_SEGAPCM:
case DIV_SYSTEM_SEGAPCM_COMPAT:
return chanShortNames[28][dispatchChanOfChan[chan]]; return chanShortNames[28][dispatchChanOfChan[chan]];
break; break;
case DIV_SYSTEM_PCSPKR: case DIV_SYSTEM_PCSPKR:
@ -1219,6 +1356,7 @@ int DivEngine::getChannelType(int chan) {
break; break;
case DIV_SYSTEM_MULTIPCM: case DIV_SYSTEM_MULTIPCM:
case DIV_SYSTEM_SEGAPCM: case DIV_SYSTEM_SEGAPCM:
case DIV_SYSTEM_SEGAPCM_COMPAT:
case DIV_SYSTEM_QSOUND: case DIV_SYSTEM_QSOUND:
return chanTypes[28][dispatchChanOfChan[chan]]; return chanTypes[28][dispatchChanOfChan[chan]];
break; break;
@ -1346,6 +1484,7 @@ DivInstrumentType DivEngine::getPreferInsType(int chan) {
break; break;
case DIV_SYSTEM_MULTIPCM: case DIV_SYSTEM_MULTIPCM:
case DIV_SYSTEM_SEGAPCM: case DIV_SYSTEM_SEGAPCM:
case DIV_SYSTEM_SEGAPCM_COMPAT:
case DIV_SYSTEM_QSOUND: case DIV_SYSTEM_QSOUND:
return chanPrefType[28][dispatchChanOfChan[chan]]; return chanPrefType[28][dispatchChanOfChan[chan]];
break; break;
@ -1401,21 +1540,21 @@ DivInstrumentType DivEngine::getPreferInsType(int chan) {
bool DivEngine::isVGMExportable(DivSystem which) { bool DivEngine::isVGMExportable(DivSystem which) {
switch (which) { switch (which) {
case DIV_SYSTEM_GENESIS:
case DIV_SYSTEM_GENESIS_EXT:
case DIV_SYSTEM_SMS: case DIV_SYSTEM_SMS:
case DIV_SYSTEM_GB: case DIV_SYSTEM_GB:
case DIV_SYSTEM_PCE: case DIV_SYSTEM_PCE:
case DIV_SYSTEM_NES: case DIV_SYSTEM_NES:
case DIV_SYSTEM_ARCADE:
case DIV_SYSTEM_YM2151: case DIV_SYSTEM_YM2151:
case DIV_SYSTEM_YM2612: case DIV_SYSTEM_YM2612:
case DIV_SYSTEM_YM2612_EXT:
case DIV_SYSTEM_YM2610: case DIV_SYSTEM_YM2610:
case DIV_SYSTEM_YM2610_EXT: case DIV_SYSTEM_YM2610_EXT:
case DIV_SYSTEM_AY8910: case DIV_SYSTEM_AY8910:
case DIV_SYSTEM_AY8930: case DIV_SYSTEM_AY8930:
case DIV_SYSTEM_SAA1099: case DIV_SYSTEM_SAA1099:
case DIV_SYSTEM_QSOUND: case DIV_SYSTEM_QSOUND:
case DIV_SYSTEM_SEGAPCM:
case DIV_SYSTEM_SEGAPCM_COMPAT:
return true; return true;
default: default:
return false; return false;

View File

@ -975,10 +975,11 @@ SafeWriter* DivEngine::saveVGM(bool* sysToExport, bool loop) {
} }
if (writeQSound && qsoundMemLen>0) { if (writeQSound && qsoundMemLen>0) {
// always write a whole bank // always write a whole bank
unsigned int blockSize=(qsoundMemLen + 0xffff) & ~0xffff; unsigned int blockSize=(qsoundMemLen+0xffff)&(~0xffff);
if(blockSize > 0x1000000) if (blockSize > 0x1000000) {
blockSize = 0x1000000; blockSize = 0x1000000;
}
w->writeC(0x67); w->writeC(0x67);
w->writeC(0x66); w->writeC(0x66);
w->writeC(0x8F); w->writeC(0x8F);

View File

@ -220,12 +220,6 @@ void putDispatchChan(void* data, int chanNum, int type) {
ImGui::Text("- outVol: %.2x",ch->outVol); ImGui::Text("- outVol: %.2x",ch->outVol);
ImGui::Text("- chVolL: %.2x",ch->chVolL); ImGui::Text("- chVolL: %.2x",ch->chVolL);
ImGui::Text("- chVolR: %.2x",ch->chVolR); 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->active?colorOn:colorOff,">> Active");
ImGui::TextColored(ch->insChanged?colorOn:colorOff,">> InsChanged"); ImGui::TextColored(ch->insChanged?colorOn:colorOff,">> InsChanged");
ImGui::TextColored(ch->freqChanged?colorOn:colorOff,">> FreqChanged"); ImGui::TextColored(ch->freqChanged?colorOn:colorOff,">> FreqChanged");

View File

@ -422,12 +422,10 @@ void FurnaceGUI::setFileName(String name) {
} }
void FurnaceGUI::updateWindowTitle() { void FurnaceGUI::updateWindowTitle() {
String type=getSystemName(e->song.system[0]);
if (e->song.systemLen>1) type="multi-system";
if (e->song.name.empty()) { 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 { } 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 --", "-- program --",
"tildearrow", "tildearrow",
"laoo",
"superctr",
"", "",
"-- graphics --", "-- graphics/UI design --",
"tildearrow", "tildearrow",
"BlastBrothers",
"", "",
"-- documentation --", "-- documentation --",
"tildearrow", "tildearrow",
@ -2096,9 +2097,9 @@ void FurnaceGUI::drawRegView() {
for (int i=0; i<e->song.systemLen; i++) { for (int i=0; i<e->song.systemLen; i++) {
ImGui::Text("%d. %s",i+1,getSystemName(e->song.system[i])); ImGui::Text("%d. %s",i+1,getSystemName(e->song.system[i]));
int size=0; int size=0;
int depth=8; int depth=8;
unsigned char* regPool=e->getRegisterPool(i,size,depth); unsigned char* regPool=e->getRegisterPool(i,size,depth);
unsigned short* regPoolW=(unsigned short*) regPool; unsigned short* regPoolW=(unsigned short*)regPool;
if (regPool==NULL) { if (regPool==NULL) {
ImGui::Text("- no register pool available"); ImGui::Text("- no register pool available");
} else { } else {
@ -2117,12 +2118,13 @@ void FurnaceGUI::drawRegView() {
for (int j=0; j<16; j++) { for (int j=0; j<16; j++) {
ImGui::TableNextColumn(); ImGui::TableNextColumn();
if (i*16+j>=size) continue; if (i*16+j>=size) continue;
if(depth == 8) if (depth == 8) {
ImGui::Text("%.2x",regPool[i*16+j]); ImGui::Text("%.2x",regPool[i*16+j]);
else if(depth == 16) } else if (depth == 16) {
ImGui::Text("%.4x",regPoolW[i*16+j]); ImGui::Text("%.4x",regPoolW[i*16+j]);
else } else {
ImGui::Text("??"); ImGui::Text("??");
}
} }
} }
ImGui::EndTable(); ImGui::EndTable();
@ -3912,6 +3914,7 @@ bool dirExists(String what) {
void FurnaceGUI::openFileDialog(FurnaceGUIFileDialogs type) { void FurnaceGUI::openFileDialog(FurnaceGUIFileDialogs type) {
if (!dirExists(workingDir)) workingDir=getHomeDir(); if (!dirExists(workingDir)) workingDir=getHomeDir();
ImGuiFileDialog::Instance()->DpiScale=dpiScale;
switch (type) { switch (type) {
case GUI_FILE_OPEN: case GUI_FILE_OPEN:
ImGuiFileDialog::Instance()->OpenModal("FileDialog","Open File","compatible files{.fur,.dmf},.*",workingDir); ImGuiFileDialog::Instance()->OpenModal("FileDialog","Open File","compatible files{.fur,.dmf},.*",workingDir);
@ -4473,23 +4476,23 @@ bool FurnaceGUI::loop() {
} }
ImGui::Separator(); ImGui::Separator();
if (ImGui::BeginMenu("add system...")) { if (ImGui::BeginMenu("add system...")) {
sysAddOption(DIV_SYSTEM_GENESIS); sysAddOption(DIV_SYSTEM_YM2612);
sysAddOption(DIV_SYSTEM_GENESIS_EXT); sysAddOption(DIV_SYSTEM_YM2612_EXT);
sysAddOption(DIV_SYSTEM_SMS); sysAddOption(DIV_SYSTEM_SMS);
sysAddOption(DIV_SYSTEM_GB); sysAddOption(DIV_SYSTEM_GB);
sysAddOption(DIV_SYSTEM_PCE); sysAddOption(DIV_SYSTEM_PCE);
sysAddOption(DIV_SYSTEM_NES); sysAddOption(DIV_SYSTEM_NES);
sysAddOption(DIV_SYSTEM_C64_8580); sysAddOption(DIV_SYSTEM_C64_8580);
sysAddOption(DIV_SYSTEM_C64_6581); 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);
sysAddOption(DIV_SYSTEM_YM2610_EXT); sysAddOption(DIV_SYSTEM_YM2610_EXT);
sysAddOption(DIV_SYSTEM_YM2610_FULL); sysAddOption(DIV_SYSTEM_YM2610_FULL);
sysAddOption(DIV_SYSTEM_YM2610_FULL_EXT); sysAddOption(DIV_SYSTEM_YM2610_FULL_EXT);
sysAddOption(DIV_SYSTEM_AY8910); sysAddOption(DIV_SYSTEM_AY8910);
sysAddOption(DIV_SYSTEM_AMIGA); sysAddOption(DIV_SYSTEM_AMIGA);
sysAddOption(DIV_SYSTEM_YM2151);
sysAddOption(DIV_SYSTEM_YM2612);
sysAddOption(DIV_SYSTEM_TIA); sysAddOption(DIV_SYSTEM_TIA);
sysAddOption(DIV_SYSTEM_SAA1099); sysAddOption(DIV_SYSTEM_SAA1099);
sysAddOption(DIV_SYSTEM_AY8930); sysAddOption(DIV_SYSTEM_AY8930);
@ -4504,23 +4507,28 @@ bool FurnaceGUI::loop() {
bool restart=settings.restartOnFlagChange; bool restart=settings.restartOnFlagChange;
bool sysPal=flags&1; bool sysPal=flags&1;
switch (e->song.system[i]) { switch (e->song.system[i]) {
case DIV_SYSTEM_GENESIS: case DIV_SYSTEM_YM2612:
case DIV_SYSTEM_GENESIS_EXT: { case DIV_SYSTEM_YM2612_EXT: {
if (ImGui::RadioButton("NTSC (7.67MHz)",(flags&3)==0)) { if (ImGui::RadioButton("NTSC (7.67MHz)",(flags&3)==0)) {
e->setSysFlags(i,(flags&0x80000000)|0,restart); e->setSysFlags(i,(flags&0x80000000)|0,restart);
updateWindowTitle();
} }
if (ImGui::RadioButton("PAL (7.61MHz)",(flags&3)==1)) { if (ImGui::RadioButton("PAL (7.61MHz)",(flags&3)==1)) {
e->setSysFlags(i,(flags&0x80000000)|1,restart); e->setSysFlags(i,(flags&0x80000000)|1,restart);
updateWindowTitle();
} }
if (ImGui::RadioButton("FM Towns (8MHz)",(flags&3)==2)) { if (ImGui::RadioButton("FM Towns (8MHz)",(flags&3)==2)) {
e->setSysFlags(i,(flags&0x80000000)|2,restart); e->setSysFlags(i,(flags&0x80000000)|2,restart);
updateWindowTitle();
} }
if (ImGui::RadioButton("AtGames Genesis (6.13MHz)",(flags&3)==3)) { if (ImGui::RadioButton("AtGames Genesis (6.13MHz)",(flags&3)==3)) {
e->setSysFlags(i,(flags&0x80000000)|3,restart); e->setSysFlags(i,(flags&0x80000000)|3,restart);
updateWindowTitle();
} }
bool ladder=flags&0x80000000; bool ladder=flags&0x80000000;
if (ImGui::Checkbox("Enable DAC distortion",&ladder)) { if (ImGui::Checkbox("Enable DAC distortion",&ladder)) {
e->setSysFlags(i,(flags&(~0x80000000))|(ladder?0x80000000:0),restart); e->setSysFlags(i,(flags&(~0x80000000))|(ladder?0x80000000:0),restart);
updateWindowTitle();
} }
break; break;
} }
@ -4528,22 +4536,28 @@ bool FurnaceGUI::loop() {
ImGui::Text("Clock rate:"); ImGui::Text("Clock rate:");
if (ImGui::RadioButton("NTSC (3.58MHz)",(flags&3)==0)) { if (ImGui::RadioButton("NTSC (3.58MHz)",(flags&3)==0)) {
e->setSysFlags(i,(flags&(~3))|0,restart); e->setSysFlags(i,(flags&(~3))|0,restart);
updateWindowTitle();
} }
if (ImGui::RadioButton("PAL (3.55MHz)",(flags&3)==1)) { if (ImGui::RadioButton("PAL (3.55MHz)",(flags&3)==1)) {
e->setSysFlags(i,(flags&(~3))|1,restart); e->setSysFlags(i,(flags&(~3))|1,restart);
updateWindowTitle();
} }
if (ImGui::RadioButton("BBC Micro (4MHz)",(flags&3)==2)) { if (ImGui::RadioButton("BBC Micro (4MHz)",(flags&3)==2)) {
e->setSysFlags(i,(flags&(~3))|2,restart); e->setSysFlags(i,(flags&(~3))|2,restart);
updateWindowTitle();
} }
ImGui::Text("Chip type:"); ImGui::Text("Chip type:");
if (ImGui::RadioButton("Sega VDP/Master System",((flags>>2)&3)==0)) { if (ImGui::RadioButton("Sega VDP/Master System",((flags>>2)&3)==0)) {
e->setSysFlags(i,(flags&(~12))|0,restart); e->setSysFlags(i,(flags&(~12))|0,restart);
updateWindowTitle();
} }
if (ImGui::RadioButton("TI SN76489",((flags>>2)&3)==1)) { if (ImGui::RadioButton("TI SN76489",((flags>>2)&3)==1)) {
e->setSysFlags(i,(flags&(~12))|4,restart); e->setSysFlags(i,(flags&(~12))|4,restart);
updateWindowTitle();
} }
if (ImGui::RadioButton("TI SN76489 with Atari-like short noise",((flags>>2)&3)==2)) { if (ImGui::RadioButton("TI SN76489 with Atari-like short noise",((flags>>2)&3)==2)) {
e->setSysFlags(i,(flags&(~12))|8,restart); e->setSysFlags(i,(flags&(~12))|8,restart);
updateWindowTitle();
} }
/*if (ImGui::RadioButton("Game Gear",(flags>>2)==3)) { /*if (ImGui::RadioButton("Game Gear",(flags>>2)==3)) {
e->setSysFlags(i,(flags&3)|12); e->setSysFlags(i,(flags&3)|12);
@ -4552,30 +4566,36 @@ bool FurnaceGUI::loop() {
bool noPhaseReset=flags&16; bool noPhaseReset=flags&16;
if (ImGui::Checkbox("Disable noise period change phase reset",&noPhaseReset)) { if (ImGui::Checkbox("Disable noise period change phase reset",&noPhaseReset)) {
e->setSysFlags(i,(flags&(~16))|(noPhaseReset<<4),restart); e->setSysFlags(i,(flags&(~16))|(noPhaseReset<<4),restart);
updateWindowTitle();
} }
break; break;
} }
case DIV_SYSTEM_ARCADE:
case DIV_SYSTEM_YM2151: case DIV_SYSTEM_YM2151:
if (ImGui::RadioButton("NTSC (3.58MHz)",flags==0)) { if (ImGui::RadioButton("NTSC (3.58MHz)",flags==0)) {
e->setSysFlags(i,0,restart); e->setSysFlags(i,0,restart);
updateWindowTitle();
} }
if (ImGui::RadioButton("PAL (3.55MHz)",flags==1)) { if (ImGui::RadioButton("PAL (3.55MHz)",flags==1)) {
e->setSysFlags(i,1,restart); e->setSysFlags(i,1,restart);
updateWindowTitle();
} }
if (ImGui::RadioButton("X68000 (4MHz)",flags==2)) { if (ImGui::RadioButton("X68000 (4MHz)",flags==2)) {
e->setSysFlags(i,2,restart); e->setSysFlags(i,2,restart);
updateWindowTitle();
} }
break; break;
case DIV_SYSTEM_NES: case DIV_SYSTEM_NES:
if (ImGui::RadioButton("NTSC (1.79MHz)",flags==0)) { if (ImGui::RadioButton("NTSC (1.79MHz)",flags==0)) {
e->setSysFlags(i,0,restart); e->setSysFlags(i,0,restart);
updateWindowTitle();
} }
if (ImGui::RadioButton("PAL (1.67MHz)",flags==1)) { if (ImGui::RadioButton("PAL (1.67MHz)",flags==1)) {
e->setSysFlags(i,1,restart); e->setSysFlags(i,1,restart);
updateWindowTitle();
} }
if (ImGui::RadioButton("Dendy (1.77MHz)",flags==2)) { if (ImGui::RadioButton("Dendy (1.77MHz)",flags==2)) {
e->setSysFlags(i,2,restart); e->setSysFlags(i,2,restart);
updateWindowTitle();
} }
break; break;
case DIV_SYSTEM_AY8910: case DIV_SYSTEM_AY8910:
@ -4583,47 +4603,60 @@ bool FurnaceGUI::loop() {
ImGui::Text("Clock rate:"); ImGui::Text("Clock rate:");
if (ImGui::RadioButton("1.79MHz (ZX Spectrum/MSX NTSC)",(flags&15)==0)) { if (ImGui::RadioButton("1.79MHz (ZX Spectrum/MSX NTSC)",(flags&15)==0)) {
e->setSysFlags(i,(flags&(~15))|0,restart); e->setSysFlags(i,(flags&(~15))|0,restart);
updateWindowTitle();
} }
if (ImGui::RadioButton("1.77MHz (ZX Spectrum/MSX PAL)",(flags&15)==1)) { if (ImGui::RadioButton("1.77MHz (ZX Spectrum/MSX PAL)",(flags&15)==1)) {
e->setSysFlags(i,(flags&(~15))|1,restart); e->setSysFlags(i,(flags&(~15))|1,restart);
updateWindowTitle();
} }
if (ImGui::RadioButton("1.75MHz (ZX Spectrum)",(flags&15)==2)) { if (ImGui::RadioButton("1.75MHz (ZX Spectrum)",(flags&15)==2)) {
e->setSysFlags(i,(flags&(~15))|2,restart); e->setSysFlags(i,(flags&(~15))|2,restart);
updateWindowTitle();
} }
if (ImGui::RadioButton("2MHz (Atari ST)",(flags&15)==3)) { if (ImGui::RadioButton("2MHz (Atari ST)",(flags&15)==3)) {
e->setSysFlags(i,(flags&(~15))|3,restart); e->setSysFlags(i,(flags&(~15))|3,restart);
updateWindowTitle();
} }
if (ImGui::RadioButton("1.5MHz (Vectrex)",(flags&15)==4)) { if (ImGui::RadioButton("1.5MHz (Vectrex)",(flags&15)==4)) {
e->setSysFlags(i,(flags&(~15))|4,restart); e->setSysFlags(i,(flags&(~15))|4,restart);
updateWindowTitle();
} }
if (ImGui::RadioButton("1MHz (Amstrad CPC)",(flags&15)==5)) { if (ImGui::RadioButton("1MHz (Amstrad CPC)",(flags&15)==5)) {
e->setSysFlags(i,(flags&(~15))|5,restart); e->setSysFlags(i,(flags&(~15))|5,restart);
updateWindowTitle();
} }
if (ImGui::RadioButton("0.89MHz (Sunsoft 5B)",(flags&15)==6)) { if (ImGui::RadioButton("0.89MHz (Sunsoft 5B)",(flags&15)==6)) {
e->setSysFlags(i,(flags&(~15))|6,restart); e->setSysFlags(i,(flags&(~15))|6,restart);
updateWindowTitle();
} }
if (ImGui::RadioButton("1.67MHz (?)",(flags&15)==7)) { if (ImGui::RadioButton("1.67MHz (?)",(flags&15)==7)) {
e->setSysFlags(i,(flags&(~15))|7,restart); e->setSysFlags(i,(flags&(~15))|7,restart);
updateWindowTitle();
} }
if (ImGui::RadioButton("0.83MHz (Sunsoft 5B on PAL)",(flags&15)==8)) { if (ImGui::RadioButton("0.83MHz (Sunsoft 5B on PAL)",(flags&15)==8)) {
e->setSysFlags(i,(flags&(~15))|8,restart); e->setSysFlags(i,(flags&(~15))|8,restart);
updateWindowTitle();
} }
if (e->song.system[i]==DIV_SYSTEM_AY8910) { if (e->song.system[i]==DIV_SYSTEM_AY8910) {
ImGui::Text("Chip type:"); ImGui::Text("Chip type:");
if (ImGui::RadioButton("AY-3-8910",(flags&0x30)==0)) { if (ImGui::RadioButton("AY-3-8910",(flags&0x30)==0)) {
e->setSysFlags(i,(flags&(~0x30))|0,restart); e->setSysFlags(i,(flags&(~0x30))|0,restart);
updateWindowTitle();
} }
if (ImGui::RadioButton("YM2149(F)",(flags&0x30)==16)) { if (ImGui::RadioButton("YM2149(F)",(flags&0x30)==16)) {
e->setSysFlags(i,(flags&(~0x30))|16,restart); e->setSysFlags(i,(flags&(~0x30))|16,restart);
updateWindowTitle();
} }
if (ImGui::RadioButton("Sunsoft 5B",(flags&0x30)==32)) { if (ImGui::RadioButton("Sunsoft 5B",(flags&0x30)==32)) {
e->setSysFlags(i,(flags&(~0x30))|32,restart); e->setSysFlags(i,(flags&(~0x30))|32,restart);
updateWindowTitle();
} }
} }
bool stereo=flags&0x40; bool stereo=flags&0x40;
ImGui::BeginDisabled((flags&0x30)==32); ImGui::BeginDisabled((flags&0x30)==32);
if (ImGui::Checkbox("Stereo##_AY_STEREO",&stereo)) { if (ImGui::Checkbox("Stereo##_AY_STEREO",&stereo)) {
e->setSysFlags(i,(flags&(~0x40))|(stereo?0x40:0),restart); e->setSysFlags(i,(flags&(~0x40))|(stereo?0x40:0),restart);
updateWindowTitle();
} }
ImGui::EndDisabled(); ImGui::EndDisabled();
break; break;
@ -4631,12 +4664,15 @@ bool FurnaceGUI::loop() {
case DIV_SYSTEM_SAA1099: case DIV_SYSTEM_SAA1099:
if (ImGui::RadioButton("SAM Coupé (8MHz)",flags==0)) { if (ImGui::RadioButton("SAM Coupé (8MHz)",flags==0)) {
e->setSysFlags(i,0,restart); e->setSysFlags(i,0,restart);
updateWindowTitle();
} }
if (ImGui::RadioButton("NTSC (7.15MHz)",flags==1)) { if (ImGui::RadioButton("NTSC (7.15MHz)",flags==1)) {
e->setSysFlags(i,1,restart); e->setSysFlags(i,1,restart);
updateWindowTitle();
} }
if (ImGui::RadioButton("PAL (7.09MHz)",flags==2)) { if (ImGui::RadioButton("PAL (7.09MHz)",flags==2)) {
e->setSysFlags(i,2,restart); e->setSysFlags(i,2,restart);
updateWindowTitle();
} }
break; break;
case DIV_SYSTEM_AMIGA: { case DIV_SYSTEM_AMIGA: {
@ -4646,6 +4682,7 @@ bool FurnaceGUI::loop() {
if (stereoSep<0) stereoSep=0; if (stereoSep<0) stereoSep=0;
if (stereoSep>127) stereoSep=127; if (stereoSep>127) stereoSep=127;
e->setSysFlags(i,(flags&1)|((stereoSep&127)<<8),restart); e->setSysFlags(i,(flags&1)|((stereoSep&127)<<8),restart);
updateWindowTitle();
} }
/* TODO LATER: I want 0.5 out already /* TODO LATER: I want 0.5 out already
if (ImGui::RadioButton("Amiga 500 (OCS)",(flags&2)==0)) { if (ImGui::RadioButton("Amiga 500 (OCS)",(flags&2)==0)) {
@ -4657,6 +4694,7 @@ bool FurnaceGUI::loop() {
sysPal=flags&1; sysPal=flags&1;
if (ImGui::Checkbox("PAL",&sysPal)) { if (ImGui::Checkbox("PAL",&sysPal)) {
e->setSysFlags(i,(flags&2)|sysPal,restart); e->setSysFlags(i,(flags&2)|sysPal,restart);
updateWindowTitle();
} }
break; break;
} }
@ -4667,6 +4705,7 @@ bool FurnaceGUI::loop() {
if (echoBufSize<0) echoBufSize=0; if (echoBufSize<0) echoBufSize=0;
if (echoBufSize>2725) echoBufSize=2725; if (echoBufSize>2725) echoBufSize=2725;
e->setSysFlags(i,(flags & ~4095) | ((2725 - echoBufSize) & 4095),restart); e->setSysFlags(i,(flags & ~4095) | ((2725 - echoBufSize) & 4095),restart);
updateWindowTitle();
} }
ImGui::Text("Echo feedback:"); ImGui::Text("Echo feedback:");
int echoFeedback=(flags>>12)&255; int echoFeedback=(flags>>12)&255;
@ -4674,6 +4713,7 @@ bool FurnaceGUI::loop() {
if (echoFeedback<0) echoFeedback=0; if (echoFeedback<0) echoFeedback=0;
if (echoFeedback>255) echoFeedback=255; if (echoFeedback>255) echoFeedback=255;
e->setSysFlags(i,(flags & ~(255 << 12)) | ((echoFeedback & 255) << 12),restart); e->setSysFlags(i,(flags & ~(255 << 12)) | ((echoFeedback & 255) << 12),restart);
updateWindowTitle();
} }
break; break;
} }
@ -4688,6 +4728,7 @@ bool FurnaceGUI::loop() {
default: default:
if (ImGui::Checkbox("PAL",&sysPal)) { if (ImGui::Checkbox("PAL",&sysPal)) {
e->setSysFlags(i,sysPal,restart); e->setSysFlags(i,sysPal,restart);
updateWindowTitle();
} }
break; break;
} }
@ -4699,23 +4740,23 @@ bool FurnaceGUI::loop() {
if (ImGui::BeginMenu("change system...")) { if (ImGui::BeginMenu("change system...")) {
for (int i=0; i<e->song.systemLen; i++) { for (int i=0; i<e->song.systemLen; i++) {
if (ImGui::BeginMenu(fmt::sprintf("%d. %s##_SYSC%d",i+1,getSystemName(e->song.system[i]),i).c_str())) { 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_YM2612);
sysChangeOption(i,DIV_SYSTEM_GENESIS_EXT); sysChangeOption(i,DIV_SYSTEM_YM2612_EXT);
sysChangeOption(i,DIV_SYSTEM_SMS); sysChangeOption(i,DIV_SYSTEM_SMS);
sysChangeOption(i,DIV_SYSTEM_GB); sysChangeOption(i,DIV_SYSTEM_GB);
sysChangeOption(i,DIV_SYSTEM_PCE); sysChangeOption(i,DIV_SYSTEM_PCE);
sysChangeOption(i,DIV_SYSTEM_NES); sysChangeOption(i,DIV_SYSTEM_NES);
sysChangeOption(i,DIV_SYSTEM_C64_8580); sysChangeOption(i,DIV_SYSTEM_C64_8580);
sysChangeOption(i,DIV_SYSTEM_C64_6581); 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);
sysChangeOption(i,DIV_SYSTEM_YM2610_EXT); sysChangeOption(i,DIV_SYSTEM_YM2610_EXT);
sysChangeOption(i,DIV_SYSTEM_YM2610_FULL); sysChangeOption(i,DIV_SYSTEM_YM2610_FULL);
sysChangeOption(i,DIV_SYSTEM_YM2610_FULL_EXT); sysChangeOption(i,DIV_SYSTEM_YM2610_FULL_EXT);
sysChangeOption(i,DIV_SYSTEM_AY8910); sysChangeOption(i,DIV_SYSTEM_AY8910);
sysChangeOption(i,DIV_SYSTEM_AMIGA); sysChangeOption(i,DIV_SYSTEM_AMIGA);
sysChangeOption(i,DIV_SYSTEM_YM2151);
sysChangeOption(i,DIV_SYSTEM_YM2612);
sysChangeOption(i,DIV_SYSTEM_TIA); sysChangeOption(i,DIV_SYSTEM_TIA);
sysChangeOption(i,DIV_SYSTEM_SAA1099); sysChangeOption(i,DIV_SYSTEM_SAA1099);
sysChangeOption(i,DIV_SYSTEM_AY8930); sysChangeOption(i,DIV_SYSTEM_AY8930);

View File

@ -24,6 +24,7 @@
#include "guiConst.h" #include "guiConst.h"
#include "intConst.h" #include "intConst.h"
#include <fmt/printf.h> #include <fmt/printf.h>
#include <imgui.h>
#include "plot_nolerp.h" #include "plot_nolerp.h"
const char* insTypes[24]={ const char* insTypes[24]={
@ -786,29 +787,103 @@ void FurnaceGUI::drawInsEdit() {
//56.0 controls vert scaling; default 96 //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)); 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,&_ZERO,&_THIRTY_ONE));
P(ImGui::SliderScalar(FM_NAME(FM_AR),ImGuiDataType_U8,&op.ar,&_THIRTY_ONE,&_ZERO)); if (ImGui::BeginTable("opParams",2,ImGuiTableFlags_SizingStretchProp)) {
P(ImGui::SliderScalar(FM_NAME(FM_DR),ImGuiDataType_U8,&op.dr,&_THIRTY_ONE,&_ZERO)); ImGui::TableSetupColumn("c0",ImGuiTableColumnFlags_WidthStretch,0.0); \
P(ImGui::SliderScalar(FM_NAME(FM_SL),ImGuiDataType_U8,&op.sl,&_FIFTEEN,&_ZERO)); ImGui::TableSetupColumn("c1",ImGuiTableColumnFlags_WidthFixed,0.0); \
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));
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)); ImGui::TableNextRow();
P(ImGui::SliderScalar(FM_NAME(FM_MULT),ImGuiDataType_U8,&op.mult,&_ZERO,&_FIFTEEN)); ImGui::TableNextColumn();
ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x);
int detune=(op.dt&7)-3; P(ImGui::SliderScalar("##DR",ImGuiDataType_U8,&op.dr,&_THIRTY_ONE,&_ZERO));
if (ImGui::SliderInt(FM_NAME(FM_DT),&detune,-3,3)) { PARAMETER ImGui::TableNextColumn();
op.dt=detune+3; 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::PopID();
} }
ImGui::EndTable(); ImGui::EndTable();