From 4a0295fd1f4eb34990d45c23bf1dfa55e27be644 Mon Sep 17 00:00:00 2001 From: Kagamiin~ Date: Sun, 15 Oct 2023 19:46:07 -0300 Subject: [PATCH] WIP: adding fixed pitch mode; fix UB in ESFM driver --- src/engine/instrument.cpp | 4 +- src/engine/instrument.h | 9 ++-- src/engine/platform/esfm.cpp | 21 ++++++---- src/engine/platform/esfm.h | 1 + src/gui/insEdit.cpp | 81 +++++++++++++++++++++++++++++------- 5 files changed, 88 insertions(+), 28 deletions(-) diff --git a/src/engine/instrument.cpp b/src/engine/instrument.cpp index 0e1340e90..d92fe4504 100644 --- a/src/engine/instrument.cpp +++ b/src/engine/instrument.cpp @@ -245,6 +245,7 @@ bool DivInstrumentESFM::Operator::operator==(const DivInstrumentESFM::Operator& _C(modIn) && _C(left) && _C(right) && + _C(fixed) && _C(ct) && _C(dt) ); @@ -764,7 +765,7 @@ void DivInstrument::writeFeatureEF(SafeWriter* w) { DivInstrumentESFM::Operator& op=esfm.op[i]; w->writeC(((op.delay&7)<<5)|((op.outLvl&7)<<2)|((op.right&1)<<1)|(op.left&1)); - w->writeC(op.modIn&7); + w->writeC((op.fixed&1)<<3|(op.modIn&7)); w->writeC(op.ct); w->writeC(op.dt); } @@ -2644,6 +2645,7 @@ void DivInstrument::readFeatureEF(SafeReader& reader, short version) { next=reader.readC(); op.modIn=next&7; + op.fixed=(next>>3)&1; op.ct=reader.readC(); op.dt=reader.readC(); diff --git a/src/engine/instrument.h b/src/engine/instrument.h index 4c1778304..6dfb49716 100644 --- a/src/engine/instrument.h +++ b/src/engine/instrument.h @@ -756,7 +756,7 @@ struct DivInstrumentSNES { // ESFM operator structure: // - DELAY, OUT, MOD, L, R, NOISE -// - Virtual: CT, DT, DTRAW +// - Virtual: CT, DT, FIXED // - In FM struct: AM, DAM, AR, DR, MULT, RR, SL, TL // - In FM struct: KSL, VIB, DVB, WS, SUS, KSR // - Not in struct: FNUML, FNUMH, BLOCK @@ -770,7 +770,7 @@ struct DivInstrumentESFM { // Only works on OP4, so putting it outside the Operator struct instead unsigned char noise; struct Operator { - unsigned char delay, outLvl, modIn, left, right; + unsigned char delay, outLvl, modIn, left, right, fixed; signed char ct, dt; bool operator==(const Operator& other); @@ -781,8 +781,9 @@ struct DivInstrumentESFM { delay(0), outLvl(0), modIn(0), - left(true), - right(true), + left(1), + right(1), + fixed(0), ct(0), dt(0) {} } op[4]; diff --git a/src/engine/platform/esfm.cpp b/src/engine/platform/esfm.cpp index 0c1a11e97..5d08205c3 100644 --- a/src/engine/platform/esfm.cpp +++ b/src/engine/platform/esfm.cpp @@ -218,12 +218,17 @@ void DivPlatformESFM::tick(bool sysTick) { DivInstrumentESFM::Operator& opE=chan[i].state.esfm.op[o]; int ct=(int)opE.ct; int dt=(int)opE.dt; - int opFreq=parent->calcFreq(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); - if (opFreq<0) opFreq=0; - if (opFreq>131071) opFreq=131071; - int freqt=toFreq(opFreq); - chan[i].freqL[o]=freqt&0xff; - chan[i].freqH[o]=freqt>>8; + if (opE.fixed) { + chan[i].freqL[o]=opE.dt; + chan[i].freqH[o]=opE.ct&0x1f; + } else { + int opFreq=parent->calcFreq(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); + if (opFreq<0) opFreq=0; + if (opFreq>131071) opFreq=131071; + int freqt=toFreq(opFreq); + chan[i].freqL[o]=freqt&0xff; + chan[i].freqH[o]=freqt>>8; + } immWrite(baseAddr+OFFSET_FREQL,chan[i].freqL[o]); immWrite(baseAddr+OFFSET_FREQH_BLOCK_DELAY,chan[i].freqH[o]|(opE.delay<<5)); } @@ -558,7 +563,7 @@ int DivPlatformESFM::dispatch(DivCommand c) { unsigned short baseAddr=c.chan*32 + o*8; DivInstrumentFM::Operator& op=chan[c.chan].state.fm.op[o]; op.rr=c.value2&15; - rWrite(baseAddr+OFFSET_SL_RR,(op.sl<<4)|op.rr); + rWrite(baseAddr+OFFSET_SL_RR,(op.sl<<4)|(op.rr&0xf)); } } else { unsigned int o = c.value; @@ -566,7 +571,7 @@ int DivPlatformESFM::dispatch(DivCommand c) { unsigned short baseAddr=c.chan*32 + o*8; DivInstrumentFM::Operator& op=chan[c.chan].state.fm.op[o]; op.rr=c.value2&15; - rWrite(baseAddr+OFFSET_SL_RR,(op.sl<<4)|op.rr); + rWrite(baseAddr+OFFSET_SL_RR,(op.sl<<4)|(op.rr&0xf)); } break; } diff --git a/src/engine/platform/esfm.h b/src/engine/platform/esfm.h index 558571a84..2c4a8c982 100644 --- a/src/engine/platform/esfm.h +++ b/src/engine/platform/esfm.h @@ -40,6 +40,7 @@ class DivPlatformESFM: public DivDispatch { SharedChannel(0), freqL{0, 0, 0, 0}, freqH{0, 0, 0, 0}, + hardReset(false), globalPan(3), macroVolMul(64) {} }; diff --git a/src/gui/insEdit.cpp b/src/gui/insEdit.cpp index 8ff7735d8..63600289e 100644 --- a/src/gui/insEdit.cpp +++ b/src/gui/insEdit.cpp @@ -44,16 +44,16 @@ const char* fmParamNames[3][32]={ {"ALG", "FB", "FMS/PMS", "AMS", "AR", "DR", "D2R", "RR", "SL", "TL", "RS", "MULT", "DT", "DT2", "SSG-EG", "AM", "DAM", "DVB", "EGT", "EGS", "KSL", "SUS", "VIB", "WS", "KSR", "DC", "DM", "EGS", "REV", "Fine", "FMS/PMS2", "AMS2"} }; -const char* esfmParamLongNames[8]={ - "OP4 Noise Mode", "Envelope Delay", "Output Level", "Modulation Input Level", "Left Output", "Right Output", "Coarse Tune (semitones)", "Detune" +const char* esfmParamLongNames[9]={ + "OP4 Noise Mode", "Envelope Delay", "Output Level", "Modulation Input Level", "Left Output", "Right Output", "Coarse Tune (semitones)", "Detune", "Fixed Frequency Mode" }; -const char* esfmParamNames[8]={ - "OP4 Noise Mode", "Env. Delay", "Output Level", "ModInput", "Left", "Right", "Coarse Tn.", "Detune" +const char* esfmParamNames[9]={ + "OP4 Noise Mode", "Env. Delay", "Output Level", "ModInput", "Left", "Right", "Coarse Tn.", "Detune", "Fixed" }; -const char* esfmParamShortNames[8]={ - "RHY", "DL", "OL", "MI", "L", "R", "CT", "DT" +const char* esfmParamShortNames[9]={ + "RHY", "DL", "OL", "MI", "L", "R", "CT", "DT", "FIX" }; const char* fmParamShortNames[3][32]={ @@ -230,7 +230,8 @@ enum ESFMParams { ESFM_LEFT=4, ESFM_RIGHT=5, ESFM_CT=6, - ESFM_DT=7 + ESFM_DT=7, + ESFM_FIXED=8 }; #define FM_NAME(x) fmParamNames[settings.fmNames][x] @@ -3951,7 +3952,7 @@ void FurnaceGUI::drawInsEdit() { } float sliderHeight=200.0f*dpiScale; - float waveWidth=140.0*dpiScale*((ins->type==DIV_INS_ESFM)?0.8f:1.0f); + float waveWidth=140.0*dpiScale*((ins->type==DIV_INS_ESFM)?0.85f:1.0f); float waveHeight=sliderHeight-ImGui::GetFrameHeightWithSpacing()*((ins->type==DIV_INS_OPZ || ins->type==DIV_INS_OPL || ins->type==DIV_INS_ESFM)?5.0f:4.5f); int maxTl=127; @@ -4312,13 +4313,44 @@ void FurnaceGUI::drawInsEdit() { snprintf(tempID,1024,"%s: %%d",FM_NAME(FM_MULT)); P(CWSliderScalar("##MULT",ImGuiDataType_U8,&op.mult,&_ZERO,&_FIFTEEN,tempID)); rightClickable - ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x); - snprintf(tempID,1024,"%s: %%d",ESFM_NAME(ESFM_CT)); - P(CWSliderScalar("##CT",ImGuiDataType_S8,&opE.ct,&_MINUS_TWENTY_FOUR,&_TWENTY_FOUR,tempID)); rightClickable + if (opE.fixed) { + int block=(opE.ct>>2)&7; + int freqNum=((opE.ct&3)<<8)|((unsigned char)opE.dt); + ImGui::Text("Blk"); + if (ImGui::IsItemHovered()) { + ImGui::SetTooltip("Block"); + } + ImGui::SameLine(); + ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x); + //ImVec2 cursorAlign=ImGui::GetCursorPos(); + if (ImGui::InputInt("##Block",&block,1,1)) { + if (block<0) block=0; + if (block>7) block=7; + opE.ct=(opE.ct&(~(7<<2)))|(block<<2); + } + + ImGui::Text("F"); + if (ImGui::IsItemHovered()) { + ImGui::SetTooltip("Frequency (F-Num)"); + } + ImGui::SameLine(); + //ImGui::SetCursorPos(ImVec2(cursorAlign.x,ImGui::GetCursorPosY())); + ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x); + if (ImGui::InputInt("##FreqNum",&freqNum,1,16)) { + if (freqNum<0) freqNum=0; + if (freqNum>1023) freqNum=1023; + opE.dt=freqNum&0xff; + opE.ct=(opE.ct&(~3))|(freqNum>>8); + } + } else { + ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x); + snprintf(tempID,1024,"%s: %%d",ESFM_NAME(ESFM_CT)); + P(CWSliderScalar("##CT",ImGuiDataType_S8,&opE.ct,&_MINUS_TWENTY_FOUR,&_TWENTY_FOUR,tempID)); rightClickable - ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x); - snprintf(tempID,1024,"%s: %%d",ESFM_NAME(ESFM_DT)); - P(CWSliderScalar("##DT",ImGuiDataType_S8,&opE.dt,&_MINUS_ONE_HUNDRED_TWENTY_EIGHT,&_ONE_HUNDRED_TWENTY_SEVEN,tempID)); rightClickable + ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x); + snprintf(tempID,1024,"%s: %%d",ESFM_NAME(ESFM_DT)); + P(CWSliderScalar("##DT",ImGuiDataType_S8,&opE.dt,&_MINUS_ONE_HUNDRED_TWENTY_EIGHT,&_ONE_HUNDRED_TWENTY_SEVEN,tempID)); rightClickable + } if (ImGui::BeginTable("panCheckboxes",2,ImGuiTableFlags_SizingStretchSame)) { ImGui::TableSetupColumn("c0",ImGuiTableColumnFlags_WidthStretch,0.0f); @@ -4347,9 +4379,12 @@ void FurnaceGUI::drawInsEdit() { ImGui::TableNextColumn(); float envHeight=sliderHeight;//-ImGui::GetStyle().ItemSpacing.y*2.0f; - if (ins->type==DIV_INS_OPZ || ins->type==DIV_INS_ESFM) { + if (ins->type==DIV_INS_OPZ) { envHeight-=ImGui::GetFrameHeightWithSpacing()*2.0f; } + if (ins->type==DIV_INS_ESFM) { + envHeight-=ImGui::GetFrameHeightWithSpacing()*3.0f; + } drawFMEnv(op.tl&maxTl,op.ar&maxArDr,op.dr&maxArDr,(ins->type==DIV_INS_OPL || ins->type==DIV_INS_OPL_DRUMS || ins->type==DIV_INS_OPLL || ins->type==DIV_INS_ESFM)?((op.rr&15)*2):op.d2r&31,op.rr&15,op.sl&15,op.sus,op.ssgEnv&8,fmOrigin.alg,maxTl,maxArDr,15,ImVec2(ImGui::GetContentRegionAvail().x,envHeight),ins->type); if (ins->type==DIV_INS_OPZ) { @@ -4408,6 +4443,7 @@ void FurnaceGUI::drawInsEdit() { } rightClickable bool amOn=op.am; + bool fixedOn=opE.fixed; ImGui::TableNextColumn(); if (ImGui::Checkbox(FM_SHORT_NAME(FM_KSR),&ksrOn)) { PARAMETER op.ksr=ksrOn; @@ -4430,12 +4466,27 @@ void FurnaceGUI::drawInsEdit() { if (ImGui::Checkbox(FM_SHORT_NAME(FM_AM),&amOn)) { PARAMETER op.am=amOn; } + + bool damOn=op.dam; + bool dvbOn=op.dvb; + ImGui::TableNextRow(); + ImGui::TableNextColumn(); + if (ImGui::Checkbox(FM_SHORT_NAME(FM_DVB),&dvbOn)) { PARAMETER + op.dvb=dvbOn; + } + ImGui::TableNextColumn(); + if (ImGui::Checkbox(FM_SHORT_NAME(FM_DAM),&damOn)) { PARAMETER + op.dam=damOn; + } ImGui::EndTable(); } ImGui::TableNextColumn(); if (ImGui::Checkbox(FM_SHORT_NAME(FM_SUS),&susOn)) { PARAMETER op.sus=susOn; } + if (ImGui::Checkbox(ESFM_NAME(ESFM_FIXED),&fixedOn)) { PARAMETER + opE.fixed=fixedOn; + } ImGui::EndTable(); }