diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 9c4b11c4..f50a7ca2 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -11,7 +11,7 @@ defaults: shell: bash env: - BUILD_TYPE: Release + BUILD_TYPE: Debug jobs: build: @@ -278,6 +278,9 @@ jobs: cp -v ../LICENSE LICENSE.txt cp -v ../README.md README.txt cp -vr ../{papers,demos,instruments} ../${binPath}/furnace.exe ./ + if [ '${{ matrix.config.compiler }}' == 'msvc' ]; then + cp -v ../${binPath}/furnace.pdb ./ + fi sha256sum ../${binPath}/furnace.exe > checksum.txt popd diff --git a/demos/snes/amalgam.fur b/demos/snes/amalgam.fur new file mode 100644 index 00000000..44e15b25 Binary files /dev/null and b/demos/snes/amalgam.fur differ diff --git a/extern/Nuked-OPLL/opll.c b/extern/Nuked-OPLL/opll.c index bd59709a..e160ce05 100644 --- a/extern/Nuked-OPLL/opll.c +++ b/extern/Nuked-OPLL/opll.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2019 Nuke.YKT + * Copyright (C) 2019-2023 Nuke.YKT * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -17,7 +17,7 @@ * siliconpr0n.org(digshadow, John McMaster): * VRC VII decap and die shot. * - * version: 1.0.1 + * version: 1.0.2 */ #include @@ -132,7 +132,7 @@ static const opll_patch_t patch_ds1001[opll_patch_max] = { { 0x00, 0x00, 0x00, 0x00,{ 0x00, 0x00 },{ 0x00, 0x00 },{ 0x00, 0x00 },{ 0x00, 0x00 },{ 0x01, 0x00 },{ 0x00, 0x00 },{ 0x0c, 0x00 },{ 0x08, 0x00 },{ 0x0a, 0x00 },{ 0x07, 0x00 } }, { 0x00, 0x00, 0x00, 0x00,{ 0x00, 0x00 },{ 0x00, 0x00 },{ 0x00, 0x00 },{ 0x00, 0x00 },{ 0x05, 0x00 },{ 0x00, 0x00 },{ 0x0f, 0x00 },{ 0x08, 0x00 },{ 0x05, 0x00 },{ 0x09, 0x00 } }, { 0x00, 0x00, 0x00, 0x00,{ 0x00, 0x00 },{ 0x00, 0x00 },{ 0x00, 0x00 },{ 0x00, 0x00 },{ 0x00, 0x01 },{ 0x00, 0x00 },{ 0x00, 0x0f },{ 0x00, 0x08 },{ 0x00, 0x06 },{ 0x00, 0x0d } }, - { 0x00, 0x00, 0x00, 0x00,{ 0x00, 0x00 },{ 0x00, 0x00 },{ 0x00, 0x00 },{ 0x00, 0x00 },{ 0x00, 0x01 },{ 0x00, 0x00 },{ 0x00, 0x0d },{ 0x00, 0x08 },{ 0x00, 0x06 },{ 0x00, 0x08 } }, + { 0x00, 0x00, 0x00, 0x00,{ 0x00, 0x00 },{ 0x00, 0x00 },{ 0x00, 0x00 },{ 0x00, 0x00 },{ 0x00, 0x01 },{ 0x00, 0x00 },{ 0x00, 0x0d },{ 0x00, 0x08 },{ 0x00, 0x04 },{ 0x00, 0x08 } }, { 0x00, 0x00, 0x00, 0x00,{ 0x00, 0x00 },{ 0x00, 0x00 },{ 0x00, 0x00 },{ 0x00, 0x00 },{ 0x00, 0x01 },{ 0x00, 0x00 },{ 0x00, 0x0a },{ 0x00, 0x0a },{ 0x00, 0x05 },{ 0x00, 0x05 } } }; @@ -1018,6 +1018,9 @@ static void OPLL_Operator(opll_t *chip) { } } + if (!(chip->rm_enable & 0x80)) + routput = 0; + chip->ch_out = ismod1 ? routput : (output>>3); if (!ismod1) { diff --git a/extern/nfd-modified/src/nfd_win.cpp b/extern/nfd-modified/src/nfd_win.cpp index b4fa5a5f..e4342243 100644 --- a/extern/nfd-modified/src/nfd_win.cpp +++ b/extern/nfd-modified/src/nfd_win.cpp @@ -30,6 +30,9 @@ // hack I know #include "../../../src/utfutils.h" +// hack 2... +#include "../../../src/ta-log.h" + class NFDWinEvents: public IFileDialogEvents { nfdselcallback_t selCallback; size_t refCount; @@ -38,21 +41,21 @@ class NFDWinEvents: public IFileDialogEvents { } public: IFACEMETHODIMP QueryInterface(REFIID riid, void** ppv) { - printf("QueryInterface called DAMN IT\n"); + logV("%p: QueryInterface called DAMN IT",(const void*)this); *ppv=NULL; return E_NOTIMPL; } IFACEMETHODIMP_(ULONG) AddRef() { - printf("AddRef() called\n"); + logV("%p: AddRef() called",(const void*)this); return InterlockedIncrement(&refCount); } IFACEMETHODIMP_(ULONG) Release() { - printf("Release() called\n"); + logV("%p: Release() called",(const void*)this); LONG ret=InterlockedDecrement(&refCount); if (ret==0) { - printf("Destroying the final object.\n"); + logV("%p: Destroying the final object.",(const void*)this); delete this; } return ret; @@ -67,30 +70,40 @@ class NFDWinEvents: public IFileDialogEvents { IFACEMETHODIMP OnSelectionChange(IFileDialog* dialog) { // Get the file name + logV("%p: OnSelectionChange() called",(const void*)this); ::IShellItem *shellItem(NULL); + logV("%p: GetCurrentSelection",(const void*)this); HRESULT result = dialog->GetCurrentSelection(&shellItem); if ( !SUCCEEDED(result) ) { - printf("failure!\n"); + logV("%p: failure!",(const void*)this); return S_OK; } wchar_t *filePath(NULL); result = shellItem->GetDisplayName(::SIGDN_FILESYSPATH, &filePath); if ( !SUCCEEDED(result) ) { - printf("GDN failure!\n"); + logV("%p: GDN failure!",(const void*)this); shellItem->Release(); return S_OK; } std::string utf8FilePath=utf16To8(filePath); - if (selCallback!=NULL) selCallback(utf8FilePath.c_str()); - printf("I got you for a value of %s\n",utf8FilePath.c_str()); + if (selCallback!=NULL) { + logV("%p: calling back.",(const void*)this); + selCallback(utf8FilePath.c_str()); + logV("%p: end of callback",(const void*)this); + } else { + logV("%p: no callback.",(const void*)this); + } + logV("%p: I got you for a value of %s",(const void*)this,utf8FilePath.c_str()); shellItem->Release(); + logV("%p: shellItem->Release()",(const void*)this); return S_OK; } NFDWinEvents(nfdselcallback_t callback): selCallback(callback), refCount(1) { + logV("%p: CONSTRUCT!",(const void*)this); } }; diff --git a/instruments/OPL/2-OP OPL3 Slap Bass.fui b/instruments/OPL/2-OP OPL3 Slap Bass.fui index fb8f1c14..04e8aadf 100644 Binary files a/instruments/OPL/2-OP OPL3 Slap Bass.fui and b/instruments/OPL/2-OP OPL3 Slap Bass.fui differ diff --git a/papers/doc/3-pattern/effects.md b/papers/doc/3-pattern/effects.md index 9965d22c..0aa1fc03 100644 --- a/papers/doc/3-pattern/effects.md +++ b/papers/doc/3-pattern/effects.md @@ -135,7 +135,7 @@ ex | FM | OPM | OPZ | OPLL | AY-3-8910 | AY8930 | Lynx W | | LFO Shape | LFO Shape | Patch | Waveform | Waveform | | Waveform | Waveform | Waveform | Waveform | Waveform | Waveform | | | | Waveform | | 1 | | AMD | AMD | | | Duty | | FilterMode | Envelope | EnvMode | WaveLen | Mod Depth | Cutoff | Filter K1 | ClockDiv | EchoFeedback | Special | GroupAtk | 2 | | PMD | PMD | | Envelope | Envelope | | Resonance | | Envelope | WaveUpdate | Mod Speed | Resonance | Filter K2 | | Echo Length | Gain | GroupDec | - 3 | | LFO Speed | LFO Speed | | AutoEnvNum | AutoEnvNum | | Special | | AutoEnvNum | WaveLoad W | | Control | Env Count | | | | Noise | + 3 | LFOSpd | LFO Speed | LFO Speed | | AutoEnvNum | AutoEnvNum | | Special | | AutoEnvNum | WaveLoad W | | Control | Env Count | | | | Noise | A | ALG | ALG | ALG | | AutoEnvDen | AutoEnvDen | | | | AutoEnvDen | WaveLoad P | | | Control | | | | | B | FB | FB | FB | | | Noise AND | | | | | WaveLoad L | | | | | | | | C | FMS | FMS | FMS | | | Noise OR | | | | | WaveLoad T | | | | | | | | @@ -144,4 +144,4 @@ ex | FM | OPM | OPZ | OPLL | AY-3-8910 | AY8930 | Lynx 5 | | | AMD2 | | | | | | | | | | | EnvRampR | | | | | 6 | | | PMD2 | | | | | | | | | | | EnvRampK1 | | | | | 7 | | | LFO2Speed | | | | | | | | | | | EnvRampK2 | | | | | - 8 | | | LFO2Shape | | | | | | | | | | | Env Mode | | | | | \ No newline at end of file + 8 | | | LFO2Shape | | | | | | | | | | | Env Mode | | | | | diff --git a/papers/format.md b/papers/format.md index 614fb3af..f0f94554 100644 --- a/papers/format.md +++ b/papers/format.md @@ -32,6 +32,8 @@ these fields are 0 in format versions prior to 100 (0.6pre1). the format versions are: +- 138: Furnace dev138 +- 137: Furnace dev137 - 136: Furnace dev136 - 135: Furnace dev135 - 134: Furnace dev134 @@ -398,6 +400,9 @@ size | description 4?? | patchbay | - see next section for more details. 1 | automatic patchbay (>=136) + --- | **a couple more compat flags** (>=138) + 1 | broken portamento during legato + 7 | reserved ``` # patchbay diff --git a/src/audio/rtmidi.cpp b/src/audio/rtmidi.cpp index 258f6929..568ea061 100644 --- a/src/audio/rtmidi.cpp +++ b/src/audio/rtmidi.cpp @@ -58,7 +58,7 @@ bool TAMidiInRtMidi::gather() { if (m.type!=TA_MIDI_SYSEX && msg.size()>1) { memcpy(m.data,msg.data()+1,MIN(msg.size()-1,7)); } else if (m.type==TA_MIDI_SYSEX) { - m.sysExData.reset(new unsigned char[msg.size()]); + m.sysExData=std::shared_ptr(new unsigned char[msg.size()],std::default_delete()); m.sysExLen=msg.size(); logD("got a SysEx of length %ld!",msg.size()); memcpy(m.sysExData.get(),msg.data(),msg.size()); diff --git a/src/engine/engine.h b/src/engine/engine.h index ffa97d75..75b0ad94 100644 --- a/src/engine/engine.h +++ b/src/engine/engine.h @@ -47,8 +47,8 @@ #define BUSY_BEGIN_SOFT softLocked=true; isBusy.lock(); #define BUSY_END isBusy.unlock(); softLocked=false; -#define DIV_VERSION "dev136" -#define DIV_ENGINE_VERSION 136 +#define DIV_VERSION "dev138" +#define DIV_ENGINE_VERSION 138 // for imports #define DIV_VERSION_MOD 0xff01 #define DIV_VERSION_FC 0xff02 diff --git a/src/engine/fileOps.cpp b/src/engine/fileOps.cpp index ffe04cfd..9ba9dd9b 100644 --- a/src/engine/fileOps.cpp +++ b/src/engine/fileOps.cpp @@ -1716,6 +1716,9 @@ bool DivEngine::loadFur(unsigned char* file, size_t len) { if (ds.version<130) { ds.oldArpStrategy=true; } + if (ds.version<138) { + ds.brokenPortaLegato=true; + } ds.isDMF=false; reader.readS(); // reserved @@ -2221,6 +2224,13 @@ bool DivEngine::loadFur(unsigned char* file, size_t len) { if (ds.version>=136) song.patchbayAuto=reader.readC(); + if (ds.version>=138) { + ds.brokenPortaLegato=reader.readC(); + for (int i=0; i<7; i++) { + reader.readC(); + } + } + // read system flags if (ds.version>=119) { logD("reading chip flags..."); @@ -2574,6 +2584,32 @@ bool DivEngine::loadFur(unsigned char* file, size_t len) { } } + // new YM2612/SN/X1-010 volumes + if (ds.version<137) { + for (int i=0; iwriteC(song.patchbayAuto); + // even more compat flags + w->writeC(song.brokenPortaLegato); + for (int i=0; i<7; i++) { + w->writeC(0); + } + blockEndSeek=w->tell(); w->seek(blockStartSeek,SEEK_SET); w->writeI(blockEndSeek-blockStartSeek-4); diff --git a/src/engine/platform/abstract.cpp b/src/engine/platform/abstract.cpp index 21415725..bd3efa72 100644 --- a/src/engine/platform/abstract.cpp +++ b/src/engine/platform/abstract.cpp @@ -18,6 +18,7 @@ */ #include "../dispatch.h" +#include "../../ta-log.h" void DivDispatch::acquire(short** buf, size_t len) { } @@ -121,7 +122,8 @@ void DivDispatch::notifyWaveChange(int ins) { } void DivDispatch::notifyInsDeletion(void* ins) { - + logE("notifyInsDeletion NOT implemented!"); + abort(); } void DivDispatch::notifyPlaybackStop() { diff --git a/src/engine/platform/arcade.cpp b/src/engine/platform/arcade.cpp index 1984e5a1..dece6b67 100644 --- a/src/engine/platform/arcade.cpp +++ b/src/engine/platform/arcade.cpp @@ -382,53 +382,57 @@ void DivPlatformArcade::muteChannel(int ch, bool mute) { } } +void DivPlatformArcade::commitState(int ch, DivInstrument* ins) { + if (chan[ch].insChanged) { + chan[ch].state=ins->fm; + chan[ch].opMask= + (chan[ch].state.op[0].enable?1:0)| + (chan[ch].state.op[2].enable?2:0)| + (chan[ch].state.op[1].enable?4:0)| + (chan[ch].state.op[3].enable?8:0); + } + + for (int i=0; i<4; i++) { + unsigned short baseAddr=chanOffs[ch]|opOffs[i]; + DivInstrumentFM::Operator op=chan[ch].state.op[i]; + if (KVS(ch,i)) { + if (!chan[ch].active || chan[ch].insChanged) { + rWrite(baseAddr+ADDR_TL,127-VOL_SCALE_LOG_BROKEN(127-op.tl,chan[ch].outVol&0x7f,127)); + } + } else { + if (chan[ch].insChanged) { + rWrite(baseAddr+ADDR_TL,op.tl); + } + } + if (chan[ch].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)|(op.dt2<<6)); + rWrite(baseAddr+ADDR_SL_RR,(op.rr&15)|(op.sl<<4)); + } + } + if (chan[ch].insChanged) { + if (isMuted[ch]) { + rWrite(chanOffs[ch]+ADDR_LR_FB_ALG,(chan[ch].state.alg&7)|(chan[ch].state.fb<<3)); + } else { + rWrite(chanOffs[ch]+ADDR_LR_FB_ALG,(chan[ch].state.alg&7)|(chan[ch].state.fb<<3)|((chan[ch].chVolL&1)<<6)|((chan[ch].chVolR&1)<<7)); + } + rWrite(chanOffs[ch]+ADDR_FMS_AMS,((chan[ch].state.fms&7)<<4)|(chan[ch].state.ams&3)); + } +} + int DivPlatformArcade::dispatch(DivCommand c) { switch (c.cmd) { case DIV_CMD_NOTE_ON: { DivInstrument* ins=parent->getIns(chan[c.chan].ins,DIV_INS_FM); - if (chan[c.chan].insChanged) { - chan[c.chan].state=ins->fm; - chan[c.chan].opMask= - (chan[c.chan].state.op[0].enable?1:0)| - (chan[c.chan].state.op[2].enable?2:0)| - (chan[c.chan].state.op[1].enable?4:0)| - (chan[c.chan].state.op[3].enable?8:0); - } - chan[c.chan].macroInit(ins); if (!chan[c.chan].std.vol.will) { chan[c.chan].outVol=chan[c.chan].vol; } - 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 (KVS(c.chan,i)) { - if (!chan[c.chan].active || chan[c.chan].insChanged) { - rWrite(baseAddr+ADDR_TL,127-VOL_SCALE_LOG_BROKEN(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)|(op.dt2<<6)); - rWrite(baseAddr+ADDR_SL_RR,(op.rr&15)|(op.sl<<4)); - } - } - if (chan[c.chan].insChanged) { - if (isMuted[c.chan]) { - rWrite(chanOffs[c.chan]+ADDR_LR_FB_ALG,(chan[c.chan].state.alg&7)|(chan[c.chan].state.fb<<3)); - } else { - rWrite(chanOffs[c.chan]+ADDR_LR_FB_ALG,(chan[c.chan].state.alg&7)|(chan[c.chan].state.fb<<3)|((chan[c.chan].chVolL&1)<<6)|((chan[c.chan].chVolR&1)<<7)); - } - rWrite(chanOffs[c.chan]+ADDR_FMS_AMS,((chan[c.chan].state.fms&7)<<4)|(chan[c.chan].state.ams&3)); - } + commitState(c.chan,ins); chan[c.chan].insChanged=false; if (c.value!=DIV_NOTE_NULL) { @@ -521,6 +525,11 @@ int DivPlatformArcade::dispatch(DivCommand c) { break; } case DIV_CMD_LEGATO: { + if (chan[c.chan].insChanged) { + DivInstrument* ins=parent->getIns(chan[c.chan].ins,DIV_INS_OPM); + commitState(c.chan,ins); + chan[c.chan].insChanged=false; + } chan[c.chan].baseFreq=NOTE_LINEAR(c.value); chan[c.chan].freqChanged=true; break; @@ -800,6 +809,12 @@ void DivPlatformArcade::notifyInsChange(int ins) { } } +void DivPlatformArcade::notifyInsDeletion(void* ins) { + for (int i=0; i<8; i++) { + chan[i].std.notifyInsDeletion((DivInstrument*)ins); + } +} + void* DivPlatformArcade::getChanState(int ch) { return &chan[ch]; } diff --git a/src/engine/platform/arcade.h b/src/engine/platform/arcade.h index 0bbd9b2c..f571ff34 100644 --- a/src/engine/platform/arcade.h +++ b/src/engine/platform/arcade.h @@ -57,6 +57,7 @@ class DivPlatformArcade: public DivPlatformOPM { int octave(int freq); int toFreq(int freq); + void commitState(int ch, DivInstrument* ins); void acquire_nuked(short** buf, size_t len); void acquire_ymfm(short** buf, size_t len); @@ -76,6 +77,7 @@ class DivPlatformArcade: public DivPlatformOPM { void muteChannel(int ch, bool mute); DivMacroInt* getChanMacroInt(int ch); void notifyInsChange(int ins); + void notifyInsDeletion(void* ins); void setFlags(const DivConfig& flags); int getOutputCount(); void setYMFM(bool use); diff --git a/src/engine/platform/dummy.cpp b/src/engine/platform/dummy.cpp index 28cb5d40..42ce1ad5 100644 --- a/src/engine/platform/dummy.cpp +++ b/src/engine/platform/dummy.cpp @@ -131,6 +131,10 @@ int DivPlatformDummy::dispatch(DivCommand c) { return 1; } +void DivPlatformDummy::notifyInsDeletion(void* ins) { + // nothing +} + void DivPlatformDummy::reset() { for (int i=0; i16 || writes.front().addrOrVal) { @@ -118,9 +119,11 @@ class DivPlatformFMBase: public DivDispatch { friend void putDispatchChan(void*,int,int); - DivPlatformFMBase():DivDispatch(), - lastBusy(0), - delay(0) {} + DivPlatformFMBase(): + DivDispatch(), + lastBusy(0), + delay(0), + flushFirst(false) {} }; #endif diff --git a/src/engine/platform/genesis.cpp b/src/engine/platform/genesis.cpp index 43767ca3..d50f382d 100644 --- a/src/engine/platform/genesis.cpp +++ b/src/engine/platform/genesis.cpp @@ -34,7 +34,7 @@ void DivYM2612Interface::ymfm_set_timer(uint32_t tnum, int32_t duration_in_clock } else if (tnum==0) { countA=duration_in_clocks; } - logV("ymfm_set_timer(%d,%d)",tnum,duration_in_clocks); + //logV("ymfm_set_timer(%d,%d)",tnum,duration_in_clocks); } void DivYM2612Interface::clock() { @@ -141,23 +141,26 @@ void DivPlatformGenesis::acquire_nuked(short** buf, size_t len) { os[0]=0; os[1]=0; for (int i=0; i<6; i++) { - if (!writes.empty() && --delay<0) { - delay=0; - QueuedWrite& w=writes.front(); - if (w.addrOrVal) { - OPN2_Write(&fm,0x1+((w.addr>>8)<<1),w.val); - //printf("write: %x = %.2x\n",w.addr,w.val); - lastBusy=0; - regPool[w.addr&0x1ff]=w.val; - writes.pop_front(); - } else { - lastBusy++; - if (fm.write_busy==0) { - //printf("busycounter: %d\n",lastBusy); - OPN2_Write(&fm,0x0+((w.addr>>8)<<1),w.addr); - w.addrOrVal=true; + if (!writes.empty()) { + if (--delay<0) { + delay=0; + QueuedWrite& w=writes.front(); + if (w.addrOrVal) { + //logV("%.3x = %.2x",w.addr,w.val); + OPN2_Write(&fm,0x1+((w.addr>>8)<<1),w.val); + lastBusy=0; + regPool[w.addr&0x1ff]=w.val; + writes.pop_front(); + } else { + lastBusy++; + if (fm.write_busy==0) { + OPN2_Write(&fm,0x0+((w.addr>>8)<<1),w.addr); + w.addrOrVal=true; + } } } + } else { + flushFirst=false; } OPN2_Clock(&fm,o); os[0]+=o[0]; os[1]+=o[1]; @@ -207,6 +210,8 @@ void DivPlatformGenesis::acquire_ymfm(short** buf, size_t len) { regPool[w.addr&0x1ff]=w.val; writes.pop_front(); lastBusy=1; + } else { + flushFirst=false; } if (ladder) { @@ -389,6 +394,10 @@ void DivPlatformGenesis::tick(bool sysTick) { chan[i].state.ams=chan[i].std.ams.val; rWrite(chanOffs[i]+ADDR_LRAF,(IS_REALLY_MUTED(i)?0:(chan[i].pan<<6))|(chan[i].state.fms&7)|((chan[i].state.ams&3)<<4)); } + if (chan[i].std.ex3.had) { + lfoValue=(chan[i].std.ex3.val>7)?0:(8|(chan[i].std.ex3.val&7)); + rWrite(0x22,lfoValue); + } if (chan[i].std.ex4.had && chan[i].active) { chan[i].opMask=chan[i].std.ex4.val&15; chan[i].opMaskChanged=true; @@ -454,6 +463,10 @@ void DivPlatformGenesis::tick(bool sysTick) { for (int i=0; i<512; i++) { if (pendingWrites[i]!=oldWrites[i]) { + if (i==0x2b && pendingWrites[i]!=0 && !parent->song.brokenDACMode) { + if (chan[5].keyOn) chan[5].keyOn=false; + chan[5].keyOff=true; + } immWrite(i,pendingWrites[i]&0xff); oldWrites[i]=pendingWrites[i]; } @@ -556,6 +569,47 @@ void DivPlatformGenesis::muteChannel(int ch, bool mute) { rWrite(chanOffs[ch]+ADDR_LRAF,(IS_REALLY_MUTED(ch)?0:(chan[ch].pan<<6))|(chan[ch].state.fms&7)|((chan[ch].state.ams&3)<<4)); } +void DivPlatformGenesis::commitState(int ch, DivInstrument* ins) { + if (chan[ch].insChanged) { + chan[ch].state=ins->fm; + chan[ch].opMask= + (chan[ch].state.op[0].enable?1:0)| + (chan[ch].state.op[2].enable?2:0)| + (chan[ch].state.op[1].enable?4:0)| + (chan[ch].state.op[3].enable?8:0); + } + + for (int i=0; i<4; i++) { + unsigned short baseAddr=chanOffs[ch]|opOffs[i]; + DivInstrumentFM::Operator& op=chan[ch].state.op[i]; + if (isMuted[ch]) { + rWrite(baseAddr+ADDR_TL,127); + } else { + if (KVS(ch,i)) { + if (!chan[ch].active || chan[ch].insChanged) { + rWrite(baseAddr+ADDR_TL,127-VOL_SCALE_LOG_BROKEN(127-op.tl,chan[ch].outVol&0x7f,127)); + } + } else { + if (chan[ch].insChanged) { + rWrite(baseAddr+ADDR_TL,op.tl); + } + } + } + if (chan[ch].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[ch].insChanged) { + rWrite(chanOffs[ch]+ADDR_FB_ALG,(chan[ch].state.alg&7)|(chan[ch].state.fb<<3)); + rWrite(chanOffs[ch]+ADDR_LRAF,(IS_REALLY_MUTED(ch)?0:(chan[ch].pan<<6))|(chan[ch].state.fms&7)|((chan[ch].state.ams&3)<<4)); + } +} + int DivPlatformGenesis::dispatch(DivCommand c) { switch (c.cmd) { case DIV_CMD_NOTE_ON: { @@ -641,49 +695,12 @@ int DivPlatformGenesis::dispatch(DivCommand c) { } if (c.chan>=6) break; - if (chan[c.chan].insChanged) { - chan[c.chan].state=ins->fm; - chan[c.chan].opMask= - (chan[c.chan].state.op[0].enable?1:0)| - (chan[c.chan].state.op[2].enable?2:0)| - (chan[c.chan].state.op[1].enable?4:0)| - (chan[c.chan].state.op[3].enable?8:0); - } - chan[c.chan].macroInit(ins); if (!chan[c.chan].std.vol.will) { chan[c.chan].outVol=chan[c.chan].vol; } - - for (int i=0; i<4; i++) { - unsigned short baseAddr=chanOffs[c.chan]|opOffs[i]; - DivInstrumentFM::Operator& op=chan[c.chan].state.op[i]; - if (isMuted[c.chan]) { - rWrite(baseAddr+ADDR_TL,127); - } else { - if (KVS(c.chan,i)) { - if (!chan[c.chan].active || chan[c.chan].insChanged) { - rWrite(baseAddr+ADDR_TL,127-VOL_SCALE_LOG_BROKEN(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[c.chan]+ADDR_FB_ALG,(chan[c.chan].state.alg&7)|(chan[c.chan].state.fb<<3)); - rWrite(chanOffs[c.chan]+ADDR_LRAF,(IS_REALLY_MUTED(c.chan)?0:(chan[c.chan].pan<<6))|(chan[c.chan].state.fms&7)|((chan[c.chan].state.ams&3)<<4)); - } + + commitState(c.chan,ins); chan[c.chan].insChanged=false; if (c.value!=DIV_NOTE_NULL) { @@ -866,6 +883,11 @@ int DivPlatformGenesis::dispatch(DivCommand c) { } else if (c.chan>=5 && chan[c.chan].furnaceDac && chan[c.chan].dacMode) { chan[c.chan].baseFreq=parent->calcBaseFreq(1,1,c.value,false); } else { + if (chan[c.chan].insChanged) { + DivInstrument* ins=parent->getIns(chan[c.chan].ins,DIV_INS_FM); + commitState(c.chan,ins); + chan[c.chan].insChanged=false; + } chan[c.chan].baseFreq=NOTE_FNUM_BLOCK(c.value,11); } chan[c.chan].note=c.value; @@ -1120,16 +1142,20 @@ void DivPlatformGenesis::forceIns() { rWrite(chanOffs[i]+ADDR_FB_ALG,(chan[i].state.alg&7)|(chan[i].state.fb<<3)); rWrite(chanOffs[i]+ADDR_LRAF,(IS_REALLY_MUTED(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; + if (i<5 || !chan[i].dacMode) { + chan[i].keyOn=true; + chan[i].freqChanged=true; + } } } + immWrite(0x2b,0x00); + //rWrite(0x2a,0x00); if (chan[5].dacMode) { chan[5].dacSample=-1; chan[6].dacSample=-1; - rWrite(0x2b,0x80); } immWrite(0x22,lfoValue); + flushFirst=true; } void DivPlatformGenesis::toggleRegisterDump(bool enable) { @@ -1156,6 +1182,10 @@ int DivPlatformGenesis::getRegisterPoolSize() { return 512; } +float DivPlatformGenesis::getPostAmp() { + return 2.0f; +} + void DivPlatformGenesis::reset() { while (!writes.empty()) writes.pop_front(); memset(regPool,0,512); @@ -1183,6 +1213,7 @@ void DivPlatformGenesis::reset() { lfoValue=8; softPCMTimer=0; extMode=false; + flushFirst=false; if (softPCM) { chan[5].dacMode=true; @@ -1219,6 +1250,9 @@ void DivPlatformGenesis::notifyInsChange(int ins) { } void DivPlatformGenesis::notifyInsDeletion(void* ins) { + for (int i=0; i<10; i++) { + chan[i].std.notifyInsDeletion((DivInstrument*)ins); + } } void DivPlatformGenesis::poke(unsigned int addr, unsigned short val) { @@ -1285,6 +1319,7 @@ int DivPlatformGenesis::init(DivEngine* p, int channels, int sugRate, const DivC dumpWrites=false; ladder=false; skipRegisterWrites=false; + flushFirst=false; for (int i=0; i<10; i++) { isMuted[i]=false; oscBuf[i]=new DivDispatchOscBuffer; diff --git a/src/engine/platform/genesis.h b/src/engine/platform/genesis.h index f57c33a3..c420ac5d 100644 --- a/src/engine/platform/genesis.h +++ b/src/engine/platform/genesis.h @@ -92,6 +92,7 @@ class DivPlatformGenesis: public DivPlatformOPN { friend void putDispatchChan(void*,int,int); inline void processDAC(int iRate); + inline void commitState(int ch, DivInstrument* ins); void acquire_nuked(short** buf, size_t len); void acquire_ymfm(short** buf, size_t len); @@ -114,10 +115,11 @@ class DivPlatformGenesis: public DivPlatformOPN { void setYMFM(bool use); bool keyOffAffectsArp(int ch); bool keyOffAffectsPorta(int ch); + float getPostAmp(); void toggleRegisterDump(bool enable); void setFlags(const DivConfig& flags); void notifyInsChange(int ins); - void notifyInsDeletion(void* ins); + virtual void notifyInsDeletion(void* ins); void setSoftPCM(bool value); int getPortaFloor(int ch); void poke(unsigned int addr, unsigned short val); diff --git a/src/engine/platform/genesisext.cpp b/src/engine/platform/genesisext.cpp index 8e1e7d12..4aa410af 100644 --- a/src/engine/platform/genesisext.cpp +++ b/src/engine/platform/genesisext.cpp @@ -746,6 +746,13 @@ void DivPlatformGenesisExt::notifyInsChange(int ins) { } } +void DivPlatformGenesisExt::notifyInsDeletion(void* ins) { + DivPlatformGenesis::notifyInsDeletion(ins); + for (int i=0; i<4; i++) { + opChan[i].std.notifyInsDeletion((DivInstrument*)ins); + } +} + int DivPlatformGenesisExt::getPortaFloor(int ch) { return (ch>8)?12:0; } diff --git a/src/engine/platform/genesisext.h b/src/engine/platform/genesisext.h index 304f609f..67955adc 100644 --- a/src/engine/platform/genesisext.h +++ b/src/engine/platform/genesisext.h @@ -41,6 +41,7 @@ class DivPlatformGenesisExt: public DivPlatformGenesis { bool keyOffAffectsArp(int ch); bool keyOffAffectsPorta(int ch); void notifyInsChange(int ins); + void notifyInsDeletion(void* ins); int getPortaFloor(int ch); void setCSMChannel(unsigned char ch); int init(DivEngine* parent, int channels, int sugRate, const DivConfig& flags); diff --git a/src/engine/platform/msm6258.cpp b/src/engine/platform/msm6258.cpp index af1472af..7814d23e 100644 --- a/src/engine/platform/msm6258.cpp +++ b/src/engine/platform/msm6258.cpp @@ -353,6 +353,9 @@ void DivPlatformMSM6258::notifyInsChange(int ins) { } void DivPlatformMSM6258::notifyInsDeletion(void* ins) { + for (int i=0; i<1; i++) { + chan[i].std.notifyInsDeletion((DivInstrument*)ins); + } } void DivPlatformMSM6258::setFlags(const DivConfig& flags) { diff --git a/src/engine/platform/msm6295.cpp b/src/engine/platform/msm6295.cpp index bc41091c..60842900 100644 --- a/src/engine/platform/msm6295.cpp +++ b/src/engine/platform/msm6295.cpp @@ -326,6 +326,9 @@ void DivPlatformMSM6295::notifyInsChange(int ins) { } void DivPlatformMSM6295::notifyInsDeletion(void* ins) { + for (int i=0; i<4; i++) { + chan[i].std.notifyInsDeletion((DivInstrument*)ins); + } } const void* DivPlatformMSM6295::getSampleMem(int index) { diff --git a/src/engine/platform/opl.cpp b/src/engine/platform/opl.cpp index 2c6c1590..014f2a40 100644 --- a/src/engine/platform/opl.cpp +++ b/src/engine/platform/opl.cpp @@ -689,6 +689,112 @@ void DivPlatformOPL::muteChannel(int ch, bool mute) { } } +void DivPlatformOPL::commitState(int ch, DivInstrument* ins) { + if (chan[ch].insChanged) { + if (ch>melodicChans && ins->type==DIV_INS_OPL_DRUMS) { + for (int i=0; i<4; i++) { + chan[melodicChans+i+1].state.alg=ins->fm.alg; + chan[melodicChans+i+1].state.fb=ins->fm.fb; + chan[melodicChans+i+1].state.opllPreset=ins->fm.opllPreset; + chan[melodicChans+i+1].state.fixedDrums=ins->fm.fixedDrums; + chan[melodicChans+i+1].state.kickFreq=ins->fm.kickFreq; + chan[melodicChans+i+1].state.snareHatFreq=ins->fm.snareHatFreq; + chan[melodicChans+i+1].state.tomTopFreq=ins->fm.tomTopFreq; + chan[melodicChans+i+1].state.op[0]=ins->fm.op[i]; + } + } else { + chan[ch].state=ins->fm; + } + } + + if (chan[ch].insChanged) { + if (ch>melodicChans && ins->type==DIV_INS_OPL_DRUMS) { + for (int i=0; i<4; i++) { + int ch=melodicChans+1+i; + unsigned char slot=slots[0][ch]; + if (slot==255) continue; + unsigned short baseAddr=slotMap[slot]; + DivInstrumentFM::Operator& op=chan[ch].state.op[0]; + chan[ch].fourOp=false; + + if (isMuted[ch]) { + rWrite(baseAddr+ADDR_KSL_TL,63|(op.ksl<<6)); + } else { + rWrite(baseAddr+ADDR_KSL_TL,(63-VOL_SCALE_LOG_BROKEN(63-op.tl,chan[ch].outVol&0x3f,63))|(op.ksl<<6)); + } + + rWrite(baseAddr+ADDR_AM_VIB_SUS_KSR_MULT,(op.am<<7)|(op.vib<<6)|(op.sus<<5)|(op.ksr<<4)|op.mult); + rWrite(baseAddr+ADDR_AR_DR,(op.ar<<4)|op.dr); + rWrite(baseAddr+ADDR_SL_RR,(op.sl<<4)|op.rr); + if (oplType>1) { + rWrite(baseAddr+ADDR_WS,op.ws&((oplType==3)?7:3)); + } + + if (isMuted[ch]) { + oldWrites[chanMap[ch]+ADDR_LR_FB_ALG]=-1; + rWrite(chanMap[ch]+ADDR_LR_FB_ALG,(chan[ch].state.alg&1)|(chan[ch].state.fb<<1)); + } else { + oldWrites[chanMap[ch]+ADDR_LR_FB_ALG]=-1; + rWrite(chanMap[ch]+ADDR_LR_FB_ALG,(chan[ch].state.alg&1)|(chan[ch].state.fb<<1)|((chan[ch].pan&15)<<4)); + } + } + } else { + int ops=(slots[3][ch]!=255 && chan[ch].state.ops==4 && oplType==3)?4:2; + chan[ch].fourOp=(ops==4); + if (chan[ch].fourOp) { + /* + if (chan[ch+1].active) { + chan[ch+1].keyOff=true; + chan[ch+1].keyOn=false; + chan[ch+1].active=false; + }*/ + chan[ch+1].insChanged=true; + chan[ch+1].macroInit(NULL); + } + update4OpMask=true; + for (int i=0; imelodicChans) { + rWrite(baseAddr+ADDR_KSL_TL,(63-VOL_SCALE_LOG_BROKEN(63-op.tl,chan[ch].outVol&0x3f,63))|(op.ksl<<6)); + } else { + rWrite(baseAddr+ADDR_KSL_TL,op.tl|(op.ksl<<6)); + } + } + + rWrite(baseAddr+ADDR_AM_VIB_SUS_KSR_MULT,(op.am<<7)|(op.vib<<6)|(op.sus<<5)|(op.ksr<<4)|op.mult); + rWrite(baseAddr+ADDR_AR_DR,(op.ar<<4)|op.dr); + rWrite(baseAddr+ADDR_SL_RR,(op.sl<<4)|op.rr); + if (oplType>1) { + rWrite(baseAddr+ADDR_WS,op.ws&((oplType==3)?7:3)); + } + } + + if (isMuted[ch]) { + oldWrites[chanMap[ch]+ADDR_LR_FB_ALG]=-1; + rWrite(chanMap[ch]+ADDR_LR_FB_ALG,(chan[ch].state.alg&1)|(chan[ch].state.fb<<1)); + if (ops==4) { + oldWrites[chanMap[ch+1]+ADDR_LR_FB_ALG]=-1; + rWrite(chanMap[ch+1]+ADDR_LR_FB_ALG,((chan[ch].state.alg>>1)&1)|(chan[ch].state.fb<<1)); + } + } else { + oldWrites[chanMap[ch]+ADDR_LR_FB_ALG]=-1; + rWrite(chanMap[ch]+ADDR_LR_FB_ALG,(chan[ch].state.alg&1)|(chan[ch].state.fb<<1)|((chan[ch].pan&15)<<4)); + if (ops==4) { + oldWrites[chanMap[ch+1]+ADDR_LR_FB_ALG]=-1; + rWrite(chanMap[ch+1]+ADDR_LR_FB_ALG,((chan[ch].state.alg>>1)&1)|(chan[ch].state.fb<<1)|((chan[ch].pan&15)<<4)); + } + } + } + } +} + int DivPlatformOPL::dispatch(DivCommand c) { if (c.chan>=totalChans && c.chan!=adpcmChan) return 0; // ineffective in 4-op mode @@ -771,114 +877,12 @@ int DivPlatformOPL::dispatch(DivCommand c) { } DivInstrument* ins=parent->getIns(chan[c.chan].ins,c.chan>melodicChans?DIV_INS_OPL_DRUMS:DIV_INS_OPL); - if (chan[c.chan].insChanged) { - if (c.chan>melodicChans && ins->type==DIV_INS_OPL_DRUMS) { - for (int i=0; i<4; i++) { - chan[melodicChans+i+1].state.alg=ins->fm.alg; - chan[melodicChans+i+1].state.fb=ins->fm.fb; - chan[melodicChans+i+1].state.opllPreset=ins->fm.opllPreset; - chan[melodicChans+i+1].state.fixedDrums=ins->fm.fixedDrums; - chan[melodicChans+i+1].state.kickFreq=ins->fm.kickFreq; - chan[melodicChans+i+1].state.snareHatFreq=ins->fm.snareHatFreq; - chan[melodicChans+i+1].state.tomTopFreq=ins->fm.tomTopFreq; - chan[melodicChans+i+1].state.op[0]=ins->fm.op[i]; - } - } else { - chan[c.chan].state=ins->fm; - } - } - chan[c.chan].macroInit(ins); if (!chan[c.chan].std.vol.will) { chan[c.chan].outVol=chan[c.chan].vol; } - if (chan[c.chan].insChanged) { - if (c.chan>melodicChans && ins->type==DIV_INS_OPL_DRUMS) { - for (int i=0; i<4; i++) { - int ch=melodicChans+1+i; - unsigned char slot=slots[0][ch]; - if (slot==255) continue; - unsigned short baseAddr=slotMap[slot]; - DivInstrumentFM::Operator& op=chan[ch].state.op[0]; - chan[ch].fourOp=false; - if (isMuted[ch]) { - rWrite(baseAddr+ADDR_KSL_TL,63|(op.ksl<<6)); - } else { - rWrite(baseAddr+ADDR_KSL_TL,(63-VOL_SCALE_LOG_BROKEN(63-op.tl,chan[ch].outVol&0x3f,63))|(op.ksl<<6)); - } - - rWrite(baseAddr+ADDR_AM_VIB_SUS_KSR_MULT,(op.am<<7)|(op.vib<<6)|(op.sus<<5)|(op.ksr<<4)|op.mult); - rWrite(baseAddr+ADDR_AR_DR,(op.ar<<4)|op.dr); - rWrite(baseAddr+ADDR_SL_RR,(op.sl<<4)|op.rr); - if (oplType>1) { - rWrite(baseAddr+ADDR_WS,op.ws&((oplType==3)?7:3)); - } - - if (isMuted[ch]) { - oldWrites[chanMap[ch]+ADDR_LR_FB_ALG]=-1; - rWrite(chanMap[ch]+ADDR_LR_FB_ALG,(chan[ch].state.alg&1)|(chan[ch].state.fb<<1)); - } else { - oldWrites[chanMap[ch]+ADDR_LR_FB_ALG]=-1; - rWrite(chanMap[ch]+ADDR_LR_FB_ALG,(chan[ch].state.alg&1)|(chan[ch].state.fb<<1)|((chan[ch].pan&15)<<4)); - } - } - } else { - int ops=(slots[3][c.chan]!=255 && chan[c.chan].state.ops==4 && oplType==3)?4:2; - chan[c.chan].fourOp=(ops==4); - if (chan[c.chan].fourOp) { - /* - if (chan[c.chan+1].active) { - chan[c.chan+1].keyOff=true; - chan[c.chan+1].keyOn=false; - chan[c.chan+1].active=false; - }*/ - chan[c.chan+1].insChanged=true; - chan[c.chan+1].macroInit(NULL); - } - update4OpMask=true; - for (int i=0; imelodicChans) { - rWrite(baseAddr+ADDR_KSL_TL,(63-VOL_SCALE_LOG_BROKEN(63-op.tl,chan[c.chan].outVol&0x3f,63))|(op.ksl<<6)); - } else { - rWrite(baseAddr+ADDR_KSL_TL,op.tl|(op.ksl<<6)); - } - } - - rWrite(baseAddr+ADDR_AM_VIB_SUS_KSR_MULT,(op.am<<7)|(op.vib<<6)|(op.sus<<5)|(op.ksr<<4)|op.mult); - rWrite(baseAddr+ADDR_AR_DR,(op.ar<<4)|op.dr); - rWrite(baseAddr+ADDR_SL_RR,(op.sl<<4)|op.rr); - if (oplType>1) { - rWrite(baseAddr+ADDR_WS,op.ws&((oplType==3)?7:3)); - } - } - - if (isMuted[c.chan]) { - oldWrites[chanMap[c.chan]+ADDR_LR_FB_ALG]=-1; - rWrite(chanMap[c.chan]+ADDR_LR_FB_ALG,(chan[c.chan].state.alg&1)|(chan[c.chan].state.fb<<1)); - if (ops==4) { - oldWrites[chanMap[c.chan+1]+ADDR_LR_FB_ALG]=-1; - rWrite(chanMap[c.chan+1]+ADDR_LR_FB_ALG,((chan[c.chan].state.alg>>1)&1)|(chan[c.chan].state.fb<<1)); - } - } else { - oldWrites[chanMap[c.chan]+ADDR_LR_FB_ALG]=-1; - rWrite(chanMap[c.chan]+ADDR_LR_FB_ALG,(chan[c.chan].state.alg&1)|(chan[c.chan].state.fb<<1)|((chan[c.chan].pan&15)<<4)); - if (ops==4) { - oldWrites[chanMap[c.chan+1]+ADDR_LR_FB_ALG]=-1; - rWrite(chanMap[c.chan+1]+ADDR_LR_FB_ALG,((chan[c.chan].state.alg>>1)&1)|(chan[c.chan].state.fb<<1)|((chan[c.chan].pan&15)<<4)); - } - } - } - } - + commitState(c.chan,ins); chan[c.chan].insChanged=false; if (c.value!=DIV_NOTE_NULL) { @@ -1075,6 +1079,11 @@ int DivPlatformOPL::dispatch(DivCommand c) { iface.sampleBank=sampleBank; break; case DIV_CMD_LEGATO: { + if (chan[c.chan].insChanged) { + DivInstrument* ins=parent->getIns(chan[c.chan].ins,DIV_INS_FM); + commitState(c.chan,ins); + chan[c.chan].insChanged=false; + } chan[c.chan].baseFreq=(c.chan==adpcmChan)?(NOTE_ADPCMB(c.value)):(NOTE_FREQUENCY(c.value)); chan[c.chan].note=c.value; chan[c.chan].freqChanged=true; @@ -1640,6 +1649,9 @@ void DivPlatformOPL::notifyInsChange(int ins) { } void DivPlatformOPL::notifyInsDeletion(void* ins) { + for (int i=0; i #include @@ -331,6 +332,55 @@ void DivPlatformOPLL::muteChannel(int ch, bool mute) { isMuted[ch]=mute; } +void DivPlatformOPLL::commitState(int ch, DivInstrument* ins) { + if (chan[ch].insChanged) { + chan[ch].state=ins->fm; + } + + if (chan[ch].insChanged) { + // update custom preset + if (chan[ch].state.opllPreset==0) { + DivInstrumentFM::Operator& mod=chan[ch].state.op[0]; + DivInstrumentFM::Operator& car=chan[ch].state.op[1]; + rWrite(0x00,(mod.am<<7)|(mod.vib<<6)|((mod.ssgEnv&8)<<2)|(mod.ksr<<4)|(mod.mult)); + rWrite(0x01,(car.am<<7)|(car.vib<<6)|((car.ssgEnv&8)<<2)|(car.ksr<<4)|(car.mult)); + rWrite(0x02,(mod.ksl<<6)|(mod.tl&63)); + rWrite(0x03,(car.ksl<<6)|((chan[ch].state.fms&1)<<4)|((chan[ch].state.ams&1)<<3)|chan[ch].state.fb); + rWrite(0x04,(mod.ar<<4)|(mod.dr)); + rWrite(0x05,(car.ar<<4)|(car.dr)); + rWrite(0x06,(mod.sl<<4)|(mod.rr)); + rWrite(0x07,(car.sl<<4)|(car.rr)); + lastCustomMemory=ch; + } + if (chan[ch].state.opllPreset==16) { // compatible drums mode + if (ch>=6) { + drums=true; + immWrite(0x16,0x20); + immWrite(0x26,0x05); + immWrite(0x16,0x20); + immWrite(0x26,0x05); + immWrite(0x17,0x50); + immWrite(0x27,0x05); + immWrite(0x17,0x50); + immWrite(0x27,0x05); + immWrite(0x18,0xC0); + immWrite(0x28,0x01); + } + } else { + if (ch>=6) { + if (drums) { + drums=false; + immWrite(0x0e,0); + drumState=0; + } + } + if (ch<9) { + rWrite(0x30+ch,((15-VOL_SCALE_LOG_BROKEN(chan[ch].outVol,15-chan[ch].state.op[1].tl,15))&15)|(chan[ch].state.opllPreset<<4)); + } + } + } +} + int DivPlatformOPLL::dispatch(DivCommand c) { switch (c.cmd) { case DIV_CMD_NOTE_ON: { @@ -375,49 +425,7 @@ int DivPlatformOPLL::dispatch(DivCommand c) { break; } - if (chan[c.chan].insChanged) { - // update custom preset - if (chan[c.chan].state.opllPreset==0) { - DivInstrumentFM::Operator& mod=chan[c.chan].state.op[0]; - DivInstrumentFM::Operator& car=chan[c.chan].state.op[1]; - rWrite(0x00,(mod.am<<7)|(mod.vib<<6)|((mod.ssgEnv&8)<<2)|(mod.ksr<<4)|(mod.mult)); - rWrite(0x01,(car.am<<7)|(car.vib<<6)|((car.ssgEnv&8)<<2)|(car.ksr<<4)|(car.mult)); - rWrite(0x02,(mod.ksl<<6)|(mod.tl&63)); - rWrite(0x03,(car.ksl<<6)|((chan[c.chan].state.fms&1)<<4)|((chan[c.chan].state.ams&1)<<3)|chan[c.chan].state.fb); - rWrite(0x04,(mod.ar<<4)|(mod.dr)); - rWrite(0x05,(car.ar<<4)|(car.dr)); - rWrite(0x06,(mod.sl<<4)|(mod.rr)); - rWrite(0x07,(car.sl<<4)|(car.rr)); - lastCustomMemory=c.chan; - } - if (chan[c.chan].state.opllPreset==16) { // compatible drums mode - if (c.chan>=6) { - drums=true; - immWrite(0x16,0x20); - immWrite(0x26,0x05); - immWrite(0x16,0x20); - immWrite(0x26,0x05); - immWrite(0x17,0x50); - immWrite(0x27,0x05); - immWrite(0x17,0x50); - immWrite(0x27,0x05); - immWrite(0x18,0xC0); - immWrite(0x28,0x01); - } - } else { - if (c.chan>=6) { - if (drums) { - drums=false; - immWrite(0x0e,0); - drumState=0; - } - } - if (c.chan<9) { - rWrite(0x30+c.chan,((15-VOL_SCALE_LOG_BROKEN(chan[c.chan].outVol,15-chan[c.chan].state.op[1].tl,15))&15)|(chan[c.chan].state.opllPreset<<4)); - } - } - } - + commitState(c.chan,ins); chan[c.chan].insChanged=false; if (c.value!=DIV_NOTE_NULL) { @@ -541,6 +549,13 @@ int DivPlatformOPLL::dispatch(DivCommand c) { } case DIV_CMD_LEGATO: { if (c.chan>=9 && !properDrums) return 0; + if (c.chan<6 || (!drums && !properDrums)) { + if (chan[c.chan].insChanged) { + DivInstrument* ins=parent->getIns(chan[c.chan].ins,DIV_INS_OPLL); + commitState(c.chan,ins); + chan[c.chan].insChanged=false; + } + } chan[c.chan].baseFreq=NOTE_FREQUENCY(c.value); chan[c.chan].note=c.value; chan[c.chan].freqChanged=true; @@ -940,6 +955,9 @@ void DivPlatformOPLL::notifyInsChange(int ins) { } void DivPlatformOPLL::notifyInsDeletion(void* ins) { + for (int i=0; i<11; i++) { + chan[i].std.notifyInsDeletion((DivInstrument*)ins); + } } void DivPlatformOPLL::poke(unsigned int addr, unsigned short val) { @@ -958,6 +976,10 @@ void DivPlatformOPLL::setYMFM(bool use) { useYMFM=use; } +float DivPlatformOPLL::getPostAmp() { + return 1.5f; +} + void DivPlatformOPLL::setFlags(const DivConfig& flags) { int clockSel=flags.getInt("clockSel",0); if (clockSel==3) { diff --git a/src/engine/platform/opll.h b/src/engine/platform/opll.h index 74d7b1bc..258113c4 100644 --- a/src/engine/platform/opll.h +++ b/src/engine/platform/opll.h @@ -73,6 +73,7 @@ class DivPlatformOPLL: public DivDispatch { int octave(int freq); int toFreq(int freq); + void commitState(int ch, DivInstrument* ins); friend void putDispatchChip(void*,int); friend void putDispatchChan(void*,int,int); @@ -95,6 +96,7 @@ class DivPlatformOPLL: public DivDispatch { void setYMFM(bool use); bool keyOffAffectsArp(int ch); bool keyOffAffectsPorta(int ch); + float getPostAmp(); void toggleRegisterDump(bool enable); void setVRC7(bool vrc); void setProperDrums(bool pd); diff --git a/src/engine/platform/qsound.cpp b/src/engine/platform/qsound.cpp index 0bf6bf2a..cea8c41a 100644 --- a/src/engine/platform/qsound.cpp +++ b/src/engine/platform/qsound.cpp @@ -606,7 +606,7 @@ void DivPlatformQSound::forceIns() { for (int i=0; i<19; i++) { chan[i].insChanged=true; chan[i].freqChanged=true; - chan[i].sample=-1; + //chan[i].sample=-1; } } @@ -645,7 +645,7 @@ bool DivPlatformQSound::keyOffAffectsArp(int ch) { } void DivPlatformQSound::notifyInsChange(int ins) { - for (int i=0; i<4; i++) { + for (int i=0; i<19; i++) { if (chan[i].ins==ins) { chan[i].insChanged=true; } @@ -657,7 +657,7 @@ void DivPlatformQSound::notifyWaveChange(int wave) { } void DivPlatformQSound::notifyInsDeletion(void* ins) { - for (int i=0; i<4; i++) { + for (int i=0; i<19; i++) { chan[i].std.notifyInsDeletion((DivInstrument*)ins); } } diff --git a/src/engine/platform/segapcm.cpp b/src/engine/platform/segapcm.cpp index 25af4211..607e1642 100644 --- a/src/engine/platform/segapcm.cpp +++ b/src/engine/platform/segapcm.cpp @@ -406,6 +406,12 @@ void DivPlatformSegaPCM::notifyInsChange(int ins) { } } +void DivPlatformSegaPCM::notifyInsDeletion(void* ins) { + for (int i=0; i<16; i++) { + chan[i].std.notifyInsDeletion((DivInstrument*)ins); + } +} + void* DivPlatformSegaPCM::getChanState(int ch) { return &chan[ch]; } diff --git a/src/engine/platform/segapcm.h b/src/engine/platform/segapcm.h index 3164e1b9..2f91f03e 100644 --- a/src/engine/platform/segapcm.h +++ b/src/engine/platform/segapcm.h @@ -89,6 +89,7 @@ class DivPlatformSegaPCM: public DivDispatch { void tick(bool sysTick=true); void muteChannel(int ch, bool mute); void notifyInsChange(int ins); + void notifyInsDeletion(void* ins); void renderSamples(int chipID); void setFlags(const DivConfig& flags); int getOutputCount(); diff --git a/src/engine/platform/sms.cpp b/src/engine/platform/sms.cpp index cfe04d18..8fcb3a39 100644 --- a/src/engine/platform/sms.cpp +++ b/src/engine/platform/sms.cpp @@ -39,6 +39,10 @@ const char** DivPlatformSMS::getRegisterSheet() { return stereo?regCheatSheetGG:regCheatSheetSN; } +float DivPlatformSMS::getPostAmp() { + return 1.5f; +} + void DivPlatformSMS::acquire_nuked(short** buf, size_t len) { int oL=0; int oR=0; diff --git a/src/engine/platform/sms.h b/src/engine/platform/sms.h index cd0a7a1f..39d4a094 100644 --- a/src/engine/platform/sms.h +++ b/src/engine/platform/sms.h @@ -81,6 +81,7 @@ class DivPlatformSMS: public DivDispatch { int getOutputCount(); bool keyOffAffectsArp(int ch); bool keyOffAffectsPorta(int ch); + float getPostAmp(); int getPortaFloor(int ch); void setFlags(const DivConfig& flags); void notifyInsDeletion(void* ins); diff --git a/src/engine/platform/tia.cpp b/src/engine/platform/tia.cpp index 02f42201..04a1432a 100644 --- a/src/engine/platform/tia.cpp +++ b/src/engine/platform/tia.cpp @@ -135,7 +135,7 @@ void DivPlatformTIA::tick(bool sysTick) { int bf=chan[i].baseFreq; if (!parent->song.oldArpStrategy) { if (!chan[i].fixedArp) { - bf+=chan[i].baseFreq+chan[i].arpOff; + bf+=chan[i].arpOff; } } chan[i].freq=dealWithFreq(chan[i].shape,bf,chan[i].pitch)+chan[i].pitch2; diff --git a/src/engine/platform/tx81z.cpp b/src/engine/platform/tx81z.cpp index fa3d4d18..09a75cac 100644 --- a/src/engine/platform/tx81z.cpp +++ b/src/engine/platform/tx81z.cpp @@ -350,56 +350,60 @@ void DivPlatformTX81Z::muteChannel(int ch, bool mute) { } } +void DivPlatformTX81Z::commitState(int ch, DivInstrument* ins) { + if (chan[ch].insChanged) { + chan[ch].state=ins->fm; + } + + for (int i=0; i<4; i++) { + unsigned short baseAddr=chanOffs[ch]|opOffs[i]; + DivInstrumentFM::Operator op=chan[ch].state.op[i]; + if (isMuted[ch]) { + rWrite(baseAddr+ADDR_TL,127); + } else { + if (KVS(ch,i)) { + if (!chan[ch].active || chan[ch].insChanged) { + rWrite(baseAddr+ADDR_TL,127-VOL_SCALE_LOG_BROKEN(127-op.tl,chan[ch].outVol&0x7f,127)); + } + } else { + if (chan[ch].insChanged) { + rWrite(baseAddr+ADDR_TL,op.tl); + } + } + } + if (chan[ch].insChanged) { + rWrite(baseAddr+ADDR_MULT_DT,(op.mult&15)|((op.egt?(op.dt&7):dtTable[op.dt&7])<<4)); + rWrite(baseAddr+ADDR_RS_AR,(op.ar&31)|(op.egt<<5)|(op.rs<<6)); + rWrite(baseAddr+ADDR_AM_DR,(op.dr&31)|(op.am<<7)); + rWrite(baseAddr+ADDR_DT2_D2R,(op.d2r&31)|(op.dt2<<6)); + rWrite(baseAddr+ADDR_SL_RR,(op.rr&15)|(op.sl<<4)); + rWrite(baseAddr+ADDR_WS_FINE,(op.dvb&15)|(op.ws<<4)); + rWrite(baseAddr+ADDR_EGS_REV,(op.dam&7)|(op.ksl<<6)); + } + } + if (chan[ch].insChanged) { + /* + if (isMuted[ch]) { + rWrite(chanOffs[ch]+ADDR_LR_FB_ALG,(chan[ch].state.alg&7)|(chan[ch].state.fb<<3)); + } else { + rWrite(chanOffs[ch]+ADDR_LR_FB_ALG,(chan[ch].state.alg&7)|(chan[ch].state.fb<<3)|((chan[ch].chVolL&1)<<6)|((chan[ch].chVolR&1)<<7)); + }*/ + rWrite(chanOffs[ch]+ADDR_FMS_AMS,((chan[ch].state.fms&7)<<4)|(chan[ch].state.ams&3)); + //rWrite(chanOffs[ch]+ADDR_FMS_AMS,0x84|((chan[ch].state.fms2&7)<<4)|(chan[ch].state.ams2&3)); + } +} + int DivPlatformTX81Z::dispatch(DivCommand c) { switch (c.cmd) { case DIV_CMD_NOTE_ON: { DivInstrument* ins=parent->getIns(chan[c.chan].ins,DIV_INS_OPZ); - if (chan[c.chan].insChanged) { - chan[c.chan].state=ins->fm; - } - chan[c.chan].macroInit(ins); if (!chan[c.chan].std.vol.will) { chan[c.chan].outVol=chan[c.chan].vol; } - for (int i=0; i<4; i++) { - unsigned short baseAddr=chanOffs[c.chan]|opOffs[i]; - DivInstrumentFM::Operator op=chan[c.chan].state.op[i]; - if (isMuted[c.chan]) { - rWrite(baseAddr+ADDR_TL,127); - } else { - if (KVS(c.chan,i)) { - if (!chan[c.chan].active || chan[c.chan].insChanged) { - rWrite(baseAddr+ADDR_TL,127-VOL_SCALE_LOG_BROKEN(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)|((op.egt?(op.dt&7):dtTable[op.dt&7])<<4)); - rWrite(baseAddr+ADDR_RS_AR,(op.ar&31)|(op.egt<<5)|(op.rs<<6)); - rWrite(baseAddr+ADDR_AM_DR,(op.dr&31)|(op.am<<7)); - rWrite(baseAddr+ADDR_DT2_D2R,(op.d2r&31)|(op.dt2<<6)); - rWrite(baseAddr+ADDR_SL_RR,(op.rr&15)|(op.sl<<4)); - rWrite(baseAddr+ADDR_WS_FINE,(op.dvb&15)|(op.ws<<4)); - rWrite(baseAddr+ADDR_EGS_REV,(op.dam&7)|(op.ksl<<6)); - } - } - if (chan[c.chan].insChanged) { - /* - if (isMuted[c.chan]) { - rWrite(chanOffs[c.chan]+ADDR_LR_FB_ALG,(chan[c.chan].state.alg&7)|(chan[c.chan].state.fb<<3)); - } else { - rWrite(chanOffs[c.chan]+ADDR_LR_FB_ALG,(chan[c.chan].state.alg&7)|(chan[c.chan].state.fb<<3)|((chan[c.chan].chVolL&1)<<6)|((chan[c.chan].chVolR&1)<<7)); - }*/ - rWrite(chanOffs[c.chan]+ADDR_FMS_AMS,((chan[c.chan].state.fms&7)<<4)|(chan[c.chan].state.ams&3)); - //rWrite(chanOffs[c.chan]+ADDR_FMS_AMS,0x84|((chan[c.chan].state.fms2&7)<<4)|(chan[c.chan].state.ams2&3)); - } + commitState(c.chan,ins); chan[c.chan].insChanged=false; if (c.value!=DIV_NOTE_NULL) { @@ -498,6 +502,11 @@ int DivPlatformTX81Z::dispatch(DivCommand c) { break; } case DIV_CMD_LEGATO: { + if (chan[c.chan].insChanged) { + DivInstrument* ins=parent->getIns(chan[c.chan].ins,DIV_INS_OPZ); + commitState(c.chan,ins); + chan[c.chan].insChanged=false; + } chan[c.chan].baseFreq=NOTE_LINEAR(c.value); chan[c.chan].freqChanged=true; break; @@ -873,6 +882,12 @@ void DivPlatformTX81Z::notifyInsChange(int ins) { } } +void DivPlatformTX81Z::notifyInsDeletion(void* ins) { + for (int i=0; i<8; i++) { + chan[i].std.notifyInsDeletion((DivInstrument*)ins); + } +} + void* DivPlatformTX81Z::getChanState(int ch) { return &chan[ch]; } diff --git a/src/engine/platform/tx81z.h b/src/engine/platform/tx81z.h index 4dea61e7..928fa546 100644 --- a/src/engine/platform/tx81z.h +++ b/src/engine/platform/tx81z.h @@ -57,6 +57,7 @@ class DivPlatformTX81Z: public DivPlatformOPM { int octave(int freq); int toFreq(int freq); + void commitState(int ch, DivInstrument* ins); friend void putDispatchChip(void*,int); public: @@ -72,6 +73,7 @@ class DivPlatformTX81Z: public DivPlatformOPM { void tick(bool sysTick=true); void muteChannel(int ch, bool mute); void notifyInsChange(int ins); + void notifyInsDeletion(void* ins); void setFlags(const DivConfig& flags); int getOutputCount(); void poke(unsigned int addr, unsigned short val); diff --git a/src/engine/platform/vera.cpp b/src/engine/platform/vera.cpp index ba9f95fe..443381bb 100644 --- a/src/engine/platform/vera.cpp +++ b/src/engine/platform/vera.cpp @@ -411,7 +411,7 @@ int DivPlatformVERA::getOutputCount() { } void DivPlatformVERA::notifyInsDeletion(void* ins) { - for (int i=0; i<2; i++) { + for (int i=0; i<17; i++) { chan[i].std.notifyInsDeletion((DivInstrument*)ins); } } diff --git a/src/engine/platform/x1_010.cpp b/src/engine/platform/x1_010.cpp index 12e6280d..5494a871 100644 --- a/src/engine/platform/x1_010.cpp +++ b/src/engine/platform/x1_010.cpp @@ -903,6 +903,10 @@ bool DivPlatformX1_010::keyOffAffectsArp(int ch) { return true; } +float DivPlatformX1_010::getPostAmp() { + return 4.0f; +} + void DivPlatformX1_010::notifyWaveChange(int wave) { for (int i=0; i<16; i++) { if (chan[i].wave==wave) { diff --git a/src/engine/platform/x1_010.h b/src/engine/platform/x1_010.h index eaa4258a..6a5a1a9f 100644 --- a/src/engine/platform/x1_010.h +++ b/src/engine/platform/x1_010.h @@ -140,6 +140,7 @@ class DivPlatformX1_010: public DivDispatch, public vgsound_emu_mem_intf { void muteChannel(int ch, bool mute); int getOutputCount(); bool keyOffAffectsArp(int ch); + float getPostAmp(); void setFlags(const DivConfig& flags); void notifyWaveChange(int wave); void notifyInsDeletion(void* ins); diff --git a/src/engine/platform/ym2203.cpp b/src/engine/platform/ym2203.cpp index 493adfc3..c2a547d8 100644 --- a/src/engine/platform/ym2203.cpp +++ b/src/engine/platform/ym2203.cpp @@ -474,6 +474,46 @@ void DivPlatformYM2203::tick(bool sysTick) { } } +void DivPlatformYM2203::commitState(int ch, DivInstrument* ins) { + if (chan[ch].insChanged) { + chan[ch].state=ins->fm; + chan[ch].opMask= + (chan[ch].state.op[0].enable?1:0)| + (chan[ch].state.op[2].enable?2:0)| + (chan[ch].state.op[1].enable?4:0)| + (chan[ch].state.op[3].enable?8:0); + } + + for (int i=0; i<4; i++) { + unsigned short baseAddr=chanOffs[ch]|opOffs[i]; + DivInstrumentFM::Operator& op=chan[ch].state.op[i]; + if (isMuted[ch]) { + rWrite(baseAddr+ADDR_TL,127); + } else { + if (KVS(ch,i)) { + if (!chan[ch].active || chan[ch].insChanged) { + rWrite(baseAddr+ADDR_TL,127-VOL_SCALE_LOG_BROKEN(127-op.tl,chan[ch].outVol&0x7f,127)); + } + } else { + if (chan[ch].insChanged) { + rWrite(baseAddr+ADDR_TL,op.tl); + } + } + } + if (chan[ch].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[ch].insChanged) { + rWrite(chanOffs[ch]+ADDR_FB_ALG,(chan[ch].state.alg&7)|(chan[ch].state.fb<<3)); + } +} + int DivPlatformYM2203::dispatch(DivCommand c) { if (c.chan>2) { c.chan-=3; @@ -489,43 +529,7 @@ int DivPlatformYM2203::dispatch(DivCommand c) { } } - if (chan[c.chan].insChanged) { - chan[c.chan].state=ins->fm; - chan[c.chan].opMask= - (chan[c.chan].state.op[0].enable?1:0)| - (chan[c.chan].state.op[2].enable?2:0)| - (chan[c.chan].state.op[1].enable?4:0)| - (chan[c.chan].state.op[3].enable?8:0); - } - - for (int i=0; i<4; i++) { - unsigned short baseAddr=chanOffs[c.chan]|opOffs[i]; - DivInstrumentFM::Operator& op=chan[c.chan].state.op[i]; - if (isMuted[c.chan]) { - rWrite(baseAddr+ADDR_TL,127); - } else { - if (KVS(c.chan,i)) { - if (!chan[c.chan].active || chan[c.chan].insChanged) { - rWrite(baseAddr+ADDR_TL,127-VOL_SCALE_LOG_BROKEN(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[c.chan]+ADDR_FB_ALG,(chan[c.chan].state.alg&7)|(chan[c.chan].state.fb<<3)); - } + commitState(c.chan,ins); chan[c.chan].insChanged=false; if (c.value!=DIV_NOTE_NULL) { @@ -616,6 +620,11 @@ int DivPlatformYM2203::dispatch(DivCommand c) { break; } case DIV_CMD_LEGATO: { + if (chan[c.chan].insChanged) { + DivInstrument* ins=parent->getIns(chan[c.chan].ins,DIV_INS_FM); + commitState(c.chan,ins); + chan[c.chan].insChanged=false; + } chan[c.chan].baseFreq=NOTE_FNUM_BLOCK(c.value,11); chan[c.chan].freqChanged=true; break; @@ -986,6 +995,9 @@ void DivPlatformYM2203::notifyInsChange(int ins) { void DivPlatformYM2203::notifyInsDeletion(void* ins) { ay->notifyInsDeletion(ins); + for (int i=0; i<3; i++) { + chan[i].std.notifyInsDeletion((DivInstrument*)ins); + } } void DivPlatformYM2203::setSkipRegisterWrites(bool value) { diff --git a/src/engine/platform/ym2203.h b/src/engine/platform/ym2203.h index 2f4da5b4..503f470b 100644 --- a/src/engine/platform/ym2203.h +++ b/src/engine/platform/ym2203.h @@ -55,6 +55,8 @@ class DivPlatformYM2203: public DivPlatformOPN { friend void putDispatchChip(void*,int); + inline void commitState(int ch, DivInstrument* ins); + void acquire_combo(short** buf, size_t len); void acquire_ymfm(short** buf, size_t len); @@ -73,7 +75,7 @@ class DivPlatformYM2203: public DivPlatformOPN { int getOutputCount(); bool keyOffAffectsArp(int ch); void notifyInsChange(int ins); - void notifyInsDeletion(void* ins); + virtual void notifyInsDeletion(void* ins); void setSkipRegisterWrites(bool val); void poke(unsigned int addr, unsigned short val); void poke(std::vector& wlist); diff --git a/src/engine/platform/ym2203ext.cpp b/src/engine/platform/ym2203ext.cpp index 701ae9e5..99d53357 100644 --- a/src/engine/platform/ym2203ext.cpp +++ b/src/engine/platform/ym2203ext.cpp @@ -641,6 +641,13 @@ void DivPlatformYM2203Ext::notifyInsChange(int ins) { } } +void DivPlatformYM2203Ext::notifyInsDeletion(void* ins) { + DivPlatformYM2203::notifyInsDeletion(ins); + for (int i=0; i<4; i++) { + opChan[i].std.notifyInsDeletion((DivInstrument*)ins); + } +} + int DivPlatformYM2203Ext::init(DivEngine* parent, int channels, int sugRate, const DivConfig& flags) { DivPlatformYM2203::init(parent,channels,sugRate,flags); for (int i=0; i<4; i++) { diff --git a/src/engine/platform/ym2203ext.h b/src/engine/platform/ym2203ext.h index 14535e2d..6d8bb0ba 100644 --- a/src/engine/platform/ym2203ext.h +++ b/src/engine/platform/ym2203ext.h @@ -39,6 +39,7 @@ class DivPlatformYM2203Ext: public DivPlatformYM2203 { void muteChannel(int ch, bool mute); bool keyOffAffectsArp(int ch); void notifyInsChange(int ins); + void notifyInsDeletion(void* ins); int init(DivEngine* parent, int channels, int sugRate, const DivConfig& flags); void quit(); ~DivPlatformYM2203Ext(); diff --git a/src/engine/platform/ym2608.cpp b/src/engine/platform/ym2608.cpp index 16953746..063b3870 100644 --- a/src/engine/platform/ym2608.cpp +++ b/src/engine/platform/ym2608.cpp @@ -545,6 +545,10 @@ void DivPlatformYM2608::tick(bool sysTick) { chan[i].state.ams=chan[i].std.ams.val; 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.ex3.had) { + lfoValue=(chan[i].std.ex3.val>7)?0:(8|(chan[i].std.ex3.val&7)); + rWrite(0x22,lfoValue); + } if (chan[i].std.ex4.had && chan[i].active) { chan[i].opMask=chan[i].std.ex4.val&15; chan[i].opMaskChanged=true; @@ -785,6 +789,43 @@ void DivPlatformYM2608::tick(bool sysTick) { ay->getRegisterWrites().clear(); } +void DivPlatformYM2608::commitState(int ch, DivInstrument* ins) { + if (chan[ch].insChanged) { + chan[ch].state=ins->fm; + chan[ch].opMask= + (chan[ch].state.op[0].enable?1:0)| + (chan[ch].state.op[2].enable?2:0)| + (chan[ch].state.op[1].enable?4:0)| + (chan[ch].state.op[3].enable?8:0); + } + + for (int i=0; i<4; i++) { + unsigned short baseAddr=chanOffs[ch]|opOffs[i]; + DivInstrumentFM::Operator& op=chan[ch].state.op[i]; + if (KVS(ch,i)) { + if (!chan[ch].active || chan[ch].insChanged) { + rWrite(baseAddr+ADDR_TL,127-VOL_SCALE_LOG_BROKEN(127-op.tl,chan[ch].outVol&0x7f,127)); + } + } else { + if (chan[ch].insChanged) { + rWrite(baseAddr+ADDR_TL,op.tl); + } + } + if (chan[ch].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[ch].insChanged) { + rWrite(chanOffs[ch]+ADDR_FB_ALG,(chan[ch].state.alg&7)|(chan[ch].state.fb<<3)); + rWrite(chanOffs[ch]+ADDR_LRAF,(isMuted[ch]?0:(chan[ch].pan<<6))|(chan[ch].state.fms&7)|((chan[ch].state.ams&3)<<4)); + } +} + int DivPlatformYM2608::dispatch(DivCommand c) { if (c.chan>5 && c.chan<9) { c.chan-=6; @@ -898,40 +939,7 @@ int DivPlatformYM2608::dispatch(DivCommand c) { } } - if (chan[c.chan].insChanged) { - chan[c.chan].state=ins->fm; - chan[c.chan].opMask= - (chan[c.chan].state.op[0].enable?1:0)| - (chan[c.chan].state.op[2].enable?2:0)| - (chan[c.chan].state.op[1].enable?4:0)| - (chan[c.chan].state.op[3].enable?8:0); - } - - 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 (KVS(c.chan,i)) { - if (!chan[c.chan].active || chan[c.chan].insChanged) { - rWrite(baseAddr+ADDR_TL,127-VOL_SCALE_LOG_BROKEN(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[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)); - } + commitState(c.chan,ins); chan[c.chan].insChanged=false; if (c.value!=DIV_NOTE_NULL) { @@ -1059,6 +1067,13 @@ int DivPlatformYM2608::dispatch(DivCommand c) { break; case DIV_CMD_LEGATO: { if (c.chan==15 && !chan[c.chan].furnacePCM) break; + if (c.chan<=psgChanOffs) { + if (chan[c.chan].insChanged) { + DivInstrument* ins=parent->getIns(chan[c.chan].ins,DIV_INS_FM); + commitState(c.chan,ins); + chan[c.chan].insChanged=false; + } + } chan[c.chan].baseFreq=NOTE_OPNB(c.chan,c.value); chan[c.chan].freqChanged=true; break; @@ -1454,6 +1469,12 @@ void DivPlatformYM2608::notifyInsChange(int ins) { void DivPlatformYM2608::notifyInsDeletion(void* ins) { ay->notifyInsDeletion(ins); + for (int i=0; i& wlist); diff --git a/src/engine/platform/ym2608ext.cpp b/src/engine/platform/ym2608ext.cpp index 0a6abe7a..e8e35cdc 100644 --- a/src/engine/platform/ym2608ext.cpp +++ b/src/engine/platform/ym2608ext.cpp @@ -679,6 +679,13 @@ void DivPlatformYM2608Ext::notifyInsChange(int ins) { } } +void DivPlatformYM2608Ext::notifyInsDeletion(void* ins) { + DivPlatformYM2608::notifyInsDeletion(ins); + for (int i=0; i<4; i++) { + opChan[i].std.notifyInsDeletion((DivInstrument*)ins); + } +} + int DivPlatformYM2608Ext::init(DivEngine* parent, int channels, int sugRate, const DivConfig& flags) { DivPlatformYM2608::init(parent,channels,sugRate,flags); for (int i=0; i<4; i++) { diff --git a/src/engine/platform/ym2608ext.h b/src/engine/platform/ym2608ext.h index 01afb361..e99518fb 100644 --- a/src/engine/platform/ym2608ext.h +++ b/src/engine/platform/ym2608ext.h @@ -39,6 +39,7 @@ class DivPlatformYM2608Ext: public DivPlatformYM2608 { void muteChannel(int ch, bool mute); bool keyOffAffectsArp(int ch); void notifyInsChange(int ins); + void notifyInsDeletion(void* ins); int init(DivEngine* parent, int channels, int sugRate, const DivConfig& flags); void quit(); ~DivPlatformYM2608Ext(); diff --git a/src/engine/platform/ym2610.cpp b/src/engine/platform/ym2610.cpp index d33a073c..e41d1af6 100644 --- a/src/engine/platform/ym2610.cpp +++ b/src/engine/platform/ym2610.cpp @@ -478,6 +478,10 @@ void DivPlatformYM2610::tick(bool sysTick) { chan[i].state.ams=chan[i].std.ams.val; 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.ex3.had) { + lfoValue=(chan[i].std.ex3.val>7)?0:(8|(chan[i].std.ex3.val&7)); + rWrite(0x22,lfoValue); + } if (chan[i].std.ex4.had && chan[i].active) { chan[i].opMask=chan[i].std.ex4.val&15; chan[i].opMaskChanged=true; @@ -720,6 +724,43 @@ void DivPlatformYM2610::tick(bool sysTick) { ay->getRegisterWrites().clear(); } +void DivPlatformYM2610::commitState(int ch, DivInstrument* ins) { + if (chan[ch].insChanged) { + chan[ch].state=ins->fm; + chan[ch].opMask= + (chan[ch].state.op[0].enable?1:0)| + (chan[ch].state.op[2].enable?2:0)| + (chan[ch].state.op[1].enable?4:0)| + (chan[ch].state.op[3].enable?8:0); + } + + for (int i=0; i<4; i++) { + unsigned short baseAddr=chanOffs[ch]|opOffs[i]; + DivInstrumentFM::Operator& op=chan[ch].state.op[i]; + if (KVS(ch,i)) { + if (!chan[ch].active || chan[ch].insChanged) { + rWrite(baseAddr+ADDR_TL,127-VOL_SCALE_LOG_BROKEN(127-op.tl,chan[ch].outVol&0x7f,127)); + } + } else { + if (chan[ch].insChanged) { + rWrite(baseAddr+ADDR_TL,op.tl); + } + } + if (chan[ch].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[ch].insChanged) { + rWrite(chanOffs[ch]+ADDR_FB_ALG,(chan[ch].state.alg&7)|(chan[ch].state.fb<<3)); + rWrite(chanOffs[ch]+ADDR_LRAF,(isMuted[ch]?0:(chan[ch].pan<<6))|(chan[ch].state.fms&7)|((chan[ch].state.ams&3)<<4)); + } +} + int DivPlatformYM2610::dispatch(DivCommand c) { if (c.chan>=psgChanOffs && c.chanfm; - chan[c.chan].opMask= - (chan[c.chan].state.op[0].enable?1:0)| - (chan[c.chan].state.op[2].enable?2:0)| - (chan[c.chan].state.op[1].enable?4:0)| - (chan[c.chan].state.op[3].enable?8:0); - } - - 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 (KVS(c.chan,i)) { - if (!chan[c.chan].active || chan[c.chan].insChanged) { - rWrite(baseAddr+ADDR_TL,127-VOL_SCALE_LOG_BROKEN(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[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)); - } + commitState(c.chan,ins); chan[c.chan].insChanged=false; if (c.value!=DIV_NOTE_NULL) { @@ -1035,6 +1043,13 @@ int DivPlatformYM2610::dispatch(DivCommand c) { break; case DIV_CMD_LEGATO: { if (c.chan==adpcmBChanOffs && !chan[c.chan].furnacePCM) break; + if (c.chan<=psgChanOffs) { + if (chan[c.chan].insChanged) { + DivInstrument* ins=parent->getIns(chan[c.chan].ins,DIV_INS_FM); + commitState(c.chan,ins); + chan[c.chan].insChanged=false; + } + } chan[c.chan].baseFreq=NOTE_OPNB(c.chan,c.value); chan[c.chan].freqChanged=true; break; @@ -1402,6 +1417,12 @@ void DivPlatformYM2610::notifyInsChange(int ins) { void DivPlatformYM2610::notifyInsDeletion(void* ins) { ay->notifyInsDeletion(ins); + for (int i=0; i& wlist); diff --git a/src/engine/platform/ym2610b.cpp b/src/engine/platform/ym2610b.cpp index a8c4d604..3f852b14 100644 --- a/src/engine/platform/ym2610b.cpp +++ b/src/engine/platform/ym2610b.cpp @@ -545,6 +545,10 @@ void DivPlatformYM2610B::tick(bool sysTick) { chan[i].state.ams=chan[i].std.ams.val; 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.ex3.had) { + lfoValue=(chan[i].std.ex3.val>7)?0:(8|(chan[i].std.ex3.val&7)); + rWrite(0x22,lfoValue); + } if (chan[i].std.ex4.had && chan[i].active) { chan[i].opMask=chan[i].std.ex4.val&15; chan[i].opMaskChanged=true; @@ -787,6 +791,43 @@ void DivPlatformYM2610B::tick(bool sysTick) { ay->getRegisterWrites().clear(); } +void DivPlatformYM2610B::commitState(int ch, DivInstrument* ins) { + if (chan[ch].insChanged) { + chan[ch].state=ins->fm; + chan[ch].opMask= + (chan[ch].state.op[0].enable?1:0)| + (chan[ch].state.op[2].enable?2:0)| + (chan[ch].state.op[1].enable?4:0)| + (chan[ch].state.op[3].enable?8:0); + } + + for (int i=0; i<4; i++) { + unsigned short baseAddr=chanOffs[ch]|opOffs[i]; + DivInstrumentFM::Operator& op=chan[ch].state.op[i]; + if (KVS(ch,i)) { + if (!chan[ch].active || chan[ch].insChanged) { + rWrite(baseAddr+ADDR_TL,127-VOL_SCALE_LOG_BROKEN(127-op.tl,chan[ch].outVol&0x7f,127)); + } + } else { + if (chan[ch].insChanged) { + rWrite(baseAddr+ADDR_TL,op.tl); + } + } + if (chan[ch].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[ch].insChanged) { + rWrite(chanOffs[ch]+ADDR_FB_ALG,(chan[ch].state.alg&7)|(chan[ch].state.fb<<3)); + rWrite(chanOffs[ch]+ADDR_LRAF,(isMuted[ch]?0:(chan[ch].pan<<6))|(chan[ch].state.fms&7)|((chan[ch].state.ams&3)<<4)); + } +} + int DivPlatformYM2610B::dispatch(DivCommand c) { if (c.chan>=psgChanOffs && c.chanfm; - chan[c.chan].opMask= - (chan[c.chan].state.op[0].enable?1:0)| - (chan[c.chan].state.op[2].enable?2:0)| - (chan[c.chan].state.op[1].enable?4:0)| - (chan[c.chan].state.op[3].enable?8:0); - } - - 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 (KVS(c.chan,i)) { - if (!chan[c.chan].active || chan[c.chan].insChanged) { - rWrite(baseAddr+ADDR_TL,127-VOL_SCALE_LOG_BROKEN(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[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)); - } + commitState(c.chan,ins); chan[c.chan].insChanged=false; if (c.value!=DIV_NOTE_NULL) { @@ -1102,6 +1110,13 @@ int DivPlatformYM2610B::dispatch(DivCommand c) { break; case DIV_CMD_LEGATO: { if (c.chan==adpcmBChanOffs && !chan[c.chan].furnacePCM) break; + if (c.chan<=psgChanOffs) { + if (chan[c.chan].insChanged) { + DivInstrument* ins=parent->getIns(chan[c.chan].ins,DIV_INS_FM); + commitState(c.chan,ins); + chan[c.chan].insChanged=false; + } + } chan[c.chan].baseFreq=NOTE_OPNB(c.chan,c.value); chan[c.chan].freqChanged=true; break; @@ -1473,6 +1488,12 @@ void DivPlatformYM2610B::notifyInsChange(int ins) { void DivPlatformYM2610B::notifyInsDeletion(void* ins) { ay->notifyInsDeletion(ins); + for (int i=0; i& wlist); diff --git a/src/engine/platform/ym2610bext.cpp b/src/engine/platform/ym2610bext.cpp index c8d0b645..28563b20 100644 --- a/src/engine/platform/ym2610bext.cpp +++ b/src/engine/platform/ym2610bext.cpp @@ -670,6 +670,13 @@ void DivPlatformYM2610BExt::notifyInsChange(int ins) { } } +void DivPlatformYM2610BExt::notifyInsDeletion(void* ins) { + DivPlatformYM2610B::notifyInsDeletion(ins); + for (int i=0; i<4; i++) { + opChan[i].std.notifyInsDeletion((DivInstrument*)ins); + } +} + int DivPlatformYM2610BExt::init(DivEngine* parent, int channels, int sugRate, const DivConfig& flags) { DivPlatformYM2610B::init(parent,channels,sugRate,flags); for (int i=0; i<4; i++) { diff --git a/src/engine/platform/ym2610bext.h b/src/engine/platform/ym2610bext.h index c7764959..50bd5bad 100644 --- a/src/engine/platform/ym2610bext.h +++ b/src/engine/platform/ym2610bext.h @@ -39,6 +39,7 @@ class DivPlatformYM2610BExt: public DivPlatformYM2610B { void muteChannel(int ch, bool mute); bool keyOffAffectsArp(int ch); void notifyInsChange(int ins); + void notifyInsDeletion(void* ins); int init(DivEngine* parent, int channels, int sugRate, const DivConfig& flags); void quit(); ~DivPlatformYM2610BExt(); diff --git a/src/engine/platform/ym2610ext.cpp b/src/engine/platform/ym2610ext.cpp index 54bbd2b7..03442dc5 100644 --- a/src/engine/platform/ym2610ext.cpp +++ b/src/engine/platform/ym2610ext.cpp @@ -670,6 +670,13 @@ void DivPlatformYM2610Ext::notifyInsChange(int ins) { } } +void DivPlatformYM2610Ext::notifyInsDeletion(void* ins) { + DivPlatformYM2610::notifyInsDeletion(ins); + for (int i=0; i<4; i++) { + opChan[i].std.notifyInsDeletion((DivInstrument*)ins); + } +} + int DivPlatformYM2610Ext::init(DivEngine* parent, int channels, int sugRate, const DivConfig& flags) { DivPlatformYM2610::init(parent,channels,sugRate,flags); for (int i=0; i<4; i++) { diff --git a/src/engine/platform/ym2610ext.h b/src/engine/platform/ym2610ext.h index 5e78c53f..e4103338 100644 --- a/src/engine/platform/ym2610ext.h +++ b/src/engine/platform/ym2610ext.h @@ -39,6 +39,7 @@ class DivPlatformYM2610Ext: public DivPlatformYM2610 { void muteChannel(int ch, bool mute); bool keyOffAffectsArp(int ch); void notifyInsChange(int ins); + void notifyInsDeletion(void* ins); int init(DivEngine* parent, int channels, int sugRate, const DivConfig& flags); void quit(); ~DivPlatformYM2610Ext(); diff --git a/src/engine/playback.cpp b/src/engine/playback.cpp index b80ff140..8759cd6a 100644 --- a/src/engine/playback.cpp +++ b/src/engine/playback.cpp @@ -908,7 +908,7 @@ void DivEngine::processRow(int i, bool afterDelay) { chan[i].vibratoPos=0; } dispatchCmd(DivCommand(DIV_CMD_PITCH,i,chan[i].pitch+(((chan[i].vibratoDepth*vibTable[chan[i].vibratoPos]*chan[i].vibratoFine)>>4)/15))); - if (chan[i].legato) { + if (chan[i].legato && (!chan[i].inPorta || song.brokenPortaLegato)) { dispatchCmd(DivCommand(DIV_CMD_LEGATO,i,chan[i].note)); dispatchCmd(DivCommand(DIV_CMD_HINT_LEGATO,i,chan[i].note)); } else { diff --git a/src/engine/song.h b/src/engine/song.h index dfe579bb..61083c18 100644 --- a/src/engine/song.h +++ b/src/engine/song.h @@ -330,6 +330,7 @@ struct DivSong { bool autoSystem; bool oldArpStrategy; bool patchbayAuto; + bool brokenPortaLegato; std::vector ins; std::vector wave; @@ -439,7 +440,8 @@ struct DivSong { disableSampleMacro(false), autoSystem(true), oldArpStrategy(false), - patchbayAuto(true) { + patchbayAuto(true), + brokenPortaLegato(false) { for (int i=0; ichipClock; - CHIP_VOL(0,2.0); + CHIP_VOL(0,4.0); willExport[i]=true; switch (song.systemFlags[i].getInt("chipType",0)) { case 1: // real SN @@ -1105,7 +1105,7 @@ SafeWriter* DivEngine::saveVGM(bool* sysToExport, bool loop, int version, bool p } else if (!(hasSN&0x40000000)) { isSecond[i]=true; willExport[i]=true; - CHIP_VOL_SECOND(0,2.0); + CHIP_VOL_SECOND(0,4.0); hasSN|=0x40000000; howManyChips++; } @@ -1170,12 +1170,12 @@ SafeWriter* DivEngine::saveVGM(bool* sysToExport, bool loop, int version, bool p case DIV_SYSTEM_X1_010: if (!hasX1) { hasX1=disCont[i].dispatch->chipClock; - CHIP_VOL(38,0.5); + CHIP_VOL(38,2.0); willExport[i]=true; writeX1010[0]=disCont[i].dispatch; } else if (!(hasX1&0x40000000)) { isSecond[i]=true; - CHIP_VOL_SECOND(38,0.5); + CHIP_VOL_SECOND(38,2.0); willExport[i]=true; writeX1010[1]=disCont[i].dispatch; hasX1|=0x40000000; @@ -1272,12 +1272,12 @@ SafeWriter* DivEngine::saveVGM(bool* sysToExport, bool loop, int version, bool p case DIV_SYSTEM_YM2612_DUALPCM_EXT: if (!hasOPN2) { hasOPN2=disCont[i].dispatch->chipClock; - CHIP_VOL(2,0.8); + CHIP_VOL(2,1.6); willExport[i]=true; writeDACSamples=true; } else if (!(hasOPN2&0x40000000)) { isSecond[i]=true; - CHIP_VOL_SECOND(2,0.8); + CHIP_VOL_SECOND(2,1.6); willExport[i]=true; hasOPN2|=0x40000000; howManyChips++; @@ -1336,11 +1336,11 @@ SafeWriter* DivEngine::saveVGM(bool* sysToExport, bool loop, int version, bool p case DIV_SYSTEM_VRC7: if (!hasOPLL) { hasOPLL=disCont[i].dispatch->chipClock; - CHIP_VOL(1,1.6); + CHIP_VOL(1,3.2); willExport[i]=true; } else if (!(hasOPLL&0x40000000)) { isSecond[i]=true; - CHIP_VOL_SECOND(1,1.6); + CHIP_VOL_SECOND(1,3.2); willExport[i]=true; hasOPLL|=0x40000000; howManyChips++; diff --git a/src/gui/about.cpp b/src/gui/about.cpp index 532cb6bd..368353cb 100644 --- a/src/gui/about.cpp +++ b/src/gui/about.cpp @@ -69,6 +69,7 @@ const char* aboutLine[]={ "brickblock369", "Burnt Fishy", "CaptainMalware", + "Clingojam", "DeMOSic", "DevEd", "Dippy", diff --git a/src/gui/compatFlags.cpp b/src/gui/compatFlags.cpp index 11c996c2..7a9fd962 100644 --- a/src/gui/compatFlags.cpp +++ b/src/gui/compatFlags.cpp @@ -214,7 +214,7 @@ void FurnaceGUI::drawCompatFlags() { e->song.delayBehavior=2; } if (ImGui::IsItemHovered()) { - ImGui::SetTooltip("no checks (like FamiTracker)"); + ImGui::SetTooltip("no checks"); } ImGui::Text("Simultaneous jump (0B+0D) treatment:"); @@ -293,6 +293,10 @@ void FurnaceGUI::drawCompatFlags() { if (ImGui::IsItemHovered()) { ImGui::SetTooltip("behavior changed in 0.6pre2"); } + ImGui::Checkbox("Broken portamento during legato",&e->song.brokenPortaLegato); + if (ImGui::IsItemHovered()) { + ImGui::SetTooltip("behavior changed in 0.6pre4"); + } } if (ImGui::IsWindowFocused(ImGuiFocusedFlags_ChildWindows)) curWindow=GUI_WINDOW_COMPAT_FLAGS; ImGui::End(); diff --git a/src/gui/doAction.cpp b/src/gui/doAction.cpp index 288a5504..2b3a2749 100644 --- a/src/gui/doAction.cpp +++ b/src/gui/doAction.cpp @@ -163,7 +163,7 @@ void FurnaceGUI::doAction(int what) { case GUI_ACTION_TX81Z_REQUEST: { TAMidiMessage msg; msg.type=TA_MIDI_SYSEX; - msg.sysExData.reset(new unsigned char[15]); + msg.sysExData.reset(new unsigned char[15],std::default_delete()); msg.sysExLen=15; memcpy(msg.sysExData.get(),avRequest,15); if (!e->sendMidiMessage(msg)) { diff --git a/src/gui/editControls.cpp b/src/gui/editControls.cpp index 173b64de..6730fbb9 100644 --- a/src/gui/editControls.cpp +++ b/src/gui/editControls.cpp @@ -506,7 +506,9 @@ void FurnaceGUI::drawMobileControls() { ImGui::SameLine(); ImGui::Button("Legacy .dmf"); ImGui::SameLine(); - ImGui::Button("Export Audio"); + if (ImGui::Button("Export Audio")) { + openFileDialog(GUI_FILE_EXPORT_AUDIO_ONE); + } ImGui::SameLine(); if (ImGui::Button("Export VGM")) { openFileDialog(GUI_FILE_EXPORT_VGM); diff --git a/src/gui/gui.cpp b/src/gui/gui.cpp index c22a1c2b..e0716732 100644 --- a/src/gui/gui.cpp +++ b/src/gui/gui.cpp @@ -1000,6 +1000,9 @@ float FurnaceGUI::calcBPM(int s1, int s2, float hz, int vN, int vD) { } void FurnaceGUI::play(int row) { + memset(chanOscVol,0,DIV_MAX_CHANS*sizeof(float)); + memset(chanOscPitch,0,DIV_MAX_CHANS*sizeof(float)); + memset(chanOscBright,0,DIV_MAX_CHANS*sizeof(float)); e->walkSong(loopOrder,loopRow,loopEnd); memset(lastIns,-1,sizeof(int)*DIV_MAX_CHANS); if (!followPattern) e->setOrder(curOrder); @@ -1026,6 +1029,9 @@ void FurnaceGUI::stop() { curNibble=false; orderNibble=false; activeNotes.clear(); + memset(chanOscVol,0,DIV_MAX_CHANS*sizeof(float)); + memset(chanOscPitch,0,DIV_MAX_CHANS*sizeof(float)); + memset(chanOscBright,0,DIV_MAX_CHANS*sizeof(float)); } void FurnaceGUI::previewNote(int refChan, int note, bool autoNote) { @@ -3132,6 +3138,14 @@ bool FurnaceGUI::loop() { break; case SDL_DISPLAYEVENT: { switch (ev.display.event) { + case SDL_DISPLAYEVENT_CONNECTED: + logD("display %d connected!",ev.display.display); + updateWindow=true; + break; + case SDL_DISPLAYEVENT_DISCONNECTED: + logD("display %d disconnected!",ev.display.display); + updateWindow=true; + break; case SDL_DISPLAYEVENT_ORIENTATION: logD("display oriented to %d",ev.display.data1); updateWindow=true; diff --git a/src/gui/gui.h b/src/gui/gui.h index 48488eac..57f4b303 100644 --- a/src/gui/gui.h +++ b/src/gui/gui.h @@ -1295,6 +1295,7 @@ class FurnaceGUI { double exportFadeOut; int macroLayout; float doubleClickTime; + int oneDigitEffects; unsigned int maxUndoSteps; String mainFontPath; String patFontPath; @@ -1430,6 +1431,7 @@ class FurnaceGUI { exportFadeOut(0.0), macroLayout(0), doubleClickTime(0.3f), + oneDigitEffects(0), maxUndoSteps(100), mainFontPath(""), patFontPath(""), diff --git a/src/gui/insEdit.cpp b/src/gui/insEdit.cpp index b2c89cf9..506472b8 100644 --- a/src/gui/insEdit.cpp +++ b/src/gui/insEdit.cpp @@ -3796,6 +3796,9 @@ void FurnaceGUI::drawInsEdit() { } } + if (ins->type==DIV_INS_FM) { + macroList.push_back(FurnaceGUIMacroDesc("LFO Speed",&ins->std.ex3Macro,0,8,96,uiColors[GUI_COLOR_MACRO_OTHER])); + } if (ins->type==DIV_INS_OPZ || ins->type==DIV_INS_OPM) { macroList.push_back(FurnaceGUIMacroDesc("AM Depth",&ins->std.ex1Macro,0,127,128,uiColors[GUI_COLOR_MACRO_OTHER])); macroList.push_back(FurnaceGUIMacroDesc("PM Depth",&ins->std.ex2Macro,0,127,128,uiColors[GUI_COLOR_MACRO_OTHER])); diff --git a/src/gui/pattern.cpp b/src/gui/pattern.cpp index fbaf4e65..8ce95852 100644 --- a/src/gui/pattern.cpp +++ b/src/gui/pattern.cpp @@ -287,10 +287,14 @@ inline void FurnaceGUI::patternRow(int i, bool isPlaying, float lineHeight, int if (pat->data[i][index]>0xff) { snprintf(id,63,"??##PE%d_%d_%d",k,i,j); ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_PATTERN_EFFECT_INVALID]); - } else { + } else if (pat->data[i][index]>0x10 || settings.oneDigitEffects==0) { const unsigned char data=pat->data[i][index]; snprintf(id,63,"%.2X##PE%d_%d_%d",data,k,i,j); ImGui::PushStyleColor(ImGuiCol_Text,uiColors[fxColors[data]]); + } else { + const unsigned char data=pat->data[i][index]; + snprintf(id,63," %.1X##PE%d_%d_%d",data,k,i,j); + ImGui::PushStyleColor(ImGuiCol_Text,uiColors[fxColors[data]]); } } ImGui::SameLine(0.0f,0.0f); diff --git a/src/gui/settings.cpp b/src/gui/settings.cpp index bcb90d35..7e4d809b 100644 --- a/src/gui/settings.cpp +++ b/src/gui/settings.cpp @@ -1602,6 +1602,11 @@ void FurnaceGUI::drawSettings() { settings.germanNotation=germanNotationB; } + bool oneDigitEffectsB=settings.oneDigitEffects; + if (ImGui::Checkbox("Single-digit effects for 00-0F",&oneDigitEffectsB)) { + settings.oneDigitEffects=oneDigitEffectsB; + } + bool centerPatternB=settings.centerPattern; if (ImGui::Checkbox("Center pattern view",¢erPatternB)) { settings.centerPattern=centerPatternB; @@ -2565,6 +2570,7 @@ void FurnaceGUI::syncSettings() { settings.exportFadeOut=e->getConfDouble("exportFadeOut",0.0); settings.macroLayout=e->getConfInt("macroLayout",0); settings.doubleClickTime=e->getConfFloat("doubleClickTime",0.3f); + settings.oneDigitEffects=e->getConfInt("oneDigitEffects",0); clampSetting(settings.mainFontSize,2,96); clampSetting(settings.patFontSize,2,96); @@ -2676,6 +2682,7 @@ void FurnaceGUI::syncSettings() { clampSetting(settings.persistFadeOut,0,1); clampSetting(settings.macroLayout,0,4); clampSetting(settings.doubleClickTime,0.02,1.0); + clampSetting(settings.oneDigitEffects,0,1); if (settings.exportLoops<0.0) settings.exportLoops=0.0; if (settings.exportFadeOut<0.0) settings.exportFadeOut=0.0; @@ -2880,6 +2887,7 @@ void FurnaceGUI::commitSettings() { e->setConf("exportFadeOut",settings.exportFadeOut); e->setConf("macroLayout",settings.macroLayout); e->setConf("doubleClickTime",settings.doubleClickTime); + e->setConf("oneDigitEffects",settings.oneDigitEffects); // colors for (int i=0; i