diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 9c4b11c4..11940eda 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: diff --git a/demos/snes/amalgam.fur b/demos/snes/amalgam.fur new file mode 100644 index 00000000..6359b145 Binary files /dev/null and b/demos/snes/amalgam.fur differ 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 new file mode 100644 index 00000000..04e8aadf Binary files /dev/null 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 6522760e..389b6e61 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/fmsharedbase.h b/src/engine/platform/fmsharedbase.h index 0c09b64a..f64c723a 100644 --- a/src/engine/platform/fmsharedbase.h +++ b/src/engine/platform/fmsharedbase.h @@ -83,6 +83,7 @@ class DivPlatformFMBase: public DivDispatch { unsigned char lastBusy; int delay; + bool flushFirst; unsigned char regPool[512]; short oldWrites[512]; @@ -102,7 +103,7 @@ class DivPlatformFMBase: public DivDispatch { } } inline void urgentWrite(unsigned short a, unsigned char v) { - if (!skipRegisterWrites) { + if (!skipRegisterWrites && !flushFirst) { if (writes.empty()) { writes.push_back(QueuedWrite(a,v)); } else if (writes.size()>16 || 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 b547181d..686df2ed 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]; } @@ -586,7 +599,7 @@ int DivPlatformGenesis::dispatch(DivCommand c) { } } if (c.chan>=5 && chan[c.chan].dacMode) { - if (skipRegisterWrites) break; + //if (skipRegisterWrites) break; if (ins->type==DIV_INS_AMIGA) { // Furnace mode if (c.value!=DIV_NOTE_NULL) chan[c.chan].dacSample=ins->amiga.getSample(c.value); if (chan[c.chan].dacSample<0 || chan[c.chan].dacSample>=parent->song.sampleLen) { @@ -1117,14 +1130,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) { - rWrite(0x2b,0x80); + chan[5].dacSample=-1; + chan[6].dacSample=-1; } immWrite(0x22,lfoValue); + flushFirst=true; } void DivPlatformGenesis::toggleRegisterDump(bool enable) { @@ -1151,6 +1170,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); @@ -1178,6 +1201,7 @@ void DivPlatformGenesis::reset() { lfoValue=8; softPCMTimer=0; extMode=false; + flushFirst=false; if (softPCM) { chan[5].dacMode=true; @@ -1280,6 +1304,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..45517719 100644 --- a/src/engine/platform/genesis.h +++ b/src/engine/platform/genesis.h @@ -114,6 +114,7 @@ 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); diff --git a/src/engine/platform/genesisext.cpp b/src/engine/platform/genesisext.cpp index 4eb34bc2..8e1e7d12 100644 --- a/src/engine/platform/genesisext.cpp +++ b/src/engine/platform/genesisext.cpp @@ -677,6 +677,8 @@ void DivPlatformGenesisExt::forceIns() { } } if (chan[5].dacMode) { + chan[5].dacSample=-1; + chan[6].dacSample=-1; rWrite(0x2b,0x80); } immWrite(0x22,lfoValue); diff --git a/src/engine/platform/opll.cpp b/src/engine/platform/opll.cpp index cba125c1..f5ab00d9 100644 --- a/src/engine/platform/opll.cpp +++ b/src/engine/platform/opll.cpp @@ -958,6 +958,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..d36c119e 100644 --- a/src/engine/platform/opll.h +++ b/src/engine/platform/opll.h @@ -95,6 +95,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/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/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/ym2608.cpp b/src/engine/platform/ym2608.cpp index 16953746..15200232 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; diff --git a/src/engine/platform/ym2610.cpp b/src/engine/platform/ym2610.cpp index bec65c78..48fa4ca0 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; diff --git a/src/engine/platform/ym2610b.cpp b/src/engine/platform/ym2610b.cpp index 4a58878e..875383e7 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; diff --git a/src/engine/playback.cpp b/src/engine/playback.cpp index 33f2d66c..88a115a3 100644 --- a/src/engine/playback.cpp +++ b/src/engine/playback.cpp @@ -896,7 +896,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/sample.cpp b/src/engine/sample.cpp index 08dee993..5df46fab 100644 --- a/src/engine/sample.cpp +++ b/src/engine/sample.cpp @@ -827,18 +827,15 @@ bool DivSample::resampleBlep(double r) { unsigned int posInt=0; double factor=r/(double)rate; float* sincITable=DivFilterTables::getSincIntegralTable(); - float s[16]; - memset(s,0,16*sizeof(float)); + float* floatData=new float[finalCount]; + memset(floatData,0,finalCount*sizeof(float)); if (depth==DIV_SAMPLE_DEPTH_16BIT) { memset(data16,0,finalCount*sizeof(short)); for (int i=0; i32767) result=32767; - data16[i]=result; + data16[i]=oldData16[posInt]; } posFrac+=1.0; @@ -853,28 +850,25 @@ bool DivSample::resampleBlep(double r) { for (int j=0; j<8; j++) { if (i-j>0) { - float result=data16[i-j]+t1[j]*-delta; - if (result<-32768) result=-32768; - if (result>32767) result=32767; - data16[i-j]=result; + floatData[i-j]+=t1[j]*-delta; } if (i+j+132767) result=32767; - data16[i+j+1]=result; + floatData[i+j+1]+=t2[j]*delta; } } } } + for (int i=0; i32767) result=32767; + data16[i]=round(result); + } } else if (depth==DIV_SAMPLE_DEPTH_8BIT) { memset(data8,0,finalCount); for (int i=0; i127) result=127; - data8[i]=result; + data8[i]=oldData8[posInt]; } posFrac+=1.0; @@ -889,21 +883,22 @@ bool DivSample::resampleBlep(double r) { for (int j=0; j<8; j++) { if (i-j>0) { - float result=data8[i-j]+t1[j]*-delta; - if (result<-128) result=-128; - if (result>127) result=127; - data8[i-j]=result; + floatData[i-j]+=t1[j]*-delta; } if (i+j+1127) result=127; - data8[i+j+1]=result; + floatData[i+j+1]+=t2[j]*delta; } } } } + for (int i=0; i127) result=127; + data16[i]=round(result); + } } + delete[] floatData; RESAMPLE_END; return true; 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 @@ -1065,7 +1065,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++; } @@ -1130,12 +1130,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; @@ -1232,12 +1232,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++; @@ -1296,11 +1296,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 c0e8cd53..70f0131c 100644 --- a/src/gui/gui.cpp +++ b/src/gui/gui.cpp @@ -3132,6 +3132,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 c72958ed..76424e84 100644 --- a/src/gui/insEdit.cpp +++ b/src/gui/insEdit.cpp @@ -352,6 +352,10 @@ const int detuneUnmap[2][11]={ {0, 0, 0, 3, 4, 5, 6, 7, 2, 1, 0} }; +const int kslMap[4]={ + 0, 2, 1, 3 +}; + // do not change these! // anything other than a checkbox will look ugly! // @@ -2778,7 +2782,11 @@ void FurnaceGUI::drawInsEdit() { if (ins->type==DIV_INS_FM || ins->type==DIV_INS_OPZ || ins->type==DIV_INS_OPM) { P(CWVSliderScalar("##RS",ImVec2(20.0f*dpiScale,sliderHeight),ImGuiDataType_U8,&op.rs,&_ZERO,&_THREE)); } else { - P(CWVSliderScalar("##KSL",ImVec2(20.0f*dpiScale,sliderHeight),ImGuiDataType_U8,&op.ksl,&_ZERO,&_THREE)); + int ksl=ins->type==DIV_INS_OPLL?op.ksl:kslMap[op.ksl&3]; + if (CWVSliderInt("##KSL",ImVec2(20.0f*dpiScale,sliderHeight),&ksl,0,3)) { + op.ksl=(ins->type==DIV_INS_OPLL?ksl:kslMap[ksl&3]); + PARAMETER; + } } if (ins->type==DIV_INS_OPZ) { @@ -3226,7 +3234,7 @@ void FurnaceGUI::drawInsEdit() { break; case DIV_INS_OPL: - case DIV_INS_OPL_DRUMS: + case DIV_INS_OPL_DRUMS: { // waveform drawWaveform(op.ws&7,ins->type==DIV_INS_OPZ,ImVec2(waveWidth,waveHeight)); ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x); @@ -3268,9 +3276,14 @@ void FurnaceGUI::drawInsEdit() { ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x); snprintf(tempID,1024,"%s: %%d",FM_NAME(FM_KSL)); - P(CWSliderScalar("##KSL",ImGuiDataType_U8,&op.ksl,&_ZERO,&_THREE,tempID)); rightClickable + int ksl=kslMap[op.ksl&3]; + if (CWSliderInt("##KSL",&ksl,0,3,tempID)) { + op.ksl=kslMap[ksl&3]; + PARAMETER; + } rightClickable break; + } case DIV_INS_OPZ: { // waveform drawWaveform(op.ws&7,ins->type==DIV_INS_OPZ,ImVec2(waveWidth,waveHeight)); @@ -3613,7 +3626,11 @@ void FurnaceGUI::drawInsEdit() { ImGui::TableNextColumn(); ImGui::Text("%s",FM_NAME(FM_RS)); } else { - P(CWSliderScalar("##KSL",ImGuiDataType_U8,&op.ksl,&_ZERO,&_THREE)); rightClickable + int ksl=ins->type==DIV_INS_OPLL?op.ksl:kslMap[op.ksl&3]; + if (CWSliderInt("##KSL",&ksl,0,3)) { + op.ksl=(ins->type==DIV_INS_OPLL?ksl:kslMap[ksl&3]); + PARAMETER; + } rightClickable ImGui::TableNextColumn(); ImGui::Text("%s",FM_NAME(FM_KSL)); } @@ -3779,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