WIP: adding fixed pitch mode; fix UB in ESFM driver

This commit is contained in:
Kagamiin~ 2023-10-15 19:46:07 -03:00
parent 84e0ec9dae
commit 4a0295fd1f
5 changed files with 88 additions and 28 deletions

View file

@ -245,6 +245,7 @@ bool DivInstrumentESFM::Operator::operator==(const DivInstrumentESFM::Operator&
_C(modIn) && _C(modIn) &&
_C(left) && _C(left) &&
_C(right) && _C(right) &&
_C(fixed) &&
_C(ct) && _C(ct) &&
_C(dt) _C(dt)
); );
@ -764,7 +765,7 @@ void DivInstrument::writeFeatureEF(SafeWriter* w) {
DivInstrumentESFM::Operator& op=esfm.op[i]; 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.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.ct);
w->writeC(op.dt); w->writeC(op.dt);
} }
@ -2644,6 +2645,7 @@ void DivInstrument::readFeatureEF(SafeReader& reader, short version) {
next=reader.readC(); next=reader.readC();
op.modIn=next&7; op.modIn=next&7;
op.fixed=(next>>3)&1;
op.ct=reader.readC(); op.ct=reader.readC();
op.dt=reader.readC(); op.dt=reader.readC();

View file

@ -756,7 +756,7 @@ struct DivInstrumentSNES {
// ESFM operator structure: // ESFM operator structure:
// - DELAY, OUT, MOD, L, R, NOISE // - 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: AM, DAM, AR, DR, MULT, RR, SL, TL
// - In FM struct: KSL, VIB, DVB, WS, SUS, KSR // - In FM struct: KSL, VIB, DVB, WS, SUS, KSR
// - Not in struct: FNUML, FNUMH, BLOCK // - Not in struct: FNUML, FNUMH, BLOCK
@ -770,7 +770,7 @@ struct DivInstrumentESFM {
// Only works on OP4, so putting it outside the Operator struct instead // Only works on OP4, so putting it outside the Operator struct instead
unsigned char noise; unsigned char noise;
struct Operator { struct Operator {
unsigned char delay, outLvl, modIn, left, right; unsigned char delay, outLvl, modIn, left, right, fixed;
signed char ct, dt; signed char ct, dt;
bool operator==(const Operator& other); bool operator==(const Operator& other);
@ -781,8 +781,9 @@ struct DivInstrumentESFM {
delay(0), delay(0),
outLvl(0), outLvl(0),
modIn(0), modIn(0),
left(true), left(1),
right(true), right(1),
fixed(0),
ct(0), ct(0),
dt(0) {} dt(0) {}
} op[4]; } op[4];

View file

@ -218,12 +218,17 @@ void DivPlatformESFM::tick(bool sysTick) {
DivInstrumentESFM::Operator& opE=chan[i].state.esfm.op[o]; DivInstrumentESFM::Operator& opE=chan[i].state.esfm.op[o];
int ct=(int)opE.ct; int ct=(int)opE.ct;
int dt=(int)opE.dt; 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 (opE.fixed) {
if (opFreq<0) opFreq=0; chan[i].freqL[o]=opE.dt;
if (opFreq>131071) opFreq=131071; chan[i].freqH[o]=opE.ct&0x1f;
int freqt=toFreq(opFreq); } else {
chan[i].freqL[o]=freqt&0xff; 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);
chan[i].freqH[o]=freqt>>8; 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_FREQL,chan[i].freqL[o]);
immWrite(baseAddr+OFFSET_FREQH_BLOCK_DELAY,chan[i].freqH[o]|(opE.delay<<5)); 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; unsigned short baseAddr=c.chan*32 + o*8;
DivInstrumentFM::Operator& op=chan[c.chan].state.fm.op[o]; DivInstrumentFM::Operator& op=chan[c.chan].state.fm.op[o];
op.rr=c.value2&15; 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 { } else {
unsigned int o = c.value; unsigned int o = c.value;
@ -566,7 +571,7 @@ int DivPlatformESFM::dispatch(DivCommand c) {
unsigned short baseAddr=c.chan*32 + o*8; unsigned short baseAddr=c.chan*32 + o*8;
DivInstrumentFM::Operator& op=chan[c.chan].state.fm.op[o]; DivInstrumentFM::Operator& op=chan[c.chan].state.fm.op[o];
op.rr=c.value2&15; 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; break;
} }

View file

@ -40,6 +40,7 @@ class DivPlatformESFM: public DivDispatch {
SharedChannel<int>(0), SharedChannel<int>(0),
freqL{0, 0, 0, 0}, freqL{0, 0, 0, 0},
freqH{0, 0, 0, 0}, freqH{0, 0, 0, 0},
hardReset(false),
globalPan(3), globalPan(3),
macroVolMul(64) {} macroVolMul(64) {}
}; };

View file

@ -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"} {"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]={ const char* esfmParamLongNames[9]={
"OP4 Noise Mode", "Envelope Delay", "Output Level", "Modulation Input Level", "Left Output", "Right Output", "Coarse Tune (semitones)", "Detune" "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]={ const char* esfmParamNames[9]={
"OP4 Noise Mode", "Env. Delay", "Output Level", "ModInput", "Left", "Right", "Coarse Tn.", "Detune" "OP4 Noise Mode", "Env. Delay", "Output Level", "ModInput", "Left", "Right", "Coarse Tn.", "Detune", "Fixed"
}; };
const char* esfmParamShortNames[8]={ const char* esfmParamShortNames[9]={
"RHY", "DL", "OL", "MI", "L", "R", "CT", "DT" "RHY", "DL", "OL", "MI", "L", "R", "CT", "DT", "FIX"
}; };
const char* fmParamShortNames[3][32]={ const char* fmParamShortNames[3][32]={
@ -230,7 +230,8 @@ enum ESFMParams {
ESFM_LEFT=4, ESFM_LEFT=4,
ESFM_RIGHT=5, ESFM_RIGHT=5,
ESFM_CT=6, ESFM_CT=6,
ESFM_DT=7 ESFM_DT=7,
ESFM_FIXED=8
}; };
#define FM_NAME(x) fmParamNames[settings.fmNames][x] #define FM_NAME(x) fmParamNames[settings.fmNames][x]
@ -3951,7 +3952,7 @@ void FurnaceGUI::drawInsEdit() {
} }
float sliderHeight=200.0f*dpiScale; 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); 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; int maxTl=127;
@ -4312,13 +4313,44 @@ void FurnaceGUI::drawInsEdit() {
snprintf(tempID,1024,"%s: %%d",FM_NAME(FM_MULT)); snprintf(tempID,1024,"%s: %%d",FM_NAME(FM_MULT));
P(CWSliderScalar("##MULT",ImGuiDataType_U8,&op.mult,&_ZERO,&_FIFTEEN,tempID)); rightClickable P(CWSliderScalar("##MULT",ImGuiDataType_U8,&op.mult,&_ZERO,&_FIFTEEN,tempID)); rightClickable
ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x); if (opE.fixed) {
snprintf(tempID,1024,"%s: %%d",ESFM_NAME(ESFM_CT)); int block=(opE.ct>>2)&7;
P(CWSliderScalar("##CT",ImGuiDataType_S8,&opE.ct,&_MINUS_TWENTY_FOUR,&_TWENTY_FOUR,tempID)); rightClickable 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); ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x);
snprintf(tempID,1024,"%s: %%d",ESFM_NAME(ESFM_DT)); 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 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)) { if (ImGui::BeginTable("panCheckboxes",2,ImGuiTableFlags_SizingStretchSame)) {
ImGui::TableSetupColumn("c0",ImGuiTableColumnFlags_WidthStretch,0.0f); ImGui::TableSetupColumn("c0",ImGuiTableColumnFlags_WidthStretch,0.0f);
@ -4347,9 +4379,12 @@ void FurnaceGUI::drawInsEdit() {
ImGui::TableNextColumn(); ImGui::TableNextColumn();
float envHeight=sliderHeight;//-ImGui::GetStyle().ItemSpacing.y*2.0f; 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; 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); 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) { if (ins->type==DIV_INS_OPZ) {
@ -4408,6 +4443,7 @@ void FurnaceGUI::drawInsEdit() {
} rightClickable } rightClickable
bool amOn=op.am; bool amOn=op.am;
bool fixedOn=opE.fixed;
ImGui::TableNextColumn(); ImGui::TableNextColumn();
if (ImGui::Checkbox(FM_SHORT_NAME(FM_KSR),&ksrOn)) { PARAMETER if (ImGui::Checkbox(FM_SHORT_NAME(FM_KSR),&ksrOn)) { PARAMETER
op.ksr=ksrOn; op.ksr=ksrOn;
@ -4430,12 +4466,27 @@ void FurnaceGUI::drawInsEdit() {
if (ImGui::Checkbox(FM_SHORT_NAME(FM_AM),&amOn)) { PARAMETER if (ImGui::Checkbox(FM_SHORT_NAME(FM_AM),&amOn)) { PARAMETER
op.am=amOn; 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::EndTable();
} }
ImGui::TableNextColumn(); ImGui::TableNextColumn();
if (ImGui::Checkbox(FM_SHORT_NAME(FM_SUS),&susOn)) { PARAMETER if (ImGui::Checkbox(FM_SHORT_NAME(FM_SUS),&susOn)) { PARAMETER
op.sus=susOn; op.sus=susOn;
} }
if (ImGui::Checkbox(ESFM_NAME(ESFM_FIXED),&fixedOn)) { PARAMETER
opE.fixed=fixedOn;
}
ImGui::EndTable(); ImGui::EndTable();
} }