From 1e2d5694b969a6b9f527917c0ebda774e4204232 Mon Sep 17 00:00:00 2001 From: cam900 Date: Fri, 25 Feb 2022 01:02:35 +0900 Subject: [PATCH 01/50] Prepare to support YM2610B and with Extended channel 3 mode Allow 8 bit volume for YM2610 ADPCM-B Remove sample mode macro in YM2610, it's always enabled and seperated channels. TODO: ADPCM-B is still not implemented, FM Channel 2 is silenced in extended channel 3 configuration --- CMakeLists.txt | 2 + papers/format.md | 1 + src/engine/dispatchContainer.cpp | 8 + src/engine/engine.cpp | 26 +- src/engine/fileOps.cpp | 8 +- src/engine/platform/ym2610Interface.cpp | 4 +- src/engine/platform/ym2610b.cpp | 965 ++++++++++++++++++++++++ src/engine/platform/ym2610b.h | 102 +++ src/engine/platform/ym2610bext.cpp | 337 +++++++++ src/engine/platform/ym2610bext.h | 51 ++ src/engine/platform/ym2610ext.cpp | 4 - src/engine/platform/ym2610shared.h | 16 +- src/engine/playback.cpp | 36 +- src/engine/song.h | 1 + src/engine/sysDef.cpp | 35 +- src/engine/vgmOps.cpp | 10 + src/gui/debug.cpp | 2 + src/gui/gui.cpp | 6 + 18 files changed, 1592 insertions(+), 22 deletions(-) create mode 100644 src/engine/platform/ym2610b.cpp create mode 100644 src/engine/platform/ym2610b.h create mode 100644 src/engine/platform/ym2610bext.cpp create mode 100644 src/engine/platform/ym2610bext.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 3b7a669aa..0704bfaef 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -311,6 +311,8 @@ src/engine/platform/c64.cpp src/engine/platform/arcade.cpp src/engine/platform/ym2610.cpp src/engine/platform/ym2610ext.cpp +src/engine/platform/ym2610b.cpp +src/engine/platform/ym2610bext.cpp src/engine/platform/ay.cpp src/engine/platform/ay8930.cpp src/engine/platform/tia.cpp diff --git a/papers/format.md b/papers/format.md index a61bb6266..899fc601a 100644 --- a/papers/format.md +++ b/papers/format.md @@ -156,6 +156,7 @@ size | description | - 0xa6: OPL3 4-op + drums (YMF262) - 14 channels | - 0xa7: OPLL drums (YM2413) - 11 channels | - 0xa8: Atari Lynx - 4 channels + | - 0xde: YM2610B extended - 19 channels | - 0xe0: QSound - 19 channels | - (compound!) means that the system is composed of two or more chips, | and has to be flattened. diff --git a/src/engine/dispatchContainer.cpp b/src/engine/dispatchContainer.cpp index 801a3d84f..5ffffa865 100644 --- a/src/engine/dispatchContainer.cpp +++ b/src/engine/dispatchContainer.cpp @@ -29,6 +29,8 @@ #include "platform/arcade.h" #include "platform/ym2610.h" #include "platform/ym2610ext.h" +#include "platform/ym2610b.h" +#include "platform/ym2610bext.h" #include "platform/ay.h" #include "platform/ay8930.h" #include "platform/tia.h" @@ -181,6 +183,12 @@ void DivDispatchContainer::init(DivSystem sys, DivEngine* eng, int chanCount, do case DIV_SYSTEM_YM2610_FULL_EXT: dispatch=new DivPlatformYM2610Ext; break; + case DIV_SYSTEM_YM2610B: + dispatch=new DivPlatformYM2610B; + break; + case DIV_SYSTEM_YM2610B_EXT: + dispatch=new DivPlatformYM2610BExt; + break; case DIV_SYSTEM_AMIGA: dispatch=new DivPlatformAmiga; break; diff --git a/src/engine/engine.cpp b/src/engine/engine.cpp index 9dee6ecfe..95e139180 100644 --- a/src/engine/engine.cpp +++ b/src/engine/engine.cpp @@ -506,6 +506,28 @@ void DivEngine::renderSamples() { memPos+=length+16; } qsoundMemLen=memPos+256; + + // step 5: allocate ADPCM-B samples + if (adpcmBMem==NULL) adpcmBMem=new unsigned char[16777216]; + + memPos=0; + for (int i=0; i=16777216) { + logW("out of ADPCM-B memory for sample %d!\n",i); + break; + } + if (memPos+s->adpcmBRendLength>=16777216) { + memcpy(adpcmBMem+memPos,s->adpcmBRendData,16777216-memPos); + logW("out of ADPCM-B memory for sample %d!\n",i); + } else { + memcpy(adpcmBMem+memPos,s->adpcmBRendData,s->adpcmBRendLength); + } + s->rendOff=memPos; + memPos+=s->adpcmBRendLength; + } + adpcmBMemLen=memPos+256; + */ } @@ -905,7 +927,9 @@ int DivEngine::getEffectiveSampleRate(int rate) { return 1789773/(1789773/rate); 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: + case DIV_SYSTEM_QSOUND: + return (24038*MIN(65535,(rate*4096/24038)))/4096; + case DIV_SYSTEM_YM2610: case DIV_SYSTEM_YM2610_EXT: case DIV_SYSTEM_YM2610_FULL: case DIV_SYSTEM_YM2610_FULL_EXT: case DIV_SYSTEM_YM2610B: case DIV_SYSTEM_YM2610B_EXT: return 18518; default: break; diff --git a/src/engine/fileOps.cpp b/src/engine/fileOps.cpp index dd3ff37e2..5177916fd 100644 --- a/src/engine/fileOps.cpp +++ b/src/engine/fileOps.cpp @@ -148,7 +148,9 @@ bool DivEngine::loadDMF(unsigned char* file, size_t len) { } // Neo Geo detune - if (ds.system[0]==DIV_SYSTEM_YM2610 || ds.system[0]==DIV_SYSTEM_YM2610_EXT) { + if (ds.system[0]==DIV_SYSTEM_YM2610 || ds.system[0]==DIV_SYSTEM_YM2610_EXT + || ds.system[0]==DIV_SYSTEM_YM2610_FULL || ds.system[0]==DIV_SYSTEM_YM2610_FULL_EXT + || ds.system[0]==DIV_SYSTEM_YM2610B || ds.system[0]==DIV_SYSTEM_YM2610B_EXT) { ds.tuning=443.23; } @@ -264,7 +266,9 @@ bool DivEngine::loadDMF(unsigned char* file, size_t len) { if (ds.system[0]==DIV_SYSTEM_C64_8580 || ds.system[0]==DIV_SYSTEM_C64_6581) { ins->type=DIV_INS_C64; } - if (ds.system[0]==DIV_SYSTEM_YM2610 || ds.system[0]==DIV_SYSTEM_YM2610_EXT) { + if (ds.system[0]==DIV_SYSTEM_YM2610 || ds.system[0]==DIV_SYSTEM_YM2610_EXT + || ds.system[0]==DIV_SYSTEM_YM2610_FULL || ds.system[0]==DIV_SYSTEM_YM2610_FULL_EXT + || ds.system[0]==DIV_SYSTEM_YM2610B || ds.system[0]==DIV_SYSTEM_YM2610B_EXT) { if (!ins->mode) { ins->type=DIV_INS_AY; } diff --git a/src/engine/platform/ym2610Interface.cpp b/src/engine/platform/ym2610Interface.cpp index 8d53e48ce..29a186ee1 100644 --- a/src/engine/platform/ym2610Interface.cpp +++ b/src/engine/platform/ym2610Interface.cpp @@ -23,8 +23,8 @@ uint8_t DivYM2610Interface::ymfm_external_read(ymfm::access_class type, uint32_t address) { //printf("wants to read from %x\n",address); - if (type!=ymfm::ACCESS_ADPCM_A) return 0; - return parent->adpcmMem[address&0xffffff]; + if (type!=ymfm::ACCESS_ADPCM_A) return /*s->dataB[address&0xffffff];*/0; + return parent->adpcmMem[address&0xffffff];/*s->dataA[address&0xffffff]*/ /*if (12*sampleBank+(address>>16)>=parent->song.sampleLen) return 0; return parent->song.sample[12*sampleBank+(address>>16)]->adpcmRendData[(address&0xffff)];*/ } diff --git a/src/engine/platform/ym2610b.cpp b/src/engine/platform/ym2610b.cpp new file mode 100644 index 000000000..bb3559e94 --- /dev/null +++ b/src/engine/platform/ym2610b.cpp @@ -0,0 +1,965 @@ +/** + * 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 "ym2610b.h" +#include "../engine.h" +#include +#include + +#include "ym2610shared.h" + +#include "fmshared_OPN.h" + +static unsigned char konOffs[6]={ + 0, 1, 2, 4, 5, 6 +}; + +#define CHIP_DIVIDER 32 + +const char* DivPlatformYM2610B::getEffectName(unsigned char effect) { + switch (effect) { + case 0x10: + return "10xy: Setup LFO (x: enable; y: speed)"; + break; + case 0x11: + return "11xx: Set feedback (0 to 7)"; + break; + case 0x12: + return "12xx: Set level of operator 1 (0 highest, 7F lowest)"; + break; + case 0x13: + return "13xx: Set level of operator 2 (0 highest, 7F lowest)"; + break; + case 0x14: + return "14xx: Set level of operator 3 (0 highest, 7F lowest)"; + break; + case 0x15: + return "15xx: Set level of operator 4 (0 highest, 7F lowest)"; + break; + case 0x16: + return "16xy: Set operator multiplier (x: operator from 1 to 4; y: multiplier)"; + break; + case 0x18: + return "18xx: Toggle extended channel 3 mode"; + break; + case 0x19: + return "19xx: Set attack of all operators (0 to 1F)"; + break; + case 0x1a: + return "1Axx: Set attack of operator 1 (0 to 1F)"; + break; + case 0x1b: + return "1Bxx: Set attack of operator 2 (0 to 1F)"; + break; + case 0x1c: + return "1Cxx: Set attack of operator 3 (0 to 1F)"; + break; + case 0x1d: + return "1Dxx: Set attack of operator 4 (0 to 1F)"; + break; + case 0x20: + return "20xx: Set SSG channel mode (bit 0: square; bit 1: noise; bit 2: envelope)"; + break; + case 0x21: + return "21xx: Set SSG noise frequency (0 to 1F)"; + break; + case 0x22: + return "22xy: Set SSG envelope mode (x: shape, y: enable for this channel)"; + break; + case 0x23: + return "23xx: Set SSG envelope period low byte"; + break; + case 0x24: + return "24xx: Set SSG envelope period high byte"; + break; + case 0x25: + return "25xx: SSG envelope slide up"; + break; + case 0x26: + return "26xx: SSG envelope slide down"; + break; + case 0x29: + return "29xy: Set SSG auto-envelope (x: numerator; y: denominator)"; + break; + } + return NULL; +} + +void DivPlatformYM2610B::acquire(short* bufL, short* bufR, size_t start, size_t len) { + static int os[2]; + + for (size_t h=start; hwrite(0x0+((w.addr>>8)<<1),w.addr); + fm->write(0x1+((w.addr>>8)<<1),w.val); + regPool[w.addr&0x1ff]=w.val; + writes.pop(); + delay=4; + } + } + + fm->generate(&fmout); + + os[0]=fmout.data[0]+(fmout.data[2]>>1); + if (os[0]<-32768) os[0]=-32768; + if (os[0]>32767) os[0]=32767; + + os[1]=fmout.data[1]+(fmout.data[2]>>1); + if (os[1]<-32768) os[1]=-32768; + if (os[1]>32767) os[1]=32767; + + bufL[h]=os[0]; + bufR[h]=os[1]; + } +} + +void DivPlatformYM2610B::tick() { + // PSG + for (int i=6; i<9; i++) { + chan[i].std.next(); + if (chan[i].std.hadVol) { + chan[i].outVol=MIN(15,chan[i].std.vol)-(15-(chan[i].vol&15)); + if (chan[i].outVol<0) chan[i].outVol=0; + if (isMuted[i]) { + rWrite(0x02+i,0); + } else { + rWrite(0x02+i,(chan[i].outVol&15)|((chan[i].psgMode&4)<<2)); + } + } + if (chan[i].std.hadArp) { + if (!chan[i].inPorta) { + if (chan[i].std.arpMode) { + chan[i].baseFreq=NOTE_PERIODIC(chan[i].std.arp); + } else { + chan[i].baseFreq=NOTE_PERIODIC(chan[i].note+chan[i].std.arp); + } + } + chan[i].freqChanged=true; + } else { + if (chan[i].std.arpMode && chan[i].std.finishedArp) { + chan[i].baseFreq=NOTE_PERIODIC(chan[i].note); + chan[i].freqChanged=true; + } + } + if (chan[i].std.hadDuty) { + ayNoiseFreq=31-chan[i].std.duty; + rWrite(0x06,ayNoiseFreq); + } + if (chan[i].std.hadWave) { + chan[i].psgMode=(chan[i].std.wave+1)&7; + if (isMuted[i]) { + rWrite(0x02+i,0); + } else { + rWrite(0x02+i,(chan[i].outVol&15)|((chan[i].psgMode&4)<<2)); + } + } + if (chan[i].std.hadEx2) { + ayEnvMode=chan[i].std.ex2; + rWrite(0x0d,ayEnvMode); + } + if (chan[i].std.hadEx3) { + chan[i].autoEnvNum=chan[i].std.ex3; + chan[i].freqChanged=true; + if (!chan[i].std.willAlg) chan[i].autoEnvDen=1; + } + if (chan[i].std.hadAlg) { + chan[i].autoEnvDen=chan[i].std.alg; + chan[i].freqChanged=true; + if (!chan[i].std.willEx3) chan[i].autoEnvNum=1; + } + if (chan[i].freqChanged || chan[i].keyOn || chan[i].keyOff) { + chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,true); + if (chan[i].freq>4095) chan[i].freq=4095; + if (chan[i].keyOn) { + } + if (chan[i].keyOff) { + rWrite(0x02+i,0); + } + rWrite((i-6)<<1,chan[i].freq&0xff); + rWrite(1+((i-6)<<1),chan[i].freq>>8); + if (chan[i].keyOn) chan[i].keyOn=false; + if (chan[i].keyOff) chan[i].keyOff=false; + if (chan[i].freqChanged && chan[i].autoEnvNum>0 && chan[i].autoEnvDen>0) { + ayEnvPeriod=(chan[i].freq*chan[i].autoEnvDen/chan[i].autoEnvNum)>>4; + immWrite(0x0b,ayEnvPeriod); + immWrite(0x0c,ayEnvPeriod>>8); + } + chan[i].freqChanged=false; + } + } + + rWrite(0x07, + ~((chan[6].psgMode&1)| + ((chan[7].psgMode&1)<<1)| + ((chan[8].psgMode&1)<<2)| + ((chan[6].psgMode&2)<<2)| + ((chan[7].psgMode&2)<<3)| + ((chan[8].psgMode&2)<<4))); + + if (ayEnvSlide!=0) { + ayEnvSlideLow+=ayEnvSlide; + while (ayEnvSlideLow>7) { + ayEnvSlideLow-=8; + if (ayEnvPeriod<0xffff) { + ayEnvPeriod++; + immWrite(0x0b,ayEnvPeriod); + immWrite(0x0c,ayEnvPeriod>>8); + } + } + while (ayEnvSlideLow<-7) { + ayEnvSlideLow+=8; + if (ayEnvPeriod>0) { + ayEnvPeriod--; + immWrite(0x0b,ayEnvPeriod); + immWrite(0x0c,ayEnvPeriod>>8); + } + } + } + + // FM + for (int i=0; i<6; i++) { + if (i==1 && extMode) continue; + chan[i].std.next(); + + if (chan[i].std.hadVol) { + chan[i].outVol=(chan[i].vol*MIN(127,chan[i].std.vol))/127; + for (int j=0; j<4; j++) { + unsigned short baseAddr=chanOffs_b[i]|opOffs[j]; + DivInstrumentFM::Operator& op=chan[i].state.op[j]; + if (isOutput[chan[i].state.alg][j]) { + rWrite(baseAddr+ADDR_TL,127-(((127-op.tl)*(chan[i].outVol&0x7f))/127)); + } else { + rWrite(baseAddr+ADDR_TL,op.tl); + } + } + } + + if (chan[i].std.hadArp) { + if (!chan[i].inPorta) { + if (chan[i].std.arpMode) { + chan[i].baseFreq=NOTE_FREQUENCY(chan[i].std.arp); + } else { + chan[i].baseFreq=NOTE_FREQUENCY(chan[i].note+(signed char)chan[i].std.arp); + } + } + chan[i].freqChanged=true; + } else { + if (chan[i].std.arpMode && chan[i].std.finishedArp) { + chan[i].baseFreq=NOTE_FREQUENCY(chan[i].note); + chan[i].freqChanged=true; + } + } + + if (chan[i].std.hadAlg) { + chan[i].state.alg=chan[i].std.alg; + rWrite(chanOffs_b[i]+ADDR_FB_ALG,(chan[i].state.alg&7)|(chan[i].state.fb<<3)); + if (!parent->song.algMacroBehavior) for (int j=0; j<4; j++) { + unsigned short baseAddr=chanOffs_b[i]|opOffs[j]; + DivInstrumentFM::Operator& op=chan[i].state.op[j]; + if (isMuted[i]) { + rWrite(baseAddr+ADDR_TL,127); + } else { + if (isOutput[chan[i].state.alg][j]) { + rWrite(baseAddr+ADDR_TL,127-(((127-op.tl)*(chan[i].outVol&0x7f))/127)); + } else { + rWrite(baseAddr+ADDR_TL,op.tl); + } + } + } + } + if (chan[i].std.hadFb) { + chan[i].state.fb=chan[i].std.fb; + rWrite(chanOffs_b[i]+ADDR_FB_ALG,(chan[i].state.alg&7)|(chan[i].state.fb<<3)); + } + if (chan[i].std.hadFms) { + chan[i].state.fms=chan[i].std.fms; + rWrite(chanOffs_b[i]+ADDR_LRAF,(isMuted[i]?0:(chan[i].pan<<6))|(chan[i].state.fms&7)|((chan[i].state.ams&3)<<4)); + } + if (chan[i].std.hadAms) { + chan[i].state.ams=chan[i].std.ams; + rWrite(chanOffs_b[i]+ADDR_LRAF,(isMuted[i]?0:(chan[i].pan<<6))|(chan[i].state.fms&7)|((chan[i].state.ams&3)<<4)); + } + for (int j=0; j<4; j++) { + unsigned short baseAddr=chanOffs_b[i]|opOffs[j]; + DivInstrumentFM::Operator& op=chan[i].state.op[j]; + DivMacroInt::IntOp& m=chan[i].std.op[j]; + if (m.hadAm) { + op.am=m.am; + rWrite(baseAddr+ADDR_AM_DR,(op.dr&31)|(op.am<<7)); + } + if (m.hadAr) { + op.ar=m.ar; + rWrite(baseAddr+ADDR_RS_AR,(op.ar&31)|(op.rs<<6)); + } + if (m.hadDr) { + op.dr=m.dr; + rWrite(baseAddr+ADDR_AM_DR,(op.dr&31)|(op.am<<7)); + } + if (m.hadMult) { + op.mult=m.mult; + rWrite(baseAddr+ADDR_MULT_DT,(op.mult&15)|(dtTable[op.dt&7]<<4)); + } + if (m.hadRr) { + op.rr=m.rr; + rWrite(baseAddr+ADDR_SL_RR,(op.rr&15)|(op.sl<<4)); + } + if (m.hadSl) { + op.sl=m.sl; + rWrite(baseAddr+ADDR_SL_RR,(op.rr&15)|(op.sl<<4)); + } + if (m.hadTl) { + op.tl=127-m.tl; + if (isOutput[chan[i].state.alg][j]) { + rWrite(baseAddr+ADDR_TL,127-(((127-op.tl)*(chan[i].outVol&0x7f))/127)); + } else { + rWrite(baseAddr+ADDR_TL,op.tl); + } + } + if (m.hadRs) { + op.rs=m.rs; + rWrite(baseAddr+ADDR_RS_AR,(op.ar&31)|(op.rs<<6)); + } + if (m.hadDt) { + op.dt=m.dt; + rWrite(baseAddr+ADDR_MULT_DT,(op.mult&15)|(dtTable[op.dt&7]<<4)); + } + if (m.hadD2r) { + op.d2r=m.d2r; + rWrite(baseAddr+ADDR_DT2_D2R,op.d2r&31); + } + if (m.hadSsg) { + op.ssgEnv=m.ssg; + rWrite(baseAddr+ADDR_SSG,op.ssgEnv&15); + } + } + + if (chan[i].keyOn || chan[i].keyOff) { + immWrite(0x28,0x00|konOffs[i]); + chan[i].keyOff=false; + } + } + + for (int i=0; i<512; i++) { + if (pendingWrites[i]!=oldWrites[i]) { + immWrite(i,pendingWrites[i]&0xff); + oldWrites[i]=pendingWrites[i]; + } + } + + for (int i=0; i<6; i++) { + if (i==1 && extMode) continue; + if (chan[i].freqChanged) { + chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,false,octave(chan[i].baseFreq)); + if (chan[i].freq>262143) chan[i].freq=262143; + int freqt=toFreq(chan[i].freq); + immWrite(chanOffs_b[i]+ADDR_FREQH,freqt>>8); + immWrite(chanOffs_b[i]+ADDR_FREQ,freqt&0xff); + chan[i].freqChanged=false; + } + if (chan[i].keyOn) { + immWrite(0x28,0xf0|konOffs[i]); + chan[i].keyOn=false; + } + } +} + +int DivPlatformYM2610B::octave(int freq) { + if (freq>=622.0f*128) { + return 128; + } else if (freq>=622.0f*64) { + return 64; + } else if (freq>=622.0f*32) { + return 32; + } else if (freq>=622.0f*16) { + return 16; + } else if (freq>=622.0f*8) { + return 8; + } else if (freq>=622.0f*4) { + return 4; + } else if (freq>=622.0f*2) { + return 2; + } else { + return 1; + } + return 1; +} + +int DivPlatformYM2610B::toFreq(int freq) { + if (freq>=622.0f*128) { + return 0x3800|((freq>>7)&0x7ff); + } else if (freq>=622.0f*64) { + return 0x3000|((freq>>6)&0x7ff); + } else if (freq>=622.0f*32) { + return 0x2800|((freq>>5)&0x7ff); + } else if (freq>=622.0f*16) { + return 0x2000|((freq>>4)&0x7ff); + } else if (freq>=622.0f*8) { + return 0x1800|((freq>>3)&0x7ff); + } else if (freq>=622.0f*4) { + return 0x1000|((freq>>2)&0x7ff); + } else if (freq>=622.0f*2) { + return 0x800|((freq>>1)&0x7ff); + } else { + return freq&0x7ff; + } +} + +int DivPlatformYM2610B::dispatch(DivCommand c) { + switch (c.cmd) { + case DIV_CMD_NOTE_ON: { + if (c.chan>8) { // ADPCM + if (skipRegisterWrites) break; + if ((12*sampleBank+c.value%12)>=parent->song.sampleLen) { + immWrite(0x100,0x80|(1<<(c.chan-9))); + immWrite(0x110+c.chan-9,0); + immWrite(0x118+c.chan-9,0); + immWrite(0x120+c.chan-9,0); + immWrite(0x128+c.chan-9,0); + break; + } + DivSample* s=parent->song.sample[12*sampleBank+c.value%12]; + immWrite(0x110+c.chan-9,(s->offA>>8)&0xff); + immWrite(0x118+c.chan-9,s->offA>>16); + int end=s->offA+s->lengthA-1; + immWrite(0x120+c.chan-9,(end>>8)&0xff); + immWrite(0x128+c.chan-9,end>>16); + immWrite(0x108+(c.chan-9),isMuted[c.chan]?0:((chan[c.chan].pan<<6)|chan[c.chan].vol)); + immWrite(0x100,0x00|(1<<(c.chan-9))); + break; + } + DivInstrument* ins=parent->getIns(chan[c.chan].ins); + chan[c.chan].std.init(ins); + if (c.chan<6) { + if (!chan[c.chan].std.willVol) { + chan[c.chan].outVol=chan[c.chan].vol; + } + } + + if (c.chan>5) { // PSG + if (c.value!=DIV_NOTE_NULL) { + chan[c.chan].baseFreq=NOTE_PERIODIC(c.value); + chan[c.chan].freqChanged=true; + chan[c.chan].note=c.value; + } + chan[c.chan].active=true; + chan[c.chan].keyOn=true; + if (isMuted[c.chan]) { + rWrite(0x02+c.chan,0); + } else { + rWrite(0x02+c.chan,(chan[c.chan].vol&15)|((chan[c.chan].psgMode&4)<<2)); + } + break; + } + + if (chan[c.chan].insChanged) { + chan[c.chan].state=ins->fm; + } + + for (int i=0; i<6; i++) { + unsigned short baseAddr=chanOffs_b[c.chan]|opOffs[i]; + DivInstrumentFM::Operator& op=chan[c.chan].state.op[i]; + 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) { + rWrite(chanOffs_b[c.chan]+ADDR_FB_ALG,(chan[c.chan].state.alg&7)|(chan[c.chan].state.fb<<3)); + rWrite(chanOffs_b[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)); + } + chan[c.chan].insChanged=false; + + if (c.value!=DIV_NOTE_NULL) { + chan[c.chan].baseFreq=NOTE_FREQUENCY(c.value); + chan[c.chan].freqChanged=true; + chan[c.chan].note=c.value; + } + chan[c.chan].keyOn=true; + chan[c.chan].active=true; + break; + } + case DIV_CMD_NOTE_OFF: + if (c.chan>8) { + immWrite(0x100,0x80|(1<<(c.chan-9))); + break; + } + chan[c.chan].keyOff=true; + chan[c.chan].keyOn=false; + chan[c.chan].active=false; + chan[c.chan].std.init(NULL); + break; + case DIV_CMD_NOTE_OFF_ENV: + if (c.chan>8) { + immWrite(0x100,0x80|(1<<(c.chan-9))); + break; + } + if (c.chan>5) { + chan[c.chan].std.release(); + break; + } + 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; + } + if (c.chan>8) { // ADPCM + immWrite(0x108+(c.chan-9),isMuted[c.chan]?0:((chan[c.chan].pan<<6)|chan[c.chan].vol)); + break; + } + if (c.chan>5) { // PSG + if (isMuted[c.chan]) { + rWrite(0x02+c.chan,0); + } else { + if (chan[c.chan].active) rWrite(0x02+c.chan,(chan[c.chan].vol&15)|((chan[c.chan].psgMode&4)<<2)); + } + break; + } + for (int i=0; i<4; i++) { + unsigned short baseAddr=chanOffs_b[c.chan]|opOffs[i]; + DivInstrumentFM::Operator& op=chan[c.chan].state.op[i]; + 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; + } + 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: { + switch (c.value) { + case 0x01: + chan[c.chan].pan=1; + break; + case 0x10: + chan[c.chan].pan=2; + break; + default: + chan[c.chan].pan=3; + break; + } + if (c.chan>8) { + immWrite(0x108+(c.chan-9),isMuted[c.chan]?0:((chan[c.chan].pan<<6)|chan[c.chan].vol)); + break; + } + if (c.chan>5) break; + rWrite(chanOffs_b[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)); + break; + } + case DIV_CMD_PITCH: { + chan[c.chan].pitch=c.value; + chan[c.chan].freqChanged=true; + break; + } + case DIV_CMD_NOTE_PORTA: { + if (c.chan>5) { // PSG + int destFreq=NOTE_PERIODIC(c.value2); + bool return2=false; + if (destFreq>chan[c.chan].baseFreq) { + chan[c.chan].baseFreq+=c.value; + if (chan[c.chan].baseFreq>=destFreq) { + chan[c.chan].baseFreq=destFreq; + return2=true; + } + } else { + chan[c.chan].baseFreq-=c.value; + if (chan[c.chan].baseFreq<=destFreq) { + chan[c.chan].baseFreq=destFreq; + return2=true; + } + } + chan[c.chan].freqChanged=true; + if (return2) { + chan[c.chan].inPorta=false; + return 2; + } + break; + } + + int destFreq=NOTE_FREQUENCY(c.value2); + int newFreq; + bool return2=false; + if (destFreq>chan[c.chan].baseFreq) { + newFreq=chan[c.chan].baseFreq+c.value*octave(chan[c.chan].baseFreq); + if (newFreq>=destFreq) { + newFreq=destFreq; + return2=true; + } + } else { + newFreq=chan[c.chan].baseFreq-c.value*octave(chan[c.chan].baseFreq); + if (newFreq<=destFreq) { + newFreq=destFreq; + return2=true; + } + } + if (!chan[c.chan].portaPause) { + if (octave(chan[c.chan].baseFreq)!=octave(newFreq)) { + chan[c.chan].portaPause=true; + break; + } + } + chan[c.chan].baseFreq=newFreq; + chan[c.chan].portaPause=false; + chan[c.chan].freqChanged=true; + if (return2) return 2; + break; + } + case DIV_CMD_SAMPLE_BANK: + sampleBank=c.value; + if (sampleBank>(parent->song.sample.size()/12)) { + sampleBank=parent->song.sample.size()/12; + } + iface.sampleBank=sampleBank; + break; + case DIV_CMD_LEGATO: { + if (c.chan>5) { // PSG + chan[c.chan].baseFreq=NOTE_PERIODIC(c.value); + } else { + chan[c.chan].baseFreq=NOTE_FREQUENCY(c.value); + } + chan[c.chan].freqChanged=true; + break; + } + case DIV_CMD_FM_LFO: { + rWrite(0x22,(c.value&7)|((c.value>>4)<<3)); + break; + } + case DIV_CMD_FM_FB: { + if (c.chan>5) break; + chan[c.chan].state.fb=c.value&7; + rWrite(chanOffs_b[c.chan]+ADDR_FB_ALG,(chan[c.chan].state.alg&7)|(chan[c.chan].state.fb<<3)); + break; + } + case DIV_CMD_FM_MULT: { + if (c.chan>5) break; + unsigned short baseAddr=chanOffs_b[c.chan]|opOffs[orderedOps[c.value]]; + DivInstrumentFM::Operator& op=chan[c.chan].state.op[orderedOps[c.value]]; + op.mult=c.value2&15; + rWrite(baseAddr+ADDR_MULT_DT,(op.mult&15)|(dtTable[op.dt&7]<<4)); + break; + } + case DIV_CMD_FM_TL: { + if (c.chan>5) break; + unsigned short baseAddr=chanOffs_b[c.chan]|opOffs[orderedOps[c.value]]; + DivInstrumentFM::Operator& op=chan[c.chan].state.op[orderedOps[c.value]]; + op.tl=c.value2; + if (isOutput[chan[c.chan].state.alg][c.value]) { + rWrite(baseAddr+ADDR_TL,127-(((127-op.tl)*(chan[c.chan].outVol&0x7f))/127)); + } else { + rWrite(baseAddr+ADDR_TL,op.tl); + } + break; + } + case DIV_CMD_FM_AR: { + if (c.chan>5) break; + if (c.value<0) { + for (int i=0; i<4; i++) { + DivInstrumentFM::Operator& op=chan[c.chan].state.op[i]; + op.ar=c.value2&31; + unsigned short baseAddr=chanOffs_b[c.chan]|opOffs[i]; + rWrite(baseAddr+ADDR_RS_AR,(op.ar&31)|(op.rs<<6)); + } + } else { + DivInstrumentFM::Operator& op=chan[c.chan].state.op[orderedOps[c.value]]; + op.ar=c.value2&31; + unsigned short baseAddr=chanOffs_b[c.chan]|opOffs[orderedOps[c.value]]; + rWrite(baseAddr+ADDR_RS_AR,(op.ar&31)|(op.rs<<6)); + } + break; + } + case DIV_CMD_STD_NOISE_MODE: + if (c.chan<6 || c.chan>8) break; + chan[c.chan].psgMode=(c.value+1)&7; + if (isMuted[c.chan]) { + rWrite(0x02+c.chan,0); + } else if (chan[c.chan].active) { + rWrite(0x02+c.chan,(chan[c.chan].outVol&15)|((chan[c.chan].psgMode&4)<<2)); + } + break; + case DIV_CMD_STD_NOISE_FREQ: + if (c.chan<6 || c.chan>8) break; + ayNoiseFreq=31-c.value; + rWrite(0x06,ayNoiseFreq); + break; + case DIV_CMD_AY_ENVELOPE_SET: + if (c.chan<6 || c.chan>8) break; + ayEnvMode=c.value>>4; + rWrite(0x0d,ayEnvMode); + if (c.value&15) { + chan[c.chan].psgMode|=4; + } else { + chan[c.chan].psgMode&=~4; + } + if (isMuted[c.chan]) { + rWrite(0x02+c.chan,0); + } else { + rWrite(0x02+c.chan,(chan[c.chan].vol&15)|((chan[c.chan].psgMode&4)<<2)); + } + break; + case DIV_CMD_AY_ENVELOPE_LOW: + if (c.chan<6 || c.chan>8) break; + ayEnvPeriod&=0xff00; + ayEnvPeriod|=c.value; + immWrite(0x0b,ayEnvPeriod); + immWrite(0x0c,ayEnvPeriod>>8); + break; + case DIV_CMD_AY_ENVELOPE_HIGH: + if (c.chan<6 || c.chan>8) break; + ayEnvPeriod&=0xff; + ayEnvPeriod|=c.value<<8; + immWrite(0x0b,ayEnvPeriod); + immWrite(0x0c,ayEnvPeriod>>8); + break; + case DIV_CMD_AY_ENVELOPE_SLIDE: + if (c.chan<6 || c.chan>8) break; + ayEnvSlide=c.value; + break; + case DIV_CMD_AY_AUTO_ENVELOPE: + if (c.chan<6 || c.chan>8) break; + chan[c.chan].autoEnvNum=c.value>>4; + chan[c.chan].autoEnvDen=c.value&15; + chan[c.chan].freqChanged=true; + break; + case DIV_ALWAYS_SET_VOLUME: + return 0; + break; + case DIV_CMD_GET_VOLMAX: + if (c.chan>14) return 255; + if (c.chan>8) return 31; + if (c.chan>5) return 15; + return 127; + break; + case DIV_CMD_PRE_PORTA: + if (c.chan>5) { + if (chan[c.chan].active && c.value2) { + if (parent->song.resetMacroOnPorta) chan[c.chan].std.init(parent->getIns(chan[c.chan].ins)); + } + } + chan[c.chan].inPorta=c.value; + break; + case DIV_CMD_PRE_NOTE: + break; + default: + //printf("WARNING: unimplemented command %d\n",c.cmd); + break; + } + return 1; +} + +void DivPlatformYM2610B::muteChannel(int ch, bool mute) { + isMuted[ch]=mute; + if (ch>8) { // ADPCM + immWrite(0x108+(ch-9),isMuted[ch]?0:((chan[ch].pan<<6)|chan[ch].vol)); + return; + } + if (ch>5) { // PSG + if (isMuted[ch]) { + rWrite(0x02+ch,0); + } else { + rWrite(0x02+ch,(chan[ch].outVol&15)|((chan[ch].psgMode&4)<<2)); + } + return; + } + // FM + rWrite(chanOffs_b[ch]+ADDR_LRAF,(isMuted[ch]?0:(chan[ch].pan<<6))|(chan[ch].state.fms&7)|((chan[ch].state.ams&3)<<4)); +} + +void DivPlatformYM2610B::forceIns() { + for (int i=0; i<6; i++) { + for (int j=0; j<4; j++) { + unsigned short baseAddr=chanOffs_b[i]|opOffs[j]; + DivInstrumentFM::Operator& op=chan[i].state.op[j]; + if (isOutput[chan[i].state.alg][j]) { + rWrite(baseAddr+ADDR_TL,127-(((127-op.tl)*(chan[i].outVol&0x7f))/127)); + } else { + rWrite(baseAddr+ADDR_TL,op.tl); + } + 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_b[i]+ADDR_FB_ALG,(chan[i].state.alg&7)|(chan[i].state.fb<<3)); + rWrite(chanOffs_b[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) { + chan[i].keyOn=true; + chan[i].freqChanged=true; + } + } + for (int i=6; i<16; i++) { + chan[i].insChanged=true; + } + immWrite(0x0b,ayEnvPeriod); + immWrite(0x0c,ayEnvPeriod>>8); + immWrite(0x0d,ayEnvMode); +} + +void* DivPlatformYM2610B::getChanState(int ch) { + return &chan[ch]; +} + +unsigned char* DivPlatformYM2610B::getRegisterPool() { + return regPool; +} + +int DivPlatformYM2610B::getRegisterPoolSize() { + return 512; +} + +void DivPlatformYM2610B::poke(unsigned int addr, unsigned short val) { + immWrite(addr,val); +} + +void DivPlatformYM2610B::poke(std::vector& wlist) { + for (DivRegWrite& i: wlist) immWrite(i.addr,i.val); +} + +void DivPlatformYM2610B::reset() { + while (!writes.empty()) writes.pop(); + memset(regPool,0,512); + if (dumpWrites) { + addWrite(0xffffffff,0); + } + fm->reset(); + for (int i=0; i<16; i++) { + chan[i]=DivPlatformYM2610B::Channel(); + } + for (int i=0; i<6; i++) { + chan[i].vol=0x7f; + chan[i].outVol=0x7f; + } + for (int i=6; i<9; i++) { + chan[i].vol=0x0f; + } + for (int i=9; i<15; i++) { + chan[i].vol=0x1f; + } + chan[15].vol=0xff; + + for (int i=0; i<512; i++) { + oldWrites[i]=-1; + pendingWrites[i]=-1; + } + + lastBusy=60; + dacMode=0; + dacPeriod=0; + dacPos=0; + dacRate=0; + dacSample=-1; + sampleBank=0; + ayEnvPeriod=0; + ayEnvMode=0; + ayEnvSlide=0; + ayEnvSlideLow=0; + ayNoiseFreq=0; + + delay=0; + + extMode=false; + + // AY noise + immWrite(0x06,ayNoiseFreq); + + // LFO + immWrite(0x22,0x08); + + // PCM volume + immWrite(0x101,0x3f); +} + +bool DivPlatformYM2610B::isStereo() { + return true; +} + +bool DivPlatformYM2610B::keyOffAffectsArp(int ch) { + return (ch>5); +} + +void DivPlatformYM2610B::notifyInsChange(int ins) { + for (int i=0; i<16; i++) { + if (chan[i].ins==ins) { + chan[i].insChanged=true; + } + } +} + +void DivPlatformYM2610B::notifyInsDeletion(void* ins) { + for (int i=6; i<9; i++) { + chan[i].std.notifyInsDeletion((DivInstrument*)ins); + } +} + +int DivPlatformYM2610B::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; + } + chipClock=8000000; + rate=chipClock/16; + iface.parent=parent; + iface.sampleBank=0; + fm=new ymfm::ym2610b(iface); + reset(); + return 16; +} + +void DivPlatformYM2610B::quit() { + delete fm; +} + +DivPlatformYM2610B::~DivPlatformYM2610B() { +} diff --git a/src/engine/platform/ym2610b.h b/src/engine/platform/ym2610b.h new file mode 100644 index 000000000..f7299efd0 --- /dev/null +++ b/src/engine/platform/ym2610b.h @@ -0,0 +1,102 @@ +/** + * 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 _YM2610B_H +#define _YM2610B_H +#include "../dispatch.h" +#include "../macroInt.h" +#include +#include "sound/ymfm/ymfm_opn.h" + +#include "ym2610.h" + +class DivPlatformYM2610B: public DivDispatch { + protected: + struct Channel { + DivInstrumentFM state; + unsigned char freqH, freqL; + int freq, baseFreq, pitch, note; + unsigned char ins, psgMode, autoEnvNum, autoEnvDen; + signed char konCycles; + bool active, insChanged, freqChanged, keyOn, keyOff, portaPause, inPorta; + int vol, outVol; + unsigned char pan; + DivMacroInt std; + Channel(): freqH(0), freqL(0), freq(0), baseFreq(0), pitch(0), note(0), ins(-1), psgMode(1), autoEnvNum(0), autoEnvDen(0), active(false), insChanged(true), freqChanged(false), keyOn(false), keyOff(false), portaPause(false), inPorta(false), vol(0), outVol(15), pan(3) {} + }; + Channel chan[16]; + bool isMuted[16]; + struct QueuedWrite { + unsigned short addr; + unsigned char val; + bool addrOrVal; + QueuedWrite(unsigned short a, unsigned char v): addr(a), val(v), addrOrVal(false) {} + }; + std::queue writes; + ymfm::ym2610b* fm; + ymfm::ym2610b::output_data fmout; + DivYM2610Interface iface; + unsigned char regPool[512]; + unsigned char lastBusy; + + bool dacMode; + int dacPeriod; + int dacRate; + int dacPos; + int dacSample; + int ayNoiseFreq; + unsigned char sampleBank; + + int delay; + + bool extMode; + + short oldWrites[512]; + short pendingWrites[512]; + unsigned char ayEnvMode; + unsigned short ayEnvPeriod; + short ayEnvSlideLow; + short ayEnvSlide; + + int octave(int freq); + int toFreq(int freq); + 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); + bool isStereo(); + bool keyOffAffectsArp(int ch); + void notifyInsChange(int ins); + void notifyInsDeletion(void* ins); + void poke(unsigned int addr, unsigned short val); + void poke(std::vector& wlist); + const char* getEffectName(unsigned char effect); + int init(DivEngine* parent, int channels, int sugRate, unsigned int flags); + void quit(); + ~DivPlatformYM2610B(); +}; +#endif diff --git a/src/engine/platform/ym2610bext.cpp b/src/engine/platform/ym2610bext.cpp new file mode 100644 index 000000000..5c3649164 --- /dev/null +++ b/src/engine/platform/ym2610bext.cpp @@ -0,0 +1,337 @@ +/** + * 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 "ym2610bext.h" +#include "../engine.h" +#include + +#include "ym2610shared.h" + +int DivPlatformYM2610BExt::dispatch(DivCommand c) { + if (c.chan<2) { + return DivPlatformYM2610B::dispatch(c); + } + if (c.chan>5) { + c.chan-=3; + return DivPlatformYM2610B::dispatch(c); + } + int ch=c.chan-2; + int ordch=orderedOps[ch]; + switch (c.cmd) { + case DIV_CMD_NOTE_ON: { + DivInstrument* ins=parent->getIns(opChan[ch].ins); + + unsigned short baseAddr=chanOffs_b[2]|opOffs[ordch]; + DivInstrumentFM::Operator op=ins->fm.op[ordch]; + // TODO: how does this work?! + if (isOpMuted[ch]) { + rWrite(baseAddr+0x40,127); + } else { + if (opChan[ch].insChanged) { + rWrite(baseAddr+0x40,127-(((127-op.tl)*(opChan[ch].vol&0x7f))/127)); + } + } + if (opChan[ch].insChanged) { + rWrite(baseAddr+0x30,(op.mult&15)|(dtTable[op.dt&7]<<4)); + rWrite(baseAddr+0x50,(op.ar&31)|(op.rs<<6)); + rWrite(baseAddr+0x60,(op.dr&31)|(op.am<<7)); + rWrite(baseAddr+0x70,op.d2r&31); + rWrite(baseAddr+0x80,(op.rr&15)|(op.sl<<4)); + rWrite(baseAddr+0x90,op.ssgEnv&15); + } + if (opChan[ch].insChanged) { // TODO how does this work? + rWrite(chanOffs_b[2]+0xb0,(ins->fm.alg&7)|(ins->fm.fb<<3)); + rWrite(chanOffs_b[2]+0xb4,(opChan[ch].pan<<6)|(ins->fm.fms&7)|((ins->fm.ams&3)<<4)); + } + opChan[ch].insChanged=false; + + if (c.value!=DIV_NOTE_NULL) { + opChan[ch].baseFreq=NOTE_FREQUENCY(c.value); + opChan[ch].freqChanged=true; + } + opChan[ch].keyOn=true; + opChan[ch].active=true; + break; + } + case DIV_CMD_NOTE_OFF: + opChan[ch].keyOff=true; + opChan[ch].keyOn=false; + opChan[ch].active=false; + break; + case DIV_CMD_VOLUME: { + opChan[ch].vol=c.value; + DivInstrument* ins=parent->getIns(opChan[ch].ins); + unsigned short baseAddr=chanOffs_b[2]|opOffs[ordch]; + DivInstrumentFM::Operator op=ins->fm.op[ordch]; + if (isOpMuted[ch]) { + rWrite(baseAddr+0x40,127); + } else { + rWrite(baseAddr+0x40,127-(((127-op.tl)*(opChan[ch].vol&0x7f))/127)); + } + break; + } + case DIV_CMD_GET_VOLUME: { + return opChan[ch].vol; + break; + } + case DIV_CMD_INSTRUMENT: + if (opChan[ch].ins!=c.value || c.value2==1) { + opChan[ch].insChanged=true; + } + opChan[ch].ins=c.value; + break; + case DIV_CMD_PANNING: { + switch (c.value) { + case 0x01: + opChan[ch].pan=1; + break; + case 0x10: + opChan[ch].pan=2; + break; + default: + opChan[ch].pan=3; + break; + } + DivInstrument* ins=parent->getIns(opChan[ch].ins); + // TODO: ??? + rWrite(chanOffs_b[2]+0xb4,(opChan[ch].pan<<6)|(ins->fm.fms&7)|((ins->fm.ams&3)<<4)); + break; + } + case DIV_CMD_PITCH: { + opChan[ch].pitch=c.value; + opChan[ch].freqChanged=true; + break; + } + case DIV_CMD_NOTE_PORTA: { + int destFreq=NOTE_FREQUENCY(c.value2); + int newFreq; + bool return2=false; + if (destFreq>opChan[ch].baseFreq) { + newFreq=opChan[ch].baseFreq+c.value*octave(opChan[ch].baseFreq); + if (newFreq>=destFreq) { + newFreq=destFreq; + return2=true; + } + } else { + newFreq=opChan[ch].baseFreq-c.value*octave(opChan[ch].baseFreq); + if (newFreq<=destFreq) { + newFreq=destFreq; + return2=true; + } + } + if (!opChan[ch].portaPause) { + if (octave(opChan[ch].baseFreq)!=octave(newFreq)) { + opChan[ch].portaPause=true; + break; + } + } + opChan[ch].baseFreq=newFreq; + opChan[ch].portaPause=false; + opChan[ch].freqChanged=true; + if (return2) return 2; + break; + } + case DIV_CMD_LEGATO: { + opChan[ch].baseFreq=NOTE_FREQUENCY(c.value); + opChan[ch].freqChanged=true; + break; + } + case DIV_CMD_FM_MULT: { // TODO + unsigned short baseAddr=chanOffs_b[2]|opOffs[orderedOps[c.value]]; + DivInstrument* ins=parent->getIns(opChan[ch].ins); + DivInstrumentFM::Operator op=ins->fm.op[orderedOps[c.value]]; + rWrite(baseAddr+0x30,(c.value2&15)|(dtTable[op.dt&7]<<4)); + break; + } + case DIV_CMD_FM_TL: { // TODO + unsigned short baseAddr=chanOffs_b[2]|opOffs[orderedOps[c.value]]; + DivInstrument* ins=parent->getIns(opChan[ch].ins); + if (isOutput[ins->fm.alg][c.value]) { + rWrite(baseAddr+0x40,127-(((127-c.value2)*(opChan[ch].vol&0x7f))/127)); + } else { + rWrite(baseAddr+0x40,c.value2); + } + break; + } + case DIV_CMD_FM_AR: { + DivInstrument* ins=parent->getIns(opChan[ch].ins); + if (c.value<0) { + for (int i=0; i<4; i++) { + DivInstrumentFM::Operator op=ins->fm.op[i]; + unsigned short baseAddr=chanOffs_b[2]|opOffs[i]; + rWrite(baseAddr+0x50,(c.value2&31)|(op.rs<<6)); + } + } else { + DivInstrumentFM::Operator op=ins->fm.op[orderedOps[c.value]]; + unsigned short baseAddr=chanOffs_b[2]|opOffs[orderedOps[c.value]]; + rWrite(baseAddr+0x50,(c.value2&31)|(op.rs<<6)); + } + break; + } + case DIV_CMD_GET_VOLMAX: + return 127; + break; + case DIV_ALWAYS_SET_VOLUME: + return 0; + break; + case DIV_CMD_PRE_PORTA: + break; + default: + //printf("WARNING: unimplemented command %d\n",c.cmd); + break; + } + return 1; +} + +static int opChanOffsL[4]={ + 0xa9, 0xaa, 0xa8, 0xa2 +}; + +static int opChanOffsH[4]={ + 0xad, 0xae, 0xac, 0xa6 +}; + +void DivPlatformYM2610BExt::tick() { + if (extMode) { + bool writeSomething=false; + unsigned char writeMask=2; + for (int i=0; i<4; i++) { + writeMask|=opChan[i].active<<(4+i); + if (opChan[i].keyOn || opChan[i].keyOff) { + writeSomething=true; + writeMask&=~(1<<(4+i)); + opChan[i].keyOff=false; + } + } + if (writeSomething) { + immWrite(0x28,writeMask); + } + } + + DivPlatformYM2610B::tick(); + + bool writeNoteOn=false; + unsigned char writeMask=2; + if (extMode) for (int i=0; i<4; i++) { + if (opChan[i].freqChanged) { + opChan[i].freq=parent->calcFreq(opChan[i].baseFreq,opChan[i].pitch); + if (opChan[i].freq>262143) opChan[i].freq=262143; + int freqt=toFreq(opChan[i].freq); + opChan[i].freqH=freqt>>8; + opChan[i].freqL=freqt&0xff; + immWrite(opChanOffsH[i],opChan[i].freqH); + immWrite(opChanOffsL[i],opChan[i].freqL); + opChan[i].freqChanged=false; + } + writeMask|=opChan[i].active<<(4+i); + if (opChan[i].keyOn) { + writeNoteOn=true; + writeMask|=1<<(4+i); + opChan[i].keyOn=false; + } + } + if (writeNoteOn) { + immWrite(0x28,writeMask); + } +} + +void DivPlatformYM2610BExt::muteChannel(int ch, bool mute) { + if (ch<2) { + DivPlatformYM2610B::muteChannel(ch,mute); + return; + } + if (ch>5) { + DivPlatformYM2610B::muteChannel(ch-3,mute); + return; + } + isOpMuted[ch-2]=mute; + + int ordch=orderedOps[ch-2]; + DivInstrument* ins=parent->getIns(opChan[ch-2].ins); + unsigned short baseAddr=chanOffs_b[2]|opOffs[ordch]; + DivInstrumentFM::Operator op=ins->fm.op[ordch]; + if (isOpMuted[ch-2]) { + rWrite(baseAddr+0x40,127); + } else if (isOutput[ins->fm.alg][ordch]) { + rWrite(baseAddr+0x40,127-(((127-op.tl)*(opChan[ch-2].vol&0x7f))/127)); + } else { + rWrite(baseAddr+0x40,op.tl); + } +} + +void DivPlatformYM2610BExt::forceIns() { + DivPlatformYM2610B::forceIns(); + for (int i=0; i<4; i++) { + opChan[i].insChanged=true; + if (opChan[i].active) { + opChan[i].keyOn=true; + opChan[i].freqChanged=true; + } + } +} + + +void* DivPlatformYM2610BExt::getChanState(int ch) { + if (ch>=6) return &chan[ch-3]; + if (ch>=2) return &opChan[ch-2]; + return &chan[ch]; +} + +void DivPlatformYM2610BExt::reset() { + DivPlatformYM2610B::reset(); + + for (int i=0; i<4; i++) { + opChan[i]=DivPlatformYM2610BExt::OpChannel(); + opChan[i].vol=127; + } + + // channel 2 mode + immWrite(0x27,0x40); + extMode=true; +} + +bool DivPlatformYM2610BExt::keyOffAffectsArp(int ch) { + return (ch>8); +} + +void DivPlatformYM2610BExt::notifyInsChange(int ins) { + DivPlatformYM2610B::notifyInsChange(ins); + for (int i=0; i<4; i++) { + if (opChan[i].ins==ins) { + opChan[i].insChanged=true; + } + } +} + +int DivPlatformYM2610BExt::init(DivEngine* parent, int channels, int sugRate, unsigned int flags) { + DivPlatformYM2610B::init(parent,channels,sugRate,flags); + for (int i=0; i<4; i++) { + isOpMuted[i]=false; + } + + reset(); + return 19; +} + +void DivPlatformYM2610BExt::quit() { + DivPlatformYM2610B::quit(); +} + +DivPlatformYM2610BExt::~DivPlatformYM2610BExt() { +} \ No newline at end of file diff --git a/src/engine/platform/ym2610bext.h b/src/engine/platform/ym2610bext.h new file mode 100644 index 000000000..25ca59196 --- /dev/null +++ b/src/engine/platform/ym2610bext.h @@ -0,0 +1,51 @@ +/** + * 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 "../dispatch.h" + +#include "ym2610b.h" + +class DivPlatformYM2610BExt: public DivPlatformYM2610B { + struct OpChannel { + DivMacroInt std; + unsigned char freqH, freqL; + int freq, baseFreq, pitch; + unsigned char ins; + signed char konCycles; + bool active, insChanged, freqChanged, keyOn, keyOff, portaPause; + int vol; + unsigned char pan; + OpChannel(): freqH(0), freqL(0), freq(0), baseFreq(0), pitch(0), ins(-1), active(false), insChanged(true), freqChanged(false), keyOn(false), keyOff(false), portaPause(false), vol(0), pan(3) {} + }; + OpChannel opChan[4]; + bool isOpMuted[4]; + friend void putDispatchChan(void*,int,int); + public: + int dispatch(DivCommand c); + void* getChanState(int chan); + void reset(); + void forceIns(); + void tick(); + void muteChannel(int ch, bool mute); + bool keyOffAffectsArp(int ch); + void notifyInsChange(int ins); + int init(DivEngine* parent, int channels, int sugRate, unsigned int flags); + void quit(); + ~DivPlatformYM2610BExt(); +}; diff --git a/src/engine/platform/ym2610ext.cpp b/src/engine/platform/ym2610ext.cpp index 000ab69da..aff4bbf33 100644 --- a/src/engine/platform/ym2610ext.cpp +++ b/src/engine/platform/ym2610ext.cpp @@ -147,10 +147,6 @@ int DivPlatformYM2610Ext::dispatch(DivCommand c) { if (return2) return 2; break; } - case DIV_CMD_SAMPLE_MODE: { - // ignored on extended channel 2 mode. - break; - } case DIV_CMD_LEGATO: { opChan[ch].baseFreq=NOTE_FREQUENCY(c.value); opChan[ch].freqChanged=true; diff --git a/src/engine/platform/ym2610shared.h b/src/engine/platform/ym2610shared.h index b548a0ec1..82dad07d8 100644 --- a/src/engine/platform/ym2610shared.h +++ b/src/engine/platform/ym2610shared.h @@ -17,9 +17,14 @@ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ +#include "sound/ymfm/ymfm_opn.h" + static unsigned short chanOffs[4]={ 0x01, 0x02, 0x101, 0x102 }; +static unsigned short chanOffs_b[6]={ + 0x00, 0x01, 0x02, 0x100, 0x101, 0x102 +}; static unsigned short opOffs[4]={ 0x00, 0x04, 0x08, 0x0c }; @@ -45,4 +50,13 @@ static int orderedOps[4]={ #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 CHIP_FREQBASE 9440540 \ No newline at end of file +#define CHIP_FREQBASE 9440540 + +class DivYM2610BInterface: public ymfm::ymfm_interface { + public: + DivEngine* parent; + int sampleBank; + uint8_t ymfm_external_read(ymfm::access_class type, uint32_t address); + void ymfm_external_write(ymfm::access_class type, uint32_t address, uint8_t data); + DivYM2610BInterface(): parent(NULL), sampleBank(0) {} +}; diff --git a/src/engine/playback.cpp b/src/engine/playback.cpp index 6c825cb71..099daadd6 100644 --- a/src/engine/playback.cpp +++ b/src/engine/playback.cpp @@ -262,6 +262,10 @@ bool DivEngine::perSystemPostEffect(int ch, unsigned char effect, unsigned char case DIV_SYSTEM_YM2151: case DIV_SYSTEM_YM2610: case DIV_SYSTEM_YM2610_EXT: + case DIV_SYSTEM_YM2610_FULL: + case DIV_SYSTEM_YM2610_FULL_EXT: + case DIV_SYSTEM_YM2610B: + case DIV_SYSTEM_YM2610B_EXT: switch (effect) { case 0x10: // LFO or noise mode if (sysOfChan[ch]==DIV_SYSTEM_YM2151) { @@ -324,42 +328,58 @@ bool DivEngine::perSystemPostEffect(int ch, unsigned char effect, unsigned char dispatchCmd(DivCommand(DIV_CMD_FM_PM_DEPTH,ch,effectVal&127)); break; case 0x20: // Neo Geo PSG mode - if (sysOfChan[ch]==DIV_SYSTEM_YM2610 || sysOfChan[ch]==DIV_SYSTEM_YM2610_EXT) { + if (sysOfChan[ch]==DIV_SYSTEM_YM2610 || sysOfChan[ch]==DIV_SYSTEM_YM2610_EXT + || sysOfChan[ch]==DIV_SYSTEM_YM2610_FULL || sysOfChan[ch]==DIV_SYSTEM_YM2610_FULL_EXT + || sysOfChan[ch]==DIV_SYSTEM_YM2610B || sysOfChan[ch]==DIV_SYSTEM_YM2610B_EXT) { dispatchCmd(DivCommand(DIV_CMD_STD_NOISE_MODE,ch,effectVal)); } break; case 0x21: // Neo Geo PSG noise freq - if (sysOfChan[ch]==DIV_SYSTEM_YM2610 || sysOfChan[ch]==DIV_SYSTEM_YM2610_EXT) { + if (sysOfChan[ch]==DIV_SYSTEM_YM2610 || sysOfChan[ch]==DIV_SYSTEM_YM2610_EXT + || sysOfChan[ch]==DIV_SYSTEM_YM2610_FULL || sysOfChan[ch]==DIV_SYSTEM_YM2610_FULL_EXT + || sysOfChan[ch]==DIV_SYSTEM_YM2610B || sysOfChan[ch]==DIV_SYSTEM_YM2610B_EXT) { dispatchCmd(DivCommand(DIV_CMD_STD_NOISE_FREQ,ch,effectVal)); } break; case 0x22: // UNOFFICIAL: Neo Geo PSG envelope enable - if (sysOfChan[ch]==DIV_SYSTEM_YM2610 || sysOfChan[ch]==DIV_SYSTEM_YM2610_EXT) { + if (sysOfChan[ch]==DIV_SYSTEM_YM2610 || sysOfChan[ch]==DIV_SYSTEM_YM2610_EXT + || sysOfChan[ch]==DIV_SYSTEM_YM2610_FULL || sysOfChan[ch]==DIV_SYSTEM_YM2610_FULL_EXT + || sysOfChan[ch]==DIV_SYSTEM_YM2610B || sysOfChan[ch]==DIV_SYSTEM_YM2610B_EXT) { dispatchCmd(DivCommand(DIV_CMD_AY_ENVELOPE_SET,ch,effectVal)); } break; case 0x23: // UNOFFICIAL: Neo Geo PSG envelope period low - if (sysOfChan[ch]==DIV_SYSTEM_YM2610 || sysOfChan[ch]==DIV_SYSTEM_YM2610_EXT) { + if (sysOfChan[ch]==DIV_SYSTEM_YM2610 || sysOfChan[ch]==DIV_SYSTEM_YM2610_EXT + || sysOfChan[ch]==DIV_SYSTEM_YM2610_FULL || sysOfChan[ch]==DIV_SYSTEM_YM2610_FULL_EXT + || sysOfChan[ch]==DIV_SYSTEM_YM2610B || sysOfChan[ch]==DIV_SYSTEM_YM2610B_EXT) { dispatchCmd(DivCommand(DIV_CMD_AY_ENVELOPE_LOW,ch,effectVal)); } break; case 0x24: // UNOFFICIAL: Neo Geo PSG envelope period high - if (sysOfChan[ch]==DIV_SYSTEM_YM2610 || sysOfChan[ch]==DIV_SYSTEM_YM2610_EXT) { + if (sysOfChan[ch]==DIV_SYSTEM_YM2610 || sysOfChan[ch]==DIV_SYSTEM_YM2610_EXT + || sysOfChan[ch]==DIV_SYSTEM_YM2610_FULL || sysOfChan[ch]==DIV_SYSTEM_YM2610_FULL_EXT + || sysOfChan[ch]==DIV_SYSTEM_YM2610B || sysOfChan[ch]==DIV_SYSTEM_YM2610B_EXT) { dispatchCmd(DivCommand(DIV_CMD_AY_ENVELOPE_HIGH,ch,effectVal)); } break; case 0x25: // UNOFFICIAL: Neo Geo PSG envelope slide up - if (sysOfChan[ch]==DIV_SYSTEM_YM2610 || sysOfChan[ch]==DIV_SYSTEM_YM2610_EXT) { + if (sysOfChan[ch]==DIV_SYSTEM_YM2610 || sysOfChan[ch]==DIV_SYSTEM_YM2610_EXT + || sysOfChan[ch]==DIV_SYSTEM_YM2610_FULL || sysOfChan[ch]==DIV_SYSTEM_YM2610_FULL_EXT + || sysOfChan[ch]==DIV_SYSTEM_YM2610B || sysOfChan[ch]==DIV_SYSTEM_YM2610B_EXT) { dispatchCmd(DivCommand(DIV_CMD_AY_ENVELOPE_SLIDE,ch,-effectVal)); } break; case 0x26: // UNOFFICIAL: Neo Geo PSG envelope slide down - if (sysOfChan[ch]==DIV_SYSTEM_YM2610 || sysOfChan[ch]==DIV_SYSTEM_YM2610_EXT) { + if (sysOfChan[ch]==DIV_SYSTEM_YM2610 || sysOfChan[ch]==DIV_SYSTEM_YM2610_EXT + || sysOfChan[ch]==DIV_SYSTEM_YM2610_FULL || sysOfChan[ch]==DIV_SYSTEM_YM2610_FULL_EXT + || sysOfChan[ch]==DIV_SYSTEM_YM2610B || sysOfChan[ch]==DIV_SYSTEM_YM2610B_EXT) { dispatchCmd(DivCommand(DIV_CMD_AY_ENVELOPE_SLIDE,ch,effectVal)); } break; case 0x29: // auto-envelope - if (sysOfChan[ch]==DIV_SYSTEM_YM2610 || sysOfChan[ch]==DIV_SYSTEM_YM2610_EXT) { + if (sysOfChan[ch]==DIV_SYSTEM_YM2610 || sysOfChan[ch]==DIV_SYSTEM_YM2610_EXT + || sysOfChan[ch]==DIV_SYSTEM_YM2610_FULL || sysOfChan[ch]==DIV_SYSTEM_YM2610_FULL_EXT + || sysOfChan[ch]==DIV_SYSTEM_YM2610B || sysOfChan[ch]==DIV_SYSTEM_YM2610B_EXT) { dispatchCmd(DivCommand(DIV_CMD_AY_AUTO_ENVELOPE,ch,effectVal)); } break; diff --git a/src/engine/song.h b/src/engine/song.h index f4620eef9..ba1413e5f 100644 --- a/src/engine/song.h +++ b/src/engine/song.h @@ -90,6 +90,7 @@ enum DivSystem { DIV_SYSTEM_OPLL_DRUMS, DIV_SYSTEM_LYNX, DIV_SYSTEM_QSOUND, + DIV_SYSTEM_YM2610B_EXT, DIV_SYSTEM_SEGAPCM_COMPAT }; diff --git a/src/engine/sysDef.cpp b/src/engine/sysDef.cpp index 54589e37d..85fe54b8e 100644 --- a/src/engine/sysDef.cpp +++ b/src/engine/sysDef.cpp @@ -135,6 +135,8 @@ DivSystem DivEngine::systemFromFile(unsigned char val) { return DIV_SYSTEM_LYNX; case 0xa9: return DIV_SYSTEM_SEGAPCM_COMPAT; + case 0xde: + return DIV_SYSTEM_YM2610B_EXT; case 0xe0: return DIV_SYSTEM_QSOUND; } @@ -256,6 +258,8 @@ unsigned char DivEngine::systemToFile(DivSystem val) { return 0xa8; case DIV_SYSTEM_SEGAPCM_COMPAT: return 0xa9; + case DIV_SYSTEM_YM2610B_EXT: + return 0xde; case DIV_SYSTEM_QSOUND: return 0xe0; @@ -376,6 +380,7 @@ int DivEngine::getChannelCount(DivSystem sys) { return 4; case DIV_SYSTEM_SEGAPCM_COMPAT: return 5; + case DIV_SYSTEM_YM2610B_EXT: case DIV_SYSTEM_QSOUND: return 19; } @@ -629,6 +634,8 @@ const char* DivEngine::getSystemName(DivSystem sys) { return "Atari Lynx"; case DIV_SYSTEM_SEGAPCM_COMPAT: return "SegaPCM (compatible 5-channel mode)"; + case DIV_SYSTEM_YM2610B_EXT: + return "Taito Arcade Extended Channel 3"; case DIV_SYSTEM_QSOUND: return "Capcom QSound"; } @@ -752,6 +759,8 @@ const char* DivEngine::getSystemChips(DivSystem sys) { return "Mikey"; case DIV_SYSTEM_SEGAPCM_COMPAT: return "SegaPCM (compatible 5-channel mode)"; + case DIV_SYSTEM_YM2610B_EXT: + return "Yamaha YM2610B Extended Channel 3"; case DIV_SYSTEM_QSOUND: return "Capcom DL-1425"; } @@ -822,6 +831,8 @@ bool DivEngine::isFMSystem(DivSystem sys) { sys==DIV_SYSTEM_YM2610_EXT || sys==DIV_SYSTEM_YM2610_FULL || sys==DIV_SYSTEM_YM2610_FULL_EXT || + sys==DIV_SYSTEM_YM2610B || + sys==DIV_SYSTEM_YM2610B_EXT || sys==DIV_SYSTEM_YMU759 || sys==DIV_SYSTEM_YM2151 || sys==DIV_SYSTEM_YM2612); @@ -834,7 +845,7 @@ bool DivEngine::isSTDSystem(DivSystem sys) { sys!=DIV_SYSTEM_YM2151); } -const char* chanNames[37][24]={ +const char* chanNames[38][24]={ {"Channel 1", "Channel 2", "Channel 3", "Channel 4", "Channel 5", "Channel 6", "Channel 7", "Channel 8", "Channel 9", "Channel 10", "Channel 11", "Channel 12", "Channel 13", "Channel 14", "Channel 15", "Channel 16", "PCM"}, // YMU759/SegaPCM {"FM 1", "FM 2", "FM 3", "FM 4", "FM 5", "FM 6", "Square 1", "Square 2", "Square 3", "Noise"}, // Genesis {"FM 1", "FM 2", "FM 3 OP1", "FM 3 OP2", "FM 3 OP3", "FM 3 OP4", "FM 4", "FM 5", "FM 6", "Square 1", "Square 2", "Square 3", "Noise"}, // Genesis (extended channel 3) @@ -872,9 +883,10 @@ const char* chanNames[37][24]={ {"FM 1", "FM 2", "FM 3", "FM 4", "FM 5", "FM 6", "4OP 1", "4OP 2", "4OP 3", "4OP 4", "4OP 5", "4OP 6"}, // OPL3 4-op {"FM 1", "FM 2", "FM 3", "4OP 1", "4OP 2", "4OP 3", "4OP 4", "4OP 5", "4OP 6", "Kick", "Snare", "Tom", "Top", "HiHat"}, // OPL3 4-op + drums {"PCM 1", "PCM 2", "PCM 3", "PCM 4", "PCM 5", "PCM 6", "PCM 7", "PCM 8", "PCM 9", "PCM 10", "PCM 11", "PCM 12", "PCM 13", "PCM 14", "PCM 15", "PCM 16", "ADPCM 1", "ADPCM 2", "ADPCM 3"}, // QSound + {"FM 1", "FM 2", "FM 3 OP1", "FM 3 OP2", "FM 3 OP3", "FM 3 OP4", "FM 4", "FM 5", "FM 6", "PSG 1", "PSG 2", "PSG 3", "ADPCM-A 1", "ADPCM-A 2", "ADPCM-A 3", "ADPCM-A 4", "ADPCM-A 5", "ADPCM-A 6", "ADPCM-B"}, // YM2610B (extended channel 3) }; -const char* chanShortNames[37][24]={ +const char* chanShortNames[38][24]={ {"1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12", "13", "14", "15", "16", "PCM"}, // YMU759 {"F1", "F2", "F3", "F4", "F5", "F6", "S1", "S2", "S3", "NO"}, // Genesis {"F1", "F2", "O1", "O2", "O3", "O4", "F4", "F5", "F6", "S1", "S2", "S3", "S4"}, // Genesis (extended channel 3) @@ -912,9 +924,10 @@ const char* chanShortNames[37][24]={ {"F1", "F2", "F3", "F4", "F5", "F6", "Q1", "Q2", "Q3", "Q4", "Q5", "Q6"}, // OPL3 4-op {"F1", "F2", "F3", "Q1", "Q2", "Q3", "Q4", "Q5", "Q6", "BD", "SD", "TM", "TP", "HH"}, // OPL3 4-op + drums {"1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12", "13", "14", "15", "16", "A1", "A2", "A3"}, // QSound + {"F1", "F2", "O1", "O2", "O3", "O4", "F4", "F5", "F6", "S1", "S2", "S3", "P1", "P2", "P3", "P4", "P5", "P6", "B"}, // YM2610B (extended channel 3) }; -const int chanTypes[37][24]={ +const int chanTypes[38][24]={ {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4}, // YMU759 {0, 0, 0, 0, 0, 0, 1, 1, 1, 2}, // Genesis {0, 0, 5, 5, 5, 5, 0, 0, 0, 1, 1, 1, 2}, // Genesis (extended channel 3) @@ -952,9 +965,10 @@ const int chanTypes[37][24]={ {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, // OPL3 4-op {0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 2}, // OPL3 4-op + drums {3, 3, 3, 3}, //Lynx + {0, 0, 5, 5, 5, 5, 0, 0, 0, 1, 1, 1, 4, 4, 4, 4, 4, 4, 4}, // YM2610B (extended channel 3) }; -const DivInstrumentType chanPrefType[43][24]={ +const DivInstrumentType chanPrefType[44][24]={ {DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_FM}, // YMU759 {DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_STD, DIV_INS_STD, DIV_INS_STD, DIV_INS_STD}, // Genesis {DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_STD, DIV_INS_STD, DIV_INS_STD, DIV_INS_STD}, // Genesis (extended channel 3) @@ -998,6 +1012,7 @@ const DivInstrumentType chanPrefType[43][24]={ {DIV_INS_SWAN, DIV_INS_SWAN, DIV_INS_SWAN, DIV_INS_SWAN}, // Swan {DIV_INS_OPZ, DIV_INS_OPZ, DIV_INS_OPZ, DIV_INS_OPZ, DIV_INS_OPZ, DIV_INS_OPZ, DIV_INS_OPZ, DIV_INS_OPZ}, // Z {DIV_INS_MIKEY, DIV_INS_MIKEY, DIV_INS_MIKEY, DIV_INS_MIKEY}, // Lynx + {DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_AY, DIV_INS_AY, DIV_INS_AY, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA}, // YM2610B (extended channel 3) }; const char* DivEngine::getChannelName(int chan) { @@ -1115,6 +1130,9 @@ const char* DivEngine::getChannelName(int chan) { case DIV_SYSTEM_YM2610B: return chanNames[31][dispatchChanOfChan[chan]]; break; + case DIV_SYSTEM_YM2610B_EXT: + return chanNames[37][dispatchChanOfChan[chan]]; + break; case DIV_SYSTEM_OPLL_DRUMS: case DIV_SYSTEM_OPL_DRUMS: case DIV_SYSTEM_OPL2_DRUMS: @@ -1251,6 +1269,9 @@ const char* DivEngine::getChannelShortName(int chan) { case DIV_SYSTEM_YM2610B: return chanShortNames[31][dispatchChanOfChan[chan]]; break; + case DIV_SYSTEM_YM2610B_EXT: + return chanShortNames[37][dispatchChanOfChan[chan]]; + break; case DIV_SYSTEM_OPLL_DRUMS: case DIV_SYSTEM_OPL_DRUMS: case DIV_SYSTEM_OPL2_DRUMS: @@ -1385,6 +1406,9 @@ int DivEngine::getChannelType(int chan) { case DIV_SYSTEM_YM2610B: return chanTypes[31][dispatchChanOfChan[chan]]; break; + case DIV_SYSTEM_YM2610B_EXT: + return chanTypes[37][dispatchChanOfChan[chan]]; + break; case DIV_SYSTEM_OPLL_DRUMS: case DIV_SYSTEM_OPL_DRUMS: case DIV_SYSTEM_OPL2_DRUMS: @@ -1519,6 +1543,9 @@ DivInstrumentType DivEngine::getPreferInsType(int chan) { case DIV_SYSTEM_YM2610B: return chanPrefType[31][dispatchChanOfChan[chan]]; break; + case DIV_SYSTEM_YM2610B_EXT: + return chanPrefType[43][dispatchChanOfChan[chan]]; + break; case DIV_SYSTEM_OPLL_DRUMS: return chanPrefType[32][dispatchChanOfChan[chan]]; break; diff --git a/src/engine/vgmOps.cpp b/src/engine/vgmOps.cpp index 93a2bcf42..5c2a4d427 100644 --- a/src/engine/vgmOps.cpp +++ b/src/engine/vgmOps.cpp @@ -158,8 +158,10 @@ void DivEngine::performVGMWrite(SafeWriter* w, DivSystem sys, DivRegWrite& write break; case DIV_SYSTEM_YM2610: case DIV_SYSTEM_YM2610_FULL: + case DIV_SYSTEM_YM2610B: case DIV_SYSTEM_YM2610_EXT: case DIV_SYSTEM_YM2610_FULL_EXT: + case DIV_SYSTEM_YM2610B_EXT: for (int i=0; i<2; i++) { // set SL and RR to highest w->writeC(isSecond?0xa8:0x58); w->writeC(0x81+i); @@ -386,8 +388,10 @@ void DivEngine::performVGMWrite(SafeWriter* w, DivSystem sys, DivRegWrite& write break; case DIV_SYSTEM_YM2610: case DIV_SYSTEM_YM2610_FULL: + case DIV_SYSTEM_YM2610B: case DIV_SYSTEM_YM2610_EXT: case DIV_SYSTEM_YM2610_FULL_EXT: + case DIV_SYSTEM_YM2610B_EXT: switch (write.addr>>8) { case 0: // port 0 w->writeC(isSecond?0xa8:0x58); @@ -655,8 +659,10 @@ SafeWriter* DivEngine::saveVGM(bool* sysToExport, bool loop) { break; case DIV_SYSTEM_YM2610: case DIV_SYSTEM_YM2610_FULL: + case DIV_SYSTEM_YM2610B: case DIV_SYSTEM_YM2610_EXT: case DIV_SYSTEM_YM2610_FULL_EXT: + case DIV_SYSTEM_YM2610B_EXT: if (!hasOPNB) { hasOPNB=disCont[i].dispatch->chipClock; willExport[i]=true; @@ -667,6 +673,10 @@ SafeWriter* DivEngine::saveVGM(bool* sysToExport, bool loop) { hasOPNB|=0x40000000; howManyChips++; } + break; + if (((song.system[i]==DIV_SYSTEM_YM2610B) || (song.system[i]==DIV_SYSTEM_YM2610B_EXT)) && (!(hasOPNB&0x80000000))) { // YM2610B flag + hasOPNB|=0x80000000; + } break; case DIV_SYSTEM_AY8910: case DIV_SYSTEM_AY8930: diff --git a/src/gui/debug.cpp b/src/gui/debug.cpp index 8b06f6dec..4a259145b 100644 --- a/src/gui/debug.cpp +++ b/src/gui/debug.cpp @@ -29,6 +29,8 @@ #include "../engine/platform/arcade.h" #include "../engine/platform/ym2610.h" #include "../engine/platform/ym2610ext.h" +#include "../engine/platform/ym2610b.h" +#include "../engine/platform/ym2610bext.h" #include "../engine/platform/ay.h" #include "../engine/platform/ay8930.h" #include "../engine/platform/tia.h" diff --git a/src/gui/gui.cpp b/src/gui/gui.cpp index ee8d5a954..f0a9bc7ec 100644 --- a/src/gui/gui.cpp +++ b/src/gui/gui.cpp @@ -4485,6 +4485,8 @@ bool FurnaceGUI::loop() { sysAddOption(DIV_SYSTEM_YM2610_EXT); sysAddOption(DIV_SYSTEM_YM2610_FULL); sysAddOption(DIV_SYSTEM_YM2610_FULL_EXT); + sysAddOption(DIV_SYSTEM_YM2610B); + sysAddOption(DIV_SYSTEM_YM2610B_EXT); sysAddOption(DIV_SYSTEM_AY8910); sysAddOption(DIV_SYSTEM_AMIGA); sysAddOption(DIV_SYSTEM_TIA); @@ -4716,6 +4718,8 @@ bool FurnaceGUI::loop() { case DIV_SYSTEM_YM2610_EXT: case DIV_SYSTEM_YM2610_FULL: case DIV_SYSTEM_YM2610_FULL_EXT: + case DIV_SYSTEM_YM2610B: + case DIV_SYSTEM_YM2610B_EXT: case DIV_SYSTEM_YMU759: ImGui::Text("nothing to configure"); break; @@ -4749,6 +4753,8 @@ bool FurnaceGUI::loop() { sysChangeOption(i,DIV_SYSTEM_YM2610_EXT); sysChangeOption(i,DIV_SYSTEM_YM2610_FULL); sysChangeOption(i,DIV_SYSTEM_YM2610_FULL_EXT); + sysChangeOption(i,DIV_SYSTEM_YM2610B); + sysChangeOption(i,DIV_SYSTEM_YM2610B_EXT); sysChangeOption(i,DIV_SYSTEM_AY8910); sysChangeOption(i,DIV_SYSTEM_AMIGA); sysChangeOption(i,DIV_SYSTEM_TIA); From a132a28fcb19c05f24d6e0b346b587b9f889fd2b Mon Sep 17 00:00:00 2001 From: cam900 Date: Fri, 25 Feb 2022 17:37:43 +0900 Subject: [PATCH 02/50] Fix VGM saving --- src/engine/vgmOps.cpp | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/engine/vgmOps.cpp b/src/engine/vgmOps.cpp index 7663914f8..fd5fa3ce4 100644 --- a/src/engine/vgmOps.cpp +++ b/src/engine/vgmOps.cpp @@ -673,10 +673,9 @@ SafeWriter* DivEngine::saveVGM(bool* sysToExport, bool loop) { hasOPNB|=0x40000000; howManyChips++; } - break; - if (((song.system[i]==DIV_SYSTEM_YM2610B) || (song.system[i]==DIV_SYSTEM_YM2610B_EXT)) && (!(hasOPNB&0x80000000))) { // YM2610B flag + if (((song.system[i]==DIV_SYSTEM_YM2610B) || (song.system[i]==DIV_SYSTEM_YM2610B_EXT)) && (!(hasOPNB&0x80000000))) { // YM2610B flag hasOPNB|=0x80000000; - } + } break; case DIV_SYSTEM_AY8910: case DIV_SYSTEM_AY8930: From e96cd77ba763c764fef45f8050ab6c297423a36e Mon Sep 17 00:00:00 2001 From: cam900 Date: Fri, 25 Feb 2022 17:39:33 +0900 Subject: [PATCH 03/50] Remove unnecessary interface --- src/engine/platform/ym2610shared.h | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/src/engine/platform/ym2610shared.h b/src/engine/platform/ym2610shared.h index 82dad07d8..2f3e563ec 100644 --- a/src/engine/platform/ym2610shared.h +++ b/src/engine/platform/ym2610shared.h @@ -17,8 +17,6 @@ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ -#include "sound/ymfm/ymfm_opn.h" - static unsigned short chanOffs[4]={ 0x01, 0x02, 0x101, 0x102 }; @@ -51,12 +49,3 @@ static int orderedOps[4]={ #define immWrite(a,v) if (!skipRegisterWrites) {writes.emplace(a,v); if (dumpWrites) {addWrite(a,v);} } #define CHIP_FREQBASE 9440540 - -class DivYM2610BInterface: public ymfm::ymfm_interface { - public: - DivEngine* parent; - int sampleBank; - uint8_t ymfm_external_read(ymfm::access_class type, uint32_t address); - void ymfm_external_write(ymfm::access_class type, uint32_t address, uint8_t data); - DivYM2610BInterface(): parent(NULL), sampleBank(0) {} -}; From ff743c92fda88e8955a79316dc9c2d34d2e0b6e4 Mon Sep 17 00:00:00 2001 From: cam900 Date: Sat, 26 Feb 2022 00:31:17 +0900 Subject: [PATCH 04/50] Fix build --- src/engine/platform/ym2610b.h | 4 ++++ src/engine/platform/ym2610shared.h | 3 --- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/engine/platform/ym2610b.h b/src/engine/platform/ym2610b.h index 8b18acd15..892f68437 100644 --- a/src/engine/platform/ym2610b.h +++ b/src/engine/platform/ym2610b.h @@ -26,6 +26,10 @@ #include "ym2610.h" +static unsigned short chanOffs_b[6]={ + 0x00, 0x01, 0x02, 0x100, 0x101, 0x102 +}; + class DivPlatformYM2610B: public DivDispatch { protected: struct Channel { diff --git a/src/engine/platform/ym2610shared.h b/src/engine/platform/ym2610shared.h index 2f3e563ec..7891a14fb 100644 --- a/src/engine/platform/ym2610shared.h +++ b/src/engine/platform/ym2610shared.h @@ -20,9 +20,6 @@ static unsigned short chanOffs[4]={ 0x01, 0x02, 0x101, 0x102 }; -static unsigned short chanOffs_b[6]={ - 0x00, 0x01, 0x02, 0x100, 0x101, 0x102 -}; static unsigned short opOffs[4]={ 0x00, 0x04, 0x08, 0x0c }; From b1a49dcdc5dcdc6808b775c63157afa28641d070 Mon Sep 17 00:00:00 2001 From: cam900 Date: Sat, 26 Feb 2022 00:50:49 +0900 Subject: [PATCH 05/50] Fix actually --- src/engine/platform/ym2610b.h | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/engine/platform/ym2610b.h b/src/engine/platform/ym2610b.h index 892f68437..f995d5975 100644 --- a/src/engine/platform/ym2610b.h +++ b/src/engine/platform/ym2610b.h @@ -26,12 +26,12 @@ #include "ym2610.h" -static unsigned short chanOffs_b[6]={ - 0x00, 0x01, 0x02, 0x100, 0x101, 0x102 -}; - class DivPlatformYM2610B: public DivDispatch { protected: + const unsigned short chanOffs_b[6]={ + 0x00, 0x01, 0x02, 0x100, 0x101, 0x102 + }; + struct Channel { DivInstrumentFM state; unsigned char freqH, freqL; From d64ddaadeefe2d1b79257c4cae61e571dcf4caf8 Mon Sep 17 00:00:00 2001 From: cam900 Date: Sat, 26 Feb 2022 01:12:37 +0900 Subject: [PATCH 06/50] Further fix build --- src/engine/platform/ym2610.h | 4 +++ src/engine/platform/ym2610b.cpp | 46 +++++++++++++++--------------- src/engine/platform/ym2610b.h | 2 +- src/engine/platform/ym2610bext.cpp | 20 ++++++------- src/engine/platform/ym2610shared.h | 3 -- 5 files changed, 38 insertions(+), 37 deletions(-) diff --git a/src/engine/platform/ym2610.h b/src/engine/platform/ym2610.h index 317870f69..65456c720 100644 --- a/src/engine/platform/ym2610.h +++ b/src/engine/platform/ym2610.h @@ -35,6 +35,10 @@ class DivYM2610Interface: public ymfm::ymfm_interface { class DivPlatformYM2610: public DivDispatch { protected: + const unsigned short chanOffs[4]={ + 0x01, 0x02, 0x101, 0x102 + }; + struct Channel { DivInstrumentFM state; unsigned char freqH, freqL; diff --git a/src/engine/platform/ym2610b.cpp b/src/engine/platform/ym2610b.cpp index 5e755eeb5..839811920 100644 --- a/src/engine/platform/ym2610b.cpp +++ b/src/engine/platform/ym2610b.cpp @@ -250,7 +250,7 @@ void DivPlatformYM2610B::tick() { if (chan[i].std.hadVol) { chan[i].outVol=(chan[i].vol*MIN(127,chan[i].std.vol))/127; for (int j=0; j<4; j++) { - unsigned short baseAddr=chanOffs_b[i]|opOffs[j]; + unsigned short baseAddr=chanOffs[i]|opOffs[j]; DivInstrumentFM::Operator& op=chan[i].state.op[j]; if (isOutput[chan[i].state.alg][j]) { rWrite(baseAddr+ADDR_TL,127-(((127-op.tl)*(chan[i].outVol&0x7f))/127)); @@ -278,9 +278,9 @@ void DivPlatformYM2610B::tick() { if (chan[i].std.hadAlg) { chan[i].state.alg=chan[i].std.alg; - rWrite(chanOffs_b[i]+ADDR_FB_ALG,(chan[i].state.alg&7)|(chan[i].state.fb<<3)); + rWrite(chanOffs[i]+ADDR_FB_ALG,(chan[i].state.alg&7)|(chan[i].state.fb<<3)); if (!parent->song.algMacroBehavior) for (int j=0; j<4; j++) { - unsigned short baseAddr=chanOffs_b[i]|opOffs[j]; + unsigned short baseAddr=chanOffs[i]|opOffs[j]; DivInstrumentFM::Operator& op=chan[i].state.op[j]; if (isMuted[i]) { rWrite(baseAddr+ADDR_TL,127); @@ -295,18 +295,18 @@ void DivPlatformYM2610B::tick() { } if (chan[i].std.hadFb) { chan[i].state.fb=chan[i].std.fb; - rWrite(chanOffs_b[i]+ADDR_FB_ALG,(chan[i].state.alg&7)|(chan[i].state.fb<<3)); + rWrite(chanOffs[i]+ADDR_FB_ALG,(chan[i].state.alg&7)|(chan[i].state.fb<<3)); } if (chan[i].std.hadFms) { chan[i].state.fms=chan[i].std.fms; - rWrite(chanOffs_b[i]+ADDR_LRAF,(isMuted[i]?0:(chan[i].pan<<6))|(chan[i].state.fms&7)|((chan[i].state.ams&3)<<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].std.hadAms) { chan[i].state.ams=chan[i].std.ams; - rWrite(chanOffs_b[i]+ADDR_LRAF,(isMuted[i]?0:(chan[i].pan<<6))|(chan[i].state.fms&7)|((chan[i].state.ams&3)<<4)); + rWrite(chanOffs[i]+ADDR_LRAF,(isMuted[i]?0:(chan[i].pan<<6))|(chan[i].state.fms&7)|((chan[i].state.ams&3)<<4)); } for (int j=0; j<4; j++) { - unsigned short baseAddr=chanOffs_b[i]|opOffs[j]; + unsigned short baseAddr=chanOffs[i]|opOffs[j]; DivInstrumentFM::Operator& op=chan[i].state.op[j]; DivMacroInt::IntOp& m=chan[i].std.op[j]; if (m.hadAm) { @@ -409,8 +409,8 @@ void DivPlatformYM2610B::tick() { chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,false,octave(chan[i].baseFreq)); if (chan[i].freq>262143) chan[i].freq=262143; int freqt=toFreq(chan[i].freq); - immWrite(chanOffs_b[i]+ADDR_FREQH,freqt>>8); - immWrite(chanOffs_b[i]+ADDR_FREQ,freqt&0xff); + immWrite(chanOffs[i]+ADDR_FREQH,freqt>>8); + immWrite(chanOffs[i]+ADDR_FREQ,freqt&0xff); chan[i].freqChanged=false; } if (chan[i].keyOn) { @@ -566,7 +566,7 @@ int DivPlatformYM2610B::dispatch(DivCommand c) { } for (int i=0; i<6; i++) { - unsigned short baseAddr=chanOffs_b[c.chan]|opOffs[i]; + unsigned short baseAddr=chanOffs[c.chan]|opOffs[i]; DivInstrumentFM::Operator& op=chan[c.chan].state.op[i]; if (isOutput[chan[c.chan].state.alg][i]) { if (!chan[c.chan].active || chan[c.chan].insChanged) { @@ -587,8 +587,8 @@ int DivPlatformYM2610B::dispatch(DivCommand c) { } } if (chan[c.chan].insChanged) { - rWrite(chanOffs_b[c.chan]+ADDR_FB_ALG,(chan[c.chan].state.alg&7)|(chan[c.chan].state.fb<<3)); - rWrite(chanOffs_b[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)); + rWrite(chanOffs[c.chan]+ADDR_FB_ALG,(chan[c.chan].state.alg&7)|(chan[c.chan].state.fb<<3)); + 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)); } chan[c.chan].insChanged=false; @@ -658,7 +658,7 @@ int DivPlatformYM2610B::dispatch(DivCommand c) { break; } for (int i=0; i<4; i++) { - unsigned short baseAddr=chanOffs_b[c.chan]|opOffs[i]; + unsigned short baseAddr=chanOffs[c.chan]|opOffs[i]; DivInstrumentFM::Operator& op=chan[c.chan].state.op[i]; if (isOutput[chan[c.chan].state.alg][i]) { rWrite(baseAddr+ADDR_TL,127-(((127-op.tl)*(chan[c.chan].outVol&0x7f))/127)); @@ -699,7 +699,7 @@ int DivPlatformYM2610B::dispatch(DivCommand c) { break; } if (c.chan>5) break; - rWrite(chanOffs_b[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)); + 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)); break; } case DIV_CMD_PITCH: { @@ -783,12 +783,12 @@ int DivPlatformYM2610B::dispatch(DivCommand c) { case DIV_CMD_FM_FB: { if (c.chan>5) break; chan[c.chan].state.fb=c.value&7; - rWrite(chanOffs_b[c.chan]+ADDR_FB_ALG,(chan[c.chan].state.alg&7)|(chan[c.chan].state.fb<<3)); + rWrite(chanOffs[c.chan]+ADDR_FB_ALG,(chan[c.chan].state.alg&7)|(chan[c.chan].state.fb<<3)); break; } case DIV_CMD_FM_MULT: { if (c.chan>5) break; - unsigned short baseAddr=chanOffs_b[c.chan]|opOffs[orderedOps[c.value]]; + unsigned short baseAddr=chanOffs[c.chan]|opOffs[orderedOps[c.value]]; DivInstrumentFM::Operator& op=chan[c.chan].state.op[orderedOps[c.value]]; op.mult=c.value2&15; rWrite(baseAddr+ADDR_MULT_DT,(op.mult&15)|(dtTable[op.dt&7]<<4)); @@ -796,7 +796,7 @@ int DivPlatformYM2610B::dispatch(DivCommand c) { } case DIV_CMD_FM_TL: { if (c.chan>5) break; - unsigned short baseAddr=chanOffs_b[c.chan]|opOffs[orderedOps[c.value]]; + unsigned short baseAddr=chanOffs[c.chan]|opOffs[orderedOps[c.value]]; DivInstrumentFM::Operator& op=chan[c.chan].state.op[orderedOps[c.value]]; op.tl=c.value2; if (isOutput[chan[c.chan].state.alg][c.value]) { @@ -812,13 +812,13 @@ int DivPlatformYM2610B::dispatch(DivCommand c) { for (int i=0; i<4; i++) { DivInstrumentFM::Operator& op=chan[c.chan].state.op[i]; op.ar=c.value2&31; - unsigned short baseAddr=chanOffs_b[c.chan]|opOffs[i]; + unsigned short baseAddr=chanOffs[c.chan]|opOffs[i]; rWrite(baseAddr+ADDR_RS_AR,(op.ar&31)|(op.rs<<6)); } } else { DivInstrumentFM::Operator& op=chan[c.chan].state.op[orderedOps[c.value]]; op.ar=c.value2&31; - unsigned short baseAddr=chanOffs_b[c.chan]|opOffs[orderedOps[c.value]]; + unsigned short baseAddr=chanOffs[c.chan]|opOffs[orderedOps[c.value]]; rWrite(baseAddr+ADDR_RS_AR,(op.ar&31)|(op.rs<<6)); } break; @@ -920,13 +920,13 @@ void DivPlatformYM2610B::muteChannel(int ch, bool mute) { return; } // FM - rWrite(chanOffs_b[ch]+ADDR_LRAF,(isMuted[ch]?0:(chan[ch].pan<<6))|(chan[ch].state.fms&7)|((chan[ch].state.ams&3)<<4)); + rWrite(chanOffs[ch]+ADDR_LRAF,(isMuted[ch]?0:(chan[ch].pan<<6))|(chan[ch].state.fms&7)|((chan[ch].state.ams&3)<<4)); } void DivPlatformYM2610B::forceIns() { for (int i=0; i<6; i++) { for (int j=0; j<4; j++) { - unsigned short baseAddr=chanOffs_b[i]|opOffs[j]; + unsigned short baseAddr=chanOffs[i]|opOffs[j]; DivInstrumentFM::Operator& op=chan[i].state.op[j]; if (isOutput[chan[i].state.alg][j]) { rWrite(baseAddr+ADDR_TL,127-(((127-op.tl)*(chan[i].outVol&0x7f))/127)); @@ -940,8 +940,8 @@ void DivPlatformYM2610B::forceIns() { rWrite(baseAddr+ADDR_SL_RR,(op.rr&15)|(op.sl<<4)); rWrite(baseAddr+ADDR_SSG,op.ssgEnv&15); } - rWrite(chanOffs_b[i]+ADDR_FB_ALG,(chan[i].state.alg&7)|(chan[i].state.fb<<3)); - rWrite(chanOffs_b[i]+ADDR_LRAF,(isMuted[i]?0:(chan[i].pan<<6))|(chan[i].state.fms&7)|((chan[i].state.ams&3)<<4)); + rWrite(chanOffs[i]+ADDR_FB_ALG,(chan[i].state.alg&7)|(chan[i].state.fb<<3)); + 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) { chan[i].keyOn=true; chan[i].freqChanged=true; diff --git a/src/engine/platform/ym2610b.h b/src/engine/platform/ym2610b.h index f995d5975..7ca766c22 100644 --- a/src/engine/platform/ym2610b.h +++ b/src/engine/platform/ym2610b.h @@ -28,7 +28,7 @@ class DivPlatformYM2610B: public DivDispatch { protected: - const unsigned short chanOffs_b[6]={ + const unsigned short chanOffs[6]={ 0x00, 0x01, 0x02, 0x100, 0x101, 0x102 }; diff --git a/src/engine/platform/ym2610bext.cpp b/src/engine/platform/ym2610bext.cpp index 5c3649164..f48fe19ab 100644 --- a/src/engine/platform/ym2610bext.cpp +++ b/src/engine/platform/ym2610bext.cpp @@ -37,7 +37,7 @@ int DivPlatformYM2610BExt::dispatch(DivCommand c) { case DIV_CMD_NOTE_ON: { DivInstrument* ins=parent->getIns(opChan[ch].ins); - unsigned short baseAddr=chanOffs_b[2]|opOffs[ordch]; + unsigned short baseAddr=chanOffs[2]|opOffs[ordch]; DivInstrumentFM::Operator op=ins->fm.op[ordch]; // TODO: how does this work?! if (isOpMuted[ch]) { @@ -56,8 +56,8 @@ int DivPlatformYM2610BExt::dispatch(DivCommand c) { rWrite(baseAddr+0x90,op.ssgEnv&15); } if (opChan[ch].insChanged) { // TODO how does this work? - rWrite(chanOffs_b[2]+0xb0,(ins->fm.alg&7)|(ins->fm.fb<<3)); - rWrite(chanOffs_b[2]+0xb4,(opChan[ch].pan<<6)|(ins->fm.fms&7)|((ins->fm.ams&3)<<4)); + rWrite(chanOffs[2]+0xb0,(ins->fm.alg&7)|(ins->fm.fb<<3)); + rWrite(chanOffs[2]+0xb4,(opChan[ch].pan<<6)|(ins->fm.fms&7)|((ins->fm.ams&3)<<4)); } opChan[ch].insChanged=false; @@ -77,7 +77,7 @@ int DivPlatformYM2610BExt::dispatch(DivCommand c) { case DIV_CMD_VOLUME: { opChan[ch].vol=c.value; DivInstrument* ins=parent->getIns(opChan[ch].ins); - unsigned short baseAddr=chanOffs_b[2]|opOffs[ordch]; + unsigned short baseAddr=chanOffs[2]|opOffs[ordch]; DivInstrumentFM::Operator op=ins->fm.op[ordch]; if (isOpMuted[ch]) { rWrite(baseAddr+0x40,127); @@ -110,7 +110,7 @@ int DivPlatformYM2610BExt::dispatch(DivCommand c) { } DivInstrument* ins=parent->getIns(opChan[ch].ins); // TODO: ??? - rWrite(chanOffs_b[2]+0xb4,(opChan[ch].pan<<6)|(ins->fm.fms&7)|((ins->fm.ams&3)<<4)); + rWrite(chanOffs[2]+0xb4,(opChan[ch].pan<<6)|(ins->fm.fms&7)|((ins->fm.ams&3)<<4)); break; } case DIV_CMD_PITCH: { @@ -153,14 +153,14 @@ int DivPlatformYM2610BExt::dispatch(DivCommand c) { break; } case DIV_CMD_FM_MULT: { // TODO - unsigned short baseAddr=chanOffs_b[2]|opOffs[orderedOps[c.value]]; + unsigned short baseAddr=chanOffs[2]|opOffs[orderedOps[c.value]]; DivInstrument* ins=parent->getIns(opChan[ch].ins); DivInstrumentFM::Operator op=ins->fm.op[orderedOps[c.value]]; rWrite(baseAddr+0x30,(c.value2&15)|(dtTable[op.dt&7]<<4)); break; } case DIV_CMD_FM_TL: { // TODO - unsigned short baseAddr=chanOffs_b[2]|opOffs[orderedOps[c.value]]; + unsigned short baseAddr=chanOffs[2]|opOffs[orderedOps[c.value]]; DivInstrument* ins=parent->getIns(opChan[ch].ins); if (isOutput[ins->fm.alg][c.value]) { rWrite(baseAddr+0x40,127-(((127-c.value2)*(opChan[ch].vol&0x7f))/127)); @@ -174,12 +174,12 @@ int DivPlatformYM2610BExt::dispatch(DivCommand c) { if (c.value<0) { for (int i=0; i<4; i++) { DivInstrumentFM::Operator op=ins->fm.op[i]; - unsigned short baseAddr=chanOffs_b[2]|opOffs[i]; + unsigned short baseAddr=chanOffs[2]|opOffs[i]; rWrite(baseAddr+0x50,(c.value2&31)|(op.rs<<6)); } } else { DivInstrumentFM::Operator op=ins->fm.op[orderedOps[c.value]]; - unsigned short baseAddr=chanOffs_b[2]|opOffs[orderedOps[c.value]]; + unsigned short baseAddr=chanOffs[2]|opOffs[orderedOps[c.value]]; rWrite(baseAddr+0x50,(c.value2&31)|(op.rs<<6)); } break; @@ -264,7 +264,7 @@ void DivPlatformYM2610BExt::muteChannel(int ch, bool mute) { int ordch=orderedOps[ch-2]; DivInstrument* ins=parent->getIns(opChan[ch-2].ins); - unsigned short baseAddr=chanOffs_b[2]|opOffs[ordch]; + unsigned short baseAddr=chanOffs[2]|opOffs[ordch]; DivInstrumentFM::Operator op=ins->fm.op[ordch]; if (isOpMuted[ch-2]) { rWrite(baseAddr+0x40,127); diff --git a/src/engine/platform/ym2610shared.h b/src/engine/platform/ym2610shared.h index 7891a14fb..c41d5520a 100644 --- a/src/engine/platform/ym2610shared.h +++ b/src/engine/platform/ym2610shared.h @@ -17,9 +17,6 @@ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ -static unsigned short chanOffs[4]={ - 0x01, 0x02, 0x101, 0x102 -}; static unsigned short opOffs[4]={ 0x00, 0x04, 0x08, 0x0c }; From 34405de03c27cfc142a42df9a7590bb8914d0aca Mon Sep 17 00:00:00 2001 From: cam900 Date: Sun, 27 Feb 2022 06:40:13 +0900 Subject: [PATCH 07/50] Fix build actually --- src/engine/platform/ym2610b.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/engine/platform/ym2610b.cpp b/src/engine/platform/ym2610b.cpp index 839811920..770454a8d 100644 --- a/src/engine/platform/ym2610b.cpp +++ b/src/engine/platform/ym2610b.cpp @@ -565,7 +565,7 @@ int DivPlatformYM2610B::dispatch(DivCommand c) { chan[c.chan].state=ins->fm; } - for (int i=0; i<6; i++) { + 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 (isOutput[chan[c.chan].state.alg][i]) { From e243a8558eca663a6f266522a8fd5a41b0c59c60 Mon Sep 17 00:00:00 2001 From: cam900 Date: Sun, 27 Feb 2022 06:47:31 +0900 Subject: [PATCH 08/50] Add YM2610B in documents --- papers/doc/7-systems/ym2610.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/papers/doc/7-systems/ym2610.md b/papers/doc/7-systems/ym2610.md index dbe8b6a54..f557ea23d 100644 --- a/papers/doc/7-systems/ym2610.md +++ b/papers/doc/7-systems/ym2610.md @@ -4,6 +4,8 @@ originally an arcade board, but SNK shortly adapted it to a rather expensive vid its soundchip is a 3-in-1: FM, YM2149 (AY-3-8910 clone) and ADPCM in a single package! +YM2610B variant is YM2610 with 2 extra FM channels, it's mainly used at some 90s Taito Arcade hardwares. + # effects - `10xy`: set LFO parameters. From 2312ab19d248573036e1e713f5ced46f7c5f337a Mon Sep 17 00:00:00 2001 From: cam900 Date: Mon, 28 Feb 2022 03:04:04 +0900 Subject: [PATCH 09/50] Split YM2610B document --- papers/doc/7-systems/README.md | 1 + papers/doc/7-systems/ym2610.md | 2 -- papers/doc/7-systems/ym2610b.md | 57 +++++++++++++++++++++++++++++++++ 3 files changed, 58 insertions(+), 2 deletions(-) create mode 100644 papers/doc/7-systems/ym2610b.md diff --git a/papers/doc/7-systems/README.md b/papers/doc/7-systems/README.md index 90a17ee2d..78aeb8cea 100644 --- a/papers/doc/7-systems/README.md +++ b/papers/doc/7-systems/README.md @@ -10,6 +10,7 @@ this is a list of systems that Furnace supports, including each system's effects - [Commodore 64](c64.md) - [Arcade (YM2151/PCM)](arcade.md) - [Neo Geo/YM2610](ym2610.md) +- [Taito Arcade/YM2610B](ym2610b.md) - [AY-3-8910](ay8910.md) - [Amiga](amiga.md) - [Yamaha YM2612 standalone](ym2612.md) diff --git a/papers/doc/7-systems/ym2610.md b/papers/doc/7-systems/ym2610.md index f557ea23d..dbe8b6a54 100644 --- a/papers/doc/7-systems/ym2610.md +++ b/papers/doc/7-systems/ym2610.md @@ -4,8 +4,6 @@ originally an arcade board, but SNK shortly adapted it to a rather expensive vid its soundchip is a 3-in-1: FM, YM2149 (AY-3-8910 clone) and ADPCM in a single package! -YM2610B variant is YM2610 with 2 extra FM channels, it's mainly used at some 90s Taito Arcade hardwares. - # effects - `10xy`: set LFO parameters. diff --git a/papers/doc/7-systems/ym2610b.md b/papers/doc/7-systems/ym2610b.md new file mode 100644 index 000000000..0910d1d3d --- /dev/null +++ b/papers/doc/7-systems/ym2610b.md @@ -0,0 +1,57 @@ +# Taito Arcade/Yamaha YM2610 + +YM2610B is basically YM2610 with 2 extra FM channels used at some 90s Taito Arcade hardwares. + +# effects + +- `10xy`: set LFO parameters. + - `x` toggles the LFO. + - `y` sets its speed. +- `11xx`: set feedback of channel. +- `12xx`: set operator 1 level. +- `13xx`: set operator 2 level. +- `14xx`: set operator 3 level. +- `15xx`: set operator 4 level. +- `16xy`: set multiplier of operator. + - `x` is the operator (1-4). + - `y` is the mutliplier. +- `18xx`: toggle extended channel 2 mode. + - 0 disables it and 1 enables it. + - only in extended channel 2 system. +- `19xx`: set attack of all operators. +- `1Axx`: set attack of operator 1. +- `1Bxx`: set attack of operator 2. +- `1Cxx`: set attack of operator 3. +- `1Dxx`: set attack of operator 4. +- `20xx`: set SSG channel mode. `xx` may be one of the following: + - `00`: square + - `01`: noise + - `02`: square and noise + - `03`: nothing (apparently) + - `04`: envelope and square + - `05`: envelope and noise + - `06`: envelope and square and noise + - `07`: nothing +- `21xx`: set noise frequency. `xx` is a value between 00 and 1F. +- `22xy`: set envelope mode. + - `x` sets the envelope shape, which may be one of the following: + - `0: \___` decay + - `4: /___` attack once + - `8: \\\\` saw + - `9: \___` decay + - `A: \/\/` inverse obelisco + - `B: \ÂŻÂŻÂŻ` decay once + - `C: ////` inverse saw + - `D: /ÂŻÂŻÂŻ` attack + - `E: /\/\` obelisco + - `F: /___` attack once + - if `y` is 1 then the envelope will affect this channel. +- `23xx`: set envelope period low byte. +- `24xx`: set envelope period high byte. +- `25xx`: slide envelope period up. +- `26xx`: slide envelope period down. +- `29xy`: enable SSG auto-envelope mode. + - in this mode the envelope period is set to the channel's notes, multiplied by a fraction. + - `x` is the numerator. + - `y` is the denominator. + - if `x` or `y` are 0 this will disable auto-envelope mode. \ No newline at end of file From d92b662851bc39ff3d2fe51e71eb7c6c0993dc39 Mon Sep 17 00:00:00 2001 From: cam900 Date: Mon, 28 Feb 2022 03:05:31 +0900 Subject: [PATCH 10/50] Typo --- papers/doc/7-systems/ym2610b.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/papers/doc/7-systems/ym2610b.md b/papers/doc/7-systems/ym2610b.md index 0910d1d3d..da4f542d4 100644 --- a/papers/doc/7-systems/ym2610b.md +++ b/papers/doc/7-systems/ym2610b.md @@ -1,4 +1,4 @@ -# Taito Arcade/Yamaha YM2610 +# Taito Arcade/Yamaha YM2610B YM2610B is basically YM2610 with 2 extra FM channels used at some 90s Taito Arcade hardwares. From ac1c65fd62310e39f0444e87f60d8a8b96d03e84 Mon Sep 17 00:00:00 2001 From: cam900 Date: Mon, 28 Feb 2022 04:45:55 +0900 Subject: [PATCH 11/50] Add register sheet for YM2610* --- src/engine/platform/ym2610.cpp | 212 ++++++++++++++++++++++++ src/engine/platform/ym2610.h | 1 + src/engine/platform/ym2610b.cpp | 276 ++++++++++++++++++++++++++++++++ src/engine/platform/ym2610b.h | 1 + 4 files changed, 490 insertions(+) diff --git a/src/engine/platform/ym2610.cpp b/src/engine/platform/ym2610.cpp index e9e77d813..a6f29e4b8 100644 --- a/src/engine/platform/ym2610.cpp +++ b/src/engine/platform/ym2610.cpp @@ -32,6 +32,218 @@ static unsigned char konOffs[4]={ #define CHIP_DIVIDER 32 +const char* regCheatSheetYM2610[]={ + // SSG + "SSG_FreqL_A", "000", + "SSG_FreqH_A", "001", + "SSG_FreqL_B", "002", + "SSG_FreqH_B", "003", + "SSG_FreqL_C", "004", + "SSG_FreqH_C", "005", + "SSG_FreqNoise", "006", + "SSG_Enable", "007", + "SSG_Volume_A", "008", + "SSG_Volume_B", "009", + "SSG_Volume_C", "00A", + "SSG_FreqL_Env", "00B", + "SSG_FreqH_Env", "00C", + "SSG_Control_Env", "00D", + // ADPCM-B + "ADPCMB_Control", "010", + "ADPCMB_L_R", "011", + "ADPCMB_StartL", "012", + "ADPCMB_StartH", "013", + "ADPCMB_EndL", "014", + "ADPCMB_EndH", "015", + "ADPCMB_FreqL", "019", + "ADPCMB_FreqH", "01A", + "ADPCMB_Volume", "01B", + "ADPCM_Flag", "01C", + // FM (Common) + "FM_Test", "021", + "FM_LFOFreq", "022", + "ClockA1", "024", + "ClockA2", "025", + "ClockB", "026", + "FM_Control", "027", + "FM_NoteCtl", "028", + // FM (Channel 1-2) + "FM1_Op1_DT_MULT", "031", + "FM2_Op1_DT_MULT", "032", + "FM1_Op2_DT_MULT", "035", + "FM2_Op2_DT_MULT", "036", + "FM1_Op3_DT_MULT", "039", + "FM2_Op3_DT_MULT", "03A", + "FM1_Op4_DT_MULT", "03D", + "FM2_Op4_DT_MULT", "03E", + "FM1_Op1_TL", "041", + "FM2_Op1_TL", "042", + "FM1_Op2_TL", "045", + "FM2_Op2_TL", "046", + "FM1_Op3_TL", "049", + "FM2_Op3_TL", "04A", + "FM1_Op4_TL", "04D", + "FM2_Op4_TL", "04E", + "FM1_Op1_KS_AR", "051", + "FM2_Op1_KS_AR", "052", + "FM1_Op2_KS_AR", "055", + "FM2_Op2_KS_AR", "056", + "FM1_Op3_KS_AR", "059", + "FM2_Op3_KS_AR", "05A", + "FM1_Op4_KS_AR", "05D", + "FM2_Op4_KS_AR", "05E", + "FM1_Op1_AM_DR", "061", + "FM2_Op1_AM_DR", "062", + "FM1_Op2_AM_DR", "065", + "FM2_Op2_AM_DR", "066", + "FM1_Op3_AM_DR", "069", + "FM2_Op3_AM_DR", "06A", + "FM1_Op4_AM_DR", "06D", + "FM2_Op4_AM_DR", "06E", + "FM1_Op1_SR", "071", + "FM2_Op1_SR", "072", + "FM1_Op2_SR", "075", + "FM2_Op2_SR", "076", + "FM1_Op3_SR", "079", + "FM2_Op3_SR", "07A", + "FM1_Op4_SR", "07D", + "FM2_Op4_SR", "07E", + "FM1_Op1_SL_RR", "081", + "FM2_Op1_SL_RR", "082", + "FM1_Op2_SL_RR", "085", + "FM2_Op2_SL_RR", "086", + "FM1_Op3_SL_RR", "089", + "FM2_Op3_SL_RR", "08A", + "FM1_Op4_SL_RR", "08D", + "FM2_Op4_SL_RR", "08E", + "FM1_Op1_SSG_EG", "091", + "FM2_Op1_SSG_EG", "092", + "FM1_Op2_SSG_EG", "095", + "FM2_Op2_SSG_EG", "096", + "FM1_Op3_SSG_EG", "099", + "FM2_Op3_SSG_EG", "09A", + "FM1_Op4_SSG_EG", "09D", + "FM2_Op4_SSG_EG", "09E", + "FM1_FNum1", "0A1", + "FM2_(Op1)FNum1", "0A2", + "FM1_FNum2", "0A5", + "FM2_(Op1)FNum2", "0A6", + "FM2_Op2_FNum1", "0A8", + "FM2_Op3_FNum1", "0A9", + "FM2_Op4_FNum1", "0AA", + "FM2_Op2_FNum2", "0AC", + "FM2_Op3_FNum2", "0AD", + "FM2_Op4_FNum2", "0AE", + "FM1_FB_ALG", "0B1", + "FM2_FB_ALG", "0B2", + "FM1_Pan_LFO", "0B5", + "FM2_Pan_LFO", "0B6", + // ADPCM-A + "ADPCMA_Control", "100", + "ADPCMA_MVol", "101", + "ADPCMA_Test", "102", + "ADPCMA_Ch1_Vol", "108", + "ADPCMA_Ch2_Vol", "109", + "ADPCMA_Ch3_Vol", "10A", + "ADPCMA_Ch4_Vol", "10B", + "ADPCMA_Ch5_Vol", "10C", + "ADPCMA_Ch6_Vol", "10D", + "ADPCMA_Ch1_StL", "110", + "ADPCMA_Ch2_StL", "111", + "ADPCMA_Ch3_StL", "112", + "ADPCMA_Ch4_StL", "113", + "ADPCMA_Ch5_StL", "114", + "ADPCMA_Ch6_StL", "115", + "ADPCMA_Ch1_StH", "118", + "ADPCMA_Ch2_StH", "119", + "ADPCMA_Ch3_StH", "11A", + "ADPCMA_Ch4_StH", "11B", + "ADPCMA_Ch5_StH", "11C", + "ADPCMA_Ch6_StH", "11D", + "ADPCMA_Ch1_EdL", "120", + "ADPCMA_Ch2_EdL", "121", + "ADPCMA_Ch3_EdL", "122", + "ADPCMA_Ch4_EdL", "123", + "ADPCMA_Ch5_EdL", "124", + "ADPCMA_Ch6_EdL", "125", + "ADPCMA_Ch1_EdH", "128", + "ADPCMA_Ch2_EdH", "129", + "ADPCMA_Ch3_EdH", "12A", + "ADPCMA_Ch4_EdH", "12B", + "ADPCMA_Ch5_EdH", "12C", + "ADPCMA_Ch6_EdH", "12D", + // FM (Channel 3-4) + "FM3_Op1_DT_MULT", "131", + "FM4_Op1_DT_MULT", "132", + "FM3_Op2_DT_MULT", "135", + "FM4_Op2_DT_MULT", "136", + "FM3_Op3_DT_MULT", "139", + "FM4_Op3_DT_MULT", "13A", + "FM3_Op4_DT_MULT", "13D", + "FM4_Op4_DT_MULT", "13E", + "FM3_Op1_TL", "141", + "FM4_Op1_TL", "142", + "FM3_Op2_TL", "145", + "FM4_Op2_TL", "146", + "FM3_Op3_TL", "149", + "FM4_Op3_TL", "14A", + "FM3_Op4_TL", "14D", + "FM4_Op4_TL", "14E", + "FM3_Op1_KS_AR", "151", + "FM4_Op1_KS_AR", "152", + "FM3_Op2_KS_AR", "155", + "FM4_Op2_KS_AR", "156", + "FM3_Op3_KS_AR", "159", + "FM4_Op3_KS_AR", "15A", + "FM3_Op4_KS_AR", "15D", + "FM4_Op4_KS_AR", "15E", + "FM3_Op1_AM_DR", "161", + "FM4_Op1_AM_DR", "162", + "FM3_Op2_AM_DR", "165", + "FM4_Op2_AM_DR", "166", + "FM3_Op3_AM_DR", "169", + "FM4_Op3_AM_DR", "16A", + "FM3_Op4_AM_DR", "16D", + "FM4_Op4_AM_DR", "16E", + "FM3_Op1_SR", "171", + "FM4_Op1_SR", "172", + "FM3_Op2_SR", "175", + "FM4_Op2_SR", "176", + "FM3_Op3_SR", "179", + "FM4_Op3_SR", "17A", + "FM3_Op4_SR", "17D", + "FM4_Op4_SR", "17E", + "FM3_Op1_SL_RR", "181", + "FM4_Op1_SL_RR", "182", + "FM3_Op2_SL_RR", "185", + "FM4_Op2_SL_RR", "186", + "FM3_Op3_SL_RR", "189", + "FM4_Op3_SL_RR", "18A", + "FM3_Op4_SL_RR", "18D", + "FM4_Op4_SL_RR", "18E", + "FM3_Op1_SSG_EG", "191", + "FM4_Op1_SSG_EG", "192", + "FM3_Op2_SSG_EG", "195", + "FM4_Op2_SSG_EG", "196", + "FM3_Op3_SSG_EG", "199", + "FM4_Op3_SSG_EG", "19A", + "FM3_Op4_SSG_EG", "19D", + "FM4_Op4_SSG_EG", "19E", + "FM3_FNum1", "1A1", + "FM4_FNum1", "1A2", + "FM3_FNum2", "1A5", + "FM4_FNum2", "1A6", + "FM3_FB_ALG", "1B1", + "FM4_FB_ALG", "1B2", + "FM3_Pan_LFO", "1B5", + "FM4_Pan_LFO", "1B6", + NULL +}; + +const char** DivPlatformYM2610::getRegisterSheet() { + return regCheatSheetYM2610; +} + const char* DivPlatformYM2610::getEffectName(unsigned char effect) { switch (effect) { case 0x10: diff --git a/src/engine/platform/ym2610.h b/src/engine/platform/ym2610.h index 65456c720..a0ee8b8d4 100644 --- a/src/engine/platform/ym2610.h +++ b/src/engine/platform/ym2610.h @@ -127,6 +127,7 @@ class DivPlatformYM2610: public DivDispatch { void notifyInsDeletion(void* ins); void poke(unsigned int addr, unsigned short val); void poke(std::vector& wlist); + const char** getRegisterSheet(); const char* getEffectName(unsigned char effect); int init(DivEngine* parent, int channels, int sugRate, unsigned int flags); void quit(); diff --git a/src/engine/platform/ym2610b.cpp b/src/engine/platform/ym2610b.cpp index 770454a8d..edf9184d5 100644 --- a/src/engine/platform/ym2610b.cpp +++ b/src/engine/platform/ym2610b.cpp @@ -32,6 +32,282 @@ static unsigned char konOffs[6]={ #define CHIP_DIVIDER 32 +const char* regCheatSheetYM2610B[]={ + // SSG + "SSG_FreqL_A", "000", + "SSG_FreqH_A", "001", + "SSG_FreqL_B", "002", + "SSG_FreqH_B", "003", + "SSG_FreqL_C", "004", + "SSG_FreqH_C", "005", + "SSG_FreqNoise", "006", + "SSG_Enable", "007", + "SSG_Volume_A", "008", + "SSG_Volume_B", "009", + "SSG_Volume_C", "00A", + "SSG_FreqL_Env", "00B", + "SSG_FreqH_Env", "00C", + "SSG_Control_Env", "00D", + // ADPCM-B + "ADPCMB_Control", "010", + "ADPCMB_L_R", "011", + "ADPCMB_StartL", "012", + "ADPCMB_StartH", "013", + "ADPCMB_EndL", "014", + "ADPCMB_EndH", "015", + "ADPCMB_FreqL", "019", + "ADPCMB_FreqH", "01A", + "ADPCMB_Volume", "01B", + "ADPCM_Flag", "01C", + // FM (Common) + "FM_Test", "021", + "FM_LFOFreq", "022", + "ClockA1", "024", + "ClockA2", "025", + "ClockB", "026", + "FM_Control", "027", + "FM_NoteCtl", "028", + // FM (Channel 1-3) + "FM1_Op1_DT_MULT", "030", + "FM2_Op1_DT_MULT", "031", + "FM3_Op1_DT_MULT", "032", + "FM1_Op2_DT_MULT", "034", + "FM2_Op2_DT_MULT", "035", + "FM3_Op2_DT_MULT", "036", + "FM1_Op3_DT_MULT", "038", + "FM2_Op3_DT_MULT", "039", + "FM3_Op3_DT_MULT", "03A", + "FM1_Op4_DT_MULT", "03C", + "FM2_Op4_DT_MULT", "03D", + "FM3_Op4_DT_MULT", "03E", + "FM1_Op1_TL", "040", + "FM2_Op1_TL", "041", + "FM3_Op1_TL", "042", + "FM1_Op2_TL", "044", + "FM2_Op2_TL", "045", + "FM3_Op2_TL", "046", + "FM1_Op3_TL", "048", + "FM2_Op3_TL", "049", + "FM3_Op3_TL", "04A", + "FM1_Op4_TL", "04C", + "FM2_Op4_TL", "04D", + "FM3_Op4_TL", "04E", + "FM1_Op1_KS_AR", "050", + "FM2_Op1_KS_AR", "051", + "FM3_Op1_KS_AR", "052", + "FM1_Op2_KS_AR", "054", + "FM2_Op2_KS_AR", "055", + "FM3_Op2_KS_AR", "056", + "FM1_Op3_KS_AR", "058", + "FM2_Op3_KS_AR", "059", + "FM3_Op3_KS_AR", "05A", + "FM1_Op4_KS_AR", "05C", + "FM2_Op4_KS_AR", "05D", + "FM3_Op4_KS_AR", "05E", + "FM1_Op1_AM_DR", "060", + "FM2_Op1_AM_DR", "061", + "FM3_Op1_AM_DR", "062", + "FM1_Op2_AM_DR", "064", + "FM2_Op2_AM_DR", "065", + "FM3_Op2_AM_DR", "066", + "FM1_Op3_AM_DR", "068", + "FM2_Op3_AM_DR", "069", + "FM3_Op3_AM_DR", "06A", + "FM1_Op4_AM_DR", "06C", + "FM2_Op4_AM_DR", "06D", + "FM3_Op4_AM_DR", "06E", + "FM1_Op1_SR", "070", + "FM2_Op1_SR", "071", + "FM3_Op1_SR", "072", + "FM1_Op2_SR", "074", + "FM2_Op2_SR", "075", + "FM3_Op2_SR", "076", + "FM1_Op3_SR", "078", + "FM2_Op3_SR", "079", + "FM3_Op3_SR", "07A", + "FM1_Op4_SR", "07C", + "FM2_Op4_SR", "07D", + "FM3_Op4_SR", "07E", + "FM1_Op1_SL_RR", "080", + "FM2_Op1_SL_RR", "081", + "FM3_Op1_SL_RR", "082", + "FM1_Op2_SL_RR", "084", + "FM2_Op2_SL_RR", "085", + "FM3_Op2_SL_RR", "086", + "FM1_Op3_SL_RR", "088", + "FM2_Op3_SL_RR", "089", + "FM3_Op3_SL_RR", "08A", + "FM1_Op4_SL_RR", "08C", + "FM2_Op4_SL_RR", "08D", + "FM3_Op4_SL_RR", "08E", + "FM1_Op1_SSG_EG", "090", + "FM2_Op1_SSG_EG", "091", + "FM3_Op1_SSG_EG", "092", + "FM1_Op2_SSG_EG", "094", + "FM2_Op2_SSG_EG", "095", + "FM3_Op2_SSG_EG", "096", + "FM1_Op3_SSG_EG", "098", + "FM2_Op3_SSG_EG", "099", + "FM3_Op3_SSG_EG", "09A", + "FM1_Op4_SSG_EG", "09C", + "FM2_Op4_SSG_EG", "09D", + "FM3_Op4_SSG_EG", "09E", + "FM1_FNum1", "0A0", + "FM2_FNum1", "0A1", + "FM3_(Op1)FNum1", "0A2", + "FM1_FNum2", "0A4", + "FM2_FNum2", "0A5", + "FM3_(Op1)FNum2", "0A6", + "FM3_Op2_FNum1", "0A8", + "FM3_Op3_FNum1", "0A9", + "FM3_Op4_FNum1", "0AA", + "FM3_Op2_FNum2", "0AC", + "FM3_Op3_FNum2", "0AD", + "FM3_Op4_FNum2", "0AE", + "FM1_FB_ALG", "0B0", + "FM2_FB_ALG", "0B1", + "FM3_FB_ALG", "0B2", + "FM1_Pan_LFO", "0B4", + "FM2_Pan_LFO", "0B5", + "FM3_Pan_LFO", "0B6", + // ADPCM-A + "ADPCMA_Control", "100", + "ADPCMA_MVol", "101", + "ADPCMA_Test", "102", + "ADPCMA_Ch1_Vol", "108", + "ADPCMA_Ch2_Vol", "109", + "ADPCMA_Ch3_Vol", "10A", + "ADPCMA_Ch4_Vol", "10B", + "ADPCMA_Ch5_Vol", "10C", + "ADPCMA_Ch6_Vol", "10D", + "ADPCMA_Ch1_StL", "110", + "ADPCMA_Ch2_StL", "111", + "ADPCMA_Ch3_StL", "112", + "ADPCMA_Ch4_StL", "113", + "ADPCMA_Ch5_StL", "114", + "ADPCMA_Ch6_StL", "115", + "ADPCMA_Ch1_StH", "118", + "ADPCMA_Ch2_StH", "119", + "ADPCMA_Ch3_StH", "11A", + "ADPCMA_Ch4_StH", "11B", + "ADPCMA_Ch5_StH", "11C", + "ADPCMA_Ch6_StH", "11D", + "ADPCMA_Ch1_EdL", "120", + "ADPCMA_Ch2_EdL", "121", + "ADPCMA_Ch3_EdL", "122", + "ADPCMA_Ch4_EdL", "123", + "ADPCMA_Ch5_EdL", "124", + "ADPCMA_Ch6_EdL", "125", + "ADPCMA_Ch1_EdH", "128", + "ADPCMA_Ch2_EdH", "129", + "ADPCMA_Ch3_EdH", "12A", + "ADPCMA_Ch4_EdH", "12B", + "ADPCMA_Ch5_EdH", "12C", + "ADPCMA_Ch6_EdH", "12D", + // FM (Channel 4-6) + "FM4_Op1_DT_MULT", "130", + "FM5_Op1_DT_MULT", "131", + "FM6_Op1_DT_MULT", "132", + "FM4_Op2_DT_MULT", "134", + "FM5_Op2_DT_MULT", "135", + "FM6_Op2_DT_MULT", "136", + "FM4_Op3_DT_MULT", "138", + "FM5_Op3_DT_MULT", "139", + "FM6_Op3_DT_MULT", "13A", + "FM4_Op4_DT_MULT", "13C", + "FM5_Op4_DT_MULT", "13D", + "FM6_Op4_DT_MULT", "13E", + "FM4_Op1_TL", "140", + "FM5_Op1_TL", "141", + "FM6_Op1_TL", "142", + "FM4_Op2_TL", "144", + "FM5_Op2_TL", "145", + "FM6_Op2_TL", "146", + "FM4_Op3_TL", "148", + "FM5_Op3_TL", "149", + "FM6_Op3_TL", "14A", + "FM4_Op4_TL", "14C", + "FM5_Op4_TL", "14D", + "FM6_Op4_TL", "14E", + "FM4_Op1_KS_AR", "150", + "FM5_Op1_KS_AR", "151", + "FM6_Op1_KS_AR", "152", + "FM4_Op2_KS_AR", "154", + "FM5_Op2_KS_AR", "155", + "FM6_Op2_KS_AR", "156", + "FM4_Op3_KS_AR", "158", + "FM5_Op3_KS_AR", "159", + "FM6_Op3_KS_AR", "15A", + "FM4_Op4_KS_AR", "15C", + "FM5_Op4_KS_AR", "15D", + "FM6_Op4_KS_AR", "15E", + "FM4_Op1_AM_DR", "160", + "FM5_Op1_AM_DR", "161", + "FM6_Op1_AM_DR", "162", + "FM4_Op2_AM_DR", "164", + "FM5_Op2_AM_DR", "165", + "FM6_Op2_AM_DR", "166", + "FM4_Op3_AM_DR", "168", + "FM5_Op3_AM_DR", "169", + "FM6_Op3_AM_DR", "16A", + "FM4_Op4_AM_DR", "16C", + "FM5_Op4_AM_DR", "16D", + "FM6_Op4_AM_DR", "16E", + "FM4_Op1_SR", "170", + "FM5_Op1_SR", "171", + "FM6_Op1_SR", "172", + "FM4_Op2_SR", "174", + "FM5_Op2_SR", "175", + "FM6_Op2_SR", "176", + "FM4_Op3_SR", "178", + "FM5_Op3_SR", "179", + "FM6_Op3_SR", "17A", + "FM4_Op4_SR", "17C", + "FM5_Op4_SR", "17D", + "FM6_Op4_SR", "17E", + "FM4_Op1_SL_RR", "180", + "FM5_Op1_SL_RR", "181", + "FM6_Op1_SL_RR", "182", + "FM4_Op2_SL_RR", "184", + "FM5_Op2_SL_RR", "185", + "FM6_Op2_SL_RR", "186", + "FM4_Op3_SL_RR", "188", + "FM5_Op3_SL_RR", "189", + "FM6_Op3_SL_RR", "18A", + "FM4_Op4_SL_RR", "18C", + "FM5_Op4_SL_RR", "18D", + "FM6_Op4_SL_RR", "18E", + "FM4_Op1_SSG_EG", "190", + "FM5_Op1_SSG_EG", "191", + "FM6_Op1_SSG_EG", "192", + "FM4_Op2_SSG_EG", "194", + "FM5_Op2_SSG_EG", "195", + "FM6_Op2_SSG_EG", "196", + "FM4_Op3_SSG_EG", "198", + "FM5_Op3_SSG_EG", "199", + "FM6_Op3_SSG_EG", "19A", + "FM4_Op4_SSG_EG", "19C", + "FM5_Op4_SSG_EG", "19D", + "FM6_Op4_SSG_EG", "19E", + "FM4_FNum1", "1A0", + "FM5_FNum1", "1A1", + "FM6_FNum1", "1A2", + "FM4_FNum2", "1A4", + "FM5_FNum2", "1A5", + "FM6_FNum2", "1A6", + "FM4_FB_ALG", "1B0", + "FM5_FB_ALG", "1B1", + "FM6_FB_ALG", "1B2", + "FM4_Pan_LFO", "1B4", + "FM5_Pan_LFO", "1B5", + "FM6_Pan_LFO", "1B6", + NULL +}; + +const char** DivPlatformYM2610B::getRegisterSheet() { + return regCheatSheetYM2610B; +} + const char* DivPlatformYM2610B::getEffectName(unsigned char effect) { switch (effect) { case 0x10: diff --git a/src/engine/platform/ym2610b.h b/src/engine/platform/ym2610b.h index 7ca766c22..ca43e36b4 100644 --- a/src/engine/platform/ym2610b.h +++ b/src/engine/platform/ym2610b.h @@ -120,6 +120,7 @@ class DivPlatformYM2610B: public DivDispatch { void notifyInsDeletion(void* ins); void poke(unsigned int addr, unsigned short val); void poke(std::vector& wlist); + const char** getRegisterSheet(); const char* getEffectName(unsigned char effect); int init(DivEngine* parent, int channels, int sugRate, unsigned int flags); void quit(); From ddf341dfc095479b782ce12baa4f94e4322c21a8 Mon Sep 17 00:00:00 2001 From: cam900 Date: Tue, 1 Mar 2022 01:11:27 +0900 Subject: [PATCH 12/50] Add support YM2610* ADPCM-B Repeat flag, Add YM2610 ADPCM notes in GUI --- src/engine/platform/ym2610.cpp | 4 ++-- src/engine/platform/ym2610b.cpp | 4 ++-- src/gui/gui.cpp | 11 +++++++++-- 3 files changed, 13 insertions(+), 6 deletions(-) diff --git a/src/engine/platform/ym2610.cpp b/src/engine/platform/ym2610.cpp index a6f29e4b8..dd2447da4 100644 --- a/src/engine/platform/ym2610.cpp +++ b/src/engine/platform/ym2610.cpp @@ -698,7 +698,7 @@ int DivPlatformYM2610::dispatch(DivCommand c) { immWrite(0x14,(end>>8)&0xff); immWrite(0x15,end>>16); immWrite(0x11,isMuted[c.chan]?0:(chan[c.chan].pan<<6)); - immWrite(0x10,0x80); // start + immWrite(0x10,(s->loopStart>=0)?0x90:0x80); // start/repeat if (c.value!=DIV_NOTE_NULL) { chan[c.chan].note=c.value; chan[c.chan].baseFreq=NOTE_ADPCMB(chan[c.chan].note); @@ -724,7 +724,7 @@ int DivPlatformYM2610::dispatch(DivCommand c) { immWrite(0x14,(end>>8)&0xff); immWrite(0x15,end>>16); immWrite(0x11,isMuted[c.chan]?0:(chan[c.chan].pan<<6)); - immWrite(0x10,0x80); // start + immWrite(0x10,(s->loopStart>=0)?0x90:0x80); // start/repeat chan[c.chan].baseFreq=(((unsigned int)s->rate)<<16)/(chipClock/144); chan[c.chan].freqChanged=true; } diff --git a/src/engine/platform/ym2610b.cpp b/src/engine/platform/ym2610b.cpp index edf9184d5..27bb2afbb 100644 --- a/src/engine/platform/ym2610b.cpp +++ b/src/engine/platform/ym2610b.cpp @@ -761,7 +761,7 @@ int DivPlatformYM2610B::dispatch(DivCommand c) { immWrite(0x14,(end>>8)&0xff); immWrite(0x15,end>>16); immWrite(0x11,isMuted[c.chan]?0:(chan[c.chan].pan<<6)); - immWrite(0x10,0x80); // start + immWrite(0x10,(s->loopStart>=0)?0x90:0x80); // start/repeat if (c.value!=DIV_NOTE_NULL) { chan[c.chan].note=c.value; chan[c.chan].baseFreq=NOTE_ADPCMB(chan[c.chan].note); @@ -787,7 +787,7 @@ int DivPlatformYM2610B::dispatch(DivCommand c) { immWrite(0x14,(end>>8)&0xff); immWrite(0x15,end>>16); immWrite(0x11,isMuted[c.chan]?0:(chan[c.chan].pan<<6)); - immWrite(0x10,0x80); // start + immWrite(0x10,(s->loopStart>=0)?0x90:0x80); // start/repeat chan[c.chan].baseFreq=(((unsigned int)s->rate)<<16)/(chipClock/144); chan[c.chan].freqChanged=true; } diff --git a/src/gui/gui.cpp b/src/gui/gui.cpp index 265624684..034afb4ac 100644 --- a/src/gui/gui.cpp +++ b/src/gui/gui.cpp @@ -1309,18 +1309,25 @@ void FurnaceGUI::drawSampleEdit() { ImGui::Text("notes:"); if (sample->loopStart>=0) { considerations=true; - ImGui::Text("- sample won't loop on Neo Geo ADPCM"); + ImGui::Text("- sample won't loop on Neo Geo ADPCM-A"); if (sample->loopStart&1) { ImGui::Text("- sample loop start will be aligned to the nearest even sample on Amiga"); } + if (sample->loopStart>0) { + ImGui::Text("- sample loop start will be ignored on Neo Geo ADPCM-B"); + } } if (sample->samples&1) { considerations=true; ImGui::Text("- sample length will be aligned to the nearest even sample on Amiga"); } + if (sample->samples&511) { + considerations=true; + ImGui::Text("- sample length will be aligned to 512 sample on Neo Geo ADPCM"); + } if (sample->samples>65535) { considerations=true; - ImGui::Text("- maximum sample length on Sega PCM is 65536 samples"); + ImGui::Text("- maximum sample length on Sega PCM and QSound is 65536 samples"); } if (sample->samples>2097151) { considerations=true; From 4e5b398994665f91d002557b673d8dafd6b8baa2 Mon Sep 17 00:00:00 2001 From: cam900 Date: Tue, 1 Mar 2022 02:11:02 +0900 Subject: [PATCH 13/50] Fix notes --- src/gui/gui.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/gui/gui.cpp b/src/gui/gui.cpp index 034afb4ac..ec566676b 100644 --- a/src/gui/gui.cpp +++ b/src/gui/gui.cpp @@ -1323,7 +1323,7 @@ void FurnaceGUI::drawSampleEdit() { } if (sample->samples&511) { considerations=true; - ImGui::Text("- sample length will be aligned to 512 sample on Neo Geo ADPCM"); + ImGui::Text("- sample length will be aligned and padded to 512 sample units on Neo Geo ADPCM."); } if (sample->samples>65535) { considerations=true; From de8c79e3062ba05abf2a4f88f455969a256ca4f9 Mon Sep 17 00:00:00 2001 From: cam900 Date: Tue, 1 Mar 2022 02:50:26 +0900 Subject: [PATCH 14/50] Further informations --- papers/doc/7-systems/ym2610.md | 2 +- papers/doc/7-systems/ym2610b.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/papers/doc/7-systems/ym2610.md b/papers/doc/7-systems/ym2610.md index dbe8b6a54..7eb66eb33 100644 --- a/papers/doc/7-systems/ym2610.md +++ b/papers/doc/7-systems/ym2610.md @@ -2,7 +2,7 @@ originally an arcade board, but SNK shortly adapted it to a rather expensive video game console with the world's biggest cartridges because some people liked the system so much they wanted a home version of it. -its soundchip is a 3-in-1: FM, YM2149 (AY-3-8910 clone) and ADPCM in a single package! +its soundchip is a 3-in-1: FM, YM2149 (AY-3-8910 clone) and 2 different format ADPCM in a single package! # effects diff --git a/papers/doc/7-systems/ym2610b.md b/papers/doc/7-systems/ym2610b.md index da4f542d4..2b2e5feaf 100644 --- a/papers/doc/7-systems/ym2610b.md +++ b/papers/doc/7-systems/ym2610b.md @@ -1,6 +1,6 @@ # Taito Arcade/Yamaha YM2610B -YM2610B is basically YM2610 with 2 extra FM channels used at some 90s Taito Arcade hardwares. +YM2610B is basically YM2610 with 2 extra FM channels used at some 90s Taito Arcade hardwares, it's backward compatible with non-B chip. # effects From bd9289cfdd5269f84a6aecdea441ea3e89f2e232 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Mon, 28 Feb 2022 18:16:05 -0500 Subject: [PATCH 15/50] store OP macros todo --- .../imgui_patched/imgui_impl_sdlrenderer.cpp | 2 + papers/format.md | 42 ++++ src/engine/engine.h | 4 +- src/engine/instrument.cpp | 124 +++++++++++ src/gui/gui.h | 2 +- src/gui/insEdit.cpp | 199 ++++++++++-------- 6 files changed, 287 insertions(+), 86 deletions(-) diff --git a/extern/imgui_patched/imgui_impl_sdlrenderer.cpp b/extern/imgui_patched/imgui_impl_sdlrenderer.cpp index ae034179c..89c87f62b 100644 --- a/extern/imgui_patched/imgui_impl_sdlrenderer.cpp +++ b/extern/imgui_patched/imgui_impl_sdlrenderer.cpp @@ -26,6 +26,7 @@ #include "imgui.h" #include "imgui_impl_sdlrenderer.h" +#include #if defined(_MSC_VER) && _MSC_VER <= 1500 // MSVC 2008 or earlier #include // intptr_t #else @@ -184,6 +185,7 @@ void ImGui_ImplSDLRenderer_RenderDrawData(ImDrawData* draw_data) // Bind texture, Draw SDL_Texture* tex = (SDL_Texture*)pcmd->GetTexID(); + SDL_SetTextureScaleMode(tex, SDL_ScaleModeBest); // ??? SDL_RenderGeometryRaw(bd->SDLRenderer, tex, xy, (int)sizeof(ImDrawVert), color, (int)sizeof(ImDrawVert), diff --git a/papers/format.md b/papers/format.md index 882efa7bd..1ca356b72 100644 --- a/papers/format.md +++ b/papers/format.md @@ -426,6 +426,48 @@ size | description 4 | DT macro release 4 | D2R macro release 4 | SSG-EG macro release + --- | **extended op macro headers** Ă— 4 (>=61) + 4 | DAM macro length + 4 | DVB macro length + 4 | EGT macro length + 4 | KSL macro length + 4 | SUS macro length + 4 | VIB macro length + 4 | WS macro length + 4 | KSR macro length + 4 | DAM macro loop + 4 | DVB macro loop + 4 | EGT macro loop + 4 | KSL macro loop + 4 | SUS macro loop + 4 | VIB macro loop + 4 | WS macro loop + 4 | KSR macro loop + 4 | DAM macro release + 4 | DVB macro release + 4 | EGT macro release + 4 | KSL macro release + 4 | SUS macro release + 4 | VIB macro release + 4 | WS macro release + 4 | KSR macro release + 1 | DAM macro open + 1 | DVB macro open + 1 | EGT macro open + 1 | KSL macro open + 1 | SUS macro open + 1 | VIB macro open + 1 | WS macro open + 1 | KSR macro open + --- | **extended op macros** Ă— 4 (>=61) + 1?? | DAM macro + 1?? | DVB macro + 1?? | EGT macro + 1?? | KSL macro + 1?? | SUS macro + 1?? | VIB macro + 1?? | WS macro + 1?? | KSR macro ``` # wavetable diff --git a/src/engine/engine.h b/src/engine/engine.h index f0ef1833a..2f060f14f 100644 --- a/src/engine/engine.h +++ b/src/engine/engine.h @@ -37,8 +37,8 @@ warnings+=(String("\n")+x); \ } -#define DIV_VERSION "dev60" -#define DIV_ENGINE_VERSION 60 +#define DIV_VERSION "dev61" +#define DIV_ENGINE_VERSION 61 enum DivStatusView { DIV_STATUS_NOTHING=0, diff --git a/src/engine/instrument.cpp b/src/engine/instrument.cpp index fa5564c93..d5933981d 100644 --- a/src/engine/instrument.cpp +++ b/src/engine/instrument.cpp @@ -302,6 +302,75 @@ void DivInstrument::putInsData(SafeWriter* w) { w->writeI(op.d2rMacroRel); w->writeI(op.ssgMacroRel); } + + // extended op macros + for (int i=0; i<4; i++) { + DivInstrumentSTD::OpMacro& op=std.opMacros[i]; + + w->writeI(op.damMacroLen); + w->writeI(op.dvbMacroLen); + w->writeI(op.egtMacroLen); + w->writeI(op.kslMacroLen); + w->writeI(op.susMacroLen); + w->writeI(op.vibMacroLen); + w->writeI(op.wsMacroLen); + w->writeI(op.ksrMacroLen); + + w->writeI(op.damMacroLoop); + w->writeI(op.dvbMacroLoop); + w->writeI(op.egtMacroLoop); + w->writeI(op.kslMacroLoop); + w->writeI(op.susMacroLoop); + w->writeI(op.vibMacroLoop); + w->writeI(op.wsMacroLoop); + w->writeI(op.ksrMacroLoop); + + w->writeI(op.damMacroRel); + w->writeI(op.dvbMacroRel); + w->writeI(op.egtMacroRel); + w->writeI(op.kslMacroRel); + w->writeI(op.susMacroRel); + w->writeI(op.vibMacroRel); + w->writeI(op.wsMacroRel); + w->writeI(op.ksrMacroRel); + + w->writeC(op.damMacroOpen); + w->writeC(op.dvbMacroOpen); + w->writeC(op.egtMacroOpen); + w->writeC(op.kslMacroOpen); + w->writeC(op.susMacroOpen); + w->writeC(op.vibMacroOpen); + w->writeC(op.wsMacroOpen); + w->writeC(op.ksrMacroOpen); + } + + for (int i=0; i<4; i++) { + DivInstrumentSTD::OpMacro& op=std.opMacros[i]; + for (int j=0; jwriteC(op.damMacro[j]); + } + for (int j=0; jwriteC(op.dvbMacro[j]); + } + for (int j=0; jwriteC(op.egtMacro[j]); + } + for (int j=0; jwriteC(op.kslMacro[j]); + } + for (int j=0; jwriteC(op.susMacro[j]); + } + for (int j=0; jwriteC(op.vibMacro[j]); + } + for (int j=0; jwriteC(op.wsMacro[j]); + } + for (int j=0; jwriteC(op.ksrMacro[j]); + } + } } DivDataErrors DivInstrument::readInsData(SafeReader& reader, short version) { @@ -570,6 +639,61 @@ DivDataErrors DivInstrument::readInsData(SafeReader& reader, short version) { } } + // extended op macros + if (version>=61) { + for (int i=0; i<4; i++) { + DivInstrumentSTD::OpMacro& op=std.opMacros[i]; + + op.damMacroLen=reader.readI(); + op.dvbMacroLen=reader.readI(); + op.egtMacroLen=reader.readI(); + op.kslMacroLen=reader.readI(); + op.susMacroLen=reader.readI(); + op.vibMacroLen=reader.readI(); + op.wsMacroLen=reader.readI(); + op.ksrMacroLen=reader.readI(); + + op.damMacroLoop=reader.readI(); + op.dvbMacroLoop=reader.readI(); + op.egtMacroLoop=reader.readI(); + op.kslMacroLoop=reader.readI(); + op.susMacroLoop=reader.readI(); + op.vibMacroLoop=reader.readI(); + op.wsMacroLoop=reader.readI(); + op.ksrMacroLoop=reader.readI(); + + op.damMacroRel=reader.readI(); + op.dvbMacroRel=reader.readI(); + op.egtMacroRel=reader.readI(); + op.kslMacroRel=reader.readI(); + op.susMacroRel=reader.readI(); + op.vibMacroRel=reader.readI(); + op.wsMacroRel=reader.readI(); + op.ksrMacroRel=reader.readI(); + + op.damMacroOpen=reader.readC(); + op.dvbMacroOpen=reader.readC(); + op.egtMacroOpen=reader.readC(); + op.kslMacroOpen=reader.readC(); + op.susMacroOpen=reader.readC(); + op.vibMacroOpen=reader.readC(); + op.wsMacroOpen=reader.readC(); + op.ksrMacroOpen=reader.readC(); + } + + for (int i=0; i<4; i++) { + DivInstrumentSTD::OpMacro& op=std.opMacros[i]; + reader.read(op.damMacro,op.damMacroLen); + reader.read(op.dvbMacro,op.dvbMacroLen); + reader.read(op.egtMacro,op.egtMacroLen); + reader.read(op.kslMacro,op.kslMacroLen); + reader.read(op.susMacro,op.susMacroLen); + reader.read(op.vibMacro,op.vibMacroLen); + reader.read(op.wsMacro,op.wsMacroLen); + reader.read(op.ksrMacro,op.ksrMacroLen); + } + } + return DIV_DATA_SUCCESS; } diff --git a/src/gui/gui.h b/src/gui/gui.h index 6eb591754..4cae110dc 100644 --- a/src/gui/gui.h +++ b/src/gui/gui.h @@ -626,7 +626,7 @@ class FurnaceGUI { int lastIns[DIV_MAX_CHANS]; void drawAlgorithm(unsigned char alg, FurnaceGUIFMAlgs algType, const ImVec2& size); - void drawFMEnv(unsigned char tl, unsigned char ar, unsigned char dr, unsigned char d2r, unsigned char rr, unsigned char sl, const ImVec2& size); + void drawFMEnv(unsigned char tl, unsigned char ar, unsigned char dr, unsigned char d2r, unsigned char rr, unsigned char sl, float maxTl, float maxArDr, const ImVec2& size); void updateWindowTitle(); void prepareLayout(); diff --git a/src/gui/insEdit.cpp b/src/gui/insEdit.cpp index 4790d61c1..a8558bb07 100644 --- a/src/gui/insEdit.cpp +++ b/src/gui/insEdit.cpp @@ -148,6 +148,10 @@ const char* mikeyFeedbackBits[11] = { "0", "1", "2", "3", "4", "5", "7", "10", "11", "int", NULL }; +const char* oneBit[2]={ + "on", NULL +}; + const int orderedOps[4]={ 0, 2, 1, 3 }; @@ -183,6 +187,13 @@ String macroLFOWaves(int id, float val) { return "???"; } +void addAALine(ImDrawList* dl, const ImVec2& p1, const ImVec2& p2, const ImU32 color, float thickness=1.0f) { + ImVec2 pt[2]; + pt[0]=p1; + pt[1]=p2; + dl->AddPolyline(pt,2,color,ImDrawFlags_None,thickness); +} + void FurnaceGUI::drawAlgorithm(unsigned char alg, FurnaceGUIFMAlgs algType, const ImVec2& size) { ImDrawList* dl=ImGui::GetWindowDrawList(); ImGuiWindow* window=ImGui::GetCurrentWindow(); @@ -199,7 +210,6 @@ void FurnaceGUI::drawAlgorithm(unsigned char alg, FurnaceGUIFMAlgs algType, cons ImGui::ItemSize(size,style.FramePadding.y); if (ImGui::ItemAdd(rect,ImGui::GetID("alg"))) { ImGui::RenderFrame(rect.Min,rect.Max,ImGui::GetColorU32(ImGuiCol_FrameBg),true,style.FrameRounding); - //ImReallyTiredOfThisGarbage(); const float circleRadius=6.0f*dpiScale+1.0f; switch (algType) { case FM_ALGS_4OP: @@ -211,11 +221,11 @@ void FurnaceGUI::drawAlgorithm(unsigned char alg, FurnaceGUIFMAlgs algType, cons ImVec2 pos4=ImLerp(rect.Min,rect.Max,ImVec2(0.8,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); + addAALine(dl,pos1,pos2,colorL); dl->AddCircleFilled(pos2,4.0f*dpiScale+1.0f,color); - dl->AddLine(pos2,pos3,colorL); + addAALine(dl,pos2,pos3,colorL); dl->AddCircleFilled(pos3,4.0f*dpiScale+1.0f,color); - dl->AddLine(pos3,pos4,colorL); + addAALine(dl,pos3,pos4,colorL); dl->AddCircleFilled(pos4,4.0f*dpiScale+1.0f,color); pos1.x-=ImGui::CalcTextSize("1").x*0.5; @@ -239,11 +249,11 @@ void FurnaceGUI::drawAlgorithm(unsigned char alg, FurnaceGUIFMAlgs algType, cons ImVec2 pos4=ImLerp(rect.Min,rect.Max,ImVec2(0.75,0.5)); dl->AddCircleFilled(pos1,4.0f*dpiScale+1.0f,color); dl->AddCircle(pos1,6.0f*dpiScale+1.0f,color); - dl->AddLine(pos1,pos3,colorL); + addAALine(dl,pos1,pos3,colorL); dl->AddCircleFilled(pos2,4.0f*dpiScale+1.0f,color); - dl->AddLine(pos2,pos3,colorL); + addAALine(dl,pos2,pos3,colorL); dl->AddCircleFilled(pos3,4.0f*dpiScale+1.0f,color); - dl->AddLine(pos3,pos4,colorL); + addAALine(dl,pos3,pos4,colorL); dl->AddCircleFilled(pos4,4.0f*dpiScale+1.0f,color); pos2.x-=ImGui::CalcTextSize("2").x+circleRadius+3.0*dpiScale; @@ -267,11 +277,11 @@ void FurnaceGUI::drawAlgorithm(unsigned char alg, FurnaceGUIFMAlgs algType, cons ImVec2 pos4=ImLerp(rect.Min,rect.Max,ImVec2(0.75,0.5)); dl->AddCircleFilled(pos1,4.0f*dpiScale+1.0f,color); dl->AddCircle(pos1,6.0f*dpiScale+1.0f,color); - dl->AddLine(pos1,pos4,colorL); + addAALine(dl,pos1,pos4,colorL); dl->AddCircleFilled(pos2,4.0f*dpiScale+1.0f,color); - dl->AddLine(pos2,pos3,colorL); + addAALine(dl,pos2,pos3,colorL); dl->AddCircleFilled(pos3,4.0f*dpiScale+1.0f,color); - dl->AddLine(pos3,pos4,colorL); + addAALine(dl,pos3,pos4,colorL); dl->AddCircleFilled(pos4,4.0f*dpiScale+1.0f,color); pos1.x-=ImGui::CalcTextSize("2").x+circleRadius+3.0*dpiScale; @@ -295,11 +305,11 @@ void FurnaceGUI::drawAlgorithm(unsigned char alg, FurnaceGUIFMAlgs algType, cons ImVec2 pos4=ImLerp(rect.Min,rect.Max,ImVec2(0.75,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); + addAALine(dl,pos1,pos2,colorL); dl->AddCircleFilled(pos2,4.0f*dpiScale+1.0f,color); - dl->AddLine(pos2,pos4,colorL); + addAALine(dl,pos2,pos4,colorL); dl->AddCircleFilled(pos3,4.0f*dpiScale+1.0f,color); - dl->AddLine(pos3,pos4,colorL); + addAALine(dl,pos3,pos4,colorL); dl->AddCircleFilled(pos4,4.0f*dpiScale+1.0f,color); pos1.x-=ImGui::CalcTextSize("2").x+circleRadius+3.0*dpiScale; @@ -324,13 +334,13 @@ void FurnaceGUI::drawAlgorithm(unsigned char alg, FurnaceGUIFMAlgs algType, cons ImVec2 pos5=ImLerp(rect.Min,rect.Max,ImVec2(0.75,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); + addAALine(dl,pos1,pos2,colorL); dl->AddCircleFilled(pos2,4.0f*dpiScale+1.0f,color); dl->AddCircleFilled(pos3,4.0f*dpiScale+1.0f,color); - dl->AddLine(pos3,pos4,colorL); + addAALine(dl,pos3,pos4,colorL); dl->AddCircleFilled(pos4,4.0f*dpiScale+1.0f,color); - dl->AddLine(pos2,pos5,colorL); - dl->AddLine(pos4,pos5,colorL); + addAALine(dl,pos2,pos5,colorL); + addAALine(dl,pos4,pos5,colorL); pos1.x-=ImGui::CalcTextSize("2").x+circleRadius+3.0*dpiScale; pos2.x-=ImGui::CalcTextSize("2").x+circleRadius+3.0*dpiScale; @@ -354,15 +364,15 @@ void FurnaceGUI::drawAlgorithm(unsigned char alg, FurnaceGUIFMAlgs algType, cons ImVec2 pos5=ImLerp(rect.Min,rect.Max,ImVec2(0.75,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->AddLine(pos1,pos3,colorL); - dl->AddLine(pos1,pos4,colorL); + addAALine(dl,pos1,pos2,colorL); + addAALine(dl,pos1,pos3,colorL); + addAALine(dl,pos1,pos4,colorL); dl->AddCircleFilled(pos2,4.0f*dpiScale+1.0f,color); dl->AddCircleFilled(pos3,4.0f*dpiScale+1.0f,color); dl->AddCircleFilled(pos4,4.0f*dpiScale+1.0f,color); - dl->AddLine(pos2,pos5,colorL); - dl->AddLine(pos3,pos5,colorL); - dl->AddLine(pos4,pos5,colorL); + addAALine(dl,pos2,pos5,colorL); + addAALine(dl,pos3,pos5,colorL); + addAALine(dl,pos4,pos5,colorL); pos1.x-=ImGui::CalcTextSize("2").x+circleRadius+3.0*dpiScale; pos2.x-=ImGui::CalcTextSize("2").x+circleRadius+3.0*dpiScale; @@ -386,13 +396,13 @@ void FurnaceGUI::drawAlgorithm(unsigned char alg, FurnaceGUIFMAlgs algType, cons ImVec2 pos5=ImLerp(rect.Min,rect.Max,ImVec2(0.75,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); + addAALine(dl,pos1,pos2,colorL); dl->AddCircleFilled(pos2,4.0f*dpiScale+1.0f,color); dl->AddCircleFilled(pos3,4.0f*dpiScale+1.0f,color); dl->AddCircleFilled(pos4,4.0f*dpiScale+1.0f,color); - dl->AddLine(pos2,pos5,colorL); - dl->AddLine(pos3,pos5,colorL); - dl->AddLine(pos4,pos5,colorL); + addAALine(dl,pos2,pos5,colorL); + addAALine(dl,pos3,pos5,colorL); + addAALine(dl,pos4,pos5,colorL); pos1.x-=ImGui::CalcTextSize("2").x+circleRadius+3.0*dpiScale; pos2.x-=ImGui::CalcTextSize("2").x+circleRadius+3.0*dpiScale; @@ -419,10 +429,10 @@ void FurnaceGUI::drawAlgorithm(unsigned char alg, FurnaceGUIFMAlgs algType, cons dl->AddCircleFilled(pos2,4.0f*dpiScale+1.0f,color); dl->AddCircleFilled(pos3,4.0f*dpiScale+1.0f,color); dl->AddCircleFilled(pos4,4.0f*dpiScale+1.0f,color); - dl->AddLine(pos1,pos5,colorL); - dl->AddLine(pos2,pos5,colorL); - dl->AddLine(pos3,pos5,colorL); - dl->AddLine(pos4,pos5,colorL); + addAALine(dl,pos1,pos5,colorL); + addAALine(dl,pos2,pos5,colorL); + addAALine(dl,pos3,pos5,colorL); + addAALine(dl,pos4,pos5,colorL); pos1.x-=ImGui::CalcTextSize("2").x+circleRadius+3.0*dpiScale; pos2.x-=ImGui::CalcTextSize("2").x+circleRadius+3.0*dpiScale; @@ -447,7 +457,7 @@ void FurnaceGUI::drawAlgorithm(unsigned char alg, FurnaceGUIFMAlgs algType, cons 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); + addAALine(dl,pos1,pos2,colorL); dl->AddCircleFilled(pos2,4.0f*dpiScale+1.0f,color); pos1.x-=ImGui::CalcTextSize("2").x+circleRadius+3.0*dpiScale; @@ -481,7 +491,7 @@ void FurnaceGUI::drawAlgorithm(unsigned char alg, FurnaceGUIFMAlgs algType, cons } } -void FurnaceGUI::drawFMEnv(unsigned char tl, unsigned char ar, unsigned char dr, unsigned char d2r, unsigned char rr, unsigned char sl, const ImVec2& size) { +void FurnaceGUI::drawFMEnv(unsigned char tl, unsigned char ar, unsigned char dr, unsigned char d2r, unsigned char rr, unsigned char sl, float maxTl, float maxArDr, const ImVec2& size) { ImDrawList* dl=ImGui::GetWindowDrawList(); ImGuiWindow* window=ImGui::GetCurrentWindow(); @@ -499,8 +509,8 @@ void FurnaceGUI::drawFMEnv(unsigned char tl, unsigned char ar, unsigned char dr, ImGui::RenderFrame(rect.Min,rect.Max,ImGui::GetColorU32(ImGuiCol_FrameBg),true,style.FrameRounding); //calculate x positions - float arPos=float(31-ar)/31.0; //peak of AR, start of DR - float drPos=arPos+((sl/15.0)*(float(31-dr)/31.0)); //end of DR, start of D2R + float arPos=float(maxArDr-ar)/maxArDr; //peak of AR, start of DR + float drPos=arPos+((sl/15.0)*(float(maxArDr-dr)/maxArDr)); //end of DR, start of D2R float d2rPos=drPos+(((15.0-sl)/15.0)*(float(31.0-d2r)/31.0)); //End of D2R float rrPos=(float(15-rr)/15.0); //end of RR @@ -511,43 +521,42 @@ void FurnaceGUI::drawFMEnv(unsigned char tl, unsigned char ar, unsigned char dr, rrPos/=1.0; ImVec2 pos1=ImLerp(rect.Min,rect.Max,ImVec2(0.0,1.0)); //the bottom corner - ImVec2 pos2=ImLerp(rect.Min,rect.Max,ImVec2(arPos,(tl/127.0))); //peak of AR, start of DR - ImVec2 pos3=ImLerp(rect.Min,rect.Max,ImVec2(drPos,(float)((tl/127.0)+(sl/15.0)-((tl/127.0)*(sl/15.0))))); //end of DR, start of D2R + ImVec2 pos2=ImLerp(rect.Min,rect.Max,ImVec2(arPos,(tl/maxTl))); //peak of AR, start of DR + ImVec2 pos3=ImLerp(rect.Min,rect.Max,ImVec2(drPos,(float)((tl/maxTl)+(sl/15.0)-((tl/maxTl)*(sl/15.0))))); //end of DR, start of D2R ImVec2 pos4=ImLerp(rect.Min,rect.Max,ImVec2(d2rPos,1.0)); //end of D2R - ImVec2 posRStart=ImLerp(rect.Min,rect.Max,ImVec2(0.0,(tl/127.0))); //release start + ImVec2 posRStart=ImLerp(rect.Min,rect.Max,ImVec2(0.0,(tl/maxTl))); //release start ImVec2 posREnd=ImLerp(rect.Min,rect.Max,ImVec2(rrPos,1.0));//release end - ImVec2 posSLineHEnd=ImLerp(rect.Min,rect.Max,ImVec2(1.0,(float)((tl/127.0)+(sl/15.0)-((tl/127.0)*(sl/15.0))))); //sustain horizontal line end + ImVec2 posSLineHEnd=ImLerp(rect.Min,rect.Max,ImVec2(1.0,(float)((tl/maxTl)+(sl/15.0)-((tl/maxTl)*(sl/15.0))))); //sustain horizontal line end ImVec2 posSLineVEnd=ImLerp(rect.Min,rect.Max,ImVec2(drPos,1.0)); //sustain vertical line end - ImVec2 posDecayRate0Pt=ImLerp(rect.Min,rect.Max,ImVec2(1.0,(tl/127.0))); //Heght of the peak of AR, forever - ImVec2 posDecay2Rate0Pt=ImLerp(rect.Min,rect.Max,ImVec2(1.0,(float)((tl/127.0)+(sl/15.0)-((tl/127.0)*(sl/15.0))))); //Heght of the peak of SR, forever + ImVec2 posDecayRate0Pt=ImLerp(rect.Min,rect.Max,ImVec2(1.0,(tl/maxTl))); //Heght of the peak of AR, forever + ImVec2 posDecay2Rate0Pt=ImLerp(rect.Min,rect.Max,ImVec2(1.0,(float)((tl/maxTl)+(sl/15.0)-((tl/maxTl)*(sl/15.0))))); //Heght of the peak of SR, forever + //dl->Flags=ImDrawListFlags_AntiAliasedLines|ImDrawListFlags_AntiAliasedLinesUseTex; if (ar==0.0) { //if AR = 0, the envelope never starts dl->AddTriangleFilled(posRStart,posREnd,pos1,colorS); //draw release as shaded triangle behind everything - dl->AddLine(pos1,pos4,color); //draw line on ground - } - else if (dr==0.0 && sl!=0.0) { //if DR = 0 and SL is not 0, then the envelope stays at max volume forever + addAALine(dl,pos1,pos4,color); //draw line on ground + } else if (dr==0.0 && sl!=0.0) { //if DR = 0 and SL is not 0, then the envelope stays at max volume forever dl->AddTriangleFilled(posRStart,posREnd,pos1,colorS); //draw release as shaded triangle behind everything - //dl->AddLine(pos3,posSLineHEnd,colorS); //draw horiz line through sustain level - //dl->AddLine(pos3,posSLineVEnd,colorS); //draw vert. line through sustain level - dl->AddLine(pos1,pos2,color); //A - dl->AddLine(pos2,posDecayRate0Pt,color); //Line from A to end of graph - } - else if(d2r==0.0) { //if D2R = 0, the envelope stays at the sustain level forever + //addAALine(dl,pos3,posSLineHEnd,colorS); //draw horiz line through sustain level + //addAALine(dl,pos3,posSLineVEnd,colorS); //draw vert. line through sustain level + addAALine(dl,pos1,pos2,color); //A + addAALine(dl,pos2,posDecayRate0Pt,color); //Line from A to end of graph + } else if (d2r==0.0) { //if D2R = 0, the envelope stays at the sustain level forever dl->AddTriangleFilled(posRStart,posREnd,pos1,colorS); //draw release as shaded triangle behind everything - dl->AddLine(pos3,posSLineHEnd,colorS); //draw horiz line through sustain level - dl->AddLine(pos3,posSLineVEnd,colorS); //draw vert. line through sustain level - dl->AddLine(pos1,pos2,color); //A - dl->AddLine(pos2,pos3,color); //D - dl->AddLine(pos3,posDecay2Rate0Pt,color); //Line from D to end of graph - } - else { //draw graph normally + addAALine(dl,pos3,posSLineHEnd,colorS); //draw horiz line through sustain level + addAALine(dl,pos3,posSLineVEnd,colorS); //draw vert. line through sustain level + addAALine(dl,pos1,pos2,color); //A + addAALine(dl,pos2,pos3,color); //D + addAALine(dl,pos3,posDecay2Rate0Pt,color); //Line from D to end of graph + } else { //draw graph normally dl->AddTriangleFilled(posRStart,posREnd,pos1,colorS); //draw release as shaded triangle behind everything - dl->AddLine(pos3,posSLineHEnd,colorS); //draw horiz line through sustain level - dl->AddLine(pos3,posSLineVEnd,colorS); //draw vert. line through sustain level - dl->AddLine(pos1,pos2,color); //A - dl->AddLine(pos2,pos3,color); //D - dl->AddLine(pos3,pos4,color); //D2 + addAALine(dl,pos3,posSLineHEnd,colorS); //draw horiz line through sustain level + addAALine(dl,pos3,posSLineVEnd,colorS); //draw vert. line through sustain level + addAALine(dl,pos1,pos2,color); //A + addAALine(dl,pos2,pos3,color); //D + addAALine(dl,pos3,pos4,color); //D2 } + //dl->Flags^=ImDrawListFlags_AntiAliasedLines|ImDrawListFlags_AntiAliasedLinesUseTex; } } @@ -899,7 +908,7 @@ void FurnaceGUI::drawInsEdit() { } //52.0 controls vert scaling; default 96 - drawFMEnv(op.tl,op.ar,op.dr,op.d2r,op.rr,op.sl,ImVec2(ImGui::GetContentRegionAvail().x,52.0*dpiScale)); + drawFMEnv(op.tl&maxTl,op.ar&maxArDr,op.dr&maxArDr,(ins->type==DIV_INS_OPL || ins->type==DIV_INS_OPLL)?((op.rr&15)*2):op.d2r&31,op.rr&15,op.sl&15,maxTl,maxArDr,ImVec2(ImGui::GetContentRegionAvail().x,52.0*dpiScale)); //P(ImGui::SliderScalar(FM_NAME(FM_AR),ImGuiDataType_U8,&op.ar,&_ZERO,&_THIRTY_ONE)); if (ImGui::BeginTable("opParams",2,ImGuiTableFlags_SizingStretchProp)) { ImGui::TableSetupColumn("c0",ImGuiTableColumnFlags_WidthStretch,0.0); \ @@ -1027,15 +1036,24 @@ void FurnaceGUI::drawInsEdit() { } if (ImGui::BeginTabItem("FM Macros")) { MACRO_BEGIN(0); - NORMAL_MACRO(ins->std.algMacro,ins->std.algMacroLen,ins->std.algMacroLoop,ins->std.algMacroRel,0,7,"alg",FM_NAME(FM_ALG),96,ins->std.algMacroOpen,false,NULL,false,NULL,0,0,0,NULL,uiColors[GUI_COLOR_MACRO_OTHER],mmlString[0],0,7,NULL,false); - NORMAL_MACRO(ins->std.fbMacro,ins->std.fbMacroLen,ins->std.fbMacroLoop,ins->std.fbMacroRel,0,7,"fb",FM_NAME(FM_FB),96,ins->std.fbMacroOpen,false,NULL,false,NULL,0,0,0,NULL,uiColors[GUI_COLOR_MACRO_OTHER],mmlString[1],0,7,NULL,false); - NORMAL_MACRO(ins->std.fmsMacro,ins->std.fmsMacroLen,ins->std.fmsMacroLoop,ins->std.fmsMacroRel,0,7,"fms",FM_NAME(FM_FMS),96,ins->std.fmsMacroOpen,false,NULL,false,NULL,0,0,0,NULL,uiColors[GUI_COLOR_MACRO_OTHER],mmlString[2],0,7,NULL,false); - NORMAL_MACRO(ins->std.amsMacro,ins->std.amsMacroLen,ins->std.amsMacroLoop,ins->std.amsMacroRel,0,3,"ams",FM_NAME(FM_AMS),48,ins->std.amsMacroOpen,false,NULL,false,NULL,0,0,0,NULL,uiColors[GUI_COLOR_MACRO_OTHER],mmlString[3],0,3,NULL,false); + if (ins->type==DIV_INS_OPLL) { + NORMAL_MACRO(ins->std.algMacro,ins->std.algMacroLen,ins->std.algMacroLoop,ins->std.algMacroRel,0,1,"alg",FM_NAME(FM_SUS),16,ins->std.algMacroOpen,true,oneBit,false,NULL,0,0,0,NULL,uiColors[GUI_COLOR_MACRO_OTHER],mmlString[0],0,1,NULL,false); + NORMAL_MACRO(ins->std.fbMacro,ins->std.fbMacroLen,ins->std.fbMacroLoop,ins->std.fbMacroRel,0,7,"fb",FM_NAME(FM_FB),96,ins->std.fbMacroOpen,false,NULL,false,NULL,0,0,0,NULL,uiColors[GUI_COLOR_MACRO_OTHER],mmlString[1],0,7,NULL,false); + NORMAL_MACRO(ins->std.fmsMacro,ins->std.fmsMacroLen,ins->std.fmsMacroLoop,ins->std.fmsMacroRel,0,1,"fms",FM_NAME(FM_DC),16,ins->std.fmsMacroOpen,true,oneBit,false,NULL,0,0,0,NULL,uiColors[GUI_COLOR_MACRO_OTHER],mmlString[2],0,1,NULL,false); + NORMAL_MACRO(ins->std.amsMacro,ins->std.amsMacroLen,ins->std.amsMacroLoop,ins->std.amsMacroRel,0,1,"ams",FM_NAME(FM_DM),16,ins->std.amsMacroOpen,true,oneBit,false,NULL,0,0,0,NULL,uiColors[GUI_COLOR_MACRO_OTHER],mmlString[3],0,1,NULL,false); + } else { + NORMAL_MACRO(ins->std.algMacro,ins->std.algMacroLen,ins->std.algMacroLoop,ins->std.algMacroRel,0,7,"alg",FM_NAME(FM_ALG),96,ins->std.algMacroOpen,false,NULL,false,NULL,0,0,0,NULL,uiColors[GUI_COLOR_MACRO_OTHER],mmlString[0],0,7,NULL,false); + NORMAL_MACRO(ins->std.fbMacro,ins->std.fbMacroLen,ins->std.fbMacroLoop,ins->std.fbMacroRel,0,7,"fb",FM_NAME(FM_FB),96,ins->std.fbMacroOpen,false,NULL,false,NULL,0,0,0,NULL,uiColors[GUI_COLOR_MACRO_OTHER],mmlString[1],0,7,NULL,false); + NORMAL_MACRO(ins->std.fmsMacro,ins->std.fmsMacroLen,ins->std.fmsMacroLoop,ins->std.fmsMacroRel,0,7,"fms",FM_NAME(FM_FMS),96,ins->std.fmsMacroOpen,false,NULL,false,NULL,0,0,0,NULL,uiColors[GUI_COLOR_MACRO_OTHER],mmlString[2],0,7,NULL,false); + NORMAL_MACRO(ins->std.amsMacro,ins->std.amsMacroLen,ins->std.amsMacroLoop,ins->std.amsMacroRel,0,3,"ams",FM_NAME(FM_AMS),48,ins->std.amsMacroOpen,false,NULL,false,NULL,0,0,0,NULL,uiColors[GUI_COLOR_MACRO_OTHER],mmlString[3],0,3,NULL,false); + } - NORMAL_MACRO(ins->std.ex1Macro,ins->std.ex1MacroLen,ins->std.ex1MacroLoop,ins->std.ex1MacroRel,0,127,"ex1","AM Depth",128,ins->std.ex1MacroOpen,false,NULL,false,NULL,0,0,0,NULL,uiColors[GUI_COLOR_MACRO_OTHER],mmlString[4],0,127,NULL,false); - NORMAL_MACRO(ins->std.ex2Macro,ins->std.ex2MacroLen,ins->std.ex2MacroLoop,ins->std.ex2MacroRel,0,127,"ex2","PM Depth",128,ins->std.ex2MacroOpen,false,NULL,false,NULL,0,0,0,NULL,uiColors[GUI_COLOR_MACRO_OTHER],mmlString[5],0,127,NULL,false); - NORMAL_MACRO(ins->std.ex3Macro,ins->std.ex3MacroLen,ins->std.ex3MacroLoop,ins->std.ex3MacroRel,0,255,"ex3","LFO Speed",128,ins->std.ex3MacroOpen,false,NULL,false,NULL,0,0,0,NULL,uiColors[GUI_COLOR_MACRO_OTHER],mmlString[6],0,255,NULL,false); - NORMAL_MACRO(ins->std.waveMacro,ins->std.waveMacroLen,ins->std.waveMacroLoop,ins->std.waveMacroRel,0,3,"wave","LFO Shape",48,ins->std.waveMacroOpen,false,NULL,false,NULL,0,0,0,NULL,uiColors[GUI_COLOR_MACRO_WAVE],mmlString[7],0,3,¯oLFOWaves,false); + if (ins->type==DIV_INS_FM || ins->type==DIV_INS_OPZ) { + NORMAL_MACRO(ins->std.ex1Macro,ins->std.ex1MacroLen,ins->std.ex1MacroLoop,ins->std.ex1MacroRel,0,127,"ex1","AM Depth",128,ins->std.ex1MacroOpen,false,NULL,false,NULL,0,0,0,NULL,uiColors[GUI_COLOR_MACRO_OTHER],mmlString[4],0,127,NULL,false); + NORMAL_MACRO(ins->std.ex2Macro,ins->std.ex2MacroLen,ins->std.ex2MacroLoop,ins->std.ex2MacroRel,0,127,"ex2","PM Depth",128,ins->std.ex2MacroOpen,false,NULL,false,NULL,0,0,0,NULL,uiColors[GUI_COLOR_MACRO_OTHER],mmlString[5],0,127,NULL,false); + NORMAL_MACRO(ins->std.ex3Macro,ins->std.ex3MacroLen,ins->std.ex3MacroLoop,ins->std.ex3MacroRel,0,255,"ex3","LFO Speed",128,ins->std.ex3MacroOpen,false,NULL,false,NULL,0,0,0,NULL,uiColors[GUI_COLOR_MACRO_OTHER],mmlString[6],0,255,NULL,false); + NORMAL_MACRO(ins->std.waveMacro,ins->std.waveMacroLen,ins->std.waveMacroLoop,ins->std.waveMacroRel,0,3,"wave","LFO Shape",48,ins->std.waveMacroOpen,false,NULL,false,NULL,0,0,0,NULL,uiColors[GUI_COLOR_MACRO_WAVE],mmlString[7],0,3,¯oLFOWaves,false); + } MACRO_END; ImGui::EndTabItem(); } @@ -1055,18 +1073,33 @@ void FurnaceGUI::drawInsEdit() { } int maxArDr=(ins->type==DIV_INS_FM || ins->type==DIV_INS_OPZ)?31:15; - OP_MACRO(ins->std.opMacros[ordi].tlMacro,ins->std.opMacros[ordi].tlMacroLen,ins->std.opMacros[ordi].tlMacroLoop,ins->std.opMacros[ordi].tlMacroRel,maxTl,ordi,"tl",FM_NAME(FM_TL),128,ins->std.opMacros[ordi].tlMacroOpen,false,NULL,mmlString[0]); - OP_MACRO(ins->std.opMacros[ordi].arMacro,ins->std.opMacros[ordi].arMacroLen,ins->std.opMacros[ordi].arMacroLoop,ins->std.opMacros[ordi].arMacroRel,maxArDr,ordi,"ar",FM_NAME(FM_AR),64,ins->std.opMacros[ordi].arMacroOpen,false,NULL,mmlString[1]); - OP_MACRO(ins->std.opMacros[ordi].drMacro,ins->std.opMacros[ordi].drMacroLen,ins->std.opMacros[ordi].drMacroLoop,ins->std.opMacros[ordi].drMacroRel,maxArDr,ordi,"dr",FM_NAME(FM_DR),64,ins->std.opMacros[ordi].drMacroOpen,false,NULL,mmlString[2]); - OP_MACRO(ins->std.opMacros[ordi].d2rMacro,ins->std.opMacros[ordi].d2rMacroLen,ins->std.opMacros[ordi].d2rMacroLoop,ins->std.opMacros[ordi].d2rMacroRel,31,ordi,"d2r",FM_NAME(FM_D2R),64,ins->std.opMacros[ordi].d2rMacroOpen,false,NULL,mmlString[3]); - OP_MACRO(ins->std.opMacros[ordi].rrMacro,ins->std.opMacros[ordi].rrMacroLen,ins->std.opMacros[ordi].rrMacroLoop,ins->std.opMacros[ordi].rrMacroRel,15,ordi,"rr",FM_NAME(FM_RR),64,ins->std.opMacros[ordi].rrMacroOpen,false,NULL,mmlString[4]); - OP_MACRO(ins->std.opMacros[ordi].slMacro,ins->std.opMacros[ordi].slMacroLen,ins->std.opMacros[ordi].slMacroLoop,ins->std.opMacros[ordi].slMacroRel,15,ordi,"sl",FM_NAME(FM_SL),64,ins->std.opMacros[ordi].slMacroOpen,false,NULL,mmlString[5]); - OP_MACRO(ins->std.opMacros[ordi].rsMacro,ins->std.opMacros[ordi].rsMacroLen,ins->std.opMacros[ordi].rsMacroLoop,ins->std.opMacros[ordi].rsMacroRel,3,ordi,"rs",FM_NAME(FM_RS),32,ins->std.opMacros[ordi].rsMacroOpen,false,NULL,mmlString[6]); - OP_MACRO(ins->std.opMacros[ordi].multMacro,ins->std.opMacros[ordi].multMacroLen,ins->std.opMacros[ordi].multMacroLoop,ins->std.opMacros[ordi].multMacroRel,15,ordi,"mult",FM_NAME(FM_MULT),64,ins->std.opMacros[ordi].multMacroOpen,false,NULL,mmlString[7]); - OP_MACRO(ins->std.opMacros[ordi].dtMacro,ins->std.opMacros[ordi].dtMacroLen,ins->std.opMacros[ordi].dtMacroLoop,ins->std.opMacros[ordi].dtMacroRel,7,ordi,"dt",FM_NAME(FM_DT),64,ins->std.opMacros[ordi].dtMacroOpen,false,NULL,mmlString[8]); - OP_MACRO(ins->std.opMacros[ordi].dt2Macro,ins->std.opMacros[ordi].dt2MacroLen,ins->std.opMacros[ordi].dt2MacroLoop,ins->std.opMacros[ordi].dt2MacroRel,3,ordi,"dt2",FM_NAME(FM_DT2),32,ins->std.opMacros[ordi].dt2MacroOpen,false,NULL,mmlString[9]); - OP_MACRO(ins->std.opMacros[ordi].amMacro,ins->std.opMacros[ordi].amMacroLen,ins->std.opMacros[ordi].amMacroLoop,ins->std.opMacros[ordi].amMacroRel,1,ordi,"am",FM_NAME(FM_AM),32,ins->std.opMacros[ordi].amMacroOpen,true,NULL,mmlString[10]); - OP_MACRO(ins->std.opMacros[ordi].ssgMacro,ins->std.opMacros[ordi].ssgMacroLen,ins->std.opMacros[ordi].ssgMacroLoop,ins->std.opMacros[ordi].ssgMacroRel,4,ordi,"ssg",FM_NAME(FM_SSG),64,ins->std.opMacros[ordi].ssgMacroOpen,true,ssgEnvBits,mmlString[11]); + if (ins->type==DIV_INS_OPLL) { + OP_MACRO(ins->std.opMacros[ordi].tlMacro,ins->std.opMacros[ordi].tlMacroLen,ins->std.opMacros[ordi].tlMacroLoop,ins->std.opMacros[ordi].tlMacroRel,maxTl,ordi,"tl",FM_NAME(FM_TL),128,ins->std.opMacros[ordi].tlMacroOpen,false,NULL,mmlString[0]); + OP_MACRO(ins->std.opMacros[ordi].arMacro,ins->std.opMacros[ordi].arMacroLen,ins->std.opMacros[ordi].arMacroLoop,ins->std.opMacros[ordi].arMacroRel,maxArDr,ordi,"ar",FM_NAME(FM_AR),64,ins->std.opMacros[ordi].arMacroOpen,false,NULL,mmlString[1]); + OP_MACRO(ins->std.opMacros[ordi].drMacro,ins->std.opMacros[ordi].drMacroLen,ins->std.opMacros[ordi].drMacroLoop,ins->std.opMacros[ordi].drMacroRel,maxArDr,ordi,"dr",FM_NAME(FM_DR),64,ins->std.opMacros[ordi].drMacroOpen,false,NULL,mmlString[2]); + OP_MACRO(ins->std.opMacros[ordi].slMacro,ins->std.opMacros[ordi].slMacroLen,ins->std.opMacros[ordi].slMacroLoop,ins->std.opMacros[ordi].slMacroRel,15,ordi,"sl",FM_NAME(FM_SL),64,ins->std.opMacros[ordi].slMacroOpen,false,NULL,mmlString[5]); + OP_MACRO(ins->std.opMacros[ordi].rrMacro,ins->std.opMacros[ordi].rrMacroLen,ins->std.opMacros[ordi].rrMacroLoop,ins->std.opMacros[ordi].rrMacroRel,15,ordi,"rr",FM_NAME(FM_RR),64,ins->std.opMacros[ordi].rrMacroOpen,false,NULL,mmlString[4]); + OP_MACRO(ins->std.opMacros[ordi].kslMacro,ins->std.opMacros[ordi].kslMacroLen,ins->std.opMacros[ordi].kslMacroLoop,ins->std.opMacros[ordi].kslMacroRel,3,ordi,"ksl",FM_NAME(FM_KSL),32,ins->std.opMacros[ordi].kslMacroOpen,false,NULL,mmlString[6]); + OP_MACRO(ins->std.opMacros[ordi].multMacro,ins->std.opMacros[ordi].multMacroLen,ins->std.opMacros[ordi].multMacroLoop,ins->std.opMacros[ordi].multMacroRel,15,ordi,"mult",FM_NAME(FM_MULT),64,ins->std.opMacros[ordi].multMacroOpen,false,NULL,mmlString[7]); + + OP_MACRO(ins->std.opMacros[ordi].dtMacro,ins->std.opMacros[ordi].dtMacroLen,ins->std.opMacros[ordi].dtMacroLoop,ins->std.opMacros[ordi].dtMacroRel,7,ordi,"dt",FM_NAME(FM_DT),64,ins->std.opMacros[ordi].dtMacroOpen,false,NULL,mmlString[8]); + OP_MACRO(ins->std.opMacros[ordi].dt2Macro,ins->std.opMacros[ordi].dt2MacroLen,ins->std.opMacros[ordi].dt2MacroLoop,ins->std.opMacros[ordi].dt2MacroRel,3,ordi,"dt2",FM_NAME(FM_DT2),32,ins->std.opMacros[ordi].dt2MacroOpen,false,NULL,mmlString[9]); + OP_MACRO(ins->std.opMacros[ordi].amMacro,ins->std.opMacros[ordi].amMacroLen,ins->std.opMacros[ordi].amMacroLoop,ins->std.opMacros[ordi].amMacroRel,1,ordi,"am",FM_NAME(FM_AM),32,ins->std.opMacros[ordi].amMacroOpen,true,NULL,mmlString[10]); + OP_MACRO(ins->std.opMacros[ordi].ssgMacro,ins->std.opMacros[ordi].ssgMacroLen,ins->std.opMacros[ordi].ssgMacroLoop,ins->std.opMacros[ordi].ssgMacroRel,4,ordi,"ssg",FM_NAME(FM_SSG),64,ins->std.opMacros[ordi].ssgMacroOpen,true,ssgEnvBits,mmlString[11]); + } else { + OP_MACRO(ins->std.opMacros[ordi].tlMacro,ins->std.opMacros[ordi].tlMacroLen,ins->std.opMacros[ordi].tlMacroLoop,ins->std.opMacros[ordi].tlMacroRel,maxTl,ordi,"tl",FM_NAME(FM_TL),128,ins->std.opMacros[ordi].tlMacroOpen,false,NULL,mmlString[0]); + OP_MACRO(ins->std.opMacros[ordi].arMacro,ins->std.opMacros[ordi].arMacroLen,ins->std.opMacros[ordi].arMacroLoop,ins->std.opMacros[ordi].arMacroRel,maxArDr,ordi,"ar",FM_NAME(FM_AR),64,ins->std.opMacros[ordi].arMacroOpen,false,NULL,mmlString[1]); + OP_MACRO(ins->std.opMacros[ordi].drMacro,ins->std.opMacros[ordi].drMacroLen,ins->std.opMacros[ordi].drMacroLoop,ins->std.opMacros[ordi].drMacroRel,maxArDr,ordi,"dr",FM_NAME(FM_DR),64,ins->std.opMacros[ordi].drMacroOpen,false,NULL,mmlString[2]); + OP_MACRO(ins->std.opMacros[ordi].d2rMacro,ins->std.opMacros[ordi].d2rMacroLen,ins->std.opMacros[ordi].d2rMacroLoop,ins->std.opMacros[ordi].d2rMacroRel,31,ordi,"d2r",FM_NAME(FM_D2R),64,ins->std.opMacros[ordi].d2rMacroOpen,false,NULL,mmlString[3]); + OP_MACRO(ins->std.opMacros[ordi].rrMacro,ins->std.opMacros[ordi].rrMacroLen,ins->std.opMacros[ordi].rrMacroLoop,ins->std.opMacros[ordi].rrMacroRel,15,ordi,"rr",FM_NAME(FM_RR),64,ins->std.opMacros[ordi].rrMacroOpen,false,NULL,mmlString[4]); + OP_MACRO(ins->std.opMacros[ordi].slMacro,ins->std.opMacros[ordi].slMacroLen,ins->std.opMacros[ordi].slMacroLoop,ins->std.opMacros[ordi].slMacroRel,15,ordi,"sl",FM_NAME(FM_SL),64,ins->std.opMacros[ordi].slMacroOpen,false,NULL,mmlString[5]); + OP_MACRO(ins->std.opMacros[ordi].rsMacro,ins->std.opMacros[ordi].rsMacroLen,ins->std.opMacros[ordi].rsMacroLoop,ins->std.opMacros[ordi].rsMacroRel,3,ordi,"rs",FM_NAME(FM_RS),32,ins->std.opMacros[ordi].rsMacroOpen,false,NULL,mmlString[6]); + OP_MACRO(ins->std.opMacros[ordi].multMacro,ins->std.opMacros[ordi].multMacroLen,ins->std.opMacros[ordi].multMacroLoop,ins->std.opMacros[ordi].multMacroRel,15,ordi,"mult",FM_NAME(FM_MULT),64,ins->std.opMacros[ordi].multMacroOpen,false,NULL,mmlString[7]); + OP_MACRO(ins->std.opMacros[ordi].dtMacro,ins->std.opMacros[ordi].dtMacroLen,ins->std.opMacros[ordi].dtMacroLoop,ins->std.opMacros[ordi].dtMacroRel,7,ordi,"dt",FM_NAME(FM_DT),64,ins->std.opMacros[ordi].dtMacroOpen,false,NULL,mmlString[8]); + OP_MACRO(ins->std.opMacros[ordi].dt2Macro,ins->std.opMacros[ordi].dt2MacroLen,ins->std.opMacros[ordi].dt2MacroLoop,ins->std.opMacros[ordi].dt2MacroRel,3,ordi,"dt2",FM_NAME(FM_DT2),32,ins->std.opMacros[ordi].dt2MacroOpen,false,NULL,mmlString[9]); + OP_MACRO(ins->std.opMacros[ordi].amMacro,ins->std.opMacros[ordi].amMacroLen,ins->std.opMacros[ordi].amMacroLoop,ins->std.opMacros[ordi].amMacroRel,1,ordi,"am",FM_NAME(FM_AM),32,ins->std.opMacros[ordi].amMacroOpen,true,NULL,mmlString[10]); + OP_MACRO(ins->std.opMacros[ordi].ssgMacro,ins->std.opMacros[ordi].ssgMacroLen,ins->std.opMacros[ordi].ssgMacroLoop,ins->std.opMacros[ordi].ssgMacroRel,4,ordi,"ssg",FM_NAME(FM_SSG),64,ins->std.opMacros[ordi].ssgMacroOpen,true,ssgEnvBits,mmlString[11]); + } MACRO_END; ImGui::PopID(); ImGui::EndTabItem(); From f8e7dd72143df2b12a48f65fc2c4aec93e7a0f3a Mon Sep 17 00:00:00 2001 From: tildearrow Date: Mon, 28 Feb 2022 18:38:36 -0500 Subject: [PATCH 16/50] GUI: hopefully finish OPLL op macro interface --- src/gui/insEdit.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/gui/insEdit.cpp b/src/gui/insEdit.cpp index a8558bb07..8a00002d0 100644 --- a/src/gui/insEdit.cpp +++ b/src/gui/insEdit.cpp @@ -1082,10 +1082,10 @@ void FurnaceGUI::drawInsEdit() { OP_MACRO(ins->std.opMacros[ordi].kslMacro,ins->std.opMacros[ordi].kslMacroLen,ins->std.opMacros[ordi].kslMacroLoop,ins->std.opMacros[ordi].kslMacroRel,3,ordi,"ksl",FM_NAME(FM_KSL),32,ins->std.opMacros[ordi].kslMacroOpen,false,NULL,mmlString[6]); OP_MACRO(ins->std.opMacros[ordi].multMacro,ins->std.opMacros[ordi].multMacroLen,ins->std.opMacros[ordi].multMacroLoop,ins->std.opMacros[ordi].multMacroRel,15,ordi,"mult",FM_NAME(FM_MULT),64,ins->std.opMacros[ordi].multMacroOpen,false,NULL,mmlString[7]); - OP_MACRO(ins->std.opMacros[ordi].dtMacro,ins->std.opMacros[ordi].dtMacroLen,ins->std.opMacros[ordi].dtMacroLoop,ins->std.opMacros[ordi].dtMacroRel,7,ordi,"dt",FM_NAME(FM_DT),64,ins->std.opMacros[ordi].dtMacroOpen,false,NULL,mmlString[8]); - OP_MACRO(ins->std.opMacros[ordi].dt2Macro,ins->std.opMacros[ordi].dt2MacroLen,ins->std.opMacros[ordi].dt2MacroLoop,ins->std.opMacros[ordi].dt2MacroRel,3,ordi,"dt2",FM_NAME(FM_DT2),32,ins->std.opMacros[ordi].dt2MacroOpen,false,NULL,mmlString[9]); - OP_MACRO(ins->std.opMacros[ordi].amMacro,ins->std.opMacros[ordi].amMacroLen,ins->std.opMacros[ordi].amMacroLoop,ins->std.opMacros[ordi].amMacroRel,1,ordi,"am",FM_NAME(FM_AM),32,ins->std.opMacros[ordi].amMacroOpen,true,NULL,mmlString[10]); - OP_MACRO(ins->std.opMacros[ordi].ssgMacro,ins->std.opMacros[ordi].ssgMacroLen,ins->std.opMacros[ordi].ssgMacroLoop,ins->std.opMacros[ordi].ssgMacroRel,4,ordi,"ssg",FM_NAME(FM_SSG),64,ins->std.opMacros[ordi].ssgMacroOpen,true,ssgEnvBits,mmlString[11]); + OP_MACRO(ins->std.opMacros[ordi].amMacro,ins->std.opMacros[ordi].amMacroLen,ins->std.opMacros[ordi].amMacroLoop,ins->std.opMacros[ordi].amMacroRel,1,ordi,"am",FM_NAME(FM_AM),16,ins->std.opMacros[ordi].amMacroOpen,true,NULL,mmlString[8]); + OP_MACRO(ins->std.opMacros[ordi].vibMacro,ins->std.opMacros[ordi].vibMacroLen,ins->std.opMacros[ordi].vibMacroLoop,ins->std.opMacros[ordi].vibMacroRel,4,ordi,"vib",FM_NAME(FM_VIB),16,ins->std.opMacros[ordi].vibMacroOpen,true,oneBit,mmlString[9]); + OP_MACRO(ins->std.opMacros[ordi].ksrMacro,ins->std.opMacros[ordi].ksrMacroLen,ins->std.opMacros[ordi].ksrMacroLoop,ins->std.opMacros[ordi].ksrMacroRel,4,ordi,"ksr",FM_NAME(FM_KSR),16,ins->std.opMacros[ordi].ksrMacroOpen,true,oneBit,mmlString[10]); + OP_MACRO(ins->std.opMacros[ordi].egtMacro,ins->std.opMacros[ordi].egtMacroLen,ins->std.opMacros[ordi].egtMacroLoop,ins->std.opMacros[ordi].egtMacroRel,4,ordi,"egt",FM_NAME(FM_EGS),16,ins->std.opMacros[ordi].egtMacroOpen,true,oneBit,mmlString[11]); } else { OP_MACRO(ins->std.opMacros[ordi].tlMacro,ins->std.opMacros[ordi].tlMacroLen,ins->std.opMacros[ordi].tlMacroLoop,ins->std.opMacros[ordi].tlMacroRel,maxTl,ordi,"tl",FM_NAME(FM_TL),128,ins->std.opMacros[ordi].tlMacroOpen,false,NULL,mmlString[0]); OP_MACRO(ins->std.opMacros[ordi].arMacro,ins->std.opMacros[ordi].arMacroLen,ins->std.opMacros[ordi].arMacroLoop,ins->std.opMacros[ordi].arMacroRel,maxArDr,ordi,"ar",FM_NAME(FM_AR),64,ins->std.opMacros[ordi].arMacroOpen,false,NULL,mmlString[1]); From f8046facfcfb916e31a2b7b6a49e80267b426665 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Mon, 28 Feb 2022 22:42:52 -0500 Subject: [PATCH 17/50] GUI: make sliders right-clickable --- src/gui/gui.cpp | 12 ++++----- src/gui/gui.h | 2 ++ src/gui/insEdit.cpp | 58 ++++++++++++++++++++++---------------------- src/gui/settings.cpp | 2 +- 4 files changed, 38 insertions(+), 36 deletions(-) diff --git a/src/gui/gui.cpp b/src/gui/gui.cpp index ee0c02370..b002afb9c 100644 --- a/src/gui/gui.cpp +++ b/src/gui/gui.cpp @@ -1348,7 +1348,7 @@ void FurnaceGUI::drawMixer() { if (ImGui::SliderFloat("Master Volume",&e->song.masterVol,0,3,"%.2fx")) { if (e->song.masterVol<0) e->song.masterVol=0; if (e->song.masterVol>3) e->song.masterVol=3; - } + } rightClickable for (int i=0; isong.systemLen; i++) { snprintf(id,31,"MixS%d",i); bool doInvert=e->song.systemVol[i]&128; @@ -1362,9 +1362,9 @@ void FurnaceGUI::drawMixer() { ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x-(50.0f*dpiScale)); if (ImGui::SliderScalar("Volume",ImGuiDataType_S8,&vol,&_ZERO,&_ONE_HUNDRED_TWENTY_SEVEN)) { e->song.systemVol[i]=(e->song.systemVol[i]&128)|vol; - } + } rightClickable ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x-(50.0f*dpiScale)); - ImGui::SliderScalar("Panning",ImGuiDataType_S8,&e->song.systemPan[i],&_MINUS_ONE_HUNDRED_TWENTY_SEVEN,&_ONE_HUNDRED_TWENTY_SEVEN); + ImGui::SliderScalar("Panning",ImGuiDataType_S8,&e->song.systemPan[i],&_MINUS_ONE_HUNDRED_TWENTY_SEVEN,&_ONE_HUNDRED_TWENTY_SEVEN); rightClickable ImGui::PopID(); } @@ -4691,7 +4691,7 @@ bool FurnaceGUI::loop() { if (stereoSep>127) stereoSep=127; e->setSysFlags(i,(flags&1)|((stereoSep&127)<<8),restart); updateWindowTitle(); - } + } rightClickable /* TODO LATER: I want 0.5 out already if (ImGui::RadioButton("Amiga 500 (OCS)",(flags&2)==0)) { e->setSysFlags(i,flags&1); @@ -4714,7 +4714,7 @@ bool FurnaceGUI::loop() { if (echoBufSize>2725) echoBufSize=2725; e->setSysFlags(i,(flags & ~4095) | ((2725 - echoBufSize) & 4095),restart); updateWindowTitle(); - } + } rightClickable ImGui::Text("Echo feedback:"); int echoFeedback=(flags>>12)&255; if (ImGui::SliderInt("##EchoFeedback",&echoFeedback,0,255)) { @@ -4722,7 +4722,7 @@ bool FurnaceGUI::loop() { if (echoFeedback>255) echoFeedback=255; e->setSysFlags(i,(flags & ~(255 << 12)) | ((echoFeedback & 255) << 12),restart); updateWindowTitle(); - } + } rightClickable break; } case DIV_SYSTEM_GB: diff --git a/src/gui/gui.h b/src/gui/gui.h index 4cae110dc..291db6d8d 100644 --- a/src/gui/gui.h +++ b/src/gui/gui.h @@ -26,6 +26,8 @@ #include #include +#define rightClickable if (ImGui::IsItemClicked(ImGuiMouseButton_Right)) ImGui::SetKeyboardFocusHere(-1); + enum FurnaceGUIColors { GUI_COLOR_BACKGROUND=0, GUI_COLOR_FRAME_BACKGROUND, diff --git a/src/gui/insEdit.cpp b/src/gui/insEdit.cpp index 8a00002d0..4608f89b1 100644 --- a/src/gui/insEdit.cpp +++ b/src/gui/insEdit.cpp @@ -808,11 +808,11 @@ void FurnaceGUI::drawInsEdit() { case DIV_INS_FM: case DIV_INS_OPZ: ImGui::TableNextColumn(); - P(ImGui::SliderScalar(FM_NAME(FM_FB),ImGuiDataType_U8,&ins->fm.fb,&_ZERO,&_SEVEN)); - P(ImGui::SliderScalar(FM_NAME(FM_FMS),ImGuiDataType_U8,&ins->fm.fms,&_ZERO,&_SEVEN)); + P(ImGui::SliderScalar(FM_NAME(FM_FB),ImGuiDataType_U8,&ins->fm.fb,&_ZERO,&_SEVEN)); rightClickable + P(ImGui::SliderScalar(FM_NAME(FM_FMS),ImGuiDataType_U8,&ins->fm.fms,&_ZERO,&_SEVEN)); rightClickable ImGui::TableNextColumn(); - 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)); + P(ImGui::SliderScalar(FM_NAME(FM_ALG),ImGuiDataType_U8,&ins->fm.alg,&_ZERO,&_SEVEN)); rightClickable + P(ImGui::SliderScalar(FM_NAME(FM_AMS),ImGuiDataType_U8,&ins->fm.ams,&_ZERO,&_THREE)); rightClickable ImGui::TableNextColumn(); drawAlgorithm(ins->fm.alg,FM_ALGS_4OP,ImVec2(ImGui::GetContentRegionAvail().x,48.0*dpiScale)); break; @@ -823,7 +823,7 @@ void FurnaceGUI::drawInsEdit() { 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)); + P(ImGui::SliderScalar(FM_NAME(FM_FB),ImGuiDataType_U8,&ins->fm.fb,&_ZERO,&_SEVEN)); rightClickable if (ImGui::Checkbox(FM_NAME(FM_DC),&dc)) { PARAMETER ins->fm.fms=dc; } @@ -863,7 +863,7 @@ void FurnaceGUI::drawInsEdit() { bool willDisplayOps=true; if (ins->type==DIV_INS_OPLL && ins->fm.opllPreset!=0) willDisplayOps=false; if (!willDisplayOps && ins->type==DIV_INS_OPLL) { - P(ImGui::SliderScalar("Volume##TL",ImGuiDataType_U8,&ins->fm.op[1].tl,&_FIFTEEN,&_ZERO)); + P(ImGui::SliderScalar("Volume##TL",ImGuiDataType_U8,&ins->fm.op[1].tl,&_FIFTEEN,&_ZERO)); rightClickable } if (willDisplayOps) if (ImGui::BeginTable("FMOperators",2,ImGuiTableFlags_SizingStretchSame)) { for (int i=0; itype==DIV_INS_OPL || ins->type==DIV_INS_OPLL)?((op.rr&15)*2):op.d2r&31,op.rr&15,op.sl&15,maxTl,maxArDr,ImVec2(ImGui::GetContentRegionAvail().x,52.0*dpiScale)); - //P(ImGui::SliderScalar(FM_NAME(FM_AR),ImGuiDataType_U8,&op.ar,&_ZERO,&_THIRTY_ONE)); + //P(ImGui::SliderScalar(FM_NAME(FM_AR),ImGuiDataType_U8,&op.ar,&_ZERO,&_THIRTY_ONE)); rightClickable if (ImGui::BeginTable("opParams",2,ImGuiTableFlags_SizingStretchProp)) { ImGui::TableSetupColumn("c0",ImGuiTableColumnFlags_WidthStretch,0.0); \ ImGui::TableSetupColumn("c1",ImGuiTableColumnFlags_WidthFixed,0.0); \ @@ -917,21 +917,21 @@ void FurnaceGUI::drawInsEdit() { ImGui::TableNextRow(); ImGui::TableNextColumn(); ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x); - P(ImGui::SliderScalar("##AR",ImGuiDataType_U8,&op.ar,&maxArDr,&_ZERO)); + P(ImGui::SliderScalar("##AR",ImGuiDataType_U8,&op.ar,&maxArDr,&_ZERO)); rightClickable 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,&maxArDr,&_ZERO)); + P(ImGui::SliderScalar("##DR",ImGuiDataType_U8,&op.dr,&maxArDr,&_ZERO)); rightClickable 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)); + P(ImGui::SliderScalar("##SL",ImGuiDataType_U8,&op.sl,&_FIFTEEN,&_ZERO)); rightClickable ImGui::TableNextColumn(); ImGui::Text("%s",FM_NAME(FM_SL)); @@ -939,7 +939,7 @@ void FurnaceGUI::drawInsEdit() { ImGui::TableNextRow(); ImGui::TableNextColumn(); ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x); - P(ImGui::SliderScalar("##D2R",ImGuiDataType_U8,&op.d2r,&_THIRTY_ONE,&_ZERO)); + P(ImGui::SliderScalar("##D2R",ImGuiDataType_U8,&op.d2r,&_THIRTY_ONE,&_ZERO)); rightClickable ImGui::TableNextColumn(); ImGui::Text("%s",FM_NAME(FM_D2R)); } @@ -947,14 +947,14 @@ void FurnaceGUI::drawInsEdit() { ImGui::TableNextRow(); ImGui::TableNextColumn(); ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x); - P(ImGui::SliderScalar("##RR",ImGuiDataType_U8,&op.rr,&_FIFTEEN,&_ZERO)); + P(ImGui::SliderScalar("##RR",ImGuiDataType_U8,&op.rr,&_FIFTEEN,&_ZERO)); rightClickable 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,&maxTl,&_ZERO)); + P(ImGui::SliderScalar("##TL",ImGuiDataType_U8,&op.tl,&maxTl,&_ZERO)); rightClickable ImGui::TableNextColumn(); ImGui::Text("%s",FM_NAME(FM_TL)); @@ -968,11 +968,11 @@ void FurnaceGUI::drawInsEdit() { ImGui::TableNextColumn(); ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x); if (ins->type==DIV_INS_FM || ins->type==DIV_INS_OPZ) { - P(ImGui::SliderScalar("##RS",ImGuiDataType_U8,&op.rs,&_ZERO,&_THREE)); + P(ImGui::SliderScalar("##RS",ImGuiDataType_U8,&op.rs,&_ZERO,&_THREE)); rightClickable ImGui::TableNextColumn(); ImGui::Text("%s",FM_NAME(FM_RS)); } else { - P(ImGui::SliderScalar("##KSL",ImGuiDataType_U8,&op.ksl,&_ZERO,&_THREE)); + P(ImGui::SliderScalar("##KSL",ImGuiDataType_U8,&op.ksl,&_ZERO,&_THREE)); rightClickable ImGui::TableNextColumn(); ImGui::Text("%s",FM_NAME(FM_KSL)); } @@ -980,7 +980,7 @@ void FurnaceGUI::drawInsEdit() { ImGui::TableNextRow(); ImGui::TableNextColumn(); ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x); - P(ImGui::SliderScalar(FM_NAME(FM_MULT),ImGuiDataType_U8,&op.mult,&_ZERO,&_FIFTEEN)); + P(ImGui::SliderScalar(FM_NAME(FM_MULT),ImGuiDataType_U8,&op.mult,&_ZERO,&_FIFTEEN)); rightClickable ImGui::TableNextColumn(); ImGui::Text("%s",FM_NAME(FM_MULT)); @@ -991,14 +991,14 @@ void FurnaceGUI::drawInsEdit() { ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x); if (ImGui::SliderInt("##DT",&detune,-3,3)) { PARAMETER op.dt=detune+3; - } + } rightClickable 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)); + P(ImGui::SliderScalar("##DT2",ImGuiDataType_U8,&op.dt2,&_ZERO,&_THREE)); rightClickable if (ImGui::IsItemHovered()) { ImGui::SetTooltip("Only for Arcade system"); } @@ -1010,7 +1010,7 @@ void FurnaceGUI::drawInsEdit() { ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x); if (ImGui::SliderScalar("##SSG",ImGuiDataType_U8,&ssgEnv,&_ZERO,&_SEVEN,ssgEnvTypes[ssgEnv])) { PARAMETER op.ssgEnv=(op.ssgEnv&8)|(ssgEnv&7); - } + } rightClickable ImGui::TableNextColumn(); ImGui::Text("%s",FM_NAME(FM_SSG)); } @@ -1107,9 +1107,9 @@ void FurnaceGUI::drawInsEdit() { } } if (ins->type==DIV_INS_GB) if (ImGui::BeginTabItem("Game Boy")) { - P(ImGui::SliderScalar("Volume",ImGuiDataType_U8,&ins->gb.envVol,&_ZERO,&_FIFTEEN)); - P(ImGui::SliderScalar("Envelope Length",ImGuiDataType_U8,&ins->gb.envLen,&_ZERO,&_SEVEN)); - P(ImGui::SliderScalar("Sound Length",ImGuiDataType_U8,&ins->gb.soundLen,&_ZERO,&_SIXTY_FOUR,ins->gb.soundLen>63?"Infinity":"%d")); + P(ImGui::SliderScalar("Volume",ImGuiDataType_U8,&ins->gb.envVol,&_ZERO,&_FIFTEEN)); rightClickable + P(ImGui::SliderScalar("Envelope Length",ImGuiDataType_U8,&ins->gb.envLen,&_ZERO,&_SEVEN)); rightClickable + P(ImGui::SliderScalar("Sound Length",ImGuiDataType_U8,&ins->gb.soundLen,&_ZERO,&_SIXTY_FOUR,ins->gb.soundLen>63?"Infinity":"%d")); rightClickable ImGui::Text("Envelope Direction:"); bool goesUp=ins->gb.envDir; @@ -1152,11 +1152,11 @@ void FurnaceGUI::drawInsEdit() { } ImGui::PopStyleColor(); - P(ImGui::SliderScalar("Attack",ImGuiDataType_U8,&ins->c64.a,&_ZERO,&_FIFTEEN)); - P(ImGui::SliderScalar("Decay",ImGuiDataType_U8,&ins->c64.d,&_ZERO,&_FIFTEEN)); - P(ImGui::SliderScalar("Sustain",ImGuiDataType_U8,&ins->c64.s,&_ZERO,&_FIFTEEN)); - P(ImGui::SliderScalar("Release",ImGuiDataType_U8,&ins->c64.r,&_ZERO,&_FIFTEEN)); - P(ImGui::SliderScalar("Duty",ImGuiDataType_U16,&ins->c64.duty,&_ZERO,&_FOUR_THOUSAND_NINETY_FIVE)); + P(ImGui::SliderScalar("Attack",ImGuiDataType_U8,&ins->c64.a,&_ZERO,&_FIFTEEN)); rightClickable + P(ImGui::SliderScalar("Decay",ImGuiDataType_U8,&ins->c64.d,&_ZERO,&_FIFTEEN)); rightClickable + P(ImGui::SliderScalar("Sustain",ImGuiDataType_U8,&ins->c64.s,&_ZERO,&_FIFTEEN)); rightClickable + P(ImGui::SliderScalar("Release",ImGuiDataType_U8,&ins->c64.r,&_ZERO,&_FIFTEEN)); rightClickable + P(ImGui::SliderScalar("Duty",ImGuiDataType_U16,&ins->c64.duty,&_ZERO,&_FOUR_THOUSAND_NINETY_FIVE)); rightClickable bool ringMod=ins->c64.ringMod; if (ImGui::Checkbox("Ring Modulation",&ringMod)) { PARAMETER @@ -1170,8 +1170,8 @@ void FurnaceGUI::drawInsEdit() { P(ImGui::Checkbox("Enable filter",&ins->c64.toFilter)); P(ImGui::Checkbox("Initialize filter",&ins->c64.initFilter)); - P(ImGui::SliderScalar("Cutoff",ImGuiDataType_U16,&ins->c64.cut,&_ZERO,&_TWO_THOUSAND_FORTY_SEVEN)); - P(ImGui::SliderScalar("Resonance",ImGuiDataType_U8,&ins->c64.res,&_ZERO,&_FIFTEEN)); + P(ImGui::SliderScalar("Cutoff",ImGuiDataType_U16,&ins->c64.cut,&_ZERO,&_TWO_THOUSAND_FORTY_SEVEN)); rightClickable + P(ImGui::SliderScalar("Resonance",ImGuiDataType_U8,&ins->c64.res,&_ZERO,&_FIFTEEN)); rightClickable ImGui::Text("Filter Mode"); ImGui::SameLine(); diff --git a/src/gui/settings.cpp b/src/gui/settings.cpp index 52faa7b95..136e95122 100644 --- a/src/gui/settings.cpp +++ b/src/gui/settings.cpp @@ -292,7 +292,7 @@ void FurnaceGUI::drawSettings() { if (ImGui::SliderFloat("UI scaling factor",&settings.dpiScale,1.0f,3.0f,"%.2fx")) { if (settings.dpiScale<0.5f) settings.dpiScale=0.5f; if (settings.dpiScale>3.0f) settings.dpiScale=3.0f; - } + } rightClickable } ImGui::Text("Main font"); ImGui::SameLine(); From f85c6913cece809df21522a261437449a2e871fa Mon Sep 17 00:00:00 2001 From: tildearrow Date: Mon, 28 Feb 2022 22:49:03 -0500 Subject: [PATCH 18/50] update doc --- papers/doc/2-interface/components.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/papers/doc/2-interface/components.md b/papers/doc/2-interface/components.md index 8ff73e3fe..0243662b8 100644 --- a/papers/doc/2-interface/components.md +++ b/papers/doc/2-interface/components.md @@ -80,4 +80,4 @@ TODO: image sliders are used for controlling values in a quick manner by being dragged. -alternatively, Ctrl-clicking a slider (Command-click on macOS) will turn it into a number input field for a short period of time, allowing you to input fine values. +alternatively, right-clicking or Ctrl-clicking or a slider (Command-click on macOS) will turn it into a number input field for a short period of time, allowing you to input fine values. From e80de1487e12ea43c7db17876a6d98bd784ebb17 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Mon, 28 Feb 2022 23:31:49 -0500 Subject: [PATCH 19/50] i think I got it but there is a leak --- src/engine/platform/opll.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/engine/platform/opll.cpp b/src/engine/platform/opll.cpp index 0ca5a6c2b..d533d411f 100644 --- a/src/engine/platform/opll.cpp +++ b/src/engine/platform/opll.cpp @@ -76,7 +76,7 @@ const char* DivPlatformOPLL::getEffectName(unsigned char effect) { } const unsigned char cycleMapOPLL[18]={ - 1, 2, 0, 1, 2, 3, 4, 5, 3, 4, 5, 6, 7, 8, 6, 7, 8, 0 + 5, 6, 7, 8, 6, 7, 8, 0, 1, 2, 0, 1, 2, 3, 4, 5, 3, 4 }; void DivPlatformOPLL::acquire_nuked(short* bufL, short* bufR, size_t start, size_t len) { @@ -103,7 +103,7 @@ void DivPlatformOPLL::acquire_nuked(short* bufL, short* bufR, size_t start, size } } - unsigned char nextOut=cycleMapOPLL[(fm.cycles+17)%18]; + unsigned char nextOut=cycleMapOPLL[fm.cycles]; OPLL_Clock(&fm,o); if (!isMuted[nextOut]) { os+=(o[0]+o[1]); From 105aed5a503a02870b5a4bcf0e2fd987f4c11fff Mon Sep 17 00:00:00 2001 From: tildearrow Date: Tue, 1 Mar 2022 00:00:30 -0500 Subject: [PATCH 20/50] OPLL: 94% muting works (mostly) TODO: - FM macros - proper drums --- src/engine/platform/opll.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/engine/platform/opll.cpp b/src/engine/platform/opll.cpp index d533d411f..a67ff111f 100644 --- a/src/engine/platform/opll.cpp +++ b/src/engine/platform/opll.cpp @@ -76,7 +76,7 @@ const char* DivPlatformOPLL::getEffectName(unsigned char effect) { } const unsigned char cycleMapOPLL[18]={ - 5, 6, 7, 8, 6, 7, 8, 0, 1, 2, 0, 1, 2, 3, 4, 5, 3, 4 + 8, 7, 6, 7, 8, 7, 8, 6, 0, 1, 2, 7, 8, 9, 3, 4, 5, 9 }; void DivPlatformOPLL::acquire_nuked(short* bufL, short* bufR, size_t start, size_t len) { @@ -103,8 +103,8 @@ void DivPlatformOPLL::acquire_nuked(short* bufL, short* bufR, size_t start, size } } - unsigned char nextOut=cycleMapOPLL[fm.cycles]; OPLL_Clock(&fm,o); + unsigned char nextOut=cycleMapOPLL[fm.cycles]; if (!isMuted[nextOut]) { os+=(o[0]+o[1]); } From d9ae033f32b9df3763d8b02821cf1c12695481c3 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Tue, 1 Mar 2022 00:56:13 -0500 Subject: [PATCH 21/50] OPLL: 95% FM macros working TODO: - proper drums --- src/engine/macroInt.cpp | 52 ++++++++++++++ src/engine/macroInt.h | 38 +++++++++- src/engine/platform/opll.cpp | 135 ++++++++++++++++++----------------- src/gui/insEdit.cpp | 14 ++-- 4 files changed, 167 insertions(+), 72 deletions(-) diff --git a/src/engine/macroInt.cpp b/src/engine/macroInt.cpp index 8ae31411b..adcb33139 100644 --- a/src/engine/macroInt.cpp +++ b/src/engine/macroInt.cpp @@ -44,6 +44,7 @@ } \ } +// CPU hell void DivMacroInt::next() { if (ins==NULL) return; @@ -79,6 +80,16 @@ void DivMacroInt::next() { doMacro(o.finishedDt,o.hadDt,o.hasDt,o.dt,o.dtPos,m.dtMacro,m.dtMacroLen,m.dtMacroLoop,m.dtMacroRel); doMacro(o.finishedD2r,o.hadD2r,o.hasD2r,o.d2r,o.d2rPos,m.d2rMacro,m.d2rMacroLen,m.d2rMacroLoop,m.d2rMacroRel); doMacro(o.finishedSsg,o.hadSsg,o.hasSsg,o.ssg,o.ssgPos,m.ssgMacro,m.ssgMacroLen,m.ssgMacroLoop,m.ssgMacroRel); + + doMacro(o.finishedDam,o.hadDam,o.hasDam,o.dam,o.damPos,m.damMacro,m.damMacroLen,m.damMacroLoop,m.damMacroRel); + doMacro(o.finishedDvb,o.hadDvb,o.hasDvb,o.dvb,o.dvbPos,m.dvbMacro,m.dvbMacroLen,m.dvbMacroLoop,m.dvbMacroRel); + doMacro(o.finishedEgt,o.hadEgt,o.hasEgt,o.egt,o.egtPos,m.egtMacro,m.egtMacroLen,m.egtMacroLoop,m.egtMacroRel); + doMacro(o.finishedKsl,o.hadKsl,o.hasKsl,o.ksl,o.kslPos,m.kslMacro,m.kslMacroLen,m.kslMacroLoop,m.kslMacroRel); + + doMacro(o.finishedSus,o.hadSus,o.hasSus,o.sus,o.susPos,m.susMacro,m.susMacroLen,m.susMacroLoop,m.susMacroRel); + doMacro(o.finishedVib,o.hadVib,o.hasVib,o.vib,o.vibPos,m.vibMacro,m.vibMacroLen,m.vibMacroLoop,m.vibMacroRel); + doMacro(o.finishedWs,o.hadWs,o.hasWs,o.ws,o.wsPos,m.wsMacro,m.wsMacroLen,m.wsMacroLoop,m.wsMacroRel); + doMacro(o.finishedKsr,o.hadKsr,o.hasKsr,o.ksr,o.ksrPos,m.ksrMacro,m.ksrMacroLen,m.ksrMacroLoop,m.ksrMacroRel); } } @@ -280,6 +291,47 @@ void DivMacroInt::init(DivInstrument* which) { o.hasSsg=true; o.willSsg=true; } + + if (m.damMacroLen>0) { + o.hadDam=true; + o.hasDam=true; + o.willDam=true; + } + if (m.dvbMacroLen>0) { + o.hadDvb=true; + o.hasDvb=true; + o.willDvb=true; + } + if (m.egtMacroLen>0) { + o.hadEgt=true; + o.hasEgt=true; + o.willEgt=true; + } + if (m.kslMacroLen>0) { + o.hadKsl=true; + o.hasKsl=true; + o.willKsl=true; + } + if (m.susMacroLen>0) { + o.hadSus=true; + o.hasSus=true; + o.willSus=true; + } + if (m.vibMacroLen>0) { + o.hadVib=true; + o.hasVib=true; + o.willVib=true; + } + if (m.wsMacroLen>0) { + o.hadWs=true; + o.hasWs=true; + o.willWs=true; + } + if (m.ksrMacroLen>0) { + o.hadKsr=true; + o.hasKsr=true; + o.willKsr=true; + } } } diff --git a/src/engine/macroInt.h b/src/engine/macroInt.h index 60221d063..b107a4732 100644 --- a/src/engine/macroInt.h +++ b/src/engine/macroInt.h @@ -42,26 +42,38 @@ class DivMacroInt { int amPos, arPos, drPos, multPos; int rrPos, slPos, tlPos, dt2Pos; int rsPos, dtPos, d2rPos, ssgPos; + int damPos, dvbPos, egtPos, kslPos; + int susPos, vibPos, wsPos, ksrPos; int am, ar, dr, mult; int rr, sl, tl, dt2; int rs, dt, d2r, ssg; + int dam, dvb, egt, ksl; + int sus, vib, ws, ksr; bool hasAm, hasAr, hasDr, hasMult; bool hasRr, hasSl, hasTl, hasDt2; bool hasRs, hasDt, hasD2r, hasSsg; + bool hasDam, hasDvb, hasEgt, hasKsl; + bool hasSus, hasVib, hasWs, hasKsr; bool hadAm, hadAr, hadDr, hadMult; bool hadRr, hadSl, hadTl, hadDt2; bool hadRs, hadDt, hadD2r, hadSsg; + bool hadDam, hadDvb, hadEgt, hadKsl; + bool hadSus, hadVib, hadWs, hadKsr; bool finishedAm, finishedAr, finishedDr, finishedMult; bool finishedRr, finishedSl, finishedTl, finishedDt2; bool finishedRs, finishedDt, finishedD2r, finishedSsg; + bool finishedDam, finishedDvb, finishedEgt, finishedKsl; + bool finishedSus, finishedVib, finishedWs, finishedKsr; bool willAm, willAr, willDr, willMult; bool willRr, willSl, willTl, willDt2; bool willRs, willDt, willD2r, willSsg; + bool willDam, willDvb, willEgt, willKsl; + bool willSus, willVib, willWs, willKsr; IntOp(): amPos(0), arPos(0), @@ -75,6 +87,14 @@ class DivMacroInt { dtPos(0), d2rPos(0), ssgPos(0), + damPos(0), + dvbPos(0), + egtPos(0), + kslPos(0), + susPos(0), + vibPos(0), + wsPos(0), + ksrPos(0), am(0), ar(0), dr(0), @@ -87,18 +107,34 @@ class DivMacroInt { dt(0), d2r(0), ssg(0), + dam(0), + dvb(0), + egt(0), + ksl(0), + sus(0), + vib(0), + ws(0), + ksr(0), hasAm(false), hasAr(false), hasDr(false), hasMult(false), hasRr(false), hasSl(false), hasTl(false), hasDt2(false), hasRs(false), hasDt(false), hasD2r(false), hasSsg(false), + hasDam(false), hasDvb(false), hasEgt(false), hasKsl(false), + hasSus(false), hasVib(false), hasWs(false), hasKsr(false), hadAm(false), hadAr(false), hadDr(false), hadMult(false), hadRr(false), hadSl(false), hadTl(false), hadDt2(false), hadRs(false), hadDt(false), hadD2r(false), hadSsg(false), + hadDam(false), hadDvb(false), hadEgt(false), hadKsl(false), + hadSus(false), hadVib(false), hadWs(false), hadKsr(false), finishedAm(false), finishedAr(false), finishedDr(false), finishedMult(false), finishedRr(false), finishedSl(false), finishedTl(false), finishedDt2(false), finishedRs(false), finishedDt(false), finishedD2r(false), finishedSsg(false), + finishedDam(false), finishedDvb(false), finishedEgt(false), finishedKsl(false), + finishedSus(false), finishedVib(false), finishedWs(false), finishedKsr(false), willAm(false), willAr(false), willDr(false), willMult(false), willRr(false), willSl(false), willTl(false), willDt2(false), - willRs(false), willDt(false), willD2r(false), willSsg(false) {} + willRs(false), willDt(false), willD2r(false), willSsg(false), + willDam(false), willDvb(false), willEgt(false), willKsl(false), + willSus(false), willVib(false), willWs(false), willKsr(false) {} } op[4]; void release(); void next(); diff --git a/src/engine/platform/opll.cpp b/src/engine/platform/opll.cpp index a67ff111f..405e2327d 100644 --- a/src/engine/platform/opll.cpp +++ b/src/engine/platform/opll.cpp @@ -128,7 +128,7 @@ void DivPlatformOPLL::tick() { chan[i].std.next(); if (chan[i].std.hadVol) { - chan[i].outVol=(chan[i].vol*MIN(127,chan[i].std.vol))/127; + chan[i].outVol=(chan[i].vol*MIN(15,chan[i].std.vol))/15; rWrite(0x30+i,(15-(chan[i].outVol*(15-chan[i].state.op[1].tl))/15)|(chan[i].state.opllPreset<<4)); } @@ -147,77 +147,84 @@ void DivPlatformOPLL::tick() { chan[i].freqChanged=true; } } -/* - if (chan[i].std.hadAlg) { - chan[i].state.alg=chan[i].std.alg; - rWrite(chanOffs[i]+ADDR_FB_ALG,(chan[i].state.alg&7)|(chan[i].state.fb<<3)); - } - if (chan[i].std.hadFb) { - chan[i].state.fb=chan[i].std.fb; - } - if (chan[i].std.hadFms) { - chan[i].state.fms=chan[i].std.fms; - } - if (chan[i].std.hadAms) { - chan[i].state.ams=chan[i].std.ams; - } - for (int j=0; j<2; j++) { - unsigned short baseAddr=chanOffs[i]|opOffs[j]; - DivInstrumentFM::Operator& op=chan[i].state.op[j]; - DivMacroInt::IntOp& m=chan[i].std.op[j]; - if (m.hadAm) { - op.am=m.am; - rWrite(baseAddr+ADDR_AM_DR,(op.dr&31)|(op.am<<7)); + + if (chan[i].state.opllPreset==0) { + if (chan[i].std.hadAlg) { // SUS + chan[i].state.alg=chan[i].std.alg; + chan[i].freqChanged=true; } - if (m.hadAr) { - op.ar=m.ar; - rWrite(baseAddr+ADDR_RS_AR,(op.ar&31)|(op.rs<<6)); + if (chan[i].std.hadFb) { + chan[i].state.fb=chan[i].std.fb; + rWrite(0x03,(chan[i].state.op[0].ksl<<6)|((chan[i].state.fms&1)<<4)|((chan[i].state.ams&1)<<3)|chan[i].state.fb); } - if (m.hadDr) { - op.dr=m.dr; - rWrite(baseAddr+ADDR_AM_DR,(op.dr&31)|(op.am<<7)); + if (chan[i].std.hadFms) { + chan[i].state.fms=chan[i].std.fms; + rWrite(0x03,(chan[i].state.op[0].ksl<<6)|((chan[i].state.fms&1)<<4)|((chan[i].state.ams&1)<<3)|chan[i].state.fb); } - if (m.hadMult) { - op.mult=m.mult; - rWrite(baseAddr+ADDR_MULT_DT,(op.mult&15)|(dtTable[op.dt&7]<<4)); + if (chan[i].std.hadAms) { + chan[i].state.ams=chan[i].std.ams; + rWrite(0x03,(chan[i].state.op[0].ksl<<6)|((chan[i].state.fms&1)<<4)|((chan[i].state.ams&1)<<3)|chan[i].state.fb); } - if (m.hadRr) { - op.rr=m.rr; - rWrite(baseAddr+ADDR_SL_RR,(op.rr&15)|(op.sl<<4)); - } - if (m.hadSl) { - op.sl=m.sl; - rWrite(baseAddr+ADDR_SL_RR,(op.rr&15)|(op.sl<<4)); - } - if (m.hadTl) { - op.tl=127-m.tl; - if (isMuted[i]) { - rWrite(baseAddr+ADDR_TL,127); - } else { - if (isOutput[chan[i].state.alg][j]) { - rWrite(baseAddr+ADDR_TL,127-(((127-op.tl)*(chan[i].outVol&0x7f))/127)); + + for (int j=0; j<2; j++) { + DivInstrumentFM::Operator& op=chan[i].state.op[j]; + DivMacroInt::IntOp& m=chan[i].std.op[j]; + + if (m.hadAm) { + op.am=m.am; + rWrite(0x00+j,(op.am<<7)|(op.vib<<6)|((op.ssgEnv&8)<<2)|(op.ksr<<4)|(op.mult)); + } + if (m.hadAr) { + op.ar=m.ar; + rWrite(0x04+j,(op.ar<<4)|(op.dr)); + } + if (m.hadDr) { + op.dr=m.dr; + rWrite(0x04+j,(op.ar<<4)|(op.dr)); + } + if (m.hadMult) { + op.mult=m.mult; + rWrite(0x00+j,(op.am<<7)|(op.vib<<6)|((op.ssgEnv&8)<<2)|(op.ksr<<4)|(op.mult)); + } + if (m.hadRr) { + op.rr=m.rr; + rWrite(0x06+j,(op.sl<<4)|(op.rr)); + } + if (m.hadSl) { + op.sl=m.sl; + rWrite(0x06+j,(op.sl<<4)|(op.rr)); + } + if (m.hadTl) { + op.tl=((j==1)?15:63)-m.tl; + if (j==1) { + rWrite(0x30+i,(15-(chan[i].outVol*(15-chan[i].state.op[1].tl))/15)|(chan[i].state.opllPreset<<4)); } else { - rWrite(baseAddr+ADDR_TL,op.tl); + rWrite(0x02,(chan[i].state.op[1].ksl<<6)|(op.tl&63)); } } + + if (m.hadEgt) { + op.ssgEnv=(m.egt&1)?8:0; + rWrite(0x00+j,(op.am<<7)|(op.vib<<6)|((op.ssgEnv&8)<<2)|(op.ksr<<4)|(op.mult)); + } + if (m.hadKsl) { + op.ksl=m.ksl; + if (j==1) { + rWrite(0x02,(op.ksl<<6)|(chan[i].state.op[0].tl&63)); + } else { + rWrite(0x03,(chan[i].state.op[0].ksl<<6)|((chan[i].state.fms&1)<<4)|((chan[i].state.ams&1)<<3)|chan[i].state.fb); + } + } + if (m.hadKsr) { + op.ksr=m.ksr; + rWrite(0x00+j,(op.am<<7)|(op.vib<<6)|((op.ssgEnv&8)<<2)|(op.ksr<<4)|(op.mult)); + } + if (m.hadVib) { + op.vib=m.vib; + rWrite(0x00+j,(op.am<<7)|(op.vib<<6)|((op.ssgEnv&8)<<2)|(op.ksr<<4)|(op.mult)); + } } - if (m.hadRs) { - op.rs=m.rs; - rWrite(baseAddr+ADDR_RS_AR,(op.ar&31)|(op.rs<<6)); - } - if (m.hadDt) { - op.dt=m.dt; - rWrite(baseAddr+ADDR_MULT_DT,(op.mult&15)|(dtTable[op.dt&7]<<4)); - } - if (m.hadD2r) { - op.d2r=m.d2r; - rWrite(baseAddr+ADDR_DT2_D2R,op.d2r&31); - } - if (m.hadSsg) { - op.ssgEnv=m.ssg; - rWrite(baseAddr+ADDR_SSG,op.ssgEnv&15); - } - }*/ + } if (chan[i].keyOn || chan[i].keyOff) { if (i>=6 && drums) { diff --git a/src/gui/insEdit.cpp b/src/gui/insEdit.cpp index 4608f89b1..408c3c150 100644 --- a/src/gui/insEdit.cpp +++ b/src/gui/insEdit.cpp @@ -1037,10 +1037,10 @@ void FurnaceGUI::drawInsEdit() { if (ImGui::BeginTabItem("FM Macros")) { MACRO_BEGIN(0); if (ins->type==DIV_INS_OPLL) { - NORMAL_MACRO(ins->std.algMacro,ins->std.algMacroLen,ins->std.algMacroLoop,ins->std.algMacroRel,0,1,"alg",FM_NAME(FM_SUS),16,ins->std.algMacroOpen,true,oneBit,false,NULL,0,0,0,NULL,uiColors[GUI_COLOR_MACRO_OTHER],mmlString[0],0,1,NULL,false); + NORMAL_MACRO(ins->std.algMacro,ins->std.algMacroLen,ins->std.algMacroLoop,ins->std.algMacroRel,0,1,"alg",FM_NAME(FM_SUS),32,ins->std.algMacroOpen,true,NULL,false,NULL,0,0,0,NULL,uiColors[GUI_COLOR_MACRO_OTHER],mmlString[0],0,1,NULL,false); NORMAL_MACRO(ins->std.fbMacro,ins->std.fbMacroLen,ins->std.fbMacroLoop,ins->std.fbMacroRel,0,7,"fb",FM_NAME(FM_FB),96,ins->std.fbMacroOpen,false,NULL,false,NULL,0,0,0,NULL,uiColors[GUI_COLOR_MACRO_OTHER],mmlString[1],0,7,NULL,false); - NORMAL_MACRO(ins->std.fmsMacro,ins->std.fmsMacroLen,ins->std.fmsMacroLoop,ins->std.fmsMacroRel,0,1,"fms",FM_NAME(FM_DC),16,ins->std.fmsMacroOpen,true,oneBit,false,NULL,0,0,0,NULL,uiColors[GUI_COLOR_MACRO_OTHER],mmlString[2],0,1,NULL,false); - NORMAL_MACRO(ins->std.amsMacro,ins->std.amsMacroLen,ins->std.amsMacroLoop,ins->std.amsMacroRel,0,1,"ams",FM_NAME(FM_DM),16,ins->std.amsMacroOpen,true,oneBit,false,NULL,0,0,0,NULL,uiColors[GUI_COLOR_MACRO_OTHER],mmlString[3],0,1,NULL,false); + NORMAL_MACRO(ins->std.fmsMacro,ins->std.fmsMacroLen,ins->std.fmsMacroLoop,ins->std.fmsMacroRel,0,1,"fms",FM_NAME(FM_DC),32,ins->std.fmsMacroOpen,true,NULL,false,NULL,0,0,0,NULL,uiColors[GUI_COLOR_MACRO_OTHER],mmlString[2],0,1,NULL,false); + NORMAL_MACRO(ins->std.amsMacro,ins->std.amsMacroLen,ins->std.amsMacroLoop,ins->std.amsMacroRel,0,1,"ams",FM_NAME(FM_DM),32,ins->std.amsMacroOpen,true,NULL,false,NULL,0,0,0,NULL,uiColors[GUI_COLOR_MACRO_OTHER],mmlString[3],0,1,NULL,false); } else { NORMAL_MACRO(ins->std.algMacro,ins->std.algMacroLen,ins->std.algMacroLoop,ins->std.algMacroRel,0,7,"alg",FM_NAME(FM_ALG),96,ins->std.algMacroOpen,false,NULL,false,NULL,0,0,0,NULL,uiColors[GUI_COLOR_MACRO_OTHER],mmlString[0],0,7,NULL,false); NORMAL_MACRO(ins->std.fbMacro,ins->std.fbMacroLen,ins->std.fbMacroLoop,ins->std.fbMacroRel,0,7,"fb",FM_NAME(FM_FB),96,ins->std.fbMacroOpen,false,NULL,false,NULL,0,0,0,NULL,uiColors[GUI_COLOR_MACRO_OTHER],mmlString[1],0,7,NULL,false); @@ -1082,10 +1082,10 @@ void FurnaceGUI::drawInsEdit() { OP_MACRO(ins->std.opMacros[ordi].kslMacro,ins->std.opMacros[ordi].kslMacroLen,ins->std.opMacros[ordi].kslMacroLoop,ins->std.opMacros[ordi].kslMacroRel,3,ordi,"ksl",FM_NAME(FM_KSL),32,ins->std.opMacros[ordi].kslMacroOpen,false,NULL,mmlString[6]); OP_MACRO(ins->std.opMacros[ordi].multMacro,ins->std.opMacros[ordi].multMacroLen,ins->std.opMacros[ordi].multMacroLoop,ins->std.opMacros[ordi].multMacroRel,15,ordi,"mult",FM_NAME(FM_MULT),64,ins->std.opMacros[ordi].multMacroOpen,false,NULL,mmlString[7]); - OP_MACRO(ins->std.opMacros[ordi].amMacro,ins->std.opMacros[ordi].amMacroLen,ins->std.opMacros[ordi].amMacroLoop,ins->std.opMacros[ordi].amMacroRel,1,ordi,"am",FM_NAME(FM_AM),16,ins->std.opMacros[ordi].amMacroOpen,true,NULL,mmlString[8]); - OP_MACRO(ins->std.opMacros[ordi].vibMacro,ins->std.opMacros[ordi].vibMacroLen,ins->std.opMacros[ordi].vibMacroLoop,ins->std.opMacros[ordi].vibMacroRel,4,ordi,"vib",FM_NAME(FM_VIB),16,ins->std.opMacros[ordi].vibMacroOpen,true,oneBit,mmlString[9]); - OP_MACRO(ins->std.opMacros[ordi].ksrMacro,ins->std.opMacros[ordi].ksrMacroLen,ins->std.opMacros[ordi].ksrMacroLoop,ins->std.opMacros[ordi].ksrMacroRel,4,ordi,"ksr",FM_NAME(FM_KSR),16,ins->std.opMacros[ordi].ksrMacroOpen,true,oneBit,mmlString[10]); - OP_MACRO(ins->std.opMacros[ordi].egtMacro,ins->std.opMacros[ordi].egtMacroLen,ins->std.opMacros[ordi].egtMacroLoop,ins->std.opMacros[ordi].egtMacroRel,4,ordi,"egt",FM_NAME(FM_EGS),16,ins->std.opMacros[ordi].egtMacroOpen,true,oneBit,mmlString[11]); + OP_MACRO(ins->std.opMacros[ordi].amMacro,ins->std.opMacros[ordi].amMacroLen,ins->std.opMacros[ordi].amMacroLoop,ins->std.opMacros[ordi].amMacroRel,1,ordi,"am",FM_NAME(FM_AM),32,ins->std.opMacros[ordi].amMacroOpen,true,NULL,mmlString[8]); + OP_MACRO(ins->std.opMacros[ordi].vibMacro,ins->std.opMacros[ordi].vibMacroLen,ins->std.opMacros[ordi].vibMacroLoop,ins->std.opMacros[ordi].vibMacroRel,1,ordi,"vib",FM_NAME(FM_VIB),32,ins->std.opMacros[ordi].vibMacroOpen,true,NULL,mmlString[9]); + OP_MACRO(ins->std.opMacros[ordi].ksrMacro,ins->std.opMacros[ordi].ksrMacroLen,ins->std.opMacros[ordi].ksrMacroLoop,ins->std.opMacros[ordi].ksrMacroRel,1,ordi,"ksr",FM_NAME(FM_KSR),32,ins->std.opMacros[ordi].ksrMacroOpen,true,NULL,mmlString[10]); + OP_MACRO(ins->std.opMacros[ordi].egtMacro,ins->std.opMacros[ordi].egtMacroLen,ins->std.opMacros[ordi].egtMacroLoop,ins->std.opMacros[ordi].egtMacroRel,1,ordi,"egt",FM_NAME(FM_EGS),32,ins->std.opMacros[ordi].egtMacroOpen,true,NULL,mmlString[11]); } else { OP_MACRO(ins->std.opMacros[ordi].tlMacro,ins->std.opMacros[ordi].tlMacroLen,ins->std.opMacros[ordi].tlMacroLoop,ins->std.opMacros[ordi].tlMacroRel,maxTl,ordi,"tl",FM_NAME(FM_TL),128,ins->std.opMacros[ordi].tlMacroOpen,false,NULL,mmlString[0]); OP_MACRO(ins->std.opMacros[ordi].arMacro,ins->std.opMacros[ordi].arMacroLen,ins->std.opMacros[ordi].arMacroLoop,ins->std.opMacros[ordi].arMacroRel,maxArDr,ordi,"ar",FM_NAME(FM_AR),64,ins->std.opMacros[ordi].arMacroOpen,false,NULL,mmlString[1]); From 2605e8670ce426efa17ce6772a49b3a324c77081 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Tue, 1 Mar 2022 01:00:33 -0500 Subject: [PATCH 22/50] OPLL: small order change fix --- src/engine/platform/opll.cpp | 4 +++- src/engine/platform/opll.h | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/engine/platform/opll.cpp b/src/engine/platform/opll.cpp index 405e2327d..03db96c23 100644 --- a/src/engine/platform/opll.cpp +++ b/src/engine/platform/opll.cpp @@ -358,6 +358,7 @@ int DivPlatformOPLL::dispatch(DivCommand c) { rWrite(0x05,(car.ar<<4)|(car.dr)); rWrite(0x06,(mod.sl<<4)|(mod.rr)); rWrite(0x07,(car.sl<<4)|(car.rr)); + lastCustomMemory=c.chan; } if (chan[c.chan].state.opllPreset==16) { // compatible drums mode if (c.chan>=6) { @@ -564,7 +565,7 @@ int DivPlatformOPLL::dispatch(DivCommand c) { void DivPlatformOPLL::forceIns() { for (int i=0; i<9; i++) { // update custom preset - if (chan[i].state.opllPreset==0) { + if (chan[i].state.opllPreset==0 && i==lastCustomMemory) { DivInstrumentFM::Operator& mod=chan[i].state.op[0]; DivInstrumentFM::Operator& car=chan[i].state.op[1]; rWrite(0x00,(mod.am<<7)|(mod.vib<<6)|((mod.ssgEnv&8)<<2)|(mod.ksr<<4)|(mod.mult)); @@ -645,6 +646,7 @@ void DivPlatformOPLL::reset() { lastBusy=60; drumState=0; + lastCustomMemory=-1; drumVol[0]=0; drumVol[1]=0; diff --git a/src/engine/platform/opll.h b/src/engine/platform/opll.h index 87e970579..0bada9036 100644 --- a/src/engine/platform/opll.h +++ b/src/engine/platform/opll.h @@ -67,7 +67,7 @@ class DivPlatformOPLL: public DivDispatch { }; std::queue writes; opll_t fm; - int delay; + int delay, lastCustomMemory; unsigned char lastBusy; unsigned char drumState; unsigned char drumVol[5]; From ee5b24dcaf180890f90c331826a5f0fe3924b980 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Tue, 1 Mar 2022 02:38:10 -0500 Subject: [PATCH 23/50] describe FM param structure --- src/engine/instrument.h | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/src/engine/instrument.h b/src/engine/instrument.h index d5dbc8a63..aef84f202 100644 --- a/src/engine/instrument.h +++ b/src/engine/instrument.h @@ -50,11 +50,26 @@ enum DivInstrumentType { DIV_INS_MIKEY=23, }; +// FM operator structure: +// - OPN: +// - AM, AR, DR, MULT, RR, SL, TL, RS, DT, D2R, SSG-EG +// - OPM: +// - AM, AR, DR, MULT, RR, SL, TL, DT2, RS, DT, D2R +// - OPLL: +// - AM, AR, DR, MULT, RR, SL, TL, SSG-EG&8 = EG-S +// - KSL, VIB, KSR +// - OPL: +// - AM, AR, DR, MULT, RR, SL, TL, SSG-EG&8 = EG-S +// - KSL, VIB, WS (OPL2/3), KSR +// - OPZ: NOT FINAL! +// - AM, AR, DR, MULT (CRS), RR, SL, TL, DT2, RS, DT, D2R +// - KSL = LS, WS, DVB = MULT (FINE), DAM = REV, EGT = EGShift + struct DivInstrumentFM { unsigned char alg, fb, fms, ams, ops, opllPreset; struct Operator { 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/OPL + unsigned char dam, dvb, egt, ksl, sus, vib, ws, ksr; // YMU759/OPL/OPZ Operator(): am(0), ar(0), From 4ff056c64f7a4923186d53d2f3b092d2e723f081 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Tue, 1 Mar 2022 02:38:19 -0500 Subject: [PATCH 24/50] NES: how is this not initialized?! --- src/engine/platform/sound/nes/apu.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/engine/platform/sound/nes/apu.c b/src/engine/platform/sound/nes/apu.c index a278cbf74..70eddc8e7 100644 --- a/src/engine/platform/sound/nes/apu.c +++ b/src/engine/platform/sound/nes/apu.c @@ -226,4 +226,6 @@ void apu_turn_on(struct NESAPU* a, BYTE apu_type) { a->DMC.length = 1; a->DMC.address_start = 0xC000; a->apu.odd_cycle = 0; + // come non viene inizializzato? Vorrei qualche spiegazione... + a->r4011.frames = 0; } From 236ab5b3feb5fd59368efb93a9b152c7500fd5d8 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Tue, 1 Mar 2022 02:38:37 -0500 Subject: [PATCH 25/50] QSound: initialize memory to 0 for some reason this fixes some glitches? --- src/engine/engine.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/engine/engine.cpp b/src/engine/engine.cpp index 87fac32dc..ad0548399 100644 --- a/src/engine/engine.cpp +++ b/src/engine/engine.cpp @@ -501,6 +501,7 @@ void DivEngine::renderSamples() { // step 4: allocate qsound pcm samples if (qsoundMem==NULL) qsoundMem=new unsigned char[16777216]; + memset(qsoundMem,0,16777216); memPos=0; for (int i=0; i Date: Tue, 1 Mar 2022 04:33:02 -0500 Subject: [PATCH 26/50] prepare for OPLL proper drums mode --- src/gui/gui.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/gui/gui.cpp b/src/gui/gui.cpp index b002afb9c..7c3aec199 100644 --- a/src/gui/gui.cpp +++ b/src/gui/gui.cpp @@ -4496,6 +4496,7 @@ bool FurnaceGUI::loop() { sysAddOption(DIV_SYSTEM_AY8910); sysAddOption(DIV_SYSTEM_AMIGA); sysAddOption(DIV_SYSTEM_OPLL); + sysAddOption(DIV_SYSTEM_OPLL_DRUMS); sysAddOption(DIV_SYSTEM_VRC7); sysAddOption(DIV_SYSTEM_TIA); sysAddOption(DIV_SYSTEM_SAA1099); @@ -4766,6 +4767,7 @@ bool FurnaceGUI::loop() { sysChangeOption(i,DIV_SYSTEM_AY8910); sysChangeOption(i,DIV_SYSTEM_AMIGA); sysChangeOption(i,DIV_SYSTEM_OPLL); + sysChangeOption(i,DIV_SYSTEM_OPLL_DRUMS); sysChangeOption(i,DIV_SYSTEM_VRC7); sysChangeOption(i,DIV_SYSTEM_TIA); sysChangeOption(i,DIV_SYSTEM_SAA1099); From 6dfa208a367e85566ca7fd1dfc40fcfef560ea11 Mon Sep 17 00:00:00 2001 From: freq-mod <32672779+freq-mod@users.noreply.github.com> Date: Tue, 1 Mar 2022 12:07:38 +0100 Subject: [PATCH 27/50] remove "standalone" from YM2612 and YM2151 given the way it's done, it's not needed anymore i guess --- src/engine/sysDef.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/engine/sysDef.cpp b/src/engine/sysDef.cpp index 54589e37d..689af53cf 100644 --- a/src/engine/sysDef.cpp +++ b/src/engine/sysDef.cpp @@ -673,9 +673,9 @@ const char* DivEngine::getSystemChips(DivSystem sys) { case DIV_SYSTEM_AMIGA: return "MOS 8364 Paula"; case DIV_SYSTEM_YM2151: - return "Yamaha YM2151 standalone"; + return "Yamaha YM2151"; case DIV_SYSTEM_YM2612: - return "Yamaha YM2612 standalone"; + return "Yamaha YM2612"; case DIV_SYSTEM_TIA: return "Atari TIA"; case DIV_SYSTEM_VIC20: From 7dae9058d70f7d4da9516d405eaaa1d88bfcf473 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Tue, 1 Mar 2022 14:16:40 -0500 Subject: [PATCH 28/50] OPLL: 97% proper drums mode kinda works --- src/engine/platform/opll.cpp | 61 +++++++++++++++++++++++++++--------- 1 file changed, 46 insertions(+), 15 deletions(-) diff --git a/src/engine/platform/opll.cpp b/src/engine/platform/opll.cpp index 03db96c23..f8b542909 100644 --- a/src/engine/platform/opll.cpp +++ b/src/engine/platform/opll.cpp @@ -79,6 +79,10 @@ const unsigned char cycleMapOPLL[18]={ 8, 7, 6, 7, 8, 7, 8, 6, 0, 1, 2, 7, 8, 9, 3, 4, 5, 9 }; +const unsigned char drumSlot[11]={ + 0, 0, 0, 0, 0, 0, 6, 7, 7, 8, 8 +}; + void DivPlatformOPLL::acquire_nuked(short* bufL, short* bufR, size_t start, size_t len) { static int o[2]; static int os; @@ -124,12 +128,12 @@ void DivPlatformOPLL::acquire(short* bufL, short* bufR, size_t start, size_t len } void DivPlatformOPLL::tick() { - for (int i=0; i<9; i++) { + for (int i=0; i<11; i++) { chan[i].std.next(); if (chan[i].std.hadVol) { chan[i].outVol=(chan[i].vol*MIN(15,chan[i].std.vol))/15; - rWrite(0x30+i,(15-(chan[i].outVol*(15-chan[i].state.op[1].tl))/15)|(chan[i].state.opllPreset<<4)); + rWrite(0x30+i,((15-(chan[i].outVol*(15-chan[i].state.op[1].tl))/15)&15)|(chan[i].state.opllPreset<<4)); } if (chan[i].std.hadArp) { @@ -197,7 +201,7 @@ void DivPlatformOPLL::tick() { if (m.hadTl) { op.tl=((j==1)?15:63)-m.tl; if (j==1) { - rWrite(0x30+i,(15-(chan[i].outVol*(15-chan[i].state.op[1].tl))/15)|(chan[i].state.opllPreset<<4)); + rWrite(0x30+i,((15-(chan[i].outVol*(15-chan[i].state.op[1].tl))/15)&15)|(chan[i].state.opllPreset<<4)); } else { rWrite(0x02,(chan[i].state.op[1].ksl<<6)|(op.tl&63)); } @@ -227,7 +231,10 @@ void DivPlatformOPLL::tick() { } if (chan[i].keyOn || chan[i].keyOff) { - if (i>=6 && drums) { + if (i>=6 && properDrums) { + drumState&=~(0x10>>(i-6)); + immWrite(0x0e,0x20|drumState); + } else if (i>=6 && drums) { drumState&=~(0x10>>(chan[i].note%12)); immWrite(0x0e,0x20|drumState); } else { @@ -245,18 +252,25 @@ void DivPlatformOPLL::tick() { } } - for (int i=0; i<9; i++) { + for (int i=0; i<11; i++) { if (chan[i].freqChanged) { chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,false,octave(chan[i].baseFreq)); if (chan[i].freq>262143) chan[i].freq=262143; int freqt=toFreq(chan[i].freq); chan[i].freqH=freqt>>8; chan[i].freqL=freqt&0xff; - if (i<6 || !drums) { + if (i>=6 && properDrums) { + immWrite(0x10+drumSlot[i],freqt&0xff); + immWrite(0x20+drumSlot[i],freqt>>8); + } else if (i<6 || !drums) { immWrite(0x10+i,freqt&0xff); } } - if (chan[i].keyOn && i>=6 && drums) { + if (chan[i].keyOn && i>=6 && properDrums) { + drumState|=(0x10>>(i-6)); + immWrite(0x0e,0x20|drumState); + chan[i].keyOn=false; + } else if (chan[i].keyOn && i>=6 && drums) { //printf("%d\n",chan[i].note%12); drumState|=(0x10>>(chan[i].note%12)); immWrite(0x0e,0x20|drumState); @@ -333,6 +347,7 @@ void DivPlatformOPLL::muteChannel(int ch, bool mute) { } int DivPlatformOPLL::dispatch(DivCommand c) { + if (c.chan>=9 && !properDrums) return 0; switch (c.cmd) { case DIV_CMD_NOTE_ON: { DivInstrument* ins=parent->getIns(chan[c.chan].ins); @@ -344,6 +359,18 @@ int DivPlatformOPLL::dispatch(DivCommand c) { if (!chan[c.chan].std.willVol) { chan[c.chan].outVol=chan[c.chan].vol; } + + if (c.chan>=6 && properDrums) { // drums mode + chan[c.chan].insChanged=false; + if (c.value!=DIV_NOTE_NULL) { + chan[c.chan].baseFreq=NOTE_FREQUENCY(c.value); + chan[c.chan].note=c.value; + chan[c.chan].freqChanged=true; + } + chan[c.chan].keyOn=true; + chan[c.chan].active=true; + break; + } if (chan[c.chan].insChanged) { // update custom preset @@ -379,7 +406,7 @@ int DivPlatformOPLL::dispatch(DivCommand c) { drums=false; immWrite(0x0e,0); } - rWrite(0x30+c.chan,(15-(chan[c.chan].outVol*(15-chan[c.chan].state.op[1].tl))/15)|(chan[c.chan].state.opllPreset<<4)); + rWrite(0x30+c.chan,((15-(chan[c.chan].outVol*(15-chan[c.chan].state.op[1].tl))/15)&15)|(chan[c.chan].state.opllPreset<<4)); } } @@ -437,7 +464,7 @@ int DivPlatformOPLL::dispatch(DivCommand c) { chan[c.chan].outVol=c.value; } if (c.chan<6 || !drums) { - rWrite(0x30+c.chan,(15-(chan[c.chan].outVol*(15-chan[c.chan].state.op[1].tl))/15)|(chan[c.chan].state.opllPreset<<4)); + rWrite(0x30+c.chan,((15-(chan[c.chan].outVol*(15-chan[c.chan].state.op[1].tl))/15)&15)|(chan[c.chan].state.opllPreset<<4)); } break; } @@ -510,7 +537,7 @@ int DivPlatformOPLL::dispatch(DivCommand c) { } else { DivInstrumentFM::Operator& car=chan[c.chan].state.op[1]; car.mult=c.value2&15; - rWrite(0x30+c.chan,(15-(chan[c.chan].outVol*(15-chan[c.chan].state.op[1].tl))/15)|(chan[c.chan].state.opllPreset<<4)); + rWrite(0x01,(car.am<<7)|(car.vib<<6)|((car.ssgEnv&8)<<2)|(car.ksr<<4)|(car.mult)); } break; } @@ -523,7 +550,7 @@ int DivPlatformOPLL::dispatch(DivCommand c) { } else { DivInstrumentFM::Operator& car=chan[c.chan].state.op[1]; car.tl=c.value2&15; - rWrite(0x01,(car.am<<7)|(car.vib<<6)|((car.ssgEnv&8)<<2)|(car.ksr<<4)|(car.mult)); + rWrite(0x30+c.chan,((15-(chan[c.chan].outVol*(15-chan[c.chan].state.op[1].tl))/15)&15)|(chan[c.chan].state.opllPreset<<4)); } break; } @@ -577,7 +604,7 @@ void DivPlatformOPLL::forceIns() { rWrite(0x06,(mod.sl<<4)|(mod.rr)); rWrite(0x07,(car.sl<<4)|(car.rr)); } - rWrite(0x30+i,(15-(chan[i].outVol*(15-chan[i].state.op[1].tl))/15)|(chan[i].state.opllPreset<<4)); + rWrite(0x30+i,((15-(chan[i].outVol*(15-chan[i].state.op[1].tl))/15)&15)|(chan[i].state.opllPreset<<4)); if (chan[i].active) { chan[i].keyOn=true; chan[i].freqChanged=true; @@ -633,7 +660,7 @@ void DivPlatformOPLL::reset() { if (dumpWrites) { addWrite(0xffffffff,0); } - for (int i=0; i<9; i++) { + for (int i=0; i<11; i++) { chan[i]=DivPlatformOPLL::Channel(); chan[i].vol=15; chan[i].outVol=15; @@ -655,6 +682,10 @@ void DivPlatformOPLL::reset() { drumVol[4]=0; delay=0; + + if (properDrums) { + immWrite(0x0e,0x20); + } } bool DivPlatformOPLL::keyOffAffectsArp(int ch) { @@ -666,7 +697,7 @@ bool DivPlatformOPLL::keyOffAffectsPorta(int ch) { } void DivPlatformOPLL::notifyInsChange(int ins) { - for (int i=0; i<9; i++) { + for (int i=0; i<11; i++) { if (chan[i].ins==ins) { chan[i].insChanged=true; } @@ -709,7 +740,7 @@ int DivPlatformOPLL::init(DivEngine* p, int channels, int sugRate, unsigned int parent=p; dumpWrites=false; skipRegisterWrites=false; - for (int i=0; i<9; i++) { + for (int i=0; i<11; i++) { isMuted[i]=false; } setFlags(flags); From 7f3460bfcd55542c7c6cbad680cb2f4c32524de3 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Tue, 1 Mar 2022 14:20:29 -0500 Subject: [PATCH 29/50] i don't get it --- src/engine/platform/opll.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/engine/platform/opll.cpp b/src/engine/platform/opll.cpp index f8b542909..078cc393b 100644 --- a/src/engine/platform/opll.cpp +++ b/src/engine/platform/opll.cpp @@ -80,7 +80,7 @@ const unsigned char cycleMapOPLL[18]={ }; const unsigned char drumSlot[11]={ - 0, 0, 0, 0, 0, 0, 6, 7, 7, 8, 8 + 0, 0, 0, 0, 0, 0, 6, 7, 8, 8, 7 }; void DivPlatformOPLL::acquire_nuked(short* bufL, short* bufR, size_t start, size_t len) { From fa5e2bc439472ff948c708870e845e16edce2758 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Tue, 1 Mar 2022 17:19:52 -0500 Subject: [PATCH 30/50] TODO: revamp new song system picker --- src/engine/engine.cpp | 19 ++++++- src/engine/engine.h | 2 +- src/gui/gui.cpp | 126 +++++++++++++++++++++++++++++++++--------- src/gui/gui.h | 21 +++++++ 4 files changed, 139 insertions(+), 29 deletions(-) diff --git a/src/engine/engine.cpp b/src/engine/engine.cpp index ad0548399..0c3621f81 100644 --- a/src/engine/engine.cpp +++ b/src/engine/engine.cpp @@ -19,6 +19,7 @@ #include "dataErrors.h" #include "song.h" +#include #define _USE_MATH_DEFINES #include "engine.h" #include "instrument.h" @@ -533,13 +534,25 @@ void DivEngine::renderSamples() { qsoundMemLen=memPos+256; } -void DivEngine::createNew() { - DivSystem sys=song.system[0]; +void DivEngine::createNew(int* description) { quitDispatch(); isBusy.lock(); song.unload(); song=DivSong(); - song.system[0]=sys; + if (description!=NULL) { + if (description[0]!=0) { + int index=0; + for (int i=0; description[i]; i+=4) { + song.system[index]=(DivSystem)description[i]; + song.systemVol[index]=description[i+1]; + song.systemPan[index]=description[i+2]; + song.systemFlags[index]=description[i+3]; + index++; + if (index>=32) break; + } + song.systemLen=index; + } + } recalcChans(); renderSamples(); isBusy.unlock(); diff --git a/src/engine/engine.h b/src/engine/engine.h index 2f060f14f..02eed74ed 100644 --- a/src/engine/engine.h +++ b/src/engine/engine.h @@ -262,7 +262,7 @@ class DivEngine { DivWavetable* getWave(int index); DivSample* getSample(int index); // start fresh - void createNew(); + void createNew(int* description); // load a file. bool load(unsigned char* f, size_t length); // save as .dmf. diff --git a/src/gui/gui.cpp b/src/gui/gui.cpp index 7c3aec199..bec846c83 100644 --- a/src/gui/gui.cpp +++ b/src/gui/gui.cpp @@ -1898,6 +1898,73 @@ void FurnaceGUI::drawDebug() { ImGui::End(); } +#define NEWSONG_CATEGORY(x) \ + if (ImGui::Selectable(x,false,ImGuiSelectableFlags_DontClosePopups)) { \ + printf("selected a category\n"); \ + } + +void FurnaceGUI::drawNewSong() { + bool accepted=false; + + ImGui::PushFont(bigFont); + ImGui::SetCursorPosX((ImGui::GetContentRegionAvail().x-ImGui::CalcTextSize("Choose a System!").x)*0.5); + ImGui::Text("Choose a System!"); + ImGui::PopFont(); + + if (ImGui::BeginTable("sysPicker",2,ImGuiTableFlags_Borders)) { + ImGui::TableSetupColumn("c0",ImGuiTableColumnFlags_WidthFixed,0.0f); + ImGui::TableSetupColumn("c1",ImGuiTableColumnFlags_WidthStretch,0.0f); + + ImGui::TableNextRow(ImGuiTableRowFlags_Headers); + ImGui::TableNextColumn(); + ImGui::Text("Categories"); + ImGui::TableNextColumn(); + ImGui::Text("Systems"); + + ImGui::TableNextRow(); + + // CATEGORIES + ImGui::TableNextColumn(); + NEWSONG_CATEGORY("All chips"); + NEWSONG_CATEGORY("FM"); + NEWSONG_CATEGORY("Square"); + NEWSONG_CATEGORY("Sample"); + NEWSONG_CATEGORY("Wavetable"); + NEWSONG_CATEGORY("Other/Special"); + NEWSONG_CATEGORY("Game consoles"); + NEWSONG_CATEGORY("Computers"); + NEWSONG_CATEGORY("Arcade systems"); + NEWSONG_CATEGORY("DefleMask-compatible"); + + // SYSTEMS + ImGui::TableNextColumn(); + if (ImGui::BeginTable("Systems",1,ImGuiTableFlags_BordersInnerV|ImGuiTableFlags_ScrollY)) { + ImGui::TableNextRow(); + ImGui::TableNextColumn(); + ImGui::Selectable("System system",false,ImGuiSelectableFlags_DontClosePopups); + ImGui::EndTable(); + } + + ImGui::EndTable(); + } + + if (ImGui::Button("Go ahead") || accepted) { + e->createNew(nextDesc); + undoHist.clear(); + redoHist.clear(); + curFileName=""; + modified=false; + curNibble=false; + orderNibble=false; + orderCursor=-1; + selStart=SelectionPoint(); + selEnd=SelectionPoint(); + cursor=SelectionPoint(); + updateWindowTitle(); + ImGui::CloseCurrentPopup(); + } +} + void FurnaceGUI::drawStats() { if (nextWindow==GUI_WINDOW_STATS) { statsOpen=true; @@ -4391,22 +4458,11 @@ bool FurnaceGUI::loop() { ImGui::BeginMainMenuBar(); if (ImGui::BeginMenu("file")) { - if (ImGui::MenuItem("new")) { + if (ImGui::MenuItem("new...")) { if (modified) { showWarning("Unsaved changes! Are you sure?",GUI_WARN_NEW); } else { - e->createNew(); - undoHist.clear(); - redoHist.clear(); - curFileName=""; - modified=false; - curNibble=false; - orderNibble=false; - orderCursor=-1; - selStart=SelectionPoint(); - selEnd=SelectionPoint(); - cursor=SelectionPoint(); - updateWindowTitle(); + displayNew=true; } } if (ImGui::MenuItem("open...",BIND_FOR(GUI_ACTION_OPEN))) { @@ -5107,6 +5163,11 @@ bool FurnaceGUI::loop() { ImGui::OpenPopup("Rendering..."); } + if (displayNew) { + displayNew=false; + ImGui::OpenPopup("New Song"); + } + if (nextWindow==GUI_WINDOW_ABOUT) { aboutOpen=true; nextWindow=GUI_WINDOW_NOTHING; @@ -5126,6 +5187,13 @@ bool FurnaceGUI::loop() { ImGui::EndPopup(); } + ImGui::SetNextWindowSizeConstraints(ImVec2(400.0f*dpiScale,200.0f*dpiScale),ImVec2(scrW*dpiScale,scrH*dpiScale)); + if (ImGui::BeginPopupModal("New Song",NULL)) { + ImGui::SetWindowPos(ImVec2(((scrW*dpiScale)-ImGui::GetWindowSize().x)*0.5,((scrH*dpiScale)-ImGui::GetWindowSize().y)*0.5)); + drawNewSong(); + ImGui::EndPopup(); + } + if (ImGui::BeginPopupModal("Error",NULL,ImGuiWindowFlags_AlwaysAutoResize)) { ImGui::Text("%s",errorString.c_str()); if (ImGui::Button("OK")) { @@ -5143,18 +5211,7 @@ bool FurnaceGUI::loop() { quit=true; break; case GUI_WARN_NEW: - e->createNew(); - undoHist.clear(); - redoHist.clear(); - curFileName=""; - modified=false; - curNibble=false; - orderNibble=false; - orderCursor=-1; - selStart=SelectionPoint(); - selEnd=SelectionPoint(); - cursor=SelectionPoint(); - updateWindowTitle(); + displayNew=true; break; case GUI_WARN_OPEN: openFileDialog(GUI_FILE_OPEN); @@ -5743,6 +5800,7 @@ FurnaceGUI::FurnaceGUI(): displayError(false), displayExporting(false), vgmExportLoop(true), + displayNew(false), curFileDialog(GUI_FILE_OPEN), warnAction(GUI_WARN_OPEN), scrW(1280), @@ -5804,6 +5862,7 @@ FurnaceGUI::FurnaceGUI(): wantPatName(false), curWindow(GUI_WINDOW_NOTHING), nextWindow(GUI_WINDOW_NOTHING), + nextDesc(NULL), wavePreviewOn(false), wavePreviewKey((SDL_Scancode)0), wavePreviewNote(0), @@ -5923,6 +5982,23 @@ FurnaceGUI::FurnaceGUI(): valueKeys[SDLK_KP_7]=7; valueKeys[SDLK_KP_8]=8; valueKeys[SDLK_KP_9]=9; + + /* +const char* sysCategories[]={ + "All chips", + "FM", + "Square", + "Sample", + "Wavetable", + "Other/Special", + "Game consoles", + "Computers", + "Arcade systems", + "DefleMask-compatible" +}; +*/ + + sysCategories.push_back(FurnaceGUISysCategory("All chips")); memset(willExport,1,32*sizeof(bool)); diff --git a/src/gui/gui.h b/src/gui/gui.h index 291db6d8d..c59705961 100644 --- a/src/gui/gui.h +++ b/src/gui/gui.h @@ -402,6 +402,22 @@ struct Particle { lifeSpeed(lS) {} }; +struct FurnaceGUISysDef { + const char* name; + const int* definition; + FurnaceGUISysDef(const char* n, const int* def): + name(n), definition(def) {} +}; + +struct FurnaceGUISysCategory { + const char* name; + std::vector systems; + FurnaceGUISysCategory(const char* n): + name(n) {} + FurnaceGUISysCategory(): + name(NULL) {} +}; + class FurnaceGUI { DivEngine* e; @@ -413,6 +429,7 @@ class FurnaceGUI { String mmlStringW; bool quit, warnQuit, willCommit, edit, modified, displayError, displayExporting, vgmExportLoop; + bool displayNew; bool willExport[32]; FurnaceGUIFileDialogs curFileDialog; @@ -533,6 +550,7 @@ class FurnaceGUI { float peak[2]; float patChanX[DIV_MAX_CHANS+1]; float patChanSlideY[DIV_MAX_CHANS+1]; + int* nextDesc; // bit 31: ctrl // bit 30: reserved for SDL scancode mask @@ -563,6 +581,8 @@ class FurnaceGUI { std::vector cmdStream; std::vector particles; + std::vector sysCategories; + bool wavePreviewOn; SDL_Scancode wavePreviewKey; int wavePreviewNote; @@ -657,6 +677,7 @@ class FurnaceGUI { void drawAbout(); void drawSettings(); void drawDebug(); + void drawNewSong(); void parseKeybinds(); void promptKey(int which); From 72178f6ce9d2833924d64c22b6e64397e179fbe8 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Tue, 1 Mar 2022 17:25:01 -0500 Subject: [PATCH 31/50] TODO: still to-do --- src/gui/gui.cpp | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/gui/gui.cpp b/src/gui/gui.cpp index bec846c83..08e73e0d4 100644 --- a/src/gui/gui.cpp +++ b/src/gui/gui.cpp @@ -5998,7 +5998,16 @@ const char* sysCategories[]={ }; */ - sysCategories.push_back(FurnaceGUISysCategory("All chips")); + FurnaceGUISysCategory cat; + + cat=FurnaceGUISysCategory("All chips"); + cat.systems.push_back(FurnaceGUISysDef( + "Yamaha YM2612", NULL /*{ + DIV_SYSTEM_YM2612, 64, 0, 0, + 0 + }*/ + )); + sysCategories.push_back(cat); memset(willExport,1,32*sizeof(bool)); From 066769379b19e82afe3d9bf65dccf736afc2a3c1 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Tue, 1 Mar 2022 20:58:27 -0500 Subject: [PATCH 32/50] update format.md a bit --- papers/format.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/papers/format.md b/papers/format.md index 1ca356b72..c7702ae2b 100644 --- a/papers/format.md +++ b/papers/format.md @@ -29,6 +29,7 @@ furthermore, an `or reserved` indicates this field is always present, but is res the format versions are: +- 61: Furnace dev61 - 60: Furnace dev60 - 59: Furnace dev59 - 58: Furnace dev58 @@ -174,7 +175,7 @@ size | description | - signed char, 64=1.0, 127=~2.0 32 | sound chip panning | - signed char, -128=left, 127=right - 128 | sound chip parameters (TODO) + 128 | sound chip parameters STR | song name STR | song author 4f | A-4 tuning From 1e5d7a623a7601dc890651577b17b94afe1548ac Mon Sep 17 00:00:00 2001 From: tildearrow Date: Tue, 1 Mar 2022 20:59:38 -0500 Subject: [PATCH 33/50] GUI: report why does window creation fail --- src/gui/gui.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/gui/gui.cpp b/src/gui/gui.cpp index 08e73e0d4..50f45c1eb 100644 --- a/src/gui/gui.cpp +++ b/src/gui/gui.cpp @@ -5650,7 +5650,7 @@ bool FurnaceGUI::init() { sdlWin=SDL_CreateWindow("Furnace",SDL_WINDOWPOS_CENTERED,SDL_WINDOWPOS_CENTERED,scrW*dpiScale,scrH*dpiScale,SDL_WINDOW_RESIZABLE|SDL_WINDOW_ALLOW_HIGHDPI); if (sdlWin==NULL) { - logE("could not open window!\n"); + logE("could not open window! %s\n",SDL_GetError()); return false; } From ceb1cd0181050e52bf58203d2f728a633ab6742d Mon Sep 17 00:00:00 2001 From: tildearrow Date: Tue, 1 Mar 2022 22:10:03 -0500 Subject: [PATCH 34/50] OPLL: more drum fixes! --- src/engine/platform/opll.cpp | 39 ++++++++++++++++++++++++++++++++++-- src/engine/platform/opll.h | 2 +- src/engine/playback.cpp | 11 ++++++++-- 3 files changed, 47 insertions(+), 5 deletions(-) diff --git a/src/engine/platform/opll.cpp b/src/engine/platform/opll.cpp index 078cc393b..489bea5dd 100644 --- a/src/engine/platform/opll.cpp +++ b/src/engine/platform/opll.cpp @@ -275,7 +275,7 @@ void DivPlatformOPLL::tick() { drumState|=(0x10>>(chan[i].note%12)); immWrite(0x0e,0x20|drumState); chan[i].keyOn=false; - } else if (chan[i].keyOn || chan[i].freqChanged) { + } else if ((chan[i].keyOn || chan[i].freqChanged) && i<9) { //immWrite(0x28,0xf0|konOffs[i]); immWrite(0x20+i,(chan[i].freqH)|(chan[i].active<<4)|(chan[i].state.alg?0x20:0)); chan[i].keyOn=false; @@ -347,9 +347,9 @@ void DivPlatformOPLL::muteChannel(int ch, bool mute) { } int DivPlatformOPLL::dispatch(DivCommand c) { - if (c.chan>=9 && !properDrums) return 0; switch (c.cmd) { case DIV_CMD_NOTE_ON: { + if (c.chan>=9 && !properDrums) return 0; DivInstrument* ins=parent->getIns(chan[c.chan].ins); if (chan[c.chan].insChanged) { chan[c.chan].state=ins->fm; @@ -445,24 +445,35 @@ int DivPlatformOPLL::dispatch(DivCommand c) { break; } case DIV_CMD_NOTE_OFF: + if (c.chan>=9 && !properDrums) return 0; chan[c.chan].keyOff=true; chan[c.chan].keyOn=false; chan[c.chan].active=false; break; case DIV_CMD_NOTE_OFF_ENV: + if (c.chan>=9 && !properDrums) return 0; 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: + if (c.chan>=9 && !properDrums) return 0; chan[c.chan].std.release(); break; case DIV_CMD_VOLUME: { + if (c.chan>=9 && !properDrums) return 0; chan[c.chan].vol=c.value; if (!chan[c.chan].std.hasVol) { chan[c.chan].outVol=c.value; } + if (c.chan>=6 || properDrums) { + drumVol[c.chan-6]=15-chan[c.chan].outVol; + rWrite(0x36,drumVol[0]); + rWrite(0x37,drumVol[1]|(drumVol[4]<<4)); + rWrite(0x38,drumVol[3]|(drumVol[2]<<4)); + break; + } if (c.chan<6 || !drums) { rWrite(0x30+c.chan,((15-(chan[c.chan].outVol*(15-chan[c.chan].state.op[1].tl))/15)&15)|(chan[c.chan].state.opllPreset<<4)); } @@ -479,11 +490,13 @@ int DivPlatformOPLL::dispatch(DivCommand c) { chan[c.chan].ins=c.value; break; case DIV_CMD_PITCH: { + if (c.chan>=9 && !properDrums) return 0; chan[c.chan].pitch=c.value; chan[c.chan].freqChanged=true; break; } case DIV_CMD_NOTE_PORTA: { + if (c.chan>=9 && !properDrums) return 0; int destFreq=NOTE_FREQUENCY(c.value2); int newFreq; bool return2=false; @@ -516,12 +529,14 @@ int DivPlatformOPLL::dispatch(DivCommand c) { break; } case DIV_CMD_LEGATO: { + if (c.chan>=9 && !properDrums) return 0; chan[c.chan].baseFreq=NOTE_FREQUENCY(c.value); chan[c.chan].note=c.value; chan[c.chan].freqChanged=true; break; } case DIV_CMD_FM_FB: { + if (c.chan>=9 && !properDrums) return 0; DivInstrumentFM::Operator& mod=chan[c.chan].state.op[0]; //DivInstrumentFM::Operator& car=chan[c.chan].state.op[1]; chan[c.chan].state.fb=c.value&7; @@ -530,6 +545,7 @@ int DivPlatformOPLL::dispatch(DivCommand c) { } case DIV_CMD_FM_MULT: { + if (c.chan>=9 && !properDrums) return 0; if (c.value==0) { DivInstrumentFM::Operator& mod=chan[c.chan].state.op[0]; mod.mult=c.value2&15; @@ -542,6 +558,7 @@ int DivPlatformOPLL::dispatch(DivCommand c) { break; } case DIV_CMD_FM_TL: { + if (c.chan>=9 && !properDrums) return 0; if (c.value==0) { DivInstrumentFM::Operator& mod=chan[c.chan].state.op[0]; DivInstrumentFM::Operator& car=chan[c.chan].state.op[1]; @@ -555,6 +572,7 @@ int DivPlatformOPLL::dispatch(DivCommand c) { break; } case DIV_CMD_FM_AR: { + if (c.chan>=9 && !properDrums) return 0; DivInstrumentFM::Operator& mod=chan[c.chan].state.op[0]; DivInstrumentFM::Operator& car=chan[c.chan].state.op[1]; if (c.value<0) { @@ -571,6 +589,18 @@ int DivPlatformOPLL::dispatch(DivCommand c) { rWrite(0x05,(car.ar<<4)|(car.dr)); break; } + case DIV_CMD_FM_EXTCH: + if (!properDrumsSys) break; + if (properDrums==c.value) break; + if (c.value) { + properDrums=true; + immWrite(0x0e,0x20); + } else { + properDrums=false; + immWrite(0x0e,0x00); + drumState=0; + } + break; case DIV_ALWAYS_SET_VOLUME: return 0; break; @@ -578,6 +608,7 @@ int DivPlatformOPLL::dispatch(DivCommand c) { return 15; break; case DIV_CMD_PRE_PORTA: + if (c.chan>=9 && !properDrums) return 0; chan[c.chan].inPorta=c.value; break; case DIV_CMD_PRE_NOTE: @@ -622,6 +653,7 @@ void DivPlatformOPLL::forceIns() { immWrite(0x18,0xC0); immWrite(0x28,0x01); } + drumState=0; } void DivPlatformOPLL::toggleRegisterDump(bool enable) { @@ -634,6 +666,7 @@ void DivPlatformOPLL::setVRC7(bool vrc) { void DivPlatformOPLL::setProperDrums(bool pd) { properDrums=pd; + properDrumsSys=pd; } @@ -682,6 +715,8 @@ void DivPlatformOPLL::reset() { drumVol[4]=0; delay=0; + drums=false; + properDrums=properDrumsSys; if (properDrums) { immWrite(0x0e,0x20); diff --git a/src/engine/platform/opll.h b/src/engine/platform/opll.h index 0bada9036..382c23636 100644 --- a/src/engine/platform/opll.h +++ b/src/engine/platform/opll.h @@ -76,7 +76,7 @@ class DivPlatformOPLL: public DivDispatch { bool useYMFM; bool drums; - bool properDrums; + bool properDrums, properDrumsSys; bool vrc7; short oldWrites[256]; diff --git a/src/engine/playback.cpp b/src/engine/playback.cpp index 656bc35c2..f99c5969d 100644 --- a/src/engine/playback.cpp +++ b/src/engine/playback.cpp @@ -232,6 +232,15 @@ bool DivEngine::perSystemEffect(int ch, unsigned char effect, unsigned char effe } return false; break; + case DIV_SYSTEM_OPLL_DRUMS: + switch (effect) { + case 0x18: // drum mode toggle + dispatchCmd(DivCommand(DIV_CMD_FM_EXTCH,ch,effectVal)); + break; + default: + return false; + } + break; case DIV_SYSTEM_QSOUND: switch (effect) { case 0x10: // echo feedback @@ -389,8 +398,6 @@ bool DivEngine::perSystemPostEffect(int ch, unsigned char effect, unsigned char dispatchCmd(DivCommand(DIV_CMD_FM_MULT,ch,(effectVal>>4)-1,effectVal&15)); } break; - case 0x18: // drum mode toggle - break; case 0x19: // AR global dispatchCmd(DivCommand(DIV_CMD_FM_AR,ch,-1,effectVal&31)); break; From 614c1a4da87c56a08dad1b08ffdb6b089918c052 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Tue, 1 Mar 2022 22:42:40 -0500 Subject: [PATCH 35/50] OPLL: 99% proper drums mode muting! --- src/engine/platform/opll.cpp | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/src/engine/platform/opll.cpp b/src/engine/platform/opll.cpp index 489bea5dd..b9949aa0d 100644 --- a/src/engine/platform/opll.cpp +++ b/src/engine/platform/opll.cpp @@ -109,7 +109,7 @@ void DivPlatformOPLL::acquire_nuked(short* bufL, short* bufR, size_t start, size OPLL_Clock(&fm,o); unsigned char nextOut=cycleMapOPLL[fm.cycles]; - if (!isMuted[nextOut]) { + if ((nextOut>=6 && properDrums) || !isMuted[nextOut]) { os+=(o[0]+o[1]); } } @@ -267,9 +267,11 @@ void DivPlatformOPLL::tick() { } } if (chan[i].keyOn && i>=6 && properDrums) { - drumState|=(0x10>>(i-6)); - immWrite(0x0e,0x20|drumState); - chan[i].keyOn=false; + if (!isMuted[i]) { + drumState|=(0x10>>(i-6)); + immWrite(0x0e,0x20|drumState); + } + chan[i].keyOn=false; } else if (chan[i].keyOn && i>=6 && drums) { //printf("%d\n",chan[i].note%12); drumState|=(0x10>>(chan[i].note%12)); @@ -636,9 +638,11 @@ void DivPlatformOPLL::forceIns() { rWrite(0x07,(car.sl<<4)|(car.rr)); } rWrite(0x30+i,((15-(chan[i].outVol*(15-chan[i].state.op[1].tl))/15)&15)|(chan[i].state.opllPreset<<4)); - if (chan[i].active) { - chan[i].keyOn=true; - chan[i].freqChanged=true; + if (!(i>=6 && properDrums)) { + if (chan[i].active) { + chan[i].keyOn=true; + chan[i].freqChanged=true; + } } } if (drums) { From b90fb02e635a4208f1207acb4be99892786fa59d Mon Sep 17 00:00:00 2001 From: tildearrow Date: Tue, 1 Mar 2022 23:07:29 -0500 Subject: [PATCH 36/50] OPLL: 99.9% all features usable, except for custom patchsets (not sure how am i gonna implement it) --- extern/Nuked-OPLL/opll.c | 22 +++++++++++++++++++ extern/Nuked-OPLL/opll.h | 2 ++ src/engine/platform/opll.cpp | 34 ++++++++++++++++++++++-------- src/engine/platform/opll.h | 2 ++ src/engine/song.h | 12 +++++++++++ src/gui/gui.cpp | 41 ++++++++++++++++++++++++++++++++++++ 6 files changed, 104 insertions(+), 9 deletions(-) diff --git a/extern/Nuked-OPLL/opll.c b/extern/Nuked-OPLL/opll.c index fd2aa239f..ecff69fd3 100644 --- a/extern/Nuked-OPLL/opll.c +++ b/extern/Nuked-OPLL/opll.c @@ -290,6 +290,28 @@ static void OPLL_DoModeWrite(opll_t *chip) { } } +const opll_patch_t* OPLL_GetPatchROM(uint32_t chip_type) { + switch (chip_type) { + case opll_type_ds1001: + return patch_ds1001; + break; + case opll_type_ymf281: + case opll_type_ymf281b: + return patch_ymf281; + break; + case opll_type_ym2423: + return patch_ym2423; + break; + case opll_type_ym2413: + case opll_type_ym2413b: + case opll_type_ym2420: + default: + return patch_ym2413; + break; + } + return patch_ym2413; +} + void OPLL_Reset(opll_t *chip, uint32_t chip_type) { uint32_t i; memset(chip, 0, sizeof(opll_t)); diff --git a/extern/Nuked-OPLL/opll.h b/extern/Nuked-OPLL/opll.h index 706eb9f3b..85c721a78 100644 --- a/extern/Nuked-OPLL/opll.h +++ b/extern/Nuked-OPLL/opll.h @@ -193,6 +193,8 @@ typedef struct { } opll_t; +const opll_patch_t* OPLL_GetPatchROM(uint32_t chip_type); + void OPLL_Reset(opll_t *chip, uint32_t chip_type); void OPLL_Clock(opll_t *chip, int32_t *buffer); void OPLL_Write(opll_t *chip, uint32_t port, uint8_t data); diff --git a/src/engine/platform/opll.cpp b/src/engine/platform/opll.cpp index b9949aa0d..08d46cf9d 100644 --- a/src/engine/platform/opll.cpp +++ b/src/engine/platform/opll.cpp @@ -25,7 +25,7 @@ #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 CHIP_FREQBASE 295017 +#define CHIP_FREQBASE 1180068 const char* DivPlatformOPLL::getEffectName(unsigned char effect) { switch (effect) { @@ -693,6 +693,20 @@ void DivPlatformOPLL::reset() { OPLL_Reset(&fm,opll_type_ds1001); } else { OPLL_Reset(&fm,opll_type_ym2413); + switch (patchSet) { + case 0: + fm.patchrom=OPLL_GetPatchROM(opll_type_ym2413); + break; + case 1: + fm.patchrom=OPLL_GetPatchROM(opll_type_ymf281); + break; + case 2: + fm.patchrom=OPLL_GetPatchROM(opll_type_ym2423); + break; + case 3: + fm.patchrom=OPLL_GetPatchROM(opll_type_ds1001); + break; + } } if (dumpWrites) { addWrite(0xffffffff,0); @@ -763,22 +777,24 @@ void DivPlatformOPLL::setYMFM(bool use) { } void DivPlatformOPLL::setFlags(unsigned int flags) { - if (flags==3) { - chipClock=COLOR_NTSC; - } else if (flags==2) { - chipClock=8000000.0; - } else if (flags==1) { - chipClock=COLOR_PAL*1.0/5.0; + if ((flags&15)==3) { + chipClock=COLOR_NTSC/2.0; + } else if ((flags&15)==2) { + chipClock=4000000.0; + } else if ((flags&15)==1) { + chipClock=COLOR_PAL*4.0/5.0; } else { - chipClock=COLOR_NTSC/4.0; + chipClock=COLOR_NTSC; } - rate=chipClock/9; + rate=chipClock/36; + patchSet=flags>>4; } int DivPlatformOPLL::init(DivEngine* p, int channels, int sugRate, unsigned int flags) { parent=p; dumpWrites=false; skipRegisterWrites=false; + patchSet=0; for (int i=0; i<11; i++) { isMuted[i]=false; } diff --git a/src/engine/platform/opll.h b/src/engine/platform/opll.h index 382c23636..4a7a4c97f 100644 --- a/src/engine/platform/opll.h +++ b/src/engine/platform/opll.h @@ -78,6 +78,8 @@ class DivPlatformOPLL: public DivDispatch { bool drums; bool properDrums, properDrumsSys; bool vrc7; + + unsigned char patchSet; short oldWrites[256]; short pendingWrites[256]; diff --git a/src/engine/song.h b/src/engine/song.h index 85752288e..a992aa49f 100644 --- a/src/engine/song.h +++ b/src/engine/song.h @@ -220,6 +220,18 @@ struct DivSong { // - bit 0-11: echo delay length // - Valid values are 0-2725 // - 0 is max length, 2725 is min length + // - OPLL: + // - bit 0-3: clock rate + // - 0: NTSC (3.58MHz) + // - 1: PAL (3.55MHz) + // - 2: Other (4MHz) + // - 3: half NTSC (1.79MHz) + // - bit 4-7: patch set + // - 0: YM2413 + // - 1: YMF281 + // - 2: YM2423 + // - 3: VRC7 + // - 4: custom (TODO) unsigned int systemFlags[32]; // song information diff --git a/src/gui/gui.cpp b/src/gui/gui.cpp index 50f45c1eb..50e883012 100644 --- a/src/gui/gui.cpp +++ b/src/gui/gui.cpp @@ -4635,6 +4635,47 @@ bool FurnaceGUI::loop() { } break; } + case DIV_SYSTEM_OPLL: + case DIV_SYSTEM_OPLL_DRUMS: + case DIV_SYSTEM_VRC7: { + ImGui::Text("Clock rate:"); + if (ImGui::RadioButton("NTSC (3.58MHz)",(flags&15)==0)) { + e->setSysFlags(i,(flags&(~15))|0,restart); + updateWindowTitle(); + } + if (ImGui::RadioButton("PAL (3.55MHz)",(flags&15)==1)) { + e->setSysFlags(i,(flags&(~15))|1,restart); + updateWindowTitle(); + } + if (ImGui::RadioButton("BBC Micro (4MHz)",(flags&15)==2)) { + e->setSysFlags(i,(flags&(~15))|2,restart); + updateWindowTitle(); + } + if (ImGui::RadioButton("Half NTSC (1.79MHz)",(flags&15)==3)) { + e->setSysFlags(i,(flags&(~15))|3,restart); + updateWindowTitle(); + } + if (e->song.system[i]!=DIV_SYSTEM_VRC7) { + ImGui::Text("Patch set:"); + if (ImGui::RadioButton("Yamaha YM2413",((flags>>4)&15)==0)) { + e->setSysFlags(i,(flags&(~0xf0))|0,restart); + updateWindowTitle(); + } + if (ImGui::RadioButton("Yamaha YMF281",((flags>>4)&15)==1)) { + e->setSysFlags(i,(flags&(~0xf0))|0x10,restart); + updateWindowTitle(); + } + if (ImGui::RadioButton("Yamaha YM2423",((flags>>4)&15)==2)) { + e->setSysFlags(i,(flags&(~0xf0))|0x20,restart); + updateWindowTitle(); + } + if (ImGui::RadioButton("Konami VRC7",((flags>>4)&15)==3)) { + e->setSysFlags(i,(flags&(~0xf0))|0x30,restart); + updateWindowTitle(); + } + } + break; + } case DIV_SYSTEM_YM2151: if (ImGui::RadioButton("NTSC (3.58MHz)",flags==0)) { e->setSysFlags(i,0,restart); From cf1711c45b12b0351a117d08259d71f12eee3508 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Tue, 1 Mar 2022 23:27:53 -0500 Subject: [PATCH 37/50] GUI: attribution --- src/gui/gui.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/gui/gui.cpp b/src/gui/gui.cpp index af193fc9a..787643514 100644 --- a/src/gui/gui.cpp +++ b/src/gui/gui.cpp @@ -1513,6 +1513,7 @@ const char* aboutLine[]={ "", "-- program --", "tildearrow", + "cam900", "laoo", "superctr", "", From 56aa4b28fb00cd51ac80797f1cfb1d881bb9fb95 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Tue, 1 Mar 2022 23:28:05 -0500 Subject: [PATCH 38/50] tiny drums mode fix --- src/engine/platform/opll.cpp | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/engine/platform/opll.cpp b/src/engine/platform/opll.cpp index 08d46cf9d..651ff4b7d 100644 --- a/src/engine/platform/opll.cpp +++ b/src/engine/platform/opll.cpp @@ -405,8 +405,10 @@ int DivPlatformOPLL::dispatch(DivCommand c) { } } else { if (c.chan>=6) { - drums=false; - immWrite(0x0e,0); + if (drums) { + drums=false; + immWrite(0x0e,0); + } } rWrite(0x30+c.chan,((15-(chan[c.chan].outVol*(15-chan[c.chan].state.op[1].tl))/15)&15)|(chan[c.chan].state.opllPreset<<4)); } @@ -642,6 +644,7 @@ void DivPlatformOPLL::forceIns() { if (chan[i].active) { chan[i].keyOn=true; chan[i].freqChanged=true; + chan[i].insChanged=true; } } } From 14611fc744389196be39e82ab38a8674b86de671 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Tue, 1 Mar 2022 23:36:23 -0500 Subject: [PATCH 39/50] add 2 demo songs requested by CaptainMalware and Mahbod Karamoozian --- demos/Another_winter.fur | Bin 0 -> 26074 bytes demos/yky.fur | Bin 0 -> 17704 bytes src/gui/gui.cpp | 2 ++ 3 files changed, 2 insertions(+) create mode 100644 demos/Another_winter.fur create mode 100644 demos/yky.fur diff --git a/demos/Another_winter.fur b/demos/Another_winter.fur new file mode 100644 index 0000000000000000000000000000000000000000..b0ccceac151b639325d90edc1841e7e853c3c07c GIT binary patch literal 26074 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

kn?N;F4O|~GjD04wqPj@kwKLiPum^tODlrFS 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 a(+a&2!JK}|SSY-TF{61P{aGkK`TqdSGV*Ky literal 0 HcmV?d00001 diff --git a/demos/yky.fur b/demos/yky.fur new file mode 100644 index 0000000000000000000000000000000000000000..106ed1004b630610b60df810d9676b16f61288d1 GIT binary patch literal 17704 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 literal 0 HcmV?d00001 diff --git a/src/gui/gui.cpp b/src/gui/gui.cpp index 787643514..98400b8f4 100644 --- a/src/gui/gui.cpp +++ b/src/gui/gui.cpp @@ -1531,7 +1531,9 @@ const char* aboutLine[]={ "-- demo songs --", "0x5066", "breakthetargets", + "CaptainMalware", "kleeder", + "Mahbod Karamoozian", "nicco1690", "NikonTeen", "SuperJet Spade", From f10816ae80402967c1822f03f8f96337c31eb26d Mon Sep 17 00:00:00 2001 From: tildearrow Date: Tue, 1 Mar 2022 23:46:04 -0500 Subject: [PATCH 40/50] GUI: add warning when layout reset is called issue #240 --- src/gui/gui.cpp | 7 +++++-- src/gui/gui.h | 1 + 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/src/gui/gui.cpp b/src/gui/gui.cpp index 98400b8f4..46692dbd5 100644 --- a/src/gui/gui.cpp +++ b/src/gui/gui.cpp @@ -4932,8 +4932,7 @@ bool FurnaceGUI::loop() { } if (ImGui::BeginMenu("settings")) { if (ImGui::MenuItem("reset layout")) { - ImGui::LoadIniSettingsFromMemory(defaultLayout); - ImGui::SaveIniSettingsToDisk(finalLayoutPath); + showWarning("Are you sure you want to reset the workspace layout?",GUI_WARN_RESET_LAYOUT); } if (ImGui::MenuItem("settings...",BIND_FOR(GUI_ACTION_WINDOW_SETTINGS))) { syncSettings(); @@ -5279,6 +5278,10 @@ bool FurnaceGUI::loop() { } nextFile=""; break; + case GUI_WARN_RESET_LAYOUT: + ImGui::LoadIniSettingsFromMemory(defaultLayout); + ImGui::SaveIniSettingsToDisk(finalLayoutPath); + break; case GUI_WARN_GENERIC: break; } diff --git a/src/gui/gui.h b/src/gui/gui.h index c59705961..0aebddfcc 100644 --- a/src/gui/gui.h +++ b/src/gui/gui.h @@ -157,6 +157,7 @@ enum FurnaceGUIWarnings { GUI_WARN_NEW, GUI_WARN_OPEN, GUI_WARN_OPEN_DROP, + GUI_WARN_RESET_LAYOUT, GUI_WARN_GENERIC }; From 36e1ead643336f176cd979fb472f60fea6e06d85 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Wed, 2 Mar 2022 00:02:52 -0500 Subject: [PATCH 41/50] GUI: lastDir per dialog type issue #227 --- src/gui/gui.cpp | 101 +++++++++++++++++++++++++++++++++++++----------- src/gui/gui.h | 1 + src/ta-utils.h | 2 + 3 files changed, 81 insertions(+), 23 deletions(-) diff --git a/src/gui/gui.cpp b/src/gui/gui.cpp index 46692dbd5..bcbebef2a 100644 --- a/src/gui/gui.cpp +++ b/src/gui/gui.cpp @@ -3992,56 +3992,70 @@ 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); + if (!dirExists(workingDirSong)) workingDirSong=getHomeDir(); + ImGuiFileDialog::Instance()->OpenModal("FileDialog","Open File","compatible files{.fur,.dmf},.*",workingDirSong); break; case GUI_FILE_SAVE: - ImGuiFileDialog::Instance()->OpenModal("FileDialog","Save File","Furnace song{.fur},DefleMask 1.1 module{.dmf}",workingDir,1,nullptr,ImGuiFileDialogFlags_ConfirmOverwrite); + if (!dirExists(workingDirSong)) workingDirSong=getHomeDir(); + ImGuiFileDialog::Instance()->OpenModal("FileDialog","Save File","Furnace song{.fur},DefleMask 1.1 module{.dmf}",workingDirSong,1,nullptr,ImGuiFileDialogFlags_ConfirmOverwrite); break; case GUI_FILE_SAVE_DMF_LEGACY: - ImGuiFileDialog::Instance()->OpenModal("FileDialog","Save File","DefleMask 1.0/legacy module{.dmf}",workingDir,1,nullptr,ImGuiFileDialogFlags_ConfirmOverwrite); + if (!dirExists(workingDirSong)) workingDirSong=getHomeDir(); + ImGuiFileDialog::Instance()->OpenModal("FileDialog","Save File","DefleMask 1.0/legacy module{.dmf}",workingDirSong,1,nullptr,ImGuiFileDialogFlags_ConfirmOverwrite); break; case GUI_FILE_INS_OPEN: - ImGuiFileDialog::Instance()->OpenModal("FileDialog","Load Instrument","compatible files{.fui,.dmp,.tfi,.vgi},.*",workingDir); + if (!dirExists(workingDirIns)) workingDirIns=getHomeDir(); + ImGuiFileDialog::Instance()->OpenModal("FileDialog","Load Instrument","compatible files{.fui,.dmp,.tfi,.vgi},.*",workingDirIns); break; case GUI_FILE_INS_SAVE: - ImGuiFileDialog::Instance()->OpenModal("FileDialog","Save Instrument","Furnace instrument{.fui}",workingDir,1,nullptr,ImGuiFileDialogFlags_ConfirmOverwrite); + if (!dirExists(workingDirIns)) workingDirIns=getHomeDir(); + ImGuiFileDialog::Instance()->OpenModal("FileDialog","Save Instrument","Furnace instrument{.fui}",workingDirIns,1,nullptr,ImGuiFileDialogFlags_ConfirmOverwrite); break; case GUI_FILE_WAVE_OPEN: - ImGuiFileDialog::Instance()->OpenModal("FileDialog","Load Wavetable","compatible files{.fuw,.dmw},.*",workingDir); + if (!dirExists(workingDirWave)) workingDirWave=getHomeDir(); + ImGuiFileDialog::Instance()->OpenModal("FileDialog","Load Wavetable","compatible files{.fuw,.dmw},.*",workingDirWave); break; case GUI_FILE_WAVE_SAVE: - ImGuiFileDialog::Instance()->OpenModal("FileDialog","Save Wavetable","Furnace wavetable{.fuw}",workingDir,1,nullptr,ImGuiFileDialogFlags_ConfirmOverwrite); + if (!dirExists(workingDirWave)) workingDirWave=getHomeDir(); + ImGuiFileDialog::Instance()->OpenModal("FileDialog","Save Wavetable","Furnace wavetable{.fuw}",workingDirWave,1,nullptr,ImGuiFileDialogFlags_ConfirmOverwrite); break; case GUI_FILE_SAMPLE_OPEN: - ImGuiFileDialog::Instance()->OpenModal("FileDialog","Load Sample","Wave file{.wav},.*",workingDir); + if (!dirExists(workingDirSample)) workingDirSample=getHomeDir(); + ImGuiFileDialog::Instance()->OpenModal("FileDialog","Load Sample","Wave file{.wav},.*",workingDirSample); break; case GUI_FILE_SAMPLE_SAVE: - ImGuiFileDialog::Instance()->OpenModal("FileDialog","Save Sample","Wave file{.wav}",workingDir,1,nullptr,ImGuiFileDialogFlags_ConfirmOverwrite); + if (!dirExists(workingDirSample)) workingDirSample=getHomeDir(); + ImGuiFileDialog::Instance()->OpenModal("FileDialog","Save Sample","Wave file{.wav}",workingDirSample,1,nullptr,ImGuiFileDialogFlags_ConfirmOverwrite); break; case GUI_FILE_EXPORT_AUDIO_ONE: - ImGuiFileDialog::Instance()->OpenModal("FileDialog","Export Audio","Wave file{.wav}",workingDir,1,nullptr,ImGuiFileDialogFlags_ConfirmOverwrite); + if (!dirExists(workingDirAudioExport)) workingDirAudioExport=getHomeDir(); + ImGuiFileDialog::Instance()->OpenModal("FileDialog","Export Audio","Wave file{.wav}",workingDirAudioExport,1,nullptr,ImGuiFileDialogFlags_ConfirmOverwrite); break; case GUI_FILE_EXPORT_AUDIO_PER_SYS: - ImGuiFileDialog::Instance()->OpenModal("FileDialog","Export Audio","Wave file{.wav}",workingDir,1,nullptr,ImGuiFileDialogFlags_ConfirmOverwrite); + if (!dirExists(workingDirAudioExport)) workingDirAudioExport=getHomeDir(); + ImGuiFileDialog::Instance()->OpenModal("FileDialog","Export Audio","Wave file{.wav}",workingDirAudioExport,1,nullptr,ImGuiFileDialogFlags_ConfirmOverwrite); break; case GUI_FILE_EXPORT_AUDIO_PER_CHANNEL: - ImGuiFileDialog::Instance()->OpenModal("FileDialog","Export Audio","Wave file{.wav}",workingDir,1,nullptr,ImGuiFileDialogFlags_ConfirmOverwrite); + if (!dirExists(workingDirAudioExport)) workingDirAudioExport=getHomeDir(); + ImGuiFileDialog::Instance()->OpenModal("FileDialog","Export Audio","Wave file{.wav}",workingDirAudioExport,1,nullptr,ImGuiFileDialogFlags_ConfirmOverwrite); break; case GUI_FILE_EXPORT_VGM: - ImGuiFileDialog::Instance()->OpenModal("FileDialog","Export VGM",".vgm",workingDir,1,nullptr,ImGuiFileDialogFlags_ConfirmOverwrite); + if (!dirExists(workingDirVGMExport)) workingDirVGMExport=getHomeDir(); + ImGuiFileDialog::Instance()->OpenModal("FileDialog","Export VGM",".vgm",workingDirVGMExport,1,nullptr,ImGuiFileDialogFlags_ConfirmOverwrite); break; case GUI_FILE_EXPORT_ROM: showError("Coming soon!"); break; case GUI_FILE_LOAD_MAIN_FONT: - ImGuiFileDialog::Instance()->OpenModal("FileDialog","Select Font","compatible files{.ttf,.otf,.ttc}",workingDir); + if (!dirExists(workingDirFont)) workingDirFont=getHomeDir(); + ImGuiFileDialog::Instance()->OpenModal("FileDialog","Select Font","compatible files{.ttf,.otf,.ttc}",workingDirFont); break; case GUI_FILE_LOAD_PAT_FONT: - ImGuiFileDialog::Instance()->OpenModal("FileDialog","Select Font","compatible files{.ttf,.otf,.ttc}",workingDir); + if (!dirExists(workingDirFont)) workingDirFont=getHomeDir(); + ImGuiFileDialog::Instance()->OpenModal("FileDialog","Select Font","compatible files{.ttf,.otf,.ttc}",workingDirFont); break; } curFileDialog=type; @@ -5068,6 +5082,38 @@ bool FurnaceGUI::loop() { if (ImGuiFileDialog::Instance()->Display("FileDialog",ImGuiWindowFlags_NoCollapse|ImGuiWindowFlags_NoMove,ImVec2(600.0f*dpiScale,400.0f*dpiScale),ImVec2(scrW*dpiScale,scrH*dpiScale))) { //ImGui::GetIO().ConfigFlags&=~ImGuiConfigFlags_NavEnableKeyboard; + switch (curFileDialog) { + case GUI_FILE_OPEN: + case GUI_FILE_SAVE: + case GUI_FILE_SAVE_DMF_LEGACY: + workingDirSong=ImGuiFileDialog::Instance()->GetCurrentPath()+DIR_SEPARATOR_STR; + break; + case GUI_FILE_INS_OPEN: + case GUI_FILE_INS_SAVE: + workingDirIns=ImGuiFileDialog::Instance()->GetCurrentPath()+DIR_SEPARATOR_STR; + break; + case GUI_FILE_WAVE_OPEN: + case GUI_FILE_WAVE_SAVE: + workingDirWave=ImGuiFileDialog::Instance()->GetCurrentPath()+DIR_SEPARATOR_STR; + break; + case GUI_FILE_SAMPLE_OPEN: + case GUI_FILE_SAMPLE_SAVE: + workingDirSample=ImGuiFileDialog::Instance()->GetCurrentPath()+DIR_SEPARATOR_STR; + break; + case GUI_FILE_EXPORT_AUDIO_ONE: + case GUI_FILE_EXPORT_AUDIO_PER_SYS: + case GUI_FILE_EXPORT_AUDIO_PER_CHANNEL: + workingDirAudioExport=ImGuiFileDialog::Instance()->GetCurrentPath()+DIR_SEPARATOR_STR; + break; + case GUI_FILE_EXPORT_VGM: + case GUI_FILE_EXPORT_ROM: + workingDirVGMExport=ImGuiFileDialog::Instance()->GetCurrentPath()+DIR_SEPARATOR_STR; + break; + case GUI_FILE_LOAD_MAIN_FONT: + case GUI_FILE_LOAD_PAT_FONT: + workingDirFont=ImGuiFileDialog::Instance()->GetCurrentPath()+DIR_SEPARATOR_STR; + break; + } if (ImGuiFileDialog::Instance()->IsOk()) { fileName=ImGuiFileDialog::Instance()->GetFilePathName(); if (fileName!="") { @@ -5195,12 +5241,6 @@ bool FurnaceGUI::loop() { curFileDialog=GUI_FILE_OPEN; } } - workingDir=ImGuiFileDialog::Instance()->GetCurrentPath(); -#ifdef _WIN32 - workingDir+='\\'; -#else - workingDir+='/'; -#endif ImGuiFileDialog::Instance()->Close(); } @@ -5663,7 +5703,15 @@ bool FurnaceGUI::init() { float dpiScaleF; #endif - workingDir=e->getConfString("lastDir",getHomeDir()); + String homeDir=getHomeDir(); + workingDir=e->getConfString("lastDir",homeDir); + workingDirSong=e->getConfString("lastDirSong",workingDir); + workingDirIns=e->getConfString("lastDirIns",workingDir); + workingDirWave=e->getConfString("lastDirWave",workingDir); + workingDirSample=e->getConfString("lastDirSample",workingDir); + workingDirAudioExport=e->getConfString("lastDirAudioExport",workingDir); + workingDirVGMExport=e->getConfString("lastDirVGMExport",workingDir); + workingDirFont=e->getConfString("lastDirFont",workingDir); editControlsOpen=e->getConfBool("editControlsOpen",true); ordersOpen=e->getConfBool("ordersOpen",true); @@ -5817,6 +5865,13 @@ bool FurnaceGUI::finish() { SDL_DestroyWindow(sdlWin); e->setConf("lastDir",workingDir); + e->setConf("lastDirSong",workingDirSong); + e->setConf("lastDirIns",workingDirIns); + e->setConf("lastDirWave",workingDirWave); + e->setConf("lastDirSample",workingDirSample); + e->setConf("lastDirAudioExport",workingDirAudioExport); + e->setConf("lastDirVGMExport",workingDirVGMExport); + e->setConf("lastDirFont",workingDirFont); // commit last open windows e->setConf("editControlsOpen",editControlsOpen); diff --git a/src/gui/gui.h b/src/gui/gui.h index 0aebddfcc..8db639ab5 100644 --- a/src/gui/gui.h +++ b/src/gui/gui.h @@ -426,6 +426,7 @@ class FurnaceGUI { SDL_Renderer* sdlRend; String workingDir, fileName, clipboard, warnString, errorString, lastError, curFileName, nextFile; + String workingDirSong, workingDirIns, workingDirWave, workingDirSample, workingDirAudioExport, workingDirVGMExport, workingDirFont; String mmlString[12]; String mmlStringW; diff --git a/src/ta-utils.h b/src/ta-utils.h index db876f43f..98b184f31 100644 --- a/src/ta-utils.h +++ b/src/ta-utils.h @@ -30,8 +30,10 @@ typedef SSIZE_T ssize_t; #ifdef _WIN32 #define DIR_SEPARATOR '\\' +#define DIR_SEPARATOR_STR "\\" #else #define DIR_SEPARATOR '/' +#define DIR_SEPARATOR_STR "/" #endif typedef std::string String; From 1957c19f345452a7fee06c54cee343c93e5c6d41 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Wed, 2 Mar 2022 00:09:28 -0500 Subject: [PATCH 42/50] GUI: insert 100ms delay when minimized issue #220 --- src/gui/gui.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/gui/gui.cpp b/src/gui/gui.cpp index bcbebef2a..e8b769a47 100644 --- a/src/gui/gui.cpp +++ b/src/gui/gui.cpp @@ -17,6 +17,7 @@ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ +#include #define _USE_MATH_DEFINES #include "gui.h" #include "util.h" @@ -5350,6 +5351,10 @@ bool FurnaceGUI::loop() { commitSettings(); willCommit=false; } + + if (SDL_GetWindowFlags(sdlWin)&SDL_WINDOW_MINIMIZED) { + SDL_Delay(100); + } } return false; } From e133fb490641d65e606a53eaf5dfdd061e704e31 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Wed, 2 Mar 2022 00:38:32 -0500 Subject: [PATCH 43/50] GUI: make several things rate-independent --- src/gui/gui.cpp | 17 +++++++++-------- src/gui/gui.h | 2 +- src/gui/pattern.cpp | 8 +++++--- 3 files changed, 15 insertions(+), 12 deletions(-) diff --git a/src/gui/gui.cpp b/src/gui/gui.cpp index e8b769a47..fd5d5a7d2 100644 --- a/src/gui/gui.cpp +++ b/src/gui/gui.cpp @@ -59,13 +59,13 @@ extern "C" { #define LAYOUT_INI "/layout.ini" #endif -bool Particle::update() { - pos.x+=speed.x; - pos.y+=speed.y; - speed.x*=friction; - speed.y*=friction; - speed.y+=gravity; - life-=lifeSpeed; +bool Particle::update(float frameTime) { + pos.x+=speed.x*frameTime; + pos.y+=speed.y*frameTime; + speed.x*=1.0-((1.0-friction)*frameTime); + speed.y*=1.0-((1.0-friction)*frameTime); + speed.y+=gravity*frameTime; + life-=lifeSpeed*frameTime; return (life>0); } @@ -1437,10 +1437,11 @@ void FurnaceGUI::drawVolMeter() { ImGuiStyle& style=ImGui::GetStyle(); ImGui::ItemSize(ImVec2(4.0f,4.0f),style.FramePadding.y); ImU32 lowColor=ImGui::GetColorU32(uiColors[GUI_COLOR_VOLMETER_LOW]); + float peakDecay=0.05f*60.0f*ImGui::GetIO().DeltaTime; if (ImGui::ItemAdd(rect,ImGui::GetID("volMeter"))) { ImGui::RenderFrame(rect.Min,rect.Max,ImGui::GetColorU32(ImGuiCol_FrameBg),true,style.FrameRounding); for (int i=0; i<2; i++) { - peak[i]*=0.95; + peak[i]*=1.0-peakDecay; if (peak[i]<0.0001) peak[i]=0.0; for (int j=0; joscSize; j++) { if (fabs(e->oscBuf[i][j])>peak[i]) { diff --git a/src/gui/gui.h b/src/gui/gui.h index 8db639ab5..495073a21 100644 --- a/src/gui/gui.h +++ b/src/gui/gui.h @@ -391,7 +391,7 @@ struct Particle { const char* type; ImVec2 pos, speed; float gravity, friction, life, lifeSpeed; - bool update(); + bool update(float frameTime); Particle(ImU32* color, const char* ty, float x, float y, float sX, float sY, float g, float fr, float l, float lS): colors(color), type(ty), diff --git a/src/gui/pattern.cpp b/src/gui/pattern.cpp index c6c78ba8d..7d9eb43ce 100644 --- a/src/gui/pattern.cpp +++ b/src/gui/pattern.cpp @@ -451,7 +451,7 @@ void FurnaceGUI::drawPattern() { chanHead.x*=0.25+keyHit[i]; chanHead.y*=0.25+keyHit[i]; chanHead.z*=0.25+keyHit[i]; chanHeadActive.x*=0.8; chanHeadActive.y*=0.8; chanHeadActive.z*=0.8; chanHeadHover.x*=0.4+keyHit[i]; chanHeadHover.y*=0.4+keyHit[i]; chanHeadHover.z*=0.4+keyHit[i]; - keyHit[i]-=0.02; + keyHit[i]-=0.02*60.0*ImGui::GetIO().DeltaTime; if (keyHit[i]<0) keyHit[i]=0; ImGui::PushStyleColor(ImGuiCol_Header,chanHead); ImGui::PushStyleColor(ImGuiCol_HeaderActive,chanHeadActive); @@ -717,6 +717,8 @@ void FurnaceGUI::drawPattern() { } } + float frameTime=ImGui::GetIO().DeltaTime*60.0f; + // note slides ImVec2 arrowPoints[7]; if (e->isPlaying()) for (int i=0; iAddPolyline(arrowPoints,7,ImGui::GetColorU32(col),ImDrawFlags_None,5.0f*dpiScale); } } - patChanSlideY[i]+=((ch->portaNote<=ch->note)?-8:8)*dpiScale; + patChanSlideY[i]+=((ch->portaNote<=ch->note)?-8:8)*dpiScale*frameTime; if (width>0) { if (patChanSlideY[i]<0) { patChanSlideY[i]=-fmod(-patChanSlideY[i],width*0.7); @@ -778,7 +780,7 @@ void FurnaceGUI::drawPattern() { ImDrawList* fdl=ImGui::GetForegroundDrawList(); for (size_t i=0; i255) part.life=255; fdl->AddText( iconFont, From 88433152568416f0a3a91b2cbbe34008b4cd2a99 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Wed, 2 Mar 2022 00:58:49 -0500 Subject: [PATCH 44/50] OPLL: 99.99% VGM export --- src/engine/sysDef.cpp | 3 +++ src/engine/vgmOps.cpp | 35 +++++++++++++++++++++++++++++++++++ 2 files changed, 38 insertions(+) diff --git a/src/engine/sysDef.cpp b/src/engine/sysDef.cpp index a0d211c68..52a4cb89e 100644 --- a/src/engine/sysDef.cpp +++ b/src/engine/sysDef.cpp @@ -1601,6 +1601,9 @@ bool DivEngine::isVGMExportable(DivSystem which) { case DIV_SYSTEM_QSOUND: case DIV_SYSTEM_SEGAPCM: case DIV_SYSTEM_SEGAPCM_COMPAT: + case DIV_SYSTEM_OPLL: + case DIV_SYSTEM_OPLL_DRUMS: + case DIV_SYSTEM_VRC7: return true; default: return false; diff --git a/src/engine/vgmOps.cpp b/src/engine/vgmOps.cpp index 71f313e43..97f427372 100644 --- a/src/engine/vgmOps.cpp +++ b/src/engine/vgmOps.cpp @@ -214,6 +214,21 @@ void DivEngine::performVGMWrite(SafeWriter* w, DivSystem sys, DivRegWrite& write w->writeC(0); w->writeC(0xbf); break; + case DIV_SYSTEM_OPLL: + case DIV_SYSTEM_OPLL_DRUMS: + case DIV_SYSTEM_VRC7: + for (int i=0; i<9; i++) { + w->writeC(isSecond?0xa1:0x51); + w->writeC(0x20+i); + w->writeC(0); + w->writeC(isSecond?0xa1:0x51); + w->writeC(0x30+i); + w->writeC(0); + w->writeC(isSecond?0xa1:0x51); + w->writeC(0x10+i); + w->writeC(0); + } + break; case DIV_SYSTEM_AY8910: w->writeC(0xa0); w->writeC(isSecond?0x87:7); @@ -395,6 +410,13 @@ void DivEngine::performVGMWrite(SafeWriter* w, DivSystem sys, DivRegWrite& write break; } break; + case DIV_SYSTEM_OPLL: + case DIV_SYSTEM_OPLL_DRUMS: + case DIV_SYSTEM_VRC7: + w->writeC(isSecond?0xa1:0x51); + w->writeC(write.addr&0xff); + w->writeC(write.val); + break; case DIV_SYSTEM_AY8910: case DIV_SYSTEM_AY8930: w->writeC(0xa0); @@ -682,6 +704,19 @@ SafeWriter* DivEngine::saveVGM(bool* sysToExport, bool loop) { howManyChips++; } break; + case DIV_SYSTEM_OPLL: + case DIV_SYSTEM_OPLL_DRUMS: + case DIV_SYSTEM_VRC7: + if (!hasOPLL) { + hasOPLL=disCont[i].dispatch->chipClock; + willExport[i]=true; + } else if (!(hasOPLL&0x40000000)) { + isSecond[i]=true; + willExport[i]=true; + hasOPLL|=0x40000000; + howManyChips++; + } + break; case DIV_SYSTEM_LYNX: if (!hasLynx) { hasLynx=disCont[i].dispatch->chipClock; From d94e0ec3e79289364a531968c9e7105a18de6447 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Wed, 2 Mar 2022 02:22:51 -0500 Subject: [PATCH 45/50] GUI: prepare a better new song thingy --- src/engine/engine.cpp | 2 +- src/engine/engine.h | 2 +- src/gui/gui.cpp | 519 ++++++++++++++++++++++++++++++++++++++---- src/gui/gui.h | 12 +- 4 files changed, 489 insertions(+), 46 deletions(-) diff --git a/src/engine/engine.cpp b/src/engine/engine.cpp index 08ec2f590..a8b023d7d 100644 --- a/src/engine/engine.cpp +++ b/src/engine/engine.cpp @@ -534,7 +534,7 @@ void DivEngine::renderSamples() { qsoundMemLen=memPos+256; } -void DivEngine::createNew(int* description) { +void DivEngine::createNew(const int* description) { quitDispatch(); isBusy.lock(); song.unload(); diff --git a/src/engine/engine.h b/src/engine/engine.h index 02eed74ed..02870178a 100644 --- a/src/engine/engine.h +++ b/src/engine/engine.h @@ -262,7 +262,7 @@ class DivEngine { DivWavetable* getWave(int index); DivSample* getSample(int index); // start fresh - void createNew(int* description); + void createNew(const int* description); // load a file. bool load(unsigned char* f, size_t length); // save as .dmf. diff --git a/src/gui/gui.cpp b/src/gui/gui.cpp index fd5d5a7d2..9be913ae1 100644 --- a/src/gui/gui.cpp +++ b/src/gui/gui.cpp @@ -1910,11 +1910,6 @@ void FurnaceGUI::drawDebug() { ImGui::End(); } -#define NEWSONG_CATEGORY(x) \ - if (ImGui::Selectable(x,false,ImGuiSelectableFlags_DontClosePopups)) { \ - printf("selected a category\n"); \ - } - void FurnaceGUI::drawNewSong() { bool accepted=false; @@ -1923,7 +1918,7 @@ void FurnaceGUI::drawNewSong() { ImGui::Text("Choose a System!"); ImGui::PopFont(); - if (ImGui::BeginTable("sysPicker",2,ImGuiTableFlags_Borders)) { + if (ImGui::BeginTable("sysPicker",2)) { ImGui::TableSetupColumn("c0",ImGuiTableColumnFlags_WidthFixed,0.0f); ImGui::TableSetupColumn("c1",ImGuiTableColumnFlags_WidthStretch,0.0f); @@ -1937,30 +1932,36 @@ void FurnaceGUI::drawNewSong() { // CATEGORIES ImGui::TableNextColumn(); - NEWSONG_CATEGORY("All chips"); - NEWSONG_CATEGORY("FM"); - NEWSONG_CATEGORY("Square"); - NEWSONG_CATEGORY("Sample"); - NEWSONG_CATEGORY("Wavetable"); - NEWSONG_CATEGORY("Other/Special"); - NEWSONG_CATEGORY("Game consoles"); - NEWSONG_CATEGORY("Computers"); - NEWSONG_CATEGORY("Arcade systems"); - NEWSONG_CATEGORY("DefleMask-compatible"); + int index=0; + for (FurnaceGUISysCategory& i: sysCategories) { + if (ImGui::Selectable(i.name,newSongCategory==index,ImGuiSelectableFlags_DontClosePopups)) { \ + newSongCategory=index; + } + index++; + } // SYSTEMS ImGui::TableNextColumn(); if (ImGui::BeginTable("Systems",1,ImGuiTableFlags_BordersInnerV|ImGuiTableFlags_ScrollY)) { - ImGui::TableNextRow(); - ImGui::TableNextColumn(); - ImGui::Selectable("System system",false,ImGuiSelectableFlags_DontClosePopups); + for (FurnaceGUISysDef& i: sysCategories[newSongCategory].systems) { + ImGui::TableNextRow(); + ImGui::TableNextColumn(); + if (ImGui::Selectable(i.name,false,ImGuiSelectableFlags_DontClosePopups)) { + nextDesc=i.definition.data(); + accepted=true; + } + } ImGui::EndTable(); } ImGui::EndTable(); } - if (ImGui::Button("Go ahead") || accepted) { + if (ImGui::Button("Cancel")) { + ImGui::CloseCurrentPopup(); + } + + if (accepted) { e->createNew(nextDesc); undoHist.clear(); redoHist.clear(); @@ -5286,7 +5287,7 @@ bool FurnaceGUI::loop() { } ImGui::SetNextWindowSizeConstraints(ImVec2(400.0f*dpiScale,200.0f*dpiScale),ImVec2(scrW*dpiScale,scrH*dpiScale)); - if (ImGui::BeginPopupModal("New Song",NULL)) { + if (ImGui::BeginPopupModal("New Song",NULL,ImGuiWindowFlags_NoMove)) { ImGui::SetWindowPos(ImVec2(((scrW*dpiScale)-ImGui::GetWindowSize().x)*0.5,((scrH*dpiScale)-ImGui::GetWindowSize().y)*0.5)); drawNewSong(); ImGui::EndPopup(); @@ -5949,6 +5950,7 @@ FurnaceGUI::FurnaceGUI(): isClipping(0), extraChannelButtons(0), patNameTarget(-1), + newSongCategory(0), editControlsOpen(true), ordersOpen(true), insListOpen(true), @@ -6103,30 +6105,469 @@ FurnaceGUI::FurnaceGUI(): valueKeys[SDLK_KP_7]=7; valueKeys[SDLK_KP_8]=8; valueKeys[SDLK_KP_9]=9; - - /* -const char* sysCategories[]={ - "All chips", - "FM", - "Square", - "Sample", - "Wavetable", - "Other/Special", - "Game consoles", - "Computers", - "Arcade systems", - "DefleMask-compatible" -}; -*/ FurnaceGUISysCategory cat; - cat=FurnaceGUISysCategory("All chips"); + cat=FurnaceGUISysCategory("FM"); cat.systems.push_back(FurnaceGUISysDef( - "Yamaha YM2612", NULL /*{ + "Yamaha YM2612", { DIV_SYSTEM_YM2612, 64, 0, 0, 0 - }*/ + } + )); + cat.systems.push_back(FurnaceGUISysDef( + "Yamaha YM2612 (extended channel 3)", { + DIV_SYSTEM_YM2612_EXT, 64, 0, 0, + 0 + } + )); + cat.systems.push_back(FurnaceGUISysDef( + "Yamaha YM2151", { + DIV_SYSTEM_YM2151, 64, 0, 0, + 0 + } + )); + cat.systems.push_back(FurnaceGUISysDef( + "Yamaha YM2610", { + DIV_SYSTEM_YM2610_FULL, 64, 0, 0, + 0 + } + )); + cat.systems.push_back(FurnaceGUISysDef( + "Yamaha YM2610 (extended channel 2)", { + DIV_SYSTEM_YM2610_FULL_EXT, 64, 0, 0, + 0 + } + )); + cat.systems.push_back(FurnaceGUISysDef( + "Yamaha YM2610B", { + DIV_SYSTEM_YM2610, 64, 0, 0, + 0 + } + )); + cat.systems.push_back(FurnaceGUISysDef( + "Yamaha YM2610B (extended channel 3)", { + DIV_SYSTEM_YM2610B_EXT, 64, 0, 0, + 0 + } + )); + cat.systems.push_back(FurnaceGUISysDef( + "Yamaha YM2413", { + DIV_SYSTEM_OPLL, 64, 0, 0, + 0 + } + )); + sysCategories.push_back(cat); + + cat=FurnaceGUISysCategory("Square"); + cat.systems.push_back(FurnaceGUISysDef( + "TI SN76489", { + DIV_SYSTEM_SMS, 64, 0, 0, + 0 + } + )); + cat.systems.push_back(FurnaceGUISysDef( + "AY-3-8910", { + DIV_SYSTEM_AY8910, 64, 0, 0, + 0 + } + )); + cat.systems.push_back(FurnaceGUISysDef( + "Philips SAA1099", { + DIV_SYSTEM_SAA1099, 64, 0, 0, + 0 + } + )); + sysCategories.push_back(cat); + + cat=FurnaceGUISysCategory("Sample"); + cat.systems.push_back(FurnaceGUISysDef( + "Amiga", { + DIV_SYSTEM_AMIGA, 64, 0, 0, + 0 + } + )); + cat.systems.push_back(FurnaceGUISysDef( + "SegaPCM", { + DIV_SYSTEM_SEGAPCM, 64, 0, 0, + 0 + } + )); + cat.systems.push_back(FurnaceGUISysDef( + "Capcom QSound", { + DIV_SYSTEM_QSOUND, 64, 0, 0, + 0 + } + )); + sysCategories.push_back(cat); + + cat=FurnaceGUISysCategory("Game consoles"); + cat.systems.push_back(FurnaceGUISysDef( + "Sega Genesis", { + DIV_SYSTEM_YM2612, 64, 0, 0, + DIV_SYSTEM_SMS, 24, 0, 0, + 0 + } + )); + cat.systems.push_back(FurnaceGUISysDef( + "Sega Genesis (extended channel 3)", { + DIV_SYSTEM_YM2612_EXT, 64, 0, 0, + DIV_SYSTEM_SMS, 24, 0, 0, + 0 + } + )); + cat.systems.push_back(FurnaceGUISysDef( + "Sega Master System", { + DIV_SYSTEM_SMS, 64, 0, 0, + 0 + } + )); + cat.systems.push_back(FurnaceGUISysDef( + "Sega Master System (with FM expansion)", { + DIV_SYSTEM_SMS, 64, 0, 0, + DIV_SYSTEM_OPLL, 64, 0, 0, + 0 + } + )); + cat.systems.push_back(FurnaceGUISysDef( + "Sega Master System (with FM expansion in drums mode)", { + DIV_SYSTEM_SMS, 64, 0, 0, + DIV_SYSTEM_OPLL_DRUMS, 64, 0, 0, + 0 + } + )); + cat.systems.push_back(FurnaceGUISysDef( + "Game Boy", { + DIV_SYSTEM_GB, 64, 0, 0, + 0 + } + )); + cat.systems.push_back(FurnaceGUISysDef( + "NEC PC Engine/TurboGrafx-16", { + DIV_SYSTEM_PCE, 64, 0, 0, + 0 + } + )); + cat.systems.push_back(FurnaceGUISysDef( + "NES", { + DIV_SYSTEM_NES, 64, 0, 0, + 0 + } + )); + cat.systems.push_back(FurnaceGUISysDef( + "NES with Konami VRC7", { + DIV_SYSTEM_NES, 64, 0, 0, + DIV_SYSTEM_VRC7, 64, 0, 0, + 0 + } + )); + cat.systems.push_back(FurnaceGUISysDef( + "NES with Sunsoft 5B", { + DIV_SYSTEM_NES, 64, 0, 0, + DIV_SYSTEM_AY8910, 64, 0, 38, + 0 + } + )); + cat.systems.push_back(FurnaceGUISysDef( + "Mattel Intellivision", { + DIV_SYSTEM_AY8910, 64, 0, 6, + 0 + } + )); + cat.systems.push_back(FurnaceGUISysDef( + "Vectrex", { + DIV_SYSTEM_AY8910, 64, 0, 4, + 0 + } + )); + cat.systems.push_back(FurnaceGUISysDef( + "Neo Geo AES", { + DIV_SYSTEM_YM2610_FULL, 64, 0, 0, + 0 + } + )); + cat.systems.push_back(FurnaceGUISysDef( + "Neo Geo AES (extended channel 2)", { + DIV_SYSTEM_YM2610_FULL_EXT, 64, 0, 0, + 0 + } + )); + cat.systems.push_back(FurnaceGUISysDef( + "Atari 2600/7800", { + DIV_SYSTEM_TIA, 64, 0, 0, + 0 + } + )); + cat.systems.push_back(FurnaceGUISysDef( + "Atari Lynx", { + DIV_SYSTEM_LYNX, 64, 0, 0, + 0 + } + )); + sysCategories.push_back(cat); + + cat=FurnaceGUISysCategory("Computers"); + cat.systems.push_back(FurnaceGUISysDef( + "Commodore PET", { + DIV_SYSTEM_PET, 64, 0, 0, + 0 + } + )); + cat.systems.push_back(FurnaceGUISysDef( + "Commodore VIC-20", { + DIV_SYSTEM_VIC20, 64, 0, 1, + 0 + } + )); + cat.systems.push_back(FurnaceGUISysDef( + "Commodore 64 (6581 SID)", { + DIV_SYSTEM_C64_6581, 64, 0, 1, + 0 + } + )); + cat.systems.push_back(FurnaceGUISysDef( + "Commodore 64 (8580 SID)", { + DIV_SYSTEM_C64_8580, 64, 0, 1, + 0 + } + )); + cat.systems.push_back(FurnaceGUISysDef( + "Amiga", { + DIV_SYSTEM_AMIGA, 64, 0, 0, + 0 + } + )); + cat.systems.push_back(FurnaceGUISysDef( + "MSX", { + DIV_SYSTEM_AY8910, 64, 0, 16, + 0 + } + )); + cat.systems.push_back(FurnaceGUISysDef( + "ZX Spectrum (48K)", { + DIV_SYSTEM_AY8910, 64, 0, 2, + 0 + } + )); + cat.systems.push_back(FurnaceGUISysDef( + "ZX Spectrum (128K)", { + DIV_SYSTEM_AY8910, 64, 0, 1, + 0 + } + )); + cat.systems.push_back(FurnaceGUISysDef( + "Amstrad CPC", { + DIV_SYSTEM_AY8910, 64, 0, 5, + 0 + } + )); + cat.systems.push_back(FurnaceGUISysDef( + "SAM Coupé", { + DIV_SYSTEM_SAA1099, 64, 0, 0, + 0 + } + )); + cat.systems.push_back(FurnaceGUISysDef( + "BBC Micro", { + DIV_SYSTEM_SMS, 64, 0, 6, + 0 + } + )); + cat.systems.push_back(FurnaceGUISysDef( + "PC (barebones)", { + DIV_SYSTEM_PCSPKR, 64, 0, 0, + 0 + } + )); + cat.systems.push_back(FurnaceGUISysDef( + "PC + Covox Sound Master", { + DIV_SYSTEM_AY8930, 64, 0, 3, + DIV_SYSTEM_PCSPKR, 64, 0, 0, + 0 + } + )); + cat.systems.push_back(FurnaceGUISysDef( + "PC + Game Blaster", { + DIV_SYSTEM_SAA1099, 64, -127, 1, + DIV_SYSTEM_SAA1099, 64, 127, 1, + DIV_SYSTEM_PCSPKR, 64, 0, 0, + 0 + } + )); + cat.systems.push_back(FurnaceGUISysDef( + "PC + AdLib/Sound Blaster", { + DIV_SYSTEM_OPL2, 64, 0, 0, + DIV_SYSTEM_PCSPKR, 64, 0, 0, + 0 + } + )); + cat.systems.push_back(FurnaceGUISysDef( + "PC + AdLib/Sound Blaster (drums mode)", { + DIV_SYSTEM_OPL2_DRUMS, 64, 0, 0, + DIV_SYSTEM_PCSPKR, 64, 0, 0, + 0 + } + )); + cat.systems.push_back(FurnaceGUISysDef( + "PC + Sound Blaster Pro 2", { + DIV_SYSTEM_OPL3, 64, 0, 0, + DIV_SYSTEM_PCSPKR, 64, 0, 0, + 0 + } + )); + cat.systems.push_back(FurnaceGUISysDef( + "PC + Sound Blaster Pro 2 (drums mode)", { + DIV_SYSTEM_OPL3_DRUMS, 64, 0, 0, + DIV_SYSTEM_PCSPKR, 64, 0, 0, + 0 + } + )); + /* + cat.systems.push_back(FurnaceGUISysDef( + "Sharp X68000", { + DIV_SYSTEM_AY8910, 64, 0, 16, + 0 + } + ));*/ + sysCategories.push_back(cat); + + cat=FurnaceGUISysCategory("Arcade systems"); + cat.systems.push_back(FurnaceGUISysDef( + "Bally Midway MCR", { + DIV_SYSTEM_AY8910, 64, 0, 0, + DIV_SYSTEM_AY8910, 64, 0, 0, + 0 + } + )); + cat.systems.push_back(FurnaceGUISysDef( + "Sega Kyugo", { + DIV_SYSTEM_AY8910, 64, 0, 4, + DIV_SYSTEM_AY8910, 64, 0, 4, + 0 + } + )); + cat.systems.push_back(FurnaceGUISysDef( + "Sega OutRun/X Board", { + DIV_SYSTEM_YM2151, 64, 0, 0, + DIV_SYSTEM_SEGAPCM, 64, 0, 0, + 0 + } + )); + cat.systems.push_back(FurnaceGUISysDef( + "Neo Geo MVS", { + DIV_SYSTEM_YM2610_FULL, 64, 0, 0, + 0 + } + )); + cat.systems.push_back(FurnaceGUISysDef( + "Neo Geo MVS (extended channel 2)", { + DIV_SYSTEM_YM2610_FULL_EXT, 64, 0, 0, + 0 + } + )); + cat.systems.push_back(FurnaceGUISysDef( + "Taito Arcade", { + DIV_SYSTEM_YM2610B, 64, 0, 0, + 0 + } + )); + cat.systems.push_back(FurnaceGUISysDef( + "Taito Arcade (extended channel 3)", { + DIV_SYSTEM_YM2610B_EXT, 64, 0, 0, + 0 + } + )); + cat.systems.push_back(FurnaceGUISysDef( + "Capcom CPS-2 (QSound)", { + DIV_SYSTEM_QSOUND, 64, 0, 0, + 0 + } + )); + sysCategories.push_back(cat); + + cat=FurnaceGUISysCategory("DefleMask-compatible"); + cat.systems.push_back(FurnaceGUISysDef( + "Sega Genesis", { + DIV_SYSTEM_YM2612, 64, 0, 0, + DIV_SYSTEM_SMS, 24, 0, 0, + 0 + } + )); + cat.systems.push_back(FurnaceGUISysDef( + "Sega Genesis (extended channel 3)", { + DIV_SYSTEM_YM2612_EXT, 64, 0, 0, + DIV_SYSTEM_SMS, 24, 0, 0, + 0 + } + )); + cat.systems.push_back(FurnaceGUISysDef( + "Sega Master System", { + DIV_SYSTEM_SMS, 64, 0, 0, + 0 + } + )); + cat.systems.push_back(FurnaceGUISysDef( + "Sega Master System (with FM expansion)", { + DIV_SYSTEM_SMS, 64, 0, 0, + DIV_SYSTEM_OPLL, 64, 0, 0, + 0 + } + )); + cat.systems.push_back(FurnaceGUISysDef( + "Game Boy", { + DIV_SYSTEM_GB, 64, 0, 0, + 0 + } + )); + cat.systems.push_back(FurnaceGUISysDef( + "NEC PC Engine/TurboGrafx-16", { + DIV_SYSTEM_PCE, 64, 0, 0, + 0 + } + )); + cat.systems.push_back(FurnaceGUISysDef( + "NES", { + DIV_SYSTEM_NES, 64, 0, 0, + 0 + } + )); + cat.systems.push_back(FurnaceGUISysDef( + "NES with Konami VRC7", { + DIV_SYSTEM_NES, 64, 0, 0, + DIV_SYSTEM_VRC7, 64, 0, 0, + 0 + } + )); + cat.systems.push_back(FurnaceGUISysDef( + "Commodore 64 (6581 SID)", { + DIV_SYSTEM_C64_6581, 64, 0, 1, + 0 + } + )); + cat.systems.push_back(FurnaceGUISysDef( + "Commodore 64 (8580 SID)", { + DIV_SYSTEM_C64_8580, 64, 0, 1, + 0 + } + )); + cat.systems.push_back(FurnaceGUISysDef( + "Arcade (YM2151 and SegaPCM)", { + DIV_SYSTEM_YM2151, 64, 0, 0, + DIV_SYSTEM_SEGAPCM_COMPAT, 64, 0, 0, + 0 + } + )); + cat.systems.push_back(FurnaceGUISysDef( + "Neo Geo CD", { + DIV_SYSTEM_YM2610, 64, 0, 0, + 0 + } + )); + cat.systems.push_back(FurnaceGUISysDef( + "Neo Geo CD (extended channel 2)", { + DIV_SYSTEM_YM2610_EXT, 64, 0, 0, + 0 + } )); sysCategories.push_back(cat); diff --git a/src/gui/gui.h b/src/gui/gui.h index 495073a21..e41d71450 100644 --- a/src/gui/gui.h +++ b/src/gui/gui.h @@ -23,6 +23,7 @@ #include "imgui_impl_sdlrenderer.h" #include #include +#include #include #include @@ -405,9 +406,10 @@ struct Particle { struct FurnaceGUISysDef { const char* name; - const int* definition; - FurnaceGUISysDef(const char* n, const int* def): - name(n), definition(def) {} + std::vector definition; + FurnaceGUISysDef(const char* n, std::initializer_list def): + name(n), definition(def) { + } }; struct FurnaceGUISysCategory { @@ -540,7 +542,7 @@ class FurnaceGUI { char finalLayoutPath[4096]; int curIns, curWave, curSample, curOctave, oldRow, oldOrder, oldOrder1, editStep, exportLoops, soloChan, soloTimeout, orderEditMode, orderCursor; - int loopOrder, loopRow, loopEnd, isClipping, extraChannelButtons, patNameTarget; + int loopOrder, loopRow, loopEnd, isClipping, extraChannelButtons, patNameTarget, newSongCategory; bool editControlsOpen, ordersOpen, insListOpen, songInfoOpen, patternOpen, insEditOpen; bool waveListOpen, waveEditOpen, sampleListOpen, sampleEditOpen, aboutOpen, settingsOpen; bool mixerOpen, debugOpen, oscOpen, volMeterOpen, statsOpen, compatFlagsOpen; @@ -552,7 +554,7 @@ class FurnaceGUI { float peak[2]; float patChanX[DIV_MAX_CHANS+1]; float patChanSlideY[DIV_MAX_CHANS+1]; - int* nextDesc; + const int* nextDesc; // bit 31: ctrl // bit 30: reserved for SDL scancode mask From f95d38521e9cf293465183d7e11d5e700582172e Mon Sep 17 00:00:00 2001 From: cam900 Date: Wed, 2 Mar 2022 17:24:07 +0900 Subject: [PATCH 46/50] Fix chip correction --- src/gui/gui.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/gui/gui.cpp b/src/gui/gui.cpp index 9be913ae1..0aeeb129e 100644 --- a/src/gui/gui.cpp +++ b/src/gui/gui.cpp @@ -6141,7 +6141,7 @@ FurnaceGUI::FurnaceGUI(): )); cat.systems.push_back(FurnaceGUISysDef( "Yamaha YM2610B", { - DIV_SYSTEM_YM2610, 64, 0, 0, + DIV_SYSTEM_YM2610B, 64, 0, 0, 0 } )); From 0114523c96e9fc07fe796d8e90eb6835ad4f0595 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Wed, 2 Mar 2022 03:35:30 -0500 Subject: [PATCH 47/50] OPLL: fix volume setting --- src/engine/platform/opll.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/engine/platform/opll.cpp b/src/engine/platform/opll.cpp index 651ff4b7d..f49f48731 100644 --- a/src/engine/platform/opll.cpp +++ b/src/engine/platform/opll.cpp @@ -471,7 +471,7 @@ int DivPlatformOPLL::dispatch(DivCommand c) { if (!chan[c.chan].std.hasVol) { chan[c.chan].outVol=c.value; } - if (c.chan>=6 || properDrums) { + if (c.chan>=6 && properDrums) { drumVol[c.chan-6]=15-chan[c.chan].outVol; rWrite(0x36,drumVol[0]); rWrite(0x37,drumVol[1]|(drumVol[4]<<4)); From 251984922891053df733429d00e4006fe567e52b Mon Sep 17 00:00:00 2001 From: freq-mod <32672779+freq-mod@users.noreply.github.com> Date: Wed, 2 Mar 2022 10:14:18 +0100 Subject: [PATCH 48/50] OPLL systems --- src/engine/sysDef.cpp | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/engine/sysDef.cpp b/src/engine/sysDef.cpp index 52a4cb89e..3c988ceb8 100644 --- a/src/engine/sysDef.cpp +++ b/src/engine/sysDef.cpp @@ -463,6 +463,18 @@ const char* DivEngine::getSongSystemName() { return "Sega Genesis Extended Channel 3"; } + if (song.system[0]==DIV_SYSTEM_OPLL && song.system[1]==DIV_SYSTEM_SMS) { + return "NTSC-J Sega Master System"; + } + if (song.system[0]==DIV_SYSTEM_OPLL_DRUMS && song.system[1]==DIV_SYSTEM_SMS) { + return "NTSC-J Sega Master System + drums"; + } + if (song.system[0]==DIV_SYSTEM_OPLL && song.system[1]==DIV_SYSTEM_AY8910) { + return "MSX-MUSIC"; + } + if (song.system[0]==DIV_SYSTEM_OPLL_DRUMS && song.system[1]==DIV_SYSTEM_AY8910) { + return "MSX-MUSIC + drums"; + } if (song.system[0]==DIV_SYSTEM_C64_6581 && song.system[1]==DIV_SYSTEM_C64_6581) { return "Commodore 64 with dual 6581"; } From 63456a88d838ba529b6c0eaf1d51cfb8b03f7d6d Mon Sep 17 00:00:00 2001 From: tildearrow Date: Wed, 2 Mar 2022 16:10:18 -0500 Subject: [PATCH 49/50] get rid of SDL1 check see pull request #244 --- CMakeLists.txt | 43 ++++++++++++------------------------------- 1 file changed, 12 insertions(+), 31 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 761a4a299..af3e3a7d1 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -150,39 +150,20 @@ endif() if (SYSTEM_SDL2) if (PKG_CONFIG_FOUND) - pkg_check_modules(SDL sdl>=${SYSTEM_SDL_MIN_VER}) - if (SDL_FOUND) - list(APPEND DEPENDENCIES_INCLUDE_DIRS ${SDL_INCLUDE_DIRS}) - list(APPEND DEPENDENCIES_COMPILE_OPTIONS ${SDL_CFLAGS_OTHER}) - list(APPEND DEPENDENCIES_LIBRARIES ${SDL_LIBRARIES}) - list(APPEND DEPENDENCIES_LIBRARY_DIRS ${SDL_LIBRARY_DIRS}) - list(APPEND DEPENDENCIES_LINK_OPTIONS ${SDL_LDFLAGS_OTHER}) - list(APPEND DEPENDENCIES_LEGACY_LDFLAGS ${SDL_LDFLAGS}) + pkg_check_modules(SDL2 sdl2>=${SYSTEM_SDL_MIN_VER}) + if (SDL2_FOUND) + list(APPEND DEPENDENCIES_INCLUDE_DIRS ${SDL2_INCLUDE_DIRS}) + list(APPEND DEPENDENCIES_COMPILE_OPTIONS ${SDL2_CFLAGS_OTHER}) + list(APPEND DEPENDENCIES_LIBRARIES ${SDL2_LIBRARIES}) + list(APPEND DEPENDENCIES_LIBRARY_DIRS ${SDL2_LIBRARY_DIRS}) + list(APPEND DEPENDENCIES_LINK_OPTIONS ${SDL2_LDFLAGS_OTHER}) + list(APPEND DEPENDENCIES_LEGACY_LDFLAGS ${SDL2_LDFLAGS}) endif() endif() - if (NOT SDL_FOUND) - find_package(SDL ${SYSTEM_SDL_MIN_VER}) - if (SDL_FOUND) - list(APPEND DEPENDENCIES_INCLUDE_DIRS ${SDL_INCLUDE_DIR}) - list(APPEND DEPENDENCIES_LIBRARIES ${SDL_LIBRARY}) - else() - if (PKG_CONFIG_FOUND) - pkg_check_modules(SDL2 sdl2>=${SYSTEM_SDL_MIN_VER}) - if (SDL2_FOUND) - list(APPEND DEPENDENCIES_INCLUDE_DIRS ${SDL2_INCLUDE_DIRS}) - list(APPEND DEPENDENCIES_COMPILE_OPTIONS ${SDL2_CFLAGS_OTHER}) - list(APPEND DEPENDENCIES_LIBRARIES ${SDL2_LIBRARIES}) - list(APPEND DEPENDENCIES_LIBRARY_DIRS ${SDL2_LIBRARY_DIRS}) - list(APPEND DEPENDENCIES_LINK_OPTIONS ${SDL2_LDFLAGS_OTHER}) - list(APPEND DEPENDENCIES_LEGACY_LDFLAGS ${SDL2_LDFLAGS}) - endif() - endif() - if (NOT SDL2_FOUND) - find_package(SDL2 ${SYSTEM_SDL_MIN_VER} REQUIRED) - list(APPEND DEPENDENCIES_INCLUDE_DIRS ${SDL2_INCLUDE_DIR}) - list(APPEND DEPENDENCIES_LIBRARIES ${SDL2_LIBRARY}) - endif() - endif() + if (NOT SDL2_FOUND) + find_package(SDL2 ${SYSTEM_SDL_MIN_VER} REQUIRED) + list(APPEND DEPENDENCIES_INCLUDE_DIRS ${SDL2_INCLUDE_DIR}) + list(APPEND DEPENDENCIES_LIBRARIES ${SDL2_LIBRARY}) endif() message(STATUS "Using system-installed SDL2") else() From abe7c12f22e37a8602f36d7a0f53ca670d3af4ec Mon Sep 17 00:00:00 2001 From: tildearrow Date: Wed, 2 Mar 2022 17:02:57 -0500 Subject: [PATCH 50/50] how am i gonna do this --- src/audio/taAudio.h | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/src/audio/taAudio.h b/src/audio/taAudio.h index 05348a7f0..10712bc03 100644 --- a/src/audio/taAudio.h +++ b/src/audio/taAudio.h @@ -128,11 +128,6 @@ class TAMidiOut { bool send(TAMidiMessage& what); }; -class TAMidi { - std::vector in; - std::vector out; -}; - class TAAudio { protected: TAAudioDesc desc; @@ -145,7 +140,8 @@ class TAAudio { void (*sampleRateChanged)(SampleRateChangeEvent); void (*bufferSizeChanged)(BufferSizeChangeEvent); public: - TAMidi* midi; + std::vector midiIn; + std::vector midiOut; void setSampleRateChangeCallback(void (*callback)(SampleRateChangeEvent)); void setBufferSizeChangeCallback(void (*callback)(BufferSizeChangeEvent));