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/saa.cpp
src/engine/platform/amiga.cpp
src/engine/platform/segapcm.cpp
src/engine/platform/qsound.cpp
src/engine/platform/dummy.cpp
src/engine/platform/lynx.cpp

BIN
demos/m7 vibe.fur Normal file

Binary file not shown.

View file

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

View file

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

View file

@ -225,7 +225,7 @@ class DivDispatch {
/**
* get the bit depth of the register pool of this dispatch.
* If the result is 16, it should be casted to unsigned short
* If the result is 16, it should be casted to unsigned short.
* @return the depth. Default value is 8
*/
virtual int getRegisterPoolDepth();

View file

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

View file

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

View file

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

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

View file

@ -130,9 +130,6 @@ const char* DivPlatformArcade::getEffectName(unsigned char effect) {
case 0x1f:
return "1Fxx: Set PM depth (0 to 7F)";
break;
case 0x20:
return "20xx: Set PCM frequency";
break;
}
return NULL;
}
@ -158,43 +155,6 @@ void DivPlatformArcade::acquire_nuked(short* bufL, short* bufR, size_t start, si
OPM_Clock(&fm,NULL,NULL,NULL,NULL);
OPM_Clock(&fm,NULL,NULL,NULL,NULL);
OPM_Clock(&fm,o,NULL,NULL,NULL);
pcmCycles+=31250;
if (pcmCycles>=rate) {
pcmCycles-=rate;
// do a PCM cycle
pcmL=0; pcmR=0;
for (int i=8; i<13; i++) {
if (chan[i].pcm.sample>=0) {
DivSample* s=parent->song.sample[chan[i].pcm.sample];
if (s->rendLength<=0) {
chan[i].pcm.sample=-1;
continue;
}
if (!isMuted[i]) {
if (s->depth==8) {
pcmL+=(s->rendData[chan[i].pcm.pos>>8]*chan[i].chVolL);
pcmR+=(s->rendData[chan[i].pcm.pos>>8]*chan[i].chVolR);
} else {
pcmL+=(s->rendData[chan[i].pcm.pos>>8]*chan[i].chVolL)>>8;
pcmR+=(s->rendData[chan[i].pcm.pos>>8]*chan[i].chVolR)>>8;
}
}
chan[i].pcm.pos+=chan[i].pcm.freq;
if (chan[i].pcm.pos>=(s->rendLength<<8)) {
if (s->loopStart>=0 && s->loopStart<=(int)s->rendLength) {
chan[i].pcm.pos=s->loopStart<<8;
} else {
chan[i].pcm.sample=-1;
}
}
}
}
}
o[0]+=pcmL;
o[1]+=pcmR;
if (o[0]<-32768) o[0]=-32768;
if (o[0]>32767) o[0]=32767;
@ -225,45 +185,11 @@ void DivPlatformArcade::acquire_ymfm(short* bufL, short* bufR, size_t start, siz
fm_ymfm->generate(&out_ymfm);
pcmCycles+=31250;
if (pcmCycles>=rate) {
pcmCycles-=rate;
// do a PCM cycle
pcmL=0; pcmR=0;
for (int i=8; i<13; i++) {
if (chan[i].pcm.sample>=0 && chan[i].pcm.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;
os[0]=out_ymfm.data[0];
if (os[0]<-32768) os[0]=-32768;
if (os[0]>32767) os[0]=32767;
os[1]=out_ymfm.data[1]+pcmR;
os[1]=out_ymfm.data[1];
if (os[1]<-32768) os[1]=-32768;
if (os[1]>32767) os[1]=32767;
@ -464,106 +390,21 @@ void DivPlatformArcade::tick() {
chan[i].keyOn=false;
}
}
for (int i=8; i<13; i++) {
if (chan[i].freqChanged) {
chan[i].freq=chan[i].baseFreq+(chan[i].pitch>>1)-64;
if (chan[i].furnacePCM) {
double off=1.0;
if (chan[i].pcm.sample>=0 && chan[i].pcm.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) {
isMuted[ch]=mute;
if (ch<8) {
if (isMuted[ch]) {
rWrite(chanOffs[ch]+ADDR_LR_FB_ALG,(chan[ch].state.alg&7)|(chan[ch].state.fb<<3));
} else {
rWrite(chanOffs[ch]+ADDR_LR_FB_ALG,(chan[ch].state.alg&7)|(chan[ch].state.fb<<3)|((chan[ch].chVolL&1)<<6)|((chan[ch].chVolR&1)<<7));
}
if (isMuted[ch]) {
rWrite(chanOffs[ch]+ADDR_LR_FB_ALG,(chan[ch].state.alg&7)|(chan[ch].state.fb<<3));
} else {
rWrite(chanOffs[ch]+ADDR_LR_FB_ALG,(chan[ch].state.alg&7)|(chan[ch].state.fb<<3)|((chan[ch].chVolL&1)<<6)|((chan[ch].chVolR&1)<<7));
}
}
int DivPlatformArcade::dispatch(DivCommand c) {
int pcmChan=c.chan-8;
switch (c.cmd) {
case DIV_CMD_NOTE_ON: {
DivInstrument* ins=parent->getIns(chan[c.chan].ins);
if (c.chan>7) {
if (skipRegisterWrites) break;
if (ins->type==DIV_INS_AMIGA) {
chan[c.chan].pcm.sample=ins->amiga.initSample;
if (chan[c.chan].pcm.sample<0 || chan[c.chan].pcm.sample>=parent->song.sampleLen) {
chan[c.chan].pcm.sample=-1;
if (dumpWrites) {
addWrite(0x10086+(pcmChan<<3),3);
}
break;
}
chan[c.chan].pcm.pos=0;
chan[c.chan].baseFreq=(c.value<<6);
chan[c.chan].freqChanged=true;
chan[c.chan].furnacePCM=true;
if (dumpWrites) { // Sega PCM writes
DivSample* s=parent->song.sample[chan[c.chan].pcm.sample];
addWrite(0x10086+(pcmChan<<3),3+((s->rendOffP>>16)<<3));
addWrite(0x10084+(pcmChan<<3),(s->rendOffP)&0xff);
addWrite(0x10085+(pcmChan<<3),(s->rendOffP>>8)&0xff);
addWrite(0x10006+(pcmChan<<3),MIN(255,((s->rendOffP&0xffff)+s->rendLength-1)>>8));
if (s->loopStart<0 || s->loopStart>=(int)s->rendLength) {
addWrite(0x10086+(pcmChan<<3),2+((s->rendOffP>>16)<<3));
} else {
int loopPos=(s->rendOffP&0xffff)+s->loopStart+s->loopOffP;
addWrite(0x10004+(pcmChan<<3),loopPos&0xff);
addWrite(0x10005+(pcmChan<<3),(loopPos>>8)&0xff);
addWrite(0x10086+(pcmChan<<3),((s->rendOffP>>16)<<3));
}
}
} else {
if (c.value!=DIV_NOTE_NULL) {
chan[c.chan].note=c.value;
}
chan[c.chan].pcm.sample=12*sampleBank+chan[c.chan].note%12;
if (chan[c.chan].pcm.sample>=parent->song.sampleLen) {
chan[c.chan].pcm.sample=-1;
if (dumpWrites) {
addWrite(0x10086+(pcmChan<<3),3);
}
break;
}
chan[c.chan].pcm.pos=0;
chan[c.chan].pcm.freq=MIN(255,(parent->song.sample[chan[c.chan].pcm.sample]->rate*255)/31250);
chan[c.chan].furnacePCM=false;
if (dumpWrites) { // Sega PCM writes
DivSample* s=parent->song.sample[chan[c.chan].pcm.sample];
addWrite(0x10086+(pcmChan<<3),3+((s->rendOffP>>16)<<3));
addWrite(0x10084+(pcmChan<<3),(s->rendOffP)&0xff);
addWrite(0x10085+(pcmChan<<3),(s->rendOffP>>8)&0xff);
addWrite(0x10006+(pcmChan<<3),MIN(255,((s->rendOffP&0xffff)+s->rendLength-1)>>8));
if (s->loopStart<0 || s->loopStart>=(int)s->rendLength) {
addWrite(0x10086+(pcmChan<<3),2+((s->rendOffP>>16)<<3));
} else {
int loopPos=(s->rendOffP&0xffff)+s->loopStart+s->loopOffP;
addWrite(0x10004+(pcmChan<<3),loopPos&0xff);
addWrite(0x10005+(pcmChan<<3),(loopPos>>8)&0xff);
addWrite(0x10086+(pcmChan<<3),((s->rendOffP>>16)<<3));
}
addWrite(0x10007+(pcmChan<<3),chan[c.chan].pcm.freq);
}
}
break;
}
if (chan[c.chan].insChanged) {
chan[c.chan].state=ins->fm;
@ -614,12 +455,6 @@ int DivPlatformArcade::dispatch(DivCommand c) {
break;
}
case DIV_CMD_NOTE_OFF:
if (c.chan>7) {
chan[c.chan].pcm.sample=-1;
if (dumpWrites) {
addWrite(0x10086+(pcmChan<<3),3);
}
}
chan[c.chan].keyOff=true;
chan[c.chan].keyOn=false;
chan[c.chan].active=false;
@ -638,15 +473,6 @@ int DivPlatformArcade::dispatch(DivCommand c) {
if (!chan[c.chan].std.hasVol) {
chan[c.chan].outVol=c.value;
}
if (c.chan>7) {
chan[c.chan].chVolL=c.value;
chan[c.chan].chVolR=c.value;
if (dumpWrites) {
addWrite(0x10002+(pcmChan<<3),chan[c.chan].chVolL);
addWrite(0x10003+(pcmChan<<3),chan[c.chan].chVolR);
}
break;
}
for (int i=0; i<4; i++) {
unsigned short baseAddr=chanOffs[c.chan]|opOffs[i];
DivInstrumentFM::Operator& op=chan[c.chan].state.op[i];
@ -669,22 +495,12 @@ int DivPlatformArcade::dispatch(DivCommand c) {
chan[c.chan].ins=c.value;
break;
case DIV_CMD_PANNING: {
// TODO
if (c.chan>7) {
chan[c.chan].chVolL=(c.value>>4)|(((c.value>>4)>>1)<<4);
chan[c.chan].chVolR=(c.value&15)|(((c.value&15)>>1)<<4);
if (dumpWrites) {
addWrite(0x10002+(pcmChan<<3),chan[c.chan].chVolL);
addWrite(0x10003+(pcmChan<<3),chan[c.chan].chVolR);
}
chan[c.chan].chVolL=((c.value>>4)==1);
chan[c.chan].chVolR=((c.value&15)==1);
if (isMuted[c.chan]) {
rWrite(chanOffs[c.chan]+ADDR_LR_FB_ALG,(chan[c.chan].state.alg&7)|(chan[c.chan].state.fb<<3));
} else {
chan[c.chan].chVolL=((c.value>>4)==1);
chan[c.chan].chVolR=((c.value&15)==1);
if (isMuted[c.chan]) {
rWrite(chanOffs[c.chan]+ADDR_LR_FB_ALG,(chan[c.chan].state.alg&7)|(chan[c.chan].state.fb<<3));
} else {
rWrite(chanOffs[c.chan]+ADDR_LR_FB_ALG,(chan[c.chan].state.alg&7)|(chan[c.chan].state.fb<<3)|((chan[c.chan].chVolL&1)<<6)|((chan[c.chan].chVolR&1)<<7));
}
rWrite(chanOffs[c.chan]+ADDR_LR_FB_ALG,(chan[c.chan].state.alg&7)|(chan[c.chan].state.fb<<3)|((chan[c.chan].chVolL&1)<<6)|((chan[c.chan].chVolR&1)<<7));
}
break;
}
@ -724,17 +540,14 @@ int DivPlatformArcade::dispatch(DivCommand c) {
break;
}
case DIV_CMD_FM_LFO: {
if (c.chan>7) break;
rWrite(0x18,c.value);
break;
}
case DIV_CMD_FM_LFO_WAVE: {
if (c.chan>7) break;
rWrite(0x1b,c.value&3);
break;
}
case DIV_CMD_FM_FB: {
if (c.chan>7) break;
chan[c.chan].state.fb=c.value&7;
if (isMuted[c.chan]) {
rWrite(chanOffs[c.chan]+ADDR_LR_FB_ALG,(chan[c.chan].state.alg&7)|(chan[c.chan].state.fb<<3));
@ -744,7 +557,6 @@ int DivPlatformArcade::dispatch(DivCommand c) {
break;
}
case DIV_CMD_FM_MULT: {
if (c.chan>7) break;
unsigned short baseAddr=chanOffs[c.chan]|opOffs[orderedOps[c.value]];
DivInstrumentFM::Operator& op=chan[c.chan].state.op[orderedOps[c.value]];
op.mult=c.value2&15;
@ -752,7 +564,6 @@ int DivPlatformArcade::dispatch(DivCommand c) {
break;
}
case DIV_CMD_FM_TL: {
if (c.chan>7) break;
unsigned short baseAddr=chanOffs[c.chan]|opOffs[orderedOps[c.value]];
DivInstrumentFM::Operator& op=chan[c.chan].state.op[orderedOps[c.value]];
op.tl=c.value2;
@ -764,7 +575,6 @@ int DivPlatformArcade::dispatch(DivCommand c) {
break;
}
case DIV_CMD_FM_AR: {
if (c.chan>7) break;
if (c.value<0) {
for (int i=0; i<4; i++) {
DivInstrumentFM::Operator& op=chan[c.chan].state.op[i];
@ -803,12 +613,6 @@ int DivPlatformArcade::dispatch(DivCommand c) {
}
break;
}
case DIV_CMD_SAMPLE_BANK:
sampleBank=c.value;
if (sampleBank>(parent->song.sample.size()/12)) {
sampleBank=parent->song.sample.size()/12;
}
break;
case DIV_ALWAYS_SET_VOLUME:
return 0;
break;
@ -820,12 +624,6 @@ int DivPlatformArcade::dispatch(DivCommand c) {
break;
case DIV_CMD_PRE_NOTE:
break;
case DIV_CMD_SAMPLE_FREQ:
chan[c.chan].pcm.freq=c.value;
if (dumpWrites) {
addWrite(0x10007+(pcmChan<<3),chan[c.chan].pcm.freq);
}
break;
default:
//printf("WARNING: unimplemented command %d\n",c.cmd);
break;
@ -860,9 +658,6 @@ void DivPlatformArcade::forceIns() {
chan[i].freqChanged=true;
}
}
for (int i=8; i<13; i++) {
chan[i].insChanged=true;
}
immWrite(0x19,amDepth);
immWrite(0x19,0x80|pmDepth);
}
@ -907,7 +702,7 @@ void DivPlatformArcade::reset() {
if (dumpWrites) {
addWrite(0xffffffff,0);
}
for (int i=0; i<13; i++) {
for (int i=0; i<8; i++) {
chan[i]=DivPlatformArcade::Channel();
chan[i].vol=0x7f;
chan[i].outVol=0x7f;
@ -922,7 +717,6 @@ void DivPlatformArcade::reset() {
pcmCycles=0;
pcmL=0;
pcmR=0;
sampleBank=0;
delay=0;
amDepth=0x7f;
pmDepth=0x7f;
@ -931,13 +725,6 @@ void DivPlatformArcade::reset() {
immWrite(0x19,amDepth);
immWrite(0x19,0x80|pmDepth);
//rWrite(0x1b,0x00);
if (dumpWrites) {
for (int i=0; i<5; i++) {
addWrite(0x10086+(i<<3),3);
addWrite(0x10002+(i<<3),0x7f);
addWrite(0x10003+(i<<3),0x7f);
}
}
extMode=false;
}
@ -972,14 +759,14 @@ int DivPlatformArcade::init(DivEngine* p, int channels, int sugRate, unsigned in
parent=p;
dumpWrites=false;
skipRegisterWrites=false;
for (int i=0; i<13; i++) {
for (int i=0; i<8; i++) {
isMuted[i]=false;
}
setFlags(flags);
if (useYMFM) fm_ymfm=new ymfm::ym2151(iface);
reset();
return 13;
return 8;
}
void DivPlatformArcade::quit() {

View file

@ -42,17 +42,9 @@ class DivPlatformArcade: public DivDispatch {
bool active, insChanged, freqChanged, keyOn, keyOff, inPorta, portaPause, furnacePCM;
int vol, outVol;
unsigned char chVolL, chVolR;
struct PCMChannel {
int sample;
unsigned int pos; // <<8
unsigned short len;
unsigned char freq;
PCMChannel(): sample(-1), pos(0), len(0), freq(0) {}
} pcm;
Channel(): freqH(0), freqL(0), freq(0), baseFreq(0), pitch(0), note(0), ins(-1), active(false), insChanged(true), freqChanged(false), keyOn(false), keyOff(false), inPorta(false), portaPause(false), furnacePCM(false), vol(0), outVol(0), chVolL(127), chVolR(127) {}
};
Channel chan[13];
Channel chan[8];
struct QueuedWrite {
unsigned short addr;
unsigned char val;
@ -63,7 +55,6 @@ class DivPlatformArcade: public DivDispatch {
opm_t fm;
int delay, baseFreqOff;
int pcmL, pcmR, pcmCycles;
unsigned char sampleBank;
unsigned char lastBusy;
unsigned char amDepth, pmDepth;
@ -75,7 +66,7 @@ class DivPlatformArcade: public DivDispatch {
bool extMode, useYMFM;
bool isMuted[13];
bool isMuted[8];
short oldWrites[256];
short pendingWrites[256];

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

View file

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

View file

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

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

View file

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

View file

@ -131,6 +131,8 @@ DivSystem DivEngine::systemFromFile(unsigned char val) {
return DIV_SYSTEM_OPLL_DRUMS;
case 0xa8:
return DIV_SYSTEM_LYNX;
case 0xa9:
return DIV_SYSTEM_SEGAPCM_COMPAT;
case 0xe0:
return DIV_SYSTEM_QSOUND;
}
@ -248,6 +250,8 @@ unsigned char DivEngine::systemToFile(DivSystem val) {
return 0xa7;
case DIV_SYSTEM_LYNX:
return 0xa8;
case DIV_SYSTEM_SEGAPCM_COMPAT:
return 0xa9;
case DIV_SYSTEM_QSOUND:
return 0xe0;
@ -364,6 +368,8 @@ int DivEngine::getChannelCount(DivSystem sys) {
return 11;
case DIV_SYSTEM_LYNX:
return 4;
case DIV_SYSTEM_SEGAPCM_COMPAT:
return 5;
case DIV_SYSTEM_QSOUND:
return 19;
}
@ -374,7 +380,132 @@ int DivEngine::getTotalChannelCount() {
return chans;
}
// TODO: replace with a better strategy to determine name
const char* DivEngine::getSongSystemName() {
switch (song.systemLen) {
case 0:
return "help! what's going on!";
case 1:
if (song.system[0]==DIV_SYSTEM_AY8910) {
switch (song.systemFlags[0]&0x3f) {
case 0: // AY-3-8910, 1.79MHz
case 1: // AY-3-8910, 1.77MHz
case 2: // AY-3-8910, 1.75MHz
return "ZX Spectrum";
case 3: // AY-3-8910, 2MHz
return "Fujitsu Micro-7";
case 4: // AY-3-8910, 1.5MHz
return "Vectrex";
case 5: // AY-3-8910, 1MHz
return "Amstrad CPC";
case 6: // AY-3-8910, 0.somethingMhz
return "Intellivision";
case 8: // AY-3-8910, 0.somethingMhz
return "Intellivision (PAL)";
case 0x10: // YM2149, 1.79MHz
return "MSX";
case 0x13: // YM2149, 2MHz
return "Atari ST";
case 0x26: // 5B NTSC
return "Sunsoft 5B standalone";
case 0x28: // 5B PAL
return "Sunsoft 5B standalone (PAL)";
default:
if ((song.systemFlags[0]&0x30)==0x00) {
return "AY-3-8910";
} else if ((song.systemFlags[0]&0x30)==0x10) {
return "Yamaha YM2149";
} else if ((song.systemFlags[0]&0x30)==0x20) {
return "Overclocked Sunsoft 5B";
}
}
} else if (song.system[0]==DIV_SYSTEM_SMS) {
switch (song.systemFlags[0]&0x0f) {
case 0: case 1:
return "Sega Master System";
case 6:
return "BBC Micro";
}
} else if (song.system[0]==DIV_SYSTEM_YM2612) {
switch (song.systemFlags[0]&3) {
case 2:
return "FM Towns";
}
} else if (song.system[0]==DIV_SYSTEM_YM2151) {
switch (song.systemFlags[0]&3) {
case 2:
return "Sharp X68000";
}
} else if (song.system[0]==DIV_SYSTEM_SAA1099) {
switch (song.systemFlags[0]&3) {
case 0:
return "SAM Coupé";
}
}
return getSystemName(song.system[0]);
case 2:
if (song.system[0]==DIV_SYSTEM_YM2612 && song.system[1]==DIV_SYSTEM_SMS) {
return "Sega Genesis/Mega Drive";
}
if (song.system[0]==DIV_SYSTEM_YM2612_EXT && song.system[1]==DIV_SYSTEM_SMS) {
return "Sega Genesis Extended Channel 3";
}
if (song.system[0]==DIV_SYSTEM_C64_6581 && song.system[1]==DIV_SYSTEM_C64_6581) {
return "Commodore 64 with dual 6581";
}
if (song.system[0]==DIV_SYSTEM_C64_8580 && song.system[1]==DIV_SYSTEM_C64_8580) {
return "Commodore 64 with dual 8580";
}
if (song.system[0]==DIV_SYSTEM_YM2151 && song.system[1]==DIV_SYSTEM_SEGAPCM_COMPAT) {
return "YM2151 + SegaPCM Arcade (compatibility)";
}
if (song.system[0]==DIV_SYSTEM_YM2151 && song.system[1]==DIV_SYSTEM_SEGAPCM) {
return "YM2151 + SegaPCM Arcade";
}
if (song.system[0]==DIV_SYSTEM_SAA1099 && song.system[1]==DIV_SYSTEM_SAA1099) {
return "Creative Music System";
}
if (song.system[0]==DIV_SYSTEM_GB && song.system[1]==DIV_SYSTEM_AY8910) {
return "Game Boy with AY expansion";
}
if (song.system[0]==DIV_SYSTEM_NES && song.system[1]==DIV_SYSTEM_VRC6) {
return "NES + Konami VRC6";
}
if (song.system[0]==DIV_SYSTEM_NES && song.system[1]==DIV_SYSTEM_VRC7) {
return "NES + Konami VRC7";
}
if (song.system[0]==DIV_SYSTEM_NES && song.system[1]==DIV_SYSTEM_OPLL) {
return "NES + Yamaha OPLL";
}
if (song.system[0]==DIV_SYSTEM_NES && song.system[1]==DIV_SYSTEM_FDS) {
return "Famicom Disk System";
}
if (song.system[0]==DIV_SYSTEM_NES && song.system[1]==DIV_SYSTEM_N163) {
return "NES + Namco 163";
}
if (song.system[0]==DIV_SYSTEM_NES && song.system[1]==DIV_SYSTEM_MMC5) {
return "NES + MMC5";
}
if (song.system[0]==DIV_SYSTEM_NES && song.system[1]==DIV_SYSTEM_AY8910) {
return "NES + Sunsoft 5B";
}
if (song.system[0]==DIV_SYSTEM_AY8910 && song.system[1]==DIV_SYSTEM_AY8910) {
return "Bally Midway MCR";
}
break;
case 3:
break;
}
return "multi-system";
}
const char* DivEngine::getSystemName(DivSystem sys) {
switch (sys) {
case DIV_SYSTEM_NULL:
@ -384,7 +515,7 @@ const char* DivEngine::getSystemName(DivSystem sys) {
case DIV_SYSTEM_GENESIS:
return "Sega Genesis/Mega Drive";
case DIV_SYSTEM_SMS:
return "Sega Master System";
return "TI SN76489";
case DIV_SYSTEM_SMS_OPLL:
return "Sega Master System + FM Expansion";
case DIV_SYSTEM_GB:
@ -431,7 +562,7 @@ const char* DivEngine::getSystemName(DivSystem sys) {
case DIV_SYSTEM_OPLL:
return "Yamaha OPLL";
case DIV_SYSTEM_FDS:
return "Famicom Disk System";
return "Famicom Disk System (chip)";
case DIV_SYSTEM_MMC5:
return "MMC5";
case DIV_SYSTEM_N163:
@ -443,7 +574,7 @@ const char* DivEngine::getSystemName(DivSystem sys) {
case DIV_SYSTEM_OPL:
return "Yamaha OPL";
case DIV_SYSTEM_OPL2:
return "Adlib Music Synthesizer Card";
return "Yamaha OPL2";
case DIV_SYSTEM_OPL3:
return "Yamaha OPL3";
case DIV_SYSTEM_MULTIPCM:
@ -457,7 +588,7 @@ const char* DivEngine::getSystemName(DivSystem sys) {
case DIV_SYSTEM_SWAN:
return "WonderSwan";
case DIV_SYSTEM_SAA1099:
return "SAM Coupé";
return "Philips SAA1099";
case DIV_SYSTEM_OPZ:
return "Yamaha TX81Z/YS200";
case DIV_SYSTEM_POKEMINI:
@ -488,6 +619,8 @@ const char* DivEngine::getSystemName(DivSystem sys) {
return "Yamaha OPLL with drums";
case DIV_SYSTEM_LYNX:
return "Atari Lynx";
case DIV_SYSTEM_SEGAPCM_COMPAT:
return "SegaPCM (compatible 5-channel mode)";
case DIV_SYSTEM_QSOUND:
return "Capcom QSound";
}
@ -607,6 +740,8 @@ const char* DivEngine::getSystemChips(DivSystem sys) {
return "Yamaha YM2413 with drums";
case DIV_SYSTEM_LYNX:
return "Mikey";
case DIV_SYSTEM_SEGAPCM_COMPAT:
return "SegaPCM (compatible 5-channel mode)";
case DIV_SYSTEM_QSOUND:
return "Capcom DL-1425";
}
@ -954,6 +1089,7 @@ const char* DivEngine::getChannelName(int chan) {
break;
case DIV_SYSTEM_MULTIPCM:
case DIV_SYSTEM_SEGAPCM:
case DIV_SYSTEM_SEGAPCM_COMPAT:
return chanNames[28][dispatchChanOfChan[chan]];
break;
case DIV_SYSTEM_PCSPKR:
@ -1088,6 +1224,7 @@ const char* DivEngine::getChannelShortName(int chan) {
break;
case DIV_SYSTEM_MULTIPCM:
case DIV_SYSTEM_SEGAPCM:
case DIV_SYSTEM_SEGAPCM_COMPAT:
return chanShortNames[28][dispatchChanOfChan[chan]];
break;
case DIV_SYSTEM_PCSPKR:
@ -1219,6 +1356,7 @@ int DivEngine::getChannelType(int chan) {
break;
case DIV_SYSTEM_MULTIPCM:
case DIV_SYSTEM_SEGAPCM:
case DIV_SYSTEM_SEGAPCM_COMPAT:
case DIV_SYSTEM_QSOUND:
return chanTypes[28][dispatchChanOfChan[chan]];
break;
@ -1346,6 +1484,7 @@ DivInstrumentType DivEngine::getPreferInsType(int chan) {
break;
case DIV_SYSTEM_MULTIPCM:
case DIV_SYSTEM_SEGAPCM:
case DIV_SYSTEM_SEGAPCM_COMPAT:
case DIV_SYSTEM_QSOUND:
return chanPrefType[28][dispatchChanOfChan[chan]];
break;
@ -1401,21 +1540,21 @@ DivInstrumentType DivEngine::getPreferInsType(int chan) {
bool DivEngine::isVGMExportable(DivSystem which) {
switch (which) {
case DIV_SYSTEM_GENESIS:
case DIV_SYSTEM_GENESIS_EXT:
case DIV_SYSTEM_SMS:
case DIV_SYSTEM_GB:
case DIV_SYSTEM_PCE:
case DIV_SYSTEM_NES:
case DIV_SYSTEM_ARCADE:
case DIV_SYSTEM_YM2151:
case DIV_SYSTEM_YM2612:
case DIV_SYSTEM_YM2612_EXT:
case DIV_SYSTEM_YM2610:
case DIV_SYSTEM_YM2610_EXT:
case DIV_SYSTEM_AY8910:
case DIV_SYSTEM_AY8930:
case DIV_SYSTEM_SAA1099:
case DIV_SYSTEM_QSOUND:
case DIV_SYSTEM_SEGAPCM:
case DIV_SYSTEM_SEGAPCM_COMPAT:
return true;
default:
return false;

View file

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

View file

@ -220,12 +220,6 @@ void putDispatchChan(void* data, int chanNum, int type) {
ImGui::Text("- outVol: %.2x",ch->outVol);
ImGui::Text("- chVolL: %.2x",ch->chVolL);
ImGui::Text("- chVolR: %.2x",ch->chVolR);
ImGui::Text("* PCM:");
ImGui::Text(" - sample: %d",ch->pcm.sample);
ImGui::Text(" - pos: %d",ch->pcm.pos>>8);
ImGui::Text(" - subPos: %d",ch->pcm.pos&0xff);
ImGui::Text(" - len: %d",ch->pcm.len);
ImGui::Text(" - freq: %.2x",ch->pcm.freq);
ImGui::TextColored(ch->active?colorOn:colorOff,">> Active");
ImGui::TextColored(ch->insChanged?colorOn:colorOff,">> InsChanged");
ImGui::TextColored(ch->freqChanged?colorOn:colorOff,">> FreqChanged");

View file

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

View file

@ -24,6 +24,7 @@
#include "guiConst.h"
#include "intConst.h"
#include <fmt/printf.h>
#include <imgui.h>
#include "plot_nolerp.h"
const char* insTypes[24]={
@ -786,29 +787,103 @@ void FurnaceGUI::drawInsEdit() {
//56.0 controls vert scaling; default 96
drawFMEnv(op.tl,op.ar,op.dr,op.d2r,op.rr,op.sl,ImVec2(ImGui::GetContentRegionAvail().x,56.0*dpiScale));
//P(ImGui::SliderScalar(FM_NAME(FM_AR),ImGuiDataType_U8,&op.ar,&_ZERO,&_THIRTY_ONE));
P(ImGui::SliderScalar(FM_NAME(FM_AR),ImGuiDataType_U8,&op.ar,&_THIRTY_ONE,&_ZERO));
P(ImGui::SliderScalar(FM_NAME(FM_DR),ImGuiDataType_U8,&op.dr,&_THIRTY_ONE,&_ZERO));
P(ImGui::SliderScalar(FM_NAME(FM_SL),ImGuiDataType_U8,&op.sl,&_FIFTEEN,&_ZERO));
P(ImGui::SliderScalar(FM_NAME(FM_D2R),ImGuiDataType_U8,&op.d2r,&_THIRTY_ONE,&_ZERO));
P(ImGui::SliderScalar(FM_NAME(FM_RR),ImGuiDataType_U8,&op.rr,&_FIFTEEN,&_ZERO));
P(ImGui::SliderScalar(FM_NAME(FM_TL),ImGuiDataType_U8,&op.tl,&_ONE_HUNDRED_TWENTY_SEVEN,&_ZERO));
if (ImGui::BeginTable("opParams",2,ImGuiTableFlags_SizingStretchProp)) {
ImGui::TableSetupColumn("c0",ImGuiTableColumnFlags_WidthStretch,0.0); \
ImGui::TableSetupColumn("c1",ImGuiTableColumnFlags_WidthFixed,0.0); \
ImGui::Separator();
ImGui::TableNextRow();
ImGui::TableNextColumn();
ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x);
P(ImGui::SliderScalar("##AR",ImGuiDataType_U8,&op.ar,&_THIRTY_ONE,&_ZERO));
ImGui::TableNextColumn();
ImGui::Text("%s",FM_NAME(FM_AR));
P(ImGui::SliderScalar(FM_NAME(FM_RS),ImGuiDataType_U8,&op.rs,&_ZERO,&_THREE));
P(ImGui::SliderScalar(FM_NAME(FM_MULT),ImGuiDataType_U8,&op.mult,&_ZERO,&_FIFTEEN));
int detune=(op.dt&7)-3;
if (ImGui::SliderInt(FM_NAME(FM_DT),&detune,-3,3)) { PARAMETER
op.dt=detune+3;
ImGui::TableNextRow();
ImGui::TableNextColumn();
ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x);
P(ImGui::SliderScalar("##DR",ImGuiDataType_U8,&op.dr,&_THIRTY_ONE,&_ZERO));
ImGui::TableNextColumn();
ImGui::Text("%s",FM_NAME(FM_DR));
ImGui::TableNextRow();
ImGui::TableNextColumn();
ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x);
P(ImGui::SliderScalar("##SL",ImGuiDataType_U8,&op.sl,&_FIFTEEN,&_ZERO));
ImGui::TableNextColumn();
ImGui::Text("%s",FM_NAME(FM_SL));
ImGui::TableNextRow();
ImGui::TableNextColumn();
ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x);
P(ImGui::SliderScalar("##D2R",ImGuiDataType_U8,&op.d2r,&_THIRTY_ONE,&_ZERO));
ImGui::TableNextColumn();
ImGui::Text("%s",FM_NAME(FM_D2R));
ImGui::TableNextRow();
ImGui::TableNextColumn();
ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x);
P(ImGui::SliderScalar("##RR",ImGuiDataType_U8,&op.rr,&_FIFTEEN,&_ZERO));
ImGui::TableNextColumn();
ImGui::Text("%s",FM_NAME(FM_RR));
ImGui::TableNextRow();
ImGui::TableNextColumn();
ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x);
P(ImGui::SliderScalar("##TL",ImGuiDataType_U8,&op.tl,&_ONE_HUNDRED_TWENTY_SEVEN,&_ZERO));
ImGui::TableNextColumn();
ImGui::Text("%s",FM_NAME(FM_TL));
ImGui::TableNextRow();
ImGui::TableNextColumn();
ImGui::Separator();
ImGui::TableNextColumn();
ImGui::Separator();
ImGui::TableNextRow();
ImGui::TableNextColumn();
ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x);
P(ImGui::SliderScalar("##RS",ImGuiDataType_U8,&op.rs,&_ZERO,&_THREE));
ImGui::TableNextColumn();
ImGui::Text("%s",FM_NAME(FM_RS));
ImGui::TableNextRow();
ImGui::TableNextColumn();
ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x);
P(ImGui::SliderScalar(FM_NAME(FM_MULT),ImGuiDataType_U8,&op.mult,&_ZERO,&_FIFTEEN));
ImGui::TableNextColumn();
ImGui::Text("%s",FM_NAME(FM_MULT));
int detune=(op.dt&7)-3;
ImGui::TableNextRow();
ImGui::TableNextColumn();
ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x);
if (ImGui::SliderInt("##DT",&detune,-3,3)) { PARAMETER
op.dt=detune+3;
}
ImGui::TableNextColumn();
ImGui::Text("%s",FM_NAME(FM_DT));
ImGui::TableNextRow();
ImGui::TableNextColumn();
ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x);
P(ImGui::SliderScalar("##DT2",ImGuiDataType_U8,&op.dt2,&_ZERO,&_THREE));
if (ImGui::IsItemHovered()) {
ImGui::SetTooltip("Only for Arcade system");
}
ImGui::TableNextColumn();
ImGui::Text("%s",FM_NAME(FM_DT2));
ImGui::TableNextRow();
ImGui::TableNextColumn();
ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x);
if (ImGui::SliderScalar("##SSG",ImGuiDataType_U8,&ssgEnv,&_ZERO,&_SEVEN,ssgEnvTypes[ssgEnv])) { PARAMETER
op.ssgEnv=(op.ssgEnv&8)|(ssgEnv&7);
}
ImGui::TableNextColumn();
ImGui::Text("%s",FM_NAME(FM_SSG));
ImGui::EndTable();
}
P(ImGui::SliderScalar(FM_NAME(FM_DT2),ImGuiDataType_U8,&op.dt2,&_ZERO,&_THREE));
if (ImGui::IsItemHovered()) {
ImGui::SetTooltip("Only for Arcade system");
}
if (ImGui::SliderScalar(FM_NAME(FM_SSG),ImGuiDataType_U8,&ssgEnv,&_ZERO,&_SEVEN,ssgEnvTypes[ssgEnv])) { PARAMETER
op.ssgEnv=(op.ssgEnv&8)|(ssgEnv&7);
}
ImGui::PopID();
}
ImGui::EndTable();