From 955682b240245ad98cfa3d69dbf6aa2cae796bf7 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Wed, 22 Nov 2023 19:28:36 -0500 Subject: [PATCH] OPL: ymfm core, part 1 --- CMakeLists.txt | 2 + src/engine/dispatchContainer.cpp | 40 +++ src/engine/platform/opl.cpp | 338 +++++++++++++++++++-- src/engine/platform/opl.h | 32 +- src/engine/platform/sound/ymfm/ymfm_fm.h | 4 + src/engine/platform/sound/ymfm/ymfm_fm.ipp | 12 +- src/engine/platform/sound/ymfm/ymfm_opl.h | 9 + src/gui/gui.h | 8 + src/gui/settings.cpp | 51 ++++ 9 files changed, 467 insertions(+), 29 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index e0bbe826c..20e6cf507 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -489,6 +489,8 @@ extern/Nuked-PSG/ympsg.c extern/opm/opm.c extern/Nuked-OPLL/opll.c extern/opl/opl3.c +extern/YM3812-LLE/fmopl2.c +extern/YMF262-LLE/fmopl3.c src/pch.cpp diff --git a/src/engine/dispatchContainer.cpp b/src/engine/dispatchContainer.cpp index 4148c2a3e..878232ca0 100644 --- a/src/engine/dispatchContainer.cpp +++ b/src/engine/dispatchContainer.cpp @@ -427,34 +427,74 @@ void DivDispatchContainer::init(DivSystem sys, DivEngine* eng, int chanCount, do case DIV_SYSTEM_OPL: dispatch=new DivPlatformOPL; ((DivPlatformOPL*)dispatch)->setOPLType(1,false); + if (isRender) { + ((DivPlatformOPL*)dispatch)->setCore(eng->getConfInt("opl2CoreRender",0)); + } else { + ((DivPlatformOPL*)dispatch)->setCore(eng->getConfInt("opl2Core",0)); + } break; case DIV_SYSTEM_OPL_DRUMS: dispatch=new DivPlatformOPL; ((DivPlatformOPL*)dispatch)->setOPLType(1,true); + if (isRender) { + ((DivPlatformOPL*)dispatch)->setCore(eng->getConfInt("opl2CoreRender",0)); + } else { + ((DivPlatformOPL*)dispatch)->setCore(eng->getConfInt("opl2Core",0)); + } break; case DIV_SYSTEM_OPL2: dispatch=new DivPlatformOPL; ((DivPlatformOPL*)dispatch)->setOPLType(2,false); + if (isRender) { + ((DivPlatformOPL*)dispatch)->setCore(eng->getConfInt("opl2CoreRender",0)); + } else { + ((DivPlatformOPL*)dispatch)->setCore(eng->getConfInt("opl2Core",0)); + } break; case DIV_SYSTEM_OPL2_DRUMS: dispatch=new DivPlatformOPL; ((DivPlatformOPL*)dispatch)->setOPLType(2,true); + if (isRender) { + ((DivPlatformOPL*)dispatch)->setCore(eng->getConfInt("opl2CoreRender",0)); + } else { + ((DivPlatformOPL*)dispatch)->setCore(eng->getConfInt("opl2Core",0)); + } break; case DIV_SYSTEM_OPL3: dispatch=new DivPlatformOPL; ((DivPlatformOPL*)dispatch)->setOPLType(3,false); + if (isRender) { + ((DivPlatformOPL*)dispatch)->setCore(eng->getConfInt("opl3CoreRender",0)); + } else { + ((DivPlatformOPL*)dispatch)->setCore(eng->getConfInt("opl3Core",0)); + } break; case DIV_SYSTEM_OPL3_DRUMS: dispatch=new DivPlatformOPL; ((DivPlatformOPL*)dispatch)->setOPLType(3,true); + if (isRender) { + ((DivPlatformOPL*)dispatch)->setCore(eng->getConfInt("opl3CoreRender",0)); + } else { + ((DivPlatformOPL*)dispatch)->setCore(eng->getConfInt("opl3Core",0)); + } break; case DIV_SYSTEM_Y8950: dispatch=new DivPlatformOPL; ((DivPlatformOPL*)dispatch)->setOPLType(8950,false); + if (isRender) { + ((DivPlatformOPL*)dispatch)->setCore(eng->getConfInt("opl2CoreRender",0)); + } else { + ((DivPlatformOPL*)dispatch)->setCore(eng->getConfInt("opl2Core",0)); + } break; case DIV_SYSTEM_Y8950_DRUMS: dispatch=new DivPlatformOPL; ((DivPlatformOPL*)dispatch)->setOPLType(8950,true); + if (isRender) { + ((DivPlatformOPL*)dispatch)->setCore(eng->getConfInt("opl2CoreRender",0)); + } else { + ((DivPlatformOPL*)dispatch)->setCore(eng->getConfInt("opl2Core",0)); + } break; case DIV_SYSTEM_OPZ: dispatch=new DivPlatformTX81Z; diff --git a/src/engine/platform/opl.cpp b/src/engine/platform/opl.cpp index 52968a1a5..f505b7d8d 100644 --- a/src/engine/platform/opl.cpp +++ b/src/engine/platform/opl.cpp @@ -292,12 +292,252 @@ void DivPlatformOPL::acquire_nuked(short** buf, size_t len) { } } +void DivPlatformOPL::acquire_ymfm1(short** buf, size_t len) { + ymfm::ymfm_output<1> out; + + ymfm::ym3526::fm_engine* fme=fm_ymfm1->debug_fm_engine(); + ymfm::fm_channel>* fmChan[9]; + + for (int i=0; i<9; i++) { + fmChan[i]=fme->debug_channel(i); + } + + for (size_t h=0; hwrite(0,w.addr); + fm_ymfm1->write(1,w.val); + + regPool[w.addr&511]=w.val; + writes.pop(); + } + + fm_ymfm1->generate(&out,1); + + buf[0][h]=out.data[0]; + + if (properDrums) { + for (int i=0; i<7; i++) { + oscBuf[i]->data[oscBuf[i]->needle++]=CLAMP(fmChan[i]->debug_output(0)<<2,-32768,32767); + } + oscBuf[7]->data[oscBuf[7]->needle++]=CLAMP(fmChan[7]->debug_special1()<<2,-32768,32767); + oscBuf[8]->data[oscBuf[8]->needle++]=CLAMP(fmChan[8]->debug_special1()<<2,-32768,32767); + oscBuf[9]->data[oscBuf[9]->needle++]=CLAMP(fmChan[8]->debug_special2()<<2,-32768,32767); + oscBuf[10]->data[oscBuf[10]->needle++]=CLAMP(fmChan[7]->debug_special2()<<2,-32768,32767); + } else { + for (int i=0; i<9; i++) { + oscBuf[i]->data[oscBuf[i]->needle++]=CLAMP(fmChan[i]->debug_output(0)<<2,-32768,32767); + } + } + } +} + +void DivPlatformOPL::acquire_ymfm2(short** buf, size_t len) { + ymfm::ymfm_output<1> out; + + ymfm::ym3812::fm_engine* fme=fm_ymfm2->debug_fm_engine(); + ymfm::fm_channel>* fmChan[9]; + + for (int i=0; i<9; i++) { + fmChan[i]=fme->debug_channel(i); + } + + for (size_t h=0; hwrite(0,w.addr); + fm_ymfm2->write(1,w.val); + + regPool[w.addr&511]=w.val; + writes.pop(); + } + + fm_ymfm2->generate(&out,1); + + buf[0][h]=out.data[0]; + + if (properDrums) { + for (int i=0; i<7; i++) { + oscBuf[i]->data[oscBuf[i]->needle++]=CLAMP(fmChan[i]->debug_output(0)<<2,-32768,32767); + } + oscBuf[7]->data[oscBuf[7]->needle++]=CLAMP(fmChan[7]->debug_special1()<<2,-32768,32767); + oscBuf[8]->data[oscBuf[8]->needle++]=CLAMP(fmChan[8]->debug_special1()<<2,-32768,32767); + oscBuf[9]->data[oscBuf[9]->needle++]=CLAMP(fmChan[8]->debug_special2()<<2,-32768,32767); + oscBuf[10]->data[oscBuf[10]->needle++]=CLAMP(fmChan[7]->debug_special2()<<2,-32768,32767); + } else { + for (int i=0; i<9; i++) { + oscBuf[i]->data[oscBuf[i]->needle++]=CLAMP(fmChan[i]->debug_output(0)<<2,-32768,32767); + } + } + } +} + +// TODO: ADPCM +void DivPlatformOPL::acquire_ymfm8950(short** buf, size_t len) { + ymfm::ymfm_output<1> out; + + ymfm::y8950::fm_engine* fme=fm_ymfm8950->debug_fm_engine(); + ymfm::fm_channel>* fmChan[9]; + + for (int i=0; i<9; i++) { + fmChan[i]=fme->debug_channel(i); + } + + for (size_t h=0; hwrite(0,w.addr); + fm_ymfm8950->write(1,w.val); + + regPool[w.addr&511]=w.val; + writes.pop(); + } + + fm_ymfm8950->generate(&out,1); + + buf[0][h]=out.data[0]; + + if (properDrums) { + for (int i=0; i<7; i++) { + oscBuf[i]->data[oscBuf[i]->needle++]=CLAMP(fmChan[i]->debug_output(0)<<2,-32768,32767); + } + oscBuf[7]->data[oscBuf[7]->needle++]=CLAMP(fmChan[7]->debug_special1()<<2,-32768,32767); + oscBuf[8]->data[oscBuf[8]->needle++]=CLAMP(fmChan[8]->debug_special1()<<2,-32768,32767); + oscBuf[9]->data[oscBuf[9]->needle++]=CLAMP(fmChan[8]->debug_special2()<<2,-32768,32767); + oscBuf[10]->data[oscBuf[10]->needle++]=CLAMP(fmChan[7]->debug_special2()<<2,-32768,32767); + } else { + for (int i=0; i<9; i++) { + oscBuf[i]->data[oscBuf[i]->needle++]=CLAMP(fmChan[i]->debug_output(0)<<2,-32768,32767); + } + } + } +} + +void DivPlatformOPL::acquire_ymfm3(short** buf, size_t len) { + ymfm::ymfm_output<4> out; + + ymfm::ymf262::fm_engine* fme=fm_ymfm3->debug_fm_engine(); + ymfm::fm_channel>* fmChan[18]; + + for (int i=0; i<18; i++) { + fmChan[i]=fme->debug_channel(i); + } + + for (size_t h=0; hwrite((w.addr&0x100)?2:0,w.addr); + fm_ymfm3->write(1,w.val); + + regPool[w.addr&511]=w.val; + writes.pop(); + } + + fm_ymfm3->generate(&out,1); + + buf[0][h]=out.data[0]>>1; + if (totalOutputs>1) { + buf[1][h]=out.data[1]>>1; + } + if (totalOutputs>2) { + buf[2][h]=out.data[2]>>1; + } + if (totalOutputs>3) { + buf[3][h]=out.data[3]>>1; + } + if (totalOutputs==6) { + // placeholder for OPL4 + buf[4][h]=0; + buf[5][h]=0; + } + + // TODO: fix 4-op view + if (properDrums) { + for (int i=0; i<16; i++) { + unsigned char ch=outChanMap[i]; + if (ch==255) continue; + int chOut=fmChan[ch]->debug_output(0); + if (chOut==0) { + chOut=fmChan[ch]->debug_output(1); + } + if (chOut==0) { + chOut=fmChan[ch]->debug_output(2); + } + if (chOut==0) { + chOut=fmChan[ch]->debug_output(3); + } + if (i==15) { + oscBuf[i]->data[oscBuf[i]->needle++]=CLAMP(chOut,-32768,32767); + } else { + oscBuf[i]->data[oscBuf[i]->needle++]=CLAMP(chOut<<1,-32768,32767); + } + } + oscBuf[16]->data[oscBuf[16]->needle++]=CLAMP(fmChan[7]->debug_special2()<<1,-32768,32767); + oscBuf[17]->data[oscBuf[17]->needle++]=CLAMP(fmChan[8]->debug_special1()<<1,-32768,32767); + oscBuf[18]->data[oscBuf[18]->needle++]=CLAMP(fmChan[8]->debug_special2()<<1,-32768,32767); + oscBuf[19]->data[oscBuf[19]->needle++]=CLAMP(fmChan[7]->debug_special1()<<1,-32768,32767); + } else { + for (int i=0; i<18; i++) { + unsigned char ch=outChanMap[i]; + if (ch==255) continue; + int chOut=fmChan[ch]->debug_output(0); + if (chOut==0) { + chOut=fmChan[ch]->debug_output(1); + } + if (chOut==0) { + chOut=fmChan[ch]->debug_output(2); + } + if (chOut==0) { + chOut=fmChan[ch]->debug_output(3); + } + oscBuf[i]->data[oscBuf[i]->needle++]=CLAMP(chOut<<1,-32768,32767); + } + } + } +} + +void DivPlatformOPL::acquire_nukedLLE2(short** buf, size_t len) { +} + +void DivPlatformOPL::acquire_nukedLLE3(short** buf, size_t len) { +} + void DivPlatformOPL::acquire(short** buf, size_t len) { - //if (useYMFM) { - // acquire_ymfm(buf,len); - //} else { + if (emuCore==2) { // LLE + switch (chipType) { + case 1: case 2: case 8950: + acquire_nukedLLE2(buf,len); + break; + case 3: case 759: + acquire_nukedLLE3(buf,len); + break; + } + } else if (emuCore==1) { // ymfm + switch (chipType) { + case 1: + acquire_ymfm1(buf,len); + break; + case 2: + acquire_ymfm2(buf,len); + break; + case 8950: + acquire_ymfm8950(buf,len); + break; + case 3: case 759: + acquire_ymfm3(buf,len); + break; + } + } else { // OPL3 acquire_nuked(buf,len); - //} + } } double DivPlatformOPL::NOTE_ADPCMB(int note) { @@ -1620,17 +1860,34 @@ int DivPlatformOPL::getRegisterPoolSize() { void DivPlatformOPL::reset() { while (!writes.empty()) writes.pop(); memset(regPool,0,512); - /* - if (useYMFM) { - fm_ymfm->reset(); - } - */ - if (downsample) { - const unsigned int downsampledRate=(unsigned int)((double)rate*round(COLOR_NTSC/72.0)/(double)chipRateBase); - OPL3_Reset(&fm,downsampledRate); + + const unsigned int downsampledRate=(unsigned int)((double)rate*round(COLOR_NTSC/72.0)/(double)chipRateBase); + + if (emuCore==2) { + // TODO: LLE reset + } else if (emuCore==1) { + switch (chipType) { + case 1: + fm_ymfm1->reset(); + break; + case 2: + fm_ymfm2->reset(); + break; + case 8950: + fm_ymfm8950->reset(); + break; + case 3: case 759: + fm_ymfm3->reset(); + break; + } } else { - OPL3_Reset(&fm,rate); + if (downsample) { + OPL3_Reset(&fm,downsampledRate); + } else { + OPL3_Reset(&fm,rate); + } } + if (dumpWrites) { addWrite(0xffffffff,0); } @@ -1744,8 +2001,8 @@ int DivPlatformOPL::getPortaFloor(int ch) { return (ch>5)?12:0; } -void DivPlatformOPL::setYMFM(bool use) { - useYMFM=use; +void DivPlatformOPL::setCore(unsigned char which) { + emuCore=which; } void DivPlatformOPL::setOPLType(int type, bool drums) { @@ -1891,11 +2148,13 @@ void DivPlatformOPL::setFlags(const DivConfig& flags) { totalOutputs=4; break; } - if (downsample) { - const unsigned int downsampledRate=(unsigned int)((double)rate*round(COLOR_NTSC/72.0)/(double)chipRateBase); - OPL3_Resample(&fm,downsampledRate); - } else { - OPL3_Resample(&fm,rate); + if (emuCore!=1 && emuCore!=2) { + if (downsample) { + const unsigned int downsampledRate=(unsigned int)((double)rate*round(COLOR_NTSC/72.0)/(double)chipRateBase); + OPL3_Resample(&fm,downsampledRate); + } else { + OPL3_Resample(&fm,rate); + } } break; case 4: @@ -1990,6 +2249,29 @@ int DivPlatformOPL::init(DivEngine* p, int channels, int sugRate, const DivConfi for (int i=0; i<20; i++) { oscBuf[i]=new DivDispatchOscBuffer; } + + fm_ymfm1=NULL; + fm_ymfm2=NULL; + fm_ymfm8950=NULL; + fm_ymfm3=NULL; + + if (emuCore==1) { + switch (chipType) { + case 1: + fm_ymfm1=new ymfm::ym3526(iface); + break; + case 2: + fm_ymfm2=new ymfm::ym3812(iface); + break; + case 8950: + fm_ymfm8950=new ymfm::y8950(iface); + break; + case 3: case 759: + fm_ymfm3=new ymfm::ymf262(iface); + break; + } + } + setFlags(flags); if (adpcmChan>=0) { @@ -2012,6 +2294,22 @@ void DivPlatformOPL::quit() { delete adpcmB; delete[] adpcmBMem; } + if (fm_ymfm1!=NULL) { + delete fm_ymfm1; + fm_ymfm1=NULL; + } + if (fm_ymfm2!=NULL) { + delete fm_ymfm2; + fm_ymfm2=NULL; + } + if (fm_ymfm8950!=NULL) { + delete fm_ymfm8950; + fm_ymfm8950=NULL; + } + if (fm_ymfm3!=NULL) { + delete fm_ymfm3; + fm_ymfm3=NULL; + } } DivPlatformOPL::~DivPlatformOPL() { diff --git a/src/engine/platform/opl.h b/src/engine/platform/opl.h index 54cf0c24d..23fd7b97e 100644 --- a/src/engine/platform/opl.h +++ b/src/engine/platform/opl.h @@ -23,7 +23,12 @@ #include "../dispatch.h" #include "../../fixedQueue.h" #include "../../../extern/opl/opl3.h" +extern "C" { +#include "../../../extern/YM3812-LLE/fmopl2.h" +#include "../../../extern/YMF262-LLE/fmopl3.h" +} #include "sound/ymfm/ymfm_adpcm.h" +#include "sound/ymfm/ymfm_opl.h" class DivOPLAInterface: public ymfm::ymfm_interface { public: @@ -68,7 +73,7 @@ class DivPlatformOPL: public DivDispatch { QueuedWrite(unsigned short a, unsigned char v): addr(a), val(v), addrOrVal(false) {} }; FixedQueue writes; - opl3_chip fm; + unsigned char* adpcmBMem; size_t adpcmBMemLen; DivOPLAInterface iface; @@ -93,11 +98,25 @@ class DivPlatformOPL: public DivDispatch { unsigned char lfoValue; - bool useYMFM, update4OpMask, pretendYMU, downsample, compatPan; + // 0: Nuked-OPL3 + // 1: ymfm + // 2: YM3812-LLE/YMF262-LLE + unsigned char emuCore; + + bool update4OpMask, pretendYMU, downsample, compatPan; short oldWrites[512]; short pendingWrites[512]; + // chips + opl3_chip fm; + ymfm::ym3526* fm_ymfm1; + ymfm::ym3812* fm_ymfm2; + ymfm::y8950* fm_ymfm8950; + ymfm::ymf262* fm_ymfm3; + fmopl2_t fm_lle2; + fmopl3_t fm_lle3; + int octave(int freq); int toFreq(int freq); double NOTE_ADPCMB(int note); @@ -106,8 +125,13 @@ class DivPlatformOPL: public DivDispatch { friend void putDispatchChip(void*,int); friend void putDispatchChan(void*,int,int); + void acquire_nukedLLE2(short** buf, size_t len); + void acquire_nukedLLE3(short** buf, size_t len); void acquire_nuked(short** buf, size_t len); - //void acquire_ymfm(short** buf, size_t len); + void acquire_ymfm3(short** buf, size_t len); + void acquire_ymfm8950(short** buf, size_t len); + void acquire_ymfm2(short** buf, size_t len); + void acquire_ymfm1(short** buf, size_t len); public: void acquire(short** buf, size_t len); @@ -124,7 +148,7 @@ class DivPlatformOPL: public DivDispatch { void tick(bool sysTick=true); void muteChannel(int ch, bool mute); int getOutputCount(); - void setYMFM(bool use); + void setCore(unsigned char which); void setOPLType(int type, bool drums); bool keyOffAffectsArp(int ch); bool keyOffAffectsPorta(int ch); diff --git a/src/engine/platform/sound/ymfm/ymfm_fm.h b/src/engine/platform/sound/ymfm/ymfm_fm.h index df3b486af..3ac36b50d 100644 --- a/src/engine/platform/sound/ymfm/ymfm_fm.h +++ b/src/engine/platform/sound/ymfm/ymfm_fm.h @@ -305,6 +305,8 @@ public: // simple getters for debugging fm_operator *debug_operator(uint32_t index) const { return m_op[index]; } int32_t debug_output(uint32_t index) const { return m_output[index]; } + int32_t debug_special1() const { return m_special1; } + int32_t debug_special2() const { return m_special2; } private: // helper to add values to the outputs based on channel enables @@ -343,6 +345,8 @@ private: RegisterType &m_regs; // direct reference to registers fm_engine_base &m_owner; // reference to the owning engine mutable int32_t m_output[4]; + mutable int32_t m_special1; + mutable int32_t m_special2; }; diff --git a/src/engine/platform/sound/ymfm/ymfm_fm.ipp b/src/engine/platform/sound/ymfm/ymfm_fm.ipp index 81b351fe9..7a37ec8a3 100644 --- a/src/engine/platform/sound/ymfm/ymfm_fm.ipp +++ b/src/engine/platform/sound/ymfm/ymfm_fm.ipp @@ -808,7 +808,9 @@ fm_channel::fm_channel(fm_engine_base &owner, uint32 m_op{ nullptr, nullptr, nullptr, nullptr }, m_regs(owner.regs()), m_owner(owner), - m_output{ 0, 0, 0, 0 } + m_output{ 0, 0, 0, 0 }, + m_special1(0), + m_special2(0) { } @@ -1139,13 +1141,13 @@ void fm_channel::output_rhythm_ch7(uint32_t phase_select, output_d // and a combination of noise and the operator 13/17 phase select // to compute the phase uint32_t phase = (phase_select << 9) | (0xd0 >> (2 * (noise_state ^ phase_select))); - int32_t result = m_op[0]->compute_volume(phase, am_offset) >> rshift; + int32_t result = m_special1 = m_op[0]->compute_volume(phase, am_offset) >> rshift; // Snare Drum: this uses the envelope from operator 16 (channel 7), // and a combination of noise and operator 13 phase to pick a phase uint32_t op13phase = m_op[0]->phase(); phase = (0x100 << bitfield(op13phase, 8)) ^ (noise_state << 8); - result += m_op[1]->compute_volume(phase, am_offset) >> rshift; + result += m_special2 = m_op[1]->compute_volume(phase, am_offset) >> rshift; result = clamp(result, -clipmax - 1, clipmax); // add to the output @@ -1166,12 +1168,12 @@ void fm_channel::output_rhythm_ch8(uint32_t phase_select, output_d uint32_t am_offset = m_regs.lfo_am_offset(m_choffs); // Tom Tom: this is just a single operator processed normally - int32_t result = m_op[0]->compute_volume(m_op[0]->phase(), am_offset) >> rshift; + int32_t result = m_special1 = m_op[0]->compute_volume(m_op[0]->phase(), am_offset) >> rshift; // Top Cymbal: this uses the envelope from operator 17 (channel 8), // and the operator 13/17 phase select to compute the phase uint32_t phase = 0x100 | (phase_select << 9); - result += m_op[1]->compute_volume(phase, am_offset) >> rshift; + result += m_special2 = m_op[1]->compute_volume(phase, am_offset) >> rshift; result = clamp(result, -clipmax - 1, clipmax); // add to the output diff --git a/src/engine/platform/sound/ymfm/ymfm_opl.h b/src/engine/platform/sound/ymfm/ymfm_opl.h index 843e5b274..8a2dd5147 100644 --- a/src/engine/platform/sound/ymfm/ymfm_opl.h +++ b/src/engine/platform/sound/ymfm/ymfm_opl.h @@ -528,6 +528,8 @@ public: // generate samples of sound void generate(output_data *output, uint32_t numsamples = 1); + + fm_engine* debug_fm_engine() { return &m_fm; } protected: // internal state uint8_t m_address; // address register @@ -575,6 +577,9 @@ public: // generate samples of sound void generate(output_data *output, uint32_t numsamples = 1); + fm_engine* debug_fm_engine() { return &m_fm; } + adpcm_b_engine* debug_adpcm_b_engine() { return &m_adpcm_b; } + protected: // internal state uint8_t m_address; // address register @@ -623,6 +628,8 @@ public: // generate samples of sound void generate(output_data *output, uint32_t numsamples = 1); + fm_engine* debug_fm_engine() { return &m_fm; } + protected: // internal state uint8_t m_address; // address register @@ -670,6 +677,8 @@ public: // generate samples of sound void generate(output_data *output, uint32_t numsamples = 1); + fm_engine* debug_fm_engine() { return &m_fm; } + protected: // internal state uint16_t m_address; // address register diff --git a/src/gui/gui.h b/src/gui/gui.h index 39a412e9b..10bcd2c66 100644 --- a/src/gui/gui.h +++ b/src/gui/gui.h @@ -1476,6 +1476,8 @@ class FurnaceGUI { int c64Core; int pokeyCore; int opnCore; + int opl2Core; + int opl3Core; int arcadeCoreRender; int ym2612CoreRender; int snCoreRender; @@ -1484,6 +1486,8 @@ class FurnaceGUI { int c64CoreRender; int pokeyCoreRender; int opnCoreRender; + int opl2CoreRender; + int opl3CoreRender; int pcSpeakerOutMethod; String yrw801Path; String tg100Path; @@ -1669,6 +1673,8 @@ class FurnaceGUI { c64Core(0), pokeyCore(1), opnCore(1), + opl2Core(0), + opl3Core(0), arcadeCoreRender(1), ym2612CoreRender(0), snCoreRender(0), @@ -1677,6 +1683,8 @@ class FurnaceGUI { c64CoreRender(1), pokeyCoreRender(1), opnCoreRender(1), + opl2CoreRender(0), + opl3CoreRender(0), pcSpeakerOutMethod(0), yrw801Path(""), tg100Path(""), diff --git a/src/gui/settings.cpp b/src/gui/settings.cpp index 86be7e547..c7ff6b29b 100644 --- a/src/gui/settings.cpp +++ b/src/gui/settings.cpp @@ -156,6 +156,18 @@ const char* opnCores[]={ "Nuked-OPN2 (FM) + ymfm (SSG/ADPCM)" }; +const char* opl2Cores[]={ + "Nuked-OPL3", + "ymfm", + "YM3812-LLE" +}; + +const char* opl3Cores[]={ + "Nuked-OPL3", + "ymfm", + "YMF262-LLE" +}; + const char* pcspkrOutMethods[]={ "evdev SND_TONE", "KIOCSOUND on /dev/tty1", @@ -1557,6 +1569,29 @@ void FurnaceGUI::drawSettings() { ImGui::TableNextColumn(); ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x); if (ImGui::Combo("##OPNCoreRender",&settings.opnCoreRender,opnCores,2)) settingsChanged=true; + + ImGui::TableNextRow(); + ImGui::TableNextColumn(); + ImGui::AlignTextToFramePadding(); + ImGui::Text("OPL/OPL2/Y8950"); + ImGui::TableNextColumn(); + ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x); + if (ImGui::Combo("##OPL2Core",&settings.opl2Core,opl2Cores,3)) settingsChanged=true; + ImGui::TableNextColumn(); + ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x); + if (ImGui::Combo("##OPL2CoreRender",&settings.opl2CoreRender,opl2Cores,3)) settingsChanged=true; + + ImGui::TableNextRow(); + ImGui::TableNextColumn(); + ImGui::AlignTextToFramePadding(); + ImGui::Text("OPL3"); + ImGui::TableNextColumn(); + ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x); + if (ImGui::Combo("##OPL3Core",&settings.opl3Core,opl3Cores,3)) settingsChanged=true; + ImGui::TableNextColumn(); + ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x); + if (ImGui::Combo("##OPL3CoreRender",&settings.opl3CoreRender,opl3Cores,3)) settingsChanged=true; + ImGui::EndTable(); } ImGui::Separator(); @@ -3594,6 +3629,8 @@ void FurnaceGUI::syncSettings() { settings.c64Core=e->getConfInt("c64Core",0); settings.pokeyCore=e->getConfInt("pokeyCore",1); settings.opnCore=e->getConfInt("opnCore",1); + settings.opl2Core=e->getConfInt("opl2Core",0); + settings.opl3Core=e->getConfInt("opl3Core",0); settings.arcadeCoreRender=e->getConfInt("arcadeCoreRender",1); settings.ym2612CoreRender=e->getConfInt("ym2612CoreRender",0); settings.snCoreRender=e->getConfInt("snCoreRender",0); @@ -3602,6 +3639,8 @@ void FurnaceGUI::syncSettings() { settings.c64CoreRender=e->getConfInt("c64CoreRender",1); settings.pokeyCoreRender=e->getConfInt("pokeyCoreRender",1); settings.opnCoreRender=e->getConfInt("opnCoreRender",1); + settings.opl2CoreRender=e->getConfInt("opl2CoreRender",0); + settings.opl3CoreRender=e->getConfInt("opl3CoreRender",0); settings.pcSpeakerOutMethod=e->getConfInt("pcSpeakerOutMethod",0); settings.yrw801Path=e->getConfString("yrw801Path",""); settings.tg100Path=e->getConfString("tg100Path",""); @@ -3778,6 +3817,8 @@ void FurnaceGUI::syncSettings() { clampSetting(settings.c64Core,0,2); clampSetting(settings.pokeyCore,0,1); clampSetting(settings.opnCore,0,1); + clampSetting(settings.opl2Core,0,2); + clampSetting(settings.opl3Core,0,2); clampSetting(settings.arcadeCoreRender,0,1); clampSetting(settings.ym2612CoreRender,0,1); clampSetting(settings.snCoreRender,0,1); @@ -3786,6 +3827,8 @@ void FurnaceGUI::syncSettings() { clampSetting(settings.c64CoreRender,0,2); clampSetting(settings.pokeyCoreRender,0,1); clampSetting(settings.opnCoreRender,0,1); + clampSetting(settings.opl2CoreRender,0,2); + clampSetting(settings.opl3CoreRender,0,2); clampSetting(settings.pcSpeakerOutMethod,0,4); clampSetting(settings.mainFont,0,6); clampSetting(settings.patFont,0,6); @@ -3991,6 +4034,8 @@ void FurnaceGUI::commitSettings() { settings.c64Core!=e->getConfInt("c64Core",0) || settings.pokeyCore!=e->getConfInt("pokeyCore",1) || settings.opnCore!=e->getConfInt("opnCore",1) || + settings.opl2Core!=e->getConfInt("opl2Core",0) || + settings.opl3Core!=e->getConfInt("opl3Core",0) || settings.arcadeCoreRender!=e->getConfInt("arcadeCoreRender",0) || settings.ym2612CoreRender!=e->getConfInt("ym2612CoreRender",0) || settings.snCoreRender!=e->getConfInt("snCoreRender",0) || @@ -3999,6 +4044,8 @@ void FurnaceGUI::commitSettings() { settings.c64CoreRender!=e->getConfInt("c64CoreRender",0) || settings.pokeyCoreRender!=e->getConfInt("pokeyCoreRender",1) || settings.opnCoreRender!=e->getConfInt("opnCoreRender",1) || + settings.opl2CoreRender!=e->getConfInt("opl2CoreRender",0) || + settings.opl3CoreRender!=e->getConfInt("opl3CoreRender",0) || settings.audioQuality!=e->getConfInt("audioQuality",0) || settings.audioHiPass!=e->getConfInt("audioHiPass",1) ); @@ -4026,6 +4073,8 @@ void FurnaceGUI::commitSettings() { e->setConf("c64Core",settings.c64Core); e->setConf("pokeyCore",settings.pokeyCore); e->setConf("opnCore",settings.opnCore); + e->setConf("opl2Core",settings.opl2Core); + e->setConf("opl3Core",settings.opl3Core); e->setConf("arcadeCoreRender",settings.arcadeCoreRender); e->setConf("ym2612CoreRender",settings.ym2612CoreRender); e->setConf("snCoreRender",settings.snCoreRender); @@ -4034,6 +4083,8 @@ void FurnaceGUI::commitSettings() { e->setConf("c64CoreRender",settings.c64CoreRender); e->setConf("pokeyCoreRender",settings.pokeyCoreRender); e->setConf("opnCoreRender",settings.opnCoreRender); + e->setConf("opl2CoreRender",settings.opl2CoreRender); + e->setConf("opl3CoreRender",settings.opl3CoreRender); e->setConf("pcSpeakerOutMethod",settings.pcSpeakerOutMethod); e->setConf("yrw801Path",settings.yrw801Path); e->setConf("tg100Path",settings.tg100Path);