From 4b32599237b07f3b3522c85eacc4280f07c1183c Mon Sep 17 00:00:00 2001 From: tildearrow Date: Sun, 12 Dec 2021 04:21:09 -0500 Subject: [PATCH] GUI: macro editor (kind of) and order view --- src/engine/engine.cpp | 83 +++++++++++++++++++++++--- src/engine/engine.h | 12 ++++ src/gui/gui.cpp | 135 +++++++++++++++++++++++++++++++++++++++++- src/gui/gui.h | 13 ++++ 4 files changed, 233 insertions(+), 10 deletions(-) diff --git a/src/engine/engine.cpp b/src/engine/engine.cpp index ab9179cdf..781ce6e34 100644 --- a/src/engine/engine.cpp +++ b/src/engine/engine.cpp @@ -104,7 +104,7 @@ unsigned char systemToFile(DivSystem val) { return 0; } -int getChannelCount(DivSystem sys) { +int DivEngine::getChannelCount(DivSystem sys) { switch (sys) { case DIV_SYSTEM_NULL: return 0; @@ -132,6 +132,78 @@ int getChannelCount(DivSystem sys) { return 0; } +bool DivEngine::isFMSystem(DivSystem sys) { + return (sys==DIV_SYSTEM_GENESIS || + sys==DIV_SYSTEM_GENESIS_EXT || + sys==DIV_SYSTEM_ARCADE || + sys==DIV_SYSTEM_YM2610 || + sys==DIV_SYSTEM_YM2610_EXT || + sys==DIV_SYSTEM_YMU759); +} + +const char* chanShortNames[11][17]={ + {"1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12", "13", "14", "15", "16", "PCM"}, // YMU759 + {"F1", "F2", "F3", "F4", "F5", "F6", "S1", "S2", "S3", "NO"}, // Genesis + {"F1", "F2", "O1", "O2", "O3", "O4", "F4", "F5", "F6", "S1", "S2", "S3", "S4"}, // Genesis (extended channel 3) + {"S1", "S2", "S3", "NO"}, // SMS + {"S1", "S2", "WA", "NO"}, // GB + {"CH1", "CH2", "CH3", "CH4", "CH5", "CH6"}, // PCE + {"S1", "S2", "TR", "NO", "PCM"}, // NES + {"CH1", "CH2", "CH3"}, // C64 + {"F1", "F2", "F3", "F4", "F5", "F6", "F7", "F8", "P1", "P2", "P3", "P4", "P5"}, // Arcade + {"F1", "F2", "F3", "F4", "S1", "S2", "S3", "P1", "P2", "P3", "P4", "P5", "P6"}, // YM2610 + {"F1", "O1", "O2", "O3", "O4", "F3", "F4", "S1", "S2", "S3", "P1", "P2", "P3", "P4", "P5", "P6"}, // YM2610 (extended channel 2) +}; + +const char* DivEngine::getChannelShortName(int chan) { + switch (song.system) { + case DIV_SYSTEM_NULL: case DIV_SYSTEM_YMU759: + return chanShortNames[0][chan]; + break; + case DIV_SYSTEM_GENESIS: + return chanShortNames[1][chan]; + break; + case DIV_SYSTEM_GENESIS_EXT: + return chanShortNames[2][chan]; + break; + case DIV_SYSTEM_SMS: + return chanShortNames[3][chan]; + break; + case DIV_SYSTEM_GB: + return chanShortNames[4][chan]; + break; + case DIV_SYSTEM_PCE: + return chanShortNames[5][chan]; + break; + case DIV_SYSTEM_NES: + return chanShortNames[6][chan]; + break; + case DIV_SYSTEM_C64_6581: case DIV_SYSTEM_C64_8580: + return chanShortNames[7][chan]; + break; + case DIV_SYSTEM_ARCADE: + return chanShortNames[8][chan]; + break; + case DIV_SYSTEM_YM2610: + return chanShortNames[9][chan]; + break; + case DIV_SYSTEM_YM2610_EXT: + return chanShortNames[10][chan]; + break; + } + return "??"; +} + +int DivEngine::getMaxVolume() { + switch (song.system) { + case DIV_SYSTEM_PCE: + return 31; + default: + return 15; + } + return 127; +} + bool DivEngine::load(void* f, size_t slen) { unsigned char* file; size_t len; @@ -350,12 +422,7 @@ bool DivEngine::load(void* f, size_t slen) { } if (ins->mode) { // FM - if (ds.system!=DIV_SYSTEM_GENESIS && - ds.system!=DIV_SYSTEM_GENESIS_EXT && - ds.system!=DIV_SYSTEM_ARCADE && - ds.system!=DIV_SYSTEM_YM2610 && - ds.system!=DIV_SYSTEM_YM2610_EXT && - ds.system!=DIV_SYSTEM_YMU759) { + if (!isFMSystem(ds.system)) { logE("FM instrument in non-FM system. oopsie?\n"); return false; } @@ -996,6 +1063,7 @@ void DivEngine::play() { curRow=0; clockDrift=0; cycles=0; + speedAB=false; playing=true; isBusy.unlock(); } @@ -1028,6 +1096,7 @@ void DivEngine::setOrder(unsigned char order) { curRow=0; clockDrift=0; cycles=0; + speedAB=false; } isBusy.unlock(); } diff --git a/src/engine/engine.h b/src/engine/engine.h index c2407e0fe..ef3afccc9 100644 --- a/src/engine/engine.h +++ b/src/engine/engine.h @@ -113,6 +113,18 @@ class DivEngine { // stop void stop(); + // get sys channel count + int getChannelCount(DivSystem sys); + + // is FM system + bool isFMSystem(DivSystem sys); + + // get channel short name + const char* getChannelShortName(int chan); + + // get max STD volume + int getMaxVolume(); + // get current order unsigned char getOrder(); diff --git a/src/gui/gui.cpp b/src/gui/gui.cpp index 53f799a9d..539b814aa 100644 --- a/src/gui/gui.cpp +++ b/src/gui/gui.cpp @@ -1,11 +1,15 @@ #include "gui.h" +#include "SDL_events.h" +#include "SDL_video.h" #include "fonts.h" #include "../ta-log.h" #include "imgui.h" #include "misc/cpp/imgui_stdlib.h" +#include #include const int _ZERO=0; +const int _ONE=1; const int _THREE=3; const int _SEVEN=7; const int _FIFTEEN=15;; @@ -26,6 +30,23 @@ bool FurnaceGUI::loop() { while (SDL_PollEvent(&ev)) { ImGui_ImplSDL2_ProcessEvent(&ev); switch (ev.type) { + case SDL_MOUSEMOTION: + if (macroDragActive) { + // do macro drag here! + if (macroDragLen>0) { + int x=(ev.motion.x-macroDragStart.x)*macroDragLen/macroDragAreaSize.x; + if (x<0) x=0; + if (x>=macroDragLen) x=macroDragLen-1; + int y=round(macroDragMax-((ev.motion.y-macroDragStart.y)*(double(macroDragMax-macroDragMin)/(double)macroDragAreaSize.y))); + if (y>macroDragMax) y=macroDragMax; + if (ygetChannelCount(e->song.system)); + ImGui::TableNextRow(); + ImGui::TableNextColumn(); + for (int i=0; igetChannelCount(e->song.system); i++) { + ImGui::TableNextColumn(); + ImGui::Text("%s",e->getChannelShortName(i)); + } + for (int i=0; isong.ordersLen; i++) { + ImGui::TableNextRow(); + if (e->getOrder()==i) ImGui::TableSetBgColor(ImGuiTableBgTarget_RowBg0,0x40ffffff); + ImGui::TableNextColumn(); + snprintf(selID,16,"%.2x##O_S%.2x",i,i); + if (ImGui::Selectable(selID)) { + e->setOrder(i); + } + for (int j=0; jgetChannelCount(e->song.system); j++) { + ImGui::TableNextColumn(); + snprintf(selID,16,"%.2x##O_%.2x_%.2x",e->song.orders.ord[j][i],j,i); + if (ImGui::Selectable(selID)) { + if (e->song.orders.ord[j][i]<0x7f) e->song.orders.ord[j][i]++; + } + if (ImGui::IsItemClicked(ImGuiMouseButton_Right)) { + if (e->song.orders.ord[j][i]>0) e->song.orders.ord[j][i]--; + } + } + } + + ImGui::EndTable(); + } + ImGui::End(); + if (ImGui::Begin("Instruments")) { for (int i=0; isong.ins.size(); i++) { DivInstrument* ins=e->song.ins[i]; @@ -84,7 +138,7 @@ bool FurnaceGUI::loop() { } else { DivInstrument* ins=e->song.ins[curIns]; ImGui::InputText("Name",&ins->name); - ImGui::Checkbox("FM",&ins->mode); + if (e->isFMSystem(e->song.system)) ImGui::Checkbox("FM",&ins->mode); if (ins->mode) { // FM ImGui::SliderScalar("Algorithm",ImGuiDataType_U8,&ins->fm.alg,&_ZERO,&_SEVEN); @@ -123,12 +177,74 @@ bool FurnaceGUI::loop() { ImGui::PopID(); } } else { // STD - + float asFloat[128]; + float loopIndicator[128]; + + // volume macro + ImGui::Separator(); + ImGui::Text("Volume Macro"); + for (int i=0; istd.volMacroLen; i++) { + asFloat[i]=ins->std.volMacro[i]; + loopIndicator[i]=(ins->std.volMacroLoop!=-1 && i>=ins->std.volMacroLoop); + } + ImGui::PushStyleVar(ImGuiStyleVar_FramePadding,ImVec2(0.0f,0.0f)); + int volMax=e->getMaxVolume(); + ImGui::PlotHistogram("##IVolMacro",asFloat,ins->std.volMacroLen,0,NULL,0,volMax,ImVec2(400.0f*dpiScale,200.0f*dpiScale)); + if (ImGui::IsItemClicked(ImGuiMouseButton_Left)) { + macroDragStart=ImGui::GetItemRectMin(); + macroDragAreaSize=ImVec2(400.0f*dpiScale,200.0f*dpiScale); + macroDragMin=0; + macroDragMax=volMax; + macroDragLen=ins->std.volMacroLen; + macroDragActive=true; + macroDragTarget=ins->std.volMacro; + printf("Start.\n"); + } + ImGui::PlotHistogram("##IVolMacroLoop",loopIndicator,ins->std.volMacroLen,0,NULL,0,1,ImVec2(400.0f*dpiScale,16.0f*dpiScale)); + if (ImGui::IsItemClicked(ImGuiMouseButton_Left)) { + macroLoopDragStart=ImGui::GetItemRectMin(); + macroLoopDragAreaSize=ImVec2(400.0f*dpiScale,16.0f*dpiScale); + macroLoopDragLen=ins->std.volMacroLen; + macroLoopDragTarget=&ins->std.volMacroLoop; + macroLoopDragActive=true; + } + ImGui::PopStyleVar(); + if (ImGui::InputScalar("Length##IVolMacroL",ImGuiDataType_U8,&ins->std.volMacroLen,&_ONE,&_THREE)) { + if (ins->std.volMacroLen>127) ins->std.volMacroLen=127; + } + + // arp macro + ImGui::Separator(); + ImGui::Text("Arpeggio Macro"); + for (int i=0; istd.arpMacroLen; i++) { + asFloat[i]=ins->std.arpMacro[i]; + } + ImGui::PushStyleVar(ImGuiStyleVar_FramePadding,ImVec2(0.0f,0.0f)); + ImGui::PlotHistogram("##IArpMacro",asFloat,ins->std.arpMacroLen,0,NULL,-92,82,ImVec2(400.0f*dpiScale,200.0f*dpiScale)); + if (ImGui::IsItemClicked(ImGuiMouseButton_Left)) { + macroDragStart=ImGui::GetItemRectMin(); + macroDragAreaSize=ImVec2(400.0f*dpiScale,200.0f*dpiScale); + macroDragMin=-92; + macroDragMax=82; + macroDragLen=ins->std.arpMacroLen; + macroDragActive=true; + macroDragTarget=ins->std.arpMacro; + printf("Start.\n"); + } + ImGui::PopStyleVar(); + if (ImGui::InputScalar("Length##IArpMacroL",ImGuiDataType_U8,&ins->std.arpMacroLen,&_ONE,&_THREE)) { + if (ins->std.arpMacroLen>127) ins->std.arpMacroLen=127; + } } } } ImGui::End(); + if (ImGui::Begin("Pattern")) { + ImGui::Text("TODO"); + } + ImGui::End(); + SDL_RenderClear(sdlRend); ImGui::Render(); ImGui_ImplSDLRenderer_RenderDrawData(ImGui::GetDrawData()); @@ -138,11 +254,17 @@ bool FurnaceGUI::loop() { } bool FurnaceGUI::init() { + float dpiScaleF; + sdlWin=SDL_CreateWindow("Furnace",SDL_WINDOWPOS_CENTERED,SDL_WINDOWPOS_CENTERED,scrW*dpiScale,scrH*dpiScale,SDL_WINDOW_RESIZABLE|SDL_WINDOW_ALLOW_HIGHDPI); if (sdlWin==NULL) { logE("could not open window!\n"); return false; } + SDL_GetDisplayDPI(SDL_GetWindowDisplayIndex(sdlWin),&dpiScaleF,NULL,NULL); + dpiScale=round(dpiScaleF/96.0f); + if (dpiScale<1) dpiScale=1; + SDL_SetWindowSize(sdlWin,scrW*dpiScale,scrH*dpiScale); sdlRend=SDL_CreateRenderer(sdlWin,-1,SDL_RENDERER_ACCELERATED|SDL_RENDERER_PRESENTVSYNC|SDL_RENDERER_TARGETTEXTURE); @@ -178,5 +300,12 @@ FurnaceGUI::FurnaceGUI(): scrH(800), dpiScale(1), curIns(0), - curOctave(3) { + curOctave(3), + macroDragStart(0,0), + macroDragAreaSize(0,0), + macroDragTarget(NULL), + macroDragLen(0), + macroDragMin(0), + macroDragMax(0), + macroDragActive(false) { } diff --git a/src/gui/gui.h b/src/gui/gui.h index 00efda1dd..45af21cd3 100644 --- a/src/gui/gui.h +++ b/src/gui/gui.h @@ -21,6 +21,19 @@ class FurnaceGUI { int curIns, curOctave; + ImVec2 macroDragStart; + ImVec2 macroDragAreaSize; + int* macroDragTarget; + int macroDragLen; + int macroDragMin, macroDragMax; + bool macroDragActive; + + ImVec2 macroLoopDragStart; + ImVec2 macroLoopDragAreaSize; + signed char* macroLoopDragTarget; + int macroLoopDragLen; + bool macroLoopDragActive; + public: void bindEngine(DivEngine* eng); bool loop();