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

@ -607,8 +607,9 @@ void DivEngine::renderSamples() {
for (int i=0; i<song.sampleLen; i++) {
DivSample* s=song.sample[i];
int length = s->rendLength;
if(length > 65536-16)
if (length > 65536-16) {
length = 65536-16;
}
if ((memPos&0xff0000)!=((memPos+length)&0xff0000)) {
memPos=(memPos+0xffff)&0xff0000;
}
@ -617,14 +618,12 @@ void DivEngine::renderSamples() {
break;
}
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);
}
logW("out of QSound PCM memory for sample %d!\n",i);
} 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);
}
}
@ -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);

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;
}
@ -159,43 +156,6 @@ void DivPlatformArcade::acquire_nuked(short* bufL, short* bufR, size_t start, si
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));
}
}
}
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,15 +495,6 @@ 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);
}
} else {
chan[c.chan].chVolL=((c.value>>4)==1);
chan[c.chan].chVolR=((c.value&15)==1);
if (isMuted[c.chan]) {
@ -685,7 +502,6 @@ int DivPlatformArcade::dispatch(DivCommand c) {
} 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;
}
case DIV_CMD_PITCH: {
@ -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

@ -258,9 +258,10 @@ const char* DivPlatformQSound::getEffectName(unsigned char effect) {
return "11xx: Set channel echo level (00 to FF)";
break;
default:
if((effect & 0xf0) == 0x30)
if ((effect & 0xf0) == 0x30) {
return "3xxx: Set echo delay buffer length (000 to AA5)";
}
}
return NULL;
}
void DivPlatformQSound::acquire(short* bufL, short* bufR, size_t start, size_t len) {
@ -277,12 +278,10 @@ 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;
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 << 5);
//logW("ch %d vol=%04x (hadVol)!\n",i,chan[i].outVol << 5);
if (chan[i].active) {
rWrite(q1_reg_map[Q1V_VOL][i], chan[i].outVol << 4);
}
}
uint16_t qsound_bank = 0;
@ -301,15 +300,13 @@ void DivPlatformQSound::tick() {
qsound_addr = s->rendOffQsound & 0xffff;
int length = s->length;
if(length > 65536 - 16)
if (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_loop = 15;
}
else
{
} else {
qsound_end = s->rendOffQsound + length;
qsound_loop = length - s->loopStart;
}
@ -333,7 +330,6 @@ 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);
@ -343,16 +339,14 @@ void DivPlatformQSound::tick() {
//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_VOL][i], chan[i].vol << 4);
}
}
if (chan[i].keyOff) {
// Disable volume
rWrite(q1_reg_map[Q1V_VOL][i], 0);
rWrite(q1_reg_map[Q1V_FREQ][i], 0);
// Disable volume
}
else if (chan[i].active) {
} 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);
}
@ -413,10 +407,8 @@ int DivPlatformQSound::dispatch(DivCommand c) {
if (!chan[c.chan].std.hasVol) {
// 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);
}
}
}
@ -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)
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)
if(echoDelay < 0) {
echoDelay = 0;
if(echoDelay > 2725)
}
if(echoDelay > 2725) {
echoDelay = 2725;
}
//rate=chipClock/CHIP_DIVIDER;
}
@ -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

@ -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,7 +241,7 @@ 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) {
if ((effect&0xf0)==0x30) {
dispatchCmd(DivCommand(DIV_CMD_QSOUND_ECHO_DELAY,ch,((effect & 0x0f) << 8) | effectVal));
} else {
return false;
@ -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

@ -976,9 +976,10 @@ 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)
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",
@ -2098,7 +2099,7 @@ void FurnaceGUI::drawRegView() {
int size=0;
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,14 +2118,15 @@ void FurnaceGUI::drawRegView() {
for (int j=0; j<16; j++) {
ImGui::TableNextColumn();
if (i*16+j>=size) continue;
if(depth == 8)
if (depth == 8) {
ImGui::Text("%.2x",regPool[i*16+j]);
else if(depth == 16)
} else if (depth == 16) {
ImGui::Text("%.4x",regPoolW[i*16+j]);
else
} else {
ImGui::Text("??");
}
}
}
ImGui::EndTable();
}
ImGui::PopFont();
@ -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::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));
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();
P(ImGui::SliderScalar(FM_NAME(FM_RS),ImGuiDataType_U8,&op.rs,&_ZERO,&_THREE));
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;
if (ImGui::SliderInt(FM_NAME(FM_DT),&detune,-3,3)) { PARAMETER
ImGui::TableNextRow();
ImGui::TableNextColumn();
ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x);
if (ImGui::SliderInt("##DT",&detune,-3,3)) { PARAMETER
op.dt=detune+3;
}
P(ImGui::SliderScalar(FM_NAME(FM_DT2),ImGuiDataType_U8,&op.dt2,&_ZERO,&_THREE));
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");
}
if (ImGui::SliderScalar(FM_NAME(FM_SSG),ImGuiDataType_U8,&ssgEnv,&_ZERO,&_SEVEN,ssgEnvTypes[ssgEnv])) { PARAMETER
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();
}
ImGui::PopID();
}
ImGui::EndTable();