Implementing per-op arpeggio/pitch macro

Co-authored-by: LTVA1 <87536432+LTVA1@users.noreply.github.com>
This commit is contained in:
Kagamiin~ 2024-01-01 14:59:00 -03:00
parent 215c8c375a
commit 4459a7d659
4 changed files with 103 additions and 16 deletions

View file

@ -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; i<ESFM_REG_POOL_SIZE; i++) {
if (pendingWrites[i]!=oldWrites[i]) {
immWrite(i,pendingWrites[i]&0xff);
@ -305,7 +304,17 @@ void DivPlatformESFM::tick(bool sysTick) {
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);
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;
}

View file

@ -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<int>(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];

View file

@ -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;

View file

@ -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));