Merge branch 'master' of https://github.com/tildearrow/furnace into ym2610b

This commit is contained in:
cam900 2022-02-27 06:41:11 +09:00
commit ee013ad7a9
10 changed files with 300 additions and 174 deletions

View file

@ -25,6 +25,7 @@ furthermore, an `or reserved` indicates this field is always present, but is res
the format versions are: the format versions are:
- 60: Furnace dev60
- 59: Furnace dev59 - 59: Furnace dev59
- 58: Furnace dev58 - 58: Furnace dev58
- 57: Furnace dev57 - 57: Furnace dev57
@ -229,7 +230,11 @@ size | description
1 | fms 1 | fms
1 | ams 1 | ams
1 | operator count (always 4) 1 | operator count (always 4)
3 | reserved 1 | OPLL preset (>=60) or reserved
| - 0: custom
| - 1-15: pre-defined patches
| - 16: drums (compatibility only!)
2 | reserved
--- | **FM operator data** × 4 --- | **FM operator data** × 4
1 | am 1 | am
1 | ar 1 | ar

View file

@ -204,6 +204,7 @@ void DivDispatchContainer::init(DivSystem sys, DivEngine* eng, int chanCount, do
break; break;
case DIV_SYSTEM_OPLL: case DIV_SYSTEM_OPLL:
case DIV_SYSTEM_OPLL_DRUMS: case DIV_SYSTEM_OPLL_DRUMS:
case DIV_SYSTEM_VRC7:
dispatch=new DivPlatformOPLL; dispatch=new DivPlatformOPLL;
break; break;
case DIV_SYSTEM_SAA1099: { case DIV_SYSTEM_SAA1099: {

View file

@ -1153,15 +1153,24 @@ enum DivInsFormats {
DIV_INSFORMAT_BTI DIV_INSFORMAT_BTI
}; };
bool DivEngine::addInstrumentFromFile(const char *path) { bool DivEngine::addInstrumentFromFile(const char* path) {
warnings=""; warnings="";
const char* pathRedux=strrchr(path,DIR_SEPARATOR); const char* pathRedux=strrchr(path,DIR_SEPARATOR);
if (pathRedux==NULL) { if (pathRedux==NULL) {
pathRedux="Instrument"; pathRedux=path;
} else { } else {
pathRedux++; pathRedux++;
} }
String stripPath;
const char* pathReduxEnd=strrchr(pathRedux,'.');
if (pathReduxEnd==NULL) {
stripPath=pathRedux;
} else {
for (const char* i=pathRedux; i!=pathReduxEnd && (*i); i++) {
stripPath+=*i;
}
}
FILE* f=ps_fopen(path,"rb"); FILE* f=ps_fopen(path,"rb");
if (f==NULL) { if (f==NULL) {
@ -1286,7 +1295,7 @@ bool DivEngine::addInstrumentFromFile(const char *path) {
return false; return false;
} }
ins->name=pathRedux; ins->name=stripPath;
if (version>=11) { // 1.0 if (version>=11) { // 1.0
try { try {
@ -1519,7 +1528,7 @@ bool DivEngine::addInstrumentFromFile(const char *path) {
reader.seek(0,SEEK_SET); reader.seek(0,SEEK_SET);
ins->type=DIV_INS_FM; ins->type=DIV_INS_FM;
ins->name=pathRedux; ins->name=stripPath;
ins->fm.alg=reader.readC(); ins->fm.alg=reader.readC();
ins->fm.fb=reader.readC(); ins->fm.fb=reader.readC();
@ -1551,7 +1560,7 @@ bool DivEngine::addInstrumentFromFile(const char *path) {
reader.seek(0,SEEK_SET); reader.seek(0,SEEK_SET);
ins->type=DIV_INS_FM; ins->type=DIV_INS_FM;
ins->name=pathRedux; ins->name=stripPath;
ins->fm.alg=reader.readC(); ins->fm.alg=reader.readC();
ins->fm.fb=reader.readC(); ins->fm.fb=reader.readC();

View file

@ -37,8 +37,8 @@
warnings+=(String("\n")+x); \ warnings+=(String("\n")+x); \
} }
#define DIV_VERSION "dev59" #define DIV_VERSION "dev60"
#define DIV_ENGINE_VERSION 59 #define DIV_ENGINE_VERSION 60
enum DivStatusView { enum DivStatusView {
DIV_STATUS_NOTHING=0, DIV_STATUS_NOTHING=0,

View file

@ -303,10 +303,16 @@ bool DivEngine::loadDMF(unsigned char* file, size_t len) {
for (int j=0; j<ins->fm.ops; j++) { for (int j=0; j<ins->fm.ops; j++) {
ins->fm.op[j].am=reader.readC(); ins->fm.op[j].am=reader.readC();
ins->fm.op[j].ar=reader.readC(); ins->fm.op[j].ar=reader.readC();
if (ds.system[0]==DIV_SYSTEM_SMS_OPLL || ds.system[0]==DIV_SYSTEM_NES_VRC7) {
ins->fm.op[j].ar&=15;
}
if (ds.version<0x13) { if (ds.version<0x13) {
ins->fm.op[j].dam=reader.readC(); ins->fm.op[j].dam=reader.readC();
} }
ins->fm.op[j].dr=reader.readC(); ins->fm.op[j].dr=reader.readC();
if (ds.system[0]==DIV_SYSTEM_SMS_OPLL || ds.system[0]==DIV_SYSTEM_NES_VRC7) {
ins->fm.op[j].dr&=15;
}
if (ds.version<0x13) { if (ds.version<0x13) {
ins->fm.op[j].dvb=reader.readC(); ins->fm.op[j].dvb=reader.readC();
ins->fm.op[j].egt=reader.readC(); ins->fm.op[j].egt=reader.readC();

View file

@ -40,9 +40,9 @@ void DivInstrument::putInsData(SafeWriter* w) {
w->writeC(fm.fms); w->writeC(fm.fms);
w->writeC(fm.ams); w->writeC(fm.ams);
w->writeC(4); // operator count; always 4 w->writeC(4); // operator count; always 4
w->writeC(fm.opllPreset);
w->writeC(0); // reserved w->writeC(0); // reserved
w->writeC(0); w->writeC(0);
w->writeC(0);
for (int j=0; j<4; j++) { for (int j=0; j<4; j++) {
DivInstrumentFM::Operator& op=fm.op[j]; DivInstrumentFM::Operator& op=fm.op[j];
@ -325,7 +325,11 @@ DivDataErrors DivInstrument::readInsData(SafeReader& reader, short version) {
fm.fms=reader.readC(); fm.fms=reader.readC();
fm.ams=reader.readC(); fm.ams=reader.readC();
fm.ops=reader.readC(); fm.ops=reader.readC();
reader.readC(); if (version>=60) {
fm.opllPreset=reader.readC();
} else {
reader.readC();
}
reader.readC(); reader.readC();
reader.readC(); reader.readC();

View file

@ -54,7 +54,7 @@ struct DivInstrumentFM {
unsigned char alg, fb, fms, ams, ops, opllPreset; unsigned char alg, fb, fms, ams, ops, opllPreset;
struct Operator { struct Operator {
unsigned char am, ar, dr, mult, rr, sl, tl, dt2, rs, dt, d2r, ssgEnv; unsigned char am, ar, dr, mult, rr, sl, tl, dt2, rs, dt, d2r, ssgEnv;
unsigned char dam, dvb, egt, ksl, sus, vib, ws, ksr; // YMU759 unsigned char dam, dvb, egt, ksl, sus, vib, ws, ksr; // YMU759/OPL
Operator(): Operator():
am(0), am(0),
ar(0), ar(0),

View file

@ -25,7 +25,7 @@
#define rWrite(a,v) if (!skipRegisterWrites) {pendingWrites[a]=v;} #define rWrite(a,v) if (!skipRegisterWrites) {pendingWrites[a]=v;}
#define immWrite(a,v) if (!skipRegisterWrites) {writes.emplace(a,v); if (dumpWrites) {addWrite(a,v);} } #define immWrite(a,v) if (!skipRegisterWrites) {writes.emplace(a,v); if (dumpWrites) {addWrite(a,v);} }
#define CHIP_FREQBASE 1180067 #define CHIP_FREQBASE 295017
const char* DivPlatformOPLL::getEffectName(unsigned char effect) { const char* DivPlatformOPLL::getEffectName(unsigned char effect) {
switch (effect) { switch (effect) {
@ -77,29 +77,33 @@ const char* DivPlatformOPLL::getEffectName(unsigned char effect) {
void DivPlatformOPLL::acquire_nuked(short* bufL, short* bufR, size_t start, size_t len) { void DivPlatformOPLL::acquire_nuked(short* bufL, short* bufR, size_t start, size_t len) {
static int o[2]; static int o[2];
static int os;
for (size_t h=start; h<start+len; h++) { for (size_t h=start; h<start+len; h++) {
if (!writes.empty() && --delay<0) { if (!writes.empty() && --delay<0) {
// 84 is safe value // 84 is safe value
delay=18;
QueuedWrite& w=writes.front(); QueuedWrite& w=writes.front();
if (w.addrOrVal) { if (w.addrOrVal) {
OPLL_Write(&fm,1,w.val); OPLL_Write(&fm,1,w.val);
printf("write: %x = %.2x\n",w.addr,w.val); //printf("write: %x = %.2x\n",w.addr,w.val);
regPool[w.addr&0xff]=w.val; regPool[w.addr&0xff]=w.val;
writes.pop(); writes.pop();
delay=21;
} else { } else {
//printf("busycounter: %d\n",lastBusy); //printf("busycounter: %d\n",lastBusy);
OPLL_Write(&fm,0,w.addr); OPLL_Write(&fm,0,w.addr);
w.addrOrVal=true; w.addrOrVal=true;
delay=3;
} }
} }
OPLL_Clock(&fm,o); OPLL_Clock(&fm,o);
//if (o[0]<-32768) o[0]=-32768; os=(o[0]+o[1])<<7;
//if (o[1]>32767) o[1]=32767; if (os<-32768) os=-32768;
if (os>32767) os=32767;
//printf("%d\n",o[0]);
bufL[h]=(o[0]+o[1])<<12; bufL[h]=os;
} }
} }
@ -235,8 +239,7 @@ void DivPlatformOPLL::tick() {
}*/ }*/
if (chan[i].keyOn || chan[i].keyOff) { if (chan[i].keyOn || chan[i].keyOff) {
// TODO: FIX immWrite(0x20+i,(chan[i].freqH)|(chan[i].state.alg?0x20:0));
immWrite(0x20+i,0x00);
//chan[i].keyOn=false; //chan[i].keyOn=false;
chan[i].keyOff=false; chan[i].keyOff=false;
} }
@ -257,31 +260,32 @@ void DivPlatformOPLL::tick() {
chan[i].freqH=freqt>>8; chan[i].freqH=freqt>>8;
chan[i].freqL=freqt&0xff; chan[i].freqL=freqt&0xff;
immWrite(0x10+i,freqt&0xff); immWrite(0x10+i,freqt&0xff);
chan[i].freqChanged=false;
} }
if (chan[i].keyOn) { if (chan[i].keyOn || chan[i].freqChanged) {
//immWrite(0x28,0xf0|konOffs[i]); //immWrite(0x28,0xf0|konOffs[i]);
immWrite(0x20+i,(chan[i].freqH)|(chan[i].active<<4)|0x20); immWrite(0x20+i,(chan[i].freqH)|(chan[i].active<<4)|(chan[i].state.alg?0x20:0));
chan[i].keyOn=false; chan[i].keyOn=false;
} }
chan[i].freqChanged=false;
} }
} }
#define OPLL_C_NUM 343
int DivPlatformOPLL::octave(int freq) { int DivPlatformOPLL::octave(int freq) {
if (freq>=32768) { if (freq>=OPLL_C_NUM*64) {
return 128; return 128;
} else if (freq>=16384) { } else if (freq>=OPLL_C_NUM*32) {
return 64; return 64;
} else if (freq>=8192) { } else if (freq>=OPLL_C_NUM*16) {
return 32; return 32;
} else if (freq>=4096) { } else if (freq>=OPLL_C_NUM*8) {
return 16; return 16;
} else if (freq>=2048) { } else if (freq>=OPLL_C_NUM*4) {
return 8; return 8;
} else if (freq>=1024) { } else if (freq>=OPLL_C_NUM*2) {
return 4; return 4;
} else if (freq>=512) { } else if (freq>=OPLL_C_NUM) {
return 2; return 2;
} else { } else {
return 1; return 1;
@ -290,19 +294,19 @@ int DivPlatformOPLL::octave(int freq) {
} }
int DivPlatformOPLL::toFreq(int freq) { int DivPlatformOPLL::toFreq(int freq) {
if (freq>=32768) { if (freq>=OPLL_C_NUM*64) {
return 0xe00|((freq>>7)&0x1ff); return 0xe00|((freq>>7)&0x1ff);
} else if (freq>=16384) { } else if (freq>=OPLL_C_NUM*32) {
return 0xc00|((freq>>6)&0x1ff); return 0xc00|((freq>>6)&0x1ff);
} else if (freq>=8192) { } else if (freq>=OPLL_C_NUM*16) {
return 0xa00|((freq>>5)&0x1ff); return 0xa00|((freq>>5)&0x1ff);
} else if (freq>=4096) { } else if (freq>=OPLL_C_NUM*8) {
return 0x800|((freq>>4)&0x1ff); return 0x800|((freq>>4)&0x1ff);
} else if (freq>=2048) { } else if (freq>=OPLL_C_NUM*4) {
return 0x600|((freq>>3)&0x1ff); return 0x600|((freq>>3)&0x1ff);
} else if (freq>=1024) { } else if (freq>=OPLL_C_NUM*2) {
return 0x400|((freq>>2)&0x1ff); return 0x400|((freq>>2)&0x1ff);
} else if (freq>=512) { } else if (freq>=OPLL_C_NUM) {
return 0x200|((freq>>1)&0x1ff); return 0x200|((freq>>1)&0x1ff);
} else { } else {
return freq&0x1ff; return freq&0x1ff;
@ -341,39 +345,23 @@ int DivPlatformOPLL::dispatch(DivCommand c) {
chan[c.chan].outVol=chan[c.chan].vol; chan[c.chan].outVol=chan[c.chan].vol;
} }
/*
for (int i=0; i<2; i++) {
unsigned short baseAddr=chanOffs[c.chan]|opOffs[i];
DivInstrumentFM::Operator& op=chan[c.chan].state.op[i];
if (isMuted[c.chan]) {
rWrite(baseAddr+ADDR_TL,127);
} else {
if (isOutput[chan[c.chan].state.alg][i]) {
if (!chan[c.chan].active || chan[c.chan].insChanged) {
rWrite(baseAddr+ADDR_TL,127-(((127-op.tl)*(chan[c.chan].outVol&0x7f))/127));
}
} else {
if (chan[c.chan].insChanged) {
rWrite(baseAddr+ADDR_TL,op.tl);
}
}
}
if (chan[c.chan].insChanged) {
rWrite(baseAddr+ADDR_MULT_DT,(op.mult&15)|(dtTable[op.dt&7]<<4));
rWrite(baseAddr+ADDR_RS_AR,(op.ar&31)|(op.rs<<6));
rWrite(baseAddr+ADDR_AM_DR,(op.dr&31)|(op.am<<7));
rWrite(baseAddr+ADDR_DT2_D2R,op.d2r&31);
rWrite(baseAddr+ADDR_SL_RR,(op.rr&15)|(op.sl<<4));
rWrite(baseAddr+ADDR_SSG,op.ssgEnv&15);
}
}
if (chan[c.chan].insChanged) { if (chan[c.chan].insChanged) {
rWrite(chanOffs[c.chan]+ADDR_FB_ALG,(chan[c.chan].state.alg&7)|(chan[c.chan].state.fb<<3)); // update custom preset
rWrite(chanOffs[c.chan]+ADDR_LRAF,(isMuted[c.chan]?0:(chan[c.chan].pan<<6))|(chan[c.chan].state.fms&7)|((chan[c.chan].state.ams&3)<<4)); if (chan[c.chan].state.opllPreset==0) {
DivInstrumentFM::Operator& mod=chan[c.chan].state.op[0];
DivInstrumentFM::Operator& car=chan[c.chan].state.op[1];
rWrite(0x00,(mod.am<<7)|(mod.vib<<6)|((mod.ssgEnv&8)<<2)|(mod.ksr<<4)|(mod.mult));
rWrite(0x01,(car.am<<7)|(car.vib<<6)|((car.ssgEnv&8)<<2)|(car.ksr<<4)|(car.mult));
rWrite(0x02,(car.ksl<<6)|(mod.tl&63));
rWrite(0x03,(mod.ksl<<6)|((chan[c.chan].state.fms&1)<<4)|((chan[c.chan].state.ams&1)<<3)|chan[c.chan].state.fb);
rWrite(0x04,(mod.ar<<4)|(mod.dr));
rWrite(0x05,(car.ar<<4)|(car.dr));
rWrite(0x06,(mod.sl<<4)|(mod.rr));
rWrite(0x07,(car.sl<<4)|(car.rr));
}
rWrite(0x30+c.chan,(15-(chan[c.chan].outVol*(15-chan[c.chan].state.op[1].tl))/15)|(chan[c.chan].state.opllPreset<<4));
} }
*/
// for now
rWrite(0x30+c.chan,0x1e);
chan[c.chan].insChanged=false; chan[c.chan].insChanged=false;
if (c.value!=DIV_NOTE_NULL) { if (c.value!=DIV_NOTE_NULL) {
@ -404,20 +392,7 @@ int DivPlatformOPLL::dispatch(DivCommand c) {
if (!chan[c.chan].std.hasVol) { if (!chan[c.chan].std.hasVol) {
chan[c.chan].outVol=c.value; chan[c.chan].outVol=c.value;
} }
/* rWrite(0x30+c.chan,(15-(chan[c.chan].outVol*(15-chan[c.chan].state.op[1].tl))/15)|(chan[c.chan].state.opllPreset<<4));
for (int i=0; i<4; i++) {
unsigned short baseAddr=chanOffs[c.chan]|opOffs[i];
DivInstrumentFM::Operator& op=chan[c.chan].state.op[i];
if (isMuted[c.chan]) {
rWrite(baseAddr+ADDR_TL,127);
} else {
if (isOutput[chan[c.chan].state.alg][i]) {
rWrite(baseAddr+ADDR_TL,127-(((127-op.tl)*(chan[c.chan].outVol&0x7f))/127));
} else {
rWrite(baseAddr+ADDR_TL,op.tl);
}
}
}*/
break; break;
} }
case DIV_CMD_GET_VOLUME: { case DIV_CMD_GET_VOLUME: {
@ -452,12 +427,12 @@ int DivPlatformOPLL::dispatch(DivCommand c) {
return2=true; return2=true;
} }
} }
if (!chan[c.chan].portaPause) { /*if (!chan[c.chan].portaPause) {
if (octave(chan[c.chan].baseFreq)!=octave(newFreq)) { if (octave(chan[c.chan].baseFreq)!=octave(newFreq)) {
chan[c.chan].portaPause=true; chan[c.chan].portaPause=true;
break; break;
} }
} }*/
chan[c.chan].baseFreq=newFreq; chan[c.chan].baseFreq=newFreq;
chan[c.chan].portaPause=false; chan[c.chan].portaPause=false;
chan[c.chan].freqChanged=true; chan[c.chan].freqChanged=true;
@ -523,7 +498,7 @@ int DivPlatformOPLL::dispatch(DivCommand c) {
return 0; return 0;
break; break;
case DIV_CMD_GET_VOLMAX: case DIV_CMD_GET_VOLMAX:
return 127; return 15;
break; break;
case DIV_CMD_PRE_PORTA: case DIV_CMD_PRE_PORTA:
chan[c.chan].inPorta=c.value; chan[c.chan].inPorta=c.value;
@ -538,38 +513,26 @@ int DivPlatformOPLL::dispatch(DivCommand c) {
} }
void DivPlatformOPLL::forceIns() { void DivPlatformOPLL::forceIns() {
/* for (int i=0; i<9; i++) {
for (int i=0; i<6; i++) { // update custom preset
for (int j=0; j<4; j++) { if (chan[i].state.opllPreset==0) {
unsigned short baseAddr=chanOffs[i]|opOffs[j]; DivInstrumentFM::Operator& mod=chan[i].state.op[0];
DivInstrumentFM::Operator& op=chan[i].state.op[j]; DivInstrumentFM::Operator& car=chan[i].state.op[1];
if (isMuted[i]) { rWrite(0x00,(mod.am<<7)|(mod.vib<<6)|((mod.ssgEnv&8)<<2)|(mod.ksr<<4)|(mod.mult));
rWrite(baseAddr+ADDR_TL,127); rWrite(0x01,(car.am<<7)|(car.vib<<6)|((car.ssgEnv&8)<<2)|(car.ksr<<4)|(car.mult));
} else { rWrite(0x02,(car.ksl<<6)|(mod.tl&63));
if (isOutput[chan[i].state.alg][j]) { rWrite(0x03,(mod.ksl<<6)|((chan[i].state.fms&1)<<4)|((chan[i].state.ams&1)<<3)|chan[i].state.fb);
rWrite(baseAddr+ADDR_TL,127-(((127-op.tl)*(chan[i].outVol&0x7f))/127)); rWrite(0x04,(mod.ar<<4)|(mod.dr));
} else { rWrite(0x05,(car.ar<<4)|(car.dr));
rWrite(baseAddr+ADDR_TL,op.tl); rWrite(0x06,(mod.sl<<4)|(mod.rr));
} rWrite(0x07,(car.sl<<4)|(car.rr));
}
rWrite(baseAddr+ADDR_MULT_DT,(op.mult&15)|(dtTable[op.dt&7]<<4));
rWrite(baseAddr+ADDR_RS_AR,(op.ar&31)|(op.rs<<6));
rWrite(baseAddr+ADDR_AM_DR,(op.dr&31)|(op.am<<7));
rWrite(baseAddr+ADDR_DT2_D2R,op.d2r&31);
rWrite(baseAddr+ADDR_SL_RR,(op.rr&15)|(op.sl<<4));
rWrite(baseAddr+ADDR_SSG,op.ssgEnv&15);
} }
rWrite(chanOffs[i]+ADDR_FB_ALG,(chan[i].state.alg&7)|(chan[i].state.fb<<3)); rWrite(0x30+i,(15-(chan[i].outVol*(15-chan[i].state.op[1].tl))/15)|(chan[i].state.opllPreset<<4));
rWrite(chanOffs[i]+ADDR_LRAF,(isMuted[i]?0:(chan[i].pan<<6))|(chan[i].state.fms&7)|((chan[i].state.ams&3)<<4));
if (chan[i].active) { if (chan[i].active) {
chan[i].keyOn=true; chan[i].keyOn=true;
chan[i].freqChanged=true; chan[i].freqChanged=true;
} }
} }
if (dacMode) {
rWrite(0x2b,0x80);
}
immWrite(0x22,lfoValue);*/
} }
void DivPlatformOPLL::toggleRegisterDump(bool enable) { void DivPlatformOPLL::toggleRegisterDump(bool enable) {
@ -585,7 +548,7 @@ unsigned char* DivPlatformOPLL::getRegisterPool() {
} }
int DivPlatformOPLL::getRegisterPoolSize() { int DivPlatformOPLL::getRegisterPoolSize() {
return 256; return 64;
} }
void DivPlatformOPLL::reset() { void DivPlatformOPLL::reset() {
@ -652,9 +615,9 @@ void DivPlatformOPLL::setFlags(unsigned int flags) {
} else if (flags==2) { } else if (flags==2) {
chipClock=8000000.0; chipClock=8000000.0;
} else if (flags==1) { } else if (flags==1) {
chipClock=COLOR_PAL*4.0/5.0; chipClock=COLOR_PAL*1.0/5.0;
} else { } else {
chipClock=COLOR_NTSC; chipClock=COLOR_NTSC/4.0;
} }
rate=chipClock; rate=chipClock;
} }

View file

@ -4497,6 +4497,8 @@ bool FurnaceGUI::loop() {
sysAddOption(DIV_SYSTEM_YM2610B_EXT); sysAddOption(DIV_SYSTEM_YM2610B_EXT);
sysAddOption(DIV_SYSTEM_AY8910); sysAddOption(DIV_SYSTEM_AY8910);
sysAddOption(DIV_SYSTEM_AMIGA); sysAddOption(DIV_SYSTEM_AMIGA);
sysAddOption(DIV_SYSTEM_OPLL);
sysAddOption(DIV_SYSTEM_VRC7);
sysAddOption(DIV_SYSTEM_TIA); sysAddOption(DIV_SYSTEM_TIA);
sysAddOption(DIV_SYSTEM_SAA1099); sysAddOption(DIV_SYSTEM_SAA1099);
sysAddOption(DIV_SYSTEM_AY8930); sysAddOption(DIV_SYSTEM_AY8930);
@ -4609,11 +4611,11 @@ bool FurnaceGUI::loop() {
case DIV_SYSTEM_AY8910: case DIV_SYSTEM_AY8910:
case DIV_SYSTEM_AY8930: { case DIV_SYSTEM_AY8930: {
ImGui::Text("Clock rate:"); ImGui::Text("Clock rate:");
if (ImGui::RadioButton("1.79MHz (ZX Spectrum/MSX NTSC)",(flags&15)==0)) { if (ImGui::RadioButton("1.79MHz (ZX Spectrum NTSC/MSX)",(flags&15)==0)) {
e->setSysFlags(i,(flags&(~15))|0,restart); e->setSysFlags(i,(flags&(~15))|0,restart);
updateWindowTitle(); updateWindowTitle();
} }
if (ImGui::RadioButton("1.77MHz (ZX Spectrum/MSX PAL)",(flags&15)==1)) { if (ImGui::RadioButton("1.77MHz (ZX Spectrum)",(flags&15)==1)) {
e->setSysFlags(i,(flags&(~15))|1,restart); e->setSysFlags(i,(flags&(~15))|1,restart);
updateWindowTitle(); updateWindowTitle();
} }
@ -4769,6 +4771,8 @@ bool FurnaceGUI::loop() {
sysChangeOption(i,DIV_SYSTEM_YM2610B_EXT); sysChangeOption(i,DIV_SYSTEM_YM2610B_EXT);
sysChangeOption(i,DIV_SYSTEM_AY8910); sysChangeOption(i,DIV_SYSTEM_AY8910);
sysChangeOption(i,DIV_SYSTEM_AMIGA); sysChangeOption(i,DIV_SYSTEM_AMIGA);
sysChangeOption(i,DIV_SYSTEM_OPLL);
sysChangeOption(i,DIV_SYSTEM_VRC7);
sysChangeOption(i,DIV_SYSTEM_TIA); sysChangeOption(i,DIV_SYSTEM_TIA);
sysChangeOption(i,DIV_SYSTEM_SAA1099); sysChangeOption(i,DIV_SYSTEM_SAA1099);
sysChangeOption(i,DIV_SYSTEM_AY8930); sysChangeOption(i,DIV_SYSTEM_AY8930);

View file

@ -58,13 +58,13 @@ const char* ssgEnvTypes[8]={
"Down Down Down", "Down.", "Down Up Down Up", "Down UP", "Up Up Up", "Up.", "Up Down Up Down", "Up DOWN" "Down Down Down", "Down.", "Down Up Down Up", "Down UP", "Up Up Up", "Up.", "Up Down Up Down", "Up DOWN"
}; };
const char* fmParamNames[3][16]={ const char* fmParamNames[3][27]={
{"Algorithm", "Feedback", "LFO > Freq", "LFO > Amp", "Attack", "Decay", "Decay 2", "Release", "Sustain", "Level", "EnvScale", "Multiplier", "Detune", "Detune 2", "SSG-EG", "AM"}, {"Algorithm", "Feedback", "LFO > Freq", "LFO > Amp", "Attack", "Decay", "Decay 2", "Release", "Sustain", "Level", "EnvScale", "Multiplier", "Detune", "Detune 2", "SSG-EG", "AM", "AM Depth", "Vibrato Depth", "EnvAlternate", "EnvAlternate", "LevelScale/key", "Sustain", "Vibrato", "Waveform", "EnvScale/key", "OP2 HalfSine", "OP1 HalfSine"},
{"ALG", "FB", "FMS/PMS", "AMS", "AR", "DR", "SR", "RR", "SL", "TL", "KS", "MULT", "DT", "DT2", "SSG-EG", "AM"}, {"ALG", "FB", "FMS/PMS", "AMS", "AR", "DR", "SR", "RR", "SL", "TL", "KS", "MULT", "DT", "DT2", "SSG-EG", "AM", "AMD", "FMD", "EGT", "EGT", "KSL", "SUS", "VIB", "WS", "KSR", "DC", "DM"},
{"ALG", "FB", "FMS/PMS", "AMS", "AR", "DR", "D2R", "RR", "SL", "TL", "RS", "MULT", "DT", "DT2", "SSG-EG", "AM"} {"ALG", "FB", "FMS/PMS", "AMS", "AR", "DR", "D2R", "RR", "SL", "TL", "RS", "MULT", "DT", "DT2", "SSG-EG", "AM", "DAM", "DVB", "EGT", "EGS", "KSL", "SUS", "VIB", "WS", "KSR", "DC", "DM"}
}; };
const char* opllInsNames[18]={ const char* opllInsNames[17]={
"User", "User",
"Violin", "Violin",
"Guitar", "Guitar",
@ -100,7 +100,18 @@ enum FMParams {
FM_DT=12, FM_DT=12,
FM_DT2=13, FM_DT2=13,
FM_SSG=14, FM_SSG=14,
FM_AM=15 FM_AM=15,
FM_DAM=16,
FM_DVB=17,
FM_EGT=18,
FM_EGS=19,
FM_KSL=20,
FM_SUS=21,
FM_VIB=22,
FM_WS=23,
FM_KSR=24,
FM_DC=25,
FM_DM=26
}; };
#define FM_NAME(x) fmParamNames[settings.fmNames][x] #define FM_NAME(x) fmParamNames[settings.fmNames][x]
@ -429,6 +440,41 @@ void FurnaceGUI::drawAlgorithm(unsigned char alg, FurnaceGUIFMAlgs algType, cons
} }
} }
break; break;
case FM_ALGS_2OP_OPL:
switch (alg) {
case 0: { // 1 > 2
ImVec2 pos1=ImLerp(rect.Min,rect.Max,ImVec2(0.33,0.5));
ImVec2 pos2=ImLerp(rect.Min,rect.Max,ImVec2(0.67,0.5));
dl->AddCircleFilled(pos1,4.0f*dpiScale+1.0f,color);
dl->AddCircle(pos1,6.0f*dpiScale+1.0f,color);
dl->AddLine(pos1,pos2,colorL);
dl->AddCircleFilled(pos2,4.0f*dpiScale+1.0f,color);
pos1.x-=ImGui::CalcTextSize("2").x+circleRadius+3.0*dpiScale;
pos2.x+=circleRadius+3.0*dpiScale;
pos1.y-=ImGui::CalcTextSize("1").y*0.5;
pos2.y-=ImGui::CalcTextSize("2").y*0.5;
dl->AddText(pos1,color,"1");
dl->AddText(pos2,color,"2");
break;
}
case 1: { // 1 + 2
ImVec2 pos1=ImLerp(rect.Min,rect.Max,ImVec2(0.33,0.5));
ImVec2 pos2=ImLerp(rect.Min,rect.Max,ImVec2(0.67,0.5));
dl->AddCircleFilled(pos1,4.0f*dpiScale+1.0f,color);
dl->AddCircle(pos1,6.0f*dpiScale+1.0f,color);
dl->AddCircleFilled(pos2,4.0f*dpiScale+1.0f,color);
pos1.x-=ImGui::CalcTextSize("2").x+circleRadius+3.0*dpiScale;
pos2.x+=circleRadius+3.0*dpiScale;
pos1.y-=ImGui::CalcTextSize("1").y*0.5;
pos2.y-=ImGui::CalcTextSize("2").y*0.5;
dl->AddText(pos1,color,"1");
dl->AddText(pos2,color,"2");
break;
}
}
break;
default: default:
break; break;
} }
@ -735,7 +781,7 @@ void FurnaceGUI::drawInsEdit() {
} }
if (ImGui::BeginTabBar("insEditTab")) { if (ImGui::BeginTabBar("insEditTab")) {
if (ins->type==DIV_INS_FM) { if (ins->type==DIV_INS_FM || ins->type==DIV_INS_OPL || ins->type==DIV_INS_OPLL || ins->type==DIV_INS_OPZ) {
char label[32]; char label[32];
float asFloat[256]; float asFloat[256];
int asInt[256]; int asInt[256];
@ -746,19 +792,72 @@ void FurnaceGUI::drawInsEdit() {
ImGui::TableSetupColumn("c1",ImGuiTableColumnFlags_WidthStretch,0.0); ImGui::TableSetupColumn("c1",ImGuiTableColumnFlags_WidthStretch,0.0);
ImGui::TableSetupColumn("c2",ImGuiTableColumnFlags_WidthStretch,0.0); ImGui::TableSetupColumn("c2",ImGuiTableColumnFlags_WidthStretch,0.0);
ImGui::TableNextRow(); ImGui::TableNextRow();
ImGui::TableNextColumn(); switch (ins->type) {
P(ImGui::SliderScalar(FM_NAME(FM_FB),ImGuiDataType_U8,&ins->fm.fb,&_ZERO,&_SEVEN)); case DIV_INS_FM:
P(ImGui::SliderScalar(FM_NAME(FM_FMS),ImGuiDataType_U8,&ins->fm.fms,&_ZERO,&_SEVEN)); case DIV_INS_OPZ:
ImGui::TableNextColumn(); ImGui::TableNextColumn();
P(ImGui::SliderScalar(FM_NAME(FM_ALG),ImGuiDataType_U8,&ins->fm.alg,&_ZERO,&_SEVEN)); P(ImGui::SliderScalar(FM_NAME(FM_FB),ImGuiDataType_U8,&ins->fm.fb,&_ZERO,&_SEVEN));
P(ImGui::SliderScalar(FM_NAME(FM_AMS),ImGuiDataType_U8,&ins->fm.ams,&_ZERO,&_THREE)); P(ImGui::SliderScalar(FM_NAME(FM_FMS),ImGuiDataType_U8,&ins->fm.fms,&_ZERO,&_SEVEN));
ImGui::TableNextColumn(); ImGui::TableNextColumn();
drawAlgorithm(ins->fm.alg,FM_ALGS_4OP,ImVec2(ImGui::GetContentRegionAvail().x,48.0*dpiScale)); P(ImGui::SliderScalar(FM_NAME(FM_ALG),ImGuiDataType_U8,&ins->fm.alg,&_ZERO,&_SEVEN));
P(ImGui::SliderScalar(FM_NAME(FM_AMS),ImGuiDataType_U8,&ins->fm.ams,&_ZERO,&_THREE));
ImGui::TableNextColumn();
drawAlgorithm(ins->fm.alg,FM_ALGS_4OP,ImVec2(ImGui::GetContentRegionAvail().x,48.0*dpiScale));
break;
case DIV_INS_OPL:
case DIV_INS_OPLL: {
bool dc=ins->fm.fms;
bool dm=ins->fm.ams;
bool sus=ins->fm.alg;
ImGui::TableNextColumn();
ImGui::BeginDisabled(ins->fm.opllPreset!=0);
P(ImGui::SliderScalar(FM_NAME(FM_FB),ImGuiDataType_U8,&ins->fm.fb,&_ZERO,&_SEVEN));
if (ImGui::Checkbox(FM_NAME(FM_DC),&dc)) { PARAMETER
ins->fm.fms=dc;
}
ImGui::EndDisabled();
ImGui::TableNextColumn();
if (ImGui::Checkbox(FM_NAME(FM_SUS),&sus)) { PARAMETER
ins->fm.alg=sus;
}
ImGui::BeginDisabled(ins->fm.opllPreset!=0);
if (ImGui::Checkbox(FM_NAME(FM_DM),&dm)) { PARAMETER
ins->fm.ams=dm;
}
ImGui::EndDisabled();
ImGui::TableNextColumn();
drawAlgorithm(0,FM_ALGS_2OP_OPL,ImVec2(ImGui::GetContentRegionAvail().x,24.0*dpiScale));
ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x);
if (ImGui::BeginCombo("##LLPreset",opllInsNames[ins->fm.opllPreset])) {
for (int i=0; i<17; i++) {
if (ImGui::Selectable(opllInsNames[i])) {
ins->fm.opllPreset=i;
}
}
ImGui::EndCombo();
}
break;
}
default:
break;
}
ImGui::EndTable(); ImGui::EndTable();
} }
if (ImGui::BeginTable("FMOperators",2,ImGuiTableFlags_SizingStretchSame)) {
for (int i=0; i<4; i++) { if (ins->type==DIV_INS_OPLL && ins->fm.opllPreset==16) {
DivInstrumentFM::Operator& op=ins->fm.op[opOrder[i]]; ImGui::Text("the Drums patch is only there for compatibility.\nit is highly encouraged you use the OPLL (drums) system instead!");
}
bool willDisplayOps=true;
int opCount=4;
if (ins->type==DIV_INS_OPLL && ins->fm.opllPreset!=0) willDisplayOps=false;
if (ins->type==DIV_INS_OPL || ins->type==DIV_INS_OPLL) opCount=2;
if (!willDisplayOps && ins->type==DIV_INS_OPLL) {
P(ImGui::SliderScalar("Volume##TL",ImGuiDataType_U8,&ins->fm.op[1].tl,&_FIFTEEN,&_ZERO));
}
if (willDisplayOps) if (ImGui::BeginTable("FMOperators",2,ImGuiTableFlags_SizingStretchSame)) {
for (int i=0; i<opCount; i++) {
DivInstrumentFM::Operator& op=ins->fm.op[(opCount==4)?opOrder[i]:i];
if ((i+1)&1) ImGui::TableNextRow(); if ((i+1)&1) ImGui::TableNextRow();
ImGui::TableNextColumn(); ImGui::TableNextColumn();
ImGui::Separator(); ImGui::Separator();
@ -775,13 +874,27 @@ void FurnaceGUI::drawInsEdit() {
ImGui::SameLine(); ImGui::SameLine();
int maxTl=127;
if (ins->type==DIV_INS_OPLL) {
if (i==1) {
maxTl=15;
} else {
maxTl=63;
}
}
bool ssgOn=op.ssgEnv&8; bool ssgOn=op.ssgEnv&8;
bool ksrOn=op.ksr;
bool vibOn=op.vib;
unsigned char ssgEnv=op.ssgEnv&7; unsigned char ssgEnv=op.ssgEnv&7;
if (ImGui::Checkbox("SSG On",&ssgOn)) { PARAMETER int maxArDr=(ins->type==DIV_INS_FM || ins->type==DIV_INS_OPZ)?31:15;
if (ImGui::Checkbox((ins->type==DIV_INS_OPL || ins->type==DIV_INS_OPLL)?FM_NAME(FM_EGS):"SSG On",&ssgOn)) { PARAMETER
op.ssgEnv=(op.ssgEnv&7)|(ssgOn<<3); op.ssgEnv=(op.ssgEnv&7)|(ssgOn<<3);
} }
if (ImGui::IsItemHovered()) { if (ins->type==DIV_INS_FM) {
ImGui::SetTooltip("Only for Genesis and Neo Geo systems"); if (ImGui::IsItemHovered()) {
ImGui::SetTooltip("Only for Genesis and Neo Geo systems");
}
} }
//52.0 controls vert scaling; default 96 //52.0 controls vert scaling; default 96
@ -794,14 +907,14 @@ void FurnaceGUI::drawInsEdit() {
ImGui::TableNextRow(); ImGui::TableNextRow();
ImGui::TableNextColumn(); ImGui::TableNextColumn();
ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x); ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x);
P(ImGui::SliderScalar("##AR",ImGuiDataType_U8,&op.ar,&_THIRTY_ONE,&_ZERO)); P(ImGui::SliderScalar("##AR",ImGuiDataType_U8,&op.ar,&maxArDr,&_ZERO));
ImGui::TableNextColumn(); ImGui::TableNextColumn();
ImGui::Text("%s",FM_NAME(FM_AR)); ImGui::Text("%s",FM_NAME(FM_AR));
ImGui::TableNextRow(); ImGui::TableNextRow();
ImGui::TableNextColumn(); ImGui::TableNextColumn();
ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x); ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x);
P(ImGui::SliderScalar("##DR",ImGuiDataType_U8,&op.dr,&_THIRTY_ONE,&_ZERO)); P(ImGui::SliderScalar("##DR",ImGuiDataType_U8,&op.dr,&maxArDr,&_ZERO));
ImGui::TableNextColumn(); ImGui::TableNextColumn();
ImGui::Text("%s",FM_NAME(FM_DR)); ImGui::Text("%s",FM_NAME(FM_DR));
@ -812,12 +925,14 @@ void FurnaceGUI::drawInsEdit() {
ImGui::TableNextColumn(); ImGui::TableNextColumn();
ImGui::Text("%s",FM_NAME(FM_SL)); ImGui::Text("%s",FM_NAME(FM_SL));
ImGui::TableNextRow(); if (ins->type==DIV_INS_FM || ins->type==DIV_INS_OPZ) {
ImGui::TableNextColumn(); ImGui::TableNextRow();
ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x); ImGui::TableNextColumn();
P(ImGui::SliderScalar("##D2R",ImGuiDataType_U8,&op.d2r,&_THIRTY_ONE,&_ZERO)); ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x);
ImGui::TableNextColumn(); P(ImGui::SliderScalar("##D2R",ImGuiDataType_U8,&op.d2r,&_THIRTY_ONE,&_ZERO));
ImGui::Text("%s",FM_NAME(FM_D2R)); ImGui::TableNextColumn();
ImGui::Text("%s",FM_NAME(FM_D2R));
}
ImGui::TableNextRow(); ImGui::TableNextRow();
ImGui::TableNextColumn(); ImGui::TableNextColumn();
@ -829,7 +944,7 @@ void FurnaceGUI::drawInsEdit() {
ImGui::TableNextRow(); ImGui::TableNextRow();
ImGui::TableNextColumn(); ImGui::TableNextColumn();
ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x); ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x);
P(ImGui::SliderScalar("##TL",ImGuiDataType_U8,&op.tl,&_ONE_HUNDRED_TWENTY_SEVEN,&_ZERO)); P(ImGui::SliderScalar("##TL",ImGuiDataType_U8,&op.tl,&maxTl,&_ZERO));
ImGui::TableNextColumn(); ImGui::TableNextColumn();
ImGui::Text("%s",FM_NAME(FM_TL)); ImGui::Text("%s",FM_NAME(FM_TL));
@ -842,9 +957,15 @@ void FurnaceGUI::drawInsEdit() {
ImGui::TableNextRow(); ImGui::TableNextRow();
ImGui::TableNextColumn(); ImGui::TableNextColumn();
ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x); ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x);
P(ImGui::SliderScalar("##RS",ImGuiDataType_U8,&op.rs,&_ZERO,&_THREE)); if (ins->type==DIV_INS_FM || ins->type==DIV_INS_OPZ) {
ImGui::TableNextColumn(); P(ImGui::SliderScalar("##RS",ImGuiDataType_U8,&op.rs,&_ZERO,&_THREE));
ImGui::Text("%s",FM_NAME(FM_RS)); ImGui::TableNextColumn();
ImGui::Text("%s",FM_NAME(FM_RS));
} else {
P(ImGui::SliderScalar("##KSL",ImGuiDataType_U8,&op.ksl,&_ZERO,&_THREE));
ImGui::TableNextColumn();
ImGui::Text("%s",FM_NAME(FM_KSL));
}
ImGui::TableNextRow(); ImGui::TableNextRow();
ImGui::TableNextColumn(); ImGui::TableNextColumn();
@ -853,37 +974,50 @@ void FurnaceGUI::drawInsEdit() {
ImGui::TableNextColumn(); ImGui::TableNextColumn();
ImGui::Text("%s",FM_NAME(FM_MULT)); ImGui::Text("%s",FM_NAME(FM_MULT));
int detune=(op.dt&7)-3; if (ins->type==DIV_INS_FM || ins->type==DIV_INS_OPZ) {
ImGui::TableNextRow(); int detune=(op.dt&7)-3;
ImGui::TableNextColumn(); ImGui::TableNextRow();
ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x); ImGui::TableNextColumn();
if (ImGui::SliderInt("##DT",&detune,-3,3)) { PARAMETER ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x);
op.dt=detune+3; if (ImGui::SliderInt("##DT",&detune,-3,3)) { PARAMETER
} op.dt=detune+3;
ImGui::TableNextColumn(); }
ImGui::Text("%s",FM_NAME(FM_DT)); ImGui::TableNextColumn();
ImGui::Text("%s",FM_NAME(FM_DT));
ImGui::TableNextRow(); ImGui::TableNextRow();
ImGui::TableNextColumn(); ImGui::TableNextColumn();
ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x); ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x);
P(ImGui::SliderScalar("##DT2",ImGuiDataType_U8,&op.dt2,&_ZERO,&_THREE)); P(ImGui::SliderScalar("##DT2",ImGuiDataType_U8,&op.dt2,&_ZERO,&_THREE));
if (ImGui::IsItemHovered()) { if (ImGui::IsItemHovered()) {
ImGui::SetTooltip("Only for Arcade system"); ImGui::SetTooltip("Only for Arcade system");
} }
ImGui::TableNextColumn(); ImGui::TableNextColumn();
ImGui::Text("%s",FM_NAME(FM_DT2)); ImGui::Text("%s",FM_NAME(FM_DT2));
ImGui::TableNextRow(); ImGui::TableNextRow();
ImGui::TableNextColumn(); ImGui::TableNextColumn();
ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x); ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x);
if (ImGui::SliderScalar("##SSG",ImGuiDataType_U8,&ssgEnv,&_ZERO,&_SEVEN,ssgEnvTypes[ssgEnv])) { PARAMETER if (ImGui::SliderScalar("##SSG",ImGuiDataType_U8,&ssgEnv,&_ZERO,&_SEVEN,ssgEnvTypes[ssgEnv])) { PARAMETER
op.ssgEnv=(op.ssgEnv&8)|(ssgEnv&7); op.ssgEnv=(op.ssgEnv&8)|(ssgEnv&7);
}
ImGui::TableNextColumn();
ImGui::Text("%s",FM_NAME(FM_SSG));
} }
ImGui::TableNextColumn();
ImGui::Text("%s",FM_NAME(FM_SSG));
ImGui::EndTable(); ImGui::EndTable();
} }
if (ins->type==DIV_INS_OPLL || ins->type==DIV_INS_OPL) {
if (ImGui::Checkbox(FM_NAME(FM_VIB),&vibOn)) { PARAMETER
op.vib=vibOn;
}
ImGui::SameLine();
if (ImGui::Checkbox(FM_NAME(FM_KSR),&ksrOn)) { PARAMETER
op.ksr=ksrOn;
}
}
ImGui::PopID(); ImGui::PopID();
} }
ImGui::EndTable(); ImGui::EndTable();