diff --git a/papers/format.md b/papers/format.md index 1a8cd721..5dd7d206 100644 --- a/papers/format.md +++ b/papers/format.md @@ -32,6 +32,7 @@ these fields are 0 in format versions prior to 100 (0.6pre1). the format versions are: +- 107: Furnace dev107 - 106: Furnace dev106 - 105: Furnace dev105 - 104: Furnace dev104 @@ -850,6 +851,21 @@ size | description --- | **Game Boy extra flags** (>=106) 1 | use software envelope 1 | always init hard env on new note + --- | **ES5506 data** (>=107) + 1 | filter mode + | - 0: HPK2_HPK2 + | - 1: HPK2_LPK1 + | - 2: LPK2_LPK2 + | - 3: LPK2_LPK1 + 2 | K1 + 2 | K2 + 2 | envelope count + 1 | left volume ramp + 1 | right volume ramp + 1 | K1 ramp + 1 | K2 ramp + 1 | K1 slow + 1 | K2 slow ``` # wavetable diff --git a/src/engine/engine.h b/src/engine/engine.h index 191414f5..8e2c094d 100644 --- a/src/engine/engine.h +++ b/src/engine/engine.h @@ -46,8 +46,8 @@ #define BUSY_BEGIN_SOFT softLocked=true; isBusy.lock(); #define BUSY_END isBusy.unlock(); softLocked=false; -#define DIV_VERSION "dev106" -#define DIV_ENGINE_VERSION 106 +#define DIV_VERSION "dev107" +#define DIV_ENGINE_VERSION 107 // for imports #define DIV_VERSION_MOD 0xff01 diff --git a/src/engine/instrument.cpp b/src/engine/instrument.cpp index 687ba53f..838e1384 100644 --- a/src/engine/instrument.cpp +++ b/src/engine/instrument.cpp @@ -543,6 +543,18 @@ void DivInstrument::putInsData(SafeWriter* w) { w->writeC(gb.softEnv); w->writeC(gb.alwaysInit); + // ES5506 + w->writeC(es5506.filter.mode); + w->writeS(es5506.filter.k1); + w->writeS(es5506.filter.k2); + w->writeS(es5506.envelope.ecount); + w->writeC(es5506.envelope.lVRamp); + w->writeC(es5506.envelope.rVRamp); + w->writeC(es5506.envelope.k1Ramp); + w->writeC(es5506.envelope.k2Ramp); + w->writeC(es5506.envelope.k1Slow); + w->writeC(es5506.envelope.k2Slow); + blockEndSeek=w->tell(); w->seek(blockStartSeek,SEEK_SET); w->writeI(blockEndSeek-blockStartSeek-4); @@ -1114,6 +1126,20 @@ DivDataErrors DivInstrument::readInsData(SafeReader& reader, short version) { gb.alwaysInit=reader.readC(); } + // ES5506 + if (version>=107) { + es5506.filter.mode=(DivInstrumentES5506::Filter::FilterMode)reader.readC(); + es5506.filter.k1=reader.readS(); + es5506.filter.k2=reader.readS(); + es5506.envelope.ecount=reader.readS(); + es5506.envelope.lVRamp=reader.readC(); + es5506.envelope.rVRamp=reader.readC(); + es5506.envelope.k1Ramp=reader.readC(); + es5506.envelope.k2Ramp=reader.readC(); + es5506.envelope.k1Slow=reader.readC(); + es5506.envelope.k2Slow=reader.readC(); + } + return DIV_DATA_SUCCESS; } diff --git a/src/engine/instrument.h b/src/engine/instrument.h index 9f49a627..32c03f45 100644 --- a/src/engine/instrument.h +++ b/src/engine/instrument.h @@ -465,6 +465,42 @@ struct DivInstrumentSoundUnit { switchRoles(false) {} }; +struct DivInstrumentES5506 { + struct Filter { + enum FilterMode: unsigned char { // filter mode for pole 4,3 + FILTER_MODE_HPK2_HPK2=0, + FILTER_MODE_HPK2_LPK1, + FILTER_MODE_LPK2_LPK2, + FILTER_MODE_LPK2_LPK1, + }; + FilterMode mode; + unsigned short k1, k2; + Filter(): + mode(FILTER_MODE_LPK2_LPK1), + k1(0xffff), + k2(0xffff) {} + }; + struct Envelope { + unsigned short ecount; + signed char lVRamp, rVRamp; + signed char k1Ramp, k2Ramp; + bool k1Slow, k2Slow; + Envelope(): + ecount(0), + lVRamp(0), + rVRamp(0), + k1Ramp(0), + k2Ramp(0), + k1Slow(false), + k2Slow(false) {} + }; + Filter filter; + Envelope envelope; + DivInstrumentES5506(): + filter(Filter()), + envelope(Envelope()) {} +}; + struct DivInstrument { String name; bool mode; @@ -479,6 +515,7 @@ struct DivInstrument { DivInstrumentMultiPCM multipcm; DivInstrumentWaveSynth ws; DivInstrumentSoundUnit su; + DivInstrumentES5506 es5506; /** * save the instrument to a SafeWriter. diff --git a/src/gui/insEdit.cpp b/src/gui/insEdit.cpp index a306e366..f928e1f8 100644 --- a/src/gui/insEdit.cpp +++ b/src/gui/insEdit.cpp @@ -250,6 +250,10 @@ const char* suControlBits[5]={ "ring mod", "low pass", "high pass", "band pass", NULL }; +const char* es5506FilterModes[4]={ + "HP/K2, HP/K2", "HP/K2, LP/K1", "LP/K2, LP/K2", "LP/K2, LP/K1", +}; + const char* panBits[3]={ "right", "left", NULL }; @@ -258,6 +262,14 @@ const char* oneBit[2]={ "on", NULL }; +const char* es5506EnvelopeModes[3]={ + "k1 slowdown", "k2 slowdown", NULL +}; + +const char* es5506ControlModes[2]={ + "pause", NULL +}; + const int orderedOps[4]={ 0, 2, 1, 3 }; @@ -293,10 +305,14 @@ const char* gbHWSeqCmdTypes[6]={ "Loop until Release" }; +// do not change these! +// anything other than a checkbox will look ugly! +// +// if you really need to, and have a good rationale (and by good I mean a VERY +// good one), please tell me and we'll sort it out. const char* macroAbsoluteMode="Fixed"; const char* macroRelativeMode="Relative"; const char* macroQSoundMode="QSound"; - const char* macroDummyMode="Bug"; String macroHoverNote(int id, float val) { @@ -314,6 +330,27 @@ String macroHoverLoop(int id, float val) { return ""; } +String macroHoverES5506FilterMode(int id, float val) { + String mode="???"; + switch (((int)val)&3) { + case 0: + mode="HP/K2, HP/K2"; + break; + case 1: + mode="HP/K2, LP/K1"; + break; + case 2: + mode="LP/K2, LP/K2"; + break; + case 3: + mode="LP/K2, LP/K1"; + break; + default: + break; + } + return fmt::sprintf("%d: %s",id,mode); +} + String macroLFOWaves(int id, float val) { switch (((int)val)&3) { case 0: @@ -1203,6 +1240,9 @@ void FurnaceGUI::drawMacros(std::vector& macros) { if (ImGui::InputScalar("##IMacroLen",ImGuiDataType_U8,&i.macro->len,&_ONE,&_THREE)) { MARK_MODIFIED if (i.macro->len>128) i.macro->len=128; } + // do not change this! + // anything other than a checkbox will look ugly! + // if you really need more than two macro modes please tell me. if (i.modeName!=NULL) { bool modeVal=i.macro->mode; String modeName=fmt::sprintf("%s##IMacroMode",i.modeName); @@ -3331,7 +3371,9 @@ void FurnaceGUI::drawInsEdit() { P(ImGui::Checkbox("Don't test/gate before new note",&ins->c64.noTest)); ImGui::EndTabItem(); } - if (ins->type==DIV_INS_AMIGA || ins->type==DIV_INS_SU) if (ImGui::BeginTabItem((ins->type==DIV_INS_SU)?"Sound Unit":"Sample")) { + if (ins->type==DIV_INS_AMIGA || + ins->type==DIV_INS_SU || + ins->type==DIV_INS_ES5506) if (ImGui::BeginTabItem((ins->type==DIV_INS_SU)?"Sound Unit":"Sample")) { String sName; if (ins->amiga.initSample<0 || ins->amiga.initSample>=e->song.sampleLen) { sName="none selected"; @@ -3488,6 +3530,42 @@ void FurnaceGUI::drawInsEdit() { } ImGui::EndTabItem(); } + if (ins->type==DIV_INS_ES5506) if (ImGui::BeginTabItem("ES5506")) { + if (ImGui::BeginTable("ESParams",2,ImGuiTableFlags_SizingStretchSame)) { + ImGui::TableSetupColumn("c0",ImGuiTableColumnFlags_WidthStretch,0.0); + ImGui::TableSetupColumn("c1",ImGuiTableColumnFlags_WidthStretch,0.0); + // filter + ImGui::TableNextRow(); + ImGui::TableNextColumn(); + P(CWSliderScalar("Filter 4,3 Mode",ImGuiDataType_U8,&ins->es5506.filter.mode,&_ZERO,&_THREE,es5506FilterModes[ins->es5506.filter.mode&3])); rightClickable + ImGui::TableNextRow(); + ImGui::TableNextColumn(); + P(CWSliderScalar("Filter K1",ImGuiDataType_U16,&ins->es5506.filter.k1,&_ZERO,&_SIXTY_FIVE_THOUSAND_FIVE_HUNDRED_THIRTY_FIVE)); rightClickable + ImGui::TableNextColumn(); + P(CWSliderScalar("Filter K2",ImGuiDataType_U16,&ins->es5506.filter.k2,&_ZERO,&_SIXTY_FIVE_THOUSAND_FIVE_HUNDRED_THIRTY_FIVE)); rightClickable + // envelope + ImGui::TableNextRow(); + ImGui::TableNextColumn(); + P(CWSliderScalar("Envelope count",ImGuiDataType_U16,&ins->es5506.envelope.ecount,&_ZERO,&_FIVE_HUNDRED_ELEVEN)); rightClickable + ImGui::TableNextRow(); + ImGui::TableNextColumn(); + P(CWSliderScalar("Left Volume Ramp",ImGuiDataType_S8,&ins->es5506.envelope.lVRamp,&_MINUS_ONE_HUNDRED_TWENTY_EIGHT,&_ONE_HUNDRED_TWENTY_SEVEN)); rightClickable + ImGui::TableNextColumn(); + P(CWSliderScalar("Right Volume Ramp",ImGuiDataType_S8,&ins->es5506.envelope.rVRamp,&_MINUS_ONE_HUNDRED_TWENTY_EIGHT,&_ONE_HUNDRED_TWENTY_SEVEN)); rightClickable + ImGui::TableNextRow(); + ImGui::TableNextColumn(); + P(CWSliderScalar("Filter K1 Ramp",ImGuiDataType_S8,&ins->es5506.envelope.k1Ramp,&_MINUS_ONE_HUNDRED_TWENTY_EIGHT,&_ONE_HUNDRED_TWENTY_SEVEN)); rightClickable + ImGui::TableNextColumn(); + P(CWSliderScalar("Filter K2 Ramp",ImGuiDataType_S8,&ins->es5506.envelope.k2Ramp,&_MINUS_ONE_HUNDRED_TWENTY_EIGHT,&_ONE_HUNDRED_TWENTY_SEVEN)); rightClickable + ImGui::TableNextRow(); + ImGui::TableNextColumn(); + ImGui::Checkbox("K1 Ramp Slowdown",&ins->es5506.envelope.k1Slow); + ImGui::TableNextColumn(); + ImGui::Checkbox("K2 Ramp Slowdown",&ins->es5506.envelope.k2Slow); + ImGui::EndTable(); + } + ImGui::EndTabItem(); + } if (ins->type==DIV_INS_MULTIPCM) { if (ImGui::BeginTabItem("MultiPCM")) { String sName; @@ -3785,6 +3863,9 @@ void FurnaceGUI::drawInsEdit() { if (ins->type==DIV_INS_FDS) { volMax=32; } + if (ins->type==DIV_INS_ES5506) { + volMax=65535; + } const char* dutyLabel="Duty/Noise"; int dutyMin=0; @@ -3847,6 +3928,10 @@ void FurnaceGUI::drawInsEdit() { if (ins->type==DIV_INS_SU) { dutyMax=127; } + if (ins->type==DIV_INS_ES5506) { + dutyLabel="Filter Mode"; + dutyMax=3; + } const char* waveLabel="Waveform"; int waveMax=(ins->type==DIV_INS_AY || ins->type==DIV_INS_AY8930 || ins->type==DIV_INS_VERA)?3:255; @@ -3902,6 +3987,10 @@ void FurnaceGUI::drawInsEdit() { ex2Max=255; } if (ins->type==DIV_INS_SAA1099) ex1Max=8; + if (ins->type==DIV_INS_ES5506) { + ex1Max=65535; + ex2Max=65535; + } int panMin=0; int panMax=0; @@ -3937,6 +4026,9 @@ void FurnaceGUI::drawInsEdit() { panMax=127; panSingleNoBit=true; } + if (ins->type==DIV_INS_ES5506) { + panMax=65535; + } if (volMax>0) { macroList.push_back(FurnaceGUIMacroDesc(volumeLabel,&ins->std.volMacro,volMin,volMax,160,uiColors[GUI_COLOR_MACRO_VOLUME])); @@ -3945,6 +4037,8 @@ void FurnaceGUI::drawInsEdit() { if (dutyMax>0) { if (ins->type==DIV_INS_MIKEY) { macroList.push_back(FurnaceGUIMacroDesc(dutyLabel,&ins->std.dutyMacro,0,dutyMax,160,uiColors[GUI_COLOR_MACRO_OTHER],false,NULL,NULL,true,mikeyFeedbackBits)); + } else if (ins->type==DIV_INS_ES5506) { + macroList.push_back(FurnaceGUIMacroDesc(dutyLabel,&ins->std.dutyMacro,dutyMin,dutyMax,160,uiColors[GUI_COLOR_MACRO_OTHER],false,NULL,¯oHoverES5506FilterMode)); } else { macroList.push_back(FurnaceGUIMacroDesc(dutyLabel,&ins->std.dutyMacro,dutyMin,dutyMax,160,uiColors[GUI_COLOR_MACRO_OTHER])); } @@ -3957,15 +4051,15 @@ void FurnaceGUI::drawInsEdit() { macroList.push_back(FurnaceGUIMacroDesc("Panning",&ins->std.panLMacro,0,2,32,uiColors[GUI_COLOR_MACRO_OTHER],false,NULL,NULL,true,panBits)); } else { if (panSingleNoBit || (ins->type==DIV_INS_AMIGA && ins->std.panLMacro.mode)) { - macroList.push_back(FurnaceGUIMacroDesc("Panning",&ins->std.panLMacro,panMin,panMax,(31+panMax-panMin),uiColors[GUI_COLOR_MACRO_OTHER],false,(ins->type==DIV_INS_AMIGA)?macroQSoundMode:NULL)); + macroList.push_back(FurnaceGUIMacroDesc("Panning",&ins->std.panLMacro,panMin,panMax,CLAMP(31+panMax-panMin,32,160),uiColors[GUI_COLOR_MACRO_OTHER],false,(ins->type==DIV_INS_AMIGA)?macroQSoundMode:NULL)); } else { - macroList.push_back(FurnaceGUIMacroDesc("Panning (left)",&ins->std.panLMacro,panMin,panMax,(31+panMax-panMin),uiColors[GUI_COLOR_MACRO_OTHER],false,(ins->type==DIV_INS_AMIGA)?macroQSoundMode:NULL)); + macroList.push_back(FurnaceGUIMacroDesc("Panning (left)",&ins->std.panLMacro,panMin,panMax,CLAMP(31+panMax-panMin,32,160),uiColors[GUI_COLOR_MACRO_OTHER],false,(ins->type==DIV_INS_AMIGA)?macroQSoundMode:NULL)); } if (!panSingleNoBit) { if (ins->type==DIV_INS_AMIGA && ins->std.panLMacro.mode) { macroList.push_back(FurnaceGUIMacroDesc("Surround",&ins->std.panRMacro,0,1,32,uiColors[GUI_COLOR_MACRO_OTHER],false,NULL,NULL,true)); } else { - macroList.push_back(FurnaceGUIMacroDesc("Panning (right)",&ins->std.panRMacro,panMin,panMax,(31+panMax-panMin),uiColors[GUI_COLOR_MACRO_OTHER])); + macroList.push_back(FurnaceGUIMacroDesc("Panning (right)",&ins->std.panRMacro,panMin,panMax,CLAMP(31+panMax-panMin,32,160),uiColors[GUI_COLOR_MACRO_OTHER])); } } } @@ -3985,7 +4079,8 @@ void FurnaceGUI::drawInsEdit() { ins->type==DIV_INS_SWAN || ins->type==DIV_INS_MULTIPCM || ins->type==DIV_INS_SU || - ins->type==DIV_INS_MIKEY) { + ins->type==DIV_INS_MIKEY || + ins->type==DIV_INS_ES5506) { macroList.push_back(FurnaceGUIMacroDesc("Phase Reset",&ins->std.phaseResetMacro,0,1,32,uiColors[GUI_COLOR_MACRO_OTHER],false,NULL,NULL,true)); } if (ex1Max>0) { @@ -4001,6 +4096,8 @@ void FurnaceGUI::drawInsEdit() { macroList.push_back(FurnaceGUIMacroDesc("Mod Depth",&ins->std.ex1Macro,0,ex1Max,160,uiColors[GUI_COLOR_MACRO_OTHER])); } else if (ins->type==DIV_INS_SU) { macroList.push_back(FurnaceGUIMacroDesc("Cutoff",&ins->std.ex1Macro,0,ex1Max,160,uiColors[GUI_COLOR_MACRO_OTHER])); + } else if (ins->type==DIV_INS_ES5506) { + macroList.push_back(FurnaceGUIMacroDesc("Filter K1",&ins->std.ex1Macro,((ins->std.ex1Macro.mode==1)?(-ex1Max):0),ex1Max,160,uiColors[GUI_COLOR_MACRO_OTHER],false,macroRelativeMode)); } else { macroList.push_back(FurnaceGUIMacroDesc("Duty",&ins->std.ex1Macro,0,ex1Max,160,uiColors[GUI_COLOR_MACRO_OTHER])); } @@ -4014,6 +4111,8 @@ void FurnaceGUI::drawInsEdit() { macroList.push_back(FurnaceGUIMacroDesc("Mod Speed",&ins->std.ex2Macro,0,ex2Max,160,uiColors[GUI_COLOR_MACRO_OTHER])); } else if (ins->type==DIV_INS_SU) { macroList.push_back(FurnaceGUIMacroDesc("Resonance",&ins->std.ex2Macro,0,ex2Max,160,uiColors[GUI_COLOR_MACRO_OTHER])); + } else if (ins->type==DIV_INS_ES5506) { + macroList.push_back(FurnaceGUIMacroDesc("Filter K2",&ins->std.ex2Macro,((ins->std.ex2Macro.mode==1)?(-ex2Max):0),ex2Max,160,uiColors[GUI_COLOR_MACRO_OTHER],false,macroRelativeMode)); } else { macroList.push_back(FurnaceGUIMacroDesc("Envelope",&ins->std.ex2Macro,0,ex2Max,ex2Bit?64:160,uiColors[GUI_COLOR_MACRO_OTHER],false,NULL,NULL,ex2Bit,ayEnvBits)); } @@ -4044,6 +4143,15 @@ void FurnaceGUI::drawInsEdit() { macroList.push_back(FurnaceGUIMacroDesc("Control",&ins->std.ex3Macro,0,4,64,uiColors[GUI_COLOR_MACRO_OTHER],false,NULL,NULL,true,suControlBits)); macroList.push_back(FurnaceGUIMacroDesc("Phase Reset Timer",&ins->std.ex4Macro,0,65535,160,uiColors[GUI_COLOR_MACRO_OTHER])); // again reuse code from resonance macro but use ex4 instead } + if (ins->type==DIV_INS_ES5506) { + macroList.push_back(FurnaceGUIMacroDesc("Envelope counter",&ins->std.ex3Macro,0,511,160,uiColors[GUI_COLOR_MACRO_OTHER])); + macroList.push_back(FurnaceGUIMacroDesc("Envelope left volume ramp",&ins->std.ex4Macro,-128,127,160,uiColors[GUI_COLOR_MACRO_OTHER])); + macroList.push_back(FurnaceGUIMacroDesc("Envelope right volume ramp",&ins->std.ex5Macro,-128,127,160,uiColors[GUI_COLOR_MACRO_OTHER])); + macroList.push_back(FurnaceGUIMacroDesc("Envelope K1 ramp",&ins->std.ex6Macro,-128,127,160,uiColors[GUI_COLOR_MACRO_OTHER])); + macroList.push_back(FurnaceGUIMacroDesc("Envelope K2 ramp",&ins->std.ex7Macro,-128,127,160,uiColors[GUI_COLOR_MACRO_OTHER])); + macroList.push_back(FurnaceGUIMacroDesc("Envelope mode",&ins->std.ex8Macro,0,2,64,uiColors[GUI_COLOR_MACRO_OTHER],false,NULL,NULL,true,es5506EnvelopeModes)); + macroList.push_back(FurnaceGUIMacroDesc("Control",&ins->std.algMacro,0,1,32,uiColors[GUI_COLOR_MACRO_OTHER],false,NULL,NULL,true,es5506ControlModes)); + } drawMacros(macroList); ImGui::EndTabItem(); diff --git a/src/gui/intConst.cpp b/src/gui/intConst.cpp index 8bee0f88..9a41486e 100644 --- a/src/gui/intConst.cpp +++ b/src/gui/intConst.cpp @@ -31,6 +31,9 @@ const int _SIXTY_FOUR=64; const int _ONE_HUNDRED=100; const int _ONE_HUNDRED_TWENTY_SEVEN=127; const int _TWO_HUNDRED_FIFTY_FIVE=255; +const int _FIVE_HUNDRED_ELEVEN=511; const int _TWO_THOUSAND_FORTY_SEVEN=2047; const int _FOUR_THOUSAND_NINETY_FIVE=4095; +const int _SIXTY_FIVE_THOUSAND_FIVE_HUNDRED_THIRTY_FIVE=65535; const int _MINUS_ONE_HUNDRED_TWENTY_SEVEN=-127; +const int _MINUS_ONE_HUNDRED_TWENTY_EIGHT=-128; diff --git a/src/gui/intConst.h b/src/gui/intConst.h index 98c6c34f..ff11a496 100644 --- a/src/gui/intConst.h +++ b/src/gui/intConst.h @@ -33,6 +33,9 @@ extern const int _SIXTY_FOUR; extern const int _ONE_HUNDRED; extern const int _ONE_HUNDRED_TWENTY_SEVEN; extern const int _TWO_HUNDRED_FIFTY_FIVE; +extern const int _FIVE_HUNDRED_ELEVEN; extern const int _TWO_THOUSAND_FORTY_SEVEN; extern const int _FOUR_THOUSAND_NINETY_FIVE; +extern const int _SIXTY_FIVE_THOUSAND_FIVE_HUNDRED_THIRTY_FIVE; extern const int _MINUS_ONE_HUNDRED_TWENTY_SEVEN; +extern const int _MINUS_ONE_HUNDRED_TWENTY_EIGHT;