diff --git a/TODO.md b/TODO.md index 077315e17..523d5e171 100644 --- a/TODO.md +++ b/TODO.md @@ -4,13 +4,18 @@ # THE REAL TO-DO LIST -because I want to focus for a couple hours - - Amiga's Period Modulation not working - Song is silent after playing an order after loop point -- Crash when opening one-column chan osc with 48 channels - Select loaded instrument on open - rewrite because I want a setting... +- re-engineer volume handling? Sound Unit cries at me +- finish status view +- finish auto-clone once you have done all of this (maybe not the first one) and merged the two or so pending pull requests, release 0.6.1 Furnace is like alcohol... + +# and then + +- new oscilloscope renderer - custom code that uses texture and fixes two issues: too many vertices, and broken anti-aliasing +- new pattern renderer - performance improvements diff --git a/doc/7-systems/c64.md b/doc/7-systems/c64.md index 28a69671b..1688f5e70 100644 --- a/doc/7-systems/c64.md +++ b/doc/7-systems/c64.md @@ -47,6 +47,7 @@ two versions of aforementioned chip exist - 6581 (original chip) and 8580 (impro - if `y` is not 0: now - this effect is not necessary if the instrument's duty macro is absolute. - `1Exy`: **change additional parameters.** + - _this effect only exists for compatibility reasons, and its use is discouraged._ - `x` may be one of the following: - `0`: attack (`y` from `0` to `F`) - `1`: decay (`y` from `0` to `F`) @@ -55,6 +56,12 @@ two versions of aforementioned chip exist - 6581 (original chip) and 8580 (impro - `4`: ring modulation (`y` is `0` or `1`) - `5`: oscillator sync (`y` is `0` or `1`) - `6`: disable channel 3 (`y` is `0` or `1`) +- `20xy`: **set attack/decay.** + - `x` is the attack. + - `y` is the decay. +- `21xy`: **set sustain/release.** + - `x` is the sustain. + - `y` is the release. - `3xxx`: **set duty cycle.** `xxx` range is `000` to `FFF`. - `4xxx`: **set cutoff.** `xxx` range is `000` to `7FF`. diff --git a/papers/newIns.md b/papers/newIns.md index 557bb7119..34afe3b28 100644 --- a/papers/newIns.md +++ b/papers/newIns.md @@ -556,6 +556,31 @@ size | description size | description -----|------------------------------------ 1 | switch roles of phase reset timer and frequency + 1 | hardware sequence length (>=185) + ??? | hardware sequence... + | - length: 5*hwSeqLen +``` + +a value in the hardware sequence has the following format: + +``` +size | description +-----|------------------------------------ + 1 | command + | - 0: set volume sweep + | - 1: set frequency sweep + | - 2: set cutoff sweep + | - 3: wait + | - 4: wait for release + | - 5: loop + | - 6: loop until release + 1 | sweep bound + 1 | sweep amount/command data + | - if "set sweep", this is amount. + | - for wait: length in ticks + | - for wait for release: nothing + | - for loop/loop until release: position + 2 | sweep period ``` # ES5506 data (ES) diff --git a/src/engine/dispatch.h b/src/engine/dispatch.h index 72cb8a3a8..7e81049e7 100644 --- a/src/engine/dispatch.h +++ b/src/engine/dispatch.h @@ -238,13 +238,16 @@ enum DivDispatchCmds { DIV_CMD_EXTERNAL, // (value) - DIV_ALWAYS_SET_VOLUME, // () -> alwaysSetVol + DIV_CMD_C64_AD, // (value) + DIV_CMD_C64_SR, // (value) DIV_CMD_ESFM_OP_PANNING, // (op, value) DIV_CMD_ESFM_OUTLVL, // (op, value) DIV_CMD_ESFM_MODIN, // (op, value) DIV_CMD_ESFM_ENV_DELAY, // (op, value) + DIV_ALWAYS_SET_VOLUME, // () -> alwaysSetVol + DIV_CMD_MAX }; diff --git a/src/engine/engine.h b/src/engine/engine.h index c634459e4..f4c46de32 100644 --- a/src/engine/engine.h +++ b/src/engine/engine.h @@ -54,8 +54,8 @@ class DivWorkPool; #define DIV_UNSTABLE -#define DIV_VERSION "dev184" -#define DIV_ENGINE_VERSION 184 +#define DIV_VERSION "dev186" +#define DIV_ENGINE_VERSION 186 // 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 91305cb62..0175c02e2 100644 --- a/src/engine/fileOps.cpp +++ b/src/engine/fileOps.cpp @@ -3008,6 +3008,15 @@ bool DivEngine::loadFur(unsigned char* file, size_t len) { } } + // C64 1Exy compat + if (ds.version<186) { + for (int i=0; iordersLen=ordCount=reader.readC(); - if (ds.subsong[0]->ordersLen<1 || ds.subsong[0]->ordersLen>127) { + ds.subsong[0]->ordersLen=ordCount=(unsigned char)reader.readC(); + if (ds.subsong[0]->ordersLen<1 || ds.subsong[0]->ordersLen>128) { logD("invalid order count!"); throw EndOfFileException(&reader,reader.tell()); } diff --git a/src/engine/instrument.cpp b/src/engine/instrument.cpp index 918a9a3fe..52c464140 100644 --- a/src/engine/instrument.cpp +++ b/src/engine/instrument.cpp @@ -196,7 +196,10 @@ bool DivInstrumentWaveSynth::operator==(const DivInstrumentWaveSynth& other) { } bool DivInstrumentSoundUnit::operator==(const DivInstrumentSoundUnit& other) { - return _C(switchRoles); + return ( + _C(switchRoles) && + _C(hwSeqLen) + ); } bool DivInstrumentES5506::operator==(const DivInstrumentES5506& other) { @@ -714,6 +717,14 @@ void DivInstrument::writeFeatureSU(SafeWriter* w) { w->writeC(su.switchRoles); + w->writeC(su.hwSeqLen); + for (int i=0; iwriteC(su.hwSeq[i].cmd); + w->writeC(su.hwSeq[i].bound); + w->writeC(su.hwSeq[i].val); + w->writeS(su.hwSeq[i].speed); + } + FEATURE_END; } @@ -2585,6 +2596,16 @@ void DivInstrument::readFeatureSU(SafeReader& reader, short version) { su.switchRoles=reader.readC(); + if (version>=185) { + su.hwSeqLen=reader.readC(); + for (int i=0; i>4) { case 0: chan[c.chan].attack=c.value&15; + if (!no1EUpdate) { + rWrite(c.chan*7+5,(chan[c.chan].attack<<4)|(chan[c.chan].decay)); + } break; case 1: chan[c.chan].decay=c.value&15; + if (!no1EUpdate) { + rWrite(c.chan*7+5,(chan[c.chan].attack<<4)|(chan[c.chan].decay)); + } break; case 2: chan[c.chan].sustain=c.value&15; + if (!no1EUpdate) { + rWrite(c.chan*7+6,(chan[c.chan].sustain<<4)|(chan[c.chan].release)); + } break; case 3: chan[c.chan].release=c.value&15; + if (!no1EUpdate) { + rWrite(c.chan*7+6,(chan[c.chan].sustain<<4)|(chan[c.chan].release)); + } break; case 4: chan[c.chan].ring=c.value; @@ -481,6 +493,16 @@ int DivPlatformC64::dispatch(DivCommand c) { break; } break; + case DIV_CMD_C64_AD: + chan[c.chan].attack=c.value>>4; + chan[c.chan].decay=c.value&15; + rWrite(c.chan*7+5,(chan[c.chan].attack<<4)|(chan[c.chan].decay)); + break; + case DIV_CMD_C64_SR: + chan[c.chan].sustain=c.value>>4; + chan[c.chan].release=c.value&15; + rWrite(c.chan*7+6,(chan[c.chan].sustain<<4)|(chan[c.chan].release)); + break; case DIV_CMD_MACRO_OFF: chan[c.chan].std.mask(c.value,true); break; @@ -652,6 +674,7 @@ void DivPlatformC64::setFlags(const DivConfig& flags) { if (sidCore==1) sid_fp->setSamplingParameters(chipClock,reSIDfp::DECIMATE,rate,0); } keyPriority=flags.getBool("keyPriority",true); + no1EUpdate=flags.getBool("no1EUpdate",false); testAD=((flags.getInt("testAttack",0)&15)<<4)|(flags.getInt("testDecay",0)&15); testSR=((flags.getInt("testSustain",0)&15)<<4)|(flags.getInt("testRelease",0)&15); diff --git a/src/engine/platform/c64.h b/src/engine/platform/c64.h index 38e735d9a..67ead6aa7 100644 --- a/src/engine/platform/c64.h +++ b/src/engine/platform/c64.h @@ -72,7 +72,7 @@ class DivPlatformC64: public DivDispatch { unsigned char sidCore; int filtCut, resetTime; - bool keyPriority, sidIs6581, needInitTables; + bool keyPriority, sidIs6581, needInitTables, no1EUpdate; unsigned char chanOrder[3]; unsigned char testAD, testSR; diff --git a/src/engine/platform/su.cpp b/src/engine/platform/su.cpp index 6ec9032e5..5a88618f3 100644 --- a/src/engine/platform/su.cpp +++ b/src/engine/platform/su.cpp @@ -134,6 +134,79 @@ void DivPlatformSoundUnit::tick(bool sysTick) { } writeControlUpper(i); } + + // run hardware sequence + if (chan[i].active) { + if (--chan[i].hwSeqDelay<=0) { + chan[i].hwSeqDelay=0; + DivInstrument* ins=parent->getIns(chan[i].ins,DIV_INS_SU); + int hwSeqCount=0; + while (chan[i].hwSeqPossu.hwSeqLen && hwSeqCount<8) { + bool leave=false; + unsigned char bound=ins->su.hwSeq[chan[i].hwSeqPos].bound; + unsigned char val=ins->su.hwSeq[chan[i].hwSeqPos].val; + unsigned short speed=ins->su.hwSeq[chan[i].hwSeqPos].speed; + switch (ins->su.hwSeq[chan[i].hwSeqPos].cmd) { + case DivInstrumentSoundUnit::DIV_SU_HWCMD_VOL: + chan[i].volSweepP=speed; + chan[i].volSweepV=val; + chan[i].volSweepB=bound; + chan[i].volSweep=(val>0); + chWrite(i,0x14,chan[i].volSweepP&0xff); + chWrite(i,0x15,chan[i].volSweepP>>8); + chWrite(i,0x16,chan[i].volSweepV); + chWrite(i,0x17,chan[i].volSweepB); + writeControlUpper(i); + break; + case DivInstrumentSoundUnit::DIV_SU_HWCMD_PITCH: + chan[i].freqSweepP=speed; + chan[i].freqSweepV=val; + chan[i].freqSweepB=bound; + chan[i].freqSweep=(val>0); + chWrite(i,0x10,chan[i].freqSweepP&0xff); + chWrite(i,0x11,chan[i].freqSweepP>>8); + chWrite(i,0x12,chan[i].freqSweepV); + chWrite(i,0x13,chan[i].freqSweepB); + writeControlUpper(i); + break; + case DivInstrumentSoundUnit::DIV_SU_HWCMD_CUT: + chan[i].cutSweepP=speed; + chan[i].cutSweepV=val; + chan[i].cutSweepB=bound; + chan[i].cutSweep=(val>0); + chWrite(i,0x18,chan[i].cutSweepP&0xff); + chWrite(i,0x19,chan[i].cutSweepP>>8); + chWrite(i,0x1a,chan[i].cutSweepV); + chWrite(i,0x1b,chan[i].cutSweepB); + writeControlUpper(i); + break; + case DivInstrumentSoundUnit::DIV_SU_HWCMD_WAIT: + chan[i].hwSeqDelay=(val+1)*parent->tickMult; + leave=true; + break; + case DivInstrumentSoundUnit::DIV_SU_HWCMD_WAIT_REL: + if (!chan[i].released) { + chan[i].hwSeqPos--; + leave=true; + } + break; + case DivInstrumentSoundUnit::DIV_SU_HWCMD_LOOP: + chan[i].hwSeqPos=val-1; + break; + case DivInstrumentSoundUnit::DIV_SU_HWCMD_LOOP_REL: + if (!chan[i].released) { + chan[i].hwSeqPos=val-1; + } + break; + } + + chan[i].hwSeqPos++; + if (leave) break; + hwSeqCount++; + } + } + } + if (chan[i].freqChanged || chan[i].keyOn || chan[i].keyOff) { //DivInstrument* ins=parent->getIns(chan[i].ins,DIV_INS_SU); chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,chan[i].fixedArp?chan[i].baseNoteOverride:chan[i].arpOff,chan[i].fixedArp,chan[i].switchRoles,2,chan[i].pitch2,chipClock,chan[i].switchRoles?CHIP_DIVIDER:CHIP_FREQBASE); @@ -160,8 +233,7 @@ void DivPlatformSoundUnit::tick(bool sysTick) { } if (chan[i].keyOn) { if (chan[i].pcm) { - DivInstrument* ins=parent->getIns(chan[i].ins,DIV_INS_SU); - int sNum=ins->amiga.getSample(chan[i].note); + int sNum=chan[i].sample; DivSample* sample=parent->getSample(sNum); if (sample!=NULL && sNum>=0 && sNumsong.sampleLen) { unsigned int sampleEnd=sampleOffSU[sNum]+(sample->getLoopEndPosition()); @@ -220,6 +292,9 @@ int DivPlatformSoundUnit::dispatch(DivCommand c) { } chan[c.chan].active=true; chan[c.chan].keyOn=true; + chan[c.chan].released=false; + chan[c.chan].hwSeqPos=0; + chan[c.chan].hwSeqDelay=0; chWrite(c.chan,0x02,chan[c.chan].vol); chan[c.chan].macroInit(ins); if (!parent->song.brokenOutVol && !chan[c.chan].std.vol.will) { @@ -231,11 +306,14 @@ int DivPlatformSoundUnit::dispatch(DivCommand c) { case DIV_CMD_NOTE_OFF: chan[c.chan].active=false; chan[c.chan].keyOff=true; + chan[c.chan].hwSeqPos=0; + chan[c.chan].hwSeqDelay=0; chan[c.chan].macroInit(NULL); break; case DIV_CMD_NOTE_OFF_ENV: case DIV_CMD_ENV_RELEASE: chan[c.chan].std.release(); + chan[c.chan].released=true; break; case DIV_CMD_INSTRUMENT: if (chan[c.chan].ins!=c.value || c.value2==1) { diff --git a/src/engine/platform/su.h b/src/engine/platform/su.h index 4f227e5b5..7062cbc8b 100644 --- a/src/engine/platform/su.h +++ b/src/engine/platform/su.h @@ -30,12 +30,14 @@ class DivPlatformSoundUnit: public DivDispatch { signed char pan; unsigned char duty; bool noise, pcm, phaseReset, filterPhaseReset, switchRoles; - bool pcmLoop, timerSync, freqSweep, volSweep, cutSweep; + bool pcmLoop, timerSync, freqSweep, volSweep, cutSweep, released; unsigned short freqSweepP, volSweepP, cutSweepP; unsigned char freqSweepB, volSweepB, cutSweepB; unsigned char freqSweepV, volSweepV, cutSweepV; unsigned short syncTimer; signed short wave; + unsigned short hwSeqPos; + short hwSeqDelay; Channel(): SharedChannel(127), cutoff(16383), @@ -56,6 +58,7 @@ class DivPlatformSoundUnit: public DivDispatch { freqSweep(false), volSweep(false), cutSweep(false), + released(false), freqSweepP(0), volSweepP(0), cutSweepP(0), @@ -66,7 +69,9 @@ class DivPlatformSoundUnit: public DivDispatch { volSweepV(0), cutSweepV(0), syncTimer(0), - wave(0) {} + wave(0), + hwSeqPos(0), + hwSeqDelay(0) {} }; Channel chan[8]; DivDispatchOscBuffer* oscBuf[8]; diff --git a/src/engine/playback.cpp b/src/engine/playback.cpp index 7a4deddea..358f8a951 100644 --- a/src/engine/playback.cpp +++ b/src/engine/playback.cpp @@ -239,12 +239,16 @@ const char* cmdName[]={ "EXTERNAL", + "C64_AD", + "C64_SR", + + "ESFM_OP_PANNING", + "ESFM_OUTLVL", + "ESFM_MODIN", + "ESFM_ENV_DELAY", + "ALWAYS_SET_VOLUME", - "DIV_CMD_ESFM_OP_PANNING", - "DIV_CMD_ESFM_OUTLVL", - "DIV_CMD_ESFM_MODIN", - "DIV_CMD_ESFM_ENV_DELAY" }; static_assert((sizeof(cmdName)/sizeof(void*))==DIV_CMD_MAX,"update cmdName!"); diff --git a/src/engine/sysDef.cpp b/src/engine/sysDef.cpp index f676b62d2..ae25e6673 100644 --- a/src/engine/sysDef.cpp +++ b/src/engine/sysDef.cpp @@ -606,6 +606,8 @@ void DivEngine::registerSystems() { {0x1b, {DIV_CMD_C64_FILTER_RESET, "1Bxy: Reset cutoff (x: on new note; y: now)"}}, {0x1c, {DIV_CMD_C64_DUTY_RESET, "1Cxy: Reset pulse width (x: on new note; y: now)"}}, {0x1e, {DIV_CMD_C64_EXTENDED, "1Exy: Change additional parameters"}}, + {0x20, {DIV_CMD_C64_AD, "20xy: Set attack/decay (x: attack; y: decay)"}}, + {0x21, {DIV_CMD_C64_SR, "21xy: Set sustain/release (x: sustain; y: release)"}}, }; const EffectHandler c64FineDutyHandler(DIV_CMD_C64_FINE_DUTY, "3xxx: Set pulse width (0 to FFF)", effectValLong<12>); const EffectHandler c64FineCutoffHandler(DIV_CMD_C64_FINE_CUTOFF, "4xxx: Set cutoff (0 to 7FF)", effectValLong<11>); diff --git a/src/gui/insEdit.cpp b/src/gui/insEdit.cpp index fbc054b0b..3e7dffbec 100644 --- a/src/gui/insEdit.cpp +++ b/src/gui/insEdit.cpp @@ -378,6 +378,16 @@ const char* gbHWSeqCmdTypes[6]={ "Loop until Release" }; +const char* suHWSeqCmdTypes[7]={ + "Volume Sweep", + "Frequency Sweep", + "Cutoff Sweep", + "Wait", + "Wait for Release", + "Loop", + "Loop until Release" +}; + const char* snesGainModes[5]={ "Direct", "Decrease (linear)", @@ -2453,7 +2463,6 @@ void FurnaceGUI::alterSampleMap(int column, int val) { void FurnaceGUI::insTabSample(DivInstrument* ins) { const char* sampleTabName="Sample"; - if (ins->type==DIV_INS_SU) sampleTabName="Sound Unit"; if (ins->type==DIV_INS_NES) sampleTabName="DPCM"; if (ImGui::BeginTabItem(sampleTabName)) { if (ins->type==DIV_INS_NES && e->song.oldDPCM) { @@ -2482,9 +2491,6 @@ void FurnaceGUI::insTabSample(DivInstrument* ins) { ins->type==DIV_INS_VRC6 || ins->type==DIV_INS_SU) { P(ImGui::Checkbox("Use sample",&ins->amiga.useSample)); - if (ins->type==DIV_INS_SU) { - P(ImGui::Checkbox("Switch roles of frequency and phase reset timer",&ins->su.switchRoles)); - } if (ins->type==DIV_INS_X1_010) { if (ImGui::InputInt("Sample bank slot##BANKSLOT",&ins->x1_010.bankSlot,1,4)) { PARAMETER if (ins->x1_010.bankSlot<0) ins->x1_010.bankSlot=0; @@ -5733,6 +5739,242 @@ void FurnaceGUI::drawInsEdit() { P(ImGui::Checkbox("Don't test before new note",&ins->c64.noTest)); ImGui::EndTabItem(); } + if (ins->type==DIV_INS_SU) if (ImGui::BeginTabItem("Sound Unit")) { + P(ImGui::Checkbox("Switch roles of frequency and phase reset timer",&ins->su.switchRoles)); + if (ImGui::BeginChild("HWSeqSU",ImGui::GetContentRegionAvail(),true,ImGuiWindowFlags_MenuBar)) { + ImGui::BeginMenuBar(); + ImGui::Text("Hardware Sequence"); + ImGui::EndMenuBar(); + + if (ins->su.hwSeqLen>0) if (ImGui::BeginTable("HWSeqListSU",3)) { + ImGui::TableSetupColumn("c0",ImGuiTableColumnFlags_WidthFixed); + ImGui::TableSetupColumn("c1",ImGuiTableColumnFlags_WidthStretch); + ImGui::TableSetupColumn("c2",ImGuiTableColumnFlags_WidthFixed); + int curFrame=0; + ImGui::TableNextRow(ImGuiTableRowFlags_Headers); + ImGui::TableNextColumn(); + ImGui::Text("Tick"); + ImGui::TableNextColumn(); + ImGui::Text("Command"); + ImGui::TableNextColumn(); + ImGui::Text("Move/Remove"); + for (int i=0; isu.hwSeqLen; i++) { + ImGui::TableNextRow(); + ImGui::TableNextColumn(); + ImGui::Text("%d (#%d)",curFrame,i); + ImGui::TableNextColumn(); + ImGui::PushID(i); + if (ins->su.hwSeq[i].cmd>=DivInstrumentSoundUnit::DIV_SU_HWCMD_MAX) { + ins->su.hwSeq[i].cmd=0; + } + int cmd=ins->su.hwSeq[i].cmd; + ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x); + if (ImGui::Combo("##HWSeqCmd",&cmd,suHWSeqCmdTypes,DivInstrumentSoundUnit::DIV_SU_HWCMD_MAX)) { + if (ins->su.hwSeq[i].cmd!=cmd) { + ins->su.hwSeq[i].cmd=cmd; + ins->su.hwSeq[i].val=0; + ins->su.hwSeq[i].bound=0; + ins->su.hwSeq[i].speed=0; + } + } + bool somethingChanged=false; + switch (ins->su.hwSeq[i].cmd) { + case DivInstrumentSoundUnit::DIV_SU_HWCMD_VOL: { + int swPeriod=ins->su.hwSeq[i].speed; + int swBound=ins->su.hwSeq[i].bound; + int swVal=ins->su.hwSeq[i].val&31; + bool swDir=ins->su.hwSeq[i].val&32; + bool swLoop=ins->su.hwSeq[i].val&64; + bool swInvert=ins->su.hwSeq[i].val&128; + + if (ImGui::InputInt("Period",&swPeriod,1,16)) { + if (swPeriod<0) swPeriod=0; + if (swPeriod>65535) swPeriod=65535; + somethingChanged=true; + } + if (CWSliderInt("Amount",&swVal,0,31)) { + somethingChanged=true; + } + if (CWSliderInt("Bound",&swBound,0,255)) { + somethingChanged=true; + } + if (ImGui::RadioButton("Up",swDir)) { PARAMETER + swDir=true; + somethingChanged=true; + } + ImGui::SameLine(); + if (ImGui::RadioButton("Down",!swDir)) { PARAMETER + swDir=false; + somethingChanged=true; + } + if (ImGui::Checkbox("Loop",&swLoop)) { PARAMETER + somethingChanged=true; + } + ImGui::SameLine(); + if (ImGui::Checkbox("Flip",&swInvert)) { PARAMETER + somethingChanged=true; + } + + if (somethingChanged) { + ins->su.hwSeq[i].speed=swPeriod; + ins->su.hwSeq[i].bound=swBound; + ins->su.hwSeq[i].val=(swVal&31)|(swDir?32:0)|(swLoop?64:0)|(swInvert?128:0); + PARAMETER; + } + break; + } + case DivInstrumentSoundUnit::DIV_SU_HWCMD_PITCH: + case DivInstrumentSoundUnit::DIV_SU_HWCMD_CUT: { + int swPeriod=ins->su.hwSeq[i].speed; + int swBound=ins->su.hwSeq[i].bound; + int swVal=ins->su.hwSeq[i].val&127; + bool swDir=ins->su.hwSeq[i].val&128; + + if (ImGui::InputInt("Period",&swPeriod,1,16)) { + if (swPeriod<0) swPeriod=0; + if (swPeriod>65535) swPeriod=65535; + somethingChanged=true; + } + if (CWSliderInt("Amount",&swVal,0,31)) { + somethingChanged=true; + } + if (CWSliderInt("Bound",&swBound,0,255)) { + somethingChanged=true; + } + if (ImGui::RadioButton("Up",swDir)) { PARAMETER + swDir=true; + somethingChanged=true; + } + ImGui::SameLine(); + if (ImGui::RadioButton("Down",!swDir)) { PARAMETER + swDir=false; + somethingChanged=true; + } + + if (somethingChanged) { + ins->su.hwSeq[i].speed=swPeriod; + ins->su.hwSeq[i].bound=swBound; + ins->su.hwSeq[i].val=(swVal&127)|(swDir?128:0); + PARAMETER; + } + break; + } + case DivInstrumentSoundUnit::DIV_SU_HWCMD_WAIT: { + int len=ins->su.hwSeq[i].val+1; + curFrame+=ins->su.hwSeq[i].val+1; + + if (ImGui::InputInt("Ticks",&len)) { + if (len<1) len=1; + if (len>255) len=256; + somethingChanged=true; + } + + if (somethingChanged) { + ins->su.hwSeq[i].val=len-1; + PARAMETER; + } + break; + } + case DivInstrumentSoundUnit::DIV_SU_HWCMD_WAIT_REL: + curFrame++; + break; + case DivInstrumentSoundUnit::DIV_SU_HWCMD_LOOP: + case DivInstrumentSoundUnit::DIV_SU_HWCMD_LOOP_REL: { + int pos=ins->su.hwSeq[i].val; + + if (ImGui::InputInt("Position",&pos)) { + if (pos<0) pos=0; + if (pos>(ins->su.hwSeqLen-1)) pos=(ins->su.hwSeqLen-1); + somethingChanged=true; + } + + if (somethingChanged) { + ins->su.hwSeq[i].val=pos; + PARAMETER; + } + break; + } + default: + break; + } + ImGui::PopID(); + ImGui::TableNextColumn(); + ImGui::PushID(i+512); + if (ImGui::Button(ICON_FA_CHEVRON_UP "##HWCmdUp")) { + if (i>0) { + e->lockEngine([ins,i]() { + ins->su.hwSeq[i-1].cmd^=ins->su.hwSeq[i].cmd; + ins->su.hwSeq[i].cmd^=ins->su.hwSeq[i-1].cmd; + ins->su.hwSeq[i-1].cmd^=ins->su.hwSeq[i].cmd; + + ins->su.hwSeq[i-1].speed^=ins->su.hwSeq[i].speed; + ins->su.hwSeq[i].speed^=ins->su.hwSeq[i-1].speed; + ins->su.hwSeq[i-1].speed^=ins->su.hwSeq[i].speed; + + ins->su.hwSeq[i-1].val^=ins->su.hwSeq[i].val; + ins->su.hwSeq[i].val^=ins->su.hwSeq[i-1].val; + ins->su.hwSeq[i-1].val^=ins->su.hwSeq[i].val; + + ins->su.hwSeq[i-1].bound^=ins->su.hwSeq[i].bound; + ins->su.hwSeq[i].bound^=ins->su.hwSeq[i-1].bound; + ins->su.hwSeq[i-1].bound^=ins->su.hwSeq[i].bound; + }); + } + MARK_MODIFIED; + } + ImGui::SameLine(); + if (ImGui::Button(ICON_FA_CHEVRON_DOWN "##HWCmdDown")) { + if (isu.hwSeqLen-1) { + e->lockEngine([ins,i]() { + ins->su.hwSeq[i+1].cmd^=ins->su.hwSeq[i].cmd; + ins->su.hwSeq[i].cmd^=ins->su.hwSeq[i+1].cmd; + ins->su.hwSeq[i+1].cmd^=ins->su.hwSeq[i].cmd; + + ins->su.hwSeq[i+1].speed^=ins->su.hwSeq[i].speed; + ins->su.hwSeq[i].speed^=ins->su.hwSeq[i+1].speed; + ins->su.hwSeq[i+1].speed^=ins->su.hwSeq[i].speed; + + ins->su.hwSeq[i+1].val^=ins->su.hwSeq[i].val; + ins->su.hwSeq[i].val^=ins->su.hwSeq[i+1].val; + ins->su.hwSeq[i+1].val^=ins->su.hwSeq[i].val; + + ins->su.hwSeq[i+1].bound^=ins->su.hwSeq[i].bound; + ins->su.hwSeq[i].bound^=ins->su.hwSeq[i+1].bound; + ins->su.hwSeq[i+1].bound^=ins->su.hwSeq[i].bound; + }); + } + MARK_MODIFIED; + } + ImGui::SameLine(); + pushDestColor(); + if (ImGui::Button(ICON_FA_TIMES "##HWCmdDel")) { + for (int j=i; jsu.hwSeqLen-1; j++) { + ins->su.hwSeq[j].cmd=ins->su.hwSeq[j+1].cmd; + ins->su.hwSeq[j].speed=ins->su.hwSeq[j+1].speed; + ins->su.hwSeq[j].val=ins->su.hwSeq[j+1].val; + ins->su.hwSeq[j].bound=ins->su.hwSeq[j+1].bound; + } + ins->su.hwSeqLen--; + } + popDestColor(); + ImGui::PopID(); + } + ImGui::EndTable(); + } + + if (ImGui::Button(ICON_FA_PLUS "##HWCmdAdd")) { + if (ins->su.hwSeqLen<255) { + ins->su.hwSeq[ins->su.hwSeqLen].cmd=0; + ins->su.hwSeq[ins->su.hwSeqLen].speed=0; + ins->su.hwSeq[ins->su.hwSeqLen].val=0; + ins->su.hwSeq[ins->su.hwSeqLen].bound=0; + ins->su.hwSeqLen++; + } + } + } + ImGui::EndChild(); + ImGui::EndTabItem(); + } if (ins->type==DIV_INS_MSM6258 || ins->type==DIV_INS_MSM6295 || ins->type==DIV_INS_ADPCMA || diff --git a/src/gui/presets.cpp b/src/gui/presets.cpp index 9947f29c7..bb07b8078 100644 --- a/src/gui/presets.cpp +++ b/src/gui/presets.cpp @@ -976,7 +976,7 @@ void FurnaceGUI::initSystemPresets() { ); ENTRY( "PC + AdLib (drums mode)", { - CH(DIV_SYSTEM_OPL2, 1.0f, 0, ""), + CH(DIV_SYSTEM_OPL2_DRUMS, 1.0f, 0, ""), CH(DIV_SYSTEM_PCSPKR, 1.0f, 0, "") } ); diff --git a/src/gui/sysConf.cpp b/src/gui/sysConf.cpp index 813942d0c..08a2f2c0a 100644 --- a/src/gui/sysConf.cpp +++ b/src/gui/sysConf.cpp @@ -586,6 +586,7 @@ bool FurnaceGUI::drawSysConf(int chan, int sysPos, DivSystem type, DivConfig& fl case DIV_SYSTEM_C64_6581: { int clockSel=flags.getInt("clockSel",0); bool keyPriority=flags.getBool("keyPriority",true); + bool no1EUpdate=flags.getBool("no1EUpdate",false); int testAttack=flags.getInt("testAttack",0); int testDecay=flags.getInt("testDecay",0); int testSustain=flags.getInt("testSustain",0); @@ -644,10 +645,15 @@ bool FurnaceGUI::drawSysConf(int chan, int sysPos, DivSystem type, DivConfig& fl altered=true; } + if (ImGui::Checkbox("Disable 1Exy env update (compatibility)",&no1EUpdate)) { + altered=true; + } + if (altered) { e->lockSave([&]() { flags.set("clockSel",clockSel); flags.set("keyPriority",keyPriority); + flags.set("no1EUpdate",no1EUpdate); flags.set("testAttack",testAttack); flags.set("testDecay",testDecay); flags.set("testSustain",testSustain);