diff --git a/src/engine/platform/esfm.cpp b/src/engine/platform/esfm.cpp index bf319fb1a..c9f8a6c62 100644 --- a/src/engine/platform/esfm.cpp +++ b/src/engine/platform/esfm.cpp @@ -229,6 +229,8 @@ void DivPlatformESFM::tick(bool sysTick) { } } + + // detune/fixed pitch if (opE.fixed) { if (m.ssg.had) { @@ -241,15 +243,10 @@ void DivPlatformESFM::tick(bool sysTick) { chan[i].freqChanged=true; } } else { - if (m.ssg.had) { - opE.ct=(signed char)m.ssg.val; - chan[i].freqChanged=true; - } - if (m.dt.had) { - opE.dt=(signed char)m.dt.val; - chan[i].freqChanged=true; - } + chan[i].handleArpFmOp(0, o); + chan[i].handlePitchFmOp(o); } + if (m.dt2.had) { opE.delay=m.dt2.val; rWrite(baseAddr+ADDR_FREQH_BLOCK_DELAY,chan[i].freqH[o]|(opE.delay<<5)); @@ -257,6 +254,8 @@ void DivPlatformESFM::tick(bool sysTick) { } } + + for (int i=0; icalcFreq(chan[i].baseFreq,chan[i].pitch,chan[i].fixedArp?chan[i].baseNoteOverride+ct:chan[i].arpOff+ct,chan[i].fixedArp,false,octave(chan[i].baseFreq)*2,chan[i].pitch2+dt,chipClock,CHIP_FREQBASE); + int arp=chan[i].fixedArp?chan[i].baseNoteOverride+ct:chan[i].arpOff+ct; + int pitch2=chan[i].pitch2+dt; + int fixedArp=chan[i].fixedArp; + if(chan[i].opsState[o].hasOpArp) { + arp=chan[i].opsState[o].fixedArp?chan[i].opsState[o].baseNoteOverride+ct:chan[i].opsState[o].arpOff+ct; + fixedArp=chan[i].opsState[o].fixedArp; + } + if(chan[i].opsState[o].hasOpPitch) { + pitch2=chan[i].opsState[o].pitch2+dt; + } + int opFreq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,arp,fixedArp,false,octave(chan[i].baseFreq)*2,pitch2,chipClock,CHIP_FREQBASE); if (opFreq<0) opFreq=0; if (opFreq>131071) opFreq=131071; int freqt=toFreq(opFreq); @@ -432,6 +441,7 @@ int DivPlatformESFM::dispatch(DivCommand c) { DivInstrument* ins=parent->getIns(chan[c.chan].ins,DIV_INS_ESFM); chan[c.chan].macroInit(ins); + memset(chan[c.chan].opsState, 0, sizeof(chan[c.chan].opsState)); if (!chan[c.chan].std.vol.will) { chan[c.chan].outVol=chan[c.chan].vol; } diff --git a/src/engine/platform/esfm.h b/src/engine/platform/esfm.h index fbb971003..a183925c7 100644 --- a/src/engine/platform/esfm.h +++ b/src/engine/platform/esfm.h @@ -36,13 +36,77 @@ class DivPlatformESFM: public DivDispatch { bool hardReset; unsigned char globalPan; int macroVolMul; + struct { + int baseNoteOverride; + bool fixedArp; + int arpOff; + int pitch2; + bool hasOpArp; + bool hasOpPitch; + } opsState[4]; + + void handleArpFmOp(int offset=0, int o=0) { + DivMacroInt::IntOp& m=this->std.op[o]; + if (m.ssg.had) { + opsState[o].hasOpArp=true; + + if (m.ssg.val<0) { + if (!(m.ssg.val&0x40000000)) { + opsState[o].baseNoteOverride=(m.ssg.val|0x40000000)+offset; + opsState[o].fixedArp=true; + } else { + opsState[o].arpOff=m.ssg.val; + opsState[o].fixedArp=false; + } + } else { + if (m.ssg.val&0x40000000) { + opsState[o].baseNoteOverride=(m.ssg.val&(~0x40000000))+offset; + opsState[o].fixedArp=true; + } else { + opsState[o].arpOff=m.ssg.val; + opsState[o].fixedArp=false; + } + } + freqChanged=true; + } + + else + { + opsState[o].hasOpArp=false; + } + } + + void handlePitchFmOp(int o) + { + DivMacroInt::IntOp& m=this->std.op[o]; + + if (m.dt.had) { + opsState[o].hasOpPitch=true; + + if (m.dt.mode) { + opsState[o].pitch2+=m.dt.val; + CLAMP_VAR(opsState[o].pitch2,-131071,131071); + } else { + opsState[o].pitch2=m.dt.val; + } + this->freqChanged=true; + } + + else + { + opsState[o].hasOpPitch=false; + } + } + Channel(): SharedChannel(0), freqL{0, 0, 0, 0}, freqH{0, 0, 0, 0}, hardReset(false), globalPan(3), - macroVolMul(64) {} + macroVolMul(64) { + memset(opsState, 0, sizeof(opsState)); + } }; Channel chan[18]; DivDispatchOscBuffer* oscBuf[18]; diff --git a/src/gui/gui.h b/src/gui/gui.h index 162ebb0ac..3e0e53137 100644 --- a/src/gui/gui.h +++ b/src/gui/gui.h @@ -1245,8 +1245,10 @@ struct FurnaceGUIMacroDesc { bool isBitfield, blockMode, bit30; String (*hoverFunc)(int,float,void*); void* hoverFuncUser; + bool isArp; + bool isPitch; - FurnaceGUIMacroDesc(const char* name, DivInstrumentMacro* m, int macroMin, int macroMax, float macroHeight, ImVec4 col=ImVec4(1.0f,1.0f,1.0f,1.0f), bool block=false, const char* mName=NULL, String (*hf)(int,float,void*)=NULL, bool bitfield=false, const char** bfVal=NULL, unsigned int bitOff=0, bool bit30Special=false, void* hfu=NULL): + FurnaceGUIMacroDesc(const char* name, DivInstrumentMacro* m, int macroMin, int macroMax, float macroHeight, ImVec4 col=ImVec4(1.0f,1.0f,1.0f,1.0f), bool block=false, const char* mName=NULL, String (*hf)(int,float,void*)=NULL, bool bitfield=false, const char** bfVal=NULL, unsigned int bitOff=0, bool bit30Special=false, void* hfu=NULL, bool isArp=false, bool isPitch=false): macro(m), height(macroHeight), displayName(name), @@ -1258,7 +1260,9 @@ struct FurnaceGUIMacroDesc { blockMode(block), bit30(bit30Special), hoverFunc(hf), - hoverFuncUser(hfu) { + hoverFuncUser(hfu), + isArp(isArp), + isPitch(isPitch) { // MSVC -> hell this->min=macroMin; this->max=macroMax; diff --git a/src/gui/insEdit.cpp b/src/gui/insEdit.cpp index 40515099e..bc0859811 100644 --- a/src/gui/insEdit.cpp +++ b/src/gui/insEdit.cpp @@ -1537,10 +1537,10 @@ void FurnaceGUI::drawMacroEdit(FurnaceGUIMacroDesc& i, int totalFit, float avail ImGui::PushStyleVar(ImGuiStyleVar_FramePadding,ImVec2(0.0f,0.0f)); if (i.macro->vZoom<1) { - if (i.macro->macroType==DIV_MACRO_ARP) { + if (i.macro->macroType==DIV_MACRO_ARP || i.isArp) { i.macro->vZoom=24; i.macro->vScroll=120-12; - } else if (i.macro->macroType==DIV_MACRO_PITCH) { + } else if (i.macro->macroType==DIV_MACRO_PITCH || i.isPitch) { i.macro->vZoom=128; i.macro->vScroll=2048-64; } else { @@ -3937,6 +3937,9 @@ void FurnaceGUI::drawInsEdit() { ImGui::SetCursorPosY(ImGui::GetCursorPosY()-0.5*ImGui::GetStyle().ItemSpacing.y); if (ImGui::Checkbox(ESFM_NAME(ESFM_FIXED),&fixedOn)) { PARAMETER opE.fixed=fixedOn; + // HACK: reset zoom and scroll in fixed pitch macros so that they draw correctly + ins->std.opMacros[i].ssgMacro.vZoom=-1; + ins->std.opMacros[i].dtMacro.vZoom=-1; } if (ins->type==DIV_INS_ESFM) { if (fixedOn) { @@ -4674,6 +4677,9 @@ void FurnaceGUI::drawInsEdit() { } if (ImGui::Checkbox(ESFM_NAME(ESFM_FIXED),&fixedOn)) { PARAMETER opE.fixed=fixedOn; + // HACK: reset zoom and scroll in fixed pitch macros so that they draw correctly + ins->std.opMacros[i].ssgMacro.vZoom=-1; + ins->std.opMacros[i].dtMacro.vZoom=-1; } ImGui::EndTable(); @@ -5198,6 +5204,9 @@ void FurnaceGUI::drawInsEdit() { ImGui::SameLine(); if (ImGui::Checkbox(ESFM_NAME(ESFM_FIXED),&fixedOn)) { PARAMETER opE.fixed=fixedOn; + // HACK: reset zoom and scroll in fixed pitch macros so that they draw correctly + ins->std.opMacros[i].ssgMacro.vZoom=-1; + ins->std.opMacros[i].dtMacro.vZoom=-1; } } @@ -5322,8 +5331,8 @@ void FurnaceGUI::drawInsEdit() { macroList.push_back(FurnaceGUIMacroDesc("Block",&ins->std.opMacros[ordi].ssgMacro,0,7,64,uiColors[GUI_COLOR_MACRO_OTHER],true)); macroList.push_back(FurnaceGUIMacroDesc("FreqNum",&ins->std.opMacros[ordi].dtMacro,0,1023,160,uiColors[GUI_COLOR_MACRO_OTHER])); } else { - macroList.push_back(FurnaceGUIMacroDesc(ESFM_NAME(ESFM_CT),&ins->std.opMacros[ordi].ssgMacro,-24,24,128,uiColors[GUI_COLOR_MACRO_OTHER],true)); - macroList.push_back(FurnaceGUIMacroDesc(ESFM_NAME(ESFM_DT),&ins->std.opMacros[ordi].dtMacro,-128,127,160,uiColors[GUI_COLOR_MACRO_OTHER])); + macroList.push_back(FurnaceGUIMacroDesc("Op. Arpeggio",&ins->std.opMacros[ordi].ssgMacro,-120,120,160,uiColors[GUI_COLOR_MACRO_PITCH],true,NULL,macroHoverNote,false,NULL,0,true,ins->std.opMacros[ordi].ssgMacro.val,true)); + macroList.push_back(FurnaceGUIMacroDesc("Op. Pitch",&ins->std.opMacros[ordi].dtMacro,-2048,2047,160,uiColors[GUI_COLOR_MACRO_PITCH],true,macroRelativeMode,NULL,false,NULL,0,false,NULL,false,true)); } macroList.push_back(FurnaceGUIMacroDesc(FM_NAME(FM_AM),&ins->std.opMacros[ordi].amMacro,0,1,32,uiColors[GUI_COLOR_MACRO_OTHER],false,NULL,NULL,true));