From 1941ca36164e5ece57eca79851e23ba1c961a791 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Sun, 20 Mar 2022 13:36:48 -0500 Subject: [PATCH 01/40] fix crash on sample preview at low rate --- src/engine/engine.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/engine/engine.cpp b/src/engine/engine.cpp index e3cf9f33..3a6812ad 100644 --- a/src/engine/engine.cpp +++ b/src/engine/engine.cpp @@ -1029,6 +1029,7 @@ void DivEngine::previewSample(int sample, int note) { rate=(song.tuning*pow(2.0,(double)(note+3)/12.0)*((double)song.sample[sample]->centerRate/8363.0)); if (rate<=0) rate=song.sample[sample]->rate; } + if (rate<100) rate=100; blip_set_rates(samp_bb,rate,got.rate); samp_prevSample=0; sPreview.pos=0; @@ -1057,7 +1058,9 @@ void DivEngine::previewWave(int wave, int note) { return; } blip_clear(samp_bb); - blip_set_rates(samp_bb,song.wave[wave]->len*((song.tuning*0.0625)*pow(2.0,(double)(note+3)/12.0)),got.rate); + double rate=song.wave[wave]->len*((song.tuning*0.0625)*pow(2.0,(double)(note+3)/12.0)); + if (rate<100) rate=100; + blip_set_rates(samp_bb,rate,got.rate); samp_prevSample=0; sPreview.pos=0; sPreview.sample=-1; From b04e1f2870c9ecc0ec034c773f260e8079c56f18 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Sun, 20 Mar 2022 18:25:48 -0500 Subject: [PATCH 02/40] GUI: implement sample draw --- src/engine/playback.cpp | 2 +- src/gui/gui.cpp | 45 +++++++++++++++++++++++++++++++++++++++-- src/gui/gui.h | 1 + src/gui/sampleEdit.cpp | 30 +++++++++++++++++++++++---- 4 files changed, 71 insertions(+), 7 deletions(-) diff --git a/src/engine/playback.cpp b/src/engine/playback.cpp index 16f883e9..cfa27f5d 100644 --- a/src/engine/playback.cpp +++ b/src/engine/playback.cpp @@ -1560,7 +1560,7 @@ void DivEngine::nextBuf(float** in, float** out, int inChans, int outChans, unsi return; } - logD("attempts: %d\n",attempts); + //logD("attempts: %d\n",attempts); if (attempts>=100) { logE("hang detected! stopping! at %d seconds %d micro\n",totalSeconds,totalTicks); freelance=false; diff --git a/src/gui/gui.cpp b/src/gui/gui.cpp index 57b152ac..d45f937a 100644 --- a/src/gui/gui.cpp +++ b/src/gui/gui.cpp @@ -5038,6 +5038,34 @@ void FurnaceGUI::processDrags(int dragX, int dragY) { modified=true; } } + if (sampleDragActive) { + int x=samplePos+(int)(double(dragX-sampleDragStart.x)*sampleZoom); + int x1=samplePos+(int)(double(dragX-sampleDragStart.x+1)*sampleZoom); + if (x<0) x=0; + if (x>=(int)sampleDragLen) x=sampleDragLen-1; + if (x1<0) x1=0; + if (x1>=(int)sampleDragLen) x1=sampleDragLen-1; + double y=0.5-double(dragY-sampleDragStart.y)/sampleDragAreaSize.y; + if (sampleDragMode) { // draw + if (sampleDrag16) { + int val=y*65536; + if (val<-32768) val=-32768; + if (val>32767) val=32767; + for (int i=x; i<=x1; i++) ((short*)sampleDragTarget)[i]=val; + } else { + int val=y*256; + if (val<-128) val=-128; + if (val>127) val=127; + for (int i=x; i<=x1; i++) ((signed char*)sampleDragTarget)[i]=val; + } + updateSampleTex=true; + } else { // select + if (sampleSelStart<0) { + sampleSelStart=x; + } + sampleSelEnd=x; + } + } } #define sysAddOption(x) \ @@ -5287,7 +5315,7 @@ bool FurnaceGUI::loop() { addScroll(1); } } - if (macroDragActive || macroLoopDragActive || waveDragActive) { + if (macroDragActive || macroLoopDragActive || waveDragActive || sampleDragActive) { int distance=fabs(motionXrel); if (distance<1) distance=1; float start=motionX-motionXrel; @@ -5304,7 +5332,7 @@ bool FurnaceGUI::loop() { break; } case SDL_MOUSEBUTTONUP: - if (macroDragActive || macroLoopDragActive || waveDragActive) modified=true; + if (macroDragActive || macroLoopDragActive || waveDragActive || (sampleDragActive && sampleDragMode)) modified=true; macroDragActive=false; macroDragBitMode=false; macroDragInitialValue=false; @@ -5313,6 +5341,19 @@ bool FurnaceGUI::loop() { macroDragLastY=-1; macroLoopDragActive=false; waveDragActive=false; + if (sampleDragActive) { + logD("stopping sample drag\n"); + if (sampleDragMode) { + e->renderSamplesP(); + } else { + if (sampleSelStart>sampleSelEnd) { + sampleSelStart^=sampleSelEnd; + sampleSelEnd^=sampleSelStart; + sampleSelStart^=sampleSelEnd; + } + } + } + sampleDragActive=false; if (selecting) { cursor=selEnd; finishSelection(); diff --git a/src/gui/gui.h b/src/gui/gui.h index 0edbea95..5ffb0181 100644 --- a/src/gui/gui.h +++ b/src/gui/gui.h @@ -747,6 +747,7 @@ class FurnaceGUI { void* sampleDragTarget; ImVec2 sampleDragStart; ImVec2 sampleDragAreaSize; + unsigned int sampleDragLen; // visualizer float keyHit[DIV_MAX_CHANS]; diff --git a/src/gui/sampleEdit.cpp b/src/gui/sampleEdit.cpp index f63080cf..f90f3d9c 100644 --- a/src/gui/sampleEdit.cpp +++ b/src/gui/sampleEdit.cpp @@ -320,7 +320,11 @@ void FurnaceGUI::drawSampleEdit() { if (xCoarse>=sample->samples) break; int y1, y2; int totalAdvance=0; - y1=((unsigned short)sample->data16[xCoarse]^0x8000)*availY/65536; + if (sample->depth==8) { + y1=((unsigned char)sample->data8[xCoarse]^0x80)*availY/256; + } else { + y1=((unsigned short)sample->data16[xCoarse]^0x8000)*availY/65536; + } xFine+=xAdvanceFine; if (xFine>=16777216) { xFine-=16777216; @@ -329,14 +333,22 @@ void FurnaceGUI::drawSampleEdit() { totalAdvance+=xAdvanceCoarse; if (xCoarse>=sample->samples) break; do { - y2=((unsigned short)sample->data16[xCoarse]^0x8000)*availY/65536; + if (sample->depth==8) { + y2=((unsigned char)sample->data8[xCoarse]^0x80)*availY/256; + } else { + y2=((unsigned short)sample->data16[xCoarse]^0x8000)*availY/65536; + } if (y1>y2) { y2^=y1; y1^=y2; y2^=y1; } + if (y1<0) y1=0; + if (y1>=availY) y1=availY-1; + if (y2<0) y2=0; + if (y2>=availY) y2=availY-1; for (int j=y1; j<=y2; j++) { - data[i+availX*j]=lineColor; + data[i+availX*(availY-j-1)]=lineColor; } if (totalAdvance>0) xCoarse++; } while ((totalAdvance--)>0); @@ -348,7 +360,17 @@ void FurnaceGUI::drawSampleEdit() { ImGui::ImageButton(sampleTex,avail,ImVec2(0,0),ImVec2(1,1),0); if (ImGui::IsItemClicked()) { - logD("drawing\n"); + if (sample->samples>0 && (sample->depth==16 || sample->depth==8)) { + sampleDragStart=ImGui::GetItemRectMin(); + sampleDragAreaSize=ImGui::GetItemRectSize(); + sampleDrag16=(sample->depth==16); + sampleDragTarget=(sample->depth==16)?((void*)sample->data16):((void*)sample->data8); + sampleDragLen=sample->samples; + sampleDragActive=true; + sampleSelStart=-1; + sampleSelEnd=-1; + processDrags(ImGui::GetMousePos().x,ImGui::GetMousePos().y); + } } String statusBar=sampleDragMode?"Draw":"Select"; From ef3bf8f92445b8ef8cd52d23a43f1be347ba7f2d Mon Sep 17 00:00:00 2001 From: tildearrow Date: Sun, 20 Mar 2022 18:31:25 -0500 Subject: [PATCH 03/40] GUI: display sample selection - NOT FINISHED --- src/gui/sampleEdit.cpp | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/src/gui/sampleEdit.cpp b/src/gui/sampleEdit.cpp index f90f3d9c..7594d1fe 100644 --- a/src/gui/sampleEdit.cpp +++ b/src/gui/sampleEdit.cpp @@ -373,9 +373,10 @@ void FurnaceGUI::drawSampleEdit() { } } String statusBar=sampleDragMode?"Draw":"Select"; + bool drawSelection=false; if (!sampleDragMode) { - if (sampleSelStart>=0 && sampleSelEnd>=0) { + if (sampleSelStart>=0 && sampleSelEnd>=0 && sampleSelStart!=sampleSelEnd) { int start=sampleSelStart; int end=sampleSelEnd; if (start>end) { @@ -384,6 +385,7 @@ void FurnaceGUI::drawSampleEdit() { start^=end; } statusBar+=fmt::sprintf(" (%d-%d)",start,end); + drawSelection=true; } } @@ -403,6 +405,24 @@ void FurnaceGUI::drawSampleEdit() { statusBar+=fmt::sprintf(" | (%d, %d)",posX,posY); } } + + if (drawSelection) { + int start=sampleSelStart; + int end=sampleSelEnd; + if (start>end) { + start^=end; + end^=start; + start^=end; + } + ImDrawList* dl=ImGui::GetWindowDrawList(); + ImVec2 p1=ImGui::GetItemRectMin(); + p1.x+=start/sampleZoom-samplePos; + + ImVec2 p2=ImVec2(ImGui::GetItemRectMin().x+end/sampleZoom-samplePos,ImGui::GetItemRectMax().y); + + // TODO: color + dl->AddRectFilled(p1,p2,0xc0c0c0c0); + } ImGui::Text("%s",statusBar.c_str()); } From 21b15d686bc137399230e1659df782200e58bfe8 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Sun, 20 Mar 2022 22:16:20 -0500 Subject: [PATCH 04/40] GUI: prevent typing out-of-range notes cope --- src/gui/gui.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/gui/gui.cpp b/src/gui/gui.cpp index d45f937a..b25253d1 100644 --- a/src/gui/gui.cpp +++ b/src/gui/gui.cpp @@ -4367,6 +4367,9 @@ void FurnaceGUI::keyDown(SDL_Event& ev) { int key=noteKeys.at(ev.key.keysym.scancode); int num=12*curOctave+key; + if (num<-60) num=-60; // C-(-5) + if (num>119) num=119; // B-9 + if (edit) { // TODO: separate when adding MIDI input. DivPattern* pat=e->song.pat[cursor.xCoarse].getPattern(e->song.orders.ord[cursor.xCoarse][e->getOrder()],true); From 20799402c8dc5ba94b672f832428422d55b28da5 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Sun, 20 Mar 2022 22:22:10 -0500 Subject: [PATCH 05/40] GUI: select all is vertical now --- src/gui/gui.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/gui/gui.cpp b/src/gui/gui.cpp index b25253d1..4e8699db 100644 --- a/src/gui/gui.cpp +++ b/src/gui/gui.cpp @@ -2742,7 +2742,7 @@ void FurnaceGUI::doSelectAll() { } float aspect=float(selEndX-selStartX+1)/float(selEnd.y-selStart.y+1); - if (aspect<1.0f && !(selStart.y==0 && selEnd.y==e->song.patLen-1)) { // up-down + if (aspect<=1.0f && !(selStart.y==0 && selEnd.y==e->song.patLen-1)) { // up-down selStart.y=0; selEnd.y=e->song.patLen-1; } else { // left-right From b83b46aa2c007229b456891b32f93ea37b48d72d Mon Sep 17 00:00:00 2001 From: tildearrow Date: Mon, 21 Mar 2022 00:41:18 -0500 Subject: [PATCH 06/40] GUI: more sample editor work scrollbar and some layout changes --- src/gui/gui.cpp | 21 ++++++-- src/gui/gui.h | 3 +- src/gui/sampleEdit.cpp | 115 +++++++++++++++++++++++++++++++---------- 3 files changed, 108 insertions(+), 31 deletions(-) diff --git a/src/gui/gui.cpp b/src/gui/gui.cpp index 4e8699db..65f1996b 100644 --- a/src/gui/gui.cpp +++ b/src/gui/gui.cpp @@ -1364,6 +1364,7 @@ void FurnaceGUI::actualSampleList() { ImGui::TableNextColumn(); if (ImGui::Selectable(fmt::sprintf("%d: %s##_SAM%d",i,sample->name,i).c_str(),curSample==i)) { curSample=i; + samplePos=0; updateSampleTex=true; } if (ImGui::IsItemHovered()) { @@ -2052,6 +2053,8 @@ void FurnaceGUI::drawNewSong() { curNibble=false; orderNibble=false; orderCursor=-1; + samplePos=0; + updateSampleTex=true; selStart=SelectionPoint(); selEnd=SelectionPoint(); cursor=SelectionPoint(); @@ -4946,6 +4949,8 @@ int FurnaceGUI::load(String path) { curNibble=false; orderNibble=false; orderCursor=-1; + samplePos=0; + updateSampleTex=true; selStart=SelectionPoint(); selEnd=SelectionPoint(); cursor=SelectionPoint(); @@ -5042,10 +5047,14 @@ void FurnaceGUI::processDrags(int dragX, int dragY) { } } if (sampleDragActive) { - int x=samplePos+(int)(double(dragX-sampleDragStart.x)*sampleZoom); - int x1=samplePos+(int)(double(dragX-sampleDragStart.x+1)*sampleZoom); + int x=samplePos+round(double(dragX-sampleDragStart.x)*sampleZoom); + int x1=samplePos+round(double(dragX-sampleDragStart.x+1)*sampleZoom); if (x<0) x=0; - if (x>=(int)sampleDragLen) x=sampleDragLen-1; + if (sampleDragMode) { + if (x>=(int)sampleDragLen) x=sampleDragLen-1; + } else { + if (x>(int)sampleDragLen) x=sampleDragLen; + } if (x1<0) x1=0; if (x1>=(int)sampleDragLen) x1=sampleDragLen-1; double y=0.5-double(dragY-sampleDragStart.y)/sampleDragAreaSize.y; @@ -6660,6 +6669,10 @@ void FurnaceGUI::applyUISettings() { sty.Colors[ImGuiCol_PlotHistogram]=uiColors[GUI_COLOR_MACRO_OTHER]; sty.Colors[ImGuiCol_PlotHistogramHovered]=uiColors[GUI_COLOR_MACRO_OTHER]; + /*sty.WindowRounding=8.0f; + sty.FrameRounding=6.0f; + sty.PopupRounding=8.0f;*/ + sty.ScaleAllSizes(dpiScale); ImGui::GetStyle()=sty; @@ -7143,6 +7156,7 @@ FurnaceGUI::FurnaceGUI(): randomMode(false), oldOrdersLen(0), sampleZoom(1.0), + prevSampleZoom(1.0), samplePos(0), resizeSize(1024), resampleTarget(32000), @@ -7153,6 +7167,7 @@ FurnaceGUI::FurnaceGUI(): sampleDragActive(false), sampleDragMode(false), sampleDrag16(false), + sampleZoomAuto(true), sampleDragTarget(NULL), sampleDragStart(0,0), sampleDragAreaSize(0,0) { diff --git a/src/gui/gui.h b/src/gui/gui.h index 5ffb0181..0ab87ef8 100644 --- a/src/gui/gui.h +++ b/src/gui/gui.h @@ -737,13 +737,14 @@ class FurnaceGUI { // sample editor specific double sampleZoom; + double prevSampleZoom; int samplePos; int resizeSize; double resampleTarget; int resampleStrat; float amplifyVol; int sampleSelStart, sampleSelEnd; - bool sampleDragActive, sampleDragMode, sampleDrag16; + bool sampleDragActive, sampleDragMode, sampleDrag16, sampleZoomAuto; void* sampleDragTarget; ImVec2 sampleDragStart; ImVec2 sampleDragAreaSize; diff --git a/src/gui/sampleEdit.cpp b/src/gui/sampleEdit.cpp index 7594d1fe..429273f6 100644 --- a/src/gui/sampleEdit.cpp +++ b/src/gui/sampleEdit.cpp @@ -18,6 +18,7 @@ */ #include "gui.h" #include +#include #include #include "../ta-log.h" #include "IconsFontAwesome4.h" @@ -110,28 +111,12 @@ void FurnaceGUI::drawSampleEdit() { ImGui::EndTable(); } - if (ImGui::InputDouble("Zoom",&sampleZoom,0.1,2.0)) { - if (sampleZoom<0.01) sampleZoom=0.01; - updateSampleTex=true; - } - if (ImGui::InputInt("Pos",&samplePos,1,10)) { - if (samplePos>=(int)sample->samples) samplePos=sample->samples-1; - if (samplePos<0) samplePos=0; - updateSampleTex=true; - } - /* if (ImGui::Button("Apply")) { e->renderSamplesP(); } ImGui::SameLine(); - if (ImGui::Button(ICON_FA_VOLUME_UP "##PreviewSample")) { - e->previewSample(curSample); - } - ImGui::SameLine(); - if (ImGui::Button(ICON_FA_VOLUME_OFF "##StopSample")) { - e->stopSamplePreview(); - }*/ + */ ImGui::Separator(); if (ImGui::Button(ICON_FA_I_CURSOR "##SSelect")) { @@ -274,13 +259,71 @@ void FurnaceGUI::drawSampleEdit() { ImGui::SetTooltip("Apply filter"); } + ImGui::SameLine(); + ImGui::Dummy(ImVec2(4.0*dpiScale,dpiScale)); + ImGui::SameLine(); + if (ImGui::Button(ICON_FA_VOLUME_UP "##PreviewSample")) { + e->previewSample(curSample); + } + if (ImGui::IsItemHovered()) { + ImGui::SetTooltip("Preview sample"); + } + ImGui::SameLine(); + if (ImGui::Button(ICON_FA_VOLUME_OFF "##StopSample")) { + e->stopSamplePreview(); + } + if (ImGui::IsItemHovered()) { + ImGui::SetTooltip("Stop sample preview"); + } + + ImGui::SameLine(); + double zoomPercent=100.0/sampleZoom; + ImGui::Text("Zoom"); + ImGui::SameLine(); + ImGui::SetNextItemWidth(150.0f*dpiScale); + if (ImGui::InputDouble("##SZoom",&zoomPercent,5.0,20.0,"%g%%")) { + if (zoomPercent>10000.0) zoomPercent=10000.0; + if (zoomPercent<1.0) zoomPercent=1.0; + sampleZoom=100.0/zoomPercent; + if (sampleZoom<0.01) sampleZoom=0.01; + sampleZoomAuto=false; + updateSampleTex=true; + } + ImGui::SameLine(); + if (sampleZoomAuto) { + if (ImGui::Button("100%")) { + sampleZoom=1.0; + sampleZoomAuto=false; + updateSampleTex=true; + } + } else { + if (ImGui::Button("Auto")) { + sampleZoomAuto=true; + updateSampleTex=true; + } + } + ImGui::Separator(); ImVec2 avail=ImGui::GetContentRegionAvail(); - avail.y-=ImGui::GetFontSize()+ImGui::GetStyle().ItemSpacing.y; + avail.y-=ImGui::GetFontSize()+ImGui::GetStyle().ItemSpacing.y+ImGui::GetStyle().ScrollbarSize; int availX=avail.x; int availY=avail.y; + + if (sampleZoomAuto) { + samplePos=0; + if (sample->samples<1 || avail.x<=0) { + sampleZoom=1.0; + } else { + sampleZoom=(double)sample->samples/avail.x; + } + if (sampleZoom!=prevSampleZoom) { + prevSampleZoom=sampleZoom; + updateSampleTex=true; + } + } + if (sampleTex==NULL || sampleTexW!=avail.x || sampleTexH!=avail.y) { if (sampleTex!=NULL) { SDL_DestroyTexture(sampleTex); @@ -359,10 +402,15 @@ void FurnaceGUI::drawSampleEdit() { } ImGui::ImageButton(sampleTex,avail,ImVec2(0,0),ImVec2(1,1),0); + + ImVec2 rectMin=ImGui::GetItemRectMin(); + ImVec2 rectMax=ImGui::GetItemRectMax(); + ImVec2 rectSize=ImGui::GetItemRectSize(); + if (ImGui::IsItemClicked()) { if (sample->samples>0 && (sample->depth==16 || sample->depth==8)) { - sampleDragStart=ImGui::GetItemRectMin(); - sampleDragAreaSize=ImGui::GetItemRectSize(); + sampleDragStart=rectMin; + sampleDragAreaSize=rectSize; sampleDrag16=(sample->depth==16); sampleDragTarget=(sample->depth==16)?((void*)sample->data16):((void*)sample->data8); sampleDragLen=sample->samples; @@ -393,14 +441,14 @@ void FurnaceGUI::drawSampleEdit() { int posX=-1; int posY=0; ImVec2 pos=ImGui::GetMousePos(); - pos.x-=ImGui::GetItemRectMin().x; - pos.y-=ImGui::GetItemRectMin().y; + pos.x-=rectMin.x; + pos.y-=rectMin.y; if (sampleZoom>0) { posX=samplePos+pos.x*sampleZoom; if (posX>(int)sample->samples) posX=-1; } - posY=(0.5-pos.y/ImGui::GetItemRectSize().y)*((sample->depth==8)?255:32767); + posY=(0.5-pos.y/rectSize.y)*((sample->depth==8)?255:32767); if (posX>=0) { statusBar+=fmt::sprintf(" | (%d, %d)",posX,posY); } @@ -415,15 +463,28 @@ void FurnaceGUI::drawSampleEdit() { start^=end; } ImDrawList* dl=ImGui::GetWindowDrawList(); - ImVec2 p1=ImGui::GetItemRectMin(); + ImVec2 p1=rectMin; p1.x+=start/sampleZoom-samplePos; - ImVec2 p2=ImVec2(ImGui::GetItemRectMin().x+end/sampleZoom-samplePos,ImGui::GetItemRectMax().y); + ImVec2 p2=ImVec2(rectMin.x+end/sampleZoom-samplePos,rectMax.y); + ImVec4 selColor=uiColors[GUI_COLOR_ACCENT_SECONDARY]; + selColor.w*=0.25; - // TODO: color - dl->AddRectFilled(p1,p2,0xc0c0c0c0); + dl->AddRectFilled(p1,p2,ImGui::GetColorU32(selColor)); + } + + ImS64 scrollV=samplePos; + ImS64 availV=round(rectSize.x*sampleZoom); + ImS64 contentsV=MAX(sample->samples,MAX(availV,1)); + + if (ImGui::ScrollbarEx(ImRect(ImVec2(rectMin.x,rectMax.y),ImVec2(rectMax.x,rectMax.y+ImGui::GetStyle().ScrollbarSize)),ImGui::GetID("sampleScroll"),ImGuiAxis_X,&scrollV,availV,contentsV,0)) { + if (!sampleZoomAuto && samplePos!=scrollV) { + samplePos=scrollV; + updateSampleTex=true; + } } + ImGui::SetCursorPosY(ImGui::GetCursorPosY()+ImGui::GetStyle().ScrollbarSize); ImGui::Text("%s",statusBar.c_str()); } From a68dbed7608f9f703dabf1ca2b70de60f6e6affb Mon Sep 17 00:00:00 2001 From: tildearrow Date: Mon, 21 Mar 2022 00:52:38 -0500 Subject: [PATCH 07/40] GUI: add options for rounded UI elements --- src/gui/gui.cpp | 18 ++++++++++++++---- src/gui/gui.h | 8 +++++++- src/gui/settings.cpp | 26 ++++++++++++++++++++++++++ 3 files changed, 47 insertions(+), 5 deletions(-) diff --git a/src/gui/gui.cpp b/src/gui/gui.cpp index 65f1996b..bdee67fb 100644 --- a/src/gui/gui.cpp +++ b/src/gui/gui.cpp @@ -1976,6 +1976,12 @@ void FurnaceGUI::drawDebug() { } ImGui::TreePop(); } + if (ImGui::TreeNode("User Interface")) { + if (ImGui::Button("Inspect")) { + inspectorOpen=!inspectorOpen; + } + ImGui::TreePop(); + } if (ImGui::TreeNode("Settings")) { if (ImGui::Button("Sync")) syncSettings(); ImGui::SameLine(); @@ -6140,7 +6146,7 @@ bool FurnaceGUI::loop() { drawChannels(); drawRegView(); - //ImGui::ShowMetricsWindow(); + if (inspectorOpen) ImGui::ShowMetricsWindow(&inspectorOpen); if (firstFrame) { firstFrame=false; @@ -6669,9 +6675,12 @@ void FurnaceGUI::applyUISettings() { sty.Colors[ImGuiCol_PlotHistogram]=uiColors[GUI_COLOR_MACRO_OTHER]; sty.Colors[ImGuiCol_PlotHistogramHovered]=uiColors[GUI_COLOR_MACRO_OTHER]; - /*sty.WindowRounding=8.0f; - sty.FrameRounding=6.0f; - sty.PopupRounding=8.0f;*/ + if (settings.roundedWindows) sty.WindowRounding=8.0f; + if (settings.roundedButtons) { + sty.FrameRounding=6.0f; + sty.GrabRounding=6.0f; + } + if (settings.roundedMenus) sty.PopupRounding=8.0f; sty.ScaleAllSizes(dpiScale); @@ -7072,6 +7081,7 @@ FurnaceGUI::FurnaceGUI(): settingsOpen(false), mixerOpen(false), debugOpen(false), + inspectorOpen(false), oscOpen(true), volMeterOpen(true), statsOpen(false), diff --git a/src/gui/gui.h b/src/gui/gui.h index 0ab87ef8..91a4e440 100644 --- a/src/gui/gui.h +++ b/src/gui/gui.h @@ -567,6 +567,9 @@ class FurnaceGUI { int unifiedDataView; int sysFileDialog; // end + int roundedWindows; + int roundedButtons; + int roundedMenus; unsigned int maxUndoSteps; String mainFontPath; String patFontPath; @@ -613,6 +616,9 @@ class FurnaceGUI { stepOnInsert(0), unifiedDataView(0), sysFileDialog(1), + roundedWindows(1), + roundedButtons(1), + roundedMenus(0), maxUndoSteps(100), mainFontPath(""), patFontPath(""), @@ -625,7 +631,7 @@ class FurnaceGUI { int loopOrder, loopRow, loopEnd, isClipping, extraChannelButtons, patNameTarget, newSongCategory; bool editControlsOpen, ordersOpen, insListOpen, songInfoOpen, patternOpen, insEditOpen; bool waveListOpen, waveEditOpen, sampleListOpen, sampleEditOpen, aboutOpen, settingsOpen; - bool mixerOpen, debugOpen, oscOpen, volMeterOpen, statsOpen, compatFlagsOpen; + bool mixerOpen, debugOpen, inspectorOpen, oscOpen, volMeterOpen, statsOpen, compatFlagsOpen; bool pianoOpen, notesOpen, channelsOpen, regViewOpen; SelectionPoint selStart, selEnd, cursor; bool selecting, curNibble, orderNibble, followOrders, followPattern, changeAllOrders; diff --git a/src/gui/settings.cpp b/src/gui/settings.cpp index aa342ae9..be1b6b16 100644 --- a/src/gui/settings.cpp +++ b/src/gui/settings.cpp @@ -449,6 +449,23 @@ void FurnaceGUI::drawSettings() { ImGui::Separator(); + bool roundedWindowsB=settings.roundedWindows; + if (ImGui::Checkbox("Rounded window corners",&roundedWindowsB)) { + settings.roundedWindows=roundedWindowsB; + } + + bool roundedButtonsB=settings.roundedButtons; + if (ImGui::Checkbox("Rounded buttons",&roundedButtonsB)) { + settings.roundedButtons=roundedButtonsB; + } + + bool roundedMenusB=settings.roundedMenus; + if (ImGui::Checkbox("Rounded menu corners",&roundedMenusB)) { + settings.roundedMenus=roundedMenusB; + } + + ImGui::Separator(); + if (ImGui::TreeNode("Color scheme")) { if (ImGui::TreeNode("General")) { ImGui::Text("Color scheme type:"); @@ -904,6 +921,9 @@ void FurnaceGUI::syncSettings() { settings.stepOnInsert=e->getConfInt("stepOnInsert",0); settings.unifiedDataView=e->getConfInt("unifiedDataView",0); settings.sysFileDialog=e->getConfInt("sysFileDialog",1); + settings.roundedWindows=e->getConfInt("roundedWindows",1); + settings.roundedButtons=e->getConfInt("roundedButtons",1); + settings.roundedMenus=e->getConfInt("roundedMenus",0); clampSetting(settings.mainFontSize,2,96); clampSetting(settings.patFontSize,2,96); @@ -944,6 +964,9 @@ void FurnaceGUI::syncSettings() { clampSetting(settings.stepOnInsert,0,1); clampSetting(settings.unifiedDataView,0,1); clampSetting(settings.sysFileDialog,0,1); + clampSetting(settings.roundedWindows,0,1); + clampSetting(settings.roundedButtons,0,1); + clampSetting(settings.roundedMenus,0,1); // keybinds LOAD_KEYBIND(GUI_ACTION_OPEN,FURKMOD_CMD|SDLK_o); @@ -1145,6 +1168,9 @@ void FurnaceGUI::commitSettings() { e->setConf("stepOnInsert",settings.stepOnInsert); e->setConf("unifiedDataView",settings.unifiedDataView); e->setConf("sysFileDialog",settings.sysFileDialog); + e->setConf("roundedWindows",settings.roundedWindows); + e->setConf("roundedButtons",settings.roundedButtons); + e->setConf("roundedMenus",settings.roundedMenus); PUT_UI_COLOR(GUI_COLOR_BACKGROUND); PUT_UI_COLOR(GUI_COLOR_FRAME_BACKGROUND); From 2df7658fd0ee00d5d67aef6ea7a8dd1f2bff5b7e Mon Sep 17 00:00:00 2001 From: tildearrow Date: Mon, 21 Mar 2022 02:43:52 -0500 Subject: [PATCH 08/40] GUI: implement sample sel operations plenty of them --- src/engine/sample.cpp | 79 ++++++++++++++ src/engine/sample.h | 18 ++++ src/gui/sampleEdit.cpp | 232 ++++++++++++++++++++++++++++++++++++++--- 3 files changed, 313 insertions(+), 16 deletions(-) diff --git a/src/engine/sample.cpp b/src/engine/sample.cpp index 8b0c78e5..be9c5029 100644 --- a/src/engine/sample.cpp +++ b/src/engine/sample.cpp @@ -179,6 +179,85 @@ bool DivSample::resize(unsigned int count) { return false; } +bool DivSample::strip(unsigned int begin, unsigned int end) { + if (begin>samples) begin=samples; + if (end>samples) end=samples; + int count=samples-(end-begin); + if (count<=0) return resize(0); + if (depth==8) { + if (data8!=NULL) { + signed char* oldData8=data8; + data8=NULL; + initInternal(8,count); + if (begin>0) { + memcpy(data8,oldData8,begin); + } + if (samples-end>0) { + memcpy(data8+begin,oldData8+end,samples-end); + } + delete[] oldData8; + } else { + // do nothing + return true; + } + samples=count; + return true; + } else if (depth==16) { + if (data16!=NULL) { + short* oldData16=data16; + data16=NULL; + initInternal(16,count); + if (begin>0) { + memcpy(data16,oldData16,sizeof(short)*begin); + } + if (samples-end>0) { + memcpy(&(data16[begin]),&(oldData16[end]),sizeof(short)*(samples-end)); + } + delete[] oldData16; + } else { + // do nothing + return true; + } + samples=count; + return true; + } + return false; +} + +bool DivSample::trim(unsigned int begin, unsigned int end) { + int count=end-begin; + if (count==0) return true; + if (begin==0 && end==samples) return true; + if (depth==8) { + if (data8!=NULL) { + signed char* oldData8=data8; + data8=NULL; + initInternal(8,count); + memcpy(data8,oldData8+begin,count); + delete[] oldData8; + } else { + // do nothing + return true; + } + samples=count; + return true; + } else if (depth==16) { + if (data16!=NULL) { + short* oldData16=data16; + data16=NULL; + initInternal(16,count); + memcpy(data16,&(oldData16[begin]),sizeof(short)*count); + delete[] oldData16; + } else { + // do nothing + return true; + } + samples=count; + return true; + } + return false; +} + #define RESAMPLE_BEGIN \ if (samples<1) return true; \ int finalCount=(double)samples*(r/(double)rate); \ diff --git a/src/engine/sample.h b/src/engine/sample.h index 4737a00d..7e4d76be 100644 --- a/src/engine/sample.h +++ b/src/engine/sample.h @@ -102,6 +102,24 @@ struct DivSample { */ bool resize(unsigned int count); + /** + * remove part of the sample data. + * @warning do not attempt to strip a sample outside of a synchronized block! + * @param start the beginning. + * @param end the end. + * @return whether it was successful. + */ + bool strip(unsigned int begin, unsigned int end); + + /** + * clip the sample data to specified boundaries. + * @warning do not attempt to trim a sample outside of a synchronized block! + * @param start the beginning. + * @param end the end. + * @return whether it was successful. + */ + bool trim(unsigned int begin, unsigned int end); + /** * change the sample rate. * @warning do not attempt to resample outside of a synchronized block! diff --git a/src/gui/sampleEdit.cpp b/src/gui/sampleEdit.cpp index 429273f6..19062189 100644 --- a/src/gui/sampleEdit.cpp +++ b/src/gui/sampleEdit.cpp @@ -26,6 +26,19 @@ #include #include "guiConst.h" +#define SAMPLE_OP_BEGIN \ + unsigned int start=0; \ + unsigned int end=sample->samples; \ + if (sampleSelStart!=-1 && sampleSelEnd!=-1 && sampleSelStart!=sampleSelEnd) { \ + start=sampleSelStart; \ + end=sampleSelEnd; \ + if (start>end) { \ + start^=end; \ + end^=start; \ + start^=end; \ + } \ + } \ + void FurnaceGUI::drawSampleEdit() { if (nextWindow==GUI_WINDOW_SAMPLE_EDIT) { sampleEditOpen=true; @@ -223,33 +236,214 @@ void FurnaceGUI::drawSampleEdit() { } ImGui::SameLine(); ImGui::Text("(%.1fdB)",20.0*log10(amplifyVol/100.0f)); - ImGui::Button("Apply"); + if (ImGui::Button("Apply")) { + e->synchronized([this,sample]() { + SAMPLE_OP_BEGIN; + float vol=amplifyVol/100.0f; + + if (sample->depth==16) { + for (unsigned int i=start; idata16[i]*vol; + if (val<-32768) val=-32768; + if (val>32767) val=32767; + sample->data16[i]=val; + } + } else if (sample->depth==8) { + for (unsigned int i=start; idata8[i]*vol; + if (val<-128) val=-128; + if (val>127) val=127; + sample->data8[i]=val; + } + } + + updateSampleTex=true; + + e->renderSamples(); + }); + ImGui::CloseCurrentPopup(); + } ImGui::EndPopup(); } ImGui::SameLine(); - ImGui::Button(ICON_FA_ARROWS_V "##SNormalize"); + if (ImGui::Button(ICON_FA_ARROWS_V "##SNormalize")) { + e->synchronized([this,sample]() { + SAMPLE_OP_BEGIN; + float maxVal=0.0f; + + if (sample->depth==16) { + for (unsigned int i=start; idata16[i]/32767.0f); + if (val>maxVal) maxVal=val; + } + if (maxVal>1.0f) maxVal=1.0f; + if (maxVal>0.0f) { + float vol=1.0f/maxVal; + for (unsigned int i=start; idata16[i]*vol; + if (val<-32768) val=-32768; + if (val>32767) val=32767; + sample->data16[i]=val; + } + } + } else if (sample->depth==8) { + for (unsigned int i=start; idata8[i]/127.0f); + if (val>maxVal) maxVal=val; + } + if (maxVal>1.0f) maxVal=1.0f; + if (maxVal>0.0f) { + float vol=1.0f/maxVal; + for (unsigned int i=start; idata8[i]*vol; + if (val<-128) val=-128; + if (val>127) val=127; + sample->data8[i]=val; + } + } + } + + updateSampleTex=true; + + e->renderSamples(); + }); + } if (ImGui::IsItemHovered()) { ImGui::SetTooltip("Normalize"); } ImGui::SameLine(); - ImGui::Button(ICON_FA_ERASER "##SSilence"); + if (ImGui::Button(ICON_FA_ERASER "##SSilence")) { + e->synchronized([this,sample]() { + SAMPLE_OP_BEGIN; + + if (sample->depth==16) { + for (unsigned int i=start; idata16[i]=0; + } + } else if (sample->depth==8) { + for (unsigned int i=start; idata8[i]=0; + } + } + + updateSampleTex=true; + + e->renderSamples(); + }); + } if (ImGui::IsItemHovered()) { ImGui::SetTooltip("Apply silence"); } ImGui::SameLine(); + if (ImGui::Button(ICON_FA_TIMES "##SDelete")) { + e->synchronized([this,sample]() { + SAMPLE_OP_BEGIN; + + sample->strip(start,end); + updateSampleTex=true; + + e->renderSamples(); + }); + sampleSelStart=-1; + sampleSelEnd=-1; + } + if (ImGui::IsItemHovered()) { + ImGui::SetTooltip("Delete"); + } + ImGui::SameLine(); + if (ImGui::Button(ICON_FA_CROP "##STrim")) { + e->synchronized([this,sample]() { + SAMPLE_OP_BEGIN; + + sample->trim(start,end); + updateSampleTex=true; + + e->renderSamples(); + }); + sampleSelStart=-1; + sampleSelEnd=-1; + } + if (ImGui::IsItemHovered()) { + ImGui::SetTooltip("Trim"); + } + ImGui::SameLine(); ImGui::Dummy(ImVec2(4.0*dpiScale,dpiScale)); ImGui::SameLine(); - ImGui::Button(ICON_FA_BACKWARD "##SReverse"); + if (ImGui::Button(ICON_FA_BACKWARD "##SReverse")) { + e->synchronized([this,sample]() { + SAMPLE_OP_BEGIN; + + if (sample->depth==16) { + for (unsigned int i=start; idata16[i]^=sample->data16[ri]; + sample->data16[ri]^=sample->data16[i]; + sample->data16[i]^=sample->data16[ri]; + } + } else if (sample->depth==8) { + for (unsigned int i=start; idata8[i]^=sample->data8[ri]; + sample->data8[ri]^=sample->data8[i]; + sample->data8[i]^=sample->data8[ri]; + } + } + + updateSampleTex=true; + + e->renderSamples(); + }); + } if (ImGui::IsItemHovered()) { ImGui::SetTooltip("Reverse"); } ImGui::SameLine(); - ImGui::Button(ICON_FA_SORT_AMOUNT_ASC "##SInvert"); + if (ImGui::Button(ICON_FA_SORT_AMOUNT_ASC "##SInvert")) { + e->synchronized([this,sample]() { + SAMPLE_OP_BEGIN; + + if (sample->depth==16) { + for (unsigned int i=start; idata16[i]=-sample->data16[i]; + if (sample->data16[i]==-32768) sample->data16[i]=32767; + } + } else if (sample->depth==8) { + for (unsigned int i=start; idata8[i]=-sample->data8[i]; + if (sample->data16[i]==-128) sample->data16[i]=127; + } + } + + updateSampleTex=true; + + e->renderSamples(); + }); + } if (ImGui::IsItemHovered()) { ImGui::SetTooltip("Invert"); } ImGui::SameLine(); - ImGui::Button(ICON_FA_LEVEL_DOWN "##SSign"); + if (ImGui::Button(ICON_FA_LEVEL_DOWN "##SSign")) { + e->synchronized([this,sample]() { + SAMPLE_OP_BEGIN; + + if (sample->depth==16) { + for (unsigned int i=start; idata16[i]^=0x8000; + } + } else if (sample->depth==8) { + for (unsigned int i=start; idata8[i]^=0x80; + } + } + + updateSampleTex=true; + + e->renderSamples(); + }); + } if (ImGui::IsItemHovered()) { ImGui::SetTooltip("Signed/unsigned exchange"); } @@ -408,16 +602,22 @@ void FurnaceGUI::drawSampleEdit() { ImVec2 rectSize=ImGui::GetItemRectSize(); if (ImGui::IsItemClicked()) { - if (sample->samples>0 && (sample->depth==16 || sample->depth==8)) { - sampleDragStart=rectMin; - sampleDragAreaSize=rectSize; - sampleDrag16=(sample->depth==16); - sampleDragTarget=(sample->depth==16)?((void*)sample->data16):((void*)sample->data8); - sampleDragLen=sample->samples; - sampleDragActive=true; - sampleSelStart=-1; - sampleSelEnd=-1; - processDrags(ImGui::GetMousePos().x,ImGui::GetMousePos().y); + if (ImGui::IsMouseDoubleClicked(ImGuiMouseButton_Left)) { + sampleDragActive=false; + sampleSelStart=0; + sampleSelEnd=sample->samples; + } else { + if (sample->samples>0 && (sample->depth==16 || sample->depth==8)) { + sampleDragStart=rectMin; + sampleDragAreaSize=rectSize; + sampleDrag16=(sample->depth==16); + sampleDragTarget=(sample->depth==16)?((void*)sample->data16):((void*)sample->data8); + sampleDragLen=sample->samples; + sampleDragActive=true; + sampleSelStart=-1; + sampleSelEnd=-1; + processDrags(ImGui::GetMousePos().x,ImGui::GetMousePos().y); + } } } String statusBar=sampleDragMode?"Draw":"Select"; From b45d2be312d2dd1d6ed2d25caa0c14fcdf5bada5 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Mon, 21 Mar 2022 03:45:20 -0500 Subject: [PATCH 09/40] GUI: sample editor almost complete only keybinds are missing --- src/gui/gui.cpp | 10 ++- src/gui/gui.h | 2 + src/gui/sampleEdit.cpp | 159 +++++++++++++++++++++++++++++++++++++++++ 3 files changed, 170 insertions(+), 1 deletion(-) diff --git a/src/gui/gui.cpp b/src/gui/gui.cpp index bdee67fb..b1dfc987 100644 --- a/src/gui/gui.cpp +++ b/src/gui/gui.cpp @@ -7180,7 +7180,15 @@ FurnaceGUI::FurnaceGUI(): sampleZoomAuto(true), sampleDragTarget(NULL), sampleDragStart(0,0), - sampleDragAreaSize(0,0) { + sampleDragAreaSize(0,0), + sampleDragLen(0), + sampleFilterL(1.0f), + sampleFilterB(0.0f), + sampleFilterH(0.0f), + sampleFilterRes(0.25f), + sampleFilterCutStart(16000.0f), + sampleFilterCutEnd(100.0f), + sampleFilterPower(1) { // octave 1 /* diff --git a/src/gui/gui.h b/src/gui/gui.h index 91a4e440..e51bc254 100644 --- a/src/gui/gui.h +++ b/src/gui/gui.h @@ -755,6 +755,8 @@ class FurnaceGUI { ImVec2 sampleDragStart; ImVec2 sampleDragAreaSize; unsigned int sampleDragLen; + float sampleFilterL, sampleFilterB, sampleFilterH, sampleFilterRes, sampleFilterCutStart, sampleFilterCutEnd; + unsigned char sampleFilterPower; // visualizer float keyHit[DIV_MAX_CHANS]; diff --git a/src/gui/sampleEdit.cpp b/src/gui/sampleEdit.cpp index 19062189..1af7ddf6 100644 --- a/src/gui/sampleEdit.cpp +++ b/src/gui/sampleEdit.cpp @@ -312,6 +312,64 @@ void FurnaceGUI::drawSampleEdit() { ImGui::SetTooltip("Normalize"); } ImGui::SameLine(); + if (ImGui::Button(ICON_FA_ARROW_UP "##SFadeIn")) { + e->synchronized([this,sample]() { + SAMPLE_OP_BEGIN; + + if (sample->depth==16) { + for (unsigned int i=start; idata16[i]*float(i-start)/float(end-start); + if (val<-32768) val=-32768; + if (val>32767) val=32767; + sample->data16[i]=val; + } + } else if (sample->depth==8) { + for (unsigned int i=start; idata8[i]*float(i-start)/float(end-start); + if (val<-128) val=-128; + if (val>127) val=127; + sample->data8[i]=val; + } + } + + updateSampleTex=true; + + e->renderSamples(); + }); + } + if (ImGui::IsItemHovered()) { + ImGui::SetTooltip("Fade in"); + } + ImGui::SameLine(); + if (ImGui::Button(ICON_FA_ARROW_DOWN "##SFadeOut")) { + e->synchronized([this,sample]() { + SAMPLE_OP_BEGIN; + + if (sample->depth==16) { + for (unsigned int i=start; idata16[i]*float(end-i)/float(end-start); + if (val<-32768) val=-32768; + if (val>32767) val=32767; + sample->data16[i]=val; + } + } else if (sample->depth==8) { + for (unsigned int i=start; idata8[i]*float(end-i)/float(end-start); + if (val<-128) val=-128; + if (val>127) val=127; + sample->data8[i]=val; + } + } + + updateSampleTex=true; + + e->renderSamples(); + }); + } + if (ImGui::IsItemHovered()) { + ImGui::SetTooltip("Fade out"); + } + ImGui::SameLine(); if (ImGui::Button(ICON_FA_ERASER "##SSilence")) { e->synchronized([this,sample]() { SAMPLE_OP_BEGIN; @@ -452,7 +510,108 @@ void FurnaceGUI::drawSampleEdit() { if (ImGui::IsItemHovered()) { ImGui::SetTooltip("Apply filter"); } + if (ImGui::BeginPopupContextItem("SFilterOpt",ImGuiPopupFlags_MouseButtonLeft)) { + float lowP=sampleFilterL*100.0f; + float bandP=sampleFilterB*100.0f; + float highP=sampleFilterH*100.0f; + float resP=sampleFilterRes*100.0f; + ImGui::Text("Cutoff:"); + if (ImGui::SliderFloat("From",&sampleFilterCutStart,0.0f,sample->rate*0.5,"%.0fHz")) { + if (sampleFilterCutStart<0.0) sampleFilterCutStart=0.0; + if (sampleFilterCutStart>sample->rate*0.5) sampleFilterCutStart=sample->rate*0.5; + } + if (ImGui::SliderFloat("To",&sampleFilterCutEnd,0.0f,sample->rate*0.5,"%.0fHz")) { + if (sampleFilterCutEnd<0.0) sampleFilterCutEnd=0.0; + if (sampleFilterCutEnd>sample->rate*0.5) sampleFilterCutEnd=sample->rate*0.5; + } + ImGui::Separator(); + if (ImGui::SliderFloat("Resonance",&resP,0.0f,99.0f,"%.1f%%")) { + sampleFilterRes=resP/100.0f; + if (sampleFilterRes<0.0f) sampleFilterRes=0.0f; + if (sampleFilterRes>0.99f) sampleFilterRes=0.99f; + } + ImGui::Text("Power"); + ImGui::SameLine(); + if (ImGui::RadioButton("1x",sampleFilterPower==1)) { + sampleFilterPower=1; + } + ImGui::SameLine(); + if (ImGui::RadioButton("2x",sampleFilterPower==2)) { + sampleFilterPower=2; + } + ImGui::SameLine(); + if (ImGui::RadioButton("3x",sampleFilterPower==3)) { + sampleFilterPower=3; + } + ImGui::Separator(); + if (ImGui::SliderFloat("Low-pass",&lowP,0.0f,100.0f,"%.1f%%")) { + sampleFilterL=lowP/100.0f; + if (sampleFilterL<0.0f) sampleFilterL=0.0f; + if (sampleFilterL>1.0f) sampleFilterL=1.0f; + } + if (ImGui::SliderFloat("Band-pass",&bandP,0.0f,100.0f,"%.1f%%")) { + sampleFilterB=bandP/100.0f; + if (sampleFilterB<0.0f) sampleFilterB=0.0f; + if (sampleFilterB>1.0f) sampleFilterB=1.0f; + } + if (ImGui::SliderFloat("High-pass",&highP,0.0f,100.0f,"%.1f%%")) { + sampleFilterH=highP/100.0f; + if (sampleFilterH<0.0f) sampleFilterH=0.0f; + if (sampleFilterH>1.0f) sampleFilterH=1.0f; + } + if (ImGui::Button("Apply")) { + e->synchronized([this,sample]() { + SAMPLE_OP_BEGIN; + float res=1.0-pow(sampleFilterRes,0.5f); + float low=0; + float band=0; + float high=0; + + double power=(sampleFilterCutStart>sampleFilterCutEnd)?0.5:2.0; + + if (sample->depth==16) { + for (unsigned int i=start; irate))*M_PI); + + for (int j=0; jdata16[i])-low-(res*band); + band=cut*high+band; + } + + float val=low*sampleFilterL+band*sampleFilterB+high*sampleFilterH; + if (val<-32768) val=-32768; + if (val>32767) val=32767; + sample->data16[i]=val; + } + } else if (sample->depth==8) { + for (unsigned int i=start; irate))*M_PI); + + for (int j=0; jdata8[i])-low-(res*band); + band=cut*high+band; + } + + float val=low*sampleFilterL+band*sampleFilterB+high*sampleFilterH; + if (val<-128) val=-128; + if (val>127) val=127; + sample->data8[i]=val; + } + } + + updateSampleTex=true; + + e->renderSamples(); + }); + ImGui::CloseCurrentPopup(); + } + ImGui::EndPopup(); + } ImGui::SameLine(); ImGui::Dummy(ImVec2(4.0*dpiScale,dpiScale)); ImGui::SameLine(); From 47c5c34e1c15f7e742e109138c1c696871f458aa Mon Sep 17 00:00:00 2001 From: Natt Akuma Date: Mon, 21 Mar 2022 21:02:51 +0700 Subject: [PATCH 10/40] Add PET support --- CMakeLists.txt | 1 + src/engine/dispatchContainer.cpp | 4 + src/engine/platform/pet.cpp | 311 +++++++++++++++++++++++++++++++ src/engine/platform/pet.h | 84 +++++++++ src/engine/playback.cpp | 1 + src/gui/gui.cpp | 3 + src/gui/insEdit.cpp | 9 +- 7 files changed, 410 insertions(+), 3 deletions(-) create mode 100644 src/engine/platform/pet.cpp create mode 100644 src/engine/platform/pet.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 6116dba8..b3d1fc37 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -320,6 +320,7 @@ src/engine/platform/lynx.cpp src/engine/platform/swan.cpp src/engine/platform/vera.cpp src/engine/platform/bubsyswsg.cpp +src/engine/platform/pet.cpp src/engine/platform/dummy.cpp ) diff --git a/src/engine/dispatchContainer.cpp b/src/engine/dispatchContainer.cpp index f80ed13b..be429c35 100644 --- a/src/engine/dispatchContainer.cpp +++ b/src/engine/dispatchContainer.cpp @@ -46,6 +46,7 @@ #include "platform/swan.h" #include "platform/lynx.h" #include "platform/bubsyswsg.h" +#include "platform/pet.h" #include "platform/dummy.h" #include "../ta-log.h" #include "song.h" @@ -275,6 +276,9 @@ void DivDispatchContainer::init(DivSystem sys, DivEngine* eng, int chanCount, do case DIV_SYSTEM_BUBSYS_WSG: dispatch=new DivPlatformBubSysWSG; break; + case DIV_SYSTEM_PET: + dispatch=new DivPlatformPET; + break; default: logW("this system is not supported yet! using dummy platform.\n"); dispatch=new DivPlatformDummy; diff --git a/src/engine/platform/pet.cpp b/src/engine/platform/pet.cpp new file mode 100644 index 00000000..bbc0562c --- /dev/null +++ b/src/engine/platform/pet.cpp @@ -0,0 +1,311 @@ +/** + * Furnace Tracker - multi-system chiptune tracker + * Copyright (C) 2021-2022 tildearrow and contributors + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include "pet.h" +#include "../engine.h" +#include + +#define rWrite(a,v) {regPool[(a)]=(v)&0xff; if((a)==10) {chan.sreg=(v); chan.cnt=2;}} + +#define CHIP_DIVIDER 16 +#define SAMP_DIVIDER 4 + +const char* regCheatSheet6522[]={ + "T2L", "08", + "SR", "0A", + "ACR", "0B", + NULL +}; + +const char** DivPlatformPET::getRegisterSheet() { + return regCheatSheet6522; +} + +const char* DivPlatformPET::getEffectName(unsigned char effect) { + switch (effect) { + case 0x10: + return "10xx: Change waveform"; + break; + } + return NULL; +} + +void DivPlatformPET::acquire(short* bufL, short* bufR, size_t start, size_t len) { + // high-level emulation of 6522 shift register for now + int t2=regPool[8]*2+4; + if (((regPool[11]>>2)&7)==4) { + for (size_t h=start; h0) { + int adv=MIN(cycs,chan.cnt); + chan.cnt-=adv; + cycs-=adv; + if (chan.cnt==0) { + chan.out=(chan.sreg&1)*32767; + chan.sreg=(chan.sreg>>1)|((chan.sreg&1)<<7); + chan.cnt=t2; + } + } + bufL[h]=chan.out; + bufR[h]=chan.out; + } + } else { + chan.out=0; + for (size_t h=start; hgetWave(chan.wave); + if (wt->max<1 || wt->len<1) { + rWrite(10,0); + } else { + unsigned char sr=0; + for (int i=0; i<8; i++) { + sr=(sr<<1)|((wt->data[i*wt->len/8]*2)/(wt->max+1)); + } + rWrite(10,sr); + } +} + +void DivPlatformPET::writeOutVol() { + if (chan.active && !isMuted && chan.outVol>0) { + if (regPool[11]!=16) { + rWrite(11,16); + rWrite(10,regPool[10]); + } + } else { + rWrite(11,0); + } +} + +void DivPlatformPET::tick() { + chan.std.next(); + if (chan.std.hadVol) { + chan.outVol=chan.std.vol&chan.vol; + writeOutVol(); + } + if (chan.std.hadArp) { + if (!chan.inPorta) { + if (chan.std.arpMode) { + chan.baseFreq=NOTE_PERIODIC(chan.std.arp); + } else { + chan.baseFreq=NOTE_PERIODIC(chan.note+chan.std.arp); + } + } + chan.freqChanged=true; + } else { + if (chan.std.arpMode && chan.std.finishedArp) { + chan.baseFreq=NOTE_PERIODIC(chan.note); + chan.freqChanged=true; + } + } + if (chan.std.hadWave) { + if (chan.wave!=chan.std.wave) { + chan.wave=chan.std.wave; + updateWave(); + } + } + if (chan.freqChanged || chan.keyOn || chan.keyOff) { + chan.freq=parent->calcFreq(chan.baseFreq,chan.pitch,true); + if (chan.freq>257) chan.freq=257; + if (chan.freq<2) chan.freq=2; + rWrite(8,chan.freq-2); + if (chan.keyOn) { + if (!chan.std.willVol) { + chan.outVol=chan.vol; + writeOutVol(); + } + if (chan.wave<0) { + chan.wave=0; + updateWave(); + } + chan.keyOn=false; + } + if (chan.keyOff) { + rWrite(11,0); + chan.keyOff=false; + } + chan.freqChanged=false; + } +} + +int DivPlatformPET::dispatch(DivCommand c) { + switch (c.cmd) { + case DIV_CMD_NOTE_ON: { + DivInstrument* ins=parent->getIns(chan.ins); + if (c.value!=DIV_NOTE_NULL) { + chan.baseFreq=NOTE_PERIODIC(c.value); + chan.freqChanged=true; + chan.note=c.value; + } + chan.active=true; + chan.keyOn=true; + chan.std.init(ins); + break; + } + case DIV_CMD_NOTE_OFF: + chan.active=false; + chan.keyOff=true; + chan.std.init(NULL); + break; + case DIV_CMD_NOTE_OFF_ENV: + case DIV_CMD_ENV_RELEASE: + chan.std.release(); + break; + case DIV_CMD_INSTRUMENT: + if (chan.ins!=c.value || c.value2==1) { + chan.ins=c.value; + } + break; + case DIV_CMD_VOLUME: + if (chan.vol!=c.value) { + chan.vol=c.value; + if (!chan.std.hadVol) { + chan.outVol=chan.vol; + writeOutVol(); + } + } + break; + case DIV_CMD_GET_VOLUME: + return chan.vol; + break; + case DIV_CMD_PITCH: + chan.pitch=c.value; + chan.freqChanged=true; + break; + case DIV_CMD_WAVE: + chan.wave=c.value; + updateWave(); + chan.keyOn=true; + break; + case DIV_CMD_NOTE_PORTA: { + int destFreq=NOTE_PERIODIC(c.value2); + bool return2=false; + if (destFreq>chan.baseFreq) { + chan.baseFreq+=c.value; + if (chan.baseFreq>=destFreq) { + chan.baseFreq=destFreq; + return2=true; + } + } else { + chan.baseFreq-=c.value; + if (chan.baseFreq<=destFreq) { + chan.baseFreq=destFreq; + return2=true; + } + } + chan.freqChanged=true; + if (return2) { + chan.inPorta=false; + return 2; + } + break; + } + case DIV_CMD_LEGATO: + chan.baseFreq=NOTE_PERIODIC(c.value+((chan.std.willArp && !chan.std.arpMode)?(chan.std.arp):(0))); + chan.freqChanged=true; + chan.note=c.value; + break; + case DIV_CMD_PRE_PORTA: + if (chan.active && c.value2) { + if (parent->song.resetMacroOnPorta) chan.std.init(parent->getIns(chan.ins)); + } + chan.inPorta=c.value; + break; + case DIV_CMD_GET_VOLMAX: + return 1; + break; + case DIV_ALWAYS_SET_VOLUME: + return 1; + break; + default: + break; + } + return 1; +} + +void DivPlatformPET::muteChannel(int ch, bool mute) { + isMuted=mute; + writeOutVol(); +} + +void DivPlatformPET::forceIns() { + chan.insChanged=true; + chan.freqChanged=true; + updateWave(); + writeOutVol(); +} + +void* DivPlatformPET::getChanState(int ch) { + return &chan; +} + +unsigned char* DivPlatformPET::getRegisterPool() { + return regPool; +} + +int DivPlatformPET::getRegisterPoolSize() { + return 16; +} + +void DivPlatformPET::reset() { + memset(regPool,0,16); + chan=Channel(); + chan.vol=1; +} + +bool DivPlatformPET::isStereo() { + return false; +} + +void DivPlatformPET::notifyWaveChange(int wave) { + if (chan.wave==wave) { + updateWave(); + } +} + +void DivPlatformPET::notifyInsDeletion(void* ins) { + chan.std.notifyInsDeletion((DivInstrument*)ins); +} + +void DivPlatformPET::poke(unsigned int addr, unsigned short val) { + rWrite(addr,val); +} + +void DivPlatformPET::poke(std::vector& wlist) { + for (DivRegWrite& i: wlist) rWrite(i.addr,i.val); +} + +int DivPlatformPET::init(DivEngine* p, int channels, int sugRate, unsigned int flags) { + parent=p; + dumpWrites=false; + skipRegisterWrites=false; + chipClock=1000000; + rate=chipClock/SAMP_DIVIDER; // = 250000kHz + isMuted=false; + reset(); + return 1; +} + +DivPlatformPET::~DivPlatformPET() { +} diff --git a/src/engine/platform/pet.h b/src/engine/platform/pet.h new file mode 100644 index 00000000..bc0a815b --- /dev/null +++ b/src/engine/platform/pet.h @@ -0,0 +1,84 @@ +/** + * Furnace Tracker - multi-system chiptune tracker + * Copyright (C) 2021-2022 tildearrow and contributors + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#ifndef _PET_H +#define _PET_H + +#include "../dispatch.h" +#include "../macroInt.h" + +class DivPlatformPET: public DivDispatch { + struct Channel { + int freq, baseFreq, pitch, note; + unsigned char ins; + bool active, insChanged, freqChanged, keyOn, keyOff, inPorta; + int vol, outVol, wave; + unsigned char sreg; + int cnt; + short out; + DivMacroInt std; + Channel(): + freq(0), + baseFreq(0), + pitch(0), + note(0), + ins(-1), + active(false), + insChanged(true), + freqChanged(false), + keyOn(false), + keyOff(false), + inPorta(false), + vol(1), + outVol(1), + wave(-1), + sreg(0), + cnt(0), + out(0) {} + }; + Channel chan; + bool isMuted; + + unsigned char regPool[16]; + void updateWave(); + friend void putDispatchChan(void*,int,int); + public: + void acquire(short* bufL, short* bufR, size_t start, size_t len); + int dispatch(DivCommand c); + void* getChanState(int chan); + unsigned char* getRegisterPool(); + int getRegisterPoolSize(); + void reset(); + void forceIns(); + void tick(); + void muteChannel(int ch, bool mute); + void notifyWaveChange(int wave); + void notifyInsDeletion(void* ins); + bool isStereo(); + void poke(unsigned int addr, unsigned short val); + void poke(std::vector& wlist); + const char** getRegisterSheet(); + const char* getEffectName(unsigned char effect); + int init(DivEngine* parent, int channels, int sugRate, unsigned int flags); + ~DivPlatformPET(); + private: + void writeOutVol(); +}; + +#endif diff --git a/src/engine/playback.cpp b/src/engine/playback.cpp index cfa27f5d..cf3e1309 100644 --- a/src/engine/playback.cpp +++ b/src/engine/playback.cpp @@ -314,6 +314,7 @@ bool DivEngine::perSystemEffect(int ch, unsigned char effect, unsigned char effe } break; case DIV_SYSTEM_BUBSYS_WSG: + case DIV_SYSTEM_PET: switch (effect) { case 0x10: // select waveform dispatchCmd(DivCommand(DIV_CMD_WAVE,ch,effectVal)); diff --git a/src/gui/gui.cpp b/src/gui/gui.cpp index b1dfc987..3bb8c568 100644 --- a/src/gui/gui.cpp +++ b/src/gui/gui.cpp @@ -5575,6 +5575,7 @@ bool FurnaceGUI::loop() { sysAddOption(DIV_SYSTEM_SWAN); sysAddOption(DIV_SYSTEM_VERA); sysAddOption(DIV_SYSTEM_BUBSYS_WSG); + sysAddOption(DIV_SYSTEM_PET); ImGui::EndMenu(); } if (ImGui::BeginMenu("configure system...")) { @@ -5918,6 +5919,7 @@ bool FurnaceGUI::loop() { case DIV_SYSTEM_YM2610B: case DIV_SYSTEM_YM2610B_EXT: case DIV_SYSTEM_YMU759: + case DIV_SYSTEM_PET: ImGui::Text("nothing to configure"); break; default: @@ -5973,6 +5975,7 @@ bool FurnaceGUI::loop() { sysChangeOption(i,DIV_SYSTEM_SWAN); sysChangeOption(i,DIV_SYSTEM_VERA); sysChangeOption(i,DIV_SYSTEM_BUBSYS_WSG); + sysChangeOption(i,DIV_SYSTEM_PET); ImGui::EndMenu(); } } diff --git a/src/gui/insEdit.cpp b/src/gui/insEdit.cpp index def41aad..5d1e23ca 100644 --- a/src/gui/insEdit.cpp +++ b/src/gui/insEdit.cpp @@ -1550,6 +1550,9 @@ void FurnaceGUI::drawInsEdit() { if (ins->type==DIV_INS_GB) { volMax=0; } + if (ins->type==DIV_INS_PET) { + volMax=1; + } bool arpMode=ins->std.arpMacroMode; @@ -1579,7 +1582,7 @@ void FurnaceGUI::drawInsEdit() { if (ins->type==DIV_INS_AY8930) { dutyMax=255; } - if (ins->type==DIV_INS_TIA || ins->type==DIV_INS_AMIGA || ins->type==DIV_INS_SCC) { + if (ins->type==DIV_INS_TIA || ins->type==DIV_INS_AMIGA || ins->type==DIV_INS_SCC || ins->type==DIV_INS_PET) { dutyMax=0; } if (ins->type==DIV_INS_PCE) { @@ -2002,7 +2005,7 @@ void FurnaceGUI::drawWaveEdit() { DivWavetable* wave=e->song.wave[curWave]; ImGui::Text("Width"); if (ImGui::IsItemHovered()) { - ImGui::SetTooltip("use a width of 32 on Game Boy, PC Engine and WonderSwan.\nany other widths will be scaled during playback."); + ImGui::SetTooltip("use a width of:\n- 8 for PET\n- 32 on Game Boy, PC Engine and WonderSwan.\nany other widths will be scaled during playback."); } ImGui::SameLine(); ImGui::SetNextItemWidth(128.0f*dpiScale); @@ -2016,7 +2019,7 @@ void FurnaceGUI::drawWaveEdit() { ImGui::SameLine(); ImGui::Text("Height"); if (ImGui::IsItemHovered()) { - ImGui::SetTooltip("use a height of:\n- 15 for Game Boy and WonderSwan\n- 31 for PC Engine\nany other heights will be scaled during playback."); + ImGui::SetTooltip("use a height of:\n- 1 for PET\n- 15 for Game Boy and WonderSwan\n- 31 for PC Engine\nany other heights will be scaled during playback."); } ImGui::SameLine(); ImGui::SetNextItemWidth(128.0f*dpiScale); From c32ea592853c68e419c3c305c571c8eb34530abc Mon Sep 17 00:00:00 2001 From: Natt Akuma Date: Tue, 22 Mar 2022 01:37:22 +0700 Subject: [PATCH 11/40] PET: Use wave bits macro instead --- src/engine/platform/pet.cpp | 32 +++----------------------------- src/engine/platform/pet.h | 4 +--- src/gui/insEdit.cpp | 7 ++++++- 3 files changed, 10 insertions(+), 33 deletions(-) diff --git a/src/engine/platform/pet.cpp b/src/engine/platform/pet.cpp index bbc0562c..b89f9510 100644 --- a/src/engine/platform/pet.cpp +++ b/src/engine/platform/pet.cpp @@ -74,24 +74,11 @@ void DivPlatformPET::acquire(short* bufL, short* bufR, size_t start, size_t len) } } -void DivPlatformPET::updateWave() { - DivWavetable* wt=parent->getWave(chan.wave); - if (wt->max<1 || wt->len<1) { - rWrite(10,0); - } else { - unsigned char sr=0; - for (int i=0; i<8; i++) { - sr=(sr<<1)|((wt->data[i*wt->len/8]*2)/(wt->max+1)); - } - rWrite(10,sr); - } -} - void DivPlatformPET::writeOutVol() { if (chan.active && !isMuted && chan.outVol>0) { if (regPool[11]!=16) { rWrite(11,16); - rWrite(10,regPool[10]); + rWrite(10,chan.wave); } } else { rWrite(11,0); @@ -122,7 +109,7 @@ void DivPlatformPET::tick() { if (chan.std.hadWave) { if (chan.wave!=chan.std.wave) { chan.wave=chan.std.wave; - updateWave(); + rWrite(10,chan.wave); } } if (chan.freqChanged || chan.keyOn || chan.keyOff) { @@ -135,10 +122,6 @@ void DivPlatformPET::tick() { chan.outVol=chan.vol; writeOutVol(); } - if (chan.wave<0) { - chan.wave=0; - updateWave(); - } chan.keyOn=false; } if (chan.keyOff) { @@ -195,8 +178,7 @@ int DivPlatformPET::dispatch(DivCommand c) { break; case DIV_CMD_WAVE: chan.wave=c.value; - updateWave(); - chan.keyOn=true; + rWrite(10,chan.wave); break; case DIV_CMD_NOTE_PORTA: { int destFreq=NOTE_PERIODIC(c.value2); @@ -252,7 +234,6 @@ void DivPlatformPET::muteChannel(int ch, bool mute) { void DivPlatformPET::forceIns() { chan.insChanged=true; chan.freqChanged=true; - updateWave(); writeOutVol(); } @@ -271,19 +252,12 @@ int DivPlatformPET::getRegisterPoolSize() { void DivPlatformPET::reset() { memset(regPool,0,16); chan=Channel(); - chan.vol=1; } bool DivPlatformPET::isStereo() { return false; } -void DivPlatformPET::notifyWaveChange(int wave) { - if (chan.wave==wave) { - updateWave(); - } -} - void DivPlatformPET::notifyInsDeletion(void* ins) { chan.std.notifyInsDeletion((DivInstrument*)ins); } diff --git a/src/engine/platform/pet.h b/src/engine/platform/pet.h index bc0a815b..3b6af48d 100644 --- a/src/engine/platform/pet.h +++ b/src/engine/platform/pet.h @@ -47,7 +47,7 @@ class DivPlatformPET: public DivDispatch { inPorta(false), vol(1), outVol(1), - wave(-1), + wave(0b00001111), sreg(0), cnt(0), out(0) {} @@ -56,7 +56,6 @@ class DivPlatformPET: public DivDispatch { bool isMuted; unsigned char regPool[16]; - void updateWave(); friend void putDispatchChan(void*,int,int); public: void acquire(short* bufL, short* bufR, size_t start, size_t len); @@ -68,7 +67,6 @@ class DivPlatformPET: public DivDispatch { void forceIns(); void tick(); void muteChannel(int ch, bool mute); - void notifyWaveChange(int wave); void notifyInsDeletion(void* ins); bool isStereo(); void poke(unsigned int addr, unsigned short val); diff --git a/src/gui/insEdit.cpp b/src/gui/insEdit.cpp index 5d1e23ca..1143e4fe 100644 --- a/src/gui/insEdit.cpp +++ b/src/gui/insEdit.cpp @@ -1612,8 +1612,13 @@ void FurnaceGUI::drawInsEdit() { if (ins->type==DIV_INS_SAA1099) waveMax=2; if (ins->type==DIV_INS_FM || ins->type==DIV_INS_OPLL || ins->type==DIV_INS_OPL || ins->type==DIV_INS_OPZ) waveMax=0; if (ins->type==DIV_INS_MIKEY) waveMax=0; + if (ins->type==DIV_INS_PET) { + waveMax=8; + bitMode=true; + } - const char** waveNames=ayShapeBits; + const char** waveNames=NULL; + if (ins->type==DIV_INS_AY || ins->type==DIV_INS_AY8930 || ins->type==DIV_INS_SAA1099) waveNames=ayShapeBits; if (ins->type==DIV_INS_C64) waveNames=c64ShapeBits; int ex1Max=(ins->type==DIV_INS_AY8930)?8:0; From 8c961f0aae9953d8696b86946aae1639566bc576 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Mon, 21 Mar 2022 14:11:28 -0500 Subject: [PATCH 12/40] add backup functionality - UNTESTED --- src/engine/engine.h | 3 ++- src/engine/fileOps.cpp | 8 ++++--- src/gui/gui.cpp | 47 ++++++++++++++++++++++++++++++++++++++++++ src/gui/gui.h | 8 +++++++ 4 files changed, 62 insertions(+), 4 deletions(-) diff --git a/src/engine/engine.h b/src/engine/engine.h index ba6a770e..958508a3 100644 --- a/src/engine/engine.h +++ b/src/engine/engine.h @@ -278,7 +278,8 @@ class DivEngine { // save as .dmf. SafeWriter* saveDMF(unsigned char version); // save as .fur. - SafeWriter* saveFur(); + // if notPrimary is true then the song will not be altered + SafeWriter* saveFur(bool notPrimary=false); // build a ROM file (TODO). // specify system to build ROM for. SafeWriter* buildROM(int sys); diff --git a/src/engine/fileOps.cpp b/src/engine/fileOps.cpp index 23570c47..afe5df63 100644 --- a/src/engine/fileOps.cpp +++ b/src/engine/fileOps.cpp @@ -1728,7 +1728,7 @@ bool DivEngine::load(unsigned char* f, size_t slen) { return false; } -SafeWriter* DivEngine::saveFur() { +SafeWriter* DivEngine::saveFur(bool notPrimary) { int insPtr[256]; int wavePtr[256]; int samplePtr[256]; @@ -1736,8 +1736,10 @@ SafeWriter* DivEngine::saveFur() { size_t ptrSeek; warnings=""; - song.isDMF=false; - song.version=DIV_ENGINE_VERSION; + if (!notPrimary) { + song.isDMF=false; + song.version=DIV_ENGINE_VERSION; + } SafeWriter* w=new SafeWriter; w->init(); diff --git a/src/gui/gui.cpp b/src/gui/gui.cpp index b1dfc987..eb62f5d1 100644 --- a/src/gui/gui.cpp +++ b/src/gui/gui.cpp @@ -52,11 +52,13 @@ extern "C" { #include #include "../utfutils.h" #define LAYOUT_INI "\\layout.ini" +#define BACKUP_FUR "\\backup.fur" #else #include #include #include #define LAYOUT_INI "/layout.ini" +#define BACKUP_FUR "/backup.fur" #endif bool Particle::update(float frameTime) { @@ -4802,11 +4804,13 @@ void FurnaceGUI::openFileDialog(FurnaceGUIFileDialogs type) { int FurnaceGUI::save(String path, int dmfVersion) { SafeWriter* w; + backupLock.lock(); if (dmfVersion) { w=e->saveDMF(dmfVersion); } else { w=e->saveFur(); } + backupLock.unlock(); if (w==NULL) { lastError=e->getLastError(); return 3; @@ -6411,6 +6415,41 @@ bool FurnaceGUI::loop() { ImGui::EndPopup(); } + // backup trigger + if (modified) { + if (backupTimer>0) { + backupTimer-=ImGui::GetIO().DeltaTime; + if (backupTimer<=0) { + backupTask=std::async(std::launch::async,[this]() -> bool { + if (backupPath==curFileName) { + logD("backup file open. not saving backup.\n"); + return true; + } + logD("saving backup...\n"); + backupLock.lock(); + SafeWriter* w=e->saveFur(true); + backupLock.unlock(); + + if (w!=NULL) { + FILE* outFile=ps_fopen(backupPath.c_str(),"wb"); + if (outFile!=NULL) { + if (fwrite(w->getFinalBuf(),1,w->size(),outFile)!=w->size()) { + logW("did not write backup entirely: %s!\n",strerror(errno)); + fclose(outFile); + w->finish(); + } + } else { + logW("could not save backup: %s!\n",strerror(errno)); + w->finish(); + } + } + backupTimer=30.0; + return true; + }); + } + } + } + SDL_SetRenderDrawColor(sdlRend,uiColors[GUI_COLOR_BACKGROUND].x*255, uiColors[GUI_COLOR_BACKGROUND].y*255, uiColors[GUI_COLOR_BACKGROUND].z*255, @@ -6930,6 +6969,7 @@ bool FurnaceGUI::init() { } strncpy(finalLayoutPath,(e->getConfigPath()+String(LAYOUT_INI)).c_str(),4095); + backupPath=e->getConfigPath()+String(BACKUP_FUR); prepareLayout(); ImGui::GetIO().ConfigFlags|=ImGuiConfigFlags_DockingEnable; @@ -6969,6 +7009,7 @@ bool FurnaceGUI::init() { #ifdef __APPLE__ SDL_RaiseWindow(sdlWin); #endif + return true; } @@ -7020,6 +7061,11 @@ bool FurnaceGUI::finish() { for (int i=0; i #include #include +#include +#include #include #include "fileDialog.h" @@ -512,6 +514,11 @@ class FurnaceGUI { double aboutScroll, aboutSin; float aboutHue; + double backupTimer; + std::future backupTask; + std::mutex backupLock; + String backupPath; + ImFont* mainFont; ImFont* iconFont; ImFont* patFont; @@ -875,6 +882,7 @@ class FurnaceGUI { void updateScroll(int amount); void addScroll(int amount); void setFileName(String name); + void runBackupThread(); bool loop(); bool finish(); bool init(); From 854d0fea488efd2ef9bd69c4de8e1b8aa634bf03 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Mon, 21 Mar 2022 14:32:33 -0500 Subject: [PATCH 13/40] GUI: add option to restore backup --- src/gui/gui.cpp | 20 +++++++++++++++++++- src/gui/gui.h | 2 ++ src/gui/settings.cpp | 3 +++ 3 files changed, 24 insertions(+), 1 deletion(-) diff --git a/src/gui/gui.cpp b/src/gui/gui.cpp index eb62f5d1..42c12ed6 100644 --- a/src/gui/gui.cpp +++ b/src/gui/gui.cpp @@ -3672,6 +3672,15 @@ void FurnaceGUI::doAction(int what) { openFileDialog(GUI_FILE_OPEN); } break; + case GUI_ACTION_OPEN_BACKUP: + if (modified) { + showWarning("Unsaved changes! Are you sure?",GUI_WARN_OPEN_BACKUP); + } else { + if (load(backupPath)>0) { + showError("No backup available! (or unable to open it)"); + } + } + break; case GUI_ACTION_SAVE: if (curFileName=="" || e->song.version>=0xff00) { openFileDialog(GUI_FILE_SAVE); @@ -5993,6 +6002,10 @@ bool FurnaceGUI::loop() { ImGui::EndMenu(); } ImGui::Separator(); + if (ImGui::MenuItem("restore backup",BIND_FOR(GUI_ACTION_OPEN_BACKUP))) { + doAction(GUI_ACTION_OPEN_BACKUP); + } + ImGui::Separator(); if (ImGui::MenuItem("exit")) { if (modified) { showWarning("Unsaved changes! Are you sure you want to quit?",GUI_WARN_QUIT); @@ -6392,6 +6405,11 @@ bool FurnaceGUI::loop() { case GUI_WARN_OPEN: openFileDialog(GUI_FILE_OPEN); break; + case GUI_WARN_OPEN_BACKUP: + if (load(backupPath)>0) { + showError("No backup available! (or unable to open it)"); + } + break; case GUI_WARN_OPEN_DROP: if (load(nextFile)>0) { showError(fmt::sprintf("Error while loading file! (%s)",lastError)); @@ -7093,7 +7111,7 @@ FurnaceGUI::FurnaceGUI(): aboutScroll(0), aboutSin(0), aboutHue(0.0f), - backupTimer(0.1), + backupTimer(15.0), curIns(0), curWave(0), curSample(0), diff --git a/src/gui/gui.h b/src/gui/gui.h index 28e28f37..ad73fba9 100644 --- a/src/gui/gui.h +++ b/src/gui/gui.h @@ -187,6 +187,7 @@ enum FurnaceGUIWarnings { GUI_WARN_QUIT, GUI_WARN_NEW, GUI_WARN_OPEN, + GUI_WARN_OPEN_BACKUP, GUI_WARN_OPEN_DROP, GUI_WARN_RESET_LAYOUT, GUI_WARN_GENERIC @@ -201,6 +202,7 @@ enum FurnaceGUIFMAlgs { enum FurnaceGUIActions { GUI_ACTION_GLOBAL_MIN=0, GUI_ACTION_OPEN, + GUI_ACTION_OPEN_BACKUP, GUI_ACTION_SAVE, GUI_ACTION_SAVE_AS, GUI_ACTION_UNDO, diff --git a/src/gui/settings.cpp b/src/gui/settings.cpp index be1b6b16..ed17a1e3 100644 --- a/src/gui/settings.cpp +++ b/src/gui/settings.cpp @@ -579,6 +579,7 @@ void FurnaceGUI::drawSettings() { KEYBIND_CONFIG_BEGIN("keysGlobal"); UI_KEYBIND_CONFIG(GUI_ACTION_OPEN,"Open file"); + UI_KEYBIND_CONFIG(GUI_ACTION_OPEN_BACKUP,"Restore backup"); UI_KEYBIND_CONFIG(GUI_ACTION_SAVE,"Save file"); UI_KEYBIND_CONFIG(GUI_ACTION_SAVE_AS,"Save as"); UI_KEYBIND_CONFIG(GUI_ACTION_UNDO,"Undo"); @@ -970,6 +971,7 @@ void FurnaceGUI::syncSettings() { // keybinds LOAD_KEYBIND(GUI_ACTION_OPEN,FURKMOD_CMD|SDLK_o); + LOAD_KEYBIND(GUI_ACTION_OPEN_BACKUP,0); LOAD_KEYBIND(GUI_ACTION_SAVE,FURKMOD_CMD|SDLK_s); LOAD_KEYBIND(GUI_ACTION_SAVE_AS,FURKMOD_CMD|FURKMOD_SHIFT|SDLK_s); LOAD_KEYBIND(GUI_ACTION_UNDO,FURKMOD_CMD|SDLK_z); @@ -1251,6 +1253,7 @@ void FurnaceGUI::commitSettings() { PUT_UI_COLOR(GUI_COLOR_PLAYBACK_STAT); SAVE_KEYBIND(GUI_ACTION_OPEN); + SAVE_KEYBIND(GUI_ACTION_OPEN_BACKUP); SAVE_KEYBIND(GUI_ACTION_SAVE); SAVE_KEYBIND(GUI_ACTION_SAVE_AS); SAVE_KEYBIND(GUI_ACTION_UNDO); From 6dc231917c42f6f845a2b5321fcf1fb7787b82b1 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Mon, 21 Mar 2022 14:35:04 -0500 Subject: [PATCH 14/40] add Nix package link --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index a1b64854..15f0dcd2 100644 --- a/README.md +++ b/README.md @@ -64,7 +64,7 @@ some people have provided packages for Unix/Unix-like distributions. here's a li ## Nix -(TODO) +[package](https://search.nixos.org/packages?channel=unstable&show=furnace&from=0&size=50&sort=relevance&type=packages&query=furnace) thanks to OPNA2608. # developer info From 0a114b1168bfb20ffcc429eb0a7621feb7077669 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Mon, 21 Mar 2022 14:47:10 -0500 Subject: [PATCH 15/40] GUI: don't allow direct saving to the backup file --- src/gui/gui.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/gui/gui.cpp b/src/gui/gui.cpp index 42c12ed6..adf7c1a2 100644 --- a/src/gui/gui.cpp +++ b/src/gui/gui.cpp @@ -3682,7 +3682,7 @@ void FurnaceGUI::doAction(int what) { } break; case GUI_ACTION_SAVE: - if (curFileName=="" || e->song.version>=0xff00) { + if (curFileName=="" || curFileName==backupPath || e->song.version>=0xff00) { openFileDialog(GUI_FILE_SAVE); } else { if (save(curFileName,e->song.isDMF?e->song.version:0)>0) { @@ -5490,7 +5490,7 @@ bool FurnaceGUI::loop() { } ImGui::Separator(); if (ImGui::MenuItem("save",BIND_FOR(GUI_ACTION_SAVE))) { - if (curFileName=="" || e->song.version>=0xff00) { + if (curFileName=="" || curFileName==backupPath || e->song.version>=0xff00) { openFileDialog(GUI_FILE_SAVE); } else { if (save(curFileName,e->song.isDMF?e->song.version:0)>0) { From 1003d9fcb8fe425d6fb9c54794a9e638758b0b89 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Mon, 21 Mar 2022 16:17:51 -0500 Subject: [PATCH 16/40] GUI: more "modified" status situations fixes #236 --- src/engine/engine.cpp | 69 ++++++++++++++++++++++- src/engine/engine.h | 8 ++- src/engine/fileOps.cpp | 12 +++- src/gui/gui.cpp | 70 ++++++++++++----------- src/gui/gui.h | 2 + src/gui/insEdit.cpp | 16 +++--- src/gui/orders.cpp | 28 ++++++---- src/gui/sampleEdit.cpp | 122 ++++++++++++++++++++++++----------------- 8 files changed, 223 insertions(+), 104 deletions(-) diff --git a/src/engine/engine.cpp b/src/engine/engine.cpp index 3a6812ad..2b328391 100644 --- a/src/engine/engine.cpp +++ b/src/engine/engine.cpp @@ -584,6 +584,7 @@ void DivEngine::renderSamples() { void DivEngine::createNew(const int* description) { quitDispatch(); isBusy.lock(); + saveLock.lock(); song.unload(); song=DivSong(); if (description!=NULL) { @@ -602,6 +603,7 @@ void DivEngine::createNew(const int* description) { } recalcChans(); renderSamples(); + saveLock.unlock(); isBusy.unlock(); initDispatch(); isBusy.lock(); @@ -1214,8 +1216,10 @@ int DivEngine::addInstrument(int refChan) { int insCount=(int)song.ins.size(); ins->name=fmt::sprintf("Instrument %d",insCount); ins->type=getPreferInsType(refChan); + saveLock.lock(); song.ins.push_back(ins); song.insLen=insCount+1; + saveLock.unlock(); isBusy.unlock(); return insCount; } @@ -1230,6 +1234,9 @@ enum DivInsFormats { DIV_INSFORMAT_SBI, }; +// TODO: re-organize this function to: +// - support replacing instruments +// - support instrument formats which contain multiple instruments bool DivEngine::addInstrumentFromFile(const char* path) { warnings=""; @@ -1354,7 +1361,7 @@ bool DivEngine::addInstrumentFromFile(const char* path) { } } - // TDOO these really should be refactored to separate functions/cpp files per instrument file type. + // TDOO these really should be re-organized to separate functions per instrument file type. switch (format) { case DIV_INSFORMAT_DMP: { // this is a ridiculous mess @@ -1937,15 +1944,18 @@ bool DivEngine::addInstrumentFromFile(const char* path) { } isBusy.lock(); + saveLock.lock(); int insCount=(int)song.ins.size(); song.ins.push_back(ins); song.insLen=insCount+1; + saveLock.unlock(); isBusy.unlock(); return true; } void DivEngine::delInstrument(int index) { isBusy.lock(); + saveLock.lock(); if (index>=0 && index<(int)song.ins.size()) { for (int i=0; inotifyInsDeletion(song.ins[index]); @@ -1964,15 +1974,18 @@ void DivEngine::delInstrument(int index) { } } } + saveLock.unlock(); isBusy.unlock(); } int DivEngine::addWave() { isBusy.lock(); + saveLock.lock(); DivWavetable* wave=new DivWavetable; int waveCount=(int)song.wave.size(); song.wave.push_back(wave); song.waveLen=waveCount+1; + saveLock.unlock(); isBusy.unlock(); return waveCount; } @@ -2088,30 +2101,36 @@ bool DivEngine::addWaveFromFile(const char* path) { } isBusy.lock(); + saveLock.lock(); int waveCount=(int)song.wave.size(); song.wave.push_back(wave); song.waveLen=waveCount+1; + saveLock.unlock(); isBusy.unlock(); return true; } void DivEngine::delWave(int index) { isBusy.lock(); + saveLock.lock(); if (index>=0 && index<(int)song.wave.size()) { delete song.wave[index]; song.wave.erase(song.wave.begin()+index); song.waveLen=song.wave.size(); } + saveLock.unlock(); isBusy.unlock(); } int DivEngine::addSample() { isBusy.lock(); + saveLock.lock(); DivSample* sample=new DivSample; int sampleCount=(int)song.sample.size(); sample->name=fmt::sprintf("Sample %d",sampleCount); song.sample.push_back(sample); song.sampleLen=sampleCount+1; + saveLock.unlock(); renderSamples(); isBusy.unlock(); return sampleCount; @@ -2189,8 +2208,10 @@ bool DivEngine::addSampleFromFile(const char* path) { if (sample->centerRate<4000) sample->centerRate=4000; if (sample->centerRate>64000) sample->centerRate=64000; sf_close(f); + saveLock.lock(); song.sample.push_back(sample); song.sampleLen=sampleCount+1; + saveLock.unlock(); renderSamples(); isBusy.unlock(); return sampleCount; @@ -2198,12 +2219,14 @@ bool DivEngine::addSampleFromFile(const char* path) { void DivEngine::delSample(int index) { isBusy.lock(); + saveLock.lock(); if (index>=0 && index<(int)song.sample.size()) { delete song.sample[index]; song.sample.erase(song.sample.begin()+index); song.sampleLen=song.sample.size(); renderSamples(); } + saveLock.unlock(); isBusy.unlock(); } @@ -2232,11 +2255,14 @@ void DivEngine::addOrder(bool duplicate, bool where) { } } if (where) { // at the end + saveLock.lock(); for (int i=0; icurOrder; j--) { song.orders.ord[i][j]=song.orders.ord[i][j-1]; @@ -2244,6 +2270,7 @@ void DivEngine::addOrder(bool duplicate, bool where) { song.orders.ord[i][curOrder+1]=order[i]; } song.ordersLen++; + saveLock.unlock(); curOrder++; if (playing && !freelance) { playSub(false); @@ -2280,11 +2307,14 @@ void DivEngine::deepCloneOrder(bool where) { } } if (where) { // at the end + saveLock.lock(); for (int i=0; icurOrder; j--) { song.orders.ord[i][j]=song.orders.ord[i][j-1]; @@ -2292,6 +2322,7 @@ void DivEngine::deepCloneOrder(bool where) { song.orders.ord[i][curOrder+1]=order[i]; } song.ordersLen++; + saveLock.unlock(); curOrder++; if (playing && !freelance) { playSub(false); @@ -2303,12 +2334,14 @@ void DivEngine::deepCloneOrder(bool where) { void DivEngine::deleteOrder() { if (song.ordersLen<=1) return; isBusy.lock(); + saveLock.lock(); for (int i=0; i=song.ordersLen) curOrder=song.ordersLen-1; if (playing && !freelance) { playSub(false); @@ -2322,11 +2355,13 @@ void DivEngine::moveOrderUp() { isBusy.unlock(); return; } + saveLock.lock(); for (int i=0; i=(int)song.ins.size()) return false; isBusy.lock(); DivInstrument* prev=song.ins[which]; + saveLock.lock(); song.ins[which]=song.ins[which-1]; song.ins[which-1]=prev; exchangeIns(which,which-1); + saveLock.unlock(); isBusy.unlock(); return true; } @@ -2382,8 +2421,10 @@ bool DivEngine::moveWaveUp(int which) { if (which<1 || which>=(int)song.wave.size()) return false; isBusy.lock(); DivWavetable* prev=song.wave[which]; + saveLock.lock(); song.wave[which]=song.wave[which-1]; song.wave[which-1]=prev; + saveLock.unlock(); isBusy.unlock(); return true; } @@ -2392,8 +2433,10 @@ bool DivEngine::moveSampleUp(int which) { if (which<1 || which>=(int)song.sample.size()) return false; isBusy.lock(); DivSample* prev=song.sample[which]; + saveLock.lock(); song.sample[which]=song.sample[which-1]; song.sample[which-1]=prev; + saveLock.unlock(); isBusy.unlock(); return true; } @@ -2402,9 +2445,11 @@ bool DivEngine::moveInsDown(int which) { if (which<0 || which>=((int)song.ins.size())-1) return false; isBusy.lock(); DivInstrument* prev=song.ins[which]; + saveLock.lock(); song.ins[which]=song.ins[which+1]; song.ins[which+1]=prev; exchangeIns(which,which+1); + saveLock.unlock(); isBusy.unlock(); return true; } @@ -2413,8 +2458,10 @@ bool DivEngine::moveWaveDown(int which) { if (which<0 || which>=((int)song.wave.size())-1) return false; isBusy.lock(); DivWavetable* prev=song.wave[which]; + saveLock.lock(); song.wave[which]=song.wave[which+1]; song.wave[which+1]=prev; + saveLock.unlock(); isBusy.unlock(); return true; } @@ -2423,8 +2470,10 @@ bool DivEngine::moveSampleDown(int which) { if (which<0 || which>=((int)song.sample.size())-1) return false; isBusy.lock(); DivSample* prev=song.sample[which]; + saveLock.lock(); song.sample[which]=song.sample[which+1]; song.sample[which+1]=prev; + saveLock.unlock(); isBusy.unlock(); return true; } @@ -2465,7 +2514,9 @@ void DivEngine::setOrder(unsigned char order) { void DivEngine::setSysFlags(int system, unsigned int flags, bool restart) { isBusy.lock(); + saveLock.lock(); song.systemFlags[system]=flags; + saveLock.unlock(); disCont[system].dispatch->setFlags(song.systemFlags[system]); disCont[system].setRates(got.rate); if (restart && isPlaying()) { @@ -2476,6 +2527,7 @@ void DivEngine::setSysFlags(int system, unsigned int flags, bool restart) { void DivEngine::setSongRate(float hz, bool pal) { isBusy.lock(); + saveLock.lock(); song.pal=!pal; song.hz=hz; // what? @@ -2490,6 +2542,7 @@ void DivEngine::setSongRate(float hz, bool pal) { divider=50; } } + saveLock.unlock(); isBusy.unlock(); } @@ -2539,6 +2592,20 @@ void DivEngine::synchronized(const std::function& what) { isBusy.unlock(); } +void DivEngine::lockSave(const std::function& what) { + saveLock.lock(); + what(); + saveLock.unlock(); +} + +void DivEngine::lockEngine(const std::function& what) { + isBusy.lock(); + saveLock.lock(); + what(); + saveLock.unlock(); + isBusy.unlock(); +} + TAAudioDesc& DivEngine::getAudioDescWant() { return want; } diff --git a/src/engine/engine.h b/src/engine/engine.h index 958508a3..9f7464e3 100644 --- a/src/engine/engine.h +++ b/src/engine/engine.h @@ -201,7 +201,7 @@ class DivEngine { std::map conf; std::queue pendingNotes; bool isMuted[DIV_MAX_CHANS]; - std::mutex isBusy; + std::mutex isBusy, saveLock; String configPath; String configFile; String lastError; @@ -621,6 +621,12 @@ class DivEngine { // perform secure/sync operation void synchronized(const std::function& what); + // perform secure/sync song operation + void lockSave(const std::function& what); + + // perform secure/sync song operation (and lock audio too) + void lockEngine(const std::function& what); + // get audio desc want TAAudioDesc& getAudioDescWant(); diff --git a/src/engine/fileOps.cpp b/src/engine/fileOps.cpp index afe5df63..460f7d8d 100644 --- a/src/engine/fileOps.cpp +++ b/src/engine/fileOps.cpp @@ -733,10 +733,12 @@ bool DivEngine::loadDMF(unsigned char* file, size_t len) { if (active) quitDispatch(); isBusy.lock(); + saveLock.lock(); song.unload(); song=ds; recalcChans(); renderSamples(); + saveLock.unlock(); isBusy.unlock(); if (active) { initDispatch(); @@ -1219,10 +1221,12 @@ bool DivEngine::loadFur(unsigned char* file, size_t len) { if (active) quitDispatch(); isBusy.lock(); + saveLock.lock(); song.unload(); song=ds; recalcChans(); renderSamples(); + saveLock.unlock(); isBusy.unlock(); if (active) { initDispatch(); @@ -1583,10 +1587,12 @@ bool DivEngine::loadMod(unsigned char* file, size_t len) { if (active) quitDispatch(); isBusy.lock(); + saveLock.lock(); song.unload(); song=ds; recalcChans(); renderSamples(); + saveLock.unlock(); isBusy.unlock(); if (active) { initDispatch(); @@ -1729,6 +1735,7 @@ bool DivEngine::load(unsigned char* f, size_t slen) { } SafeWriter* DivEngine::saveFur(bool notPrimary) { + saveLock.lock(); int insPtr[256]; int wavePtr[256]; int samplePtr[256]; @@ -1969,6 +1976,7 @@ SafeWriter* DivEngine::saveFur(bool notPrimary) { w->writeI(i); } + saveLock.unlock(); return w; } @@ -2028,6 +2036,7 @@ SafeWriter* DivEngine::saveDMF(unsigned char version) { lastError="this system is not possible on .dmf"; return NULL; } + saveLock.lock(); warnings=""; song.version=version; song.isDMF=true; @@ -2259,7 +2268,8 @@ SafeWriter* DivEngine::saveDMF(unsigned char version) { w->writeC(16); w->write(i->data16,i->length16); } - + + saveLock.unlock(); return w; } diff --git a/src/gui/gui.cpp b/src/gui/gui.cpp index adf7c1a2..17d14f6e 100644 --- a/src/gui/gui.cpp +++ b/src/gui/gui.cpp @@ -1004,13 +1004,17 @@ void FurnaceGUI::drawSongInfo() { ImGui::TableNextColumn(); float avail=ImGui::GetContentRegionAvail().x; ImGui::SetNextItemWidth(avail); - if (ImGui::InputText("##Name",&e->song.name)) updateWindowTitle(); + if (ImGui::InputText("##Name",&e->song.name)) { MARK_MODIFIED + updateWindowTitle(); + } ImGui::TableNextRow(); ImGui::TableNextColumn(); ImGui::Text("Author"); ImGui::TableNextColumn(); ImGui::SetNextItemWidth(avail); - ImGui::InputText("##Author",&e->song.author); + if (ImGui::InputText("##Author",&e->song.author)) { + MARK_MODIFIED; + } ImGui::EndTable(); } @@ -1026,7 +1030,7 @@ void FurnaceGUI::drawSongInfo() { float avail=ImGui::GetContentRegionAvail().x; ImGui::SetNextItemWidth(avail); unsigned char realTB=e->song.timeBase+1; - if (ImGui::InputScalar("##TimeBase",ImGuiDataType_U8,&realTB,&_ONE,&_THREE)) { + if (ImGui::InputScalar("##TimeBase",ImGuiDataType_U8,&realTB,&_ONE,&_THREE)) { MARK_MODIFIED if (realTB<1) realTB=1; if (realTB>16) realTB=16; e->song.timeBase=realTB-1; @@ -1039,13 +1043,13 @@ void FurnaceGUI::drawSongInfo() { ImGui::Text("Speed"); ImGui::TableNextColumn(); ImGui::SetNextItemWidth(avail); - if (ImGui::InputScalar("##Speed1",ImGuiDataType_U8,&e->song.speed1,&_ONE,&_THREE)) { + if (ImGui::InputScalar("##Speed1",ImGuiDataType_U8,&e->song.speed1,&_ONE,&_THREE)) { MARK_MODIFIED if (e->song.speed1<1) e->song.speed1=1; if (e->isPlaying()) play(); } ImGui::TableNextColumn(); ImGui::SetNextItemWidth(avail); - if (ImGui::InputScalar("##Speed2",ImGuiDataType_U8,&e->song.speed2,&_ONE,&_THREE)) { + if (ImGui::InputScalar("##Speed2",ImGuiDataType_U8,&e->song.speed2,&_ONE,&_THREE)) { MARK_MODIFIED if (e->song.speed2<1) e->song.speed2=1; if (e->isPlaying()) play(); } @@ -1055,10 +1059,14 @@ void FurnaceGUI::drawSongInfo() { ImGui::Text("Highlight"); ImGui::TableNextColumn(); ImGui::SetNextItemWidth(avail); - ImGui::InputScalar("##Highlight1",ImGuiDataType_U8,&e->song.hilightA,&_ONE,&_THREE); + if (ImGui::InputScalar("##Highlight1",ImGuiDataType_U8,&e->song.hilightA,&_ONE,&_THREE)) { + MARK_MODIFIED; + } ImGui::TableNextColumn(); ImGui::SetNextItemWidth(avail); - ImGui::InputScalar("##Highlight2",ImGuiDataType_U8,&e->song.hilightB,&_ONE,&_THREE); + if (ImGui::InputScalar("##Highlight2",ImGuiDataType_U8,&e->song.hilightB,&_ONE,&_THREE)) { + MARK_MODIFIED; + } ImGui::TableNextRow(); ImGui::TableNextColumn(); @@ -1066,7 +1074,7 @@ void FurnaceGUI::drawSongInfo() { ImGui::TableNextColumn(); ImGui::SetNextItemWidth(avail); int patLen=e->song.patLen; - if (ImGui::InputInt("##PatLength",&patLen,1,3)) { + if (ImGui::InputInt("##PatLength",&patLen,1,3)) { MARK_MODIFIED if (patLen<1) patLen=1; if (patLen>256) patLen=256; e->song.patLen=patLen; @@ -1078,7 +1086,7 @@ void FurnaceGUI::drawSongInfo() { ImGui::TableNextColumn(); ImGui::SetNextItemWidth(avail); int ordLen=e->song.ordersLen; - if (ImGui::InputInt("##OrdLength",&ordLen,1,3)) { + if (ImGui::InputInt("##OrdLength",&ordLen,1,3)) { MARK_MODIFIED if (ordLen<1) ordLen=1; if (ordLen>127) ordLen=127; e->song.ordersLen=ordLen; @@ -1092,7 +1100,7 @@ void FurnaceGUI::drawSongInfo() { ImGui::TableNextColumn(); ImGui::SetNextItemWidth(avail); float setHz=tempoView?e->song.hz*2.5:e->song.hz; - if (ImGui::InputFloat("##Rate",&setHz,1.0f,1.0f,"%g")) { + if (ImGui::InputFloat("##Rate",&setHz,1.0f,1.0f,"%g")) { MARK_MODIFIED if (tempoView) setHz/=2.5; if (setHz<10) setHz=10; if (setHz>999) setHz=999; @@ -1118,7 +1126,7 @@ void FurnaceGUI::drawSongInfo() { ImGui::TableNextColumn(); float tune=e->song.tuning; ImGui::SetNextItemWidth(avail); - if (ImGui::InputFloat("##Tuning",&tune,1.0f,3.0f,"%g")) { + if (ImGui::InputFloat("##Tuning",&tune,1.0f,3.0f,"%g")) { MARK_MODIFIED if (tune<220.0f) tune=220.0f; if (tune>880.0f) tune=880.0f; e->song.tuning=tune; @@ -2713,7 +2721,7 @@ void FurnaceGUI::makeUndo(ActionType action) { break; } if (doPush) { - modified=true; + MARK_MODIFIED; undoHist.push_back(s); redoHist.clear(); if (undoHist.size()>settings.maxUndoSteps) undoHist.pop_front(); @@ -3509,7 +3517,7 @@ void FurnaceGUI::doUndo() { if (undoHist.empty()) return; UndoStep& us=undoHist.back(); redoHist.push_back(us); - modified=true; + MARK_MODIFIED; switch (us.type) { case GUI_UNDO_CHANGE_ORDER: @@ -3555,7 +3563,7 @@ void FurnaceGUI::doRedo() { if (redoHist.empty()) return; UndoStep& us=redoHist.back(); undoHist.push_back(us); - modified=true; + MARK_MODIFIED; switch (us.type) { case GUI_UNDO_CHANGE_ORDER: @@ -4095,14 +4103,14 @@ void FurnaceGUI::doAction(int what) { case GUI_ACTION_INS_LIST_ADD: curIns=e->addInstrument(cursor.xCoarse); - modified=true; + MARK_MODIFIED; break; case GUI_ACTION_INS_LIST_DUPLICATE: if (curIns>=0 && curIns<(int)e->song.ins.size()) { int prevIns=curIns; curIns=e->addInstrument(cursor.xCoarse); (*e->song.ins[curIns])=(*e->song.ins[prevIns]); - modified=true; + MARK_MODIFIED; } break; case GUI_ACTION_INS_LIST_OPEN: @@ -4120,7 +4128,7 @@ void FurnaceGUI::doAction(int what) { case GUI_ACTION_INS_LIST_DELETE: if (curIns>=0 && curIns<(int)e->song.ins.size()) { e->delInstrument(curIns); - modified=true; + MARK_MODIFIED; if (curIns>=(int)e->song.ins.size()) { curIns--; } @@ -4138,14 +4146,14 @@ void FurnaceGUI::doAction(int what) { case GUI_ACTION_WAVE_LIST_ADD: curWave=e->addWave(); - modified=true; + MARK_MODIFIED; break; case GUI_ACTION_WAVE_LIST_DUPLICATE: if (curWave>=0 && curWave<(int)e->song.wave.size()) { int prevWave=curWave; curWave=e->addWave(); (*e->song.wave[curWave])=(*e->song.wave[prevWave]); - modified=true; + MARK_MODIFIED; } break; case GUI_ACTION_WAVE_LIST_OPEN: @@ -4163,7 +4171,7 @@ void FurnaceGUI::doAction(int what) { case GUI_ACTION_WAVE_LIST_DELETE: if (curWave>=0 && curWave<(int)e->song.wave.size()) { e->delWave(curWave); - modified=true; + MARK_MODIFIED; if (curWave>=(int)e->song.wave.size()) { curWave--; } @@ -4181,7 +4189,7 @@ void FurnaceGUI::doAction(int what) { case GUI_ACTION_SAMPLE_LIST_ADD: curSample=e->addSample(); - modified=true; + MARK_MODIFIED; break; case GUI_ACTION_SAMPLE_LIST_OPEN: openFileDialog(GUI_FILE_SAMPLE_OPEN); @@ -4197,7 +4205,7 @@ void FurnaceGUI::doAction(int what) { break; case GUI_ACTION_SAMPLE_LIST_DELETE: e->delSample(curSample); - modified=true; + MARK_MODIFIED; if (curSample>=(int)e->song.sample.size()) { curSample--; } @@ -4498,7 +4506,9 @@ void FurnaceGUI::keyDown(SDL_Event& ev) { int num=valueKeys.at(ev.key.keysym.sym); if (orderCursor>=0 && orderCursorgetTotalChannelCount()) { int curOrder=e->getOrder(); - e->song.orders.ord[orderCursor][curOrder]=((e->song.orders.ord[orderCursor][curOrder]<<4)|num)&0x7f; + e->lockSave([this,curOrder,num]() { + e->song.orders.ord[orderCursor][curOrder]=((e->song.orders.ord[orderCursor][curOrder]<<4)|num)&0x7f; + }); if (orderEditMode==2 || orderEditMode==3) { curNibble=!curNibble; if (!curNibble) { @@ -4813,13 +4823,11 @@ void FurnaceGUI::openFileDialog(FurnaceGUIFileDialogs type) { int FurnaceGUI::save(String path, int dmfVersion) { SafeWriter* w; - backupLock.lock(); if (dmfVersion) { w=e->saveDMF(dmfVersion); } else { w=e->saveFur(); } - backupLock.unlock(); if (w==NULL) { lastError=e->getLastError(); return 3; @@ -5062,7 +5070,7 @@ void FurnaceGUI::processDrags(int dragX, int dragY) { if (ynotifyWaveChange(curWave); - modified=true; + MARK_MODIFIED; } } if (sampleDragActive) { @@ -5363,7 +5371,9 @@ bool FurnaceGUI::loop() { break; } case SDL_MOUSEBUTTONUP: - if (macroDragActive || macroLoopDragActive || waveDragActive || (sampleDragActive && sampleDragMode)) modified=true; + if (macroDragActive || macroLoopDragActive || waveDragActive || (sampleDragActive && sampleDragMode)) { + MARK_MODIFIED; + } macroDragActive=false; macroDragBitMode=false; macroDragInitialValue=false; @@ -6272,7 +6282,7 @@ bool FurnaceGUI::loop() { break; case GUI_FILE_SAMPLE_OPEN: e->addSampleFromFile(copyOfName.c_str()); - modified=true; + MARK_MODIFIED; break; case GUI_FILE_SAMPLE_SAVE: if (curSample>=0 && curSample<(int)e->song.sample.size()) { @@ -6299,7 +6309,7 @@ bool FurnaceGUI::loop() { break; case GUI_FILE_WAVE_OPEN: e->addWaveFromFile(copyOfName.c_str()); - modified=true; + MARK_MODIFIED; break; case GUI_FILE_EXPORT_VGM: { SafeWriter* w=e->saveVGM(willExport,vgmExportLoop); @@ -6444,9 +6454,7 @@ bool FurnaceGUI::loop() { return true; } logD("saving backup...\n"); - backupLock.lock(); SafeWriter* w=e->saveFur(true); - backupLock.unlock(); if (w!=NULL) { FILE* outFile=ps_fopen(backupPath.c_str(),"wb"); diff --git a/src/gui/gui.h b/src/gui/gui.h index ad73fba9..75ebab6e 100644 --- a/src/gui/gui.h +++ b/src/gui/gui.h @@ -36,6 +36,8 @@ #define handleUnimportant if (settings.insFocusesPattern && patternOpen) {nextWindow=GUI_WINDOW_PATTERN;} #define unimportant(x) if (x) {handleUnimportant} +#define MARK_MODIFIED modified=true; + enum FurnaceGUIColors { GUI_COLOR_BACKGROUND=0, GUI_COLOR_FRAME_BACKGROUND, diff --git a/src/gui/insEdit.cpp b/src/gui/insEdit.cpp index def41aad..0257c607 100644 --- a/src/gui/insEdit.cpp +++ b/src/gui/insEdit.cpp @@ -669,11 +669,11 @@ void FurnaceGUI::drawFMEnv(unsigned char tl, unsigned char ar, unsigned char dr, } #define P(x) if (x) { \ - modified=true; \ + MARK_MODIFIED; \ e->notifyInsChange(curIns); \ } -#define PARAMETER modified=true; e->notifyInsChange(curIns); +#define PARAMETER MARK_MODIFIED; e->notifyInsChange(curIns); #define NORMAL_MACRO(macro,macroLen,macroLoop,macroRel,macroMin,macroHeight,macroName,displayName,displayHeight,displayLoop,bitfield,bfVal,drawSlider,sliderVal,sliderLow,macroDispMin,bitOff,macroMode,macroColor,mmlStr,macroAMin,macroAMax,hoverFunc,blockMode) \ ImGui::TableNextRow(); \ @@ -685,7 +685,7 @@ void FurnaceGUI::drawFMEnv(unsigned char tl, unsigned char ar, unsigned char dr, } \ if (displayLoop) { \ ImGui::SetNextItemWidth(lenAvail); \ - if (ImGui::InputScalar("##IMacroLen_" macroName,ImGuiDataType_U8,¯oLen,&_ONE,&_THREE)) { \ + if (ImGui::InputScalar("##IMacroLen_" macroName,ImGuiDataType_U8,¯oLen,&_ONE,&_THREE)) { MARK_MODIFIED \ if (macroLen>127) macroLen=127; \ } \ if (macroMode!=NULL) { \ @@ -774,7 +774,7 @@ void FurnaceGUI::drawFMEnv(unsigned char tl, unsigned char ar, unsigned char dr, } \ if (displayLoop) { \ ImGui::SetNextItemWidth(lenAvail); \ - if (ImGui::InputScalar("##IOPMacroLen_" #op macroName,ImGuiDataType_U8,¯oLen,&_ONE,&_THREE)) { \ + if (ImGui::InputScalar("##IOPMacroLen_" #op macroName,ImGuiDataType_U8,¯oLen,&_ONE,&_THREE)) { MARK_MODIFIED \ if (macroLen>127) macroLen=127; \ } \ } \ @@ -924,7 +924,9 @@ void FurnaceGUI::drawInsEdit() { ImGui::Text("no instrument selected"); } else { DivInstrument* ins=e->song.ins[curIns]; - ImGui::InputText("Name",&ins->name); + if (ImGui::InputText("Name",&ins->name)) { + MARK_MODIFIED; + } if (ins->type<0 || ins->type>=DIV_INS_MAX) ins->type=DIV_INS_FM; int insType=ins->type; if (ImGui::Combo("Type",&insType,insTypes,DIV_INS_MAX,DIV_INS_MAX)) { @@ -2011,7 +2013,7 @@ void FurnaceGUI::drawWaveEdit() { if (wave->len<1) wave->len=1; e->notifyWaveChange(curWave); if (wavePreviewOn) e->previewWave(curWave,wavePreviewNote); - modified=true; + MARK_MODIFIED; } ImGui::SameLine(); ImGui::Text("Height"); @@ -2024,7 +2026,7 @@ void FurnaceGUI::drawWaveEdit() { if (wave->max>255) wave->max=255; if (wave->max<1) wave->max=1; e->notifyWaveChange(curWave); - modified=true; + MARK_MODIFIED; } for (int i=0; ilen; i++) { if (wave->data[i]>wave->max) wave->data[i]=wave->max; diff --git a/src/gui/orders.cpp b/src/gui/orders.cpp index 43976fdc..37faba8d 100644 --- a/src/gui/orders.cpp +++ b/src/gui/orders.cpp @@ -97,13 +97,15 @@ void FurnaceGUI::drawOrders() { if (curOrder==i) { if (orderEditMode==0) { prepareUndo(GUI_UNDO_CHANGE_ORDER); - if (changeAllOrders) { - for (int k=0; kgetTotalChannelCount(); k++) { - if (e->song.orders.ord[k][i]<0x7f) e->song.orders.ord[k][i]++; + e->lockSave([this,i,j]() { + if (changeAllOrders) { + for (int k=0; kgetTotalChannelCount(); k++) { + if (e->song.orders.ord[k][i]<0x7f) e->song.orders.ord[k][i]++; + } + } else { + if (e->song.orders.ord[j][i]<0x7f) e->song.orders.ord[j][i]++; } - } else { - if (e->song.orders.ord[j][i]<0x7f) e->song.orders.ord[j][i]++; - } + }); e->walkSong(loopOrder,loopRow,loopEnd); makeUndo(GUI_UNDO_CHANGE_ORDER); } else { @@ -130,13 +132,15 @@ void FurnaceGUI::drawOrders() { if (curOrder==i) { if (orderEditMode==0) { prepareUndo(GUI_UNDO_CHANGE_ORDER); - if (changeAllOrders) { - for (int k=0; kgetTotalChannelCount(); k++) { - if (e->song.orders.ord[k][i]>0) e->song.orders.ord[k][i]--; + e->lockSave([this,i,j]() { + if (changeAllOrders) { + for (int k=0; kgetTotalChannelCount(); k++) { + if (e->song.orders.ord[k][i]>0) e->song.orders.ord[k][i]--; + } + } else { + if (e->song.orders.ord[j][i]>0) e->song.orders.ord[j][i]--; } - } else { - if (e->song.orders.ord[j][i]>0) e->song.orders.ord[j][i]--; - } + }); e->walkSong(loopOrder,loopRow,loopEnd); makeUndo(GUI_UNDO_CHANGE_ORDER); } else { diff --git a/src/gui/sampleEdit.cpp b/src/gui/sampleEdit.cpp index 1af7ddf6..55d2e485 100644 --- a/src/gui/sampleEdit.cpp +++ b/src/gui/sampleEdit.cpp @@ -60,7 +60,9 @@ void FurnaceGUI::drawSampleEdit() { ImGui::Text("Name"); ImGui::SameLine(); ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x); - ImGui::InputText("##SampleName",&sample->name); + if (ImGui::InputText("##SampleName",&sample->name)) { + MARK_MODIFIED; + } if (ImGui::BeginTable("SampleProps",4,ImGuiTableFlags_SizingStretchSame)) { ImGui::TableNextRow(); @@ -75,6 +77,7 @@ void FurnaceGUI::drawSampleEdit() { sample->depth=i; e->renderSamplesP(); updateSampleTex=true; + MARK_MODIFIED; } if (ImGui::IsItemHovered()) { ImGui::SetTooltip("no undo for sample type change operations!"); @@ -87,7 +90,7 @@ void FurnaceGUI::drawSampleEdit() { ImGui::Text("Rate (Hz)"); ImGui::SameLine(); ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x); - if (ImGui::InputInt("##SampleRate",&sample->rate,10,200)) { + if (ImGui::InputInt("##SampleRate",&sample->rate,10,200)) { MARK_MODIFIED if (sample->rate<100) sample->rate=100; if (sample->rate>96000) sample->rate=96000; } @@ -96,14 +99,14 @@ void FurnaceGUI::drawSampleEdit() { ImGui::Text("C-4 (Hz)"); ImGui::SameLine(); ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x); - if (ImGui::InputInt("##SampleCenter",&sample->centerRate,10,200)) { + if (ImGui::InputInt("##SampleCenter",&sample->centerRate,10,200)) { MARK_MODIFIED if (sample->centerRate<100) sample->centerRate=100; if (sample->centerRate>65535) sample->centerRate=65535; } ImGui::TableNextColumn(); bool doLoop=(sample->loopStart>=0); - if (ImGui::Checkbox("Loop",&doLoop)) { + if (ImGui::Checkbox("Loop",&doLoop)) { MARK_MODIFIED if (doLoop) { sample->loopStart=0; } else { @@ -114,7 +117,7 @@ void FurnaceGUI::drawSampleEdit() { if (doLoop) { ImGui::SameLine(); ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x); - if (ImGui::InputInt("##LoopPosition",&sample->loopStart,1,10)) { + if (ImGui::InputInt("##LoopPosition",&sample->loopStart,1,10)) { MARK_MODIFIED if (sample->loopStart<0 || sample->loopStart>=(int)sample->samples) { sample->loopStart=0; } @@ -161,13 +164,16 @@ void FurnaceGUI::drawSampleEdit() { if (resizeSize>16777215) resizeSize=16777215; } if (ImGui::Button("Resize")) { - e->synchronized([this,sample]() { + e->lockEngine([this,sample]() { if (!sample->resize(resizeSize)) { showError("couldn't resize! make sure your sample is 8 or 16-bit."); } e->renderSamples(); }); updateSampleTex=true; + sampleSelStart=-1; + sampleSelEnd=-1; + MARK_MODIFIED; ImGui::CloseCurrentPopup(); } ImGui::EndPopup(); @@ -208,13 +214,16 @@ void FurnaceGUI::drawSampleEdit() { } ImGui::Combo("Filter",&resampleStrat,resampleStrats,6); if (ImGui::Button("Resample")) { - e->synchronized([this,sample]() { + e->lockEngine([this,sample]() { if (!sample->resample(resampleTarget,resampleStrat)) { showError("couldn't resample! make sure your sample is 8 or 16-bit."); } e->renderSamples(); }); updateSampleTex=true; + sampleSelStart=-1; + sampleSelEnd=-1; + MARK_MODIFIED; ImGui::CloseCurrentPopup(); } ImGui::EndPopup(); @@ -237,7 +246,7 @@ void FurnaceGUI::drawSampleEdit() { ImGui::SameLine(); ImGui::Text("(%.1fdB)",20.0*log10(amplifyVol/100.0f)); if (ImGui::Button("Apply")) { - e->synchronized([this,sample]() { + e->lockEngine([this,sample]() { SAMPLE_OP_BEGIN; float vol=amplifyVol/100.0f; @@ -261,59 +270,61 @@ void FurnaceGUI::drawSampleEdit() { e->renderSamples(); }); + MARK_MODIFIED; ImGui::CloseCurrentPopup(); } ImGui::EndPopup(); } ImGui::SameLine(); if (ImGui::Button(ICON_FA_ARROWS_V "##SNormalize")) { - e->synchronized([this,sample]() { - SAMPLE_OP_BEGIN; - float maxVal=0.0f; + e->lockEngine([this,sample]() { + SAMPLE_OP_BEGIN; + float maxVal=0.0f; - if (sample->depth==16) { - for (unsigned int i=start; idata16[i]/32767.0f); - if (val>maxVal) maxVal=val; - } - if (maxVal>1.0f) maxVal=1.0f; - if (maxVal>0.0f) { - float vol=1.0f/maxVal; + if (sample->depth==16) { for (unsigned int i=start; idata16[i]*vol; - if (val<-32768) val=-32768; - if (val>32767) val=32767; - sample->data16[i]=val; + float val=fabs((float)sample->data16[i]/32767.0f); + if (val>maxVal) maxVal=val; + } + if (maxVal>1.0f) maxVal=1.0f; + if (maxVal>0.0f) { + float vol=1.0f/maxVal; + for (unsigned int i=start; idata16[i]*vol; + if (val<-32768) val=-32768; + if (val>32767) val=32767; + sample->data16[i]=val; + } + } + } else if (sample->depth==8) { + for (unsigned int i=start; idata8[i]/127.0f); + if (val>maxVal) maxVal=val; + } + if (maxVal>1.0f) maxVal=1.0f; + if (maxVal>0.0f) { + float vol=1.0f/maxVal; + for (unsigned int i=start; idata8[i]*vol; + if (val<-128) val=-128; + if (val>127) val=127; + sample->data8[i]=val; + } } } - } else if (sample->depth==8) { - for (unsigned int i=start; idata8[i]/127.0f); - if (val>maxVal) maxVal=val; - } - if (maxVal>1.0f) maxVal=1.0f; - if (maxVal>0.0f) { - float vol=1.0f/maxVal; - for (unsigned int i=start; idata8[i]*vol; - if (val<-128) val=-128; - if (val>127) val=127; - sample->data8[i]=val; - } - } - } - updateSampleTex=true; + updateSampleTex=true; - e->renderSamples(); - }); + e->renderSamples(); + }); + MARK_MODIFIED; } if (ImGui::IsItemHovered()) { ImGui::SetTooltip("Normalize"); } ImGui::SameLine(); if (ImGui::Button(ICON_FA_ARROW_UP "##SFadeIn")) { - e->synchronized([this,sample]() { + e->lockEngine([this,sample]() { SAMPLE_OP_BEGIN; if (sample->depth==16) { @@ -336,13 +347,14 @@ void FurnaceGUI::drawSampleEdit() { e->renderSamples(); }); + MARK_MODIFIED; } if (ImGui::IsItemHovered()) { ImGui::SetTooltip("Fade in"); } ImGui::SameLine(); if (ImGui::Button(ICON_FA_ARROW_DOWN "##SFadeOut")) { - e->synchronized([this,sample]() { + e->lockEngine([this,sample]() { SAMPLE_OP_BEGIN; if (sample->depth==16) { @@ -365,13 +377,14 @@ void FurnaceGUI::drawSampleEdit() { e->renderSamples(); }); + MARK_MODIFIED; } if (ImGui::IsItemHovered()) { ImGui::SetTooltip("Fade out"); } ImGui::SameLine(); if (ImGui::Button(ICON_FA_ERASER "##SSilence")) { - e->synchronized([this,sample]() { + e->lockEngine([this,sample]() { SAMPLE_OP_BEGIN; if (sample->depth==16) { @@ -388,13 +401,14 @@ void FurnaceGUI::drawSampleEdit() { e->renderSamples(); }); + MARK_MODIFIED; } if (ImGui::IsItemHovered()) { ImGui::SetTooltip("Apply silence"); } ImGui::SameLine(); if (ImGui::Button(ICON_FA_TIMES "##SDelete")) { - e->synchronized([this,sample]() { + e->lockEngine([this,sample]() { SAMPLE_OP_BEGIN; sample->strip(start,end); @@ -404,13 +418,14 @@ void FurnaceGUI::drawSampleEdit() { }); sampleSelStart=-1; sampleSelEnd=-1; + MARK_MODIFIED; } if (ImGui::IsItemHovered()) { ImGui::SetTooltip("Delete"); } ImGui::SameLine(); if (ImGui::Button(ICON_FA_CROP "##STrim")) { - e->synchronized([this,sample]() { + e->lockEngine([this,sample]() { SAMPLE_OP_BEGIN; sample->trim(start,end); @@ -420,6 +435,7 @@ void FurnaceGUI::drawSampleEdit() { }); sampleSelStart=-1; sampleSelEnd=-1; + MARK_MODIFIED; } if (ImGui::IsItemHovered()) { ImGui::SetTooltip("Trim"); @@ -428,7 +444,7 @@ void FurnaceGUI::drawSampleEdit() { ImGui::Dummy(ImVec2(4.0*dpiScale,dpiScale)); ImGui::SameLine(); if (ImGui::Button(ICON_FA_BACKWARD "##SReverse")) { - e->synchronized([this,sample]() { + e->lockEngine([this,sample]() { SAMPLE_OP_BEGIN; if (sample->depth==16) { @@ -453,13 +469,14 @@ void FurnaceGUI::drawSampleEdit() { e->renderSamples(); }); + MARK_MODIFIED; } if (ImGui::IsItemHovered()) { ImGui::SetTooltip("Reverse"); } ImGui::SameLine(); if (ImGui::Button(ICON_FA_SORT_AMOUNT_ASC "##SInvert")) { - e->synchronized([this,sample]() { + e->lockEngine([this,sample]() { SAMPLE_OP_BEGIN; if (sample->depth==16) { @@ -478,13 +495,14 @@ void FurnaceGUI::drawSampleEdit() { e->renderSamples(); }); + MARK_MODIFIED; } if (ImGui::IsItemHovered()) { ImGui::SetTooltip("Invert"); } ImGui::SameLine(); if (ImGui::Button(ICON_FA_LEVEL_DOWN "##SSign")) { - e->synchronized([this,sample]() { + e->lockEngine([this,sample]() { SAMPLE_OP_BEGIN; if (sample->depth==16) { @@ -501,6 +519,7 @@ void FurnaceGUI::drawSampleEdit() { e->renderSamples(); }); + MARK_MODIFIED; } if (ImGui::IsItemHovered()) { ImGui::SetTooltip("Signed/unsigned exchange"); @@ -561,7 +580,7 @@ void FurnaceGUI::drawSampleEdit() { } if (ImGui::Button("Apply")) { - e->synchronized([this,sample]() { + e->lockEngine([this,sample]() { SAMPLE_OP_BEGIN; float res=1.0-pow(sampleFilterRes,0.5f); float low=0; @@ -608,6 +627,7 @@ void FurnaceGUI::drawSampleEdit() { e->renderSamples(); }); + MARK_MODIFIED; ImGui::CloseCurrentPopup(); } ImGui::EndPopup(); From cbbca2d6c85c02976425d414fd57a2476a229a8d Mon Sep 17 00:00:00 2001 From: tildearrow Date: Mon, 21 Mar 2022 16:22:43 -0500 Subject: [PATCH 17/40] GUI: no mention of PET in wave edit --- src/gui/insEdit.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/gui/insEdit.cpp b/src/gui/insEdit.cpp index b3ab2f0d..ab206e16 100644 --- a/src/gui/insEdit.cpp +++ b/src/gui/insEdit.cpp @@ -2012,7 +2012,7 @@ void FurnaceGUI::drawWaveEdit() { DivWavetable* wave=e->song.wave[curWave]; ImGui::Text("Width"); if (ImGui::IsItemHovered()) { - ImGui::SetTooltip("use a width of:\n- 8 for PET\n- 32 on Game Boy, PC Engine and WonderSwan.\nany other widths will be scaled during playback."); + ImGui::SetTooltip("use a width of:\n- any on Amiga/X1-010\n- 32 on Game Boy, PC Engine and WonderSwan\nany other widths will be scaled during playback."); } ImGui::SameLine(); ImGui::SetNextItemWidth(128.0f*dpiScale); @@ -2026,7 +2026,7 @@ void FurnaceGUI::drawWaveEdit() { ImGui::SameLine(); ImGui::Text("Height"); if (ImGui::IsItemHovered()) { - ImGui::SetTooltip("use a height of:\n- 1 for PET\n- 15 for Game Boy and WonderSwan\n- 31 for PC Engine\nany other heights will be scaled during playback."); + ImGui::SetTooltip("use a height of:\n- 15 for Game Boy and WonderSwan\n- 31 for PC Engine\nany other heights will be scaled during playback."); } ImGui::SameLine(); ImGui::SetNextItemWidth(128.0f*dpiScale); From e62f9bffd344c2b6128257bdfa375744e30d4e61 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Mon, 21 Mar 2022 16:34:19 -0500 Subject: [PATCH 18/40] GUI: re-organize code ins/wave/sample lists now in dataList.cpp wave editor now under its own file --- CMakeLists.txt | 2 + src/gui/dataList.cpp | 348 +++++++++++++++++++++++++++++++++++++++++++ src/gui/gui.cpp | 298 ------------------------------------ src/gui/insEdit.cpp | 130 +--------------- src/gui/waveEdit.cpp | 82 ++++++++++ 5 files changed, 433 insertions(+), 427 deletions(-) create mode 100644 src/gui/dataList.cpp create mode 100644 src/gui/waveEdit.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index b3d1fc37..8f51dc9e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -357,12 +357,14 @@ src/gui/fileDialog.cpp src/gui/intConst.cpp src/gui/guiConst.cpp +src/gui/dataList.cpp src/gui/insEdit.cpp src/gui/orders.cpp src/gui/pattern.cpp src/gui/sampleEdit.cpp src/gui/settings.cpp src/gui/util.cpp +src/gui/waveEdit.cpp src/gui/gui.cpp ) diff --git a/src/gui/dataList.cpp b/src/gui/dataList.cpp new file mode 100644 index 00000000..f32ffda7 --- /dev/null +++ b/src/gui/dataList.cpp @@ -0,0 +1,348 @@ +#include "gui.h" +#include "IconsFontAwesome4.h" +#include "misc/cpp/imgui_stdlib.h" +#include "plot_nolerp.h" +#include "guiConst.h" +#include + +const char* sampleNote[12]={ + "C", "C#", "D", "D#", "E", "F", "F#", "G", "G#", "A", "A#", "B" +}; + +void FurnaceGUI::drawInsList() { + if (nextWindow==GUI_WINDOW_INS_LIST) { + insListOpen=true; + ImGui::SetNextWindowFocus(); + nextWindow=GUI_WINDOW_NOTHING; + } + if (!insListOpen) return; + if (ImGui::Begin("Instruments",&insListOpen)) { + if (ImGui::Button(ICON_FA_PLUS "##InsAdd")) { + doAction(GUI_ACTION_INS_LIST_ADD); + } + ImGui::SameLine(); + if (ImGui::Button(ICON_FA_FILES_O "##InsClone")) { + doAction(GUI_ACTION_INS_LIST_DUPLICATE); + } + ImGui::SameLine(); + if (ImGui::Button(ICON_FA_FOLDER_OPEN "##InsLoad")) { + doAction(GUI_ACTION_INS_LIST_OPEN); + } + ImGui::SameLine(); + if (ImGui::Button(ICON_FA_FLOPPY_O "##InsSave")) { + doAction(GUI_ACTION_INS_LIST_SAVE); + } + ImGui::SameLine(); + if (ImGui::ArrowButton("InsUp",ImGuiDir_Up)) { + doAction(GUI_ACTION_INS_LIST_MOVE_UP); + } + ImGui::SameLine(); + if (ImGui::ArrowButton("InsDown",ImGuiDir_Down)) { + doAction(GUI_ACTION_INS_LIST_MOVE_DOWN); + } + ImGui::SameLine(); + if (ImGui::Button(ICON_FA_TIMES "##InsDelete")) { + doAction(GUI_ACTION_INS_LIST_DELETE); + } + ImGui::Separator(); + if (ImGui::BeginTable("InsListScroll",1,ImGuiTableFlags_ScrollY)) { + if (settings.unifiedDataView) { + ImGui::TableNextRow(); + ImGui::TableNextColumn(); + ImGui::Text(ICON_FA_TASKS " Instruments"); + ImGui::Indent(); + } + + for (int i=0; i<(int)e->song.ins.size(); i++) { + DivInstrument* ins=e->song.ins[i]; + String name; + switch (ins->type) { + case DIV_INS_FM: + ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_FM]); + name=fmt::sprintf(ICON_FA_AREA_CHART " %.2X: %s##_INS%d",i,ins->name,i); + break; + case DIV_INS_STD: + ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_STD]); + name=fmt::sprintf(ICON_FA_BAR_CHART " %.2X: %s##_INS%d",i,ins->name,i); + break; + case DIV_INS_GB: + ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_GB]); + name=fmt::sprintf(ICON_FA_GAMEPAD " %.2X: %s##_INS%d",i,ins->name,i); + break; + case DIV_INS_C64: + ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_C64]); + name=fmt::sprintf(ICON_FA_KEYBOARD_O " %.2X: %s##_INS%d",i,ins->name,i); + break; + case DIV_INS_AMIGA: + ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_AMIGA]); + name=fmt::sprintf(ICON_FA_VOLUME_UP " %.2X: %s##_INS%d",i,ins->name,i); + break; + case DIV_INS_PCE: + ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_PCE]); + name=fmt::sprintf(ICON_FA_ID_BADGE " %.2X: %s##_INS%d",i,ins->name,i); + break; + case DIV_INS_AY: + ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_AY]); + name=fmt::sprintf(ICON_FA_BAR_CHART " %.2X: %s##_INS%d",i,ins->name,i); + break; + case DIV_INS_AY8930: + ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_AY8930]); + name=fmt::sprintf(ICON_FA_BAR_CHART " %.2X: %s##_INS%d",i,ins->name,i); + break; + case DIV_INS_TIA: + ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_TIA]); + name=fmt::sprintf(ICON_FA_BAR_CHART " %.2X: %s##_INS%d",i,ins->name,i); + break; + case DIV_INS_SAA1099: + ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_SAA1099]); + name=fmt::sprintf(ICON_FA_BAR_CHART " %.2X: %s##_INS%d",i,ins->name,i); + break; + case DIV_INS_VIC: + ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_VIC]); + name=fmt::sprintf(ICON_FA_BAR_CHART " %.2X: %s##_INS%d",i,ins->name,i); + break; + case DIV_INS_PET: + ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_PET]); + name=fmt::sprintf(ICON_FA_SQUARE " %.2X: %s##_INS%d",i,ins->name,i); + break; + case DIV_INS_VRC6: + ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_VRC6]); + name=fmt::sprintf(ICON_FA_BAR_CHART " %.2X: %s##_INS%d",i,ins->name,i); + break; + case DIV_INS_OPLL: + ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_OPLL]); + name=fmt::sprintf(ICON_FA_AREA_CHART " %.2X: %s##_INS%d",i,ins->name,i); + break; + case DIV_INS_OPL: + ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_OPL]); + name=fmt::sprintf(ICON_FA_AREA_CHART " %.2X: %s##_INS%d",i,ins->name,i); + break; + case DIV_INS_FDS: + ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_FDS]); + name=fmt::sprintf(ICON_FA_FLOPPY_O " %.2X: %s##_INS%d",i,ins->name,i); + break; + case DIV_INS_VBOY: + ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_VBOY]); + name=fmt::sprintf(ICON_FA_BINOCULARS " %.2X: %s##_INS%d",i,ins->name,i); + break; + case DIV_INS_N163: + ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_N163]); + name=fmt::sprintf(ICON_FA_CALCULATOR " %.2X: %s##_INS%d",i,ins->name,i); + break; + case DIV_INS_SCC: + ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_SCC]); + name=fmt::sprintf(ICON_FA_CALCULATOR " %.2X: %s##_INS%d",i,ins->name,i); + break; + case DIV_INS_OPZ: + ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_OPZ]); + name=fmt::sprintf(ICON_FA_AREA_CHART " %.2X: %s##_INS%d",i,ins->name,i); + break; + case DIV_INS_POKEY: + ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_POKEY]); + name=fmt::sprintf(ICON_FA_BAR_CHART " %.2X: %s##_INS%d",i,ins->name,i); + break; + case DIV_INS_BEEPER: + ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_BEEPER]); + name=fmt::sprintf(ICON_FA_SQUARE " %.2X: %s##_INS%d",i,ins->name,i); + break; + case DIV_INS_SWAN: + ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_SWAN]); + name=fmt::sprintf(ICON_FA_GAMEPAD " %.2X: %s##_INS%d",i,ins->name,i); + break; + case DIV_INS_MIKEY: + ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_MIKEY]); + name=fmt::sprintf(ICON_FA_BAR_CHART " %.2X: %s##_INS%d",i,ins->name,i); + break; + case DIV_INS_VERA: + ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_VERA]); + name=fmt::sprintf(ICON_FA_KEYBOARD_O " %.2X: %s##_INS%d",i,ins->name,i); + break; + case DIV_INS_X1_010: + ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_X1_010]); + name=fmt::sprintf(ICON_FA_BAR_CHART " %.2X: %s##_INS%d",i,ins->name,i); + break; + default: + ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_UNKNOWN]); + name=fmt::sprintf(ICON_FA_QUESTION " %.2X: %s##_INS%d",i,ins->name,i); + break; + } + ImGui::TableNextRow(); + ImGui::TableNextColumn(); + if (ImGui::Selectable(name.c_str(),curIns==i)) { + curIns=i; + } + if (settings.insFocusesPattern && patternOpen && ImGui::IsItemActivated()) { + nextWindow=GUI_WINDOW_PATTERN; + curIns=i; + } + ImGui::PopStyleColor(); + if (ImGui::IsItemHovered()) { + ImGui::SetTooltip("%s",(ins->type>DIV_INS_MAX)?"Unknown":insTypes[ins->type]); + if (ImGui::IsMouseDoubleClicked(ImGuiMouseButton_Left)) { + insEditOpen=true; + nextWindow=GUI_WINDOW_INS_EDIT; + } + } + } + + if (settings.unifiedDataView) { + ImGui::Unindent(); + + ImGui::TableNextRow(); + ImGui::TableNextColumn(); + ImGui::Text(ICON_FA_AREA_CHART " Wavetables"); + ImGui::Indent(); + actualWaveList(); + ImGui::Unindent(); + + ImGui::TableNextRow(); + ImGui::TableNextColumn(); + ImGui::Text(ICON_FA_VOLUME_UP " Samples"); + ImGui::Indent(); + actualSampleList(); + ImGui::Unindent(); + } + + ImGui::EndTable(); + } + } + if (ImGui::IsWindowFocused(ImGuiFocusedFlags_ChildWindows)) curWindow=GUI_WINDOW_INS_LIST; + ImGui::End(); +} + +void FurnaceGUI::drawWaveList() { + if (nextWindow==GUI_WINDOW_WAVE_LIST) { + waveListOpen=true; + ImGui::SetNextWindowFocus(); + nextWindow=GUI_WINDOW_NOTHING; + } + if (!waveListOpen) return; + if (ImGui::Begin("Wavetables",&waveListOpen)) { + if (ImGui::Button(ICON_FA_PLUS "##WaveAdd")) { + doAction(GUI_ACTION_WAVE_LIST_ADD); + } + ImGui::SameLine(); + if (ImGui::Button(ICON_FA_FILES_O "##WaveClone")) { + doAction(GUI_ACTION_WAVE_LIST_DUPLICATE); + } + ImGui::SameLine(); + if (ImGui::Button(ICON_FA_FOLDER_OPEN "##WaveLoad")) { + doAction(GUI_ACTION_WAVE_LIST_OPEN); + } + ImGui::SameLine(); + if (ImGui::Button(ICON_FA_FLOPPY_O "##WaveSave")) { + doAction(GUI_ACTION_WAVE_LIST_SAVE); + } + ImGui::SameLine(); + if (ImGui::ArrowButton("WaveUp",ImGuiDir_Up)) { + doAction(GUI_ACTION_WAVE_LIST_UP); + } + ImGui::SameLine(); + if (ImGui::ArrowButton("WaveDown",ImGuiDir_Down)) { + doAction(GUI_ACTION_WAVE_LIST_DOWN); + } + ImGui::SameLine(); + if (ImGui::Button(ICON_FA_TIMES "##WaveDelete")) { + doAction(GUI_ACTION_WAVE_LIST_DELETE); + } + ImGui::Separator(); + if (ImGui::BeginTable("WaveListScroll",1,ImGuiTableFlags_ScrollY)) { + actualWaveList(); + ImGui::EndTable(); + } + } + if (ImGui::IsWindowFocused(ImGuiFocusedFlags_ChildWindows)) curWindow=GUI_WINDOW_WAVE_LIST; + ImGui::End(); +} + +void FurnaceGUI::drawSampleList() { + if (nextWindow==GUI_WINDOW_SAMPLE_LIST) { + sampleListOpen=true; + ImGui::SetNextWindowFocus(); + nextWindow=GUI_WINDOW_NOTHING; + } + if (!sampleListOpen) return; + if (ImGui::Begin("Samples",&sampleListOpen)) { + if (ImGui::Button(ICON_FA_PLUS "##SampleAdd")) { + doAction(GUI_ACTION_SAMPLE_LIST_ADD); + } + ImGui::SameLine(); + if (ImGui::Button(ICON_FA_FOLDER_OPEN "##SampleLoad")) { + doAction(GUI_ACTION_SAMPLE_LIST_OPEN); + } + ImGui::SameLine(); + if (ImGui::Button(ICON_FA_FLOPPY_O "##SampleSave")) { + doAction(GUI_ACTION_SAMPLE_LIST_SAVE); + } + ImGui::SameLine(); + if (ImGui::ArrowButton("SampleUp",ImGuiDir_Up)) { + doAction(GUI_ACTION_SAMPLE_LIST_MOVE_UP); + } + ImGui::SameLine(); + if (ImGui::ArrowButton("SampleDown",ImGuiDir_Down)) { + doAction(GUI_ACTION_SAMPLE_LIST_MOVE_DOWN); + } + ImGui::SameLine(); + if (ImGui::Button(ICON_FA_TIMES "##SampleDelete")) { + doAction(GUI_ACTION_SAMPLE_LIST_DELETE); + } + ImGui::SameLine(); + if (ImGui::Button(ICON_FA_VOLUME_UP "##PreviewSampleL")) { + doAction(GUI_ACTION_SAMPLE_LIST_PREVIEW); + } + ImGui::SameLine(); + if (ImGui::Button(ICON_FA_VOLUME_OFF "##StopSampleL")) { + doAction(GUI_ACTION_SAMPLE_LIST_STOP_PREVIEW); + } + ImGui::Separator(); + if (ImGui::BeginTable("SampleListScroll",1,ImGuiTableFlags_ScrollY)) { + actualSampleList(); + ImGui::EndTable(); + } + ImGui::Unindent(); + } + if (ImGui::IsWindowFocused(ImGuiFocusedFlags_ChildWindows)) curWindow=GUI_WINDOW_SAMPLE_LIST; + ImGui::End(); +} + +void FurnaceGUI::actualWaveList() { + float wavePreview[256]; + for (int i=0; i<(int)e->song.wave.size(); i++) { + DivWavetable* wave=e->song.wave[i]; + for (int i=0; ilen; i++) { + wavePreview[i]=wave->data[i]; + } + if (wave->len>0) wavePreview[wave->len]=wave->data[wave->len-1]; + ImGui::TableNextRow(); + ImGui::TableNextColumn(); + if (ImGui::Selectable(fmt::sprintf("%d##_WAVE%d\n",i,i).c_str(),curWave==i)) { + curWave=i; + } + if (ImGui::IsItemHovered()) { + if (ImGui::IsMouseDoubleClicked(ImGuiMouseButton_Left)) { + waveEditOpen=true; + } + } + ImGui::SameLine(); + PlotNoLerp(fmt::sprintf("##_WAVEP%d",i).c_str(),wavePreview,wave->len+1,0,NULL,0,wave->max); + } +} + +void FurnaceGUI::actualSampleList() { + for (int i=0; i<(int)e->song.sample.size(); i++) { + DivSample* sample=e->song.sample[i]; + ImGui::TableNextRow(); + ImGui::TableNextColumn(); + if (ImGui::Selectable(fmt::sprintf("%d: %s##_SAM%d",i,sample->name,i).c_str(),curSample==i)) { + curSample=i; + samplePos=0; + updateSampleTex=true; + } + if (ImGui::IsItemHovered()) { + ImGui::SetTooltip("Bank %d: %s",i/12,sampleNote[i%12]); + if (ImGui::IsMouseDoubleClicked(ImGuiMouseButton_Left)) { + sampleEditOpen=true; + } + } + } +} \ No newline at end of file diff --git a/src/gui/gui.cpp b/src/gui/gui.cpp index 980ca7c8..f3ae5a7f 100644 --- a/src/gui/gui.cpp +++ b/src/gui/gui.cpp @@ -1138,304 +1138,6 @@ void FurnaceGUI::drawSongInfo() { ImGui::End(); } -void FurnaceGUI::drawInsList() { - if (nextWindow==GUI_WINDOW_INS_LIST) { - insListOpen=true; - ImGui::SetNextWindowFocus(); - nextWindow=GUI_WINDOW_NOTHING; - } - if (!insListOpen) return; - if (ImGui::Begin("Instruments",&insListOpen)) { - if (ImGui::Button(ICON_FA_PLUS "##InsAdd")) { - doAction(GUI_ACTION_INS_LIST_ADD); - } - ImGui::SameLine(); - if (ImGui::Button(ICON_FA_FILES_O "##InsClone")) { - doAction(GUI_ACTION_INS_LIST_DUPLICATE); - } - ImGui::SameLine(); - if (ImGui::Button(ICON_FA_FOLDER_OPEN "##InsLoad")) { - doAction(GUI_ACTION_INS_LIST_OPEN); - } - ImGui::SameLine(); - if (ImGui::Button(ICON_FA_FLOPPY_O "##InsSave")) { - doAction(GUI_ACTION_INS_LIST_SAVE); - } - ImGui::SameLine(); - if (ImGui::ArrowButton("InsUp",ImGuiDir_Up)) { - doAction(GUI_ACTION_INS_LIST_MOVE_UP); - } - ImGui::SameLine(); - if (ImGui::ArrowButton("InsDown",ImGuiDir_Down)) { - doAction(GUI_ACTION_INS_LIST_MOVE_DOWN); - } - ImGui::SameLine(); - if (ImGui::Button(ICON_FA_TIMES "##InsDelete")) { - doAction(GUI_ACTION_INS_LIST_DELETE); - } - ImGui::Separator(); - if (ImGui::BeginTable("InsListScroll",1,ImGuiTableFlags_ScrollY)) { - if (settings.unifiedDataView) { - ImGui::TableNextRow(); - ImGui::TableNextColumn(); - ImGui::Text(ICON_FA_TASKS " Instruments"); - ImGui::Indent(); - } - - for (int i=0; i<(int)e->song.ins.size(); i++) { - DivInstrument* ins=e->song.ins[i]; - String name; - switch (ins->type) { - case DIV_INS_FM: - ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_FM]); - name=fmt::sprintf(ICON_FA_AREA_CHART " %.2X: %s##_INS%d",i,ins->name,i); - break; - case DIV_INS_STD: - ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_STD]); - name=fmt::sprintf(ICON_FA_BAR_CHART " %.2X: %s##_INS%d",i,ins->name,i); - break; - case DIV_INS_GB: - ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_GB]); - name=fmt::sprintf(ICON_FA_GAMEPAD " %.2X: %s##_INS%d",i,ins->name,i); - break; - case DIV_INS_C64: - ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_C64]); - name=fmt::sprintf(ICON_FA_KEYBOARD_O " %.2X: %s##_INS%d",i,ins->name,i); - break; - case DIV_INS_AMIGA: - ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_AMIGA]); - name=fmt::sprintf(ICON_FA_VOLUME_UP " %.2X: %s##_INS%d",i,ins->name,i); - break; - case DIV_INS_PCE: - ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_PCE]); - name=fmt::sprintf(ICON_FA_ID_BADGE " %.2X: %s##_INS%d",i,ins->name,i); - break; - case DIV_INS_AY: - ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_AY]); - name=fmt::sprintf(ICON_FA_BAR_CHART " %.2X: %s##_INS%d",i,ins->name,i); - break; - case DIV_INS_AY8930: - ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_AY8930]); - name=fmt::sprintf(ICON_FA_BAR_CHART " %.2X: %s##_INS%d",i,ins->name,i); - break; - case DIV_INS_TIA: - ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_TIA]); - name=fmt::sprintf(ICON_FA_BAR_CHART " %.2X: %s##_INS%d",i,ins->name,i); - break; - case DIV_INS_SAA1099: - ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_SAA1099]); - name=fmt::sprintf(ICON_FA_BAR_CHART " %.2X: %s##_INS%d",i,ins->name,i); - break; - case DIV_INS_VIC: - ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_VIC]); - name=fmt::sprintf(ICON_FA_BAR_CHART " %.2X: %s##_INS%d",i,ins->name,i); - break; - case DIV_INS_PET: - ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_PET]); - name=fmt::sprintf(ICON_FA_SQUARE " %.2X: %s##_INS%d",i,ins->name,i); - break; - case DIV_INS_VRC6: - ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_VRC6]); - name=fmt::sprintf(ICON_FA_BAR_CHART " %.2X: %s##_INS%d",i,ins->name,i); - break; - case DIV_INS_OPLL: - ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_OPLL]); - name=fmt::sprintf(ICON_FA_AREA_CHART " %.2X: %s##_INS%d",i,ins->name,i); - break; - case DIV_INS_OPL: - ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_OPL]); - name=fmt::sprintf(ICON_FA_AREA_CHART " %.2X: %s##_INS%d",i,ins->name,i); - break; - case DIV_INS_FDS: - ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_FDS]); - name=fmt::sprintf(ICON_FA_FLOPPY_O " %.2X: %s##_INS%d",i,ins->name,i); - break; - case DIV_INS_VBOY: - ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_VBOY]); - name=fmt::sprintf(ICON_FA_BINOCULARS " %.2X: %s##_INS%d",i,ins->name,i); - break; - case DIV_INS_N163: - ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_N163]); - name=fmt::sprintf(ICON_FA_CALCULATOR " %.2X: %s##_INS%d",i,ins->name,i); - break; - case DIV_INS_SCC: - ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_SCC]); - name=fmt::sprintf(ICON_FA_CALCULATOR " %.2X: %s##_INS%d",i,ins->name,i); - break; - case DIV_INS_OPZ: - ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_OPZ]); - name=fmt::sprintf(ICON_FA_AREA_CHART " %.2X: %s##_INS%d",i,ins->name,i); - break; - case DIV_INS_POKEY: - ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_POKEY]); - name=fmt::sprintf(ICON_FA_BAR_CHART " %.2X: %s##_INS%d",i,ins->name,i); - break; - case DIV_INS_BEEPER: - ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_BEEPER]); - name=fmt::sprintf(ICON_FA_SQUARE " %.2X: %s##_INS%d",i,ins->name,i); - break; - case DIV_INS_SWAN: - ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_SWAN]); - name=fmt::sprintf(ICON_FA_GAMEPAD " %.2X: %s##_INS%d",i,ins->name,i); - break; - case DIV_INS_MIKEY: - ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_MIKEY]); - name=fmt::sprintf(ICON_FA_BAR_CHART " %.2X: %s##_INS%d",i,ins->name,i); - break; - case DIV_INS_VERA: - ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_VERA]); - name=fmt::sprintf(ICON_FA_KEYBOARD_O " %.2X: %s##_INS%d",i,ins->name,i); - break; - case DIV_INS_X1_010: - ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_X1_010]); - name=fmt::sprintf(ICON_FA_BAR_CHART " %.2X: %s##_INS%d",i,ins->name,i); - break; - default: - ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_UNKNOWN]); - name=fmt::sprintf(ICON_FA_QUESTION " %.2X: %s##_INS%d",i,ins->name,i); - break; - } - ImGui::TableNextRow(); - ImGui::TableNextColumn(); - if (ImGui::Selectable(name.c_str(),curIns==i)) { - curIns=i; - } - if (settings.insFocusesPattern && patternOpen && ImGui::IsItemActivated()) { - nextWindow=GUI_WINDOW_PATTERN; - curIns=i; - } - ImGui::PopStyleColor(); - if (ImGui::IsItemHovered()) { - ImGui::SetTooltip("%s",(ins->type>DIV_INS_MAX)?"Unknown":insTypes[ins->type]); - if (ImGui::IsMouseDoubleClicked(ImGuiMouseButton_Left)) { - insEditOpen=true; - nextWindow=GUI_WINDOW_INS_EDIT; - } - } - } - - if (settings.unifiedDataView) { - ImGui::Unindent(); - - ImGui::TableNextRow(); - ImGui::TableNextColumn(); - ImGui::Text(ICON_FA_AREA_CHART " Wavetables"); - ImGui::Indent(); - actualWaveList(); - ImGui::Unindent(); - - ImGui::TableNextRow(); - ImGui::TableNextColumn(); - ImGui::Text(ICON_FA_VOLUME_UP " Samples"); - ImGui::Indent(); - actualSampleList(); - ImGui::Unindent(); - } - - ImGui::EndTable(); - } - } - if (ImGui::IsWindowFocused(ImGuiFocusedFlags_ChildWindows)) curWindow=GUI_WINDOW_INS_LIST; - ImGui::End(); -} - -const char* sampleNote[12]={ - "C", "C#", "D", "D#", "E", "F", "F#", "G", "G#", "A", "A#", "B" -}; - - -void FurnaceGUI::actualWaveList() { - float wavePreview[256]; - for (int i=0; i<(int)e->song.wave.size(); i++) { - DivWavetable* wave=e->song.wave[i]; - for (int i=0; ilen; i++) { - wavePreview[i]=wave->data[i]; - } - if (wave->len>0) wavePreview[wave->len]=wave->data[wave->len-1]; - ImGui::TableNextRow(); - ImGui::TableNextColumn(); - if (ImGui::Selectable(fmt::sprintf("%d##_WAVE%d\n",i,i).c_str(),curWave==i)) { - curWave=i; - } - if (ImGui::IsItemHovered()) { - if (ImGui::IsMouseDoubleClicked(ImGuiMouseButton_Left)) { - waveEditOpen=true; - } - } - ImGui::SameLine(); - PlotNoLerp(fmt::sprintf("##_WAVEP%d",i).c_str(),wavePreview,wave->len+1,0,NULL,0,wave->max); - } -} - -void FurnaceGUI::actualSampleList() { - for (int i=0; i<(int)e->song.sample.size(); i++) { - DivSample* sample=e->song.sample[i]; - ImGui::TableNextRow(); - ImGui::TableNextColumn(); - if (ImGui::Selectable(fmt::sprintf("%d: %s##_SAM%d",i,sample->name,i).c_str(),curSample==i)) { - curSample=i; - samplePos=0; - updateSampleTex=true; - } - if (ImGui::IsItemHovered()) { - ImGui::SetTooltip("Bank %d: %s",i/12,sampleNote[i%12]); - if (ImGui::IsMouseDoubleClicked(ImGuiMouseButton_Left)) { - sampleEditOpen=true; - } - } - } -} - -void FurnaceGUI::drawSampleList() { - if (nextWindow==GUI_WINDOW_SAMPLE_LIST) { - sampleListOpen=true; - ImGui::SetNextWindowFocus(); - nextWindow=GUI_WINDOW_NOTHING; - } - if (!sampleListOpen) return; - if (ImGui::Begin("Samples",&sampleListOpen)) { - if (ImGui::Button(ICON_FA_PLUS "##SampleAdd")) { - doAction(GUI_ACTION_SAMPLE_LIST_ADD); - } - ImGui::SameLine(); - if (ImGui::Button(ICON_FA_FOLDER_OPEN "##SampleLoad")) { - doAction(GUI_ACTION_SAMPLE_LIST_OPEN); - } - ImGui::SameLine(); - if (ImGui::Button(ICON_FA_FLOPPY_O "##SampleSave")) { - doAction(GUI_ACTION_SAMPLE_LIST_SAVE); - } - ImGui::SameLine(); - if (ImGui::ArrowButton("SampleUp",ImGuiDir_Up)) { - doAction(GUI_ACTION_SAMPLE_LIST_MOVE_UP); - } - ImGui::SameLine(); - if (ImGui::ArrowButton("SampleDown",ImGuiDir_Down)) { - doAction(GUI_ACTION_SAMPLE_LIST_MOVE_DOWN); - } - ImGui::SameLine(); - if (ImGui::Button(ICON_FA_TIMES "##SampleDelete")) { - doAction(GUI_ACTION_SAMPLE_LIST_DELETE); - } - ImGui::SameLine(); - if (ImGui::Button(ICON_FA_VOLUME_UP "##PreviewSampleL")) { - doAction(GUI_ACTION_SAMPLE_LIST_PREVIEW); - } - ImGui::SameLine(); - if (ImGui::Button(ICON_FA_VOLUME_OFF "##StopSampleL")) { - doAction(GUI_ACTION_SAMPLE_LIST_STOP_PREVIEW); - } - ImGui::Separator(); - if (ImGui::BeginTable("SampleListScroll",1,ImGuiTableFlags_ScrollY)) { - actualSampleList(); - ImGui::EndTable(); - } - ImGui::Unindent(); - } - if (ImGui::IsWindowFocused(ImGuiFocusedFlags_ChildWindows)) curWindow=GUI_WINDOW_SAMPLE_LIST; - ImGui::End(); -} - void FurnaceGUI::drawMixer() { if (nextWindow==GUI_WINDOW_MIXER) { mixerOpen=true; diff --git a/src/gui/insEdit.cpp b/src/gui/insEdit.cpp index ab206e16..a1ba5f57 100644 --- a/src/gui/insEdit.cpp +++ b/src/gui/insEdit.cpp @@ -1946,132 +1946,4 @@ void FurnaceGUI::drawInsEdit() { } if (ImGui::IsWindowFocused(ImGuiFocusedFlags_ChildWindows)) curWindow=GUI_WINDOW_INS_EDIT; ImGui::End(); -} - -#undef P -#undef PARAMETER - -void FurnaceGUI::drawWaveList() { - if (nextWindow==GUI_WINDOW_WAVE_LIST) { - waveListOpen=true; - ImGui::SetNextWindowFocus(); - nextWindow=GUI_WINDOW_NOTHING; - } - if (!waveListOpen) return; - if (ImGui::Begin("Wavetables",&waveListOpen)) { - if (ImGui::Button(ICON_FA_PLUS "##WaveAdd")) { - doAction(GUI_ACTION_WAVE_LIST_ADD); - } - ImGui::SameLine(); - if (ImGui::Button(ICON_FA_FILES_O "##WaveClone")) { - doAction(GUI_ACTION_WAVE_LIST_DUPLICATE); - } - ImGui::SameLine(); - if (ImGui::Button(ICON_FA_FOLDER_OPEN "##WaveLoad")) { - doAction(GUI_ACTION_WAVE_LIST_OPEN); - } - ImGui::SameLine(); - if (ImGui::Button(ICON_FA_FLOPPY_O "##WaveSave")) { - doAction(GUI_ACTION_WAVE_LIST_SAVE); - } - ImGui::SameLine(); - if (ImGui::ArrowButton("WaveUp",ImGuiDir_Up)) { - doAction(GUI_ACTION_WAVE_LIST_UP); - } - ImGui::SameLine(); - if (ImGui::ArrowButton("WaveDown",ImGuiDir_Down)) { - doAction(GUI_ACTION_WAVE_LIST_DOWN); - } - ImGui::SameLine(); - if (ImGui::Button(ICON_FA_TIMES "##WaveDelete")) { - doAction(GUI_ACTION_WAVE_LIST_DELETE); - } - ImGui::Separator(); - if (ImGui::BeginTable("WaveListScroll",1,ImGuiTableFlags_ScrollY)) { - actualWaveList(); - ImGui::EndTable(); - } - } - if (ImGui::IsWindowFocused(ImGuiFocusedFlags_ChildWindows)) curWindow=GUI_WINDOW_WAVE_LIST; - ImGui::End(); -} - -void FurnaceGUI::drawWaveEdit() { - if (nextWindow==GUI_WINDOW_WAVE_EDIT) { - waveEditOpen=true; - ImGui::SetNextWindowFocus(); - nextWindow=GUI_WINDOW_NOTHING; - } - if (!waveEditOpen) return; - float wavePreview[256]; - ImGui::SetNextWindowSizeConstraints(ImVec2(450.0f*dpiScale,300.0f*dpiScale),ImVec2(scrW*dpiScale,scrH*dpiScale)); - if (ImGui::Begin("Wavetable Editor",&waveEditOpen,settings.allowEditDocking?0:ImGuiWindowFlags_NoDocking)) { - if (curWave<0 || curWave>=(int)e->song.wave.size()) { - ImGui::Text("no wavetable selected"); - } else { - DivWavetable* wave=e->song.wave[curWave]; - ImGui::Text("Width"); - if (ImGui::IsItemHovered()) { - ImGui::SetTooltip("use a width of:\n- any on Amiga/X1-010\n- 32 on Game Boy, PC Engine and WonderSwan\nany other widths will be scaled during playback."); - } - ImGui::SameLine(); - ImGui::SetNextItemWidth(128.0f*dpiScale); - if (ImGui::InputInt("##_WTW",&wave->len,1,2)) { - if (wave->len>256) wave->len=256; - if (wave->len<1) wave->len=1; - e->notifyWaveChange(curWave); - if (wavePreviewOn) e->previewWave(curWave,wavePreviewNote); - MARK_MODIFIED; - } - ImGui::SameLine(); - ImGui::Text("Height"); - if (ImGui::IsItemHovered()) { - ImGui::SetTooltip("use a height of:\n- 15 for Game Boy and WonderSwan\n- 31 for PC Engine\nany other heights will be scaled during playback."); - } - ImGui::SameLine(); - ImGui::SetNextItemWidth(128.0f*dpiScale); - if (ImGui::InputInt("##_WTH",&wave->max,1,2)) { - if (wave->max>255) wave->max=255; - if (wave->max<1) wave->max=1; - e->notifyWaveChange(curWave); - MARK_MODIFIED; - } - for (int i=0; ilen; i++) { - if (wave->data[i]>wave->max) wave->data[i]=wave->max; - wavePreview[i]=wave->data[i]; - } - if (wave->len>0) wavePreview[wave->len]=wave->data[wave->len-1]; - ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x); //wavetable text input size found here - if (ImGui::InputText("##MMLWave",&mmlStringW)) { - decodeMMLStrW(mmlStringW,wave->data,wave->len,wave->max); - } - if (!ImGui::IsItemActive()) { - encodeMMLStr(mmlStringW,wave->data,wave->len,-1,-1); - } - - ImGui::PushStyleVar(ImGuiStyleVar_FramePadding,ImVec2(0.0f,0.0f)); - - ImVec2 contentRegion=ImGui::GetContentRegionAvail(); //wavetable graph size determined here - if (ImGui::GetContentRegionAvail().y > (ImGui::GetContentRegionAvail().x / 2.0f)) { - contentRegion=ImVec2(ImGui::GetContentRegionAvail().x,ImGui::GetContentRegionAvail().x / 2.0f); - } - PlotNoLerp("##Waveform",wavePreview,wave->len+1,0,NULL,0,wave->max,contentRegion); - if (ImGui::IsItemClicked(ImGuiMouseButton_Left)) { - waveDragStart=ImGui::GetItemRectMin(); - waveDragAreaSize=contentRegion; - waveDragMin=0; - waveDragMax=wave->max; - waveDragLen=wave->len; - waveDragActive=true; - waveDragTarget=wave->data; - processDrags(ImGui::GetMousePos().x,ImGui::GetMousePos().y); - e->notifyWaveChange(curWave); - modified=true; - } - ImGui::PopStyleVar(); - } - } - if (ImGui::IsWindowFocused(ImGuiFocusedFlags_ChildWindows)) curWindow=GUI_WINDOW_WAVE_EDIT; - ImGui::End(); -} - +} \ No newline at end of file diff --git a/src/gui/waveEdit.cpp b/src/gui/waveEdit.cpp new file mode 100644 index 00000000..09ec95b1 --- /dev/null +++ b/src/gui/waveEdit.cpp @@ -0,0 +1,82 @@ +#include "gui.h" +#include "plot_nolerp.h" +#include "misc/cpp/imgui_stdlib.h" + +void FurnaceGUI::drawWaveEdit() { + if (nextWindow==GUI_WINDOW_WAVE_EDIT) { + waveEditOpen=true; + ImGui::SetNextWindowFocus(); + nextWindow=GUI_WINDOW_NOTHING; + } + if (!waveEditOpen) return; + float wavePreview[256]; + ImGui::SetNextWindowSizeConstraints(ImVec2(450.0f*dpiScale,300.0f*dpiScale),ImVec2(scrW*dpiScale,scrH*dpiScale)); + if (ImGui::Begin("Wavetable Editor",&waveEditOpen,settings.allowEditDocking?0:ImGuiWindowFlags_NoDocking)) { + if (curWave<0 || curWave>=(int)e->song.wave.size()) { + ImGui::Text("no wavetable selected"); + } else { + DivWavetable* wave=e->song.wave[curWave]; + ImGui::Text("Width"); + if (ImGui::IsItemHovered()) { + ImGui::SetTooltip("use a width of:\n- any on Amiga/X1-010\n- 32 on Game Boy, PC Engine and WonderSwan\nany other widths will be scaled during playback."); + } + ImGui::SameLine(); + ImGui::SetNextItemWidth(128.0f*dpiScale); + if (ImGui::InputInt("##_WTW",&wave->len,1,2)) { + if (wave->len>256) wave->len=256; + if (wave->len<1) wave->len=1; + e->notifyWaveChange(curWave); + if (wavePreviewOn) e->previewWave(curWave,wavePreviewNote); + MARK_MODIFIED; + } + ImGui::SameLine(); + ImGui::Text("Height"); + if (ImGui::IsItemHovered()) { + ImGui::SetTooltip("use a height of:\n- 15 for Game Boy and WonderSwan\n- 31 for PC Engine\nany other heights will be scaled during playback."); + } + ImGui::SameLine(); + ImGui::SetNextItemWidth(128.0f*dpiScale); + if (ImGui::InputInt("##_WTH",&wave->max,1,2)) { + if (wave->max>255) wave->max=255; + if (wave->max<1) wave->max=1; + e->notifyWaveChange(curWave); + MARK_MODIFIED; + } + for (int i=0; ilen; i++) { + if (wave->data[i]>wave->max) wave->data[i]=wave->max; + wavePreview[i]=wave->data[i]; + } + if (wave->len>0) wavePreview[wave->len]=wave->data[wave->len-1]; + ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x); // wavetable text input size found here + if (ImGui::InputText("##MMLWave",&mmlStringW)) { + decodeMMLStrW(mmlStringW,wave->data,wave->len,wave->max); + } + if (!ImGui::IsItemActive()) { + encodeMMLStr(mmlStringW,wave->data,wave->len,-1,-1); + } + + ImGui::PushStyleVar(ImGuiStyleVar_FramePadding,ImVec2(0.0f,0.0f)); + + ImVec2 contentRegion=ImGui::GetContentRegionAvail(); // wavetable graph size determined here + if (ImGui::GetContentRegionAvail().y > (ImGui::GetContentRegionAvail().x / 2.0f)) { + contentRegion=ImVec2(ImGui::GetContentRegionAvail().x,ImGui::GetContentRegionAvail().x / 2.0f); + } + PlotNoLerp("##Waveform",wavePreview,wave->len+1,0,NULL,0,wave->max,contentRegion); + if (ImGui::IsItemClicked(ImGuiMouseButton_Left)) { + waveDragStart=ImGui::GetItemRectMin(); + waveDragAreaSize=contentRegion; + waveDragMin=0; + waveDragMax=wave->max; + waveDragLen=wave->len; + waveDragActive=true; + waveDragTarget=wave->data; + processDrags(ImGui::GetMousePos().x,ImGui::GetMousePos().y); + e->notifyWaveChange(curWave); + modified=true; + } + ImGui::PopStyleVar(); + } + } + if (ImGui::IsWindowFocused(ImGuiFocusedFlags_ChildWindows)) curWindow=GUI_WINDOW_WAVE_EDIT; + ImGui::End(); +} \ No newline at end of file From 7e5c27c5b7eb835db861e52e2503989056cba708 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Mon, 21 Mar 2022 17:34:43 -0500 Subject: [PATCH 19/40] GUI: massive code split gui.cpp now less than 3000 lines --- CMakeLists.txt | 19 + src/gui/about.cpp | 211 ++ src/gui/actionUtil.h | 21 + src/gui/channels.cpp | 53 + src/gui/compatFlags.cpp | 136 ++ src/gui/cursor.cpp | 285 +++ src/gui/dataList.cpp | 19 + src/gui/debugWindow.cpp | 250 ++ src/gui/doAction.cpp | 687 ++++++ src/gui/editControls.cpp | 330 +++ src/gui/editing.cpp | 1015 ++++++++ src/gui/gui.cpp | 4802 +------------------------------------- src/gui/gui.h | 7 + src/gui/guiConst.cpp | 46 +- src/gui/guiConst.h | 3 +- src/gui/mixer.cpp | 59 + src/gui/newSong.cpp | 90 + src/gui/osc.cpp | 47 + src/gui/piano.cpp | 46 + src/gui/presets.cpp | 639 +++++ src/gui/regView.cpp | 71 + src/gui/settings.cpp | 397 ++++ src/gui/songInfo.cpp | 173 ++ src/gui/songNotes.cpp | 37 + src/gui/stats.cpp | 50 + src/gui/sysConf.cpp | 371 +++ src/gui/volMeter.cpp | 111 + src/gui/waveEdit.cpp | 19 + 28 files changed, 5201 insertions(+), 4793 deletions(-) create mode 100644 src/gui/about.cpp create mode 100644 src/gui/actionUtil.h create mode 100644 src/gui/channels.cpp create mode 100644 src/gui/compatFlags.cpp create mode 100644 src/gui/cursor.cpp create mode 100644 src/gui/debugWindow.cpp create mode 100644 src/gui/doAction.cpp create mode 100644 src/gui/editControls.cpp create mode 100644 src/gui/editing.cpp create mode 100644 src/gui/mixer.cpp create mode 100644 src/gui/newSong.cpp create mode 100644 src/gui/osc.cpp create mode 100644 src/gui/piano.cpp create mode 100644 src/gui/presets.cpp create mode 100644 src/gui/regView.cpp create mode 100644 src/gui/songInfo.cpp create mode 100644 src/gui/songNotes.cpp create mode 100644 src/gui/stats.cpp create mode 100644 src/gui/sysConf.cpp create mode 100644 src/gui/volMeter.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 8f51dc9e..f09c88a6 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -357,14 +357,33 @@ src/gui/fileDialog.cpp src/gui/intConst.cpp src/gui/guiConst.cpp +src/gui/about.cpp +src/gui/channels.cpp +src/gui/compatFlags.cpp +src/gui/cursor.cpp src/gui/dataList.cpp +src/gui/debugWindow.cpp +src/gui/doAction.cpp +src/gui/editing.cpp +src/gui/editControls.cpp src/gui/insEdit.cpp +src/gui/mixer.cpp +src/gui/newSong.cpp src/gui/orders.cpp +src/gui/osc.cpp src/gui/pattern.cpp +src/gui/piano.cpp +src/gui/presets.cpp +src/gui/regView.cpp src/gui/sampleEdit.cpp src/gui/settings.cpp +src/gui/songInfo.cpp +src/gui/songNotes.cpp +src/gui/stats.cpp +src/gui/sysConf.cpp src/gui/util.cpp src/gui/waveEdit.cpp +src/gui/volMeter.cpp src/gui/gui.cpp ) diff --git a/src/gui/about.cpp b/src/gui/about.cpp new file mode 100644 index 00000000..cd5888c6 --- /dev/null +++ b/src/gui/about.cpp @@ -0,0 +1,211 @@ +/** + * Furnace Tracker - multi-system chiptune tracker + * Copyright (C) 2021-2022 tildearrow and contributors + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include "gui.h" + +const char* aboutLine[]={ + "tildearrow", + "is proud to present", + "", + ("Furnace " DIV_VERSION), + "", + "the free software chiptune tracker,", + "compatible with DefleMask modules.", + "", + "zero disassembly.", + "just clean-room design,", + "time and dedication.", + "", + "> CREDITS <", + "", + "-- program --", + "tildearrow", + "akumanatt", + "cam900", + "djtuBIG-MaliceX", + "laoo", + "superctr", + "", + "-- graphics/UI design --", + "tildearrow", + "BlastBrothers", + "", + "-- documentation --", + "tildearrow", + "freq-mod", + "nicco1690", + "DeMOSic", + "cam900", + "", + "-- demo songs --", + "0x5066", + "ActualNK358", + "breakthetargets", + "CaptainMalware", + "kleeder", + "Mahbod Karamoozian", + "nicco1690", + "NikonTeen", + "SuperJet Spade", + "TheDuccinator", + "TheRealHedgehogSonic", + "tildearrow", + "Ultraprogramer", + "", + "-- additional feedback/fixes --", + "fd", + "OPNA2608", + "plane", + "TheEssem", + "", + "powered by:", + "Dear ImGui by Omar Cornut", + "SDL2 by Sam Lantinga", + "zlib by Jean-loup Gailly", + "and Mark Adler", + "libsndfile by Erik de Castro Lopo", + "Nuked-OPM & Nuked-OPN2 by Nuke.YKT", + "ymfm by Aaron Giles", + "MAME SN76496 by Nicola Salmoria", + "MAME AY-3-8910 by Couriersud", + "with AY8930 fixes by Eulous", + "MAME SAA1099 by Juergen Buchmueller and Manuel Abadia", + "SAASound", + "SameBoy by Lior Halphon", + "Mednafen PCE", + "puNES by FHorse", + "reSID by Dag Lem", + "Stella by Stella Team", + "QSound emulator by Ian Karlsson and Valley Bell", + "", + "greetings to:", + "Delek", + "fd", + "ILLUMIDARO", + "all members of Deflers of Noice!", + "", + "copyright © 2021-2022 tildearrow", + "(and contributors).", + "licensed under GPLv2+! see", + "LICENSE for more information.", + "", + "help Furnace grow:", + "https://github.com/tildearrow/furnace", + "", + "contact tildearrow at:", + "https://tildearrow.org/?p=contact", + "", + "disclaimer:", + "despite the fact this program works", + "with the .dmf file format, it is NOT", + "affiliated with Delek or DefleMask in", + "any way, nor it is a replacement for", + "the original program.", + "", + "it also comes with ABSOLUTELY NO WARRANTY.", + "", + "thanks to all contributors/bug reporters!" +}; + +const size_t aboutCount=sizeof(aboutLine)/sizeof(aboutLine[0]); + +void FurnaceGUI::drawAbout() { + // do stuff + if (ImGui::Begin("About Furnace",NULL,ImGuiWindowFlags_Modal|ImGuiWindowFlags_NoMove|ImGuiWindowFlags_NoResize|ImGuiWindowFlags_NoDocking|ImGuiWindowFlags_NoTitleBar)) { + ImGui::SetWindowPos(ImVec2(0,0)); + ImGui::SetWindowSize(ImVec2(scrW*dpiScale,scrH*dpiScale)); + ImGui::PushFont(bigFont); + ImDrawList* dl=ImGui::GetWindowDrawList(); + float r=0; + float g=0; + float b=0; + float peakMix=settings.partyTime?((peak[0]+peak[1])*0.5):0.3; + ImGui::ColorConvertHSVtoRGB(aboutHue,1.0,0.25+MIN(0.75f,peakMix*0.75f),r,g,b); + dl->AddRectFilled(ImVec2(0,0),ImVec2(scrW*dpiScale,scrH*dpiScale),0xff000000); + bool skip=false; + bool skip2=false; + for (int i=(-80-sin(double(aboutSin)*2*M_PI/120.0)*80.0)*2; iAddRectFilled(ImVec2(i*dpiScale,j*dpiScale),ImVec2((i+160)*dpiScale,(j+160)*dpiScale),ImGui::GetColorU32(ImVec4(r*0.25,g*0.25,b*0.25,1.0))); + } + } + + skip=false; + skip2=false; + for (int i=(-80-cos(double(aboutSin)*2*M_PI/120.0)*80.0)*2; iAddRectFilled(ImVec2(i*dpiScale,j*dpiScale),ImVec2((i+160)*dpiScale,(j+160)*dpiScale),ImGui::GetColorU32(ImVec4(r*0.5,g*0.5,b*0.5,1.0))); + } + } + + skip=false; + skip2=false; + for (int i=(-160+fmod(aboutSin*2,160))*2; iAddRectFilled(ImVec2(i*dpiScale,j*dpiScale),ImVec2((i+160)*dpiScale,(j+160)*dpiScale),ImGui::GetColorU32(ImVec4(r*0.75,g*0.75,b*0.75,1.0))); + } + } + + for (size_t i=0; iscrH*dpiScale) continue; + dl->AddText(bigFont,bigFont->FontSize, + ImVec2(posX+dpiScale,posY+dpiScale), + 0xff000000,aboutLine[i]); + dl->AddText(bigFont,bigFont->FontSize, + ImVec2(posX+dpiScale,posY-dpiScale), + 0xff000000,aboutLine[i]); + dl->AddText(bigFont,bigFont->FontSize, + ImVec2(posX-dpiScale,posY+dpiScale), + 0xff000000,aboutLine[i]); + dl->AddText(bigFont,bigFont->FontSize, + ImVec2(posX-dpiScale,posY-dpiScale), + 0xff000000,aboutLine[i]); + dl->AddText(bigFont,bigFont->FontSize, + ImVec2(posX,posY), + 0xffffffff,aboutLine[i]); + } + ImGui::PopFont(); + + float timeScale=60.0f*ImGui::GetIO().DeltaTime; + + aboutHue+=(0.001+peakMix*0.004)*timeScale; + aboutScroll+=(2+(peakMix>0.78)*3)*timeScale; + aboutSin+=(1+(peakMix>0.75)*2)*timeScale; + + while (aboutHue>1) aboutHue--; + while (aboutSin>=2400) aboutSin-=2400; + if (aboutScroll>(42*aboutCount+scrH)) aboutScroll=-20; + } + if (ImGui::IsWindowFocused(ImGuiFocusedFlags_ChildWindows)) curWindow=GUI_WINDOW_ABOUT; + ImGui::End(); +} \ No newline at end of file diff --git a/src/gui/actionUtil.h b/src/gui/actionUtil.h new file mode 100644 index 00000000..38ed9a5c --- /dev/null +++ b/src/gui/actionUtil.h @@ -0,0 +1,21 @@ +#define DETERMINE_FIRST \ + int firstChannel=0; \ + for (int i=0; igetTotalChannelCount(); i++) { \ + if (e->song.chanShow[i]) { \ + firstChannel=i; \ + break; \ + } \ + } \ + +#define DETERMINE_LAST \ + int lastChannel=0; \ + for (int i=e->getTotalChannelCount()-1; i>=0; i--) { \ + if (e->song.chanShow[i]) { \ + lastChannel=i+1; \ + break; \ + } \ + } + +#define DETERMINE_FIRST_LAST \ + DETERMINE_FIRST \ + DETERMINE_LAST \ No newline at end of file diff --git a/src/gui/channels.cpp b/src/gui/channels.cpp new file mode 100644 index 00000000..5b2a7e0c --- /dev/null +++ b/src/gui/channels.cpp @@ -0,0 +1,53 @@ +/** + * Furnace Tracker - multi-system chiptune tracker + * Copyright (C) 2021-2022 tildearrow and contributors + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include "gui.h" +#include "misc/cpp/imgui_stdlib.h" + +void FurnaceGUI::drawChannels() { + if (nextWindow==GUI_WINDOW_CHANNELS) { + channelsOpen=true; + ImGui::SetNextWindowFocus(); + nextWindow=GUI_WINDOW_NOTHING; + } + if (!channelsOpen) return; + if (ImGui::Begin("Channels",&channelsOpen)) { + if (ImGui::BeginTable("ChannelList",3)) { + ImGui::TableSetupColumn("c0",ImGuiTableColumnFlags_WidthFixed,0.0); + ImGui::TableSetupColumn("c1",ImGuiTableColumnFlags_WidthStretch,0.0); + ImGui::TableSetupColumn("c2",ImGuiTableColumnFlags_WidthFixed,48.0f*dpiScale); + for (int i=0; igetTotalChannelCount(); i++) { + ImGui::PushID(i); + ImGui::TableNextRow(); + ImGui::TableNextColumn(); + ImGui::Checkbox("##Visible",&e->song.chanShow[i]); + ImGui::TableNextColumn(); + ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x); + ImGui::InputTextWithHint("##ChanName",e->getChannelName(i),&e->song.chanName[i]); + ImGui::TableNextColumn(); + ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x); + ImGui::InputTextWithHint("##ChanShortName",e->getChannelShortName(i),&e->song.chanShortName[i]); + ImGui::PopID(); + } + ImGui::EndTable(); + } + } + if (ImGui::IsWindowFocused(ImGuiFocusedFlags_ChildWindows)) curWindow=GUI_WINDOW_CHANNELS; + ImGui::End(); +} \ No newline at end of file diff --git a/src/gui/compatFlags.cpp b/src/gui/compatFlags.cpp new file mode 100644 index 00000000..46424fc5 --- /dev/null +++ b/src/gui/compatFlags.cpp @@ -0,0 +1,136 @@ +/** + * Furnace Tracker - multi-system chiptune tracker + * Copyright (C) 2021-2022 tildearrow and contributors + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include "gui.h" + +void FurnaceGUI::drawCompatFlags() { + if (nextWindow==GUI_WINDOW_COMPAT_FLAGS) { + compatFlagsOpen=true; + ImGui::SetNextWindowFocus(); + nextWindow=GUI_WINDOW_NOTHING; + } + if (!compatFlagsOpen) return; + if (ImGui::Begin("Compatibility Flags",&compatFlagsOpen)) { + ImGui::TextWrapped("these flags are designed to provide better DefleMask/older Furnace compatibility."); + ImGui::Checkbox("Limit slide range",&e->song.limitSlides); + if (ImGui::IsItemHovered()) { + ImGui::SetTooltip("when enabled, slides are limited to a compatible range.\nmay cause problems with slides in negative octaves."); + } + ImGui::Checkbox("Linear pitch control",&e->song.linearPitch); + if (ImGui::IsItemHovered()) { + ImGui::SetTooltip("linear pitch:\n- slides work in frequency/period space\n- E5xx and 04xx effects work in tonality space\nnon-linear pitch:\n- slides work in frequency/period space\n- E5xx and 04xx effects work on frequency/period space"); + } + ImGui::Checkbox("Proper noise layout on NES and PC Engine",&e->song.properNoiseLayout); + if (ImGui::IsItemHovered()) { + ImGui::SetTooltip("use a proper noise channel note mapping (0-15) instead of a rather unusual compatible one.\nunlocks all noise frequencies on PC Engine."); + } + ImGui::Checkbox("Game Boy instrument duty is wave volume",&e->song.waveDutyIsVol); + if (ImGui::IsItemHovered()) { + ImGui::SetTooltip("if enabled, an instrument with duty macro in the wave channel will be mapped to wavetable volume."); + } + + ImGui::Checkbox("Restart macro on portamento",&e->song.resetMacroOnPorta); + if (ImGui::IsItemHovered()) { + ImGui::SetTooltip("when enabled, a portamento effect will reset the channel's macro if used in combination with a note."); + } + ImGui::Checkbox("Legacy volume slides",&e->song.legacyVolumeSlides); + if (ImGui::IsItemHovered()) { + ImGui::SetTooltip("simulate glitchy volume slide behavior by silently overflowing the volume when the slide goes below 0."); + } + ImGui::Checkbox("Compatible arpeggio",&e->song.compatibleArpeggio); + if (ImGui::IsItemHovered()) { + ImGui::SetTooltip("delay arpeggio by one tick on every new note."); + } + ImGui::Checkbox("Reset slides after note off",&e->song.noteOffResetsSlides); + if (ImGui::IsItemHovered()) { + ImGui::SetTooltip("when enabled, note off will reset the channel's slide effect."); + } + ImGui::Checkbox("Reset portamento after reaching target",&e->song.targetResetsSlides); + if (ImGui::IsItemHovered()) { + ImGui::SetTooltip("when enabled, the slide effect is disabled after it reaches its target."); + } + ImGui::Checkbox("Ignore duplicate slide effects",&e->song.ignoreDuplicateSlides); + if (ImGui::IsItemHovered()) { + ImGui::SetTooltip("if this is on, only the first slide of a row in a channel will be considered."); + } + ImGui::Checkbox("Continuous vibrato",&e->song.continuousVibrato); + if (ImGui::IsItemHovered()) { + ImGui::SetTooltip("when enabled, vibrato will not be reset on a new note."); + } + ImGui::Checkbox("Broken DAC mode",&e->song.brokenDACMode); + if (ImGui::IsItemHovered()) { + ImGui::SetTooltip("when enabled, the DAC in YM2612 will be disabled if there isn't any sample playing."); + } + ImGui::Checkbox("Auto-insert one tick gap between notes",&e->song.oneTickCut); + if (ImGui::IsItemHovered()) { + ImGui::SetTooltip("when enabled, a one-tick note cut will be inserted between non-legato/non-portamento notes.\nthis simulates the behavior of some Amiga/SNES music engines."); + } + + ImGui::Text("Loop modality:"); + if (ImGui::RadioButton("Reset channels",e->song.loopModality==0)) { + e->song.loopModality=0; + } + if (ImGui::IsItemHovered()) { + ImGui::SetTooltip("select to reset channels on loop. may trigger a voltage click on every loop!"); + } + if (ImGui::RadioButton("Soft reset channels",e->song.loopModality==1)) { + e->song.loopModality=1; + } + if (ImGui::IsItemHovered()) { + ImGui::SetTooltip("select to turn channels off on loop."); + } + if (ImGui::RadioButton("Do nothing",e->song.loopModality==2)) { + e->song.loopModality=2; + } + if (ImGui::IsItemHovered()) { + ImGui::SetTooltip("select to not reset channels on loop."); + } + + ImGui::Separator(); + + ImGui::TextWrapped("the following flags are for compatibility with older Furnace versions."); + + ImGui::Checkbox("Arpeggio inhibits non-porta slides",&e->song.arpNonPorta); + if (ImGui::IsItemHovered()) { + ImGui::SetTooltip("behavior changed in 0.5.5"); + } + ImGui::Checkbox("Wack FM algorithm macro",&e->song.algMacroBehavior); + if (ImGui::IsItemHovered()) { + ImGui::SetTooltip("behavior changed in 0.5.5"); + } + ImGui::Checkbox("Broken shortcut slides (E1xy/E2xy)",&e->song.brokenShortcutSlides); + if (ImGui::IsItemHovered()) { + ImGui::SetTooltip("behavior changed in 0.5.7"); + } + ImGui::Checkbox("Stop portamento on note off",&e->song.stopPortaOnNoteOff); + if (ImGui::IsItemHovered()) { + ImGui::SetTooltip("behavior changed in 0.6"); + } + ImGui::Checkbox("Allow instrument change during slides",&e->song.newInsTriggersInPorta); + if (ImGui::IsItemHovered()) { + ImGui::SetTooltip("behavior changed in 0.6"); + } + ImGui::Checkbox("Reset note to base on arpeggio stop",&e->song.arp0Reset); + if (ImGui::IsItemHovered()) { + ImGui::SetTooltip("behavior changed in 0.6"); + } + } + if (ImGui::IsWindowFocused(ImGuiFocusedFlags_ChildWindows)) curWindow=GUI_WINDOW_COMPAT_FLAGS; + ImGui::End(); +} \ No newline at end of file diff --git a/src/gui/cursor.cpp b/src/gui/cursor.cpp new file mode 100644 index 00000000..c783856a --- /dev/null +++ b/src/gui/cursor.cpp @@ -0,0 +1,285 @@ +/** + * Furnace Tracker - multi-system chiptune tracker + * Copyright (C) 2021-2022 tildearrow and contributors + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include "gui.h" + +#include "actionUtil.h" + +void FurnaceGUI::startSelection(int xCoarse, int xFine, int y) { + if (xCoarse!=selStart.xCoarse || xFine!=selStart.xFine || y!=selStart.y) { + curNibble=false; + } + cursor.xCoarse=xCoarse; + cursor.xFine=xFine; + cursor.y=y; + selStart.xCoarse=xCoarse; + selStart.xFine=xFine; + selStart.y=y; + selEnd.xCoarse=xCoarse; + selEnd.xFine=xFine; + selEnd.y=y; + selecting=true; +} + +void FurnaceGUI::updateSelection(int xCoarse, int xFine, int y) { + if (!selecting) return; + selEnd.xCoarse=xCoarse; + selEnd.xFine=xFine; + selEnd.y=y; +} + +void FurnaceGUI::finishSelection() { + // swap points if needed + if (selEnd.ygetTotalChannelCount(); + + if (selStart.xCoarse<0) selStart.xCoarse=0; + if (selStart.xCoarse>=chanCount) selStart.xCoarse=chanCount-1; + if (selStart.y<0) selStart.y=0; + if (selStart.y>=e->song.patLen) selStart.y=e->song.patLen-1; + if (selEnd.xCoarse<0) selEnd.xCoarse=0; + if (selEnd.xCoarse>=chanCount) selEnd.xCoarse=chanCount-1; + if (selEnd.y<0) selEnd.y=0; + if (selEnd.y>=e->song.patLen) selEnd.y=e->song.patLen-1; + if (cursor.xCoarse<0) cursor.xCoarse=0; + if (cursor.xCoarse>=chanCount) cursor.xCoarse=chanCount-1; + if (cursor.y<0) cursor.y=0; + if (cursor.y>=e->song.patLen) cursor.y=e->song.patLen-1; + + if (e->song.chanCollapse[selEnd.xCoarse]) { + selStart.xFine=0; + } + if (e->song.chanCollapse[selEnd.xCoarse]) { + selEnd.xFine=2+e->song.pat[cursor.xCoarse].effectRows*2; + } +} + +void FurnaceGUI::moveCursor(int x, int y, bool select) { + if (!select) { + finishSelection(); + } + + DETERMINE_FIRST_LAST; + + curNibble=false; + if (x!=0) { + demandScrollX=true; + if (x>0) { + for (int i=0; i=(e->song.chanCollapse[cursor.xCoarse]?1:(3+e->song.pat[cursor.xCoarse].effectRows*2))) { + cursor.xFine=0; + if (++cursor.xCoarse>=lastChannel) { + if (settings.wrapHorizontal!=0 && !select) { + cursor.xCoarse=firstChannel; + if (settings.wrapHorizontal==2) y++; + } else { + cursor.xCoarse=lastChannel-1; + cursor.xFine=e->song.chanCollapse[cursor.xCoarse]?0:(2+e->song.pat[cursor.xCoarse].effectRows*2); + } + } else { + while (!e->song.chanShow[cursor.xCoarse]) { + cursor.xCoarse++; + if (cursor.xCoarse>=e->getTotalChannelCount()) break; + } + } + } + } + } else { + for (int i=0; i<-x; i++) { + if (--cursor.xFine<0) { + if (--cursor.xCoarsesong.pat[cursor.xCoarse].effectRows*2; + if (settings.wrapHorizontal==2) y--; + } else { + cursor.xCoarse=firstChannel; + cursor.xFine=0; + } + } else { + while (!e->song.chanShow[cursor.xCoarse]) { + cursor.xCoarse--; + if (cursor.xCoarse<0) break; + } + if (e->song.chanCollapse[cursor.xCoarse]) { + cursor.xFine=0; + } else { + cursor.xFine=2+e->song.pat[cursor.xCoarse].effectRows*2; + } + } + } + } + } + } + if (y!=0) { + if (y>0) { + for (int i=0; i=e->song.patLen) { + if (settings.wrapVertical!=0 && !select) { + cursor.y=0; + if (settings.wrapVertical==2) { + if (!e->isPlaying() && e->getOrder()<(e->song.ordersLen-1)) { + e->setOrder(e->getOrder()+1); + } else { + cursor.y=e->song.patLen-1; + } + } + } else { + cursor.y=e->song.patLen-1; + } + } + } + } else { + for (int i=0; i<-y; i++) { + cursor.y--; + if (cursor.y<0) { + if (settings.wrapVertical!=0 && !select) { + cursor.y=e->song.patLen-1; + if (settings.wrapVertical==2) { + if (!e->isPlaying() && e->getOrder()>0) { + e->setOrder(e->getOrder()-1); + } else { + cursor.y=0; + } + } + } else { + cursor.y=0; + } + } + } + } + } + if (!select) { + selStart=cursor; + } + selEnd=cursor; + updateScroll(cursor.y); +} + +void FurnaceGUI::moveCursorPrevChannel(bool overflow) { + finishSelection(); + curNibble=false; + + DETERMINE_FIRST_LAST; + + do { + cursor.xCoarse--; + if (cursor.xCoarse<0) break; + } while (!e->song.chanShow[cursor.xCoarse]); + if (cursor.xCoarse=e->getTotalChannelCount()) break; + } while (!e->song.chanShow[cursor.xCoarse]); + if (cursor.xCoarse>=lastChannel) { + if (overflow) { + cursor.xCoarse=firstChannel; + } else { + cursor.xCoarse=lastChannel-1; + } + } + + selStart=cursor; + selEnd=cursor; + demandScrollX=true; +} + +void FurnaceGUI::moveCursorTop(bool select) { + finishSelection(); + curNibble=false; + if (cursor.y==0) { + DETERMINE_FIRST; + cursor.xCoarse=firstChannel; + cursor.xFine=0; + demandScrollX=true; + } else { + cursor.y=0; + } + selStart=cursor; + if (!select) { + selEnd=cursor; + } + updateScroll(cursor.y); +} + +void FurnaceGUI::moveCursorBottom(bool select) { + finishSelection(); + curNibble=false; + if (cursor.y==e->song.patLen-1) { + DETERMINE_LAST; + cursor.xCoarse=lastChannel-1; + cursor.xFine=2+e->song.pat[cursor.xCoarse].effectRows*2; + demandScrollX=true; + } else { + cursor.y=e->song.patLen-1; + } + if (!select) { + selStart=cursor; + } + selEnd=cursor; + updateScroll(cursor.y); +} + +void FurnaceGUI::editAdvance() { + finishSelection(); + cursor.y+=editStep; + if (cursor.y>=e->song.patLen) cursor.y=e->song.patLen-1; + selStart=cursor; + selEnd=cursor; + updateScroll(cursor.y); +} \ No newline at end of file diff --git a/src/gui/dataList.cpp b/src/gui/dataList.cpp index f32ffda7..d6c88a78 100644 --- a/src/gui/dataList.cpp +++ b/src/gui/dataList.cpp @@ -1,3 +1,22 @@ +/** + * Furnace Tracker - multi-system chiptune tracker + * Copyright (C) 2021-2022 tildearrow and contributors + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + #include "gui.h" #include "IconsFontAwesome4.h" #include "misc/cpp/imgui_stdlib.h" diff --git a/src/gui/debugWindow.cpp b/src/gui/debugWindow.cpp new file mode 100644 index 00000000..31830681 --- /dev/null +++ b/src/gui/debugWindow.cpp @@ -0,0 +1,250 @@ +/** + * Furnace Tracker - multi-system chiptune tracker + * Copyright (C) 2021-2022 tildearrow and contributors + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include "gui.h" +#include "debug.h" +#include "IconsFontAwesome4.h" +#include + +void FurnaceGUI::drawDebug() { + static int bpOrder; + static int bpRow; + static int bpTick; + static bool bpOn; + if (nextWindow==GUI_WINDOW_DEBUG) { + debugOpen=true; + ImGui::SetNextWindowFocus(); + nextWindow=GUI_WINDOW_NOTHING; + } + if (!debugOpen) return; + ImGui::SetNextWindowSizeConstraints(ImVec2(400.0f*dpiScale,200.0f*dpiScale),ImVec2(scrW*dpiScale,scrH*dpiScale)); + if (ImGui::Begin("Debug",&debugOpen,ImGuiWindowFlags_NoDocking)) { + ImGui::Text("NOTE: use with caution."); + if (ImGui::TreeNode("Debug Controls")) { + if (e->isHalted()) { + if (ImGui::Button("Resume")) e->resume(); + } else { + if (ImGui::Button("Pause")) e->halt(); + } + ImGui::SameLine(); + if (ImGui::Button("Frame Advance")) e->haltWhen(DIV_HALT_TICK); + ImGui::SameLine(); + if (ImGui::Button("Row Advance")) e->haltWhen(DIV_HALT_ROW); + ImGui::SameLine(); + if (ImGui::Button("Pattern Advance")) e->haltWhen(DIV_HALT_PATTERN); + + if (ImGui::Button("Panic")) e->syncReset(); + ImGui::SameLine(); + if (ImGui::Button("Abort")) { + abort(); + } + ImGui::TreePop(); + } + if (ImGui::TreeNode("Breakpoint")) { + ImGui::InputInt("Order",&bpOrder); + ImGui::InputInt("Row",&bpRow); + ImGui::InputInt("Tick",&bpTick); + ImGui::Checkbox("Enable",&bpOn); + ImGui::TreePop(); + } + if (ImGui::TreeNode("Dispatch Status")) { + ImGui::Text("for best results set latency to minimum or use the Frame Advance button."); + ImGui::Columns(e->getTotalChannelCount()); + for (int i=0; igetTotalChannelCount(); i++) { + void* ch=e->getDispatchChanState(i); + ImGui::TextColored(uiColors[GUI_COLOR_ACCENT_PRIMARY],"Ch. %d: %d, %d",i,e->dispatchOfChan[i],e->dispatchChanOfChan[i]); + if (ch==NULL) { + ImGui::Text("NULL"); + } else { + putDispatchChan(ch,e->dispatchChanOfChan[i],e->sysOfChan[i]); + } + ImGui::NextColumn(); + } + ImGui::Columns(); + ImGui::TreePop(); + } + if (ImGui::TreeNode("Playback Status")) { + ImGui::Text("for best results set latency to minimum or use the Frame Advance button."); + ImGui::Columns(e->getTotalChannelCount()); + for (int i=0; igetTotalChannelCount(); i++) { + DivChannelState* ch=e->getChanState(i); + ImGui::TextColored(uiColors[GUI_COLOR_ACCENT_PRIMARY],"Channel %d:",i); + if (ch==NULL) { + ImGui::Text("NULL"); + } else { + ImGui::Text("* General:"); + ImGui::Text("- note = %d",ch->note); + ImGui::Text("- oldNote = %d",ch->oldNote); + ImGui::Text("- pitch = %d",ch->pitch); + ImGui::Text("- portaSpeed = %d",ch->portaSpeed); + ImGui::Text("- portaNote = %d",ch->portaNote); + ImGui::Text("- volume = %.4x",ch->volume); + ImGui::Text("- volSpeed = %d",ch->volSpeed); + ImGui::Text("- cut = %d",ch->cut); + ImGui::Text("- rowDelay = %d",ch->rowDelay); + ImGui::Text("- volMax = %.4x",ch->volMax); + ImGui::Text("- delayOrder = %d",ch->delayOrder); + ImGui::Text("- delayRow = %d",ch->delayRow); + ImGui::Text("- retrigSpeed = %d",ch->retrigSpeed); + ImGui::Text("- retrigTick = %d",ch->retrigTick); + ImGui::PushStyleColor(ImGuiCol_Text,(ch->vibratoDepth>0)?uiColors[GUI_COLOR_MACRO_VOLUME]:uiColors[GUI_COLOR_TEXT]); + ImGui::Text("* Vibrato:"); + ImGui::Text("- depth = %d",ch->vibratoDepth); + ImGui::Text("- rate = %d",ch->vibratoRate); + ImGui::Text("- pos = %d",ch->vibratoPos); + ImGui::Text("- dir = %d",ch->vibratoDir); + ImGui::Text("- fine = %d",ch->vibratoFine); + ImGui::PopStyleColor(); + ImGui::PushStyleColor(ImGuiCol_Text,(ch->tremoloDepth>0)?uiColors[GUI_COLOR_MACRO_VOLUME]:uiColors[GUI_COLOR_TEXT]); + ImGui::Text("* Tremolo:"); + ImGui::Text("- depth = %d",ch->tremoloDepth); + ImGui::Text("- rate = %d",ch->tremoloRate); + ImGui::Text("- pos = %d",ch->tremoloPos); + ImGui::PopStyleColor(); + ImGui::PushStyleColor(ImGuiCol_Text,(ch->arp>0)?uiColors[GUI_COLOR_MACRO_VOLUME]:uiColors[GUI_COLOR_TEXT]); + ImGui::Text("* Arpeggio:"); + ImGui::Text("- arp = %.2X",ch->arp); + ImGui::Text("- stage = %d",ch->arpStage); + ImGui::Text("- ticks = %d",ch->arpTicks); + ImGui::PopStyleColor(); + ImGui::Text("* Miscellaneous:"); + ImGui::TextColored(ch->doNote?uiColors[GUI_COLOR_MACRO_VOLUME]:uiColors[GUI_COLOR_HEADER],">> Do Note"); + ImGui::TextColored(ch->legato?uiColors[GUI_COLOR_MACRO_VOLUME]:uiColors[GUI_COLOR_HEADER],">> Legato"); + ImGui::TextColored(ch->portaStop?uiColors[GUI_COLOR_MACRO_VOLUME]:uiColors[GUI_COLOR_HEADER],">> PortaStop"); + ImGui::TextColored(ch->keyOn?uiColors[GUI_COLOR_MACRO_VOLUME]:uiColors[GUI_COLOR_HEADER],">> Key On"); + ImGui::TextColored(ch->keyOff?uiColors[GUI_COLOR_MACRO_VOLUME]:uiColors[GUI_COLOR_HEADER],">> Key Off"); + ImGui::TextColored(ch->nowYouCanStop?uiColors[GUI_COLOR_MACRO_VOLUME]:uiColors[GUI_COLOR_HEADER],">> NowYouCanStop"); + ImGui::TextColored(ch->stopOnOff?uiColors[GUI_COLOR_MACRO_VOLUME]:uiColors[GUI_COLOR_HEADER],">> Stop on Off"); + ImGui::TextColored(ch->arpYield?uiColors[GUI_COLOR_MACRO_VOLUME]:uiColors[GUI_COLOR_HEADER],">> Arp Yield"); + ImGui::TextColored(ch->delayLocked?uiColors[GUI_COLOR_MACRO_VOLUME]:uiColors[GUI_COLOR_HEADER],">> DelayLocked"); + ImGui::TextColored(ch->inPorta?uiColors[GUI_COLOR_MACRO_VOLUME]:uiColors[GUI_COLOR_HEADER],">> InPorta"); + ImGui::TextColored(ch->scheduledSlideReset?uiColors[GUI_COLOR_MACRO_VOLUME]:uiColors[GUI_COLOR_HEADER],">> SchedSlide"); + } + ImGui::NextColumn(); + } + ImGui::Columns(); + ImGui::TreePop(); + } + if (ImGui::TreeNode("Playground")) { + if (pgSys<0 || pgSys>=e->song.systemLen) pgSys=0; + if (ImGui::BeginCombo("System",fmt::sprintf("%d. %s",pgSys+1,e->getSystemName(e->song.system[pgSys])).c_str())) { + for (int i=0; isong.systemLen; i++) { + if (ImGui::Selectable(fmt::sprintf("%d. %s",i+1,e->getSystemName(e->song.system[i])).c_str())) { + pgSys=i; + break; + } + } + ImGui::EndCombo(); + } + ImGui::Text("Program"); + if (pgProgram.empty()) { + ImGui::Text("-nothing here-"); + } else { + char id[32]; + for (size_t index=0; indexpoke(pgSys,pgProgram); + } + ImGui::SameLine(); + if (ImGui::Button("Clear")) { + pgProgram.clear(); + } + + ImGui::Text("Address"); + ImGui::SameLine(); + ImGui::SetNextItemWidth(100.0f*dpiScale); + ImGui::InputInt("##PAddress",&pgAddr,0,0,ImGuiInputTextFlags_CharsHexadecimal); + ImGui::SameLine(); + ImGui::Text("Value"); + ImGui::SameLine(); + ImGui::SetNextItemWidth(100.0f*dpiScale); + ImGui::InputInt("##PValue",&pgVal,0,0,ImGuiInputTextFlags_CharsHexadecimal); + ImGui::SameLine(); + if (ImGui::Button("Write")) { + e->poke(pgSys,pgAddr,pgVal); + } + ImGui::SameLine(); + if (ImGui::Button("Add")) { + pgProgram.push_back(DivRegWrite(pgAddr,pgVal)); + } + if (ImGui::TreeNode("Register Cheatsheet")) { + const char** sheet=e->getRegisterSheet(pgSys); + if (sheet==NULL) { + ImGui::Text("no cheatsheet available for this system."); + } else { + if (ImGui::BeginTable("RegisterSheet",2,ImGuiTableFlags_SizingFixedSame)) { + ImGui::TableNextRow(); + ImGui::TableNextColumn(); + ImGui::Text("Name"); + ImGui::TableNextColumn(); + ImGui::Text("Address"); + for (int i=0; sheet[i]!=NULL; i+=2) { + ImGui::TableNextRow(); + ImGui::TableNextColumn(); + ImGui::Text("%s",sheet[i]); + ImGui::TableNextColumn(); + ImGui::Text("$%s",sheet[i+1]); + } + ImGui::EndTable(); + } + } + ImGui::TreePop(); + } + ImGui::TreePop(); + } + if (ImGui::TreeNode("User Interface")) { + if (ImGui::Button("Inspect")) { + inspectorOpen=!inspectorOpen; + } + ImGui::TreePop(); + } + if (ImGui::TreeNode("Settings")) { + if (ImGui::Button("Sync")) syncSettings(); + ImGui::SameLine(); + if (ImGui::Button("Commit")) commitSettings(); + ImGui::SameLine(); + if (ImGui::Button("Force Load")) e->loadConf(); + ImGui::SameLine(); + if (ImGui::Button("Force Save")) e->saveConf(); + ImGui::TreePop(); + } + ImGui::Text("Song format version %d",e->song.version); + ImGui::Text("Furnace version " DIV_VERSION " (%d)",DIV_ENGINE_VERSION); + } + if (ImGui::IsWindowFocused(ImGuiFocusedFlags_ChildWindows)) curWindow=GUI_WINDOW_DEBUG; + ImGui::End(); +} \ No newline at end of file diff --git a/src/gui/doAction.cpp b/src/gui/doAction.cpp new file mode 100644 index 00000000..903f6edf --- /dev/null +++ b/src/gui/doAction.cpp @@ -0,0 +1,687 @@ +/** + * Furnace Tracker - multi-system chiptune tracker + * Copyright (C) 2021-2022 tildearrow and contributors + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include "gui.h" +#include + +#include "actionUtil.h" + +void FurnaceGUI::doAction(int what) { + switch (what) { + case GUI_ACTION_OPEN: + if (modified) { + showWarning("Unsaved changes! Are you sure?",GUI_WARN_OPEN); + } else { + openFileDialog(GUI_FILE_OPEN); + } + break; + case GUI_ACTION_OPEN_BACKUP: + if (modified) { + showWarning("Unsaved changes! Are you sure?",GUI_WARN_OPEN_BACKUP); + } else { + if (load(backupPath)>0) { + showError("No backup available! (or unable to open it)"); + } + } + break; + case GUI_ACTION_SAVE: + if (curFileName=="" || curFileName==backupPath || e->song.version>=0xff00) { + openFileDialog(GUI_FILE_SAVE); + } else { + if (save(curFileName,e->song.isDMF?e->song.version:0)>0) { + showError(fmt::sprintf("Error while saving file! (%s)",lastError)); + } + } + break; + case GUI_ACTION_SAVE_AS: + openFileDialog(GUI_FILE_SAVE); + break; + case GUI_ACTION_UNDO: + doUndo(); + break; + case GUI_ACTION_REDO: + doRedo(); + break; + case GUI_ACTION_PLAY_TOGGLE: + if (e->isPlaying() && !e->isStepping()) { + stop(); + } else { + play(); + } + break; + case GUI_ACTION_PLAY: + play(); + break; + case GUI_ACTION_STOP: + stop(); + break; + case GUI_ACTION_PLAY_REPEAT: + play(); + e->setRepeatPattern(true); + break; + case GUI_ACTION_PLAY_CURSOR: + if (e->isPlaying() && !e->isStepping()) { + stop(); + } else { + play(cursor.y); + } + break; + case GUI_ACTION_STEP_ONE: + e->stepOne(cursor.y); + break; + case GUI_ACTION_OCTAVE_UP: + if (++curOctave>7) { + curOctave=7; + } else { + for (size_t i=0; inoteOff(activeNotes[i].chan); + } + activeNotes.clear(); + } + break; + case GUI_ACTION_OCTAVE_DOWN: + if (--curOctave<-5) { + curOctave=-5; + } else { + for (size_t i=0; inoteOff(activeNotes[i].chan); + } + activeNotes.clear(); + } + break; + case GUI_ACTION_INS_UP: + if (--curIns<-1) curIns=-1; + break; + case GUI_ACTION_INS_DOWN: + if (++curIns>=(int)e->song.ins.size()) curIns=((int)e->song.ins.size())-1; + break; + case GUI_ACTION_STEP_UP: + if (++editStep>64) editStep=64; + break; + case GUI_ACTION_STEP_DOWN: + if (--editStep<0) editStep=0; + break; + case GUI_ACTION_TOGGLE_EDIT: + edit=!edit; + break; + case GUI_ACTION_METRONOME: + e->setMetronome(!e->getMetronome()); + break; + case GUI_ACTION_REPEAT_PATTERN: + e->setRepeatPattern(!e->getRepeatPattern()); + break; + case GUI_ACTION_FOLLOW_ORDERS: + followOrders=!followOrders; + break; + case GUI_ACTION_FOLLOW_PATTERN: + followPattern=!followPattern; + break; + case GUI_ACTION_PANIC: + e->syncReset(); + break; + + case GUI_ACTION_WINDOW_EDIT_CONTROLS: + nextWindow=GUI_WINDOW_EDIT_CONTROLS; + break; + case GUI_ACTION_WINDOW_ORDERS: + nextWindow=GUI_WINDOW_ORDERS; + break; + case GUI_ACTION_WINDOW_INS_LIST: + nextWindow=GUI_WINDOW_INS_LIST; + break; + case GUI_ACTION_WINDOW_INS_EDIT: + nextWindow=GUI_WINDOW_INS_EDIT; + break; + case GUI_ACTION_WINDOW_SONG_INFO: + nextWindow=GUI_WINDOW_SONG_INFO; + break; + case GUI_ACTION_WINDOW_PATTERN: + nextWindow=GUI_WINDOW_PATTERN; + break; + case GUI_ACTION_WINDOW_WAVE_LIST: + nextWindow=GUI_WINDOW_WAVE_LIST; + break; + case GUI_ACTION_WINDOW_WAVE_EDIT: + nextWindow=GUI_WINDOW_WAVE_EDIT; + break; + case GUI_ACTION_WINDOW_SAMPLE_LIST: + nextWindow=GUI_WINDOW_SAMPLE_LIST; + break; + case GUI_ACTION_WINDOW_SAMPLE_EDIT: + nextWindow=GUI_WINDOW_SAMPLE_EDIT; + break; + case GUI_ACTION_WINDOW_ABOUT: + nextWindow=GUI_WINDOW_ABOUT; + break; + case GUI_ACTION_WINDOW_SETTINGS: + nextWindow=GUI_WINDOW_SETTINGS; + break; + case GUI_ACTION_WINDOW_MIXER: + nextWindow=GUI_WINDOW_MIXER; + break; + case GUI_ACTION_WINDOW_DEBUG: + nextWindow=GUI_WINDOW_DEBUG; + break; + case GUI_ACTION_WINDOW_OSCILLOSCOPE: + nextWindow=GUI_WINDOW_OSCILLOSCOPE; + break; + case GUI_ACTION_WINDOW_VOL_METER: + nextWindow=GUI_WINDOW_VOL_METER; + break; + case GUI_ACTION_WINDOW_STATS: + nextWindow=GUI_WINDOW_STATS; + break; + case GUI_ACTION_WINDOW_COMPAT_FLAGS: + nextWindow=GUI_WINDOW_COMPAT_FLAGS; + break; + case GUI_ACTION_WINDOW_PIANO: + nextWindow=GUI_WINDOW_PIANO; + break; + case GUI_ACTION_WINDOW_NOTES: + nextWindow=GUI_WINDOW_NOTES; + break; + case GUI_ACTION_WINDOW_CHANNELS: + nextWindow=GUI_WINDOW_CHANNELS; + break; + case GUI_ACTION_WINDOW_REGISTER_VIEW: + nextWindow=GUI_WINDOW_REGISTER_VIEW; + break; + + case GUI_ACTION_COLLAPSE_WINDOW: + collapseWindow=true; + break; + case GUI_ACTION_CLOSE_WINDOW: + switch (curWindow) { + case GUI_WINDOW_EDIT_CONTROLS: + editControlsOpen=false; + break; + case GUI_WINDOW_SONG_INFO: + songInfoOpen=false; + break; + case GUI_WINDOW_ORDERS: + ordersOpen=false; + break; + case GUI_WINDOW_INS_LIST: + insListOpen=false; + break; + case GUI_WINDOW_PATTERN: + patternOpen=false; + break; + case GUI_WINDOW_INS_EDIT: + insEditOpen=false; + break; + case GUI_WINDOW_WAVE_LIST: + waveListOpen=false; + break; + case GUI_WINDOW_WAVE_EDIT: + waveEditOpen=false; + break; + case GUI_WINDOW_SAMPLE_LIST: + sampleListOpen=false; + break; + case GUI_WINDOW_SAMPLE_EDIT: + sampleEditOpen=false; + break; + case GUI_WINDOW_MIXER: + mixerOpen=false; + break; + case GUI_WINDOW_ABOUT: + aboutOpen=false; + break; + case GUI_WINDOW_SETTINGS: + settingsOpen=false; + break; + case GUI_WINDOW_DEBUG: + debugOpen=false; + break; + case GUI_WINDOW_OSCILLOSCOPE: + oscOpen=false; + break; + case GUI_WINDOW_VOL_METER: + volMeterOpen=false; + break; + case GUI_WINDOW_STATS: + statsOpen=false; + break; + case GUI_WINDOW_COMPAT_FLAGS: + compatFlagsOpen=false; + break; + case GUI_WINDOW_PIANO: + pianoOpen=false; + break; + case GUI_WINDOW_NOTES: + notesOpen=false; + break; + case GUI_WINDOW_CHANNELS: + channelsOpen=false; + break; + case GUI_WINDOW_REGISTER_VIEW: + regViewOpen=false; + break; + default: + break; + } + curWindow=GUI_WINDOW_NOTHING; + break; + + case GUI_ACTION_PAT_NOTE_UP: + doTranspose(1); + break; + case GUI_ACTION_PAT_NOTE_DOWN: + doTranspose(-1); + break; + case GUI_ACTION_PAT_OCTAVE_UP: + doTranspose(12); + break; + case GUI_ACTION_PAT_OCTAVE_DOWN: + doTranspose(-12); + break; + case GUI_ACTION_PAT_SELECT_ALL: + doSelectAll(); + break; + case GUI_ACTION_PAT_CUT: + doCopy(true); + break; + case GUI_ACTION_PAT_COPY: + doCopy(false); + break; + case GUI_ACTION_PAT_PASTE: + doPaste(); + break; + case GUI_ACTION_PAT_CURSOR_UP: + moveCursor(0,-MAX(1,settings.scrollStep?editStep:1),false); + break; + case GUI_ACTION_PAT_CURSOR_DOWN: + moveCursor(0,MAX(1,settings.scrollStep?editStep:1),false); + break; + case GUI_ACTION_PAT_CURSOR_LEFT: + moveCursor(-1,0,false); + break; + case GUI_ACTION_PAT_CURSOR_RIGHT: + moveCursor(1,0,false); + break; + case GUI_ACTION_PAT_CURSOR_UP_ONE: + moveCursor(0,-1,false); + break; + case GUI_ACTION_PAT_CURSOR_DOWN_ONE: + moveCursor(0,1,false); + break; + case GUI_ACTION_PAT_CURSOR_LEFT_CHANNEL: + moveCursorPrevChannel(false); + break; + case GUI_ACTION_PAT_CURSOR_RIGHT_CHANNEL: + moveCursorNextChannel(false); + break; + case GUI_ACTION_PAT_CURSOR_NEXT_CHANNEL: + moveCursorNextChannel(true); + break; + case GUI_ACTION_PAT_CURSOR_PREVIOUS_CHANNEL: + moveCursorPrevChannel(true); + break; + case GUI_ACTION_PAT_CURSOR_BEGIN: + moveCursorTop(false); + break; + case GUI_ACTION_PAT_CURSOR_END: + moveCursorBottom(false); + break; + case GUI_ACTION_PAT_CURSOR_UP_COARSE: + moveCursor(0,-16,false); + break; + case GUI_ACTION_PAT_CURSOR_DOWN_COARSE: + moveCursor(0,16,false); + break; + case GUI_ACTION_PAT_SELECTION_UP: + moveCursor(0,-MAX(1,settings.scrollStep?editStep:1),true); + break; + case GUI_ACTION_PAT_SELECTION_DOWN: + moveCursor(0,MAX(1,settings.scrollStep?editStep:1),true); + break; + case GUI_ACTION_PAT_SELECTION_LEFT: + moveCursor(-1,0,true); + break; + case GUI_ACTION_PAT_SELECTION_RIGHT: + moveCursor(1,0,true); + break; + case GUI_ACTION_PAT_SELECTION_UP_ONE: + moveCursor(0,-1,true); + break; + case GUI_ACTION_PAT_SELECTION_DOWN_ONE: + moveCursor(0,1,true); + break; + case GUI_ACTION_PAT_SELECTION_BEGIN: + moveCursorTop(true); + break; + case GUI_ACTION_PAT_SELECTION_END: + moveCursorBottom(true); + break; + case GUI_ACTION_PAT_SELECTION_UP_COARSE: + moveCursor(0,-16,true); + break; + case GUI_ACTION_PAT_SELECTION_DOWN_COARSE: + moveCursor(0,16,true); + break; + case GUI_ACTION_PAT_DELETE: + doDelete(); + if (settings.stepOnDelete) { + moveCursor(0,editStep,false); + } + break; + case GUI_ACTION_PAT_PULL_DELETE: + doPullDelete(); + break; + case GUI_ACTION_PAT_INSERT: + doInsert(); + if (settings.stepOnInsert) { + moveCursor(0,editStep,false); + } + break; + case GUI_ACTION_PAT_MUTE_CURSOR: + if (cursor.xCoarse<0 || cursor.xCoarse>=e->getTotalChannelCount()) break; + e->toggleMute(cursor.xCoarse); + break; + case GUI_ACTION_PAT_SOLO_CURSOR: + if (cursor.xCoarse<0 || cursor.xCoarse>=e->getTotalChannelCount()) break; + e->toggleSolo(cursor.xCoarse); + break; + case GUI_ACTION_PAT_UNMUTE_ALL: + e->unmuteAll(); + break; + case GUI_ACTION_PAT_NEXT_ORDER: + if (e->getOrder()song.ordersLen-1) { + e->setOrder(e->getOrder()+1); + } + break; + case GUI_ACTION_PAT_PREV_ORDER: + if (e->getOrder()>0) { + e->setOrder(e->getOrder()-1); + } + break; + case GUI_ACTION_PAT_COLLAPSE: + if (cursor.xCoarse<0 || cursor.xCoarse>=e->getTotalChannelCount()) break; + e->song.chanCollapse[cursor.xCoarse]=!e->song.chanCollapse[cursor.xCoarse]; + break; + case GUI_ACTION_PAT_INCREASE_COLUMNS: + if (cursor.xCoarse<0 || cursor.xCoarse>=e->getTotalChannelCount()) break; + e->song.pat[cursor.xCoarse].effectRows++; + if (e->song.pat[cursor.xCoarse].effectRows>8) e->song.pat[cursor.xCoarse].effectRows=8; + break; + case GUI_ACTION_PAT_DECREASE_COLUMNS: + if (cursor.xCoarse<0 || cursor.xCoarse>=e->getTotalChannelCount()) break; + e->song.pat[cursor.xCoarse].effectRows--; + if (e->song.pat[cursor.xCoarse].effectRows<1) e->song.pat[cursor.xCoarse].effectRows=1; + break; + case GUI_ACTION_PAT_INTERPOLATE: + doInterpolate(); + break; + case GUI_ACTION_PAT_INVERT_VALUES: + doInvertValues(); + break; + case GUI_ACTION_PAT_FLIP_SELECTION: + doFlip(); + break; + case GUI_ACTION_PAT_COLLAPSE_ROWS: + doCollapse(2); + break; + case GUI_ACTION_PAT_EXPAND_ROWS: + doExpand(2); + break; + case GUI_ACTION_PAT_COLLAPSE_PAT: // TODO + break; + case GUI_ACTION_PAT_EXPAND_PAT: // TODO + break; + case GUI_ACTION_PAT_COLLAPSE_SONG: // TODO + break; + case GUI_ACTION_PAT_EXPAND_SONG: // TODO + break; + case GUI_ACTION_PAT_LATCH: // TODO + break; + + case GUI_ACTION_INS_LIST_ADD: + curIns=e->addInstrument(cursor.xCoarse); + MARK_MODIFIED; + break; + case GUI_ACTION_INS_LIST_DUPLICATE: + if (curIns>=0 && curIns<(int)e->song.ins.size()) { + int prevIns=curIns; + curIns=e->addInstrument(cursor.xCoarse); + (*e->song.ins[curIns])=(*e->song.ins[prevIns]); + MARK_MODIFIED; + } + break; + case GUI_ACTION_INS_LIST_OPEN: + openFileDialog(GUI_FILE_INS_OPEN); + break; + case GUI_ACTION_INS_LIST_SAVE: + if (curIns>=0 && curIns<(int)e->song.ins.size()) openFileDialog(GUI_FILE_INS_SAVE); + break; + case GUI_ACTION_INS_LIST_MOVE_UP: + if (e->moveInsUp(curIns)) curIns--; + break; + case GUI_ACTION_INS_LIST_MOVE_DOWN: + if (e->moveInsDown(curIns)) curIns++; + break; + case GUI_ACTION_INS_LIST_DELETE: + if (curIns>=0 && curIns<(int)e->song.ins.size()) { + e->delInstrument(curIns); + MARK_MODIFIED; + if (curIns>=(int)e->song.ins.size()) { + curIns--; + } + } + break; + case GUI_ACTION_INS_LIST_EDIT: + insEditOpen=true; + break; + case GUI_ACTION_INS_LIST_UP: + if (--curIns<0) curIns=0; + break; + case GUI_ACTION_INS_LIST_DOWN: + if (++curIns>=(int)e->song.ins.size()) curIns=((int)e->song.ins.size())-1; + break; + + case GUI_ACTION_WAVE_LIST_ADD: + curWave=e->addWave(); + MARK_MODIFIED; + break; + case GUI_ACTION_WAVE_LIST_DUPLICATE: + if (curWave>=0 && curWave<(int)e->song.wave.size()) { + int prevWave=curWave; + curWave=e->addWave(); + (*e->song.wave[curWave])=(*e->song.wave[prevWave]); + MARK_MODIFIED; + } + break; + case GUI_ACTION_WAVE_LIST_OPEN: + openFileDialog(GUI_FILE_WAVE_OPEN); + break; + case GUI_ACTION_WAVE_LIST_SAVE: + if (curWave>=0 && curWave<(int)e->song.wave.size()) openFileDialog(GUI_FILE_WAVE_SAVE); + break; + case GUI_ACTION_WAVE_LIST_MOVE_UP: + if (e->moveWaveUp(curWave)) curWave--; + break; + case GUI_ACTION_WAVE_LIST_MOVE_DOWN: + if (e->moveWaveDown(curWave)) curWave++; + break; + case GUI_ACTION_WAVE_LIST_DELETE: + if (curWave>=0 && curWave<(int)e->song.wave.size()) { + e->delWave(curWave); + MARK_MODIFIED; + if (curWave>=(int)e->song.wave.size()) { + curWave--; + } + } + break; + case GUI_ACTION_WAVE_LIST_EDIT: + waveEditOpen=true; + break; + case GUI_ACTION_WAVE_LIST_UP: + if (--curWave<0) curWave=0; + break; + case GUI_ACTION_WAVE_LIST_DOWN: + if (++curWave>=(int)e->song.wave.size()) curWave=((int)e->song.wave.size())-1; + break; + + case GUI_ACTION_SAMPLE_LIST_ADD: + curSample=e->addSample(); + MARK_MODIFIED; + break; + case GUI_ACTION_SAMPLE_LIST_OPEN: + openFileDialog(GUI_FILE_SAMPLE_OPEN); + break; + case GUI_ACTION_SAMPLE_LIST_SAVE: + if (curSample>=0 && curSample<(int)e->song.sample.size()) openFileDialog(GUI_FILE_SAMPLE_SAVE); + break; + case GUI_ACTION_SAMPLE_LIST_MOVE_UP: + if (e->moveSampleUp(curSample)) curSample--; + break; + case GUI_ACTION_SAMPLE_LIST_MOVE_DOWN: + if (e->moveSampleDown(curSample)) curSample++; + break; + case GUI_ACTION_SAMPLE_LIST_DELETE: + e->delSample(curSample); + MARK_MODIFIED; + if (curSample>=(int)e->song.sample.size()) { + curSample--; + } + break; + case GUI_ACTION_SAMPLE_LIST_EDIT: + sampleEditOpen=true; + break; + case GUI_ACTION_SAMPLE_LIST_UP: + if (--curSample<0) curSample=0; + break; + case GUI_ACTION_SAMPLE_LIST_DOWN: + if (++curSample>=(int)e->song.sample.size()) curSample=((int)e->song.sample.size())-1; + break; + case GUI_ACTION_SAMPLE_LIST_PREVIEW: + e->previewSample(curSample); + break; + case GUI_ACTION_SAMPLE_LIST_STOP_PREVIEW: + e->stopSamplePreview(); + break; + + case GUI_ACTION_ORDERS_UP: + if (e->getOrder()>0) { + e->setOrder(e->getOrder()-1); + } + break; + case GUI_ACTION_ORDERS_DOWN: + if (e->getOrder()song.ordersLen-1) { + e->setOrder(e->getOrder()+1); + } + break; + case GUI_ACTION_ORDERS_LEFT: { + DETERMINE_FIRST; + + do { + orderCursor--; + if (orderCursorsong.chanShow[orderCursor]); + break; + } + case GUI_ACTION_ORDERS_RIGHT: { + DETERMINE_LAST; + + do { + orderCursor++; + if (orderCursor>=lastChannel) { + orderCursor=lastChannel-1; + break; + } + } while (!e->song.chanShow[orderCursor]); + break; + } + case GUI_ACTION_ORDERS_INCREASE: { + if (orderCursor<0 || orderCursor>=e->getTotalChannelCount()) break; + int curOrder=e->getOrder(); + if (e->song.orders.ord[orderCursor][curOrder]<0x7f) { + e->song.orders.ord[orderCursor][curOrder]++; + } + break; + } + case GUI_ACTION_ORDERS_DECREASE: { + if (orderCursor<0 || orderCursor>=e->getTotalChannelCount()) break; + int curOrder=e->getOrder(); + if (e->song.orders.ord[orderCursor][curOrder]>0) { + e->song.orders.ord[orderCursor][curOrder]--; + } + break; + } + case GUI_ACTION_ORDERS_EDIT_MODE: + orderEditMode++; + if (orderEditMode>3) orderEditMode=0; + break; + case GUI_ACTION_ORDERS_LINK: + changeAllOrders=!changeAllOrders; + break; + case GUI_ACTION_ORDERS_ADD: + prepareUndo(GUI_UNDO_CHANGE_ORDER); + e->addOrder(false,false); + makeUndo(GUI_UNDO_CHANGE_ORDER); + break; + case GUI_ACTION_ORDERS_DUPLICATE: + prepareUndo(GUI_UNDO_CHANGE_ORDER); + e->addOrder(true,false); + makeUndo(GUI_UNDO_CHANGE_ORDER); + break; + case GUI_ACTION_ORDERS_DEEP_CLONE: + prepareUndo(GUI_UNDO_CHANGE_ORDER); + e->deepCloneOrder(false); + makeUndo(GUI_UNDO_CHANGE_ORDER); + if (!e->getWarnings().empty()) { + showWarning(e->getWarnings(),GUI_WARN_GENERIC); + } + break; + case GUI_ACTION_ORDERS_DUPLICATE_END: + prepareUndo(GUI_UNDO_CHANGE_ORDER); + e->addOrder(true,true); + makeUndo(GUI_UNDO_CHANGE_ORDER); + break; + case GUI_ACTION_ORDERS_DEEP_CLONE_END: + prepareUndo(GUI_UNDO_CHANGE_ORDER); + e->deepCloneOrder(true); + makeUndo(GUI_UNDO_CHANGE_ORDER); + if (!e->getWarnings().empty()) { + showWarning(e->getWarnings(),GUI_WARN_GENERIC); + } + break; + case GUI_ACTION_ORDERS_REMOVE: + prepareUndo(GUI_UNDO_CHANGE_ORDER); + e->deleteOrder(); + makeUndo(GUI_UNDO_CHANGE_ORDER); + break; + case GUI_ACTION_ORDERS_MOVE_UP: + prepareUndo(GUI_UNDO_CHANGE_ORDER); + e->moveOrderUp(); + makeUndo(GUI_UNDO_CHANGE_ORDER); + break; + case GUI_ACTION_ORDERS_MOVE_DOWN: + prepareUndo(GUI_UNDO_CHANGE_ORDER); + e->moveOrderDown(); + makeUndo(GUI_UNDO_CHANGE_ORDER); + break; + case GUI_ACTION_ORDERS_REPLAY: + e->setOrder(e->getOrder()); + break; + } +} \ No newline at end of file diff --git a/src/gui/editControls.cpp b/src/gui/editControls.cpp new file mode 100644 index 00000000..68f0fee2 --- /dev/null +++ b/src/gui/editControls.cpp @@ -0,0 +1,330 @@ +/** + * Furnace Tracker - multi-system chiptune tracker + * Copyright (C) 2021-2022 tildearrow and contributors + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include "gui.h" +#include "IconsFontAwesome4.h" + +void FurnaceGUI::drawEditControls() { + if (nextWindow==GUI_WINDOW_EDIT_CONTROLS) { + editControlsOpen=true; + ImGui::SetNextWindowFocus(); + nextWindow=GUI_WINDOW_NOTHING; + } + if (!editControlsOpen) return; + switch (settings.controlLayout) { + case 0: // classic + if (ImGui::Begin("Play/Edit Controls",&editControlsOpen)) { + ImGui::Text("Octave"); + ImGui::SameLine(); + if (ImGui::InputInt("##Octave",&curOctave,1,1)) { + if (curOctave>7) curOctave=7; + if (curOctave<-5) curOctave=-5; + for (size_t i=0; inoteOff(activeNotes[i].chan); + } + activeNotes.clear(); + + if (settings.insFocusesPattern && !ImGui::IsItemActive() && patternOpen) { + nextWindow=GUI_WINDOW_PATTERN; + } + } + + ImGui::Text("Edit Step"); + ImGui::SameLine(); + if (ImGui::InputInt("##EditStep",&editStep,1,1)) { + if (editStep>=e->song.patLen) editStep=e->song.patLen-1; + if (editStep<0) editStep=0; + + if (settings.insFocusesPattern && !ImGui::IsItemActive() && patternOpen) { + nextWindow=GUI_WINDOW_PATTERN; + } + } + + if (ImGui::Button(ICON_FA_PLAY "##Play")) { + play(); + } + ImGui::SameLine(); + if (ImGui::Button(ICON_FA_STOP "##Stop")) { + stop(); + } + ImGui::SameLine(); + ImGui::Checkbox("Edit",&edit); + ImGui::SameLine(); + bool metro=e->getMetronome(); + if (ImGui::Checkbox("Metronome",&metro)) { + e->setMetronome(metro); + } + + ImGui::Text("Follow"); + ImGui::SameLine(); + unimportant(ImGui::Checkbox("Orders",&followOrders)); + ImGui::SameLine(); + unimportant(ImGui::Checkbox("Pattern",&followPattern)); + + bool repeatPattern=e->getRepeatPattern(); + if (ImGui::Checkbox("Repeat pattern",&repeatPattern)) { + e->setRepeatPattern(repeatPattern); + } + ImGui::SameLine(); + if (ImGui::Button(ICON_FA_ARROW_DOWN "##StepOne")) { + e->stepOne(cursor.y); + } + } + if (ImGui::IsWindowFocused(ImGuiFocusedFlags_ChildWindows)) curWindow=GUI_WINDOW_EDIT_CONTROLS; + ImGui::End(); + break; + case 1: // compact + if (ImGui::Begin("Play/Edit Controls",&editControlsOpen,ImGuiWindowFlags_NoScrollbar|ImGuiWindowFlags_NoScrollWithMouse)) { + if (ImGui::Button(ICON_FA_STOP "##Stop")) { + stop(); + } + ImGui::SameLine(); + if (ImGui::Button(ICON_FA_PLAY "##Play")) { + play(); + } + ImGui::SameLine(); + if (ImGui::Button(ICON_FA_ARROW_DOWN "##StepOne")) { + e->stepOne(cursor.y); + } + + ImGui::SameLine(); + bool repeatPattern=e->getRepeatPattern(); + ImGui::PushStyleColor(ImGuiCol_Button,ImVec4(0.2f,(repeatPattern)?0.6f:0.2f,0.2f,1.0f)); + if (ImGui::Button(ICON_FA_REPEAT "##RepeatPattern")) { + e->setRepeatPattern(!repeatPattern); + } + ImGui::PopStyleColor(); + + ImGui::SameLine(); + ImGui::PushStyleColor(ImGuiCol_Button,ImVec4(0.2f,(edit)?0.6f:0.2f,0.2f,1.0f)); + if (ImGui::Button(ICON_FA_CIRCLE "##Edit")) { + edit=!edit; + } + ImGui::PopStyleColor(); + + ImGui::SameLine(); + bool metro=e->getMetronome(); + ImGui::PushStyleColor(ImGuiCol_Button,ImVec4(0.2f,(metro)?0.6f:0.2f,0.2f,1.0f)); + if (ImGui::Button(ICON_FA_BELL_O "##Metronome")) { + e->setMetronome(!metro); + } + ImGui::PopStyleColor(); + + ImGui::SameLine(); + ImGui::Text("Octave"); + ImGui::SameLine(); + ImGui::SetNextItemWidth(96.0f*dpiScale); + if (ImGui::InputInt("##Octave",&curOctave,1,1)) { + if (curOctave>7) curOctave=7; + if (curOctave<-5) curOctave=-5; + for (size_t i=0; inoteOff(activeNotes[i].chan); + } + activeNotes.clear(); + + if (settings.insFocusesPattern && !ImGui::IsItemActive() && patternOpen) { + nextWindow=GUI_WINDOW_PATTERN; + } + } + + ImGui::SameLine(); + ImGui::Text("Edit Step"); + ImGui::SameLine(); + ImGui::SetNextItemWidth(96.0f*dpiScale); + if (ImGui::InputInt("##EditStep",&editStep,1,1)) { + if (editStep>=e->song.patLen) editStep=e->song.patLen-1; + if (editStep<0) editStep=0; + + if (settings.insFocusesPattern && !ImGui::IsItemActive() && patternOpen) { + nextWindow=GUI_WINDOW_PATTERN; + } + } + + ImGui::SameLine(); + ImGui::Text("Follow"); + ImGui::SameLine(); + unimportant(ImGui::Checkbox("Orders",&followOrders)); + ImGui::SameLine(); + unimportant(ImGui::Checkbox("Pattern",&followPattern)); + } + if (ImGui::IsWindowFocused(ImGuiFocusedFlags_ChildWindows)) curWindow=GUI_WINDOW_EDIT_CONTROLS; + ImGui::End(); + break; + case 2: // compact vertical + if (ImGui::Begin("Play/Edit Controls",&editControlsOpen,ImGuiWindowFlags_NoScrollbar|ImGuiWindowFlags_NoScrollWithMouse)) { + if (ImGui::Button(ICON_FA_PLAY "##Play")) { + play(); + } + if (ImGui::Button(ICON_FA_STOP "##Stop")) { + stop(); + } + if (ImGui::Button(ICON_FA_ARROW_DOWN "##StepOne")) { + e->stepOne(cursor.y); + } + + bool repeatPattern=e->getRepeatPattern(); + ImGui::PushStyleColor(ImGuiCol_Button,ImVec4(0.2f,(repeatPattern)?0.6f:0.2f,0.2f,1.0f)); + if (ImGui::Button(ICON_FA_REPEAT "##RepeatPattern")) { + e->setRepeatPattern(!repeatPattern); + } + ImGui::PopStyleColor(); + + ImGui::PushStyleColor(ImGuiCol_Button,ImVec4(0.2f,(edit)?0.6f:0.2f,0.2f,1.0f)); + if (ImGui::Button(ICON_FA_CIRCLE "##Edit")) { + edit=!edit; + } + ImGui::PopStyleColor(); + + bool metro=e->getMetronome(); + ImGui::PushStyleColor(ImGuiCol_Button,ImVec4(0.2f,(metro)?0.6f:0.2f,0.2f,1.0f)); + if (ImGui::Button(ICON_FA_BELL_O "##Metronome")) { + e->setMetronome(!metro); + } + ImGui::PopStyleColor(); + + ImGui::Text("Oct."); + float avail=ImGui::GetContentRegionAvail().x; + ImGui::SetNextItemWidth(avail); + if (ImGui::InputInt("##Octave",&curOctave,0,0)) { + if (curOctave>7) curOctave=7; + if (curOctave<-5) curOctave=-5; + for (size_t i=0; inoteOff(activeNotes[i].chan); + } + activeNotes.clear(); + + if (settings.insFocusesPattern && !ImGui::IsItemActive() && patternOpen) { + nextWindow=GUI_WINDOW_PATTERN; + } + } + + ImGui::Text("Step"); + ImGui::SetNextItemWidth(avail); + if (ImGui::InputInt("##EditStep",&editStep,0,0)) { + if (editStep>=e->song.patLen) editStep=e->song.patLen-1; + if (editStep<0) editStep=0; + + if (settings.insFocusesPattern && !ImGui::IsItemActive() && patternOpen) { + nextWindow=GUI_WINDOW_PATTERN; + } + } + + ImGui::Text("Foll."); + ImGui::PushStyleColor(ImGuiCol_Button,ImVec4(0.2f,(followOrders)?0.6f:0.2f,0.2f,1.0f)); + if (ImGui::SmallButton("Ord##FollowOrders")) { handleUnimportant + followOrders=!followOrders; + } + ImGui::PopStyleColor(); + ImGui::PushStyleColor(ImGuiCol_Button,ImVec4(0.2f,(followPattern)?0.6f:0.2f,0.2f,1.0f)); + if (ImGui::SmallButton("Pat##FollowPattern")) { handleUnimportant + followPattern=!followPattern; + } + ImGui::PopStyleColor(); + } + if (ImGui::IsWindowFocused(ImGuiFocusedFlags_ChildWindows)) curWindow=GUI_WINDOW_EDIT_CONTROLS; + ImGui::End(); + break; + case 3: // split + if (ImGui::Begin("Play Controls",&editControlsOpen,ImGuiWindowFlags_NoScrollbar|ImGuiWindowFlags_NoScrollWithMouse)) { + if (e->isPlaying()) { + if (ImGui::Button(ICON_FA_STOP "##Stop")) { + stop(); + } + } else { + if (ImGui::Button(ICON_FA_PLAY "##Play")) { + play(); + } + } + ImGui::SameLine(); + if (ImGui::Button(ICON_FA_PLAY_CIRCLE "##PlayAgain")) { + play(); + } + ImGui::SameLine(); + if (ImGui::Button(ICON_FA_ARROW_DOWN "##StepOne")) { + e->stepOne(cursor.y); + } + + ImGui::SameLine(); + ImGui::PushStyleColor(ImGuiCol_Button,ImVec4(0.2f,(edit)?0.6f:0.2f,0.2f,1.0f)); + if (ImGui::Button(ICON_FA_CIRCLE "##Edit")) { + edit=!edit; + } + ImGui::PopStyleColor(); + + bool metro=e->getMetronome(); + ImGui::SameLine(); + ImGui::PushStyleColor(ImGuiCol_Button,ImVec4(0.2f,(metro)?0.6f:0.2f,0.2f,1.0f)); + if (ImGui::Button(ICON_FA_BELL_O "##Metronome")) { + e->setMetronome(!metro); + } + ImGui::PopStyleColor(); + + ImGui::SameLine(); + bool repeatPattern=e->getRepeatPattern(); + ImGui::PushStyleColor(ImGuiCol_Button,ImVec4(0.2f,(repeatPattern)?0.6f:0.2f,0.2f,1.0f)); + if (ImGui::Button(ICON_FA_REPEAT "##RepeatPattern")) { + e->setRepeatPattern(!repeatPattern); + } + ImGui::PopStyleColor(); + } + if (ImGui::IsWindowFocused(ImGuiFocusedFlags_ChildWindows)) curWindow=GUI_WINDOW_EDIT_CONTROLS; + ImGui::End(); + + if (ImGui::Begin("Edit Controls",&editControlsOpen)) { + ImGui::Columns(2); + ImGui::Text("Octave"); + ImGui::SameLine(); + float cursor=ImGui::GetCursorPosX(); + float avail=ImGui::GetContentRegionAvail().x; + ImGui::SetNextItemWidth(avail); + if (ImGui::InputInt("##Octave",&curOctave,1,1)) { + if (curOctave>7) curOctave=7; + if (curOctave<-5) curOctave=-5; + for (size_t i=0; inoteOff(activeNotes[i].chan); + } + activeNotes.clear(); + + if (settings.insFocusesPattern && !ImGui::IsItemActive() && patternOpen) { + nextWindow=GUI_WINDOW_PATTERN; + } + } + + ImGui::Text("Step"); + ImGui::SameLine(); + ImGui::SetCursorPosX(cursor); + ImGui::SetNextItemWidth(avail); + if (ImGui::InputInt("##EditStep",&editStep,1,1)) { + if (editStep>=e->song.patLen) editStep=e->song.patLen-1; + if (editStep<0) editStep=0; + + if (settings.insFocusesPattern && !ImGui::IsItemActive() && patternOpen) { + nextWindow=GUI_WINDOW_PATTERN; + } + } + ImGui::NextColumn(); + + unimportant(ImGui::Checkbox("Follow orders",&followOrders)); + unimportant(ImGui::Checkbox("Follow pattern",&followPattern)); + } + if (ImGui::IsWindowFocused(ImGuiFocusedFlags_ChildWindows)) curWindow=GUI_WINDOW_EDIT_CONTROLS; + ImGui::End(); + break; + } +} \ No newline at end of file diff --git a/src/gui/editing.cpp b/src/gui/editing.cpp new file mode 100644 index 00000000..59c1fc34 --- /dev/null +++ b/src/gui/editing.cpp @@ -0,0 +1,1015 @@ +/** + * Furnace Tracker - multi-system chiptune tracker + * Copyright (C) 2021-2022 tildearrow and contributors + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include "gui.h" +#include "../ta-log.h" +#include "guiConst.h" +#include + +#include "actionUtil.h" + +const char* noteNameNormal(short note, short octave) { + if (note==100) { // note cut + return "OFF"; + } else if (note==101) { // note off and envelope release + return "==="; + } else if (note==102) { // envelope release only + return "REL"; + } else if (octave==0 && note==0) { + return "..."; + } + int seek=(note+(signed char)octave*12)+60; + if (seek<0 || seek>=180) { + return "???"; + } + return noteNames[seek]; +} + +void FurnaceGUI::prepareUndo(ActionType action) { + int order=e->getOrder(); + switch (action) { + case GUI_UNDO_CHANGE_ORDER: + oldOrders=e->song.orders; + oldOrdersLen=e->song.ordersLen; + break; + case GUI_UNDO_PATTERN_EDIT: + case GUI_UNDO_PATTERN_DELETE: + case GUI_UNDO_PATTERN_PULL: + case GUI_UNDO_PATTERN_PUSH: + case GUI_UNDO_PATTERN_CUT: + case GUI_UNDO_PATTERN_PASTE: + case GUI_UNDO_PATTERN_CHANGE_INS: + case GUI_UNDO_PATTERN_INTERPOLATE: + case GUI_UNDO_PATTERN_FADE: + case GUI_UNDO_PATTERN_SCALE: + case GUI_UNDO_PATTERN_RANDOMIZE: + case GUI_UNDO_PATTERN_INVERT_VAL: + case GUI_UNDO_PATTERN_FLIP: + case GUI_UNDO_PATTERN_COLLAPSE: + case GUI_UNDO_PATTERN_EXPAND: + for (int i=0; igetTotalChannelCount(); i++) { + e->song.pat[i].getPattern(e->song.orders.ord[i][order],false)->copyOn(oldPat[i]); + } + break; + } +} + +void FurnaceGUI::makeUndo(ActionType action) { + bool doPush=false; + UndoStep s; + s.type=action; + s.cursor=cursor; + s.selStart=selStart; + s.selEnd=selEnd; + int order=e->getOrder(); + s.order=order; + s.nibble=curNibble; + switch (action) { + case GUI_UNDO_CHANGE_ORDER: + for (int i=0; isong.orders.ord[i][j]) { + s.ord.push_back(UndoOrderData(i,j,oldOrders.ord[i][j],e->song.orders.ord[i][j])); + } + } + } + s.oldOrdersLen=oldOrdersLen; + s.newOrdersLen=e->song.ordersLen; + if (oldOrdersLen!=e->song.ordersLen) { + doPush=true; + } + if (!s.ord.empty()) { + doPush=true; + } + break; + case GUI_UNDO_PATTERN_EDIT: + case GUI_UNDO_PATTERN_DELETE: + case GUI_UNDO_PATTERN_PULL: + case GUI_UNDO_PATTERN_PUSH: + case GUI_UNDO_PATTERN_CUT: + case GUI_UNDO_PATTERN_PASTE: + case GUI_UNDO_PATTERN_CHANGE_INS: + case GUI_UNDO_PATTERN_INTERPOLATE: + case GUI_UNDO_PATTERN_FADE: + case GUI_UNDO_PATTERN_SCALE: + case GUI_UNDO_PATTERN_RANDOMIZE: + case GUI_UNDO_PATTERN_INVERT_VAL: + case GUI_UNDO_PATTERN_FLIP: + case GUI_UNDO_PATTERN_COLLAPSE: + case GUI_UNDO_PATTERN_EXPAND: + for (int i=0; igetTotalChannelCount(); i++) { + DivPattern* p=e->song.pat[i].getPattern(e->song.orders.ord[i][order],false); + for (int j=0; jsong.patLen; j++) { + for (int k=0; k<32; k++) { + if (p->data[j][k]!=oldPat[i]->data[j][k]) { + s.pat.push_back(UndoPatternData(i,e->song.orders.ord[i][order],j,k,oldPat[i]->data[j][k],p->data[j][k])); + } + } + } + } + if (!s.pat.empty()) { + doPush=true; + } + break; + } + if (doPush) { + MARK_MODIFIED; + undoHist.push_back(s); + redoHist.clear(); + if (undoHist.size()>settings.maxUndoSteps) undoHist.pop_front(); + } +} + +void FurnaceGUI::doSelectAll() { + finishSelection(); + curNibble=false; + if (selStart.xFine==0 && selEnd.xFine==2+e->song.pat[selEnd.xCoarse].effectRows*2) { + if (selStart.y==0 && selEnd.y==e->song.patLen-1) { // select entire pattern + selStart.xCoarse=0; + selStart.xFine=0; + selEnd.xCoarse=e->getTotalChannelCount()-1; + selEnd.xFine=2+e->song.pat[selEnd.xCoarse].effectRows*2; + } else { // select entire column + selStart.y=0; + selEnd.y=e->song.patLen-1; + } + } else { + int selStartX=0; + int selEndX=0; + // find row position + for (SelectionPoint i; i.xCoarse!=selStart.xCoarse || i.xFine!=selStart.xFine; selStartX++) { + i.xFine++; + if (i.xFine>=3+e->song.pat[i.xCoarse].effectRows*2) { + i.xFine=0; + i.xCoarse++; + } + } + for (SelectionPoint i; i.xCoarse!=selEnd.xCoarse || i.xFine!=selEnd.xFine; selEndX++) { + i.xFine++; + if (i.xFine>=3+e->song.pat[i.xCoarse].effectRows*2) { + i.xFine=0; + i.xCoarse++; + } + } + + float aspect=float(selEndX-selStartX+1)/float(selEnd.y-selStart.y+1); + if (aspect<=1.0f && !(selStart.y==0 && selEnd.y==e->song.patLen-1)) { // up-down + selStart.y=0; + selEnd.y=e->song.patLen-1; + } else { // left-right + selStart.xFine=0; + selEnd.xFine=2+e->song.pat[selEnd.xCoarse].effectRows*2; + } + } +} + +#define maskOut(x) \ + if (x==0) { \ + if (!opMaskNote) continue; \ + } else if (x==1) { \ + if (!opMaskIns) continue; \ + } else if (x==2) { \ + if (!opMaskVol) continue; \ + } else if (((x)&1)==0) { \ + if (!opMaskEffectVal) continue; \ + } else if (((x)&1)==1) { \ + if (!opMaskEffect) continue; \ + } + +void FurnaceGUI::doDelete() { + finishSelection(); + prepareUndo(GUI_UNDO_PATTERN_DELETE); + curNibble=false; + + int iCoarse=selStart.xCoarse; + int iFine=selStart.xFine; + int ord=e->getOrder(); + for (; iCoarse<=selEnd.xCoarse; iCoarse++) { + if (!e->song.chanShow[iCoarse]) continue; + DivPattern* pat=e->song.pat[iCoarse].getPattern(e->song.orders.ord[iCoarse][ord],true); + for (; iFine<3+e->song.pat[iCoarse].effectRows*2 && (iCoarsedata[j][iFine]=0; + if (selStart.y==selEnd.y) pat->data[j][2]=-1; + } + pat->data[j][iFine+1]=(iFine<1)?0:-1; + } + } + iFine=0; + } + + makeUndo(GUI_UNDO_PATTERN_DELETE); +} + +void FurnaceGUI::doPullDelete() { + finishSelection(); + prepareUndo(GUI_UNDO_PATTERN_PULL); + curNibble=false; + + if (settings.pullDeleteBehavior) { + if (--selStart.y<0) selStart.y=0; + if (--selEnd.y<0) selEnd.y=0; + if (--cursor.y<0) cursor.y=0; + updateScroll(cursor.y); + } + + int iCoarse=selStart.xCoarse; + int iFine=selStart.xFine; + int ord=e->getOrder(); + for (; iCoarse<=selEnd.xCoarse; iCoarse++) { + if (!e->song.chanShow[iCoarse]) continue; + DivPattern* pat=e->song.pat[iCoarse].getPattern(e->song.orders.ord[iCoarse][ord],true); + for (; iFine<3+e->song.pat[iCoarse].effectRows*2 && (iCoarsesong.patLen; j++) { + if (jsong.patLen-1) { + if (iFine==0) { + pat->data[j][iFine]=pat->data[j+1][iFine]; + } + pat->data[j][iFine+1]=pat->data[j+1][iFine+1]; + } else { + if (iFine==0) { + pat->data[j][iFine]=0; + } + pat->data[j][iFine+1]=(iFine<1)?0:-1; + } + } + } + iFine=0; + } + + makeUndo(GUI_UNDO_PATTERN_PULL); +} + +void FurnaceGUI::doInsert() { + finishSelection(); + prepareUndo(GUI_UNDO_PATTERN_PUSH); + curNibble=false; + + int iCoarse=selStart.xCoarse; + int iFine=selStart.xFine; + int ord=e->getOrder(); + for (; iCoarse<=selEnd.xCoarse; iCoarse++) { + if (!e->song.chanShow[iCoarse]) continue; + DivPattern* pat=e->song.pat[iCoarse].getPattern(e->song.orders.ord[iCoarse][ord],true); + for (; iFine<3+e->song.pat[iCoarse].effectRows*2 && (iCoarsesong.patLen-1; j>=selStart.y; j--) { + if (j==selStart.y) { + if (iFine==0) { + pat->data[j][iFine]=0; + } + pat->data[j][iFine+1]=(iFine<1)?0:-1; + } else { + if (iFine==0) { + pat->data[j][iFine]=pat->data[j-1][iFine]; + } + pat->data[j][iFine+1]=pat->data[j-1][iFine+1]; + } + } + } + iFine=0; + } + + makeUndo(GUI_UNDO_PATTERN_PUSH); +} + +void FurnaceGUI::doTranspose(int amount) { + finishSelection(); + prepareUndo(GUI_UNDO_PATTERN_DELETE); + curNibble=false; + + int iCoarse=selStart.xCoarse; + int iFine=selStart.xFine; + int ord=e->getOrder(); + for (; iCoarse<=selEnd.xCoarse; iCoarse++) { + if (!e->song.chanShow[iCoarse]) continue; + DivPattern* pat=e->song.pat[iCoarse].getPattern(e->song.orders.ord[iCoarse][ord],true); + for (; iFine<3+e->song.pat[iCoarse].effectRows*2 && (iCoarsedata[j][0]; + int origOctave=(signed char)pat->data[j][1]; + if (origNote!=0 && origNote!=100 && origNote!=101 && origNote!=102) { + origNote+=amount; + while (origNote>12) { + origNote-=12; + origOctave++; + } + while (origNote<1) { + origNote+=12; + origOctave--; + } + if (origOctave==9 && origNote>11) { + origNote=11; + origOctave=9; + } + if (origOctave>9) { + origNote=11; + origOctave=9; + } + if (origOctave<-5) { + origNote=1; + origOctave=-5; + } + pat->data[j][0]=origNote; + pat->data[j][1]=(unsigned char)origOctave; + } + } else { + int top=255; + if (iFine==1) { + if (e->song.ins.empty()) continue; + top=e->song.ins.size()-1; + } else if (iFine==2) { // volume + top=e->getMaxVolumeChan(iCoarse); + } + if (pat->data[j][iFine+1]==-1) continue; + pat->data[j][iFine+1]=MIN(top,MAX(0,pat->data[j][iFine+1]+amount)); + } + } + } + iFine=0; + } + + makeUndo(GUI_UNDO_PATTERN_DELETE); +} + +void FurnaceGUI::doCopy(bool cut) { + finishSelection(); + if (cut) { + curNibble=false; + prepareUndo(GUI_UNDO_PATTERN_CUT); + } + clipboard=fmt::sprintf("org.tildearrow.furnace - Pattern Data (%d)\n%d",DIV_ENGINE_VERSION,selStart.xFine); + + for (int j=selStart.y; j<=selEnd.y; j++) { + int iCoarse=selStart.xCoarse; + int iFine=selStart.xFine; + if (iFine>3 && !(iFine&1)) { + iFine--; + } + int ord=e->getOrder(); + clipboard+='\n'; + for (; iCoarse<=selEnd.xCoarse; iCoarse++) { + if (!e->song.chanShow[iCoarse]) continue; + DivPattern* pat=e->song.pat[iCoarse].getPattern(e->song.orders.ord[iCoarse][ord],true); + for (; iFine<3+e->song.pat[iCoarse].effectRows*2 && (iCoarsedata[j][0],pat->data[j][1]); + if (cut) { + pat->data[j][0]=0; + pat->data[j][1]=0; + } + } else { + if (pat->data[j][iFine+1]==-1) { + clipboard+=".."; + } else { + clipboard+=fmt::sprintf("%.2X",pat->data[j][iFine+1]); + } + if (cut) { + pat->data[j][iFine+1]=-1; + } + } + } + clipboard+='|'; + iFine=0; + } + } + SDL_SetClipboardText(clipboard.c_str()); + + if (cut) { + makeUndo(GUI_UNDO_PATTERN_CUT); + } +} + +void FurnaceGUI::doPaste(PasteMode mode) { + finishSelection(); + prepareUndo(GUI_UNDO_PATTERN_PASTE); + char* clipText=SDL_GetClipboardText(); + if (clipText!=NULL) { + if (clipText[0]) { + clipboard=clipText; + } + SDL_free(clipText); + } + std::vector data; + String tempS; + for (char i: clipboard) { + if (i=='\r') continue; + if (i=='\n') { + data.push_back(tempS); + tempS=""; + continue; + } + tempS+=i; + } + data.push_back(tempS); + + int startOff=-1; + bool invalidData=false; + if (data.size()<2) return; + if (data[0]!=fmt::sprintf("org.tildearrow.furnace - Pattern Data (%d)",DIV_ENGINE_VERSION)) return; + if (sscanf(data[1].c_str(),"%d",&startOff)!=1) return; + if (startOff<0) return; + + DETERMINE_LAST; + + int j=cursor.y; + char note[4]; + int ord=e->getOrder(); + for (size_t i=2; isong.patLen; i++) { + size_t charPos=0; + int iCoarse=cursor.xCoarse; + int iFine=(startOff>2 && cursor.xFine>2)?(((cursor.xFine-1)&(~1))|1):startOff; + + String& line=data[i]; + + while (charPossong.pat[iCoarse].getPattern(e->song.orders.ord[iCoarse][ord],true); + if (line[charPos]=='|') { + iCoarse++; + if (iCoarsesong.chanShow[iCoarse]) { + iCoarse++; + if (iCoarse>=lastChannel) break; + } + iFine=0; + charPos++; + continue; + } + if (iFine==0) { + if (charPos>=line.size()) { + invalidData=true; + break; + } + note[0]=line[charPos++]; + if (charPos>=line.size()) { + invalidData=true; + break; + } + note[1]=line[charPos++]; + if (charPos>=line.size()) { + invalidData=true; + break; + } + note[2]=line[charPos++]; + note[3]=0; + + if (iFine==0 && !opMaskNote) { + iFine++; + continue; + } + + if ((mode==GUI_PASTE_MODE_MIX_BG || mode==GUI_PASTE_MODE_MIX_FG) && strcmp(note,"...")==0) { + // do nothing. + } else { + if (mode!=GUI_PASTE_MODE_MIX_BG || (pat->data[j][0]==0 && pat->data[j][1]==0)) { + if (!decodeNote(note,pat->data[j][0],pat->data[j][1])) { + invalidData=true; + break; + } + } + } + } else { + if (charPos>=line.size()) { + invalidData=true; + break; + } + note[0]=line[charPos++]; + if (charPos>=line.size()) { + invalidData=true; + break; + } + note[1]=line[charPos++]; + note[2]=0; + + if (iFine==1) { + if (!opMaskIns) { + iFine++; + continue; + } + } else if (iFine==2) { + if (!opMaskVol) { + iFine++; + continue; + } + } else if ((iFine&1)==0) { + if (!opMaskEffectVal) { + iFine++; + continue; + } + } else if ((iFine&1)==1) { + if (!opMaskEffect) { + iFine++; + continue; + } + } + + if (strcmp(note,"..")==0) { + if (!(mode==GUI_PASTE_MODE_MIX_BG || mode==GUI_PASTE_MODE_MIX_FG)) { + pat->data[j][iFine+1]=-1; + } + } else { + unsigned int val=0; + if (sscanf(note,"%2X",&val)!=1) { + invalidData=true; + break; + } + if (mode!=GUI_PASTE_MODE_MIX_BG || pat->data[j][iFine+1]==-1) { + if (iFine<(3+e->song.pat[iCoarse].effectRows*2)) pat->data[j][iFine+1]=val; + } + } + } + iFine++; + } + + if (invalidData) { + logW("invalid clipboard data! failed at line %d char %d\n",i,charPos); + logW("%s\n",line.c_str()); + break; + } + j++; + if (mode==GUI_PASTE_MODE_OVERFLOW && j>=e->song.patLen && ordsong.ordersLen-1) { + j=0; + ord++; + } + + if (mode==GUI_PASTE_MODE_FLOOD && i==data.size()-1) { + i=1; + } + } + + makeUndo(GUI_UNDO_PATTERN_PASTE); +} + +void FurnaceGUI::doChangeIns(int ins) { + finishSelection(); + prepareUndo(GUI_UNDO_PATTERN_CHANGE_INS); + + int iCoarse=selStart.xCoarse; + int ord=e->getOrder(); + for (; iCoarse<=selEnd.xCoarse; iCoarse++) { + if (!e->song.chanShow[iCoarse]) continue; + DivPattern* pat=e->song.pat[iCoarse].getPattern(e->song.orders.ord[iCoarse][ord],true); + for (int j=selStart.y; j<=selEnd.y; j++) { + if (pat->data[j][2]!=-1 || !(pat->data[j][0]==0 && pat->data[j][1]==0)) { + pat->data[j][2]=ins; + } + } + } + + makeUndo(GUI_UNDO_PATTERN_CHANGE_INS); +} + +void FurnaceGUI::doInterpolate() { + finishSelection(); + prepareUndo(GUI_UNDO_PATTERN_INTERPOLATE); + + std::vector> points; + int iCoarse=selStart.xCoarse; + int iFine=selStart.xFine; + int ord=e->getOrder(); + for (; iCoarse<=selEnd.xCoarse; iCoarse++) { + if (!e->song.chanShow[iCoarse]) continue; + DivPattern* pat=e->song.pat[iCoarse].getPattern(e->song.orders.ord[iCoarse][ord],true); + for (; iFine<3+e->song.pat[iCoarse].effectRows*2 && (iCoarsedata[j][iFine+1]!=-1) { + points.emplace(points.end(),j,pat->data[j][iFine+1]); + } + } + + if (points.size()>1) for (size_t j=0; j& curPoint=points[j]; + std::pair& nextPoint=points[j+1]; + double distance=nextPoint.first-curPoint.first; + for (int k=0; k<(nextPoint.first-curPoint.first); k++) { + pat->data[k+curPoint.first][iFine+1]=curPoint.second+((nextPoint.second-curPoint.second)*(double)k/distance); + } + } + } else { + for (int j=selStart.y; j<=selEnd.y; j++) { + if (pat->data[j][0]!=0 && pat->data[j][1]!=0) { + if (pat->data[j][0]!=100 && pat->data[j][0]!=101 && pat->data[j][0]!=102) { + points.emplace(points.end(),j,pat->data[j][0]+pat->data[j][1]*12); + } + } + } + + if (points.size()>1) for (size_t j=0; j& curPoint=points[j]; + std::pair& nextPoint=points[j+1]; + double distance=nextPoint.first-curPoint.first; + for (int k=0; k<(nextPoint.first-curPoint.first); k++) { + int val=curPoint.second+((nextPoint.second-curPoint.second)*(double)k/distance); + pat->data[k+curPoint.first][0]=val%12; + pat->data[k+curPoint.first][1]=val/12; + if (pat->data[k+curPoint.first][0]==0) { + pat->data[k+curPoint.first][0]=12; + pat->data[k+curPoint.first][1]--; + } + pat->data[k+curPoint.first][1]&=255; + } + } + } + } + iFine=0; + } + + makeUndo(GUI_UNDO_PATTERN_INTERPOLATE); +} + +void FurnaceGUI::doFade(int p0, int p1, bool mode) { + finishSelection(); + prepareUndo(GUI_UNDO_PATTERN_FADE); + + int iCoarse=selStart.xCoarse; + int iFine=selStart.xFine; + int ord=e->getOrder(); + for (; iCoarse<=selEnd.xCoarse; iCoarse++) { + if (!e->song.chanShow[iCoarse]) continue; + DivPattern* pat=e->song.pat[iCoarse].getPattern(e->song.orders.ord[iCoarse][ord],true); + for (; iFine<3+e->song.pat[iCoarse].effectRows*2 && (iCoarsesong.ins.empty()) continue; + absoluteTop=e->song.ins.size()-1; + } else if (iFine==2) { // volume + absoluteTop=e->getMaxVolumeChan(iCoarse); + } + if (selEnd.y-selStart.y<1) continue; + for (int j=selStart.y; j<=selEnd.y; j++) { + double fraction=double(j-selStart.y)/double(selEnd.y-selStart.y); + int value=p0+double(p1-p0)*fraction; + if (mode) { // nibble + value&=15; + pat->data[j][iFine+1]=MIN(absoluteTop,value|(value<<4)); + } else { // byte + pat->data[j][iFine+1]=MIN(absoluteTop,value); + } + } + } + } + iFine=0; + } + + makeUndo(GUI_UNDO_PATTERN_FADE); +} + +void FurnaceGUI::doInvertValues() { + finishSelection(); + prepareUndo(GUI_UNDO_PATTERN_INVERT_VAL); + + int iCoarse=selStart.xCoarse; + int iFine=selStart.xFine; + int ord=e->getOrder(); + for (; iCoarse<=selEnd.xCoarse; iCoarse++) { + if (!e->song.chanShow[iCoarse]) continue; + DivPattern* pat=e->song.pat[iCoarse].getPattern(e->song.orders.ord[iCoarse][ord],true); + for (; iFine<3+e->song.pat[iCoarse].effectRows*2 && (iCoarsesong.ins.empty()) continue; + top=e->song.ins.size()-1; + } else if (iFine==2) { // volume + top=e->getMaxVolumeChan(iCoarse); + } + for (int j=selStart.y; j<=selEnd.y; j++) { + if (pat->data[j][iFine+1]==-1) continue; + pat->data[j][iFine+1]=top-pat->data[j][iFine+1]; + } + } + } + iFine=0; + } + + makeUndo(GUI_UNDO_PATTERN_INVERT_VAL); +} + +void FurnaceGUI::doScale(float top) { + finishSelection(); + prepareUndo(GUI_UNDO_PATTERN_SCALE); + + int iCoarse=selStart.xCoarse; + int iFine=selStart.xFine; + int ord=e->getOrder(); + for (; iCoarse<=selEnd.xCoarse; iCoarse++) { + if (!e->song.chanShow[iCoarse]) continue; + DivPattern* pat=e->song.pat[iCoarse].getPattern(e->song.orders.ord[iCoarse][ord],true); + for (; iFine<3+e->song.pat[iCoarse].effectRows*2 && (iCoarsesong.ins.empty()) continue; + absoluteTop=e->song.ins.size()-1; + } else if (iFine==2) { // volume + absoluteTop=e->getMaxVolumeChan(iCoarse); + } + for (int j=selStart.y; j<=selEnd.y; j++) { + if (pat->data[j][iFine+1]==-1) continue; + pat->data[j][iFine+1]=MIN(absoluteTop,(double)pat->data[j][iFine+1]*(top/100.0f)); + } + } + } + iFine=0; + } + + makeUndo(GUI_UNDO_PATTERN_SCALE); +} + +void FurnaceGUI::doRandomize(int bottom, int top, bool mode) { + finishSelection(); + prepareUndo(GUI_UNDO_PATTERN_RANDOMIZE); + + int iCoarse=selStart.xCoarse; + int iFine=selStart.xFine; + int ord=e->getOrder(); + for (; iCoarse<=selEnd.xCoarse; iCoarse++) { + if (!e->song.chanShow[iCoarse]) continue; + DivPattern* pat=e->song.pat[iCoarse].getPattern(e->song.orders.ord[iCoarse][ord],true); + for (; iFine<3+e->song.pat[iCoarse].effectRows*2 && (iCoarsesong.ins.empty()) continue; + absoluteTop=e->song.ins.size()-1; + } else if (iFine==2) { // volume + absoluteTop=e->getMaxVolumeChan(iCoarse); + } + for (int j=selStart.y; j<=selEnd.y; j++) { + int value=0; + int value2=0; + if (top-bottom<=0) { + value=MIN(absoluteTop,bottom); + value2=MIN(absoluteTop,bottom); + } else { + value=MIN(absoluteTop,bottom+(rand()%(top-bottom+1))); + value2=MIN(absoluteTop,bottom+(rand()%(top-bottom+1))); + } + if (mode) { + value&=15; + value2&=15; + pat->data[j][iFine+1]=value|(value2<<4); + } else { + pat->data[j][iFine+1]=value; + } + } + } + } + iFine=0; + } + + makeUndo(GUI_UNDO_PATTERN_RANDOMIZE); +} + +void FurnaceGUI::doFlip() { + finishSelection(); + prepareUndo(GUI_UNDO_PATTERN_FLIP); + + DivPattern patBuffer; + int iCoarse=selStart.xCoarse; + int iFine=selStart.xFine; + int ord=e->getOrder(); + for (; iCoarse<=selEnd.xCoarse; iCoarse++) { + if (!e->song.chanShow[iCoarse]) continue; + DivPattern* pat=e->song.pat[iCoarse].getPattern(e->song.orders.ord[iCoarse][ord],true); + for (; iFine<3+e->song.pat[iCoarse].effectRows*2 && (iCoarsedata[j][0]; + } + patBuffer.data[j][iFine+1]=pat->data[j][iFine+1]; + } + for (int j=selStart.y; j<=selEnd.y; j++) { + if (iFine==0) { + pat->data[j][0]=patBuffer.data[selEnd.y-j+selStart.y][0]; + } + pat->data[j][iFine+1]=patBuffer.data[selEnd.y-j+selStart.y][iFine+1]; + } + } + iFine=0; + } + + makeUndo(GUI_UNDO_PATTERN_FLIP); +} + +void FurnaceGUI::doCollapse(int divider) { + finishSelection(); + prepareUndo(GUI_UNDO_PATTERN_COLLAPSE); + + DivPattern patBuffer; + int iCoarse=selStart.xCoarse; + int iFine=selStart.xFine; + int ord=e->getOrder(); + for (; iCoarse<=selEnd.xCoarse; iCoarse++) { + if (!e->song.chanShow[iCoarse]) continue; + DivPattern* pat=e->song.pat[iCoarse].getPattern(e->song.orders.ord[iCoarse][ord],true); + for (; iFine<3+e->song.pat[iCoarse].effectRows*2 && (iCoarsedata[j][0]; + } + patBuffer.data[j][iFine+1]=pat->data[j][iFine+1]; + } + for (int j=0; j<=selEnd.y-selStart.y; j++) { + if (j*divider>=selEnd.y-selStart.y) { + if (iFine==0) { + pat->data[j+selStart.y][0]=0; + pat->data[j+selStart.y][1]=0; + } else { + pat->data[j+selStart.y][iFine+1]=-1; + } + } else { + if (iFine==0) { + pat->data[j+selStart.y][0]=patBuffer.data[j*divider+selStart.y][0]; + } + pat->data[j+selStart.y][iFine+1]=patBuffer.data[j*divider+selStart.y][iFine+1]; + + if (iFine==0) { + for (int k=1; k=selEnd.y-selStart.y) break; + if (!(pat->data[j+selStart.y][0]==0 && pat->data[j+selStart.y][1]==0)) break; + pat->data[j+selStart.y][0]=patBuffer.data[j*divider+selStart.y+k][0]; + pat->data[j+selStart.y][1]=patBuffer.data[j*divider+selStart.y+k][1]; + } + } else { + for (int k=1; k=selEnd.y-selStart.y) break; + if (pat->data[j+selStart.y][iFine+1]!=-1) break; + pat->data[j+selStart.y][iFine+1]=patBuffer.data[j*divider+selStart.y+k][iFine+1]; + } + } + } + } + } + iFine=0; + } + + makeUndo(GUI_UNDO_PATTERN_COLLAPSE); +} + +void FurnaceGUI::doExpand(int multiplier) { + if (multiplier<1) return; + + finishSelection(); + prepareUndo(GUI_UNDO_PATTERN_EXPAND); + + DivPattern patBuffer; + int iCoarse=selStart.xCoarse; + int iFine=selStart.xFine; + int ord=e->getOrder(); + for (; iCoarse<=selEnd.xCoarse; iCoarse++) { + if (!e->song.chanShow[iCoarse]) continue; + DivPattern* pat=e->song.pat[iCoarse].getPattern(e->song.orders.ord[iCoarse][ord],true); + for (; iFine<3+e->song.pat[iCoarse].effectRows*2 && (iCoarsedata[j][0]; + } + patBuffer.data[j][iFine+1]=pat->data[j][iFine+1]; + } + for (int j=0; j<=(selEnd.y-selStart.y)*multiplier; j++) { + if ((j+selStart.y)>=e->song.patLen) break; + if ((j%multiplier)!=0) { + if (iFine==0) { + pat->data[j+selStart.y][0]=0; + pat->data[j+selStart.y][1]=0; + } else { + pat->data[j+selStart.y][iFine+1]=-1; + } + continue; + } + if (iFine==0) { + pat->data[j+selStart.y][0]=patBuffer.data[j/multiplier+selStart.y][0]; + } + pat->data[j+selStart.y][iFine+1]=patBuffer.data[j/multiplier+selStart.y][iFine+1]; + } + } + iFine=0; + } + + makeUndo(GUI_UNDO_PATTERN_EXPAND); +} + +void FurnaceGUI::doUndo() { + if (undoHist.empty()) return; + UndoStep& us=undoHist.back(); + redoHist.push_back(us); + MARK_MODIFIED; + + switch (us.type) { + case GUI_UNDO_CHANGE_ORDER: + e->song.ordersLen=us.oldOrdersLen; + for (UndoOrderData& i: us.ord) { + e->song.orders.ord[i.chan][i.ord]=i.oldVal; + } + break; + case GUI_UNDO_PATTERN_EDIT: + case GUI_UNDO_PATTERN_DELETE: + case GUI_UNDO_PATTERN_PULL: + case GUI_UNDO_PATTERN_PUSH: + case GUI_UNDO_PATTERN_CUT: + case GUI_UNDO_PATTERN_PASTE: + case GUI_UNDO_PATTERN_CHANGE_INS: + case GUI_UNDO_PATTERN_INTERPOLATE: + case GUI_UNDO_PATTERN_FADE: + case GUI_UNDO_PATTERN_SCALE: + case GUI_UNDO_PATTERN_RANDOMIZE: + case GUI_UNDO_PATTERN_INVERT_VAL: + case GUI_UNDO_PATTERN_FLIP: + case GUI_UNDO_PATTERN_COLLAPSE: + case GUI_UNDO_PATTERN_EXPAND: + for (UndoPatternData& i: us.pat) { + DivPattern* p=e->song.pat[i.chan].getPattern(i.pat,true); + p->data[i.row][i.col]=i.oldVal; + } + if (!e->isPlaying()) { + cursor=us.cursor; + selStart=us.selStart; + selEnd=us.selEnd; + curNibble=us.nibble; + updateScroll(cursor.y); + e->setOrder(us.order); + } + break; + } + + undoHist.pop_back(); +} + +void FurnaceGUI::doRedo() { + if (redoHist.empty()) return; + UndoStep& us=redoHist.back(); + undoHist.push_back(us); + MARK_MODIFIED; + + switch (us.type) { + case GUI_UNDO_CHANGE_ORDER: + e->song.ordersLen=us.newOrdersLen; + for (UndoOrderData& i: us.ord) { + e->song.orders.ord[i.chan][i.ord]=i.newVal; + } + break; + case GUI_UNDO_PATTERN_EDIT: + case GUI_UNDO_PATTERN_DELETE: + case GUI_UNDO_PATTERN_PULL: + case GUI_UNDO_PATTERN_PUSH: + case GUI_UNDO_PATTERN_CUT: + case GUI_UNDO_PATTERN_PASTE: + case GUI_UNDO_PATTERN_CHANGE_INS: + case GUI_UNDO_PATTERN_INTERPOLATE: + case GUI_UNDO_PATTERN_FADE: + case GUI_UNDO_PATTERN_SCALE: + case GUI_UNDO_PATTERN_RANDOMIZE: + case GUI_UNDO_PATTERN_INVERT_VAL: + case GUI_UNDO_PATTERN_FLIP: + case GUI_UNDO_PATTERN_COLLAPSE: + case GUI_UNDO_PATTERN_EXPAND: + for (UndoPatternData& i: us.pat) { + DivPattern* p=e->song.pat[i.chan].getPattern(i.pat,true); + p->data[i.row][i.col]=i.newVal; + } + if (!e->isPlaying()) { + cursor=us.cursor; + selStart=us.selStart; + selEnd=us.selEnd; + curNibble=us.nibble; + updateScroll(cursor.y); + e->setOrder(us.order); + } + + break; + } + + redoHist.pop_back(); +} \ No newline at end of file diff --git a/src/gui/gui.cpp b/src/gui/gui.cpp index f3ae5a7f..2ba2131c 100644 --- a/src/gui/gui.cpp +++ b/src/gui/gui.cpp @@ -20,15 +20,12 @@ #define _USE_MATH_DEFINES #include "gui.h" #include "util.h" -#include "debug.h" -#include "fonts.h" #include "icon.h" #include "../ta-log.h" #include "../fileutils.h" #include "imgui.h" #include "imgui_impl_sdl.h" #include "imgui_impl_sdlrenderer.h" -#include "imgui_internal.h" #include "ImGuiFileDialog.h" #include "IconsFontAwesome4.h" #include "misc/cpp/imgui_stdlib.h" @@ -61,6 +58,8 @@ extern "C" { #define BACKUP_FUR "/backup.fur" #endif +#include "actionUtil.h" + bool Particle::update(float frameTime) { pos.x+=speed.x*frameTime; pos.y+=speed.y*frameTime; @@ -75,23 +74,6 @@ void FurnaceGUI::bindEngine(DivEngine* eng) { e=eng; } -const char* noteNameNormal(short note, short octave) { - if (note==100) { // note cut - return "OFF"; - } else if (note==101) { // note off and envelope release - return "==="; - } else if (note==102) { // envelope release only - return "REL"; - } else if (octave==0 && note==0) { - return "..."; - } - int seek=(note+(signed char)octave*12)+60; - if (seek<0 || seek>=180) { - return "???"; - } - return noteNames[seek]; -} - const char* FurnaceGUI::noteName(short note, short octave) { if (note==100) { return "OFF"; @@ -678,2636 +660,6 @@ float FurnaceGUI::calcBPM(int s1, int s2, float hz) { return 120.0f*hz/(timeBase*hl*speedSum); } -void FurnaceGUI::drawEditControls() { - if (nextWindow==GUI_WINDOW_EDIT_CONTROLS) { - editControlsOpen=true; - ImGui::SetNextWindowFocus(); - nextWindow=GUI_WINDOW_NOTHING; - } - if (!editControlsOpen) return; - switch (settings.controlLayout) { - case 0: // classic - if (ImGui::Begin("Play/Edit Controls",&editControlsOpen)) { - ImGui::Text("Octave"); - ImGui::SameLine(); - if (ImGui::InputInt("##Octave",&curOctave,1,1)) { - if (curOctave>7) curOctave=7; - if (curOctave<-5) curOctave=-5; - for (size_t i=0; inoteOff(activeNotes[i].chan); - } - activeNotes.clear(); - - if (settings.insFocusesPattern && !ImGui::IsItemActive() && patternOpen) { - nextWindow=GUI_WINDOW_PATTERN; - } - } - - ImGui::Text("Edit Step"); - ImGui::SameLine(); - if (ImGui::InputInt("##EditStep",&editStep,1,1)) { - if (editStep>=e->song.patLen) editStep=e->song.patLen-1; - if (editStep<0) editStep=0; - - if (settings.insFocusesPattern && !ImGui::IsItemActive() && patternOpen) { - nextWindow=GUI_WINDOW_PATTERN; - } - } - - if (ImGui::Button(ICON_FA_PLAY "##Play")) { - play(); - } - ImGui::SameLine(); - if (ImGui::Button(ICON_FA_STOP "##Stop")) { - stop(); - } - ImGui::SameLine(); - ImGui::Checkbox("Edit",&edit); - ImGui::SameLine(); - bool metro=e->getMetronome(); - if (ImGui::Checkbox("Metronome",&metro)) { - e->setMetronome(metro); - } - - ImGui::Text("Follow"); - ImGui::SameLine(); - unimportant(ImGui::Checkbox("Orders",&followOrders)); - ImGui::SameLine(); - unimportant(ImGui::Checkbox("Pattern",&followPattern)); - - bool repeatPattern=e->getRepeatPattern(); - if (ImGui::Checkbox("Repeat pattern",&repeatPattern)) { - e->setRepeatPattern(repeatPattern); - } - ImGui::SameLine(); - if (ImGui::Button(ICON_FA_ARROW_DOWN "##StepOne")) { - e->stepOne(cursor.y); - } - } - if (ImGui::IsWindowFocused(ImGuiFocusedFlags_ChildWindows)) curWindow=GUI_WINDOW_EDIT_CONTROLS; - ImGui::End(); - break; - case 1: // compact - if (ImGui::Begin("Play/Edit Controls",&editControlsOpen,ImGuiWindowFlags_NoScrollbar|ImGuiWindowFlags_NoScrollWithMouse)) { - if (ImGui::Button(ICON_FA_STOP "##Stop")) { - stop(); - } - ImGui::SameLine(); - if (ImGui::Button(ICON_FA_PLAY "##Play")) { - play(); - } - ImGui::SameLine(); - if (ImGui::Button(ICON_FA_ARROW_DOWN "##StepOne")) { - e->stepOne(cursor.y); - } - - ImGui::SameLine(); - bool repeatPattern=e->getRepeatPattern(); - ImGui::PushStyleColor(ImGuiCol_Button,ImVec4(0.2f,(repeatPattern)?0.6f:0.2f,0.2f,1.0f)); - if (ImGui::Button(ICON_FA_REPEAT "##RepeatPattern")) { - e->setRepeatPattern(!repeatPattern); - } - ImGui::PopStyleColor(); - - ImGui::SameLine(); - ImGui::PushStyleColor(ImGuiCol_Button,ImVec4(0.2f,(edit)?0.6f:0.2f,0.2f,1.0f)); - if (ImGui::Button(ICON_FA_CIRCLE "##Edit")) { - edit=!edit; - } - ImGui::PopStyleColor(); - - ImGui::SameLine(); - bool metro=e->getMetronome(); - ImGui::PushStyleColor(ImGuiCol_Button,ImVec4(0.2f,(metro)?0.6f:0.2f,0.2f,1.0f)); - if (ImGui::Button(ICON_FA_BELL_O "##Metronome")) { - e->setMetronome(!metro); - } - ImGui::PopStyleColor(); - - ImGui::SameLine(); - ImGui::Text("Octave"); - ImGui::SameLine(); - ImGui::SetNextItemWidth(96.0f*dpiScale); - if (ImGui::InputInt("##Octave",&curOctave,1,1)) { - if (curOctave>7) curOctave=7; - if (curOctave<-5) curOctave=-5; - for (size_t i=0; inoteOff(activeNotes[i].chan); - } - activeNotes.clear(); - - if (settings.insFocusesPattern && !ImGui::IsItemActive() && patternOpen) { - nextWindow=GUI_WINDOW_PATTERN; - } - } - - ImGui::SameLine(); - ImGui::Text("Edit Step"); - ImGui::SameLine(); - ImGui::SetNextItemWidth(96.0f*dpiScale); - if (ImGui::InputInt("##EditStep",&editStep,1,1)) { - if (editStep>=e->song.patLen) editStep=e->song.patLen-1; - if (editStep<0) editStep=0; - - if (settings.insFocusesPattern && !ImGui::IsItemActive() && patternOpen) { - nextWindow=GUI_WINDOW_PATTERN; - } - } - - ImGui::SameLine(); - ImGui::Text("Follow"); - ImGui::SameLine(); - unimportant(ImGui::Checkbox("Orders",&followOrders)); - ImGui::SameLine(); - unimportant(ImGui::Checkbox("Pattern",&followPattern)); - } - if (ImGui::IsWindowFocused(ImGuiFocusedFlags_ChildWindows)) curWindow=GUI_WINDOW_EDIT_CONTROLS; - ImGui::End(); - break; - case 2: // compact vertical - if (ImGui::Begin("Play/Edit Controls",&editControlsOpen,ImGuiWindowFlags_NoScrollbar|ImGuiWindowFlags_NoScrollWithMouse)) { - if (ImGui::Button(ICON_FA_PLAY "##Play")) { - play(); - } - if (ImGui::Button(ICON_FA_STOP "##Stop")) { - stop(); - } - if (ImGui::Button(ICON_FA_ARROW_DOWN "##StepOne")) { - e->stepOne(cursor.y); - } - - bool repeatPattern=e->getRepeatPattern(); - ImGui::PushStyleColor(ImGuiCol_Button,ImVec4(0.2f,(repeatPattern)?0.6f:0.2f,0.2f,1.0f)); - if (ImGui::Button(ICON_FA_REPEAT "##RepeatPattern")) { - e->setRepeatPattern(!repeatPattern); - } - ImGui::PopStyleColor(); - - ImGui::PushStyleColor(ImGuiCol_Button,ImVec4(0.2f,(edit)?0.6f:0.2f,0.2f,1.0f)); - if (ImGui::Button(ICON_FA_CIRCLE "##Edit")) { - edit=!edit; - } - ImGui::PopStyleColor(); - - bool metro=e->getMetronome(); - ImGui::PushStyleColor(ImGuiCol_Button,ImVec4(0.2f,(metro)?0.6f:0.2f,0.2f,1.0f)); - if (ImGui::Button(ICON_FA_BELL_O "##Metronome")) { - e->setMetronome(!metro); - } - ImGui::PopStyleColor(); - - ImGui::Text("Oct."); - float avail=ImGui::GetContentRegionAvail().x; - ImGui::SetNextItemWidth(avail); - if (ImGui::InputInt("##Octave",&curOctave,0,0)) { - if (curOctave>7) curOctave=7; - if (curOctave<-5) curOctave=-5; - for (size_t i=0; inoteOff(activeNotes[i].chan); - } - activeNotes.clear(); - - if (settings.insFocusesPattern && !ImGui::IsItemActive() && patternOpen) { - nextWindow=GUI_WINDOW_PATTERN; - } - } - - ImGui::Text("Step"); - ImGui::SetNextItemWidth(avail); - if (ImGui::InputInt("##EditStep",&editStep,0,0)) { - if (editStep>=e->song.patLen) editStep=e->song.patLen-1; - if (editStep<0) editStep=0; - - if (settings.insFocusesPattern && !ImGui::IsItemActive() && patternOpen) { - nextWindow=GUI_WINDOW_PATTERN; - } - } - - ImGui::Text("Foll."); - ImGui::PushStyleColor(ImGuiCol_Button,ImVec4(0.2f,(followOrders)?0.6f:0.2f,0.2f,1.0f)); - if (ImGui::SmallButton("Ord##FollowOrders")) { handleUnimportant - followOrders=!followOrders; - } - ImGui::PopStyleColor(); - ImGui::PushStyleColor(ImGuiCol_Button,ImVec4(0.2f,(followPattern)?0.6f:0.2f,0.2f,1.0f)); - if (ImGui::SmallButton("Pat##FollowPattern")) { handleUnimportant - followPattern=!followPattern; - } - ImGui::PopStyleColor(); - } - if (ImGui::IsWindowFocused(ImGuiFocusedFlags_ChildWindows)) curWindow=GUI_WINDOW_EDIT_CONTROLS; - ImGui::End(); - break; - case 3: // split - if (ImGui::Begin("Play Controls",&editControlsOpen,ImGuiWindowFlags_NoScrollbar|ImGuiWindowFlags_NoScrollWithMouse)) { - if (e->isPlaying()) { - if (ImGui::Button(ICON_FA_STOP "##Stop")) { - stop(); - } - } else { - if (ImGui::Button(ICON_FA_PLAY "##Play")) { - play(); - } - } - ImGui::SameLine(); - if (ImGui::Button(ICON_FA_PLAY_CIRCLE "##PlayAgain")) { - play(); - } - ImGui::SameLine(); - if (ImGui::Button(ICON_FA_ARROW_DOWN "##StepOne")) { - e->stepOne(cursor.y); - } - - ImGui::SameLine(); - ImGui::PushStyleColor(ImGuiCol_Button,ImVec4(0.2f,(edit)?0.6f:0.2f,0.2f,1.0f)); - if (ImGui::Button(ICON_FA_CIRCLE "##Edit")) { - edit=!edit; - } - ImGui::PopStyleColor(); - - bool metro=e->getMetronome(); - ImGui::SameLine(); - ImGui::PushStyleColor(ImGuiCol_Button,ImVec4(0.2f,(metro)?0.6f:0.2f,0.2f,1.0f)); - if (ImGui::Button(ICON_FA_BELL_O "##Metronome")) { - e->setMetronome(!metro); - } - ImGui::PopStyleColor(); - - ImGui::SameLine(); - bool repeatPattern=e->getRepeatPattern(); - ImGui::PushStyleColor(ImGuiCol_Button,ImVec4(0.2f,(repeatPattern)?0.6f:0.2f,0.2f,1.0f)); - if (ImGui::Button(ICON_FA_REPEAT "##RepeatPattern")) { - e->setRepeatPattern(!repeatPattern); - } - ImGui::PopStyleColor(); - } - if (ImGui::IsWindowFocused(ImGuiFocusedFlags_ChildWindows)) curWindow=GUI_WINDOW_EDIT_CONTROLS; - ImGui::End(); - - if (ImGui::Begin("Edit Controls",&editControlsOpen)) { - ImGui::Columns(2); - ImGui::Text("Octave"); - ImGui::SameLine(); - float cursor=ImGui::GetCursorPosX(); - float avail=ImGui::GetContentRegionAvail().x; - ImGui::SetNextItemWidth(avail); - if (ImGui::InputInt("##Octave",&curOctave,1,1)) { - if (curOctave>7) curOctave=7; - if (curOctave<-5) curOctave=-5; - for (size_t i=0; inoteOff(activeNotes[i].chan); - } - activeNotes.clear(); - - if (settings.insFocusesPattern && !ImGui::IsItemActive() && patternOpen) { - nextWindow=GUI_WINDOW_PATTERN; - } - } - - ImGui::Text("Step"); - ImGui::SameLine(); - ImGui::SetCursorPosX(cursor); - ImGui::SetNextItemWidth(avail); - if (ImGui::InputInt("##EditStep",&editStep,1,1)) { - if (editStep>=e->song.patLen) editStep=e->song.patLen-1; - if (editStep<0) editStep=0; - - if (settings.insFocusesPattern && !ImGui::IsItemActive() && patternOpen) { - nextWindow=GUI_WINDOW_PATTERN; - } - } - ImGui::NextColumn(); - - unimportant(ImGui::Checkbox("Follow orders",&followOrders)); - unimportant(ImGui::Checkbox("Follow pattern",&followPattern)); - } - if (ImGui::IsWindowFocused(ImGuiFocusedFlags_ChildWindows)) curWindow=GUI_WINDOW_EDIT_CONTROLS; - ImGui::End(); - break; - } -} - -void FurnaceGUI::drawSongInfo() { - if (nextWindow==GUI_WINDOW_SONG_INFO) { - songInfoOpen=true; - ImGui::SetNextWindowFocus(); - nextWindow=GUI_WINDOW_NOTHING; - } - if (!songInfoOpen) return; - if (ImGui::Begin("Song Information",&songInfoOpen)) { - if (ImGui::BeginTable("NameAuthor",2,ImGuiTableFlags_SizingStretchProp)) { - ImGui::TableSetupColumn("c0",ImGuiTableColumnFlags_WidthFixed,0.0); - ImGui::TableSetupColumn("c1",ImGuiTableColumnFlags_WidthStretch,0.0); - ImGui::TableNextRow(); - ImGui::TableNextColumn(); - ImGui::Text("Name"); - ImGui::TableNextColumn(); - float avail=ImGui::GetContentRegionAvail().x; - ImGui::SetNextItemWidth(avail); - if (ImGui::InputText("##Name",&e->song.name)) { MARK_MODIFIED - updateWindowTitle(); - } - ImGui::TableNextRow(); - ImGui::TableNextColumn(); - ImGui::Text("Author"); - ImGui::TableNextColumn(); - ImGui::SetNextItemWidth(avail); - if (ImGui::InputText("##Author",&e->song.author)) { - MARK_MODIFIED; - } - ImGui::EndTable(); - } - - if (ImGui::BeginTable("OtherProps",3,ImGuiTableFlags_SizingStretchProp)) { - ImGui::TableSetupColumn("c0",ImGuiTableColumnFlags_WidthFixed,0.0); - ImGui::TableSetupColumn("c1",ImGuiTableColumnFlags_WidthStretch,0.0); - ImGui::TableSetupColumn("c2",ImGuiTableColumnFlags_WidthStretch,0.0); - - ImGui::TableNextRow(); - ImGui::TableNextColumn(); - ImGui::Text("TimeBase"); - ImGui::TableNextColumn(); - float avail=ImGui::GetContentRegionAvail().x; - ImGui::SetNextItemWidth(avail); - unsigned char realTB=e->song.timeBase+1; - if (ImGui::InputScalar("##TimeBase",ImGuiDataType_U8,&realTB,&_ONE,&_THREE)) { MARK_MODIFIED - if (realTB<1) realTB=1; - if (realTB>16) realTB=16; - e->song.timeBase=realTB-1; - } - ImGui::TableNextColumn(); - ImGui::Text("%.2f BPM",calcBPM(e->song.speed1,e->song.speed2,e->song.hz)); - - ImGui::TableNextRow(); - ImGui::TableNextColumn(); - ImGui::Text("Speed"); - ImGui::TableNextColumn(); - ImGui::SetNextItemWidth(avail); - if (ImGui::InputScalar("##Speed1",ImGuiDataType_U8,&e->song.speed1,&_ONE,&_THREE)) { MARK_MODIFIED - if (e->song.speed1<1) e->song.speed1=1; - if (e->isPlaying()) play(); - } - ImGui::TableNextColumn(); - ImGui::SetNextItemWidth(avail); - if (ImGui::InputScalar("##Speed2",ImGuiDataType_U8,&e->song.speed2,&_ONE,&_THREE)) { MARK_MODIFIED - if (e->song.speed2<1) e->song.speed2=1; - if (e->isPlaying()) play(); - } - - ImGui::TableNextRow(); - ImGui::TableNextColumn(); - ImGui::Text("Highlight"); - ImGui::TableNextColumn(); - ImGui::SetNextItemWidth(avail); - if (ImGui::InputScalar("##Highlight1",ImGuiDataType_U8,&e->song.hilightA,&_ONE,&_THREE)) { - MARK_MODIFIED; - } - ImGui::TableNextColumn(); - ImGui::SetNextItemWidth(avail); - if (ImGui::InputScalar("##Highlight2",ImGuiDataType_U8,&e->song.hilightB,&_ONE,&_THREE)) { - MARK_MODIFIED; - } - - ImGui::TableNextRow(); - ImGui::TableNextColumn(); - ImGui::Text("Pattern Length"); - ImGui::TableNextColumn(); - ImGui::SetNextItemWidth(avail); - int patLen=e->song.patLen; - if (ImGui::InputInt("##PatLength",&patLen,1,3)) { MARK_MODIFIED - if (patLen<1) patLen=1; - if (patLen>256) patLen=256; - e->song.patLen=patLen; - } - - ImGui::TableNextRow(); - ImGui::TableNextColumn(); - ImGui::Text("Song Length"); - ImGui::TableNextColumn(); - ImGui::SetNextItemWidth(avail); - int ordLen=e->song.ordersLen; - if (ImGui::InputInt("##OrdLength",&ordLen,1,3)) { MARK_MODIFIED - if (ordLen<1) ordLen=1; - if (ordLen>127) ordLen=127; - e->song.ordersLen=ordLen; - } - - ImGui::TableNextRow(); - ImGui::TableNextColumn(); - if (ImGui::Selectable(tempoView?"Base Tempo##TempoOrHz":"Tick Rate##TempoOrHz")) { - tempoView=!tempoView; - } - ImGui::TableNextColumn(); - ImGui::SetNextItemWidth(avail); - float setHz=tempoView?e->song.hz*2.5:e->song.hz; - if (ImGui::InputFloat("##Rate",&setHz,1.0f,1.0f,"%g")) { MARK_MODIFIED - if (tempoView) setHz/=2.5; - if (setHz<10) setHz=10; - if (setHz>999) setHz=999; - e->setSongRate(setHz,setHz<52); - } - if (tempoView) { - ImGui::TableNextColumn(); - ImGui::Text("= %gHz",e->song.hz); - } else { - if (e->song.hz>=49.98 && e->song.hz<=50.02) { - ImGui::TableNextColumn(); - ImGui::Text("PAL"); - } - if (e->song.hz>=59.9 && e->song.hz<=60.11) { - ImGui::TableNextColumn(); - ImGui::Text("NTSC"); - } - } - - ImGui::TableNextRow(); - ImGui::TableNextColumn(); - ImGui::Text("Tuning (A-4)"); - ImGui::TableNextColumn(); - float tune=e->song.tuning; - ImGui::SetNextItemWidth(avail); - if (ImGui::InputFloat("##Tuning",&tune,1.0f,3.0f,"%g")) { MARK_MODIFIED - if (tune<220.0f) tune=220.0f; - if (tune>880.0f) tune=880.0f; - e->song.tuning=tune; - } - ImGui::EndTable(); - } - } - if (ImGui::IsWindowFocused(ImGuiFocusedFlags_ChildWindows)) curWindow=GUI_WINDOW_SONG_INFO; - ImGui::End(); -} - -void FurnaceGUI::drawMixer() { - if (nextWindow==GUI_WINDOW_MIXER) { - mixerOpen=true; - ImGui::SetNextWindowFocus(); - nextWindow=GUI_WINDOW_NOTHING; - } - if (!mixerOpen) return; - ImGui::SetNextWindowSizeConstraints(ImVec2(400.0f*dpiScale,200.0f*dpiScale),ImVec2(scrW*dpiScale,scrH*dpiScale)); - if (ImGui::Begin("Mixer",&mixerOpen,settings.allowEditDocking?0:ImGuiWindowFlags_NoDocking)) { - char id[32]; - if (ImGui::SliderFloat("Master Volume",&e->song.masterVol,0,3,"%.2fx")) { - if (e->song.masterVol<0) e->song.masterVol=0; - if (e->song.masterVol>3) e->song.masterVol=3; - } rightClickable - for (int i=0; isong.systemLen; i++) { - snprintf(id,31,"MixS%d",i); - bool doInvert=e->song.systemVol[i]&128; - signed char vol=e->song.systemVol[i]&127; - ImGui::PushID(id); - ImGui::Text("%d. %s",i+1,getSystemName(e->song.system[i])); - ImGui::SameLine(ImGui::GetWindowWidth()-(82.0f*dpiScale)); - if (ImGui::Checkbox("Invert",&doInvert)) { - e->song.systemVol[i]^=128; - } - ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x-(50.0f*dpiScale)); - if (ImGui::SliderScalar("Volume",ImGuiDataType_S8,&vol,&_ZERO,&_ONE_HUNDRED_TWENTY_SEVEN)) { - e->song.systemVol[i]=(e->song.systemVol[i]&128)|vol; - } rightClickable - ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x-(50.0f*dpiScale)); - ImGui::SliderScalar("Panning",ImGuiDataType_S8,&e->song.systemPan[i],&_MINUS_ONE_HUNDRED_TWENTY_SEVEN,&_ONE_HUNDRED_TWENTY_SEVEN); rightClickable - - ImGui::PopID(); - } - } - if (ImGui::IsWindowFocused(ImGuiFocusedFlags_ChildWindows)) curWindow=GUI_WINDOW_MIXER; - ImGui::End(); -} - -void FurnaceGUI::drawOsc() { - if (nextWindow==GUI_WINDOW_OSCILLOSCOPE) { - oscOpen=true; - ImGui::SetNextWindowFocus(); - nextWindow=GUI_WINDOW_NOTHING; - } - if (!oscOpen) return; - ImGui::SetNextWindowSizeConstraints(ImVec2(64.0f*dpiScale,32.0f*dpiScale),ImVec2(scrW*dpiScale,scrH*dpiScale)); - ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding,ImVec2(0,0)); - ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing,ImVec2(0,0)); - ImGui::PushStyleVar(ImGuiStyleVar_ItemInnerSpacing,ImVec2(0,0)); - if (ImGui::Begin("Oscilloscope",&oscOpen)) { - float values[512]; - for (int i=0; i<512; i++) { - int pos=i*e->oscSize/512; - values[i]=(e->oscBuf[0][pos]+e->oscBuf[1][pos])*0.5f; - } - //ImGui::SetCursorPos(ImVec2(0,0)); - ImGui::BeginDisabled(); - ImGui::PlotLines("##SingleOsc",values,512,0,NULL,-1.0f,1.0f,ImGui::GetContentRegionAvail()); - ImGui::EndDisabled(); - } - ImGui::PopStyleVar(3); - if (ImGui::IsWindowFocused(ImGuiFocusedFlags_ChildWindows)) curWindow=GUI_WINDOW_OSCILLOSCOPE; - ImGui::End(); -} - -void FurnaceGUI::drawVolMeter() { - if (nextWindow==GUI_WINDOW_VOL_METER) { - volMeterOpen=true; - ImGui::SetNextWindowFocus(); - nextWindow=GUI_WINDOW_NOTHING; - } - if (!volMeterOpen) return; - if (--isClipping<0) isClipping=0; - ImGui::SetNextWindowSizeConstraints(ImVec2(6.0f*dpiScale,6.0f*dpiScale),ImVec2(scrW*dpiScale,scrH*dpiScale)); - ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding,ImVec2(0,0)); - ImGui::PushStyleVar(ImGuiStyleVar_FramePadding,ImVec2(0,0)); - ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing,ImVec2(0,0)); - ImGui::PushStyleVar(ImGuiStyleVar_ItemInnerSpacing,ImVec2(0,0)); - if (ImGui::Begin("Volume Meter",&volMeterOpen)) { - ImDrawList* dl=ImGui::GetWindowDrawList(); - bool aspectRatio=(ImGui::GetWindowSize().x/ImGui::GetWindowSize().y)>1.0; - - ImVec2 minArea=ImVec2( - ImGui::GetWindowPos().x+ImGui::GetCursorPos().x, - ImGui::GetWindowPos().y+ImGui::GetCursorPos().y - ); - ImVec2 maxArea=ImVec2( - ImGui::GetWindowPos().x+ImGui::GetCursorPos().x+ImGui::GetContentRegionAvail().x, - ImGui::GetWindowPos().y+ImGui::GetCursorPos().y+ImGui::GetContentRegionAvail().y - ); - ImRect rect=ImRect(minArea,maxArea); - ImGuiStyle& style=ImGui::GetStyle(); - ImGui::ItemSize(ImVec2(4.0f,4.0f),style.FramePadding.y); - ImU32 lowColor=ImGui::GetColorU32(uiColors[GUI_COLOR_VOLMETER_LOW]); - float peakDecay=0.05f*60.0f*ImGui::GetIO().DeltaTime; - if (ImGui::ItemAdd(rect,ImGui::GetID("volMeter"))) { - ImGui::RenderFrame(rect.Min,rect.Max,ImGui::GetColorU32(ImGuiCol_FrameBg),true,style.FrameRounding); - for (int i=0; i<2; i++) { - peak[i]*=1.0-peakDecay; - if (peak[i]<0.0001) peak[i]=0.0; - for (int j=0; joscSize; j++) { - if (fabs(e->oscBuf[i][j])>peak[i]) { - peak[i]=fabs(e->oscBuf[i][j]); - } - } - float logPeak=(20*log10(peak[i])/36.0); - if (logPeak==NAN) logPeak=0.0; - if (logPeak<-1.0) logPeak=-1.0; - if (logPeak>0.0) { - isClipping=8; - logPeak=0.0; - } - logPeak+=1.0; - ImU32 highColor=ImGui::GetColorU32( - ImLerp(uiColors[GUI_COLOR_VOLMETER_LOW],uiColors[GUI_COLOR_VOLMETER_HIGH],logPeak) - ); - ImRect s; - if (aspectRatio) { - s=ImRect( - ImLerp(rect.Min,rect.Max,ImVec2(0,float(i)*0.5)), - ImLerp(rect.Min,rect.Max,ImVec2(logPeak,float(i+1)*0.5)) - ); - if (i==0) s.Max.y-=dpiScale; - if (isClipping) { - dl->AddRectFilled(s.Min,s.Max,ImGui::GetColorU32(uiColors[GUI_COLOR_VOLMETER_PEAK])); - } else { - dl->AddRectFilledMultiColor(s.Min,s.Max,lowColor,highColor,highColor,lowColor); - } - } else { - s=ImRect( - ImLerp(rect.Min,rect.Max,ImVec2(float(i)*0.5,1.0-logPeak)), - ImLerp(rect.Min,rect.Max,ImVec2(float(i+1)*0.5,1.0)) - ); - if (i==0) s.Max.x-=dpiScale; - if (isClipping) { - dl->AddRectFilled(s.Min,s.Max,ImGui::GetColorU32(uiColors[GUI_COLOR_VOLMETER_PEAK])); - } else { - dl->AddRectFilledMultiColor(s.Min,s.Max,highColor,highColor,lowColor,lowColor); - } - } - } - if (ImGui::IsItemHovered()) { - if (aspectRatio) { - ImGui::SetTooltip("%.1fdB",36*((ImGui::GetMousePos().x-ImGui::GetItemRectMin().x)/(rect.Max.x-rect.Min.x)-1.0)); - } else { - ImGui::SetTooltip("%.1fdB",-(36+36*((ImGui::GetMousePos().y-ImGui::GetItemRectMin().y)/(rect.Max.y-rect.Min.y)-1.0))); - } - } - } - } - ImGui::PopStyleVar(4); - if (ImGui::IsWindowFocused(ImGuiFocusedFlags_ChildWindows)) curWindow=GUI_WINDOW_VOL_METER; - ImGui::End(); -} - -const char* aboutLine[]={ - "tildearrow", - "is proud to present", - "", - ("Furnace " DIV_VERSION), - "", - "the free software chiptune tracker,", - "compatible with DefleMask modules.", - "", - "zero disassembly.", - "just clean-room design,", - "time and dedication.", - "", - "> CREDITS <", - "", - "-- program --", - "tildearrow", - "akumanatt", - "cam900", - "djtuBIG-MaliceX", - "laoo", - "superctr", - "", - "-- graphics/UI design --", - "tildearrow", - "BlastBrothers", - "", - "-- documentation --", - "tildearrow", - "freq-mod", - "nicco1690", - "DeMOSic", - "cam900", - "", - "-- demo songs --", - "0x5066", - "ActualNK358", - "breakthetargets", - "CaptainMalware", - "kleeder", - "Mahbod Karamoozian", - "nicco1690", - "NikonTeen", - "SuperJet Spade", - "TheDuccinator", - "TheRealHedgehogSonic", - "tildearrow", - "Ultraprogramer", - "", - "-- additional feedback/fixes --", - "fd", - "OPNA2608", - "plane", - "TheEssem", - "", - "powered by:", - "Dear ImGui by Omar Cornut", - "SDL2 by Sam Lantinga", - "zlib by Jean-loup Gailly", - "and Mark Adler", - "libsndfile by Erik de Castro Lopo", - "Nuked-OPM & Nuked-OPN2 by Nuke.YKT", - "ymfm by Aaron Giles", - "MAME SN76496 by Nicola Salmoria", - "MAME AY-3-8910 by Couriersud", - "with AY8930 fixes by Eulous", - "MAME SAA1099 by Juergen Buchmueller and Manuel Abadia", - "SAASound", - "SameBoy by Lior Halphon", - "Mednafen PCE", - "puNES by FHorse", - "reSID by Dag Lem", - "Stella by Stella Team", - "QSound emulator by Ian Karlsson and Valley Bell", - "", - "greetings to:", - "Delek", - "fd", - "ILLUMIDARO", - "all members of Deflers of Noice!", - "", - "copyright © 2021-2022 tildearrow", - "(and contributors).", - "licensed under GPLv2+! see", - "LICENSE for more information.", - "", - "help Furnace grow:", - "https://github.com/tildearrow/furnace", - "", - "contact tildearrow at:", - "https://tildearrow.org/?p=contact", - "", - "disclaimer:", - "despite the fact this program works", - "with the .dmf file format, it is NOT", - "affiliated with Delek or DefleMask in", - "any way, nor it is a replacement for", - "the original program.", - "", - "it also comes with ABSOLUTELY NO WARRANTY.", - "", - "thanks to all contributors/bug reporters!" -}; - -const size_t aboutCount = sizeof(aboutLine)/sizeof(aboutLine[0]); - -void FurnaceGUI::drawAbout() { - // do stuff - if (ImGui::Begin("About Furnace",NULL,ImGuiWindowFlags_Modal|ImGuiWindowFlags_NoMove|ImGuiWindowFlags_NoResize|ImGuiWindowFlags_NoDocking|ImGuiWindowFlags_NoTitleBar)) { - ImGui::SetWindowPos(ImVec2(0,0)); - ImGui::SetWindowSize(ImVec2(scrW*dpiScale,scrH*dpiScale)); - ImGui::PushFont(bigFont); - ImDrawList* dl=ImGui::GetWindowDrawList(); - float r=0; - float g=0; - float b=0; - float peakMix=settings.partyTime?((peak[0]+peak[1])*0.5):0.3; - ImGui::ColorConvertHSVtoRGB(aboutHue,1.0,0.25+MIN(0.75f,peakMix*0.75f),r,g,b); - dl->AddRectFilled(ImVec2(0,0),ImVec2(scrW*dpiScale,scrH*dpiScale),0xff000000); - bool skip=false; - bool skip2=false; - for (int i=(-80-sin(double(aboutSin)*2*M_PI/120.0)*80.0)*2; iAddRectFilled(ImVec2(i*dpiScale,j*dpiScale),ImVec2((i+160)*dpiScale,(j+160)*dpiScale),ImGui::GetColorU32(ImVec4(r*0.25,g*0.25,b*0.25,1.0))); - } - } - - skip=false; - skip2=false; - for (int i=(-80-cos(double(aboutSin)*2*M_PI/120.0)*80.0)*2; iAddRectFilled(ImVec2(i*dpiScale,j*dpiScale),ImVec2((i+160)*dpiScale,(j+160)*dpiScale),ImGui::GetColorU32(ImVec4(r*0.5,g*0.5,b*0.5,1.0))); - } - } - - skip=false; - skip2=false; - for (int i=(-160+fmod(aboutSin*2,160))*2; iAddRectFilled(ImVec2(i*dpiScale,j*dpiScale),ImVec2((i+160)*dpiScale,(j+160)*dpiScale),ImGui::GetColorU32(ImVec4(r*0.75,g*0.75,b*0.75,1.0))); - } - } - - for (size_t i=0; iscrH*dpiScale) continue; - dl->AddText(bigFont,bigFont->FontSize, - ImVec2(posX+dpiScale,posY+dpiScale), - 0xff000000,aboutLine[i]); - dl->AddText(bigFont,bigFont->FontSize, - ImVec2(posX+dpiScale,posY-dpiScale), - 0xff000000,aboutLine[i]); - dl->AddText(bigFont,bigFont->FontSize, - ImVec2(posX-dpiScale,posY+dpiScale), - 0xff000000,aboutLine[i]); - dl->AddText(bigFont,bigFont->FontSize, - ImVec2(posX-dpiScale,posY-dpiScale), - 0xff000000,aboutLine[i]); - dl->AddText(bigFont,bigFont->FontSize, - ImVec2(posX,posY), - 0xffffffff,aboutLine[i]); - } - ImGui::PopFont(); - - float timeScale=60.0f*ImGui::GetIO().DeltaTime; - - aboutHue+=(0.001+peakMix*0.004)*timeScale; - aboutScroll+=(2+(peakMix>0.78)*3)*timeScale; - aboutSin+=(1+(peakMix>0.75)*2)*timeScale; - - while (aboutHue>1) aboutHue--; - while (aboutSin>=2400) aboutSin-=2400; - if (aboutScroll>(42*aboutCount+scrH)) aboutScroll=-20; - } - if (ImGui::IsWindowFocused(ImGuiFocusedFlags_ChildWindows)) curWindow=GUI_WINDOW_ABOUT; - ImGui::End(); -} - -void FurnaceGUI::drawDebug() { - static int bpOrder; - static int bpRow; - static int bpTick; - static bool bpOn; - if (nextWindow==GUI_WINDOW_DEBUG) { - debugOpen=true; - ImGui::SetNextWindowFocus(); - nextWindow=GUI_WINDOW_NOTHING; - } - if (!debugOpen) return; - ImGui::SetNextWindowSizeConstraints(ImVec2(400.0f*dpiScale,200.0f*dpiScale),ImVec2(scrW*dpiScale,scrH*dpiScale)); - if (ImGui::Begin("Debug",&debugOpen,ImGuiWindowFlags_NoDocking)) { - ImGui::Text("NOTE: use with caution."); - if (ImGui::TreeNode("Debug Controls")) { - if (e->isHalted()) { - if (ImGui::Button("Resume")) e->resume(); - } else { - if (ImGui::Button("Pause")) e->halt(); - } - ImGui::SameLine(); - if (ImGui::Button("Frame Advance")) e->haltWhen(DIV_HALT_TICK); - ImGui::SameLine(); - if (ImGui::Button("Row Advance")) e->haltWhen(DIV_HALT_ROW); - ImGui::SameLine(); - if (ImGui::Button("Pattern Advance")) e->haltWhen(DIV_HALT_PATTERN); - - if (ImGui::Button("Panic")) e->syncReset(); - ImGui::SameLine(); - if (ImGui::Button("Abort")) { - abort(); - } - ImGui::TreePop(); - } - if (ImGui::TreeNode("Breakpoint")) { - ImGui::InputInt("Order",&bpOrder); - ImGui::InputInt("Row",&bpRow); - ImGui::InputInt("Tick",&bpTick); - ImGui::Checkbox("Enable",&bpOn); - ImGui::TreePop(); - } - if (ImGui::TreeNode("Dispatch Status")) { - ImGui::Text("for best results set latency to minimum or use the Frame Advance button."); - ImGui::Columns(e->getTotalChannelCount()); - for (int i=0; igetTotalChannelCount(); i++) { - void* ch=e->getDispatchChanState(i); - ImGui::TextColored(uiColors[GUI_COLOR_ACCENT_PRIMARY],"Ch. %d: %d, %d",i,e->dispatchOfChan[i],e->dispatchChanOfChan[i]); - if (ch==NULL) { - ImGui::Text("NULL"); - } else { - putDispatchChan(ch,e->dispatchChanOfChan[i],e->sysOfChan[i]); - } - ImGui::NextColumn(); - } - ImGui::Columns(); - ImGui::TreePop(); - } - if (ImGui::TreeNode("Playback Status")) { - ImGui::Text("for best results set latency to minimum or use the Frame Advance button."); - ImGui::Columns(e->getTotalChannelCount()); - for (int i=0; igetTotalChannelCount(); i++) { - DivChannelState* ch=e->getChanState(i); - ImGui::TextColored(uiColors[GUI_COLOR_ACCENT_PRIMARY],"Channel %d:",i); - if (ch==NULL) { - ImGui::Text("NULL"); - } else { - ImGui::Text("* General:"); - ImGui::Text("- note = %d",ch->note); - ImGui::Text("- oldNote = %d",ch->oldNote); - ImGui::Text("- pitch = %d",ch->pitch); - ImGui::Text("- portaSpeed = %d",ch->portaSpeed); - ImGui::Text("- portaNote = %d",ch->portaNote); - ImGui::Text("- volume = %.4x",ch->volume); - ImGui::Text("- volSpeed = %d",ch->volSpeed); - ImGui::Text("- cut = %d",ch->cut); - ImGui::Text("- rowDelay = %d",ch->rowDelay); - ImGui::Text("- volMax = %.4x",ch->volMax); - ImGui::Text("- delayOrder = %d",ch->delayOrder); - ImGui::Text("- delayRow = %d",ch->delayRow); - ImGui::Text("- retrigSpeed = %d",ch->retrigSpeed); - ImGui::Text("- retrigTick = %d",ch->retrigTick); - ImGui::PushStyleColor(ImGuiCol_Text,(ch->vibratoDepth>0)?uiColors[GUI_COLOR_MACRO_VOLUME]:uiColors[GUI_COLOR_TEXT]); - ImGui::Text("* Vibrato:"); - ImGui::Text("- depth = %d",ch->vibratoDepth); - ImGui::Text("- rate = %d",ch->vibratoRate); - ImGui::Text("- pos = %d",ch->vibratoPos); - ImGui::Text("- dir = %d",ch->vibratoDir); - ImGui::Text("- fine = %d",ch->vibratoFine); - ImGui::PopStyleColor(); - ImGui::PushStyleColor(ImGuiCol_Text,(ch->tremoloDepth>0)?uiColors[GUI_COLOR_MACRO_VOLUME]:uiColors[GUI_COLOR_TEXT]); - ImGui::Text("* Tremolo:"); - ImGui::Text("- depth = %d",ch->tremoloDepth); - ImGui::Text("- rate = %d",ch->tremoloRate); - ImGui::Text("- pos = %d",ch->tremoloPos); - ImGui::PopStyleColor(); - ImGui::PushStyleColor(ImGuiCol_Text,(ch->arp>0)?uiColors[GUI_COLOR_MACRO_VOLUME]:uiColors[GUI_COLOR_TEXT]); - ImGui::Text("* Arpeggio:"); - ImGui::Text("- arp = %.2X",ch->arp); - ImGui::Text("- stage = %d",ch->arpStage); - ImGui::Text("- ticks = %d",ch->arpTicks); - ImGui::PopStyleColor(); - ImGui::Text("* Miscellaneous:"); - ImGui::TextColored(ch->doNote?uiColors[GUI_COLOR_MACRO_VOLUME]:uiColors[GUI_COLOR_HEADER],">> Do Note"); - ImGui::TextColored(ch->legato?uiColors[GUI_COLOR_MACRO_VOLUME]:uiColors[GUI_COLOR_HEADER],">> Legato"); - ImGui::TextColored(ch->portaStop?uiColors[GUI_COLOR_MACRO_VOLUME]:uiColors[GUI_COLOR_HEADER],">> PortaStop"); - ImGui::TextColored(ch->keyOn?uiColors[GUI_COLOR_MACRO_VOLUME]:uiColors[GUI_COLOR_HEADER],">> Key On"); - ImGui::TextColored(ch->keyOff?uiColors[GUI_COLOR_MACRO_VOLUME]:uiColors[GUI_COLOR_HEADER],">> Key Off"); - ImGui::TextColored(ch->nowYouCanStop?uiColors[GUI_COLOR_MACRO_VOLUME]:uiColors[GUI_COLOR_HEADER],">> NowYouCanStop"); - ImGui::TextColored(ch->stopOnOff?uiColors[GUI_COLOR_MACRO_VOLUME]:uiColors[GUI_COLOR_HEADER],">> Stop on Off"); - ImGui::TextColored(ch->arpYield?uiColors[GUI_COLOR_MACRO_VOLUME]:uiColors[GUI_COLOR_HEADER],">> Arp Yield"); - ImGui::TextColored(ch->delayLocked?uiColors[GUI_COLOR_MACRO_VOLUME]:uiColors[GUI_COLOR_HEADER],">> DelayLocked"); - ImGui::TextColored(ch->inPorta?uiColors[GUI_COLOR_MACRO_VOLUME]:uiColors[GUI_COLOR_HEADER],">> InPorta"); - ImGui::TextColored(ch->scheduledSlideReset?uiColors[GUI_COLOR_MACRO_VOLUME]:uiColors[GUI_COLOR_HEADER],">> SchedSlide"); - } - ImGui::NextColumn(); - } - ImGui::Columns(); - ImGui::TreePop(); - } - if (ImGui::TreeNode("Playground")) { - if (pgSys<0 || pgSys>=e->song.systemLen) pgSys=0; - if (ImGui::BeginCombo("System",fmt::sprintf("%d. %s",pgSys+1,e->getSystemName(e->song.system[pgSys])).c_str())) { - for (int i=0; isong.systemLen; i++) { - if (ImGui::Selectable(fmt::sprintf("%d. %s",i+1,e->getSystemName(e->song.system[i])).c_str())) { - pgSys=i; - break; - } - } - ImGui::EndCombo(); - } - ImGui::Text("Program"); - if (pgProgram.empty()) { - ImGui::Text("-nothing here-"); - } else { - char id[32]; - for (size_t index=0; indexpoke(pgSys,pgProgram); - } - ImGui::SameLine(); - if (ImGui::Button("Clear")) { - pgProgram.clear(); - } - - ImGui::Text("Address"); - ImGui::SameLine(); - ImGui::SetNextItemWidth(100.0f*dpiScale); - ImGui::InputInt("##PAddress",&pgAddr,0,0,ImGuiInputTextFlags_CharsHexadecimal); - ImGui::SameLine(); - ImGui::Text("Value"); - ImGui::SameLine(); - ImGui::SetNextItemWidth(100.0f*dpiScale); - ImGui::InputInt("##PValue",&pgVal,0,0,ImGuiInputTextFlags_CharsHexadecimal); - ImGui::SameLine(); - if (ImGui::Button("Write")) { - e->poke(pgSys,pgAddr,pgVal); - } - ImGui::SameLine(); - if (ImGui::Button("Add")) { - pgProgram.push_back(DivRegWrite(pgAddr,pgVal)); - } - if (ImGui::TreeNode("Register Cheatsheet")) { - const char** sheet=e->getRegisterSheet(pgSys); - if (sheet==NULL) { - ImGui::Text("no cheatsheet available for this system."); - } else { - if (ImGui::BeginTable("RegisterSheet",2,ImGuiTableFlags_SizingFixedSame)) { - ImGui::TableNextRow(); - ImGui::TableNextColumn(); - ImGui::Text("Name"); - ImGui::TableNextColumn(); - ImGui::Text("Address"); - for (int i=0; sheet[i]!=NULL; i+=2) { - ImGui::TableNextRow(); - ImGui::TableNextColumn(); - ImGui::Text("%s",sheet[i]); - ImGui::TableNextColumn(); - ImGui::Text("$%s",sheet[i+1]); - } - ImGui::EndTable(); - } - } - ImGui::TreePop(); - } - ImGui::TreePop(); - } - if (ImGui::TreeNode("User Interface")) { - if (ImGui::Button("Inspect")) { - inspectorOpen=!inspectorOpen; - } - ImGui::TreePop(); - } - if (ImGui::TreeNode("Settings")) { - if (ImGui::Button("Sync")) syncSettings(); - ImGui::SameLine(); - if (ImGui::Button("Commit")) commitSettings(); - ImGui::SameLine(); - if (ImGui::Button("Force Load")) e->loadConf(); - ImGui::SameLine(); - if (ImGui::Button("Force Save")) e->saveConf(); - ImGui::TreePop(); - } - ImGui::Text("Song format version %d",e->song.version); - ImGui::Text("Furnace version " DIV_VERSION " (%d)",DIV_ENGINE_VERSION); - } - if (ImGui::IsWindowFocused(ImGuiFocusedFlags_ChildWindows)) curWindow=GUI_WINDOW_DEBUG; - ImGui::End(); -} - -void FurnaceGUI::drawNewSong() { - bool accepted=false; - - ImGui::PushFont(bigFont); - ImGui::SetCursorPosX((ImGui::GetContentRegionAvail().x-ImGui::CalcTextSize("Choose a System!").x)*0.5); - ImGui::Text("Choose a System!"); - ImGui::PopFont(); - - if (ImGui::BeginTable("sysPicker",2)) { - ImGui::TableSetupColumn("c0",ImGuiTableColumnFlags_WidthFixed,0.0f); - ImGui::TableSetupColumn("c1",ImGuiTableColumnFlags_WidthStretch,0.0f); - - ImGui::TableNextRow(ImGuiTableRowFlags_Headers); - ImGui::TableNextColumn(); - ImGui::Text("Categories"); - ImGui::TableNextColumn(); - ImGui::Text("Systems"); - - ImGui::TableNextRow(); - - // CATEGORIES - ImGui::TableNextColumn(); - int index=0; - for (FurnaceGUISysCategory& i: sysCategories) { - if (ImGui::Selectable(i.name,newSongCategory==index,ImGuiSelectableFlags_DontClosePopups)) { \ - newSongCategory=index; - } - index++; - } - - // SYSTEMS - ImGui::TableNextColumn(); - if (ImGui::BeginTable("Systems",1,ImGuiTableFlags_BordersInnerV|ImGuiTableFlags_ScrollY)) { - for (FurnaceGUISysDef& i: sysCategories[newSongCategory].systems) { - ImGui::TableNextRow(); - ImGui::TableNextColumn(); - if (ImGui::Selectable(i.name,false,ImGuiSelectableFlags_DontClosePopups)) { - nextDesc=i.definition.data(); - accepted=true; - } - } - ImGui::EndTable(); - } - - ImGui::EndTable(); - } - - if (ImGui::Button("Cancel")) { - ImGui::CloseCurrentPopup(); - } - - if (accepted) { - e->createNew(nextDesc); - undoHist.clear(); - redoHist.clear(); - curFileName=""; - modified=false; - curNibble=false; - orderNibble=false; - orderCursor=-1; - samplePos=0; - updateSampleTex=true; - selStart=SelectionPoint(); - selEnd=SelectionPoint(); - cursor=SelectionPoint(); - updateWindowTitle(); - ImGui::CloseCurrentPopup(); - } -} - -void FurnaceGUI::drawStats() { - if (nextWindow==GUI_WINDOW_STATS) { - statsOpen=true; - ImGui::SetNextWindowFocus(); - nextWindow=GUI_WINDOW_NOTHING; - } - if (!statsOpen) return; - if (ImGui::Begin("Statistics",&statsOpen)) { - String adpcmAUsage=fmt::sprintf("%d/16384KB",e->adpcmAMemLen/1024); - String adpcmBUsage=fmt::sprintf("%d/16384KB",e->adpcmBMemLen/1024); - String qsoundUsage=fmt::sprintf("%d/16384KB",e->qsoundMemLen/1024); - String x1_010Usage=fmt::sprintf("%d/1024KB",e->x1_010MemLen/1024); - ImGui::Text("ADPCM-A"); - ImGui::SameLine(); - ImGui::ProgressBar(((float)e->adpcmAMemLen)/16777216.0f,ImVec2(-FLT_MIN,0),adpcmAUsage.c_str()); - ImGui::Text("ADPCM-B"); - ImGui::SameLine(); - ImGui::ProgressBar(((float)e->adpcmBMemLen)/16777216.0f,ImVec2(-FLT_MIN,0),adpcmBUsage.c_str()); - ImGui::Text("QSound"); - ImGui::SameLine(); - ImGui::ProgressBar(((float)e->qsoundMemLen)/16777216.0f,ImVec2(-FLT_MIN,0),qsoundUsage.c_str()); - ImGui::Text("X1-010"); - ImGui::SameLine(); - ImGui::ProgressBar(((float)e->x1_010MemLen)/1048576.0f,ImVec2(-FLT_MIN,0),x1_010Usage.c_str()); - } - if (ImGui::IsWindowFocused(ImGuiFocusedFlags_ChildWindows)) curWindow=GUI_WINDOW_STATS; - ImGui::End(); -} - -void FurnaceGUI::drawCompatFlags() { - if (nextWindow==GUI_WINDOW_COMPAT_FLAGS) { - compatFlagsOpen=true; - ImGui::SetNextWindowFocus(); - nextWindow=GUI_WINDOW_NOTHING; - } - if (!compatFlagsOpen) return; - if (ImGui::Begin("Compatibility Flags",&compatFlagsOpen)) { - ImGui::TextWrapped("these flags are designed to provide better DefleMask/older Furnace compatibility."); - ImGui::Checkbox("Limit slide range",&e->song.limitSlides); - if (ImGui::IsItemHovered()) { - ImGui::SetTooltip("when enabled, slides are limited to a compatible range.\nmay cause problems with slides in negative octaves."); - } - ImGui::Checkbox("Linear pitch control",&e->song.linearPitch); - if (ImGui::IsItemHovered()) { - ImGui::SetTooltip("linear pitch:\n- slides work in frequency/period space\n- E5xx and 04xx effects work in tonality space\nnon-linear pitch:\n- slides work in frequency/period space\n- E5xx and 04xx effects work on frequency/period space"); - } - ImGui::Checkbox("Proper noise layout on NES and PC Engine",&e->song.properNoiseLayout); - if (ImGui::IsItemHovered()) { - ImGui::SetTooltip("use a proper noise channel note mapping (0-15) instead of a rather unusual compatible one.\nunlocks all noise frequencies on PC Engine."); - } - ImGui::Checkbox("Game Boy instrument duty is wave volume",&e->song.waveDutyIsVol); - if (ImGui::IsItemHovered()) { - ImGui::SetTooltip("if enabled, an instrument with duty macro in the wave channel will be mapped to wavetable volume."); - } - - ImGui::Checkbox("Restart macro on portamento",&e->song.resetMacroOnPorta); - if (ImGui::IsItemHovered()) { - ImGui::SetTooltip("when enabled, a portamento effect will reset the channel's macro if used in combination with a note."); - } - ImGui::Checkbox("Legacy volume slides",&e->song.legacyVolumeSlides); - if (ImGui::IsItemHovered()) { - ImGui::SetTooltip("simulate glitchy volume slide behavior by silently overflowing the volume when the slide goes below 0."); - } - ImGui::Checkbox("Compatible arpeggio",&e->song.compatibleArpeggio); - if (ImGui::IsItemHovered()) { - ImGui::SetTooltip("delay arpeggio by one tick on every new note."); - } - ImGui::Checkbox("Reset slides after note off",&e->song.noteOffResetsSlides); - if (ImGui::IsItemHovered()) { - ImGui::SetTooltip("when enabled, note off will reset the channel's slide effect."); - } - ImGui::Checkbox("Reset portamento after reaching target",&e->song.targetResetsSlides); - if (ImGui::IsItemHovered()) { - ImGui::SetTooltip("when enabled, the slide effect is disabled after it reaches its target."); - } - ImGui::Checkbox("Ignore duplicate slide effects",&e->song.ignoreDuplicateSlides); - if (ImGui::IsItemHovered()) { - ImGui::SetTooltip("if this is on, only the first slide of a row in a channel will be considered."); - } - ImGui::Checkbox("Continuous vibrato",&e->song.continuousVibrato); - if (ImGui::IsItemHovered()) { - ImGui::SetTooltip("when enabled, vibrato will not be reset on a new note."); - } - ImGui::Checkbox("Broken DAC mode",&e->song.brokenDACMode); - if (ImGui::IsItemHovered()) { - ImGui::SetTooltip("when enabled, the DAC in YM2612 will be disabled if there isn't any sample playing."); - } - ImGui::Checkbox("Auto-insert one tick gap between notes",&e->song.oneTickCut); - if (ImGui::IsItemHovered()) { - ImGui::SetTooltip("when enabled, a one-tick note cut will be inserted between non-legato/non-portamento notes.\nthis simulates the behavior of some Amiga/SNES music engines."); - } - - ImGui::Text("Loop modality:"); - if (ImGui::RadioButton("Reset channels",e->song.loopModality==0)) { - e->song.loopModality=0; - } - if (ImGui::IsItemHovered()) { - ImGui::SetTooltip("select to reset channels on loop. may trigger a voltage click on every loop!"); - } - if (ImGui::RadioButton("Soft reset channels",e->song.loopModality==1)) { - e->song.loopModality=1; - } - if (ImGui::IsItemHovered()) { - ImGui::SetTooltip("select to turn channels off on loop."); - } - if (ImGui::RadioButton("Do nothing",e->song.loopModality==2)) { - e->song.loopModality=2; - } - if (ImGui::IsItemHovered()) { - ImGui::SetTooltip("select to not reset channels on loop."); - } - - ImGui::Separator(); - - ImGui::TextWrapped("the following flags are for compatibility with older Furnace versions."); - - ImGui::Checkbox("Arpeggio inhibits non-porta slides",&e->song.arpNonPorta); - if (ImGui::IsItemHovered()) { - ImGui::SetTooltip("behavior changed in 0.5.5"); - } - ImGui::Checkbox("Wack FM algorithm macro",&e->song.algMacroBehavior); - if (ImGui::IsItemHovered()) { - ImGui::SetTooltip("behavior changed in 0.5.5"); - } - ImGui::Checkbox("Broken shortcut slides (E1xy/E2xy)",&e->song.brokenShortcutSlides); - if (ImGui::IsItemHovered()) { - ImGui::SetTooltip("behavior changed in 0.5.7"); - } - ImGui::Checkbox("Stop portamento on note off",&e->song.stopPortaOnNoteOff); - if (ImGui::IsItemHovered()) { - ImGui::SetTooltip("behavior changed in 0.6"); - } - ImGui::Checkbox("Allow instrument change during slides",&e->song.newInsTriggersInPorta); - if (ImGui::IsItemHovered()) { - ImGui::SetTooltip("behavior changed in 0.6"); - } - ImGui::Checkbox("Reset note to base on arpeggio stop",&e->song.arp0Reset); - if (ImGui::IsItemHovered()) { - ImGui::SetTooltip("behavior changed in 0.6"); - } - } - if (ImGui::IsWindowFocused(ImGuiFocusedFlags_ChildWindows)) curWindow=GUI_WINDOW_COMPAT_FLAGS; - ImGui::End(); -} - -void FurnaceGUI::drawPiano() { - if (nextWindow==GUI_WINDOW_PIANO) { - pianoOpen=true; - ImGui::SetNextWindowFocus(); - nextWindow=GUI_WINDOW_NOTHING; - } - if (!pianoOpen) return; - if (ImGui::Begin("Piano",&pianoOpen)) { - for (int i=0; igetTotalChannelCount(); i++) { - DivChannelState* cs=e->getChanState(i); - if (cs->keyOn) { - const char* noteName=NULL; - if (cs->note<-60 || cs->note>120) { - noteName="???"; - } else { - noteName=noteNames[cs->note+60]; - } - ImGui::Text("%d: %s",i,noteName); - } - } - } - if (ImGui::IsWindowFocused(ImGuiFocusedFlags_ChildWindows)) curWindow=GUI_WINDOW_PIANO; - ImGui::End(); -} - -// NOTE: please don't ask me to enable text wrap. -// Dear ImGui doesn't have that feature. D: -void FurnaceGUI::drawNotes() { - if (nextWindow==GUI_WINDOW_NOTES) { - notesOpen=true; - ImGui::SetNextWindowFocus(); - nextWindow=GUI_WINDOW_NOTHING; - } - if (!notesOpen) return; - if (ImGui::Begin("Song Comments",¬esOpen)) { - ImGui::InputTextMultiline("##SongNotes",&e->song.notes,ImGui::GetContentRegionAvail()); - } - if (ImGui::IsWindowFocused(ImGuiFocusedFlags_ChildWindows)) curWindow=GUI_WINDOW_NOTES; - ImGui::End(); -} - -void FurnaceGUI::drawChannels() { - if (nextWindow==GUI_WINDOW_CHANNELS) { - channelsOpen=true; - ImGui::SetNextWindowFocus(); - nextWindow=GUI_WINDOW_NOTHING; - } - if (!channelsOpen) return; - if (ImGui::Begin("Channels",&channelsOpen)) { - if (ImGui::BeginTable("ChannelList",3)) { - ImGui::TableSetupColumn("c0",ImGuiTableColumnFlags_WidthFixed,0.0); - ImGui::TableSetupColumn("c1",ImGuiTableColumnFlags_WidthStretch,0.0); - ImGui::TableSetupColumn("c2",ImGuiTableColumnFlags_WidthFixed,48.0f*dpiScale); - for (int i=0; igetTotalChannelCount(); i++) { - ImGui::PushID(i); - ImGui::TableNextRow(); - ImGui::TableNextColumn(); - ImGui::Checkbox("##Visible",&e->song.chanShow[i]); - ImGui::TableNextColumn(); - ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x); - ImGui::InputTextWithHint("##ChanName",e->getChannelName(i),&e->song.chanName[i]); - ImGui::TableNextColumn(); - ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x); - ImGui::InputTextWithHint("##ChanShortName",e->getChannelShortName(i),&e->song.chanShortName[i]); - ImGui::PopID(); - } - ImGui::EndTable(); - } - } - if (ImGui::IsWindowFocused(ImGuiFocusedFlags_ChildWindows)) curWindow=GUI_WINDOW_CHANNELS; - ImGui::End(); -} - -void FurnaceGUI::drawRegView() { - if (nextWindow==GUI_WINDOW_REGISTER_VIEW) { - channelsOpen=true; - ImGui::SetNextWindowFocus(); - nextWindow=GUI_WINDOW_NOTHING; - } - if (!regViewOpen) return; - if (ImGui::Begin("Register View",®ViewOpen)) { - for (int i=0; isong.systemLen; i++) { - ImGui::Text("%d. %s",i+1,getSystemName(e->song.system[i])); - int size=0; - int depth=8; - unsigned char* regPool=e->getRegisterPool(i,size,depth); - unsigned short* regPoolW=(unsigned short*)regPool; - if (regPool==NULL) { - ImGui::Text("- no register pool available"); - } else { - ImGui::PushFont(patFont); - if (ImGui::BeginTable("Memory",17)) { - ImGui::TableNextRow(); - ImGui::TableNextColumn(); - for (int i=0; i<16; i++) { - ImGui::TableNextColumn(); - ImGui::TextColored(uiColors[GUI_COLOR_PATTERN_ROW_INDEX]," %X",i); - } - for (int i=0; i<=((size-1)>>4); i++) { - ImGui::TableNextRow(); - ImGui::TableNextColumn(); - ImGui::TextColored(uiColors[GUI_COLOR_PATTERN_ROW_INDEX],"%.2X",i*16); - for (int j=0; j<16; j++) { - ImGui::TableNextColumn(); - if (i*16+j>=size) continue; - if (depth == 8) { - ImGui::Text("%.2x",regPool[i*16+j]); - } else if (depth == 16) { - ImGui::Text("%.4x",regPoolW[i*16+j]); - } else { - ImGui::Text("??"); - } - } - } - ImGui::EndTable(); - } - ImGui::PopFont(); - } - } - } - if (ImGui::IsWindowFocused(ImGuiFocusedFlags_ChildWindows)) curWindow=GUI_WINDOW_REGISTER_VIEW; - ImGui::End(); -} - -void FurnaceGUI::startSelection(int xCoarse, int xFine, int y) { - if (xCoarse!=selStart.xCoarse || xFine!=selStart.xFine || y!=selStart.y) { - curNibble=false; - } - cursor.xCoarse=xCoarse; - cursor.xFine=xFine; - cursor.y=y; - selStart.xCoarse=xCoarse; - selStart.xFine=xFine; - selStart.y=y; - selEnd.xCoarse=xCoarse; - selEnd.xFine=xFine; - selEnd.y=y; - selecting=true; -} - -void FurnaceGUI::updateSelection(int xCoarse, int xFine, int y) { - if (!selecting) return; - selEnd.xCoarse=xCoarse; - selEnd.xFine=xFine; - selEnd.y=y; -} - -void FurnaceGUI::finishSelection() { - // swap points if needed - if (selEnd.ygetTotalChannelCount(); - - if (selStart.xCoarse<0) selStart.xCoarse=0; - if (selStart.xCoarse>=chanCount) selStart.xCoarse=chanCount-1; - if (selStart.y<0) selStart.y=0; - if (selStart.y>=e->song.patLen) selStart.y=e->song.patLen-1; - if (selEnd.xCoarse<0) selEnd.xCoarse=0; - if (selEnd.xCoarse>=chanCount) selEnd.xCoarse=chanCount-1; - if (selEnd.y<0) selEnd.y=0; - if (selEnd.y>=e->song.patLen) selEnd.y=e->song.patLen-1; - if (cursor.xCoarse<0) cursor.xCoarse=0; - if (cursor.xCoarse>=chanCount) cursor.xCoarse=chanCount-1; - if (cursor.y<0) cursor.y=0; - if (cursor.y>=e->song.patLen) cursor.y=e->song.patLen-1; - - if (e->song.chanCollapse[selEnd.xCoarse]) { - selStart.xFine=0; - } - if (e->song.chanCollapse[selEnd.xCoarse]) { - selEnd.xFine=2+e->song.pat[cursor.xCoarse].effectRows*2; - } -} - -#define DETERMINE_FIRST \ - int firstChannel=0; \ - for (int i=0; igetTotalChannelCount(); i++) { \ - if (e->song.chanShow[i]) { \ - firstChannel=i; \ - break; \ - } \ - } \ - -#define DETERMINE_LAST \ - int lastChannel=0; \ - for (int i=e->getTotalChannelCount()-1; i>=0; i--) { \ - if (e->song.chanShow[i]) { \ - lastChannel=i+1; \ - break; \ - } \ - } - -#define DETERMINE_FIRST_LAST \ - DETERMINE_FIRST \ - DETERMINE_LAST - -void FurnaceGUI::moveCursor(int x, int y, bool select) { - if (!select) { - finishSelection(); - } - - DETERMINE_FIRST_LAST; - - curNibble=false; - if (x!=0) { - demandScrollX=true; - if (x>0) { - for (int i=0; i=(e->song.chanCollapse[cursor.xCoarse]?1:(3+e->song.pat[cursor.xCoarse].effectRows*2))) { - cursor.xFine=0; - if (++cursor.xCoarse>=lastChannel) { - if (settings.wrapHorizontal!=0 && !select) { - cursor.xCoarse=firstChannel; - if (settings.wrapHorizontal==2) y++; - } else { - cursor.xCoarse=lastChannel-1; - cursor.xFine=e->song.chanCollapse[cursor.xCoarse]?0:(2+e->song.pat[cursor.xCoarse].effectRows*2); - } - } else { - while (!e->song.chanShow[cursor.xCoarse]) { - cursor.xCoarse++; - if (cursor.xCoarse>=e->getTotalChannelCount()) break; - } - } - } - } - } else { - for (int i=0; i<-x; i++) { - if (--cursor.xFine<0) { - if (--cursor.xCoarsesong.pat[cursor.xCoarse].effectRows*2; - if (settings.wrapHorizontal==2) y--; - } else { - cursor.xCoarse=firstChannel; - cursor.xFine=0; - } - } else { - while (!e->song.chanShow[cursor.xCoarse]) { - cursor.xCoarse--; - if (cursor.xCoarse<0) break; - } - if (e->song.chanCollapse[cursor.xCoarse]) { - cursor.xFine=0; - } else { - cursor.xFine=2+e->song.pat[cursor.xCoarse].effectRows*2; - } - } - } - } - } - } - if (y!=0) { - if (y>0) { - for (int i=0; i=e->song.patLen) { - if (settings.wrapVertical!=0 && !select) { - cursor.y=0; - if (settings.wrapVertical==2) { - if (!e->isPlaying() && e->getOrder()<(e->song.ordersLen-1)) { - e->setOrder(e->getOrder()+1); - } else { - cursor.y=e->song.patLen-1; - } - } - } else { - cursor.y=e->song.patLen-1; - } - } - } - } else { - for (int i=0; i<-y; i++) { - cursor.y--; - if (cursor.y<0) { - if (settings.wrapVertical!=0 && !select) { - cursor.y=e->song.patLen-1; - if (settings.wrapVertical==2) { - if (!e->isPlaying() && e->getOrder()>0) { - e->setOrder(e->getOrder()-1); - } else { - cursor.y=0; - } - } - } else { - cursor.y=0; - } - } - } - } - } - if (!select) { - selStart=cursor; - } - selEnd=cursor; - updateScroll(cursor.y); -} - -void FurnaceGUI::moveCursorPrevChannel(bool overflow) { - finishSelection(); - curNibble=false; - - DETERMINE_FIRST_LAST; - - do { - cursor.xCoarse--; - if (cursor.xCoarse<0) break; - } while (!e->song.chanShow[cursor.xCoarse]); - if (cursor.xCoarse=e->getTotalChannelCount()) break; - } while (!e->song.chanShow[cursor.xCoarse]); - if (cursor.xCoarse>=lastChannel) { - if (overflow) { - cursor.xCoarse=firstChannel; - } else { - cursor.xCoarse=lastChannel-1; - } - } - - selStart=cursor; - selEnd=cursor; - demandScrollX=true; -} - -void FurnaceGUI::moveCursorTop(bool select) { - finishSelection(); - curNibble=false; - if (cursor.y==0) { - DETERMINE_FIRST; - cursor.xCoarse=firstChannel; - cursor.xFine=0; - demandScrollX=true; - } else { - cursor.y=0; - } - selStart=cursor; - if (!select) { - selEnd=cursor; - } - updateScroll(cursor.y); -} - -void FurnaceGUI::moveCursorBottom(bool select) { - finishSelection(); - curNibble=false; - if (cursor.y==e->song.patLen-1) { - DETERMINE_LAST; - cursor.xCoarse=lastChannel-1; - cursor.xFine=2+e->song.pat[cursor.xCoarse].effectRows*2; - demandScrollX=true; - } else { - cursor.y=e->song.patLen-1; - } - if (!select) { - selStart=cursor; - } - selEnd=cursor; - updateScroll(cursor.y); -} - -void FurnaceGUI::editAdvance() { - finishSelection(); - cursor.y+=editStep; - if (cursor.y>=e->song.patLen) cursor.y=e->song.patLen-1; - selStart=cursor; - selEnd=cursor; - updateScroll(cursor.y); -} - -void FurnaceGUI::prepareUndo(ActionType action) { - int order=e->getOrder(); - switch (action) { - case GUI_UNDO_CHANGE_ORDER: - oldOrders=e->song.orders; - oldOrdersLen=e->song.ordersLen; - break; - case GUI_UNDO_PATTERN_EDIT: - case GUI_UNDO_PATTERN_DELETE: - case GUI_UNDO_PATTERN_PULL: - case GUI_UNDO_PATTERN_PUSH: - case GUI_UNDO_PATTERN_CUT: - case GUI_UNDO_PATTERN_PASTE: - case GUI_UNDO_PATTERN_CHANGE_INS: - case GUI_UNDO_PATTERN_INTERPOLATE: - case GUI_UNDO_PATTERN_FADE: - case GUI_UNDO_PATTERN_SCALE: - case GUI_UNDO_PATTERN_RANDOMIZE: - case GUI_UNDO_PATTERN_INVERT_VAL: - case GUI_UNDO_PATTERN_FLIP: - case GUI_UNDO_PATTERN_COLLAPSE: - case GUI_UNDO_PATTERN_EXPAND: - for (int i=0; igetTotalChannelCount(); i++) { - e->song.pat[i].getPattern(e->song.orders.ord[i][order],false)->copyOn(oldPat[i]); - } - break; - } -} - -void FurnaceGUI::makeUndo(ActionType action) { - bool doPush=false; - UndoStep s; - s.type=action; - s.cursor=cursor; - s.selStart=selStart; - s.selEnd=selEnd; - int order=e->getOrder(); - s.order=order; - s.nibble=curNibble; - switch (action) { - case GUI_UNDO_CHANGE_ORDER: - for (int i=0; isong.orders.ord[i][j]) { - s.ord.push_back(UndoOrderData(i,j,oldOrders.ord[i][j],e->song.orders.ord[i][j])); - } - } - } - s.oldOrdersLen=oldOrdersLen; - s.newOrdersLen=e->song.ordersLen; - if (oldOrdersLen!=e->song.ordersLen) { - doPush=true; - } - if (!s.ord.empty()) { - doPush=true; - } - break; - case GUI_UNDO_PATTERN_EDIT: - case GUI_UNDO_PATTERN_DELETE: - case GUI_UNDO_PATTERN_PULL: - case GUI_UNDO_PATTERN_PUSH: - case GUI_UNDO_PATTERN_CUT: - case GUI_UNDO_PATTERN_PASTE: - case GUI_UNDO_PATTERN_CHANGE_INS: - case GUI_UNDO_PATTERN_INTERPOLATE: - case GUI_UNDO_PATTERN_FADE: - case GUI_UNDO_PATTERN_SCALE: - case GUI_UNDO_PATTERN_RANDOMIZE: - case GUI_UNDO_PATTERN_INVERT_VAL: - case GUI_UNDO_PATTERN_FLIP: - case GUI_UNDO_PATTERN_COLLAPSE: - case GUI_UNDO_PATTERN_EXPAND: - for (int i=0; igetTotalChannelCount(); i++) { - DivPattern* p=e->song.pat[i].getPattern(e->song.orders.ord[i][order],false); - for (int j=0; jsong.patLen; j++) { - for (int k=0; k<32; k++) { - if (p->data[j][k]!=oldPat[i]->data[j][k]) { - s.pat.push_back(UndoPatternData(i,e->song.orders.ord[i][order],j,k,oldPat[i]->data[j][k],p->data[j][k])); - } - } - } - } - if (!s.pat.empty()) { - doPush=true; - } - break; - } - if (doPush) { - MARK_MODIFIED; - undoHist.push_back(s); - redoHist.clear(); - if (undoHist.size()>settings.maxUndoSteps) undoHist.pop_front(); - } -} - -void FurnaceGUI::doSelectAll() { - finishSelection(); - curNibble=false; - if (selStart.xFine==0 && selEnd.xFine==2+e->song.pat[selEnd.xCoarse].effectRows*2) { - if (selStart.y==0 && selEnd.y==e->song.patLen-1) { // select entire pattern - selStart.xCoarse=0; - selStart.xFine=0; - selEnd.xCoarse=e->getTotalChannelCount()-1; - selEnd.xFine=2+e->song.pat[selEnd.xCoarse].effectRows*2; - } else { // select entire column - selStart.y=0; - selEnd.y=e->song.patLen-1; - } - } else { - int selStartX=0; - int selEndX=0; - // find row position - for (SelectionPoint i; i.xCoarse!=selStart.xCoarse || i.xFine!=selStart.xFine; selStartX++) { - i.xFine++; - if (i.xFine>=3+e->song.pat[i.xCoarse].effectRows*2) { - i.xFine=0; - i.xCoarse++; - } - } - for (SelectionPoint i; i.xCoarse!=selEnd.xCoarse || i.xFine!=selEnd.xFine; selEndX++) { - i.xFine++; - if (i.xFine>=3+e->song.pat[i.xCoarse].effectRows*2) { - i.xFine=0; - i.xCoarse++; - } - } - - float aspect=float(selEndX-selStartX+1)/float(selEnd.y-selStart.y+1); - if (aspect<=1.0f && !(selStart.y==0 && selEnd.y==e->song.patLen-1)) { // up-down - selStart.y=0; - selEnd.y=e->song.patLen-1; - } else { // left-right - selStart.xFine=0; - selEnd.xFine=2+e->song.pat[selEnd.xCoarse].effectRows*2; - } - } -} - -#define maskOut(x) \ - if (x==0) { \ - if (!opMaskNote) continue; \ - } else if (x==1) { \ - if (!opMaskIns) continue; \ - } else if (x==2) { \ - if (!opMaskVol) continue; \ - } else if (((x)&1)==0) { \ - if (!opMaskEffectVal) continue; \ - } else if (((x)&1)==1) { \ - if (!opMaskEffect) continue; \ - } - -void FurnaceGUI::doDelete() { - finishSelection(); - prepareUndo(GUI_UNDO_PATTERN_DELETE); - curNibble=false; - - int iCoarse=selStart.xCoarse; - int iFine=selStart.xFine; - int ord=e->getOrder(); - for (; iCoarse<=selEnd.xCoarse; iCoarse++) { - if (!e->song.chanShow[iCoarse]) continue; - DivPattern* pat=e->song.pat[iCoarse].getPattern(e->song.orders.ord[iCoarse][ord],true); - for (; iFine<3+e->song.pat[iCoarse].effectRows*2 && (iCoarsedata[j][iFine]=0; - if (selStart.y==selEnd.y) pat->data[j][2]=-1; - } - pat->data[j][iFine+1]=(iFine<1)?0:-1; - } - } - iFine=0; - } - - makeUndo(GUI_UNDO_PATTERN_DELETE); -} - -void FurnaceGUI::doPullDelete() { - finishSelection(); - prepareUndo(GUI_UNDO_PATTERN_PULL); - curNibble=false; - - if (settings.pullDeleteBehavior) { - if (--selStart.y<0) selStart.y=0; - if (--selEnd.y<0) selEnd.y=0; - if (--cursor.y<0) cursor.y=0; - updateScroll(cursor.y); - } - - int iCoarse=selStart.xCoarse; - int iFine=selStart.xFine; - int ord=e->getOrder(); - for (; iCoarse<=selEnd.xCoarse; iCoarse++) { - if (!e->song.chanShow[iCoarse]) continue; - DivPattern* pat=e->song.pat[iCoarse].getPattern(e->song.orders.ord[iCoarse][ord],true); - for (; iFine<3+e->song.pat[iCoarse].effectRows*2 && (iCoarsesong.patLen; j++) { - if (jsong.patLen-1) { - if (iFine==0) { - pat->data[j][iFine]=pat->data[j+1][iFine]; - } - pat->data[j][iFine+1]=pat->data[j+1][iFine+1]; - } else { - if (iFine==0) { - pat->data[j][iFine]=0; - } - pat->data[j][iFine+1]=(iFine<1)?0:-1; - } - } - } - iFine=0; - } - - makeUndo(GUI_UNDO_PATTERN_PULL); -} - -void FurnaceGUI::doInsert() { - finishSelection(); - prepareUndo(GUI_UNDO_PATTERN_PUSH); - curNibble=false; - - int iCoarse=selStart.xCoarse; - int iFine=selStart.xFine; - int ord=e->getOrder(); - for (; iCoarse<=selEnd.xCoarse; iCoarse++) { - if (!e->song.chanShow[iCoarse]) continue; - DivPattern* pat=e->song.pat[iCoarse].getPattern(e->song.orders.ord[iCoarse][ord],true); - for (; iFine<3+e->song.pat[iCoarse].effectRows*2 && (iCoarsesong.patLen-1; j>=selStart.y; j--) { - if (j==selStart.y) { - if (iFine==0) { - pat->data[j][iFine]=0; - } - pat->data[j][iFine+1]=(iFine<1)?0:-1; - } else { - if (iFine==0) { - pat->data[j][iFine]=pat->data[j-1][iFine]; - } - pat->data[j][iFine+1]=pat->data[j-1][iFine+1]; - } - } - } - iFine=0; - } - - makeUndo(GUI_UNDO_PATTERN_PUSH); -} - -void FurnaceGUI::doTranspose(int amount) { - finishSelection(); - prepareUndo(GUI_UNDO_PATTERN_DELETE); - curNibble=false; - - int iCoarse=selStart.xCoarse; - int iFine=selStart.xFine; - int ord=e->getOrder(); - for (; iCoarse<=selEnd.xCoarse; iCoarse++) { - if (!e->song.chanShow[iCoarse]) continue; - DivPattern* pat=e->song.pat[iCoarse].getPattern(e->song.orders.ord[iCoarse][ord],true); - for (; iFine<3+e->song.pat[iCoarse].effectRows*2 && (iCoarsedata[j][0]; - int origOctave=(signed char)pat->data[j][1]; - if (origNote!=0 && origNote!=100 && origNote!=101 && origNote!=102) { - origNote+=amount; - while (origNote>12) { - origNote-=12; - origOctave++; - } - while (origNote<1) { - origNote+=12; - origOctave--; - } - if (origOctave==9 && origNote>11) { - origNote=11; - origOctave=9; - } - if (origOctave>9) { - origNote=11; - origOctave=9; - } - if (origOctave<-5) { - origNote=1; - origOctave=-5; - } - pat->data[j][0]=origNote; - pat->data[j][1]=(unsigned char)origOctave; - } - } else { - int top=255; - if (iFine==1) { - if (e->song.ins.empty()) continue; - top=e->song.ins.size()-1; - } else if (iFine==2) { // volume - top=e->getMaxVolumeChan(iCoarse); - } - if (pat->data[j][iFine+1]==-1) continue; - pat->data[j][iFine+1]=MIN(top,MAX(0,pat->data[j][iFine+1]+amount)); - } - } - } - iFine=0; - } - - makeUndo(GUI_UNDO_PATTERN_DELETE); -} - -void FurnaceGUI::doCopy(bool cut) { - finishSelection(); - if (cut) { - curNibble=false; - prepareUndo(GUI_UNDO_PATTERN_CUT); - } - clipboard=fmt::sprintf("org.tildearrow.furnace - Pattern Data (%d)\n%d",DIV_ENGINE_VERSION,selStart.xFine); - - for (int j=selStart.y; j<=selEnd.y; j++) { - int iCoarse=selStart.xCoarse; - int iFine=selStart.xFine; - if (iFine>3 && !(iFine&1)) { - iFine--; - } - int ord=e->getOrder(); - clipboard+='\n'; - for (; iCoarse<=selEnd.xCoarse; iCoarse++) { - if (!e->song.chanShow[iCoarse]) continue; - DivPattern* pat=e->song.pat[iCoarse].getPattern(e->song.orders.ord[iCoarse][ord],true); - for (; iFine<3+e->song.pat[iCoarse].effectRows*2 && (iCoarsedata[j][0],pat->data[j][1]); - if (cut) { - pat->data[j][0]=0; - pat->data[j][1]=0; - } - } else { - if (pat->data[j][iFine+1]==-1) { - clipboard+=".."; - } else { - clipboard+=fmt::sprintf("%.2X",pat->data[j][iFine+1]); - } - if (cut) { - pat->data[j][iFine+1]=-1; - } - } - } - clipboard+='|'; - iFine=0; - } - } - SDL_SetClipboardText(clipboard.c_str()); - - if (cut) { - makeUndo(GUI_UNDO_PATTERN_CUT); - } -} - -void FurnaceGUI::doPaste(PasteMode mode) { - finishSelection(); - prepareUndo(GUI_UNDO_PATTERN_PASTE); - char* clipText=SDL_GetClipboardText(); - if (clipText!=NULL) { - if (clipText[0]) { - clipboard=clipText; - } - SDL_free(clipText); - } - std::vector data; - String tempS; - for (char i: clipboard) { - if (i=='\r') continue; - if (i=='\n') { - data.push_back(tempS); - tempS=""; - continue; - } - tempS+=i; - } - data.push_back(tempS); - - int startOff=-1; - bool invalidData=false; - if (data.size()<2) return; - if (data[0]!=fmt::sprintf("org.tildearrow.furnace - Pattern Data (%d)",DIV_ENGINE_VERSION)) return; - if (sscanf(data[1].c_str(),"%d",&startOff)!=1) return; - if (startOff<0) return; - - DETERMINE_LAST; - - int j=cursor.y; - char note[4]; - int ord=e->getOrder(); - for (size_t i=2; isong.patLen; i++) { - size_t charPos=0; - int iCoarse=cursor.xCoarse; - int iFine=(startOff>2 && cursor.xFine>2)?(((cursor.xFine-1)&(~1))|1):startOff; - - String& line=data[i]; - - while (charPossong.pat[iCoarse].getPattern(e->song.orders.ord[iCoarse][ord],true); - if (line[charPos]=='|') { - iCoarse++; - if (iCoarsesong.chanShow[iCoarse]) { - iCoarse++; - if (iCoarse>=lastChannel) break; - } - iFine=0; - charPos++; - continue; - } - if (iFine==0) { - if (charPos>=line.size()) { - invalidData=true; - break; - } - note[0]=line[charPos++]; - if (charPos>=line.size()) { - invalidData=true; - break; - } - note[1]=line[charPos++]; - if (charPos>=line.size()) { - invalidData=true; - break; - } - note[2]=line[charPos++]; - note[3]=0; - - if (iFine==0 && !opMaskNote) { - iFine++; - continue; - } - - if ((mode==GUI_PASTE_MODE_MIX_BG || mode==GUI_PASTE_MODE_MIX_FG) && strcmp(note,"...")==0) { - // do nothing. - } else { - if (mode!=GUI_PASTE_MODE_MIX_BG || (pat->data[j][0]==0 && pat->data[j][1]==0)) { - if (!decodeNote(note,pat->data[j][0],pat->data[j][1])) { - invalidData=true; - break; - } - } - } - } else { - if (charPos>=line.size()) { - invalidData=true; - break; - } - note[0]=line[charPos++]; - if (charPos>=line.size()) { - invalidData=true; - break; - } - note[1]=line[charPos++]; - note[2]=0; - - if (iFine==1) { - if (!opMaskIns) { - iFine++; - continue; - } - } else if (iFine==2) { - if (!opMaskVol) { - iFine++; - continue; - } - } else if ((iFine&1)==0) { - if (!opMaskEffectVal) { - iFine++; - continue; - } - } else if ((iFine&1)==1) { - if (!opMaskEffect) { - iFine++; - continue; - } - } - - if (strcmp(note,"..")==0) { - if (!(mode==GUI_PASTE_MODE_MIX_BG || mode==GUI_PASTE_MODE_MIX_FG)) { - pat->data[j][iFine+1]=-1; - } - } else { - unsigned int val=0; - if (sscanf(note,"%2X",&val)!=1) { - invalidData=true; - break; - } - if (mode!=GUI_PASTE_MODE_MIX_BG || pat->data[j][iFine+1]==-1) { - if (iFine<(3+e->song.pat[iCoarse].effectRows*2)) pat->data[j][iFine+1]=val; - } - } - } - iFine++; - } - - if (invalidData) { - logW("invalid clipboard data! failed at line %d char %d\n",i,charPos); - logW("%s\n",line.c_str()); - break; - } - j++; - if (mode==GUI_PASTE_MODE_OVERFLOW && j>=e->song.patLen && ordsong.ordersLen-1) { - j=0; - ord++; - } - - if (mode==GUI_PASTE_MODE_FLOOD && i==data.size()-1) { - i=1; - } - } - - makeUndo(GUI_UNDO_PATTERN_PASTE); -} - -void FurnaceGUI::doChangeIns(int ins) { - finishSelection(); - prepareUndo(GUI_UNDO_PATTERN_CHANGE_INS); - - int iCoarse=selStart.xCoarse; - int ord=e->getOrder(); - for (; iCoarse<=selEnd.xCoarse; iCoarse++) { - if (!e->song.chanShow[iCoarse]) continue; - DivPattern* pat=e->song.pat[iCoarse].getPattern(e->song.orders.ord[iCoarse][ord],true); - for (int j=selStart.y; j<=selEnd.y; j++) { - if (pat->data[j][2]!=-1 || !(pat->data[j][0]==0 && pat->data[j][1]==0)) { - pat->data[j][2]=ins; - } - } - } - - makeUndo(GUI_UNDO_PATTERN_CHANGE_INS); -} - -void FurnaceGUI::doInterpolate() { - finishSelection(); - prepareUndo(GUI_UNDO_PATTERN_INTERPOLATE); - - std::vector> points; - int iCoarse=selStart.xCoarse; - int iFine=selStart.xFine; - int ord=e->getOrder(); - for (; iCoarse<=selEnd.xCoarse; iCoarse++) { - if (!e->song.chanShow[iCoarse]) continue; - DivPattern* pat=e->song.pat[iCoarse].getPattern(e->song.orders.ord[iCoarse][ord],true); - for (; iFine<3+e->song.pat[iCoarse].effectRows*2 && (iCoarsedata[j][iFine+1]!=-1) { - points.emplace(points.end(),j,pat->data[j][iFine+1]); - } - } - - if (points.size()>1) for (size_t j=0; j& curPoint=points[j]; - std::pair& nextPoint=points[j+1]; - double distance=nextPoint.first-curPoint.first; - for (int k=0; k<(nextPoint.first-curPoint.first); k++) { - pat->data[k+curPoint.first][iFine+1]=curPoint.second+((nextPoint.second-curPoint.second)*(double)k/distance); - } - } - } else { - for (int j=selStart.y; j<=selEnd.y; j++) { - if (pat->data[j][0]!=0 && pat->data[j][1]!=0) { - if (pat->data[j][0]!=100 && pat->data[j][0]!=101 && pat->data[j][0]!=102) { - points.emplace(points.end(),j,pat->data[j][0]+pat->data[j][1]*12); - } - } - } - - if (points.size()>1) for (size_t j=0; j& curPoint=points[j]; - std::pair& nextPoint=points[j+1]; - double distance=nextPoint.first-curPoint.first; - for (int k=0; k<(nextPoint.first-curPoint.first); k++) { - int val=curPoint.second+((nextPoint.second-curPoint.second)*(double)k/distance); - pat->data[k+curPoint.first][0]=val%12; - pat->data[k+curPoint.first][1]=val/12; - if (pat->data[k+curPoint.first][0]==0) { - pat->data[k+curPoint.first][0]=12; - pat->data[k+curPoint.first][1]--; - } - pat->data[k+curPoint.first][1]&=255; - } - } - } - } - iFine=0; - } - - makeUndo(GUI_UNDO_PATTERN_INTERPOLATE); -} - -void FurnaceGUI::doFade(int p0, int p1, bool mode) { - finishSelection(); - prepareUndo(GUI_UNDO_PATTERN_FADE); - - int iCoarse=selStart.xCoarse; - int iFine=selStart.xFine; - int ord=e->getOrder(); - for (; iCoarse<=selEnd.xCoarse; iCoarse++) { - if (!e->song.chanShow[iCoarse]) continue; - DivPattern* pat=e->song.pat[iCoarse].getPattern(e->song.orders.ord[iCoarse][ord],true); - for (; iFine<3+e->song.pat[iCoarse].effectRows*2 && (iCoarsesong.ins.empty()) continue; - absoluteTop=e->song.ins.size()-1; - } else if (iFine==2) { // volume - absoluteTop=e->getMaxVolumeChan(iCoarse); - } - if (selEnd.y-selStart.y<1) continue; - for (int j=selStart.y; j<=selEnd.y; j++) { - double fraction=double(j-selStart.y)/double(selEnd.y-selStart.y); - int value=p0+double(p1-p0)*fraction; - if (mode) { // nibble - value&=15; - pat->data[j][iFine+1]=MIN(absoluteTop,value|(value<<4)); - } else { // byte - pat->data[j][iFine+1]=MIN(absoluteTop,value); - } - } - } - } - iFine=0; - } - - makeUndo(GUI_UNDO_PATTERN_FADE); -} - -void FurnaceGUI::doInvertValues() { - finishSelection(); - prepareUndo(GUI_UNDO_PATTERN_INVERT_VAL); - - int iCoarse=selStart.xCoarse; - int iFine=selStart.xFine; - int ord=e->getOrder(); - for (; iCoarse<=selEnd.xCoarse; iCoarse++) { - if (!e->song.chanShow[iCoarse]) continue; - DivPattern* pat=e->song.pat[iCoarse].getPattern(e->song.orders.ord[iCoarse][ord],true); - for (; iFine<3+e->song.pat[iCoarse].effectRows*2 && (iCoarsesong.ins.empty()) continue; - top=e->song.ins.size()-1; - } else if (iFine==2) { // volume - top=e->getMaxVolumeChan(iCoarse); - } - for (int j=selStart.y; j<=selEnd.y; j++) { - if (pat->data[j][iFine+1]==-1) continue; - pat->data[j][iFine+1]=top-pat->data[j][iFine+1]; - } - } - } - iFine=0; - } - - makeUndo(GUI_UNDO_PATTERN_INVERT_VAL); -} - -void FurnaceGUI::doScale(float top) { - finishSelection(); - prepareUndo(GUI_UNDO_PATTERN_SCALE); - - int iCoarse=selStart.xCoarse; - int iFine=selStart.xFine; - int ord=e->getOrder(); - for (; iCoarse<=selEnd.xCoarse; iCoarse++) { - if (!e->song.chanShow[iCoarse]) continue; - DivPattern* pat=e->song.pat[iCoarse].getPattern(e->song.orders.ord[iCoarse][ord],true); - for (; iFine<3+e->song.pat[iCoarse].effectRows*2 && (iCoarsesong.ins.empty()) continue; - absoluteTop=e->song.ins.size()-1; - } else if (iFine==2) { // volume - absoluteTop=e->getMaxVolumeChan(iCoarse); - } - for (int j=selStart.y; j<=selEnd.y; j++) { - if (pat->data[j][iFine+1]==-1) continue; - pat->data[j][iFine+1]=MIN(absoluteTop,(double)pat->data[j][iFine+1]*(top/100.0f)); - } - } - } - iFine=0; - } - - makeUndo(GUI_UNDO_PATTERN_SCALE); -} - -void FurnaceGUI::doRandomize(int bottom, int top, bool mode) { - finishSelection(); - prepareUndo(GUI_UNDO_PATTERN_RANDOMIZE); - - int iCoarse=selStart.xCoarse; - int iFine=selStart.xFine; - int ord=e->getOrder(); - for (; iCoarse<=selEnd.xCoarse; iCoarse++) { - if (!e->song.chanShow[iCoarse]) continue; - DivPattern* pat=e->song.pat[iCoarse].getPattern(e->song.orders.ord[iCoarse][ord],true); - for (; iFine<3+e->song.pat[iCoarse].effectRows*2 && (iCoarsesong.ins.empty()) continue; - absoluteTop=e->song.ins.size()-1; - } else if (iFine==2) { // volume - absoluteTop=e->getMaxVolumeChan(iCoarse); - } - for (int j=selStart.y; j<=selEnd.y; j++) { - int value=0; - int value2=0; - if (top-bottom<=0) { - value=MIN(absoluteTop,bottom); - value2=MIN(absoluteTop,bottom); - } else { - value=MIN(absoluteTop,bottom+(rand()%(top-bottom+1))); - value2=MIN(absoluteTop,bottom+(rand()%(top-bottom+1))); - } - if (mode) { - value&=15; - value2&=15; - pat->data[j][iFine+1]=value|(value2<<4); - } else { - pat->data[j][iFine+1]=value; - } - } - } - } - iFine=0; - } - - makeUndo(GUI_UNDO_PATTERN_RANDOMIZE); -} - -void FurnaceGUI::doFlip() { - finishSelection(); - prepareUndo(GUI_UNDO_PATTERN_FLIP); - - DivPattern patBuffer; - int iCoarse=selStart.xCoarse; - int iFine=selStart.xFine; - int ord=e->getOrder(); - for (; iCoarse<=selEnd.xCoarse; iCoarse++) { - if (!e->song.chanShow[iCoarse]) continue; - DivPattern* pat=e->song.pat[iCoarse].getPattern(e->song.orders.ord[iCoarse][ord],true); - for (; iFine<3+e->song.pat[iCoarse].effectRows*2 && (iCoarsedata[j][0]; - } - patBuffer.data[j][iFine+1]=pat->data[j][iFine+1]; - } - for (int j=selStart.y; j<=selEnd.y; j++) { - if (iFine==0) { - pat->data[j][0]=patBuffer.data[selEnd.y-j+selStart.y][0]; - } - pat->data[j][iFine+1]=patBuffer.data[selEnd.y-j+selStart.y][iFine+1]; - } - } - iFine=0; - } - - makeUndo(GUI_UNDO_PATTERN_FLIP); -} - -void FurnaceGUI::doCollapse(int divider) { - finishSelection(); - prepareUndo(GUI_UNDO_PATTERN_COLLAPSE); - - DivPattern patBuffer; - int iCoarse=selStart.xCoarse; - int iFine=selStart.xFine; - int ord=e->getOrder(); - for (; iCoarse<=selEnd.xCoarse; iCoarse++) { - if (!e->song.chanShow[iCoarse]) continue; - DivPattern* pat=e->song.pat[iCoarse].getPattern(e->song.orders.ord[iCoarse][ord],true); - for (; iFine<3+e->song.pat[iCoarse].effectRows*2 && (iCoarsedata[j][0]; - } - patBuffer.data[j][iFine+1]=pat->data[j][iFine+1]; - } - for (int j=0; j<=selEnd.y-selStart.y; j++) { - if (j*divider>=selEnd.y-selStart.y) { - if (iFine==0) { - pat->data[j+selStart.y][0]=0; - pat->data[j+selStart.y][1]=0; - } else { - pat->data[j+selStart.y][iFine+1]=-1; - } - } else { - if (iFine==0) { - pat->data[j+selStart.y][0]=patBuffer.data[j*divider+selStart.y][0]; - } - pat->data[j+selStart.y][iFine+1]=patBuffer.data[j*divider+selStart.y][iFine+1]; - - if (iFine==0) { - for (int k=1; k=selEnd.y-selStart.y) break; - if (!(pat->data[j+selStart.y][0]==0 && pat->data[j+selStart.y][1]==0)) break; - pat->data[j+selStart.y][0]=patBuffer.data[j*divider+selStart.y+k][0]; - pat->data[j+selStart.y][1]=patBuffer.data[j*divider+selStart.y+k][1]; - } - } else { - for (int k=1; k=selEnd.y-selStart.y) break; - if (pat->data[j+selStart.y][iFine+1]!=-1) break; - pat->data[j+selStart.y][iFine+1]=patBuffer.data[j*divider+selStart.y+k][iFine+1]; - } - } - } - } - } - iFine=0; - } - - makeUndo(GUI_UNDO_PATTERN_COLLAPSE); -} - -void FurnaceGUI::doExpand(int multiplier) { - if (multiplier<1) return; - - finishSelection(); - prepareUndo(GUI_UNDO_PATTERN_EXPAND); - - DivPattern patBuffer; - int iCoarse=selStart.xCoarse; - int iFine=selStart.xFine; - int ord=e->getOrder(); - for (; iCoarse<=selEnd.xCoarse; iCoarse++) { - if (!e->song.chanShow[iCoarse]) continue; - DivPattern* pat=e->song.pat[iCoarse].getPattern(e->song.orders.ord[iCoarse][ord],true); - for (; iFine<3+e->song.pat[iCoarse].effectRows*2 && (iCoarsedata[j][0]; - } - patBuffer.data[j][iFine+1]=pat->data[j][iFine+1]; - } - for (int j=0; j<=(selEnd.y-selStart.y)*multiplier; j++) { - if ((j+selStart.y)>=e->song.patLen) break; - if ((j%multiplier)!=0) { - if (iFine==0) { - pat->data[j+selStart.y][0]=0; - pat->data[j+selStart.y][1]=0; - } else { - pat->data[j+selStart.y][iFine+1]=-1; - } - continue; - } - if (iFine==0) { - pat->data[j+selStart.y][0]=patBuffer.data[j/multiplier+selStart.y][0]; - } - pat->data[j+selStart.y][iFine+1]=patBuffer.data[j/multiplier+selStart.y][iFine+1]; - } - } - iFine=0; - } - - makeUndo(GUI_UNDO_PATTERN_EXPAND); -} - -void FurnaceGUI::doUndo() { - if (undoHist.empty()) return; - UndoStep& us=undoHist.back(); - redoHist.push_back(us); - MARK_MODIFIED; - - switch (us.type) { - case GUI_UNDO_CHANGE_ORDER: - e->song.ordersLen=us.oldOrdersLen; - for (UndoOrderData& i: us.ord) { - e->song.orders.ord[i.chan][i.ord]=i.oldVal; - } - break; - case GUI_UNDO_PATTERN_EDIT: - case GUI_UNDO_PATTERN_DELETE: - case GUI_UNDO_PATTERN_PULL: - case GUI_UNDO_PATTERN_PUSH: - case GUI_UNDO_PATTERN_CUT: - case GUI_UNDO_PATTERN_PASTE: - case GUI_UNDO_PATTERN_CHANGE_INS: - case GUI_UNDO_PATTERN_INTERPOLATE: - case GUI_UNDO_PATTERN_FADE: - case GUI_UNDO_PATTERN_SCALE: - case GUI_UNDO_PATTERN_RANDOMIZE: - case GUI_UNDO_PATTERN_INVERT_VAL: - case GUI_UNDO_PATTERN_FLIP: - case GUI_UNDO_PATTERN_COLLAPSE: - case GUI_UNDO_PATTERN_EXPAND: - for (UndoPatternData& i: us.pat) { - DivPattern* p=e->song.pat[i.chan].getPattern(i.pat,true); - p->data[i.row][i.col]=i.oldVal; - } - if (!e->isPlaying()) { - cursor=us.cursor; - selStart=us.selStart; - selEnd=us.selEnd; - curNibble=us.nibble; - updateScroll(cursor.y); - e->setOrder(us.order); - } - break; - } - - undoHist.pop_back(); -} - -void FurnaceGUI::doRedo() { - if (redoHist.empty()) return; - UndoStep& us=redoHist.back(); - undoHist.push_back(us); - MARK_MODIFIED; - - switch (us.type) { - case GUI_UNDO_CHANGE_ORDER: - e->song.ordersLen=us.newOrdersLen; - for (UndoOrderData& i: us.ord) { - e->song.orders.ord[i.chan][i.ord]=i.newVal; - } - break; - case GUI_UNDO_PATTERN_EDIT: - case GUI_UNDO_PATTERN_DELETE: - case GUI_UNDO_PATTERN_PULL: - case GUI_UNDO_PATTERN_PUSH: - case GUI_UNDO_PATTERN_CUT: - case GUI_UNDO_PATTERN_PASTE: - case GUI_UNDO_PATTERN_CHANGE_INS: - case GUI_UNDO_PATTERN_INTERPOLATE: - case GUI_UNDO_PATTERN_FADE: - case GUI_UNDO_PATTERN_SCALE: - case GUI_UNDO_PATTERN_RANDOMIZE: - case GUI_UNDO_PATTERN_INVERT_VAL: - case GUI_UNDO_PATTERN_FLIP: - case GUI_UNDO_PATTERN_COLLAPSE: - case GUI_UNDO_PATTERN_EXPAND: - for (UndoPatternData& i: us.pat) { - DivPattern* p=e->song.pat[i.chan].getPattern(i.pat,true); - p->data[i.row][i.col]=i.newVal; - } - if (!e->isPlaying()) { - cursor=us.cursor; - selStart=us.selStart; - selEnd=us.selEnd; - curNibble=us.nibble; - updateScroll(cursor.y); - e->setOrder(us.order); - } - - break; - } - - redoHist.pop_back(); -} - void FurnaceGUI::play(int row) { e->walkSong(loopOrder,loopRow,loopEnd); memset(lastIns,-1,sizeof(int)*DIV_MAX_CHANS); @@ -3373,670 +725,6 @@ void FurnaceGUI::stopPreviewNote(SDL_Scancode scancode) { } } -void FurnaceGUI::doAction(int what) { - switch (what) { - case GUI_ACTION_OPEN: - if (modified) { - showWarning("Unsaved changes! Are you sure?",GUI_WARN_OPEN); - } else { - openFileDialog(GUI_FILE_OPEN); - } - break; - case GUI_ACTION_OPEN_BACKUP: - if (modified) { - showWarning("Unsaved changes! Are you sure?",GUI_WARN_OPEN_BACKUP); - } else { - if (load(backupPath)>0) { - showError("No backup available! (or unable to open it)"); - } - } - break; - case GUI_ACTION_SAVE: - if (curFileName=="" || curFileName==backupPath || e->song.version>=0xff00) { - openFileDialog(GUI_FILE_SAVE); - } else { - if (save(curFileName,e->song.isDMF?e->song.version:0)>0) { - showError(fmt::sprintf("Error while saving file! (%s)",lastError)); - } - } - break; - case GUI_ACTION_SAVE_AS: - openFileDialog(GUI_FILE_SAVE); - break; - case GUI_ACTION_UNDO: - doUndo(); - break; - case GUI_ACTION_REDO: - doRedo(); - break; - case GUI_ACTION_PLAY_TOGGLE: - if (e->isPlaying() && !e->isStepping()) { - stop(); - } else { - play(); - } - break; - case GUI_ACTION_PLAY: - play(); - break; - case GUI_ACTION_STOP: - stop(); - break; - case GUI_ACTION_PLAY_REPEAT: - play(); - e->setRepeatPattern(true); - break; - case GUI_ACTION_PLAY_CURSOR: - if (e->isPlaying() && !e->isStepping()) { - stop(); - } else { - play(cursor.y); - } - break; - case GUI_ACTION_STEP_ONE: - e->stepOne(cursor.y); - break; - case GUI_ACTION_OCTAVE_UP: - if (++curOctave>7) { - curOctave=7; - } else { - for (size_t i=0; inoteOff(activeNotes[i].chan); - } - activeNotes.clear(); - } - break; - case GUI_ACTION_OCTAVE_DOWN: - if (--curOctave<-5) { - curOctave=-5; - } else { - for (size_t i=0; inoteOff(activeNotes[i].chan); - } - activeNotes.clear(); - } - break; - case GUI_ACTION_INS_UP: - if (--curIns<-1) curIns=-1; - break; - case GUI_ACTION_INS_DOWN: - if (++curIns>=(int)e->song.ins.size()) curIns=((int)e->song.ins.size())-1; - break; - case GUI_ACTION_STEP_UP: - if (++editStep>64) editStep=64; - break; - case GUI_ACTION_STEP_DOWN: - if (--editStep<0) editStep=0; - break; - case GUI_ACTION_TOGGLE_EDIT: - edit=!edit; - break; - case GUI_ACTION_METRONOME: - e->setMetronome(!e->getMetronome()); - break; - case GUI_ACTION_REPEAT_PATTERN: - e->setRepeatPattern(!e->getRepeatPattern()); - break; - case GUI_ACTION_FOLLOW_ORDERS: - followOrders=!followOrders; - break; - case GUI_ACTION_FOLLOW_PATTERN: - followPattern=!followPattern; - break; - case GUI_ACTION_PANIC: - e->syncReset(); - break; - - case GUI_ACTION_WINDOW_EDIT_CONTROLS: - nextWindow=GUI_WINDOW_EDIT_CONTROLS; - break; - case GUI_ACTION_WINDOW_ORDERS: - nextWindow=GUI_WINDOW_ORDERS; - break; - case GUI_ACTION_WINDOW_INS_LIST: - nextWindow=GUI_WINDOW_INS_LIST; - break; - case GUI_ACTION_WINDOW_INS_EDIT: - nextWindow=GUI_WINDOW_INS_EDIT; - break; - case GUI_ACTION_WINDOW_SONG_INFO: - nextWindow=GUI_WINDOW_SONG_INFO; - break; - case GUI_ACTION_WINDOW_PATTERN: - nextWindow=GUI_WINDOW_PATTERN; - break; - case GUI_ACTION_WINDOW_WAVE_LIST: - nextWindow=GUI_WINDOW_WAVE_LIST; - break; - case GUI_ACTION_WINDOW_WAVE_EDIT: - nextWindow=GUI_WINDOW_WAVE_EDIT; - break; - case GUI_ACTION_WINDOW_SAMPLE_LIST: - nextWindow=GUI_WINDOW_SAMPLE_LIST; - break; - case GUI_ACTION_WINDOW_SAMPLE_EDIT: - nextWindow=GUI_WINDOW_SAMPLE_EDIT; - break; - case GUI_ACTION_WINDOW_ABOUT: - nextWindow=GUI_WINDOW_ABOUT; - break; - case GUI_ACTION_WINDOW_SETTINGS: - nextWindow=GUI_WINDOW_SETTINGS; - break; - case GUI_ACTION_WINDOW_MIXER: - nextWindow=GUI_WINDOW_MIXER; - break; - case GUI_ACTION_WINDOW_DEBUG: - nextWindow=GUI_WINDOW_DEBUG; - break; - case GUI_ACTION_WINDOW_OSCILLOSCOPE: - nextWindow=GUI_WINDOW_OSCILLOSCOPE; - break; - case GUI_ACTION_WINDOW_VOL_METER: - nextWindow=GUI_WINDOW_VOL_METER; - break; - case GUI_ACTION_WINDOW_STATS: - nextWindow=GUI_WINDOW_STATS; - break; - case GUI_ACTION_WINDOW_COMPAT_FLAGS: - nextWindow=GUI_WINDOW_COMPAT_FLAGS; - break; - case GUI_ACTION_WINDOW_PIANO: - nextWindow=GUI_WINDOW_PIANO; - break; - case GUI_ACTION_WINDOW_NOTES: - nextWindow=GUI_WINDOW_NOTES; - break; - case GUI_ACTION_WINDOW_CHANNELS: - nextWindow=GUI_WINDOW_CHANNELS; - break; - case GUI_ACTION_WINDOW_REGISTER_VIEW: - nextWindow=GUI_WINDOW_REGISTER_VIEW; - break; - - case GUI_ACTION_COLLAPSE_WINDOW: - collapseWindow=true; - break; - case GUI_ACTION_CLOSE_WINDOW: - switch (curWindow) { - case GUI_WINDOW_EDIT_CONTROLS: - editControlsOpen=false; - break; - case GUI_WINDOW_SONG_INFO: - songInfoOpen=false; - break; - case GUI_WINDOW_ORDERS: - ordersOpen=false; - break; - case GUI_WINDOW_INS_LIST: - insListOpen=false; - break; - case GUI_WINDOW_PATTERN: - patternOpen=false; - break; - case GUI_WINDOW_INS_EDIT: - insEditOpen=false; - break; - case GUI_WINDOW_WAVE_LIST: - waveListOpen=false; - break; - case GUI_WINDOW_WAVE_EDIT: - waveEditOpen=false; - break; - case GUI_WINDOW_SAMPLE_LIST: - sampleListOpen=false; - break; - case GUI_WINDOW_SAMPLE_EDIT: - sampleEditOpen=false; - break; - case GUI_WINDOW_MIXER: - mixerOpen=false; - break; - case GUI_WINDOW_ABOUT: - aboutOpen=false; - break; - case GUI_WINDOW_SETTINGS: - settingsOpen=false; - break; - case GUI_WINDOW_DEBUG: - debugOpen=false; - break; - case GUI_WINDOW_OSCILLOSCOPE: - oscOpen=false; - break; - case GUI_WINDOW_VOL_METER: - volMeterOpen=false; - break; - case GUI_WINDOW_STATS: - statsOpen=false; - break; - case GUI_WINDOW_COMPAT_FLAGS: - compatFlagsOpen=false; - break; - case GUI_WINDOW_PIANO: - pianoOpen=false; - break; - case GUI_WINDOW_NOTES: - notesOpen=false; - break; - case GUI_WINDOW_CHANNELS: - channelsOpen=false; - break; - case GUI_WINDOW_REGISTER_VIEW: - regViewOpen=false; - break; - default: - break; - } - curWindow=GUI_WINDOW_NOTHING; - break; - - case GUI_ACTION_PAT_NOTE_UP: - doTranspose(1); - break; - case GUI_ACTION_PAT_NOTE_DOWN: - doTranspose(-1); - break; - case GUI_ACTION_PAT_OCTAVE_UP: - doTranspose(12); - break; - case GUI_ACTION_PAT_OCTAVE_DOWN: - doTranspose(-12); - break; - case GUI_ACTION_PAT_SELECT_ALL: - doSelectAll(); - break; - case GUI_ACTION_PAT_CUT: - doCopy(true); - break; - case GUI_ACTION_PAT_COPY: - doCopy(false); - break; - case GUI_ACTION_PAT_PASTE: - doPaste(); - break; - case GUI_ACTION_PAT_CURSOR_UP: - moveCursor(0,-MAX(1,settings.scrollStep?editStep:1),false); - break; - case GUI_ACTION_PAT_CURSOR_DOWN: - moveCursor(0,MAX(1,settings.scrollStep?editStep:1),false); - break; - case GUI_ACTION_PAT_CURSOR_LEFT: - moveCursor(-1,0,false); - break; - case GUI_ACTION_PAT_CURSOR_RIGHT: - moveCursor(1,0,false); - break; - case GUI_ACTION_PAT_CURSOR_UP_ONE: - moveCursor(0,-1,false); - break; - case GUI_ACTION_PAT_CURSOR_DOWN_ONE: - moveCursor(0,1,false); - break; - case GUI_ACTION_PAT_CURSOR_LEFT_CHANNEL: - moveCursorPrevChannel(false); - break; - case GUI_ACTION_PAT_CURSOR_RIGHT_CHANNEL: - moveCursorNextChannel(false); - break; - case GUI_ACTION_PAT_CURSOR_NEXT_CHANNEL: - moveCursorNextChannel(true); - break; - case GUI_ACTION_PAT_CURSOR_PREVIOUS_CHANNEL: - moveCursorPrevChannel(true); - break; - case GUI_ACTION_PAT_CURSOR_BEGIN: - moveCursorTop(false); - break; - case GUI_ACTION_PAT_CURSOR_END: - moveCursorBottom(false); - break; - case GUI_ACTION_PAT_CURSOR_UP_COARSE: - moveCursor(0,-16,false); - break; - case GUI_ACTION_PAT_CURSOR_DOWN_COARSE: - moveCursor(0,16,false); - break; - case GUI_ACTION_PAT_SELECTION_UP: - moveCursor(0,-MAX(1,settings.scrollStep?editStep:1),true); - break; - case GUI_ACTION_PAT_SELECTION_DOWN: - moveCursor(0,MAX(1,settings.scrollStep?editStep:1),true); - break; - case GUI_ACTION_PAT_SELECTION_LEFT: - moveCursor(-1,0,true); - break; - case GUI_ACTION_PAT_SELECTION_RIGHT: - moveCursor(1,0,true); - break; - case GUI_ACTION_PAT_SELECTION_UP_ONE: - moveCursor(0,-1,true); - break; - case GUI_ACTION_PAT_SELECTION_DOWN_ONE: - moveCursor(0,1,true); - break; - case GUI_ACTION_PAT_SELECTION_BEGIN: - moveCursorTop(true); - break; - case GUI_ACTION_PAT_SELECTION_END: - moveCursorBottom(true); - break; - case GUI_ACTION_PAT_SELECTION_UP_COARSE: - moveCursor(0,-16,true); - break; - case GUI_ACTION_PAT_SELECTION_DOWN_COARSE: - moveCursor(0,16,true); - break; - case GUI_ACTION_PAT_DELETE: - doDelete(); - if (settings.stepOnDelete) { - moveCursor(0,editStep,false); - } - break; - case GUI_ACTION_PAT_PULL_DELETE: - doPullDelete(); - break; - case GUI_ACTION_PAT_INSERT: - doInsert(); - if (settings.stepOnInsert) { - moveCursor(0,editStep,false); - } - break; - case GUI_ACTION_PAT_MUTE_CURSOR: - if (cursor.xCoarse<0 || cursor.xCoarse>=e->getTotalChannelCount()) break; - e->toggleMute(cursor.xCoarse); - break; - case GUI_ACTION_PAT_SOLO_CURSOR: - if (cursor.xCoarse<0 || cursor.xCoarse>=e->getTotalChannelCount()) break; - e->toggleSolo(cursor.xCoarse); - break; - case GUI_ACTION_PAT_UNMUTE_ALL: - e->unmuteAll(); - break; - case GUI_ACTION_PAT_NEXT_ORDER: - if (e->getOrder()song.ordersLen-1) { - e->setOrder(e->getOrder()+1); - } - break; - case GUI_ACTION_PAT_PREV_ORDER: - if (e->getOrder()>0) { - e->setOrder(e->getOrder()-1); - } - break; - case GUI_ACTION_PAT_COLLAPSE: - if (cursor.xCoarse<0 || cursor.xCoarse>=e->getTotalChannelCount()) break; - e->song.chanCollapse[cursor.xCoarse]=!e->song.chanCollapse[cursor.xCoarse]; - break; - case GUI_ACTION_PAT_INCREASE_COLUMNS: - if (cursor.xCoarse<0 || cursor.xCoarse>=e->getTotalChannelCount()) break; - e->song.pat[cursor.xCoarse].effectRows++; - if (e->song.pat[cursor.xCoarse].effectRows>8) e->song.pat[cursor.xCoarse].effectRows=8; - break; - case GUI_ACTION_PAT_DECREASE_COLUMNS: - if (cursor.xCoarse<0 || cursor.xCoarse>=e->getTotalChannelCount()) break; - e->song.pat[cursor.xCoarse].effectRows--; - if (e->song.pat[cursor.xCoarse].effectRows<1) e->song.pat[cursor.xCoarse].effectRows=1; - break; - case GUI_ACTION_PAT_INTERPOLATE: - doInterpolate(); - break; - case GUI_ACTION_PAT_INVERT_VALUES: - doInvertValues(); - break; - case GUI_ACTION_PAT_FLIP_SELECTION: - doFlip(); - break; - case GUI_ACTION_PAT_COLLAPSE_ROWS: - doCollapse(2); - break; - case GUI_ACTION_PAT_EXPAND_ROWS: - doExpand(2); - break; - case GUI_ACTION_PAT_COLLAPSE_PAT: // TODO - break; - case GUI_ACTION_PAT_EXPAND_PAT: // TODO - break; - case GUI_ACTION_PAT_COLLAPSE_SONG: // TODO - break; - case GUI_ACTION_PAT_EXPAND_SONG: // TODO - break; - case GUI_ACTION_PAT_LATCH: // TODO - break; - - case GUI_ACTION_INS_LIST_ADD: - curIns=e->addInstrument(cursor.xCoarse); - MARK_MODIFIED; - break; - case GUI_ACTION_INS_LIST_DUPLICATE: - if (curIns>=0 && curIns<(int)e->song.ins.size()) { - int prevIns=curIns; - curIns=e->addInstrument(cursor.xCoarse); - (*e->song.ins[curIns])=(*e->song.ins[prevIns]); - MARK_MODIFIED; - } - break; - case GUI_ACTION_INS_LIST_OPEN: - openFileDialog(GUI_FILE_INS_OPEN); - break; - case GUI_ACTION_INS_LIST_SAVE: - if (curIns>=0 && curIns<(int)e->song.ins.size()) openFileDialog(GUI_FILE_INS_SAVE); - break; - case GUI_ACTION_INS_LIST_MOVE_UP: - if (e->moveInsUp(curIns)) curIns--; - break; - case GUI_ACTION_INS_LIST_MOVE_DOWN: - if (e->moveInsDown(curIns)) curIns++; - break; - case GUI_ACTION_INS_LIST_DELETE: - if (curIns>=0 && curIns<(int)e->song.ins.size()) { - e->delInstrument(curIns); - MARK_MODIFIED; - if (curIns>=(int)e->song.ins.size()) { - curIns--; - } - } - break; - case GUI_ACTION_INS_LIST_EDIT: - insEditOpen=true; - break; - case GUI_ACTION_INS_LIST_UP: - if (--curIns<0) curIns=0; - break; - case GUI_ACTION_INS_LIST_DOWN: - if (++curIns>=(int)e->song.ins.size()) curIns=((int)e->song.ins.size())-1; - break; - - case GUI_ACTION_WAVE_LIST_ADD: - curWave=e->addWave(); - MARK_MODIFIED; - break; - case GUI_ACTION_WAVE_LIST_DUPLICATE: - if (curWave>=0 && curWave<(int)e->song.wave.size()) { - int prevWave=curWave; - curWave=e->addWave(); - (*e->song.wave[curWave])=(*e->song.wave[prevWave]); - MARK_MODIFIED; - } - break; - case GUI_ACTION_WAVE_LIST_OPEN: - openFileDialog(GUI_FILE_WAVE_OPEN); - break; - case GUI_ACTION_WAVE_LIST_SAVE: - if (curWave>=0 && curWave<(int)e->song.wave.size()) openFileDialog(GUI_FILE_WAVE_SAVE); - break; - case GUI_ACTION_WAVE_LIST_MOVE_UP: - if (e->moveWaveUp(curWave)) curWave--; - break; - case GUI_ACTION_WAVE_LIST_MOVE_DOWN: - if (e->moveWaveDown(curWave)) curWave++; - break; - case GUI_ACTION_WAVE_LIST_DELETE: - if (curWave>=0 && curWave<(int)e->song.wave.size()) { - e->delWave(curWave); - MARK_MODIFIED; - if (curWave>=(int)e->song.wave.size()) { - curWave--; - } - } - break; - case GUI_ACTION_WAVE_LIST_EDIT: - waveEditOpen=true; - break; - case GUI_ACTION_WAVE_LIST_UP: - if (--curWave<0) curWave=0; - break; - case GUI_ACTION_WAVE_LIST_DOWN: - if (++curWave>=(int)e->song.wave.size()) curWave=((int)e->song.wave.size())-1; - break; - - case GUI_ACTION_SAMPLE_LIST_ADD: - curSample=e->addSample(); - MARK_MODIFIED; - break; - case GUI_ACTION_SAMPLE_LIST_OPEN: - openFileDialog(GUI_FILE_SAMPLE_OPEN); - break; - case GUI_ACTION_SAMPLE_LIST_SAVE: - if (curSample>=0 && curSample<(int)e->song.sample.size()) openFileDialog(GUI_FILE_SAMPLE_SAVE); - break; - case GUI_ACTION_SAMPLE_LIST_MOVE_UP: - if (e->moveSampleUp(curSample)) curSample--; - break; - case GUI_ACTION_SAMPLE_LIST_MOVE_DOWN: - if (e->moveSampleDown(curSample)) curSample++; - break; - case GUI_ACTION_SAMPLE_LIST_DELETE: - e->delSample(curSample); - MARK_MODIFIED; - if (curSample>=(int)e->song.sample.size()) { - curSample--; - } - break; - case GUI_ACTION_SAMPLE_LIST_EDIT: - sampleEditOpen=true; - break; - case GUI_ACTION_SAMPLE_LIST_UP: - if (--curSample<0) curSample=0; - break; - case GUI_ACTION_SAMPLE_LIST_DOWN: - if (++curSample>=(int)e->song.sample.size()) curSample=((int)e->song.sample.size())-1; - break; - case GUI_ACTION_SAMPLE_LIST_PREVIEW: - e->previewSample(curSample); - break; - case GUI_ACTION_SAMPLE_LIST_STOP_PREVIEW: - e->stopSamplePreview(); - break; - - case GUI_ACTION_ORDERS_UP: - if (e->getOrder()>0) { - e->setOrder(e->getOrder()-1); - } - break; - case GUI_ACTION_ORDERS_DOWN: - if (e->getOrder()song.ordersLen-1) { - e->setOrder(e->getOrder()+1); - } - break; - case GUI_ACTION_ORDERS_LEFT: { - DETERMINE_FIRST; - - do { - orderCursor--; - if (orderCursorsong.chanShow[orderCursor]); - break; - } - case GUI_ACTION_ORDERS_RIGHT: { - DETERMINE_LAST; - - do { - orderCursor++; - if (orderCursor>=lastChannel) { - orderCursor=lastChannel-1; - break; - } - } while (!e->song.chanShow[orderCursor]); - break; - } - case GUI_ACTION_ORDERS_INCREASE: { - if (orderCursor<0 || orderCursor>=e->getTotalChannelCount()) break; - int curOrder=e->getOrder(); - if (e->song.orders.ord[orderCursor][curOrder]<0x7f) { - e->song.orders.ord[orderCursor][curOrder]++; - } - break; - } - case GUI_ACTION_ORDERS_DECREASE: { - if (orderCursor<0 || orderCursor>=e->getTotalChannelCount()) break; - int curOrder=e->getOrder(); - if (e->song.orders.ord[orderCursor][curOrder]>0) { - e->song.orders.ord[orderCursor][curOrder]--; - } - break; - } - case GUI_ACTION_ORDERS_EDIT_MODE: - orderEditMode++; - if (orderEditMode>3) orderEditMode=0; - break; - case GUI_ACTION_ORDERS_LINK: - changeAllOrders=!changeAllOrders; - break; - case GUI_ACTION_ORDERS_ADD: - prepareUndo(GUI_UNDO_CHANGE_ORDER); - e->addOrder(false,false); - makeUndo(GUI_UNDO_CHANGE_ORDER); - break; - case GUI_ACTION_ORDERS_DUPLICATE: - prepareUndo(GUI_UNDO_CHANGE_ORDER); - e->addOrder(true,false); - makeUndo(GUI_UNDO_CHANGE_ORDER); - break; - case GUI_ACTION_ORDERS_DEEP_CLONE: - prepareUndo(GUI_UNDO_CHANGE_ORDER); - e->deepCloneOrder(false); - makeUndo(GUI_UNDO_CHANGE_ORDER); - if (!e->getWarnings().empty()) { - showWarning(e->getWarnings(),GUI_WARN_GENERIC); - } - break; - case GUI_ACTION_ORDERS_DUPLICATE_END: - prepareUndo(GUI_UNDO_CHANGE_ORDER); - e->addOrder(true,true); - makeUndo(GUI_UNDO_CHANGE_ORDER); - break; - case GUI_ACTION_ORDERS_DEEP_CLONE_END: - prepareUndo(GUI_UNDO_CHANGE_ORDER); - e->deepCloneOrder(true); - makeUndo(GUI_UNDO_CHANGE_ORDER); - if (!e->getWarnings().empty()) { - showWarning(e->getWarnings(),GUI_WARN_GENERIC); - } - break; - case GUI_ACTION_ORDERS_REMOVE: - prepareUndo(GUI_UNDO_CHANGE_ORDER); - e->deleteOrder(); - makeUndo(GUI_UNDO_CHANGE_ORDER); - break; - case GUI_ACTION_ORDERS_MOVE_UP: - prepareUndo(GUI_UNDO_CHANGE_ORDER); - e->moveOrderUp(); - makeUndo(GUI_UNDO_CHANGE_ORDER); - break; - case GUI_ACTION_ORDERS_MOVE_DOWN: - prepareUndo(GUI_UNDO_CHANGE_ORDER); - e->moveOrderDown(); - makeUndo(GUI_UNDO_CHANGE_ORDER); - break; - case GUI_ACTION_ORDERS_REPLAY: - e->setOrder(e->getOrder()); - break; - } -} - void FurnaceGUI::keyDown(SDL_Event& ev) { if (ImGuiFileDialog::Instance()->IsOpened()) return; if (aboutOpen) return; @@ -5262,398 +1950,15 @@ bool FurnaceGUI::loop() { } ImGui::Separator(); if (ImGui::BeginMenu("add system...")) { - sysAddOption(DIV_SYSTEM_YM2612); - sysAddOption(DIV_SYSTEM_YM2612_EXT); - sysAddOption(DIV_SYSTEM_SMS); - sysAddOption(DIV_SYSTEM_GB); - sysAddOption(DIV_SYSTEM_PCE); - sysAddOption(DIV_SYSTEM_NES); - sysAddOption(DIV_SYSTEM_C64_8580); - sysAddOption(DIV_SYSTEM_C64_6581); - sysAddOption(DIV_SYSTEM_YM2151); - sysAddOption(DIV_SYSTEM_SEGAPCM); - sysAddOption(DIV_SYSTEM_SEGAPCM_COMPAT); - sysAddOption(DIV_SYSTEM_YM2610); - sysAddOption(DIV_SYSTEM_YM2610_EXT); - sysAddOption(DIV_SYSTEM_YM2610_FULL); - sysAddOption(DIV_SYSTEM_YM2610_FULL_EXT); - sysAddOption(DIV_SYSTEM_YM2610B); - sysAddOption(DIV_SYSTEM_YM2610B_EXT); - sysAddOption(DIV_SYSTEM_AY8910); - sysAddOption(DIV_SYSTEM_AMIGA); - sysAddOption(DIV_SYSTEM_PCSPKR); - sysAddOption(DIV_SYSTEM_OPLL); - sysAddOption(DIV_SYSTEM_OPLL_DRUMS); - sysAddOption(DIV_SYSTEM_VRC7); - sysAddOption(DIV_SYSTEM_OPL); - sysAddOption(DIV_SYSTEM_OPL_DRUMS); - sysAddOption(DIV_SYSTEM_OPL2); - sysAddOption(DIV_SYSTEM_OPL2_DRUMS); - sysAddOption(DIV_SYSTEM_OPL3); - sysAddOption(DIV_SYSTEM_OPL3_DRUMS); - sysAddOption(DIV_SYSTEM_TIA); - sysAddOption(DIV_SYSTEM_SAA1099); - sysAddOption(DIV_SYSTEM_AY8930); - sysAddOption(DIV_SYSTEM_LYNX); - sysAddOption(DIV_SYSTEM_QSOUND); - sysAddOption(DIV_SYSTEM_X1_010); - sysAddOption(DIV_SYSTEM_SWAN); - sysAddOption(DIV_SYSTEM_VERA); - sysAddOption(DIV_SYSTEM_BUBSYS_WSG); - sysAddOption(DIV_SYSTEM_PET); + for (int j=0; availableSystems[j]; j++) { + sysAddOption((DivSystem)availableSystems[j]); + } ImGui::EndMenu(); } if (ImGui::BeginMenu("configure system...")) { for (int i=0; isong.systemLen; i++) { if (ImGui::TreeNode(fmt::sprintf("%d. %s##_SYSP%d",i+1,getSystemName(e->song.system[i]),i).c_str())) { - unsigned int flags=e->song.systemFlags[i]; - bool restart=settings.restartOnFlagChange; - bool sysPal=flags&1; - switch (e->song.system[i]) { - case DIV_SYSTEM_YM2612: - case DIV_SYSTEM_YM2612_EXT: { - if (ImGui::RadioButton("NTSC (7.67MHz)",(flags&3)==0)) { - e->setSysFlags(i,(flags&0x80000000)|0,restart); - updateWindowTitle(); - } - if (ImGui::RadioButton("PAL (7.61MHz)",(flags&3)==1)) { - e->setSysFlags(i,(flags&0x80000000)|1,restart); - updateWindowTitle(); - } - if (ImGui::RadioButton("FM Towns (8MHz)",(flags&3)==2)) { - e->setSysFlags(i,(flags&0x80000000)|2,restart); - updateWindowTitle(); - } - if (ImGui::RadioButton("AtGames Genesis (6.13MHz)",(flags&3)==3)) { - e->setSysFlags(i,(flags&0x80000000)|3,restart); - updateWindowTitle(); - } - bool ladder=flags&0x80000000; - if (ImGui::Checkbox("Enable DAC distortion",&ladder)) { - e->setSysFlags(i,(flags&(~0x80000000))|(ladder?0x80000000:0),restart); - updateWindowTitle(); - } - break; - } - case DIV_SYSTEM_SMS: { - ImGui::Text("Clock rate:"); - if (ImGui::RadioButton("NTSC (3.58MHz)",(flags&3)==0)) { - e->setSysFlags(i,(flags&(~3))|0,restart); - updateWindowTitle(); - } - if (ImGui::RadioButton("PAL (3.55MHz)",(flags&3)==1)) { - e->setSysFlags(i,(flags&(~3))|1,restart); - updateWindowTitle(); - } - if (ImGui::RadioButton("BBC Micro (4MHz)",(flags&3)==2)) { - e->setSysFlags(i,(flags&(~3))|2,restart); - updateWindowTitle(); - } - if (ImGui::RadioButton("Half NTSC (1.79MHz)",(flags&3)==3)) { - e->setSysFlags(i,(flags&(~3))|3,restart); - updateWindowTitle(); - } - ImGui::Text("Chip type:"); - if (ImGui::RadioButton("Sega VDP/Master System",((flags>>2)&3)==0)) { - e->setSysFlags(i,(flags&(~12))|0,restart); - updateWindowTitle(); - } - if (ImGui::RadioButton("TI SN76489",((flags>>2)&3)==1)) { - e->setSysFlags(i,(flags&(~12))|4,restart); - updateWindowTitle(); - } - if (ImGui::RadioButton("TI SN76489 with Atari-like short noise",((flags>>2)&3)==2)) { - e->setSysFlags(i,(flags&(~12))|8,restart); - updateWindowTitle(); - } - /*if (ImGui::RadioButton("Game Gear",(flags>>2)==3)) { - e->setSysFlags(i,(flags&3)|12); - }*/ - - bool noPhaseReset=flags&16; - if (ImGui::Checkbox("Disable noise period change phase reset",&noPhaseReset)) { - e->setSysFlags(i,(flags&(~16))|(noPhaseReset<<4),restart); - updateWindowTitle(); - } - break; - } - case DIV_SYSTEM_OPLL: - case DIV_SYSTEM_OPLL_DRUMS: - case DIV_SYSTEM_VRC7: { - ImGui::Text("Clock rate:"); - if (ImGui::RadioButton("NTSC (3.58MHz)",(flags&15)==0)) { - e->setSysFlags(i,(flags&(~15))|0,restart); - updateWindowTitle(); - } - if (ImGui::RadioButton("PAL (3.55MHz)",(flags&15)==1)) { - e->setSysFlags(i,(flags&(~15))|1,restart); - updateWindowTitle(); - } - if (ImGui::RadioButton("BBC Micro (4MHz)",(flags&15)==2)) { - e->setSysFlags(i,(flags&(~15))|2,restart); - updateWindowTitle(); - } - if (ImGui::RadioButton("Half NTSC (1.79MHz)",(flags&15)==3)) { - e->setSysFlags(i,(flags&(~15))|3,restart); - updateWindowTitle(); - } - if (e->song.system[i]!=DIV_SYSTEM_VRC7) { - ImGui::Text("Patch set:"); - if (ImGui::RadioButton("Yamaha YM2413",((flags>>4)&15)==0)) { - e->setSysFlags(i,(flags&(~0xf0))|0,restart); - updateWindowTitle(); - } - if (ImGui::RadioButton("Yamaha YMF281",((flags>>4)&15)==1)) { - e->setSysFlags(i,(flags&(~0xf0))|0x10,restart); - updateWindowTitle(); - } - if (ImGui::RadioButton("Yamaha YM2423",((flags>>4)&15)==2)) { - e->setSysFlags(i,(flags&(~0xf0))|0x20,restart); - updateWindowTitle(); - } - if (ImGui::RadioButton("Konami VRC7",((flags>>4)&15)==3)) { - e->setSysFlags(i,(flags&(~0xf0))|0x30,restart); - updateWindowTitle(); - } - } - break; - } - case DIV_SYSTEM_YM2151: - if (ImGui::RadioButton("NTSC/X16 (3.58MHz)",flags==0)) { - e->setSysFlags(i,0,restart); - updateWindowTitle(); - } - if (ImGui::RadioButton("PAL (3.55MHz)",flags==1)) { - e->setSysFlags(i,1,restart); - updateWindowTitle(); - } - if (ImGui::RadioButton("X1/X68000 (4MHz)",flags==2)) { - e->setSysFlags(i,2,restart); - updateWindowTitle(); - } - break; - case DIV_SYSTEM_NES: - if (ImGui::RadioButton("NTSC (1.79MHz)",flags==0)) { - e->setSysFlags(i,0,restart); - updateWindowTitle(); - } - if (ImGui::RadioButton("PAL (1.67MHz)",flags==1)) { - e->setSysFlags(i,1,restart); - updateWindowTitle(); - } - if (ImGui::RadioButton("Dendy (1.77MHz)",flags==2)) { - e->setSysFlags(i,2,restart); - updateWindowTitle(); - } - break; - case DIV_SYSTEM_C64_8580: - case DIV_SYSTEM_C64_6581: - if (ImGui::RadioButton("NTSC (1.02MHz)",flags==0)) { - e->setSysFlags(i,0,restart); - updateWindowTitle(); - } - if (ImGui::RadioButton("PAL (0.99MHz)",flags==1)) { - e->setSysFlags(i,1,restart); - updateWindowTitle(); - } - if (ImGui::RadioButton("SSI 2001 (0.89MHz)",flags==2)) { - e->setSysFlags(i,2,restart); - updateWindowTitle(); - } - break; - case DIV_SYSTEM_AY8910: - case DIV_SYSTEM_AY8930: { - ImGui::Text("Clock rate:"); - if (ImGui::RadioButton("1.79MHz (ZX Spectrum NTSC/MSX)",(flags&15)==0)) { - e->setSysFlags(i,(flags&(~15))|0,restart); - updateWindowTitle(); - } - if (ImGui::RadioButton("1.77MHz (ZX Spectrum)",(flags&15)==1)) { - e->setSysFlags(i,(flags&(~15))|1,restart); - updateWindowTitle(); - } - if (ImGui::RadioButton("1.75MHz (ZX Spectrum)",(flags&15)==2)) { - e->setSysFlags(i,(flags&(~15))|2,restart); - updateWindowTitle(); - } - if (ImGui::RadioButton("2MHz (Atari ST/Sharp X1)",(flags&15)==3)) { - e->setSysFlags(i,(flags&(~15))|3,restart); - updateWindowTitle(); - } - if (ImGui::RadioButton("1.5MHz (Vectrex)",(flags&15)==4)) { - e->setSysFlags(i,(flags&(~15))|4,restart); - updateWindowTitle(); - } - if (ImGui::RadioButton("1MHz (Amstrad CPC)",(flags&15)==5)) { - e->setSysFlags(i,(flags&(~15))|5,restart); - updateWindowTitle(); - } - if (ImGui::RadioButton("0.89MHz (Sunsoft 5B)",(flags&15)==6)) { - e->setSysFlags(i,(flags&(~15))|6,restart); - updateWindowTitle(); - } - if (ImGui::RadioButton("1.67MHz (?)",(flags&15)==7)) { - e->setSysFlags(i,(flags&(~15))|7,restart); - updateWindowTitle(); - } - if (ImGui::RadioButton("0.83MHz (Sunsoft 5B on PAL)",(flags&15)==8)) { - e->setSysFlags(i,(flags&(~15))|8,restart); - updateWindowTitle(); - } - if (ImGui::RadioButton("1.10MHz (Gamate/VIC-20 PAL)",(flags&15)==9)) { - e->setSysFlags(i,(flags&(~15))|9,restart); - updateWindowTitle(); - } - if (ImGui::RadioButton("2^21Hz (Game Boy)",(flags&15)==10)) { - e->setSysFlags(i,(flags&(~15))|10,restart); - updateWindowTitle(); - } - if (e->song.system[i]==DIV_SYSTEM_AY8910) { - ImGui::Text("Chip type:"); - if (ImGui::RadioButton("AY-3-8910",(flags&0x30)==0)) { - e->setSysFlags(i,(flags&(~0x30))|0,restart); - updateWindowTitle(); - } - if (ImGui::RadioButton("YM2149(F)",(flags&0x30)==16)) { - e->setSysFlags(i,(flags&(~0x30))|16,restart); - updateWindowTitle(); - } - if (ImGui::RadioButton("Sunsoft 5B",(flags&0x30)==32)) { - e->setSysFlags(i,(flags&(~0x30))|32,restart); - updateWindowTitle(); - } - if (ImGui::RadioButton("AY-3-8914",(flags&0x30)==48)) { - e->setSysFlags(i,(flags&(~0x30))|48,restart); - updateWindowTitle(); - } - } - bool stereo=flags&0x40; - ImGui::BeginDisabled((flags&0x30)==32); - if (ImGui::Checkbox("Stereo##_AY_STEREO",&stereo)) { - e->setSysFlags(i,(flags&(~0x40))|(stereo?0x40:0),restart); - updateWindowTitle(); - } - ImGui::EndDisabled(); - break; - } - case DIV_SYSTEM_SAA1099: - if (ImGui::RadioButton("SAM Coupé (8MHz)",flags==0)) { - e->setSysFlags(i,0,restart); - updateWindowTitle(); - } - if (ImGui::RadioButton("NTSC (7.15MHz)",flags==1)) { - e->setSysFlags(i,1,restart); - updateWindowTitle(); - } - if (ImGui::RadioButton("PAL (7.09MHz)",flags==2)) { - e->setSysFlags(i,2,restart); - updateWindowTitle(); - } - break; - case DIV_SYSTEM_AMIGA: { - ImGui::Text("Stereo separation:"); - int stereoSep=(flags>>8)&127; - if (ImGui::SliderInt("##StereoSep",&stereoSep,0,127)) { - if (stereoSep<0) stereoSep=0; - if (stereoSep>127) stereoSep=127; - e->setSysFlags(i,(flags&(~0x7f00))|((stereoSep&127)<<8),restart); - updateWindowTitle(); - } rightClickable - if (ImGui::RadioButton("Amiga 500 (OCS)",(flags&2)==0)) { - e->setSysFlags(i,flags&(~2),restart); - } - if (ImGui::RadioButton("Amiga 1200 (AGA)",(flags&2)==2)) { - e->setSysFlags(i,(flags&(~2))|2,restart); - } - sysPal=flags&1; - if (ImGui::Checkbox("PAL",&sysPal)) { - e->setSysFlags(i,(flags&(~1))|sysPal,restart); - updateWindowTitle(); - } - bool bypassLimits=flags&4; - if (ImGui::Checkbox("Bypass frequency limits",&bypassLimits)) { - e->setSysFlags(i,(flags&(~4))|(bypassLimits<<2),restart); - updateWindowTitle(); - } - break; - } - case DIV_SYSTEM_PCSPKR: { - ImGui::Text("Speaker type:"); - if (ImGui::RadioButton("Unfiltered",(flags&3)==0)) { - e->setSysFlags(i,(flags&(~3))|0,restart); - updateWindowTitle(); - } - if (ImGui::RadioButton("Cone",(flags&3)==1)) { - e->setSysFlags(i,(flags&(~3))|1,restart); - updateWindowTitle(); - } - if (ImGui::RadioButton("Piezo",(flags&3)==2)) { - e->setSysFlags(i,(flags&(~3))|2,restart); - updateWindowTitle(); - } - if (ImGui::RadioButton("Use system beeper (Linux only!)",(flags&3)==3)) { - e->setSysFlags(i,(flags&(~3))|3,restart); - updateWindowTitle(); - } - break; - } - case DIV_SYSTEM_QSOUND: { - ImGui::Text("Echo delay:"); - int echoBufSize=2725 - (flags & 4095); - if (ImGui::SliderInt("##EchoBufSize",&echoBufSize,0,2725)) { - if (echoBufSize<0) echoBufSize=0; - if (echoBufSize>2725) echoBufSize=2725; - e->setSysFlags(i,(flags & ~4095) | ((2725 - echoBufSize) & 4095),restart); - updateWindowTitle(); - } rightClickable - ImGui::Text("Echo feedback:"); - int echoFeedback=(flags>>12)&255; - if (ImGui::SliderInt("##EchoFeedback",&echoFeedback,0,255)) { - if (echoFeedback<0) echoFeedback=0; - if (echoFeedback>255) echoFeedback=255; - e->setSysFlags(i,(flags & ~(255 << 12)) | ((echoFeedback & 255) << 12),restart); - updateWindowTitle(); - } rightClickable - break; - } - case DIV_SYSTEM_X1_010: { - ImGui::Text("Clock rate:"); - if (ImGui::RadioButton("16MHz (Seta 1)",(flags&15)==0)) { - e->setSysFlags(i,(flags&(~15))|0,restart); - updateWindowTitle(); - } - if (ImGui::RadioButton("16.67MHz (Seta 2)",(flags&15)==1)) { - e->setSysFlags(i,(flags&(~15))|1,restart); - updateWindowTitle(); - } - bool x1_010Stereo=flags&16; - if (ImGui::Checkbox("Stereo",&x1_010Stereo)) { - e->setSysFlags(i,(flags&(~16))|(x1_010Stereo<<4),restart); - updateWindowTitle(); - } - break; - } - case DIV_SYSTEM_GB: - case DIV_SYSTEM_SWAN: - case DIV_SYSTEM_VERA: - case DIV_SYSTEM_BUBSYS_WSG: - case DIV_SYSTEM_YM2610: - case DIV_SYSTEM_YM2610_EXT: - case DIV_SYSTEM_YM2610_FULL: - case DIV_SYSTEM_YM2610_FULL_EXT: - case DIV_SYSTEM_YM2610B: - case DIV_SYSTEM_YM2610B_EXT: - case DIV_SYSTEM_YMU759: - case DIV_SYSTEM_PET: - ImGui::Text("nothing to configure"); - break; - default: - if (ImGui::Checkbox("PAL",&sysPal)) { - e->setSysFlags(i,sysPal,restart); - updateWindowTitle(); - } - break; - } + drawSysConf(i); ImGui::TreePop(); } } @@ -5662,45 +1967,9 @@ bool FurnaceGUI::loop() { if (ImGui::BeginMenu("change system...")) { for (int i=0; isong.systemLen; i++) { if (ImGui::BeginMenu(fmt::sprintf("%d. %s##_SYSC%d",i+1,getSystemName(e->song.system[i]),i).c_str())) { - sysChangeOption(i,DIV_SYSTEM_YM2612); - sysChangeOption(i,DIV_SYSTEM_YM2612_EXT); - sysChangeOption(i,DIV_SYSTEM_SMS); - sysChangeOption(i,DIV_SYSTEM_GB); - sysChangeOption(i,DIV_SYSTEM_PCE); - sysChangeOption(i,DIV_SYSTEM_NES); - sysChangeOption(i,DIV_SYSTEM_C64_8580); - sysChangeOption(i,DIV_SYSTEM_C64_6581); - sysChangeOption(i,DIV_SYSTEM_YM2151); - sysChangeOption(i,DIV_SYSTEM_SEGAPCM); - sysChangeOption(i,DIV_SYSTEM_SEGAPCM_COMPAT); - sysChangeOption(i,DIV_SYSTEM_YM2610); - sysChangeOption(i,DIV_SYSTEM_YM2610_EXT); - sysChangeOption(i,DIV_SYSTEM_YM2610_FULL); - sysChangeOption(i,DIV_SYSTEM_YM2610_FULL_EXT); - sysChangeOption(i,DIV_SYSTEM_YM2610B); - sysChangeOption(i,DIV_SYSTEM_YM2610B_EXT); - sysChangeOption(i,DIV_SYSTEM_AY8910); - sysChangeOption(i,DIV_SYSTEM_AMIGA); - sysChangeOption(i,DIV_SYSTEM_PCSPKR); - sysChangeOption(i,DIV_SYSTEM_OPLL); - sysChangeOption(i,DIV_SYSTEM_OPLL_DRUMS); - sysChangeOption(i,DIV_SYSTEM_VRC7); - sysChangeOption(i,DIV_SYSTEM_OPL); - sysChangeOption(i,DIV_SYSTEM_OPL_DRUMS); - sysChangeOption(i,DIV_SYSTEM_OPL2); - sysChangeOption(i,DIV_SYSTEM_OPL2_DRUMS); - sysChangeOption(i,DIV_SYSTEM_OPL3); - sysChangeOption(i,DIV_SYSTEM_OPL3_DRUMS); - sysChangeOption(i,DIV_SYSTEM_TIA); - sysChangeOption(i,DIV_SYSTEM_SAA1099); - sysChangeOption(i,DIV_SYSTEM_AY8930); - sysChangeOption(i,DIV_SYSTEM_LYNX); - sysChangeOption(i,DIV_SYSTEM_QSOUND); - sysChangeOption(i,DIV_SYSTEM_X1_010); - sysChangeOption(i,DIV_SYSTEM_SWAN); - sysChangeOption(i,DIV_SYSTEM_VERA); - sysChangeOption(i,DIV_SYSTEM_BUBSYS_WSG); - sysChangeOption(i,DIV_SYSTEM_PET); + for (int j=0; availableSystems[j]; j++) { + sysChangeOption(i,(DivSystem)availableSystems[j]); + } ImGui::EndMenu(); } } @@ -6204,379 +2473,6 @@ bool FurnaceGUI::loop() { return false; } -void FurnaceGUI::parseKeybinds() { - actionMapGlobal.clear(); - actionMapPat.clear(); - actionMapInsList.clear(); - actionMapWaveList.clear(); - actionMapSampleList.clear(); - actionMapOrders.clear(); - - for (int i=GUI_ACTION_GLOBAL_MIN+1; igetConfInt(#target,ImGui::GetColorU32(def))); - -#ifdef _WIN32 -#define SYSTEM_FONT_PATH_1 "C:\\Windows\\Fonts\\segoeui.ttf" -#define SYSTEM_FONT_PATH_2 "C:\\Windows\\Fonts\\tahoma.ttf" -// TODO! -#define SYSTEM_FONT_PATH_3 "C:\\Windows\\Fonts\\tahoma.ttf" -// TODO! -#define SYSTEM_PAT_FONT_PATH_1 "C:\\Windows\\Fonts\\consola.ttf" -#define SYSTEM_PAT_FONT_PATH_2 "C:\\Windows\\Fonts\\cour.ttf" -// GOOD LUCK WITH THIS ONE - UNTESTED -#define SYSTEM_PAT_FONT_PATH_3 "C:\\Windows\\Fonts\\vgasys.fon" -#elif defined(__APPLE__) -#define SYSTEM_FONT_PATH_1 "/System/Library/Fonts/SFAANS.ttf" -#define SYSTEM_FONT_PATH_2 "/System/Library/Fonts/Helvetica.ttc" -#define SYSTEM_FONT_PATH_3 "/System/Library/Fonts/Helvetica.dfont" -#define SYSTEM_PAT_FONT_PATH_1 "/System/Library/Fonts/SFNSMono.ttf" -#define SYSTEM_PAT_FONT_PATH_2 "/System/Library/Fonts/Courier New.ttf" -#define SYSTEM_PAT_FONT_PATH_3 "/System/Library/Fonts/Courier New.ttf" -#else -#define SYSTEM_FONT_PATH_1 "/usr/share/fonts/truetype/dejavu/DejaVuSans.ttf" -#define SYSTEM_FONT_PATH_2 "/usr/share/fonts/TTF/DejaVuSans.ttf" -#define SYSTEM_FONT_PATH_3 "/usr/share/fonts/ubuntu/Ubuntu-R.ttf" -#define SYSTEM_PAT_FONT_PATH_1 "/usr/share/fonts/truetype/dejavu/DejaVuSansMono.ttf" -#define SYSTEM_PAT_FONT_PATH_2 "/usr/share/fonts/TTF/DejaVuSansMono.ttf" -#define SYSTEM_PAT_FONT_PATH_3 "/usr/share/fonts/ubuntu/UbuntuMono-R.ttf" -#endif - -void FurnaceGUI::applyUISettings() { - ImGuiStyle sty; - if (settings.guiColorsBase) { - ImGui::StyleColorsLight(&sty); - } else { - ImGui::StyleColorsDark(&sty); - } - - if (settings.dpiScale>=0.5f) dpiScale=settings.dpiScale; - - GET_UI_COLOR(GUI_COLOR_BACKGROUND,ImVec4(0.1f,0.1f,0.1f,1.0f)); - GET_UI_COLOR(GUI_COLOR_FRAME_BACKGROUND,ImVec4(0.0f,0.0f,0.0f,0.85f)); - GET_UI_COLOR(GUI_COLOR_MODAL_BACKDROP,ImVec4(0.0f,0.0f,0.0f,0.55f)); - GET_UI_COLOR(GUI_COLOR_HEADER,ImVec4(0.2f,0.2f,0.2f,1.0f)); - GET_UI_COLOR(GUI_COLOR_TEXT,ImVec4(1.0f,1.0f,1.0f,1.0f)); - GET_UI_COLOR(GUI_COLOR_ACCENT_PRIMARY,ImVec4(0.06f,0.53f,0.98f,1.0f)); - GET_UI_COLOR(GUI_COLOR_ACCENT_SECONDARY,ImVec4(0.26f,0.59f,0.98f,1.0f)); - GET_UI_COLOR(GUI_COLOR_EDITING,ImVec4(0.2f,0.1f,0.1f,1.0f)); - GET_UI_COLOR(GUI_COLOR_SONG_LOOP,ImVec4(0.3f,0.5f,0.8f,0.4f)); - GET_UI_COLOR(GUI_COLOR_VOLMETER_LOW,ImVec4(0.2f,0.6f,0.2f,1.0f)); - GET_UI_COLOR(GUI_COLOR_VOLMETER_HIGH,ImVec4(1.0f,0.9f,0.2f,1.0f)); - GET_UI_COLOR(GUI_COLOR_VOLMETER_PEAK,ImVec4(1.0f,0.1f,0.1f,1.0f)); - GET_UI_COLOR(GUI_COLOR_MACRO_VOLUME,ImVec4(0.2f,1.0f,0.0f,1.0f)); - GET_UI_COLOR(GUI_COLOR_MACRO_PITCH,ImVec4(1.0f,0.8f,0.0f,1.0f)); - GET_UI_COLOR(GUI_COLOR_MACRO_OTHER,ImVec4(0.0f,0.9f,1.0f,1.0f)); - GET_UI_COLOR(GUI_COLOR_MACRO_WAVE,ImVec4(1.0f,0.4f,0.0f,1.0f)); - GET_UI_COLOR(GUI_COLOR_INSTR_FM,ImVec4(0.6f,0.9f,1.0f,1.0f)); - GET_UI_COLOR(GUI_COLOR_INSTR_STD,ImVec4(0.6f,1.0f,0.5f,1.0f)); - GET_UI_COLOR(GUI_COLOR_INSTR_GB,ImVec4(1.0f,1.0f,0.5f,1.0f)); - GET_UI_COLOR(GUI_COLOR_INSTR_C64,ImVec4(0.85f,0.8f,1.0f,1.0f)); - GET_UI_COLOR(GUI_COLOR_INSTR_AMIGA,ImVec4(1.0f,0.5f,0.5f,1.0f)); - GET_UI_COLOR(GUI_COLOR_INSTR_PCE,ImVec4(1.0f,0.8f,0.5f,1.0f)); - GET_UI_COLOR(GUI_COLOR_INSTR_AY,ImVec4(1.0f,0.5f,1.0f,1.0f)); - GET_UI_COLOR(GUI_COLOR_INSTR_AY8930,ImVec4(0.7f,0.5f,1.0f,1.0f)); - GET_UI_COLOR(GUI_COLOR_INSTR_TIA,ImVec4(1.0f,0.6f,0.4f,1.0f)); - GET_UI_COLOR(GUI_COLOR_INSTR_SAA1099,ImVec4(0.3f,0.3f,1.0f,1.0f)); - GET_UI_COLOR(GUI_COLOR_INSTR_VIC,ImVec4(0.2f,1.0f,0.6f,1.0f)); - GET_UI_COLOR(GUI_COLOR_INSTR_PET,ImVec4(1.0f,1.0f,0.8f,1.0f)); - GET_UI_COLOR(GUI_COLOR_INSTR_VRC6,ImVec4(1.0f,0.9f,0.5f,1.0f)); - GET_UI_COLOR(GUI_COLOR_INSTR_OPLL,ImVec4(0.6f,0.7f,1.0f,1.0f)); - GET_UI_COLOR(GUI_COLOR_INSTR_OPL,ImVec4(0.3f,1.0f,0.9f,1.0f)); - GET_UI_COLOR(GUI_COLOR_INSTR_FDS,ImVec4(0.8f,0.5f,1.0f,1.0f)); - GET_UI_COLOR(GUI_COLOR_INSTR_VBOY,ImVec4(1.0f,0.1f,0.1f,1.0f)); - GET_UI_COLOR(GUI_COLOR_INSTR_N163,ImVec4(1.0f,0.4f,0.1f,1.0f)); - GET_UI_COLOR(GUI_COLOR_INSTR_SCC,ImVec4(0.7f,1.0f,0.3f,1.0f)); - GET_UI_COLOR(GUI_COLOR_INSTR_OPZ,ImVec4(0.2f,0.8f,1.0f,1.0f)); - GET_UI_COLOR(GUI_COLOR_INSTR_POKEY,ImVec4(0.5f,1.0f,0.3f,1.0f)); - GET_UI_COLOR(GUI_COLOR_INSTR_BEEPER,ImVec4(0.0f,1.0f,0.0f,1.0f)); - GET_UI_COLOR(GUI_COLOR_INSTR_SWAN,ImVec4(0.3f,0.5f,1.0f,1.0f)); - GET_UI_COLOR(GUI_COLOR_INSTR_MIKEY,ImVec4(0.5f,1.0f,0.3f,1.0f)); - GET_UI_COLOR(GUI_COLOR_INSTR_VERA,ImVec4(0.4f,0.6f,1.0f,1.0f)); - GET_UI_COLOR(GUI_COLOR_INSTR_X1_010,ImVec4(0.3f,0.5f,1.0f,1.0f)); - GET_UI_COLOR(GUI_COLOR_INSTR_UNKNOWN,ImVec4(0.3f,0.3f,0.3f,1.0f)); - GET_UI_COLOR(GUI_COLOR_CHANNEL_FM,ImVec4(0.2f,0.8f,1.0f,1.0f)); - GET_UI_COLOR(GUI_COLOR_CHANNEL_PULSE,ImVec4(0.4f,1.0f,0.2f,1.0f)); - GET_UI_COLOR(GUI_COLOR_CHANNEL_NOISE,ImVec4(0.8f,0.8f,0.8f,1.0f)); - GET_UI_COLOR(GUI_COLOR_CHANNEL_PCM,ImVec4(1.0f,0.9f,0.2f,1.0f)); - GET_UI_COLOR(GUI_COLOR_CHANNEL_WAVE,ImVec4(1.0f,0.5f,0.2f,1.0f)); - GET_UI_COLOR(GUI_COLOR_CHANNEL_OP,ImVec4(0.2f,0.4f,1.0f,1.0f)); - GET_UI_COLOR(GUI_COLOR_CHANNEL_MUTED,ImVec4(0.5f,0.5f,0.5f,1.0f)); - GET_UI_COLOR(GUI_COLOR_PATTERN_CURSOR,ImVec4(0.1f,0.3f,0.5f,1.0f)); - GET_UI_COLOR(GUI_COLOR_PATTERN_CURSOR_HOVER,ImVec4(0.2f,0.4f,0.6f,1.0f)); - GET_UI_COLOR(GUI_COLOR_PATTERN_CURSOR_ACTIVE,ImVec4(0.2f,0.5f,0.7f,1.0f)); - GET_UI_COLOR(GUI_COLOR_PATTERN_SELECTION,ImVec4(0.15f,0.15f,0.2f,1.0f)); - GET_UI_COLOR(GUI_COLOR_PATTERN_SELECTION_HOVER,ImVec4(0.2f,0.2f,0.3f,1.0f)); - GET_UI_COLOR(GUI_COLOR_PATTERN_SELECTION_ACTIVE,ImVec4(0.4f,0.4f,0.5f,1.0f)); - GET_UI_COLOR(GUI_COLOR_PATTERN_HI_1,ImVec4(0.6f,0.6f,0.6f,0.2f)); - GET_UI_COLOR(GUI_COLOR_PATTERN_HI_2,ImVec4(0.5f,0.8f,1.0f,0.2f)); - GET_UI_COLOR(GUI_COLOR_PATTERN_ROW_INDEX,ImVec4(0.5f,0.8f,1.0f,1.0f)); - GET_UI_COLOR(GUI_COLOR_PATTERN_ACTIVE,ImVec4(1.0f,1.0f,1.0f,1.0f)); - GET_UI_COLOR(GUI_COLOR_PATTERN_INACTIVE,ImVec4(0.5f,0.5f,0.5f,1.0f)); - GET_UI_COLOR(GUI_COLOR_PATTERN_INS,ImVec4(0.4f,0.7f,1.0f,1.0f)); - GET_UI_COLOR(GUI_COLOR_PATTERN_VOLUME_MIN,ImVec4(0.0f,0.5f,0.0f,1.0f)); - GET_UI_COLOR(GUI_COLOR_PATTERN_VOLUME_HALF,ImVec4(0.0f,0.75f,0.0f,1.0f)); - GET_UI_COLOR(GUI_COLOR_PATTERN_VOLUME_MAX,ImVec4(0.0f,1.0f,0.0f,1.0f)); - GET_UI_COLOR(GUI_COLOR_PATTERN_EFFECT_INVALID,ImVec4(1.0f,0.0f,0.0f,1.0f)); - GET_UI_COLOR(GUI_COLOR_PATTERN_EFFECT_PITCH,ImVec4(1.0f,1.0f,0.0f,1.0f)); - GET_UI_COLOR(GUI_COLOR_PATTERN_EFFECT_VOLUME,ImVec4(0.0f,1.0f,0.0f,1.0f)); - GET_UI_COLOR(GUI_COLOR_PATTERN_EFFECT_PANNING,ImVec4(0.0f,1.0f,1.0f,1.0f)); - GET_UI_COLOR(GUI_COLOR_PATTERN_EFFECT_SONG,ImVec4(1.0f,0.0f,0.0f,1.0f)); - GET_UI_COLOR(GUI_COLOR_PATTERN_EFFECT_TIME,ImVec4(0.5f,0.0f,1.0f,1.0f)); - GET_UI_COLOR(GUI_COLOR_PATTERN_EFFECT_SPEED,ImVec4(1.0f,0.0f,1.0f,1.0f)); - GET_UI_COLOR(GUI_COLOR_PATTERN_EFFECT_SYS_PRIMARY,ImVec4(0.5f,1.0f,0.0f,1.0f)); - GET_UI_COLOR(GUI_COLOR_PATTERN_EFFECT_SYS_SECONDARY,ImVec4(0.0f,1.0f,0.5f,1.0f)); - GET_UI_COLOR(GUI_COLOR_PATTERN_EFFECT_MISC,ImVec4(0.3f,0.3f,1.0f,1.0f)); - GET_UI_COLOR(GUI_COLOR_EE_VALUE,ImVec4(0.0f,1.0f,1.0f,1.0f)); - GET_UI_COLOR(GUI_COLOR_PLAYBACK_STAT,ImVec4(0.6f,0.6f,0.6f,1.0f)); - - for (int i=0; i<64; i++) { - ImVec4 col1=uiColors[GUI_COLOR_PATTERN_VOLUME_MIN]; - ImVec4 col2=uiColors[GUI_COLOR_PATTERN_VOLUME_HALF]; - ImVec4 col3=uiColors[GUI_COLOR_PATTERN_VOLUME_MAX]; - volColors[i]=ImVec4(col1.x+((col2.x-col1.x)*float(i)/64.0f), - col1.y+((col2.y-col1.y)*float(i)/64.0f), - col1.z+((col2.z-col1.z)*float(i)/64.0f), - 1.0f); - volColors[i+64]=ImVec4(col2.x+((col3.x-col2.x)*float(i)/64.0f), - col2.y+((col3.y-col2.y)*float(i)/64.0f), - col2.z+((col3.z-col2.z)*float(i)/64.0f), - 1.0f); - } - - float hue, sat, val; - - ImVec4 primaryActive=uiColors[GUI_COLOR_ACCENT_PRIMARY]; - ImVec4 primaryHover, primary; - primaryHover.w=primaryActive.w; - primary.w=primaryActive.w; - ImGui::ColorConvertRGBtoHSV(primaryActive.x,primaryActive.y,primaryActive.z,hue,sat,val); - if (settings.guiColorsBase) { - primary=primaryActive; - ImGui::ColorConvertHSVtoRGB(hue,sat*0.9,val*0.9,primaryHover.x,primaryHover.y,primaryHover.z); - ImGui::ColorConvertHSVtoRGB(hue,sat,val*0.5,primaryActive.x,primaryActive.y,primaryActive.z); - } else { - ImGui::ColorConvertHSVtoRGB(hue,sat*0.9,val*0.5,primaryHover.x,primaryHover.y,primaryHover.z); - ImGui::ColorConvertHSVtoRGB(hue,sat*0.8,val*0.35,primary.x,primary.y,primary.z); - } - - ImVec4 secondaryActive=uiColors[GUI_COLOR_ACCENT_SECONDARY]; - ImVec4 secondaryHover, secondary, secondarySemiActive; - secondarySemiActive.w=secondaryActive.w; - secondaryHover.w=secondaryActive.w; - secondary.w=secondaryActive.w; - ImGui::ColorConvertRGBtoHSV(secondaryActive.x,secondaryActive.y,secondaryActive.z,hue,sat,val); - if (settings.guiColorsBase) { - secondary=secondaryActive; - ImGui::ColorConvertHSVtoRGB(hue,sat*0.9,val*0.7,secondarySemiActive.x,secondarySemiActive.y,secondarySemiActive.z); - ImGui::ColorConvertHSVtoRGB(hue,sat*0.9,val*0.9,secondaryHover.x,secondaryHover.y,secondaryHover.z); - ImGui::ColorConvertHSVtoRGB(hue,sat,val*0.5,secondaryActive.x,secondaryActive.y,secondaryActive.z); - } else { - ImGui::ColorConvertHSVtoRGB(hue,sat*0.9,val*0.75,secondarySemiActive.x,secondarySemiActive.y,secondarySemiActive.z); - ImGui::ColorConvertHSVtoRGB(hue,sat*0.9,val*0.5,secondaryHover.x,secondaryHover.y,secondaryHover.z); - ImGui::ColorConvertHSVtoRGB(hue,sat*0.9,val*0.25,secondary.x,secondary.y,secondary.z); - } - - - sty.Colors[ImGuiCol_WindowBg]=uiColors[GUI_COLOR_FRAME_BACKGROUND]; - sty.Colors[ImGuiCol_ModalWindowDimBg]=uiColors[GUI_COLOR_MODAL_BACKDROP]; - sty.Colors[ImGuiCol_Text]=uiColors[GUI_COLOR_TEXT]; - - sty.Colors[ImGuiCol_Button]=primary; - sty.Colors[ImGuiCol_ButtonHovered]=primaryHover; - sty.Colors[ImGuiCol_ButtonActive]=primaryActive; - sty.Colors[ImGuiCol_Tab]=primary; - sty.Colors[ImGuiCol_TabHovered]=secondaryHover; - sty.Colors[ImGuiCol_TabActive]=secondarySemiActive; - sty.Colors[ImGuiCol_TabUnfocused]=primary; - sty.Colors[ImGuiCol_TabUnfocusedActive]=primaryHover; - sty.Colors[ImGuiCol_Header]=secondary; - sty.Colors[ImGuiCol_HeaderHovered]=secondaryHover; - sty.Colors[ImGuiCol_HeaderActive]=secondaryActive; - sty.Colors[ImGuiCol_ResizeGrip]=secondary; - sty.Colors[ImGuiCol_ResizeGripHovered]=secondaryHover; - sty.Colors[ImGuiCol_ResizeGripActive]=secondaryActive; - sty.Colors[ImGuiCol_FrameBg]=secondary; - sty.Colors[ImGuiCol_FrameBgHovered]=secondaryHover; - sty.Colors[ImGuiCol_FrameBgActive]=secondaryActive; - sty.Colors[ImGuiCol_SliderGrab]=primaryActive; - sty.Colors[ImGuiCol_SliderGrabActive]=primaryActive; - sty.Colors[ImGuiCol_TitleBgActive]=primary; - sty.Colors[ImGuiCol_CheckMark]=primaryActive; - sty.Colors[ImGuiCol_TextSelectedBg]=secondaryHover; - sty.Colors[ImGuiCol_PlotHistogram]=uiColors[GUI_COLOR_MACRO_OTHER]; - sty.Colors[ImGuiCol_PlotHistogramHovered]=uiColors[GUI_COLOR_MACRO_OTHER]; - - if (settings.roundedWindows) sty.WindowRounding=8.0f; - if (settings.roundedButtons) { - sty.FrameRounding=6.0f; - sty.GrabRounding=6.0f; - } - if (settings.roundedMenus) sty.PopupRounding=8.0f; - - sty.ScaleAllSizes(dpiScale); - - ImGui::GetStyle()=sty; - - for (int i=0; i<256; i++) { - ImVec4& base=uiColors[GUI_COLOR_PATTERN_EFFECT_PITCH]; - pitchGrad[i]=ImGui::GetColorU32(ImVec4(base.x,base.y,base.z,((float)i/255.0f)*base.w)); - } - for (int i=0; i<256; i++) { - ImVec4& base=uiColors[GUI_COLOR_PATTERN_ACTIVE]; - noteGrad[i]=ImGui::GetColorU32(ImVec4(base.x,base.y,base.z,((float)i/255.0f)*base.w)); - } - for (int i=0; i<256; i++) { - ImVec4& base=uiColors[GUI_COLOR_PATTERN_EFFECT_PANNING]; - panGrad[i]=ImGui::GetColorU32(ImVec4(base.x,base.y,base.z,((float)i/255.0f)*base.w)); - } - for (int i=0; i<256; i++) { - ImVec4& base=uiColors[GUI_COLOR_PATTERN_INS]; - insGrad[i]=ImGui::GetColorU32(ImVec4(base.x,base.y,base.z,((float)i/255.0f)*base.w)); - } - for (int i=0; i<256; i++) { - ImVec4& base=volColors[i/2]; - volGrad[i]=ImGui::GetColorU32(ImVec4(base.x,base.y,base.z,((float)i/255.0f)*base.w)); - } - for (int i=0; i<256; i++) { - ImVec4& base=uiColors[GUI_COLOR_PATTERN_EFFECT_SYS_PRIMARY]; - sysCmd1Grad[i]=ImGui::GetColorU32(ImVec4(base.x,base.y,base.z,((float)i/255.0f)*base.w)); - } - for (int i=0; i<256; i++) { - ImVec4& base=uiColors[GUI_COLOR_PATTERN_EFFECT_SYS_SECONDARY]; - sysCmd2Grad[i]=ImGui::GetColorU32(ImVec4(base.x,base.y,base.z,((float)i/255.0f)*base.w)); - } - - // set to 800 for now due to problems with unifont - static const ImWchar loadEverything[]={0x20,0x800,0}; - - if (settings.mainFont<0 || settings.mainFont>6) settings.mainFont=0; - if (settings.patFont<0 || settings.patFont>6) settings.patFont=0; - - if (settings.mainFont==6 && settings.mainFontPath.empty()) { - logW("UI font path is empty! reverting to default font\n"); - settings.mainFont=0; - } - if (settings.patFont==6 && settings.patFontPath.empty()) { - logW("pattern font path is empty! reverting to default font\n"); - settings.patFont=0; - } - - if (settings.mainFont==6) { // custom font - if ((mainFont=ImGui::GetIO().Fonts->AddFontFromFileTTF(settings.mainFontPath.c_str(),e->getConfInt("mainFontSize",18)*dpiScale,NULL,loadEverything))==NULL) { - logW("could not load UI font! reverting to default font\n"); - settings.mainFont=0; - if ((mainFont=ImGui::GetIO().Fonts->AddFontFromMemoryCompressedTTF(builtinFont[settings.mainFont],builtinFontLen[settings.mainFont],e->getConfInt("mainFontSize",18)*dpiScale,NULL,loadEverything))==NULL) { - logE("could not load UI font! falling back to Proggy Clean.\n"); - mainFont=ImGui::GetIO().Fonts->AddFontDefault(); - } - } - } else if (settings.mainFont==5) { // system font - if ((mainFont=ImGui::GetIO().Fonts->AddFontFromFileTTF(SYSTEM_FONT_PATH_1,e->getConfInt("mainFontSize",18)*dpiScale,NULL,loadEverything))==NULL) { - if ((mainFont=ImGui::GetIO().Fonts->AddFontFromFileTTF(SYSTEM_FONT_PATH_2,e->getConfInt("mainFontSize",18)*dpiScale,NULL,loadEverything))==NULL) { - if ((mainFont=ImGui::GetIO().Fonts->AddFontFromFileTTF(SYSTEM_FONT_PATH_3,e->getConfInt("mainFontSize",18)*dpiScale,NULL,loadEverything))==NULL) { - logW("could not load UI font! reverting to default font\n"); - settings.mainFont=0; - if ((mainFont=ImGui::GetIO().Fonts->AddFontFromMemoryCompressedTTF(builtinFont[settings.mainFont],builtinFontLen[settings.mainFont],e->getConfInt("mainFontSize",18)*dpiScale,NULL,loadEverything))==NULL) { - logE("could not load UI font! falling back to Proggy Clean.\n"); - mainFont=ImGui::GetIO().Fonts->AddFontDefault(); - } - } - } - } - } else { - if ((mainFont=ImGui::GetIO().Fonts->AddFontFromMemoryCompressedTTF(builtinFont[settings.mainFont],builtinFontLen[settings.mainFont],e->getConfInt("mainFontSize",18)*dpiScale,NULL,loadEverything))==NULL) { - logE("could not load UI font! falling back to Proggy Clean.\n"); - mainFont=ImGui::GetIO().Fonts->AddFontDefault(); - } - } - - ImFontConfig fc; - fc.MergeMode=true; - fc.GlyphMinAdvanceX=e->getConfInt("iconSize",16)*dpiScale; - static const ImWchar fontRange[]={ICON_MIN_FA,ICON_MAX_FA,0}; - if ((iconFont=ImGui::GetIO().Fonts->AddFontFromMemoryCompressedTTF(iconFont_compressed_data,iconFont_compressed_size,e->getConfInt("iconSize",16)*dpiScale,&fc,fontRange))==NULL) { - logE("could not load icon font!\n"); - } - if (settings.mainFontSize==settings.patFontSize && settings.patFont<5 && builtinFontM[settings.patFont]==builtinFont[settings.mainFont]) { - logD("using main font for pat font.\n"); - patFont=mainFont; - } else { - if (settings.patFont==6) { // custom font - if ((patFont=ImGui::GetIO().Fonts->AddFontFromFileTTF(settings.patFontPath.c_str(),e->getConfInt("patFontSize",18)*dpiScale,NULL,loadEverything))==NULL) { - logW("could not load pattern font! reverting to default font\n"); - settings.patFont=0; - if ((patFont=ImGui::GetIO().Fonts->AddFontFromMemoryCompressedTTF(builtinFontM[settings.patFont],builtinFontMLen[settings.patFont],e->getConfInt("patFontSize",18)*dpiScale,NULL,loadEverything))==NULL) { - logE("could not load pattern font! falling back to Proggy Clean.\n"); - patFont=ImGui::GetIO().Fonts->AddFontDefault(); - } - } - } else if (settings.patFont==5) { // system font - if ((patFont=ImGui::GetIO().Fonts->AddFontFromFileTTF(SYSTEM_PAT_FONT_PATH_1,e->getConfInt("patFontSize",18)*dpiScale,NULL,loadEverything))==NULL) { - if ((patFont=ImGui::GetIO().Fonts->AddFontFromFileTTF(SYSTEM_PAT_FONT_PATH_2,e->getConfInt("patFontSize",18)*dpiScale,NULL,loadEverything))==NULL) { - if ((patFont=ImGui::GetIO().Fonts->AddFontFromFileTTF(SYSTEM_PAT_FONT_PATH_3,e->getConfInt("patFontSize",18)*dpiScale,NULL,loadEverything))==NULL) { - logW("could not load pattern font! reverting to default font\n"); - settings.patFont=0; - if ((patFont=ImGui::GetIO().Fonts->AddFontFromMemoryCompressedTTF(builtinFontM[settings.patFont],builtinFontMLen[settings.patFont],e->getConfInt("patFontSize",18)*dpiScale,NULL,loadEverything))==NULL) { - logE("could not load pattern font! falling back to Proggy Clean.\n"); - patFont=ImGui::GetIO().Fonts->AddFontDefault(); - } - } - } - } - } else { - if ((patFont=ImGui::GetIO().Fonts->AddFontFromMemoryCompressedTTF(builtinFontM[settings.patFont],builtinFontMLen[settings.patFont],e->getConfInt("patFontSize",18)*dpiScale,NULL,loadEverything))==NULL) { - logE("could not load pattern font!\n"); - patFont=ImGui::GetIO().Fonts->AddFontDefault(); - } - } - } - if ((bigFont=ImGui::GetIO().Fonts->AddFontFromMemoryCompressedTTF(font_plexSans_compressed_data,font_plexSans_compressed_size,40*dpiScale))==NULL) { - logE("could not load big UI font!\n"); - } - - if (fileDialog!=NULL) delete fileDialog; - fileDialog=new FurnaceGUIFileDialog(settings.sysFileDialog); -} - bool FurnaceGUI::init() { #ifndef __APPLE__ float dpiScaleF; @@ -6707,28 +2603,6 @@ bool FurnaceGUI::init() { ImGui::GetIO().IniFilename=finalLayoutPath; ImGui::LoadIniSettingsFromDisk(finalLayoutPath); - // TODO: allow changing these colors. - ImGuiFileDialog::Instance()->SetFileStyle(IGFD_FileStyleByTypeDir,"",ImVec4(0.0f,1.0f,1.0f,1.0f),ICON_FA_FOLDER_O); - ImGuiFileDialog::Instance()->SetFileStyle(IGFD_FileStyleByTypeFile,"",ImVec4(0.7f,0.7f,0.7f,1.0f),ICON_FA_FILE_O); - ImGuiFileDialog::Instance()->SetFileStyle(IGFD_FileStyleByExtension,".fur",ImVec4(0.5f,1.0f,0.5f,1.0f),ICON_FA_FILE); - ImGuiFileDialog::Instance()->SetFileStyle(IGFD_FileStyleByExtension,".fui",ImVec4(1.0f,0.5f,0.5f,1.0f),ICON_FA_FILE); - ImGuiFileDialog::Instance()->SetFileStyle(IGFD_FileStyleByExtension,".fuw",ImVec4(1.0f,0.75f,0.5f,1.0f),ICON_FA_FILE); - ImGuiFileDialog::Instance()->SetFileStyle(IGFD_FileStyleByExtension,".dmf",ImVec4(0.5f,1.0f,0.5f,1.0f),ICON_FA_FILE); - ImGuiFileDialog::Instance()->SetFileStyle(IGFD_FileStyleByExtension,".dmp",ImVec4(1.0f,0.5f,0.5f,1.0f),ICON_FA_FILE); - ImGuiFileDialog::Instance()->SetFileStyle(IGFD_FileStyleByExtension,".dmw",ImVec4(1.0f,0.75f,0.5f,1.0f),ICON_FA_FILE); - ImGuiFileDialog::Instance()->SetFileStyle(IGFD_FileStyleByExtension,".wav",ImVec4(1.0f,1.0f,0.5f,1.0f),ICON_FA_FILE_AUDIO_O); - ImGuiFileDialog::Instance()->SetFileStyle(IGFD_FileStyleByExtension,".vgm",ImVec4(1.0f,1.0f,0.5f,1.0f),ICON_FA_FILE_AUDIO_O); - ImGuiFileDialog::Instance()->SetFileStyle(IGFD_FileStyleByExtension,".ttf",ImVec4(0.3f,1.0f,0.6f,1.0f),ICON_FA_FONT); - ImGuiFileDialog::Instance()->SetFileStyle(IGFD_FileStyleByExtension,".otf",ImVec4(0.3f,1.0f,0.6f,1.0f),ICON_FA_FONT); - ImGuiFileDialog::Instance()->SetFileStyle(IGFD_FileStyleByExtension,".ttc",ImVec4(0.3f,1.0f,0.6f,1.0f),ICON_FA_FONT); - - ImGuiFileDialog::Instance()->SetFileStyle(IGFD_FileStyleByExtension,".tfi",ImVec4(1.0f,0.5f,0.5f,1.0f),ICON_FA_FILE); - ImGuiFileDialog::Instance()->SetFileStyle(IGFD_FileStyleByExtension,".vgi",ImVec4(1.0f,0.5f,0.5f,1.0f),ICON_FA_FILE); - ImGuiFileDialog::Instance()->SetFileStyle(IGFD_FileStyleByExtension,".s3i",ImVec4(1.0f,0.5f,0.5f,1.0f),ICON_FA_FILE); - ImGuiFileDialog::Instance()->SetFileStyle(IGFD_FileStyleByExtension,".sbi",ImVec4(1.0f,0.5f,0.5f,1.0f),ICON_FA_FILE); - ImGuiFileDialog::Instance()->SetFileStyle(IGFD_FileStyleByExtension,".fti",ImVec4(1.0f,0.5f,0.5f,1.0f),ICON_FA_FILE); - ImGuiFileDialog::Instance()->SetFileStyle(IGFD_FileStyleByExtension,".bti",ImVec4(1.0f,0.5f,0.5f,1.0f),ICON_FA_FILE); - updateWindowTitle(); for (int i=0; isong.masterVol,0,3,"%.2fx")) { + if (e->song.masterVol<0) e->song.masterVol=0; + if (e->song.masterVol>3) e->song.masterVol=3; + } rightClickable + for (int i=0; isong.systemLen; i++) { + snprintf(id,31,"MixS%d",i); + bool doInvert=e->song.systemVol[i]&128; + signed char vol=e->song.systemVol[i]&127; + ImGui::PushID(id); + ImGui::Text("%d. %s",i+1,getSystemName(e->song.system[i])); + ImGui::SameLine(ImGui::GetWindowWidth()-(82.0f*dpiScale)); + if (ImGui::Checkbox("Invert",&doInvert)) { + e->song.systemVol[i]^=128; + } + ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x-(50.0f*dpiScale)); + if (ImGui::SliderScalar("Volume",ImGuiDataType_S8,&vol,&_ZERO,&_ONE_HUNDRED_TWENTY_SEVEN)) { + e->song.systemVol[i]=(e->song.systemVol[i]&128)|vol; + } rightClickable + ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x-(50.0f*dpiScale)); + ImGui::SliderScalar("Panning",ImGuiDataType_S8,&e->song.systemPan[i],&_MINUS_ONE_HUNDRED_TWENTY_SEVEN,&_ONE_HUNDRED_TWENTY_SEVEN); rightClickable + + ImGui::PopID(); + } + } + if (ImGui::IsWindowFocused(ImGuiFocusedFlags_ChildWindows)) curWindow=GUI_WINDOW_MIXER; + ImGui::End(); +} \ No newline at end of file diff --git a/src/gui/newSong.cpp b/src/gui/newSong.cpp new file mode 100644 index 00000000..d6188b3e --- /dev/null +++ b/src/gui/newSong.cpp @@ -0,0 +1,90 @@ +/** + * Furnace Tracker - multi-system chiptune tracker + * Copyright (C) 2021-2022 tildearrow and contributors + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include "gui.h" + +void FurnaceGUI::drawNewSong() { + bool accepted=false; + + ImGui::PushFont(bigFont); + ImGui::SetCursorPosX((ImGui::GetContentRegionAvail().x-ImGui::CalcTextSize("Choose a System!").x)*0.5); + ImGui::Text("Choose a System!"); + ImGui::PopFont(); + + if (ImGui::BeginTable("sysPicker",2)) { + ImGui::TableSetupColumn("c0",ImGuiTableColumnFlags_WidthFixed,0.0f); + ImGui::TableSetupColumn("c1",ImGuiTableColumnFlags_WidthStretch,0.0f); + + ImGui::TableNextRow(ImGuiTableRowFlags_Headers); + ImGui::TableNextColumn(); + ImGui::Text("Categories"); + ImGui::TableNextColumn(); + ImGui::Text("Systems"); + + ImGui::TableNextRow(); + + // CATEGORIES + ImGui::TableNextColumn(); + int index=0; + for (FurnaceGUISysCategory& i: sysCategories) { + if (ImGui::Selectable(i.name,newSongCategory==index,ImGuiSelectableFlags_DontClosePopups)) { \ + newSongCategory=index; + } + index++; + } + + // SYSTEMS + ImGui::TableNextColumn(); + if (ImGui::BeginTable("Systems",1,ImGuiTableFlags_BordersInnerV|ImGuiTableFlags_ScrollY)) { + for (FurnaceGUISysDef& i: sysCategories[newSongCategory].systems) { + ImGui::TableNextRow(); + ImGui::TableNextColumn(); + if (ImGui::Selectable(i.name,false,ImGuiSelectableFlags_DontClosePopups)) { + nextDesc=i.definition.data(); + accepted=true; + } + } + ImGui::EndTable(); + } + + ImGui::EndTable(); + } + + if (ImGui::Button("Cancel")) { + ImGui::CloseCurrentPopup(); + } + + if (accepted) { + e->createNew(nextDesc); + undoHist.clear(); + redoHist.clear(); + curFileName=""; + modified=false; + curNibble=false; + orderNibble=false; + orderCursor=-1; + samplePos=0; + updateSampleTex=true; + selStart=SelectionPoint(); + selEnd=SelectionPoint(); + cursor=SelectionPoint(); + updateWindowTitle(); + ImGui::CloseCurrentPopup(); + } +} \ No newline at end of file diff --git a/src/gui/osc.cpp b/src/gui/osc.cpp new file mode 100644 index 00000000..0bdcdf8a --- /dev/null +++ b/src/gui/osc.cpp @@ -0,0 +1,47 @@ +/** + * Furnace Tracker - multi-system chiptune tracker + * Copyright (C) 2021-2022 tildearrow and contributors + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include "gui.h" + +void FurnaceGUI::drawOsc() { + if (nextWindow==GUI_WINDOW_OSCILLOSCOPE) { + oscOpen=true; + ImGui::SetNextWindowFocus(); + nextWindow=GUI_WINDOW_NOTHING; + } + if (!oscOpen) return; + ImGui::SetNextWindowSizeConstraints(ImVec2(64.0f*dpiScale,32.0f*dpiScale),ImVec2(scrW*dpiScale,scrH*dpiScale)); + ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding,ImVec2(0,0)); + ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing,ImVec2(0,0)); + ImGui::PushStyleVar(ImGuiStyleVar_ItemInnerSpacing,ImVec2(0,0)); + if (ImGui::Begin("Oscilloscope",&oscOpen)) { + float values[512]; + for (int i=0; i<512; i++) { + int pos=i*e->oscSize/512; + values[i]=(e->oscBuf[0][pos]+e->oscBuf[1][pos])*0.5f; + } + //ImGui::SetCursorPos(ImVec2(0,0)); + ImGui::BeginDisabled(); + ImGui::PlotLines("##SingleOsc",values,512,0,NULL,-1.0f,1.0f,ImGui::GetContentRegionAvail()); + ImGui::EndDisabled(); + } + ImGui::PopStyleVar(3); + if (ImGui::IsWindowFocused(ImGuiFocusedFlags_ChildWindows)) curWindow=GUI_WINDOW_OSCILLOSCOPE; + ImGui::End(); +} \ No newline at end of file diff --git a/src/gui/piano.cpp b/src/gui/piano.cpp new file mode 100644 index 00000000..d3a3d35e --- /dev/null +++ b/src/gui/piano.cpp @@ -0,0 +1,46 @@ +/** + * Furnace Tracker - multi-system chiptune tracker + * Copyright (C) 2021-2022 tildearrow and contributors + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include "gui.h" +#include "guiConst.h" + +void FurnaceGUI::drawPiano() { + if (nextWindow==GUI_WINDOW_PIANO) { + pianoOpen=true; + ImGui::SetNextWindowFocus(); + nextWindow=GUI_WINDOW_NOTHING; + } + if (!pianoOpen) return; + if (ImGui::Begin("Piano",&pianoOpen)) { + for (int i=0; igetTotalChannelCount(); i++) { + DivChannelState* cs=e->getChanState(i); + if (cs->keyOn) { + const char* noteName=NULL; + if (cs->note<-60 || cs->note>120) { + noteName="???"; + } else { + noteName=noteNames[cs->note+60]; + } + ImGui::Text("%d: %s",i,noteName); + } + } + } + if (ImGui::IsWindowFocused(ImGuiFocusedFlags_ChildWindows)) curWindow=GUI_WINDOW_PIANO; + ImGui::End(); +} \ No newline at end of file diff --git a/src/gui/presets.cpp b/src/gui/presets.cpp new file mode 100644 index 00000000..0ab7c2ba --- /dev/null +++ b/src/gui/presets.cpp @@ -0,0 +1,639 @@ +/** + * Furnace Tracker - multi-system chiptune tracker + * Copyright (C) 2021-2022 tildearrow and contributors + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include "gui.h" + +// add system configurations here. +// every entry is written in the following format: +// cat.systems.push_back(FurnaceGUISysDef( +// "System Name", { +// DIV_SYSTEM_???, Volume, Panning, Flags, +// DIV_SYSTEM_???, Volume, Panning, Flags, +// ... +// 0 +// } +// )); + +void FurnaceGUI::initSystemPresets() { + FurnaceGUISysCategory cat; + + cat=FurnaceGUISysCategory("FM"); + cat.systems.push_back(FurnaceGUISysDef( + "Yamaha YM2612", { + DIV_SYSTEM_YM2612, 64, 0, 0, + 0 + } + )); + cat.systems.push_back(FurnaceGUISysDef( + "Yamaha YM2612 (extended channel 3)", { + DIV_SYSTEM_YM2612_EXT, 64, 0, 0, + 0 + } + )); + cat.systems.push_back(FurnaceGUISysDef( + "Yamaha YM2151", { + DIV_SYSTEM_YM2151, 64, 0, 0, + 0 + } + )); + cat.systems.push_back(FurnaceGUISysDef( + "Yamaha YM2610", { + DIV_SYSTEM_YM2610_FULL, 64, 0, 0, + 0 + } + )); + cat.systems.push_back(FurnaceGUISysDef( + "Yamaha YM2610 (extended channel 2)", { + DIV_SYSTEM_YM2610_FULL_EXT, 64, 0, 0, + 0 + } + )); + cat.systems.push_back(FurnaceGUISysDef( + "Yamaha YM2610B", { + DIV_SYSTEM_YM2610B, 64, 0, 0, + 0 + } + )); + cat.systems.push_back(FurnaceGUISysDef( + "Yamaha YM2610B (extended channel 3)", { + DIV_SYSTEM_YM2610B_EXT, 64, 0, 0, + 0 + } + )); + cat.systems.push_back(FurnaceGUISysDef( + "Yamaha YM2413", { + DIV_SYSTEM_OPLL, 64, 0, 0, + 0 + } + )); + cat.systems.push_back(FurnaceGUISysDef( + "Yamaha YM2413 (drums mode)", { + DIV_SYSTEM_OPLL_DRUMS, 64, 0, 0, + 0 + } + )); + sysCategories.push_back(cat); + + cat=FurnaceGUISysCategory("Square"); + cat.systems.push_back(FurnaceGUISysDef( + "TI SN76489", { + DIV_SYSTEM_SMS, 64, 0, 0, + 0 + } + )); + cat.systems.push_back(FurnaceGUISysDef( + "AY-3-8910", { + DIV_SYSTEM_AY8910, 64, 0, 0, + 0 + } + )); + cat.systems.push_back(FurnaceGUISysDef( + "Philips SAA1099", { + DIV_SYSTEM_SAA1099, 64, 0, 0, + 0 + } + )); + sysCategories.push_back(cat); + + cat=FurnaceGUISysCategory("Sample"); + cat.systems.push_back(FurnaceGUISysDef( + "Amiga", { + DIV_SYSTEM_AMIGA, 64, 0, 0, + 0 + } + )); + cat.systems.push_back(FurnaceGUISysDef( + "SegaPCM", { + DIV_SYSTEM_SEGAPCM, 64, 0, 0, + 0 + } + )); + cat.systems.push_back(FurnaceGUISysDef( + "Capcom QSound", { + DIV_SYSTEM_QSOUND, 64, 0, 0, + 0 + } + )); + cat.systems.push_back(FurnaceGUISysDef( + "Seta/Allumer X1-010", { + DIV_SYSTEM_X1_010, 64, 0, 0, + 0 + } + )); + sysCategories.push_back(cat); + + cat=FurnaceGUISysCategory("Game consoles"); + cat.systems.push_back(FurnaceGUISysDef( + "Sega Genesis", { + DIV_SYSTEM_YM2612, 64, 0, 0, + DIV_SYSTEM_SMS, 24, 0, 0, + 0 + } + )); + cat.systems.push_back(FurnaceGUISysDef( + "Sega Genesis (extended channel 3)", { + DIV_SYSTEM_YM2612_EXT, 64, 0, 0, + DIV_SYSTEM_SMS, 24, 0, 0, + 0 + } + )); + cat.systems.push_back(FurnaceGUISysDef( + "Sega Master System", { + DIV_SYSTEM_SMS, 64, 0, 0, + 0 + } + )); + cat.systems.push_back(FurnaceGUISysDef( + "Sega Master System (with FM expansion)", { + DIV_SYSTEM_SMS, 64, 0, 0, + DIV_SYSTEM_OPLL, 64, 0, 0, + 0 + } + )); + cat.systems.push_back(FurnaceGUISysDef( + "Sega Master System (with FM expansion in drums mode)", { + DIV_SYSTEM_SMS, 64, 0, 0, + DIV_SYSTEM_OPLL_DRUMS, 64, 0, 0, + 0 + } + )); + cat.systems.push_back(FurnaceGUISysDef( + "Game Boy", { + DIV_SYSTEM_GB, 64, 0, 0, + 0 + } + )); + cat.systems.push_back(FurnaceGUISysDef( + "NEC PC Engine/TurboGrafx-16", { + DIV_SYSTEM_PCE, 64, 0, 0, + 0 + } + )); + cat.systems.push_back(FurnaceGUISysDef( + "NES", { + DIV_SYSTEM_NES, 64, 0, 0, + 0 + } + )); + cat.systems.push_back(FurnaceGUISysDef( + "NES with Konami VRC7", { + DIV_SYSTEM_NES, 64, 0, 0, + DIV_SYSTEM_VRC7, 64, 0, 0, + 0 + } + )); + cat.systems.push_back(FurnaceGUISysDef( + "NES with Sunsoft 5B", { + DIV_SYSTEM_NES, 64, 0, 0, + DIV_SYSTEM_AY8910, 64, 0, 38, + 0 + } + )); + cat.systems.push_back(FurnaceGUISysDef( + "Mattel Intellivision", { + DIV_SYSTEM_AY8910, 64, 0, 48, + 0 + } + )); + cat.systems.push_back(FurnaceGUISysDef( + "Vectrex", { + DIV_SYSTEM_AY8910, 64, 0, 4, + 0 + } + )); + cat.systems.push_back(FurnaceGUISysDef( + "Neo Geo AES", { + DIV_SYSTEM_YM2610_FULL, 64, 0, 0, + 0 + } + )); + cat.systems.push_back(FurnaceGUISysDef( + "Neo Geo AES (extended channel 2)", { + DIV_SYSTEM_YM2610_FULL_EXT, 64, 0, 0, + 0 + } + )); + cat.systems.push_back(FurnaceGUISysDef( + "Atari 2600/7800", { + DIV_SYSTEM_TIA, 64, 0, 0, + 0 + } + )); + cat.systems.push_back(FurnaceGUISysDef( + "Atari Lynx", { + DIV_SYSTEM_LYNX, 64, 0, 0, + 0 + } + )); + cat.systems.push_back(FurnaceGUISysDef( + "WonderSwan", { + DIV_SYSTEM_SWAN, 64, 0, 0, + 0 + } + )); + cat.systems.push_back(FurnaceGUISysDef( + "Gamate", { + DIV_SYSTEM_AY8910, 64, 0, 73, + 0 + } + )); + sysCategories.push_back(cat); + + cat=FurnaceGUISysCategory("Computers"); + cat.systems.push_back(FurnaceGUISysDef( + "Commodore PET", { + DIV_SYSTEM_PET, 64, 0, 0, + 0 + } + )); + cat.systems.push_back(FurnaceGUISysDef( + "Commodore VIC-20", { + DIV_SYSTEM_VIC20, 64, 0, 1, + 0 + } + )); + cat.systems.push_back(FurnaceGUISysDef( + "Commodore 64 (6581 SID)", { + DIV_SYSTEM_C64_6581, 64, 0, 1, + 0 + } + )); + cat.systems.push_back(FurnaceGUISysDef( + "Commodore 64 (8580 SID)", { + DIV_SYSTEM_C64_8580, 64, 0, 1, + 0 + } + )); + /* + cat.systems.push_back(FurnaceGUISysDef( + "Commodore 64 (6581 SID + Sound Expander)", { + DIV_SYSTEM_OPL, 64, 0, 0, + DIV_SYSTEM_C64_6581, 64, 0, 1, + 0 + } + )); + cat.systems.push_back(FurnaceGUISysDef( + "Commodore 64 (6581 SID + Sound Expander with drums mode)", { + DIV_SYSTEM_OPL_DRUMS, 64, 0, 0, + DIV_SYSTEM_C64_6581, 64, 0, 1, + 0 + } + )); + cat.systems.push_back(FurnaceGUISysDef( + "Commodore 64 (8580 SID + Sound Expander)", { + DIV_SYSTEM_OPL, 64, 0, 0, + DIV_SYSTEM_C64_8580, 64, 0, 1, + 0 + } + )); + cat.systems.push_back(FurnaceGUISysDef( + "Commodore 64 (8580 SID + Sound Expander with drums mode)", { + DIV_SYSTEM_OPL_DRUMS, 64, 0, 0, + DIV_SYSTEM_C64_8580, 64, 0, 1, + 0 + } + ));*/ + cat.systems.push_back(FurnaceGUISysDef( + "Amiga", { + DIV_SYSTEM_AMIGA, 64, 0, 0, + 0 + } + )); + cat.systems.push_back(FurnaceGUISysDef( + "MSX", { + DIV_SYSTEM_AY8910, 64, 0, 16, + 0 + } + )); + cat.systems.push_back(FurnaceGUISysDef( + "MSX + SFG-01", { + DIV_SYSTEM_YM2151, 64, 0, 0, + DIV_SYSTEM_AY8910, 64, 0, 16, + 0 + } + )); + cat.systems.push_back(FurnaceGUISysDef( + "MSX + MSX-MUSIC", { + DIV_SYSTEM_OPLL, 64, 0, 0, + DIV_SYSTEM_AY8910, 64, 0, 16, + 0 + } + )); + cat.systems.push_back(FurnaceGUISysDef( + "MSX + MSX-MUSIC (drums mode)", { + DIV_SYSTEM_OPLL_DRUMS, 64, 0, 0, + DIV_SYSTEM_AY8910, 64, 0, 16, + 0 + } + )); + cat.systems.push_back(FurnaceGUISysDef( + "ZX Spectrum (48K)", { + DIV_SYSTEM_AY8910, 64, 0, 2, + 0 + } + )); + cat.systems.push_back(FurnaceGUISysDef( + "ZX Spectrum (128K)", { + DIV_SYSTEM_AY8910, 64, 0, 1, + 0 + } + )); + cat.systems.push_back(FurnaceGUISysDef( + "Amstrad CPC", { + DIV_SYSTEM_AY8910, 64, 0, 5, + 0 + } + )); + cat.systems.push_back(FurnaceGUISysDef( + "SAM Coupé", { + DIV_SYSTEM_SAA1099, 64, 0, 0, + 0 + } + )); + cat.systems.push_back(FurnaceGUISysDef( + "BBC Micro", { + DIV_SYSTEM_SMS, 64, 0, 6, + 0 + } + )); + cat.systems.push_back(FurnaceGUISysDef( + "PC (barebones)", { + DIV_SYSTEM_PCSPKR, 64, 0, 0, + 0 + } + )); + cat.systems.push_back(FurnaceGUISysDef( + "PC + Covox Sound Master", { + DIV_SYSTEM_AY8930, 64, 0, 3, + DIV_SYSTEM_PCSPKR, 64, 0, 0, + 0 + } + )); + cat.systems.push_back(FurnaceGUISysDef( + "PC + SSI 2001", { + DIV_SYSTEM_C64_6581, 64, 0, 2, + DIV_SYSTEM_PCSPKR, 64, 0, 0, + 0 + } + )); + cat.systems.push_back(FurnaceGUISysDef( + "PC + Game Blaster", { + DIV_SYSTEM_SAA1099, 64, -127, 1, + DIV_SYSTEM_SAA1099, 64, 127, 1, + DIV_SYSTEM_PCSPKR, 64, 0, 0, + 0 + } + )); + cat.systems.push_back(FurnaceGUISysDef( + "PC + AdLib/Sound Blaster", { + DIV_SYSTEM_OPL2, 64, 0, 0, + DIV_SYSTEM_PCSPKR, 64, 0, 0, + 0 + } + )); + cat.systems.push_back(FurnaceGUISysDef( + "PC + AdLib/Sound Blaster (drums mode)", { + DIV_SYSTEM_OPL2_DRUMS, 64, 0, 0, + DIV_SYSTEM_PCSPKR, 64, 0, 0, + 0 + } + )); + cat.systems.push_back(FurnaceGUISysDef( + "PC + Sound Blaster w/Game Blaster Compatible", { + DIV_SYSTEM_OPL2, 64, 0, 0, + DIV_SYSTEM_SAA1099, 64, -127, 1, + DIV_SYSTEM_SAA1099, 64, 127, 1, + DIV_SYSTEM_PCSPKR, 64, 0, 0, + 0 + } + )); + cat.systems.push_back(FurnaceGUISysDef( + "PC + Sound Blaster w/Game Blaster Compatible (drums mode)", { + DIV_SYSTEM_OPL2_DRUMS, 64, 0, 0, + DIV_SYSTEM_SAA1099, 64, -127, 1, + DIV_SYSTEM_SAA1099, 64, 127, 1, + DIV_SYSTEM_PCSPKR, 64, 0, 0, + 0 + } + )); + cat.systems.push_back(FurnaceGUISysDef( + "PC + Sound Blaster Pro 2", { + DIV_SYSTEM_OPL3, 64, 0, 0, + DIV_SYSTEM_PCSPKR, 64, 0, 0, + 0 + } + )); + cat.systems.push_back(FurnaceGUISysDef( + "PC + Sound Blaster Pro 2 (drums mode)", { + DIV_SYSTEM_OPL3_DRUMS, 64, 0, 0, + DIV_SYSTEM_PCSPKR, 64, 0, 0, + 0 + } + )); + cat.systems.push_back(FurnaceGUISysDef( + "Sharp X1", { + DIV_SYSTEM_AY8910, 64, 0, 3, + 0 + } + )); + cat.systems.push_back(FurnaceGUISysDef( + "Sharp X1 + FM Addon", { + DIV_SYSTEM_AY8910, 64, 0, 3, + DIV_SYSTEM_YM2151, 64, 0, 2, + 0 + } + )); + /* + cat.systems.push_back(FurnaceGUISysDef( + "Sharp X68000", { + DIV_SYSTEM_YM2151, 64, 0, 2, + DIV_SYSTEM_MSM6258, 64, 0, 0, + 0 + } + ));*/ + cat.systems.push_back(FurnaceGUISysDef( + "Commander X16", { + DIV_SYSTEM_YM2151, 64, 0, 0, + DIV_SYSTEM_VERA, 64, 0, 0, + 0 + } + )); + sysCategories.push_back(cat); + + cat=FurnaceGUISysCategory("Arcade systems"); + cat.systems.push_back(FurnaceGUISysDef( + "Bally Midway MCR", { + DIV_SYSTEM_AY8910, 64, 0, 0, + DIV_SYSTEM_AY8910, 64, 0, 0, + 0 + } + )); + cat.systems.push_back(FurnaceGUISysDef( + "Sega Kyugo", { + DIV_SYSTEM_AY8910, 64, 0, 4, + DIV_SYSTEM_AY8910, 64, 0, 4, + 0 + } + )); + cat.systems.push_back(FurnaceGUISysDef( + "Sega OutRun/X Board", { + DIV_SYSTEM_YM2151, 64, 0, 2, + DIV_SYSTEM_SEGAPCM, 64, 0, 0, + 0 + } + )); + cat.systems.push_back(FurnaceGUISysDef( + "Neo Geo MVS", { + DIV_SYSTEM_YM2610_FULL, 64, 0, 0, + 0 + } + )); + cat.systems.push_back(FurnaceGUISysDef( + "Neo Geo MVS (extended channel 2)", { + DIV_SYSTEM_YM2610_FULL_EXT, 64, 0, 0, + 0 + } + )); + cat.systems.push_back(FurnaceGUISysDef( + "Taito Arcade", { + DIV_SYSTEM_YM2610B, 64, 0, 0, + 0 + } + )); + cat.systems.push_back(FurnaceGUISysDef( + "Taito Arcade (extended channel 3)", { + DIV_SYSTEM_YM2610B_EXT, 64, 0, 0, + 0 + } + )); + cat.systems.push_back(FurnaceGUISysDef( + "Capcom CPS-2 (QSound)", { + DIV_SYSTEM_QSOUND, 64, 0, 0, + 0 + } + )); + cat.systems.push_back(FurnaceGUISysDef( + "Seta 1", { + DIV_SYSTEM_X1_010, 64, 0, 0, + 0 + } + )); + cat.systems.push_back(FurnaceGUISysDef( + "Seta 2", { + DIV_SYSTEM_X1_010, 64, 0, 1, + 0 + } + )); + cat.systems.push_back(FurnaceGUISysDef( + "Konami Bubble System", { + DIV_SYSTEM_AY8910, 64, 0, 0, + DIV_SYSTEM_AY8910, 64, 0, 0, + DIV_SYSTEM_BUBSYS_WSG, 64, 0, 0, + // VLM5030 exists but not used for music at all + 0 + } + )); + sysCategories.push_back(cat); + + cat=FurnaceGUISysCategory("DefleMask-compatible"); + cat.systems.push_back(FurnaceGUISysDef( + "Sega Genesis", { + DIV_SYSTEM_YM2612, 64, 0, 0, + DIV_SYSTEM_SMS, 24, 0, 0, + 0 + } + )); + cat.systems.push_back(FurnaceGUISysDef( + "Sega Genesis (extended channel 3)", { + DIV_SYSTEM_YM2612_EXT, 64, 0, 0, + DIV_SYSTEM_SMS, 24, 0, 0, + 0 + } + )); + cat.systems.push_back(FurnaceGUISysDef( + "Sega Master System", { + DIV_SYSTEM_SMS, 64, 0, 0, + 0 + } + )); + cat.systems.push_back(FurnaceGUISysDef( + "Sega Master System (with FM expansion)", { + DIV_SYSTEM_SMS, 64, 0, 0, + DIV_SYSTEM_OPLL, 64, 0, 0, + 0 + } + )); + cat.systems.push_back(FurnaceGUISysDef( + "Game Boy", { + DIV_SYSTEM_GB, 64, 0, 0, + 0 + } + )); + cat.systems.push_back(FurnaceGUISysDef( + "NEC PC Engine/TurboGrafx-16", { + DIV_SYSTEM_PCE, 64, 0, 0, + 0 + } + )); + cat.systems.push_back(FurnaceGUISysDef( + "NES", { + DIV_SYSTEM_NES, 64, 0, 0, + 0 + } + )); + cat.systems.push_back(FurnaceGUISysDef( + "NES with Konami VRC7", { + DIV_SYSTEM_NES, 64, 0, 0, + DIV_SYSTEM_VRC7, 64, 0, 0, + 0 + } + )); + cat.systems.push_back(FurnaceGUISysDef( + "Commodore 64 (6581 SID)", { + DIV_SYSTEM_C64_6581, 64, 0, 1, + 0 + } + )); + cat.systems.push_back(FurnaceGUISysDef( + "Commodore 64 (8580 SID)", { + DIV_SYSTEM_C64_8580, 64, 0, 1, + 0 + } + )); + cat.systems.push_back(FurnaceGUISysDef( + "Arcade (YM2151 and SegaPCM)", { + DIV_SYSTEM_YM2151, 64, 0, 0, + DIV_SYSTEM_SEGAPCM_COMPAT, 64, 0, 0, + 0 + } + )); + cat.systems.push_back(FurnaceGUISysDef( + "Neo Geo CD", { + DIV_SYSTEM_YM2610, 64, 0, 0, + 0 + } + )); + cat.systems.push_back(FurnaceGUISysDef( + "Neo Geo CD (extended channel 2)", { + DIV_SYSTEM_YM2610_EXT, 64, 0, 0, + 0 + } + )); + sysCategories.push_back(cat); +} diff --git a/src/gui/regView.cpp b/src/gui/regView.cpp new file mode 100644 index 00000000..9755e4a8 --- /dev/null +++ b/src/gui/regView.cpp @@ -0,0 +1,71 @@ +/** + * Furnace Tracker - multi-system chiptune tracker + * Copyright (C) 2021-2022 tildearrow and contributors + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include "gui.h" + +void FurnaceGUI::drawRegView() { + if (nextWindow==GUI_WINDOW_REGISTER_VIEW) { + channelsOpen=true; + ImGui::SetNextWindowFocus(); + nextWindow=GUI_WINDOW_NOTHING; + } + if (!regViewOpen) return; + if (ImGui::Begin("Register View",®ViewOpen)) { + for (int i=0; isong.systemLen; i++) { + ImGui::Text("%d. %s",i+1,getSystemName(e->song.system[i])); + int size=0; + int depth=8; + unsigned char* regPool=e->getRegisterPool(i,size,depth); + unsigned short* regPoolW=(unsigned short*)regPool; + if (regPool==NULL) { + ImGui::Text("- no register pool available"); + } else { + ImGui::PushFont(patFont); + if (ImGui::BeginTable("Memory",17)) { + ImGui::TableNextRow(); + ImGui::TableNextColumn(); + for (int i=0; i<16; i++) { + ImGui::TableNextColumn(); + ImGui::TextColored(uiColors[GUI_COLOR_PATTERN_ROW_INDEX]," %X",i); + } + for (int i=0; i<=((size-1)>>4); i++) { + ImGui::TableNextRow(); + ImGui::TableNextColumn(); + ImGui::TextColored(uiColors[GUI_COLOR_PATTERN_ROW_INDEX],"%.2X",i*16); + for (int j=0; j<16; j++) { + ImGui::TableNextColumn(); + if (i*16+j>=size) continue; + if (depth == 8) { + ImGui::Text("%.2x",regPool[i*16+j]); + } else if (depth == 16) { + ImGui::Text("%.4x",regPoolW[i*16+j]); + } else { + ImGui::Text("??"); + } + } + } + ImGui::EndTable(); + } + ImGui::PopFont(); + } + } + } + if (ImGui::IsWindowFocused(ImGuiFocusedFlags_ChildWindows)) curWindow=GUI_WINDOW_REGISTER_VIEW; + ImGui::End(); +} \ No newline at end of file diff --git a/src/gui/settings.cpp b/src/gui/settings.cpp index ed17a1e3..a3fd347d 100644 --- a/src/gui/settings.cpp +++ b/src/gui/settings.cpp @@ -18,8 +18,10 @@ */ #include "gui.h" +#include "fonts.h" #include "../ta-log.h" #include "util.h" +#include "ImGuiFileDialog.h" #include "IconsFontAwesome4.h" #include "misc/cpp/imgui_stdlib.h" #include @@ -1425,3 +1427,398 @@ void FurnaceGUI::commitSettings() { } } } + +void FurnaceGUI::parseKeybinds() { + actionMapGlobal.clear(); + actionMapPat.clear(); + actionMapInsList.clear(); + actionMapWaveList.clear(); + actionMapSampleList.clear(); + actionMapOrders.clear(); + + for (int i=GUI_ACTION_GLOBAL_MIN+1; igetConfInt(#target,ImGui::GetColorU32(def))); + +#ifdef _WIN32 +#define SYSTEM_FONT_PATH_1 "C:\\Windows\\Fonts\\segoeui.ttf" +#define SYSTEM_FONT_PATH_2 "C:\\Windows\\Fonts\\tahoma.ttf" +// TODO! +#define SYSTEM_FONT_PATH_3 "C:\\Windows\\Fonts\\tahoma.ttf" +// TODO! +#define SYSTEM_PAT_FONT_PATH_1 "C:\\Windows\\Fonts\\consola.ttf" +#define SYSTEM_PAT_FONT_PATH_2 "C:\\Windows\\Fonts\\cour.ttf" +// GOOD LUCK WITH THIS ONE - UNTESTED +#define SYSTEM_PAT_FONT_PATH_3 "C:\\Windows\\Fonts\\vgasys.fon" +#elif defined(__APPLE__) +#define SYSTEM_FONT_PATH_1 "/System/Library/Fonts/SFAANS.ttf" +#define SYSTEM_FONT_PATH_2 "/System/Library/Fonts/Helvetica.ttc" +#define SYSTEM_FONT_PATH_3 "/System/Library/Fonts/Helvetica.dfont" +#define SYSTEM_PAT_FONT_PATH_1 "/System/Library/Fonts/SFNSMono.ttf" +#define SYSTEM_PAT_FONT_PATH_2 "/System/Library/Fonts/Courier New.ttf" +#define SYSTEM_PAT_FONT_PATH_3 "/System/Library/Fonts/Courier New.ttf" +#else +#define SYSTEM_FONT_PATH_1 "/usr/share/fonts/truetype/dejavu/DejaVuSans.ttf" +#define SYSTEM_FONT_PATH_2 "/usr/share/fonts/TTF/DejaVuSans.ttf" +#define SYSTEM_FONT_PATH_3 "/usr/share/fonts/ubuntu/Ubuntu-R.ttf" +#define SYSTEM_PAT_FONT_PATH_1 "/usr/share/fonts/truetype/dejavu/DejaVuSansMono.ttf" +#define SYSTEM_PAT_FONT_PATH_2 "/usr/share/fonts/TTF/DejaVuSansMono.ttf" +#define SYSTEM_PAT_FONT_PATH_3 "/usr/share/fonts/ubuntu/UbuntuMono-R.ttf" +#endif + +void FurnaceGUI::applyUISettings() { + ImGuiStyle sty; + if (settings.guiColorsBase) { + ImGui::StyleColorsLight(&sty); + } else { + ImGui::StyleColorsDark(&sty); + } + + if (settings.dpiScale>=0.5f) dpiScale=settings.dpiScale; + + GET_UI_COLOR(GUI_COLOR_BACKGROUND,ImVec4(0.1f,0.1f,0.1f,1.0f)); + GET_UI_COLOR(GUI_COLOR_FRAME_BACKGROUND,ImVec4(0.0f,0.0f,0.0f,0.85f)); + GET_UI_COLOR(GUI_COLOR_MODAL_BACKDROP,ImVec4(0.0f,0.0f,0.0f,0.55f)); + GET_UI_COLOR(GUI_COLOR_HEADER,ImVec4(0.2f,0.2f,0.2f,1.0f)); + GET_UI_COLOR(GUI_COLOR_TEXT,ImVec4(1.0f,1.0f,1.0f,1.0f)); + GET_UI_COLOR(GUI_COLOR_ACCENT_PRIMARY,ImVec4(0.06f,0.53f,0.98f,1.0f)); + GET_UI_COLOR(GUI_COLOR_ACCENT_SECONDARY,ImVec4(0.26f,0.59f,0.98f,1.0f)); + GET_UI_COLOR(GUI_COLOR_EDITING,ImVec4(0.2f,0.1f,0.1f,1.0f)); + GET_UI_COLOR(GUI_COLOR_SONG_LOOP,ImVec4(0.3f,0.5f,0.8f,0.4f)); + GET_UI_COLOR(GUI_COLOR_VOLMETER_LOW,ImVec4(0.2f,0.6f,0.2f,1.0f)); + GET_UI_COLOR(GUI_COLOR_VOLMETER_HIGH,ImVec4(1.0f,0.9f,0.2f,1.0f)); + GET_UI_COLOR(GUI_COLOR_VOLMETER_PEAK,ImVec4(1.0f,0.1f,0.1f,1.0f)); + GET_UI_COLOR(GUI_COLOR_MACRO_VOLUME,ImVec4(0.2f,1.0f,0.0f,1.0f)); + GET_UI_COLOR(GUI_COLOR_MACRO_PITCH,ImVec4(1.0f,0.8f,0.0f,1.0f)); + GET_UI_COLOR(GUI_COLOR_MACRO_OTHER,ImVec4(0.0f,0.9f,1.0f,1.0f)); + GET_UI_COLOR(GUI_COLOR_MACRO_WAVE,ImVec4(1.0f,0.4f,0.0f,1.0f)); + GET_UI_COLOR(GUI_COLOR_INSTR_FM,ImVec4(0.6f,0.9f,1.0f,1.0f)); + GET_UI_COLOR(GUI_COLOR_INSTR_STD,ImVec4(0.6f,1.0f,0.5f,1.0f)); + GET_UI_COLOR(GUI_COLOR_INSTR_GB,ImVec4(1.0f,1.0f,0.5f,1.0f)); + GET_UI_COLOR(GUI_COLOR_INSTR_C64,ImVec4(0.85f,0.8f,1.0f,1.0f)); + GET_UI_COLOR(GUI_COLOR_INSTR_AMIGA,ImVec4(1.0f,0.5f,0.5f,1.0f)); + GET_UI_COLOR(GUI_COLOR_INSTR_PCE,ImVec4(1.0f,0.8f,0.5f,1.0f)); + GET_UI_COLOR(GUI_COLOR_INSTR_AY,ImVec4(1.0f,0.5f,1.0f,1.0f)); + GET_UI_COLOR(GUI_COLOR_INSTR_AY8930,ImVec4(0.7f,0.5f,1.0f,1.0f)); + GET_UI_COLOR(GUI_COLOR_INSTR_TIA,ImVec4(1.0f,0.6f,0.4f,1.0f)); + GET_UI_COLOR(GUI_COLOR_INSTR_SAA1099,ImVec4(0.3f,0.3f,1.0f,1.0f)); + GET_UI_COLOR(GUI_COLOR_INSTR_VIC,ImVec4(0.2f,1.0f,0.6f,1.0f)); + GET_UI_COLOR(GUI_COLOR_INSTR_PET,ImVec4(1.0f,1.0f,0.8f,1.0f)); + GET_UI_COLOR(GUI_COLOR_INSTR_VRC6,ImVec4(1.0f,0.9f,0.5f,1.0f)); + GET_UI_COLOR(GUI_COLOR_INSTR_OPLL,ImVec4(0.6f,0.7f,1.0f,1.0f)); + GET_UI_COLOR(GUI_COLOR_INSTR_OPL,ImVec4(0.3f,1.0f,0.9f,1.0f)); + GET_UI_COLOR(GUI_COLOR_INSTR_FDS,ImVec4(0.8f,0.5f,1.0f,1.0f)); + GET_UI_COLOR(GUI_COLOR_INSTR_VBOY,ImVec4(1.0f,0.1f,0.1f,1.0f)); + GET_UI_COLOR(GUI_COLOR_INSTR_N163,ImVec4(1.0f,0.4f,0.1f,1.0f)); + GET_UI_COLOR(GUI_COLOR_INSTR_SCC,ImVec4(0.7f,1.0f,0.3f,1.0f)); + GET_UI_COLOR(GUI_COLOR_INSTR_OPZ,ImVec4(0.2f,0.8f,1.0f,1.0f)); + GET_UI_COLOR(GUI_COLOR_INSTR_POKEY,ImVec4(0.5f,1.0f,0.3f,1.0f)); + GET_UI_COLOR(GUI_COLOR_INSTR_BEEPER,ImVec4(0.0f,1.0f,0.0f,1.0f)); + GET_UI_COLOR(GUI_COLOR_INSTR_SWAN,ImVec4(0.3f,0.5f,1.0f,1.0f)); + GET_UI_COLOR(GUI_COLOR_INSTR_MIKEY,ImVec4(0.5f,1.0f,0.3f,1.0f)); + GET_UI_COLOR(GUI_COLOR_INSTR_VERA,ImVec4(0.4f,0.6f,1.0f,1.0f)); + GET_UI_COLOR(GUI_COLOR_INSTR_X1_010,ImVec4(0.3f,0.5f,1.0f,1.0f)); + GET_UI_COLOR(GUI_COLOR_INSTR_UNKNOWN,ImVec4(0.3f,0.3f,0.3f,1.0f)); + GET_UI_COLOR(GUI_COLOR_CHANNEL_FM,ImVec4(0.2f,0.8f,1.0f,1.0f)); + GET_UI_COLOR(GUI_COLOR_CHANNEL_PULSE,ImVec4(0.4f,1.0f,0.2f,1.0f)); + GET_UI_COLOR(GUI_COLOR_CHANNEL_NOISE,ImVec4(0.8f,0.8f,0.8f,1.0f)); + GET_UI_COLOR(GUI_COLOR_CHANNEL_PCM,ImVec4(1.0f,0.9f,0.2f,1.0f)); + GET_UI_COLOR(GUI_COLOR_CHANNEL_WAVE,ImVec4(1.0f,0.5f,0.2f,1.0f)); + GET_UI_COLOR(GUI_COLOR_CHANNEL_OP,ImVec4(0.2f,0.4f,1.0f,1.0f)); + GET_UI_COLOR(GUI_COLOR_CHANNEL_MUTED,ImVec4(0.5f,0.5f,0.5f,1.0f)); + GET_UI_COLOR(GUI_COLOR_PATTERN_CURSOR,ImVec4(0.1f,0.3f,0.5f,1.0f)); + GET_UI_COLOR(GUI_COLOR_PATTERN_CURSOR_HOVER,ImVec4(0.2f,0.4f,0.6f,1.0f)); + GET_UI_COLOR(GUI_COLOR_PATTERN_CURSOR_ACTIVE,ImVec4(0.2f,0.5f,0.7f,1.0f)); + GET_UI_COLOR(GUI_COLOR_PATTERN_SELECTION,ImVec4(0.15f,0.15f,0.2f,1.0f)); + GET_UI_COLOR(GUI_COLOR_PATTERN_SELECTION_HOVER,ImVec4(0.2f,0.2f,0.3f,1.0f)); + GET_UI_COLOR(GUI_COLOR_PATTERN_SELECTION_ACTIVE,ImVec4(0.4f,0.4f,0.5f,1.0f)); + GET_UI_COLOR(GUI_COLOR_PATTERN_HI_1,ImVec4(0.6f,0.6f,0.6f,0.2f)); + GET_UI_COLOR(GUI_COLOR_PATTERN_HI_2,ImVec4(0.5f,0.8f,1.0f,0.2f)); + GET_UI_COLOR(GUI_COLOR_PATTERN_ROW_INDEX,ImVec4(0.5f,0.8f,1.0f,1.0f)); + GET_UI_COLOR(GUI_COLOR_PATTERN_ACTIVE,ImVec4(1.0f,1.0f,1.0f,1.0f)); + GET_UI_COLOR(GUI_COLOR_PATTERN_INACTIVE,ImVec4(0.5f,0.5f,0.5f,1.0f)); + GET_UI_COLOR(GUI_COLOR_PATTERN_INS,ImVec4(0.4f,0.7f,1.0f,1.0f)); + GET_UI_COLOR(GUI_COLOR_PATTERN_VOLUME_MIN,ImVec4(0.0f,0.5f,0.0f,1.0f)); + GET_UI_COLOR(GUI_COLOR_PATTERN_VOLUME_HALF,ImVec4(0.0f,0.75f,0.0f,1.0f)); + GET_UI_COLOR(GUI_COLOR_PATTERN_VOLUME_MAX,ImVec4(0.0f,1.0f,0.0f,1.0f)); + GET_UI_COLOR(GUI_COLOR_PATTERN_EFFECT_INVALID,ImVec4(1.0f,0.0f,0.0f,1.0f)); + GET_UI_COLOR(GUI_COLOR_PATTERN_EFFECT_PITCH,ImVec4(1.0f,1.0f,0.0f,1.0f)); + GET_UI_COLOR(GUI_COLOR_PATTERN_EFFECT_VOLUME,ImVec4(0.0f,1.0f,0.0f,1.0f)); + GET_UI_COLOR(GUI_COLOR_PATTERN_EFFECT_PANNING,ImVec4(0.0f,1.0f,1.0f,1.0f)); + GET_UI_COLOR(GUI_COLOR_PATTERN_EFFECT_SONG,ImVec4(1.0f,0.0f,0.0f,1.0f)); + GET_UI_COLOR(GUI_COLOR_PATTERN_EFFECT_TIME,ImVec4(0.5f,0.0f,1.0f,1.0f)); + GET_UI_COLOR(GUI_COLOR_PATTERN_EFFECT_SPEED,ImVec4(1.0f,0.0f,1.0f,1.0f)); + GET_UI_COLOR(GUI_COLOR_PATTERN_EFFECT_SYS_PRIMARY,ImVec4(0.5f,1.0f,0.0f,1.0f)); + GET_UI_COLOR(GUI_COLOR_PATTERN_EFFECT_SYS_SECONDARY,ImVec4(0.0f,1.0f,0.5f,1.0f)); + GET_UI_COLOR(GUI_COLOR_PATTERN_EFFECT_MISC,ImVec4(0.3f,0.3f,1.0f,1.0f)); + GET_UI_COLOR(GUI_COLOR_EE_VALUE,ImVec4(0.0f,1.0f,1.0f,1.0f)); + GET_UI_COLOR(GUI_COLOR_PLAYBACK_STAT,ImVec4(0.6f,0.6f,0.6f,1.0f)); + + for (int i=0; i<64; i++) { + ImVec4 col1=uiColors[GUI_COLOR_PATTERN_VOLUME_MIN]; + ImVec4 col2=uiColors[GUI_COLOR_PATTERN_VOLUME_HALF]; + ImVec4 col3=uiColors[GUI_COLOR_PATTERN_VOLUME_MAX]; + volColors[i]=ImVec4(col1.x+((col2.x-col1.x)*float(i)/64.0f), + col1.y+((col2.y-col1.y)*float(i)/64.0f), + col1.z+((col2.z-col1.z)*float(i)/64.0f), + 1.0f); + volColors[i+64]=ImVec4(col2.x+((col3.x-col2.x)*float(i)/64.0f), + col2.y+((col3.y-col2.y)*float(i)/64.0f), + col2.z+((col3.z-col2.z)*float(i)/64.0f), + 1.0f); + } + + float hue, sat, val; + + ImVec4 primaryActive=uiColors[GUI_COLOR_ACCENT_PRIMARY]; + ImVec4 primaryHover, primary; + primaryHover.w=primaryActive.w; + primary.w=primaryActive.w; + ImGui::ColorConvertRGBtoHSV(primaryActive.x,primaryActive.y,primaryActive.z,hue,sat,val); + if (settings.guiColorsBase) { + primary=primaryActive; + ImGui::ColorConvertHSVtoRGB(hue,sat*0.9,val*0.9,primaryHover.x,primaryHover.y,primaryHover.z); + ImGui::ColorConvertHSVtoRGB(hue,sat,val*0.5,primaryActive.x,primaryActive.y,primaryActive.z); + } else { + ImGui::ColorConvertHSVtoRGB(hue,sat*0.9,val*0.5,primaryHover.x,primaryHover.y,primaryHover.z); + ImGui::ColorConvertHSVtoRGB(hue,sat*0.8,val*0.35,primary.x,primary.y,primary.z); + } + + ImVec4 secondaryActive=uiColors[GUI_COLOR_ACCENT_SECONDARY]; + ImVec4 secondaryHover, secondary, secondarySemiActive; + secondarySemiActive.w=secondaryActive.w; + secondaryHover.w=secondaryActive.w; + secondary.w=secondaryActive.w; + ImGui::ColorConvertRGBtoHSV(secondaryActive.x,secondaryActive.y,secondaryActive.z,hue,sat,val); + if (settings.guiColorsBase) { + secondary=secondaryActive; + ImGui::ColorConvertHSVtoRGB(hue,sat*0.9,val*0.7,secondarySemiActive.x,secondarySemiActive.y,secondarySemiActive.z); + ImGui::ColorConvertHSVtoRGB(hue,sat*0.9,val*0.9,secondaryHover.x,secondaryHover.y,secondaryHover.z); + ImGui::ColorConvertHSVtoRGB(hue,sat,val*0.5,secondaryActive.x,secondaryActive.y,secondaryActive.z); + } else { + ImGui::ColorConvertHSVtoRGB(hue,sat*0.9,val*0.75,secondarySemiActive.x,secondarySemiActive.y,secondarySemiActive.z); + ImGui::ColorConvertHSVtoRGB(hue,sat*0.9,val*0.5,secondaryHover.x,secondaryHover.y,secondaryHover.z); + ImGui::ColorConvertHSVtoRGB(hue,sat*0.9,val*0.25,secondary.x,secondary.y,secondary.z); + } + + + sty.Colors[ImGuiCol_WindowBg]=uiColors[GUI_COLOR_FRAME_BACKGROUND]; + sty.Colors[ImGuiCol_ModalWindowDimBg]=uiColors[GUI_COLOR_MODAL_BACKDROP]; + sty.Colors[ImGuiCol_Text]=uiColors[GUI_COLOR_TEXT]; + + sty.Colors[ImGuiCol_Button]=primary; + sty.Colors[ImGuiCol_ButtonHovered]=primaryHover; + sty.Colors[ImGuiCol_ButtonActive]=primaryActive; + sty.Colors[ImGuiCol_Tab]=primary; + sty.Colors[ImGuiCol_TabHovered]=secondaryHover; + sty.Colors[ImGuiCol_TabActive]=secondarySemiActive; + sty.Colors[ImGuiCol_TabUnfocused]=primary; + sty.Colors[ImGuiCol_TabUnfocusedActive]=primaryHover; + sty.Colors[ImGuiCol_Header]=secondary; + sty.Colors[ImGuiCol_HeaderHovered]=secondaryHover; + sty.Colors[ImGuiCol_HeaderActive]=secondaryActive; + sty.Colors[ImGuiCol_ResizeGrip]=secondary; + sty.Colors[ImGuiCol_ResizeGripHovered]=secondaryHover; + sty.Colors[ImGuiCol_ResizeGripActive]=secondaryActive; + sty.Colors[ImGuiCol_FrameBg]=secondary; + sty.Colors[ImGuiCol_FrameBgHovered]=secondaryHover; + sty.Colors[ImGuiCol_FrameBgActive]=secondaryActive; + sty.Colors[ImGuiCol_SliderGrab]=primaryActive; + sty.Colors[ImGuiCol_SliderGrabActive]=primaryActive; + sty.Colors[ImGuiCol_TitleBgActive]=primary; + sty.Colors[ImGuiCol_CheckMark]=primaryActive; + sty.Colors[ImGuiCol_TextSelectedBg]=secondaryHover; + sty.Colors[ImGuiCol_PlotHistogram]=uiColors[GUI_COLOR_MACRO_OTHER]; + sty.Colors[ImGuiCol_PlotHistogramHovered]=uiColors[GUI_COLOR_MACRO_OTHER]; + + if (settings.roundedWindows) sty.WindowRounding=8.0f; + if (settings.roundedButtons) { + sty.FrameRounding=6.0f; + sty.GrabRounding=6.0f; + } + if (settings.roundedMenus) sty.PopupRounding=8.0f; + + sty.ScaleAllSizes(dpiScale); + + ImGui::GetStyle()=sty; + + for (int i=0; i<256; i++) { + ImVec4& base=uiColors[GUI_COLOR_PATTERN_EFFECT_PITCH]; + pitchGrad[i]=ImGui::GetColorU32(ImVec4(base.x,base.y,base.z,((float)i/255.0f)*base.w)); + } + for (int i=0; i<256; i++) { + ImVec4& base=uiColors[GUI_COLOR_PATTERN_ACTIVE]; + noteGrad[i]=ImGui::GetColorU32(ImVec4(base.x,base.y,base.z,((float)i/255.0f)*base.w)); + } + for (int i=0; i<256; i++) { + ImVec4& base=uiColors[GUI_COLOR_PATTERN_EFFECT_PANNING]; + panGrad[i]=ImGui::GetColorU32(ImVec4(base.x,base.y,base.z,((float)i/255.0f)*base.w)); + } + for (int i=0; i<256; i++) { + ImVec4& base=uiColors[GUI_COLOR_PATTERN_INS]; + insGrad[i]=ImGui::GetColorU32(ImVec4(base.x,base.y,base.z,((float)i/255.0f)*base.w)); + } + for (int i=0; i<256; i++) { + ImVec4& base=volColors[i/2]; + volGrad[i]=ImGui::GetColorU32(ImVec4(base.x,base.y,base.z,((float)i/255.0f)*base.w)); + } + for (int i=0; i<256; i++) { + ImVec4& base=uiColors[GUI_COLOR_PATTERN_EFFECT_SYS_PRIMARY]; + sysCmd1Grad[i]=ImGui::GetColorU32(ImVec4(base.x,base.y,base.z,((float)i/255.0f)*base.w)); + } + for (int i=0; i<256; i++) { + ImVec4& base=uiColors[GUI_COLOR_PATTERN_EFFECT_SYS_SECONDARY]; + sysCmd2Grad[i]=ImGui::GetColorU32(ImVec4(base.x,base.y,base.z,((float)i/255.0f)*base.w)); + } + + // set to 800 for now due to problems with unifont + static const ImWchar loadEverything[]={0x20,0x800,0}; + + if (settings.mainFont<0 || settings.mainFont>6) settings.mainFont=0; + if (settings.patFont<0 || settings.patFont>6) settings.patFont=0; + + if (settings.mainFont==6 && settings.mainFontPath.empty()) { + logW("UI font path is empty! reverting to default font\n"); + settings.mainFont=0; + } + if (settings.patFont==6 && settings.patFontPath.empty()) { + logW("pattern font path is empty! reverting to default font\n"); + settings.patFont=0; + } + + if (settings.mainFont==6) { // custom font + if ((mainFont=ImGui::GetIO().Fonts->AddFontFromFileTTF(settings.mainFontPath.c_str(),e->getConfInt("mainFontSize",18)*dpiScale,NULL,loadEverything))==NULL) { + logW("could not load UI font! reverting to default font\n"); + settings.mainFont=0; + if ((mainFont=ImGui::GetIO().Fonts->AddFontFromMemoryCompressedTTF(builtinFont[settings.mainFont],builtinFontLen[settings.mainFont],e->getConfInt("mainFontSize",18)*dpiScale,NULL,loadEverything))==NULL) { + logE("could not load UI font! falling back to Proggy Clean.\n"); + mainFont=ImGui::GetIO().Fonts->AddFontDefault(); + } + } + } else if (settings.mainFont==5) { // system font + if ((mainFont=ImGui::GetIO().Fonts->AddFontFromFileTTF(SYSTEM_FONT_PATH_1,e->getConfInt("mainFontSize",18)*dpiScale,NULL,loadEverything))==NULL) { + if ((mainFont=ImGui::GetIO().Fonts->AddFontFromFileTTF(SYSTEM_FONT_PATH_2,e->getConfInt("mainFontSize",18)*dpiScale,NULL,loadEverything))==NULL) { + if ((mainFont=ImGui::GetIO().Fonts->AddFontFromFileTTF(SYSTEM_FONT_PATH_3,e->getConfInt("mainFontSize",18)*dpiScale,NULL,loadEverything))==NULL) { + logW("could not load UI font! reverting to default font\n"); + settings.mainFont=0; + if ((mainFont=ImGui::GetIO().Fonts->AddFontFromMemoryCompressedTTF(builtinFont[settings.mainFont],builtinFontLen[settings.mainFont],e->getConfInt("mainFontSize",18)*dpiScale,NULL,loadEverything))==NULL) { + logE("could not load UI font! falling back to Proggy Clean.\n"); + mainFont=ImGui::GetIO().Fonts->AddFontDefault(); + } + } + } + } + } else { + if ((mainFont=ImGui::GetIO().Fonts->AddFontFromMemoryCompressedTTF(builtinFont[settings.mainFont],builtinFontLen[settings.mainFont],e->getConfInt("mainFontSize",18)*dpiScale,NULL,loadEverything))==NULL) { + logE("could not load UI font! falling back to Proggy Clean.\n"); + mainFont=ImGui::GetIO().Fonts->AddFontDefault(); + } + } + + ImFontConfig fc; + fc.MergeMode=true; + fc.GlyphMinAdvanceX=e->getConfInt("iconSize",16)*dpiScale; + static const ImWchar fontRange[]={ICON_MIN_FA,ICON_MAX_FA,0}; + if ((iconFont=ImGui::GetIO().Fonts->AddFontFromMemoryCompressedTTF(iconFont_compressed_data,iconFont_compressed_size,e->getConfInt("iconSize",16)*dpiScale,&fc,fontRange))==NULL) { + logE("could not load icon font!\n"); + } + if (settings.mainFontSize==settings.patFontSize && settings.patFont<5 && builtinFontM[settings.patFont]==builtinFont[settings.mainFont]) { + logD("using main font for pat font.\n"); + patFont=mainFont; + } else { + if (settings.patFont==6) { // custom font + if ((patFont=ImGui::GetIO().Fonts->AddFontFromFileTTF(settings.patFontPath.c_str(),e->getConfInt("patFontSize",18)*dpiScale,NULL,loadEverything))==NULL) { + logW("could not load pattern font! reverting to default font\n"); + settings.patFont=0; + if ((patFont=ImGui::GetIO().Fonts->AddFontFromMemoryCompressedTTF(builtinFontM[settings.patFont],builtinFontMLen[settings.patFont],e->getConfInt("patFontSize",18)*dpiScale,NULL,loadEverything))==NULL) { + logE("could not load pattern font! falling back to Proggy Clean.\n"); + patFont=ImGui::GetIO().Fonts->AddFontDefault(); + } + } + } else if (settings.patFont==5) { // system font + if ((patFont=ImGui::GetIO().Fonts->AddFontFromFileTTF(SYSTEM_PAT_FONT_PATH_1,e->getConfInt("patFontSize",18)*dpiScale,NULL,loadEverything))==NULL) { + if ((patFont=ImGui::GetIO().Fonts->AddFontFromFileTTF(SYSTEM_PAT_FONT_PATH_2,e->getConfInt("patFontSize",18)*dpiScale,NULL,loadEverything))==NULL) { + if ((patFont=ImGui::GetIO().Fonts->AddFontFromFileTTF(SYSTEM_PAT_FONT_PATH_3,e->getConfInt("patFontSize",18)*dpiScale,NULL,loadEverything))==NULL) { + logW("could not load pattern font! reverting to default font\n"); + settings.patFont=0; + if ((patFont=ImGui::GetIO().Fonts->AddFontFromMemoryCompressedTTF(builtinFontM[settings.patFont],builtinFontMLen[settings.patFont],e->getConfInt("patFontSize",18)*dpiScale,NULL,loadEverything))==NULL) { + logE("could not load pattern font! falling back to Proggy Clean.\n"); + patFont=ImGui::GetIO().Fonts->AddFontDefault(); + } + } + } + } + } else { + if ((patFont=ImGui::GetIO().Fonts->AddFontFromMemoryCompressedTTF(builtinFontM[settings.patFont],builtinFontMLen[settings.patFont],e->getConfInt("patFontSize",18)*dpiScale,NULL,loadEverything))==NULL) { + logE("could not load pattern font!\n"); + patFont=ImGui::GetIO().Fonts->AddFontDefault(); + } + } + } + if ((bigFont=ImGui::GetIO().Fonts->AddFontFromMemoryCompressedTTF(font_plexSans_compressed_data,font_plexSans_compressed_size,40*dpiScale))==NULL) { + logE("could not load big UI font!\n"); + } + + // TODO: allow changing these colors. + ImGuiFileDialog::Instance()->SetFileStyle(IGFD_FileStyleByTypeDir,"",ImVec4(0.0f,1.0f,1.0f,1.0f),ICON_FA_FOLDER_O); + ImGuiFileDialog::Instance()->SetFileStyle(IGFD_FileStyleByTypeFile,"",ImVec4(0.7f,0.7f,0.7f,1.0f),ICON_FA_FILE_O); + ImGuiFileDialog::Instance()->SetFileStyle(IGFD_FileStyleByExtension,".fur",ImVec4(0.5f,1.0f,0.5f,1.0f),ICON_FA_FILE); + ImGuiFileDialog::Instance()->SetFileStyle(IGFD_FileStyleByExtension,".fui",ImVec4(1.0f,0.5f,0.5f,1.0f),ICON_FA_FILE); + ImGuiFileDialog::Instance()->SetFileStyle(IGFD_FileStyleByExtension,".fuw",ImVec4(1.0f,0.75f,0.5f,1.0f),ICON_FA_FILE); + ImGuiFileDialog::Instance()->SetFileStyle(IGFD_FileStyleByExtension,".dmf",ImVec4(0.5f,1.0f,0.5f,1.0f),ICON_FA_FILE); + ImGuiFileDialog::Instance()->SetFileStyle(IGFD_FileStyleByExtension,".dmp",ImVec4(1.0f,0.5f,0.5f,1.0f),ICON_FA_FILE); + ImGuiFileDialog::Instance()->SetFileStyle(IGFD_FileStyleByExtension,".dmw",ImVec4(1.0f,0.75f,0.5f,1.0f),ICON_FA_FILE); + ImGuiFileDialog::Instance()->SetFileStyle(IGFD_FileStyleByExtension,".wav",ImVec4(1.0f,1.0f,0.5f,1.0f),ICON_FA_FILE_AUDIO_O); + ImGuiFileDialog::Instance()->SetFileStyle(IGFD_FileStyleByExtension,".vgm",ImVec4(1.0f,1.0f,0.5f,1.0f),ICON_FA_FILE_AUDIO_O); + ImGuiFileDialog::Instance()->SetFileStyle(IGFD_FileStyleByExtension,".ttf",ImVec4(0.3f,1.0f,0.6f,1.0f),ICON_FA_FONT); + ImGuiFileDialog::Instance()->SetFileStyle(IGFD_FileStyleByExtension,".otf",ImVec4(0.3f,1.0f,0.6f,1.0f),ICON_FA_FONT); + ImGuiFileDialog::Instance()->SetFileStyle(IGFD_FileStyleByExtension,".ttc",ImVec4(0.3f,1.0f,0.6f,1.0f),ICON_FA_FONT); + + ImGuiFileDialog::Instance()->SetFileStyle(IGFD_FileStyleByExtension,".tfi",ImVec4(1.0f,0.5f,0.5f,1.0f),ICON_FA_FILE); + ImGuiFileDialog::Instance()->SetFileStyle(IGFD_FileStyleByExtension,".vgi",ImVec4(1.0f,0.5f,0.5f,1.0f),ICON_FA_FILE); + ImGuiFileDialog::Instance()->SetFileStyle(IGFD_FileStyleByExtension,".s3i",ImVec4(1.0f,0.5f,0.5f,1.0f),ICON_FA_FILE); + ImGuiFileDialog::Instance()->SetFileStyle(IGFD_FileStyleByExtension,".sbi",ImVec4(1.0f,0.5f,0.5f,1.0f),ICON_FA_FILE); + ImGuiFileDialog::Instance()->SetFileStyle(IGFD_FileStyleByExtension,".fti",ImVec4(1.0f,0.5f,0.5f,1.0f),ICON_FA_FILE); + ImGuiFileDialog::Instance()->SetFileStyle(IGFD_FileStyleByExtension,".bti",ImVec4(1.0f,0.5f,0.5f,1.0f),ICON_FA_FILE); + + if (fileDialog!=NULL) delete fileDialog; + fileDialog=new FurnaceGUIFileDialog(settings.sysFileDialog); +} \ No newline at end of file diff --git a/src/gui/songInfo.cpp b/src/gui/songInfo.cpp new file mode 100644 index 00000000..e9caf5af --- /dev/null +++ b/src/gui/songInfo.cpp @@ -0,0 +1,173 @@ +/** + * Furnace Tracker - multi-system chiptune tracker + * Copyright (C) 2021-2022 tildearrow and contributors + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include "gui.h" +#include "misc/cpp/imgui_stdlib.h" +#include "intConst.h" + +void FurnaceGUI::drawSongInfo() { + if (nextWindow==GUI_WINDOW_SONG_INFO) { + songInfoOpen=true; + ImGui::SetNextWindowFocus(); + nextWindow=GUI_WINDOW_NOTHING; + } + if (!songInfoOpen) return; + if (ImGui::Begin("Song Information",&songInfoOpen)) { + if (ImGui::BeginTable("NameAuthor",2,ImGuiTableFlags_SizingStretchProp)) { + ImGui::TableSetupColumn("c0",ImGuiTableColumnFlags_WidthFixed,0.0); + ImGui::TableSetupColumn("c1",ImGuiTableColumnFlags_WidthStretch,0.0); + ImGui::TableNextRow(); + ImGui::TableNextColumn(); + ImGui::Text("Name"); + ImGui::TableNextColumn(); + float avail=ImGui::GetContentRegionAvail().x; + ImGui::SetNextItemWidth(avail); + if (ImGui::InputText("##Name",&e->song.name)) { MARK_MODIFIED + updateWindowTitle(); + } + ImGui::TableNextRow(); + ImGui::TableNextColumn(); + ImGui::Text("Author"); + ImGui::TableNextColumn(); + ImGui::SetNextItemWidth(avail); + if (ImGui::InputText("##Author",&e->song.author)) { + MARK_MODIFIED; + } + ImGui::EndTable(); + } + + if (ImGui::BeginTable("OtherProps",3,ImGuiTableFlags_SizingStretchProp)) { + ImGui::TableSetupColumn("c0",ImGuiTableColumnFlags_WidthFixed,0.0); + ImGui::TableSetupColumn("c1",ImGuiTableColumnFlags_WidthStretch,0.0); + ImGui::TableSetupColumn("c2",ImGuiTableColumnFlags_WidthStretch,0.0); + + ImGui::TableNextRow(); + ImGui::TableNextColumn(); + ImGui::Text("TimeBase"); + ImGui::TableNextColumn(); + float avail=ImGui::GetContentRegionAvail().x; + ImGui::SetNextItemWidth(avail); + unsigned char realTB=e->song.timeBase+1; + if (ImGui::InputScalar("##TimeBase",ImGuiDataType_U8,&realTB,&_ONE,&_THREE)) { MARK_MODIFIED + if (realTB<1) realTB=1; + if (realTB>16) realTB=16; + e->song.timeBase=realTB-1; + } + ImGui::TableNextColumn(); + ImGui::Text("%.2f BPM",calcBPM(e->song.speed1,e->song.speed2,e->song.hz)); + + ImGui::TableNextRow(); + ImGui::TableNextColumn(); + ImGui::Text("Speed"); + ImGui::TableNextColumn(); + ImGui::SetNextItemWidth(avail); + if (ImGui::InputScalar("##Speed1",ImGuiDataType_U8,&e->song.speed1,&_ONE,&_THREE)) { MARK_MODIFIED + if (e->song.speed1<1) e->song.speed1=1; + if (e->isPlaying()) play(); + } + ImGui::TableNextColumn(); + ImGui::SetNextItemWidth(avail); + if (ImGui::InputScalar("##Speed2",ImGuiDataType_U8,&e->song.speed2,&_ONE,&_THREE)) { MARK_MODIFIED + if (e->song.speed2<1) e->song.speed2=1; + if (e->isPlaying()) play(); + } + + ImGui::TableNextRow(); + ImGui::TableNextColumn(); + ImGui::Text("Highlight"); + ImGui::TableNextColumn(); + ImGui::SetNextItemWidth(avail); + if (ImGui::InputScalar("##Highlight1",ImGuiDataType_U8,&e->song.hilightA,&_ONE,&_THREE)) { + MARK_MODIFIED; + } + ImGui::TableNextColumn(); + ImGui::SetNextItemWidth(avail); + if (ImGui::InputScalar("##Highlight2",ImGuiDataType_U8,&e->song.hilightB,&_ONE,&_THREE)) { + MARK_MODIFIED; + } + + ImGui::TableNextRow(); + ImGui::TableNextColumn(); + ImGui::Text("Pattern Length"); + ImGui::TableNextColumn(); + ImGui::SetNextItemWidth(avail); + int patLen=e->song.patLen; + if (ImGui::InputInt("##PatLength",&patLen,1,3)) { MARK_MODIFIED + if (patLen<1) patLen=1; + if (patLen>256) patLen=256; + e->song.patLen=patLen; + } + + ImGui::TableNextRow(); + ImGui::TableNextColumn(); + ImGui::Text("Song Length"); + ImGui::TableNextColumn(); + ImGui::SetNextItemWidth(avail); + int ordLen=e->song.ordersLen; + if (ImGui::InputInt("##OrdLength",&ordLen,1,3)) { MARK_MODIFIED + if (ordLen<1) ordLen=1; + if (ordLen>127) ordLen=127; + e->song.ordersLen=ordLen; + } + + ImGui::TableNextRow(); + ImGui::TableNextColumn(); + if (ImGui::Selectable(tempoView?"Base Tempo##TempoOrHz":"Tick Rate##TempoOrHz")) { + tempoView=!tempoView; + } + ImGui::TableNextColumn(); + ImGui::SetNextItemWidth(avail); + float setHz=tempoView?e->song.hz*2.5:e->song.hz; + if (ImGui::InputFloat("##Rate",&setHz,1.0f,1.0f,"%g")) { MARK_MODIFIED + if (tempoView) setHz/=2.5; + if (setHz<10) setHz=10; + if (setHz>999) setHz=999; + e->setSongRate(setHz,setHz<52); + } + if (tempoView) { + ImGui::TableNextColumn(); + ImGui::Text("= %gHz",e->song.hz); + } else { + if (e->song.hz>=49.98 && e->song.hz<=50.02) { + ImGui::TableNextColumn(); + ImGui::Text("PAL"); + } + if (e->song.hz>=59.9 && e->song.hz<=60.11) { + ImGui::TableNextColumn(); + ImGui::Text("NTSC"); + } + } + + ImGui::TableNextRow(); + ImGui::TableNextColumn(); + ImGui::Text("Tuning (A-4)"); + ImGui::TableNextColumn(); + float tune=e->song.tuning; + ImGui::SetNextItemWidth(avail); + if (ImGui::InputFloat("##Tuning",&tune,1.0f,3.0f,"%g")) { MARK_MODIFIED + if (tune<220.0f) tune=220.0f; + if (tune>880.0f) tune=880.0f; + e->song.tuning=tune; + } + ImGui::EndTable(); + } + } + if (ImGui::IsWindowFocused(ImGuiFocusedFlags_ChildWindows)) curWindow=GUI_WINDOW_SONG_INFO; + ImGui::End(); +} \ No newline at end of file diff --git a/src/gui/songNotes.cpp b/src/gui/songNotes.cpp new file mode 100644 index 00000000..7c7cbdaa --- /dev/null +++ b/src/gui/songNotes.cpp @@ -0,0 +1,37 @@ +/** + * Furnace Tracker - multi-system chiptune tracker + * Copyright (C) 2021-2022 tildearrow and contributors + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include "gui.h" +#include "misc/cpp/imgui_stdlib.h" + +// NOTE: please don't ask me to enable text wrap. +// Dear ImGui doesn't have that feature. D: +void FurnaceGUI::drawNotes() { + if (nextWindow==GUI_WINDOW_NOTES) { + notesOpen=true; + ImGui::SetNextWindowFocus(); + nextWindow=GUI_WINDOW_NOTHING; + } + if (!notesOpen) return; + if (ImGui::Begin("Song Comments",¬esOpen)) { + ImGui::InputTextMultiline("##SongNotes",&e->song.notes,ImGui::GetContentRegionAvail()); + } + if (ImGui::IsWindowFocused(ImGuiFocusedFlags_ChildWindows)) curWindow=GUI_WINDOW_NOTES; + ImGui::End(); +} \ No newline at end of file diff --git a/src/gui/stats.cpp b/src/gui/stats.cpp new file mode 100644 index 00000000..274be2ed --- /dev/null +++ b/src/gui/stats.cpp @@ -0,0 +1,50 @@ +/** + * Furnace Tracker - multi-system chiptune tracker + * Copyright (C) 2021-2022 tildearrow and contributors + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include "gui.h" +#include + +void FurnaceGUI::drawStats() { + if (nextWindow==GUI_WINDOW_STATS) { + statsOpen=true; + ImGui::SetNextWindowFocus(); + nextWindow=GUI_WINDOW_NOTHING; + } + if (!statsOpen) return; + if (ImGui::Begin("Statistics",&statsOpen)) { + String adpcmAUsage=fmt::sprintf("%d/16384KB",e->adpcmAMemLen/1024); + String adpcmBUsage=fmt::sprintf("%d/16384KB",e->adpcmBMemLen/1024); + String qsoundUsage=fmt::sprintf("%d/16384KB",e->qsoundMemLen/1024); + String x1_010Usage=fmt::sprintf("%d/1024KB",e->x1_010MemLen/1024); + ImGui::Text("ADPCM-A"); + ImGui::SameLine(); + ImGui::ProgressBar(((float)e->adpcmAMemLen)/16777216.0f,ImVec2(-FLT_MIN,0),adpcmAUsage.c_str()); + ImGui::Text("ADPCM-B"); + ImGui::SameLine(); + ImGui::ProgressBar(((float)e->adpcmBMemLen)/16777216.0f,ImVec2(-FLT_MIN,0),adpcmBUsage.c_str()); + ImGui::Text("QSound"); + ImGui::SameLine(); + ImGui::ProgressBar(((float)e->qsoundMemLen)/16777216.0f,ImVec2(-FLT_MIN,0),qsoundUsage.c_str()); + ImGui::Text("X1-010"); + ImGui::SameLine(); + ImGui::ProgressBar(((float)e->x1_010MemLen)/1048576.0f,ImVec2(-FLT_MIN,0),x1_010Usage.c_str()); + } + if (ImGui::IsWindowFocused(ImGuiFocusedFlags_ChildWindows)) curWindow=GUI_WINDOW_STATS; + ImGui::End(); +} \ No newline at end of file diff --git a/src/gui/sysConf.cpp b/src/gui/sysConf.cpp new file mode 100644 index 00000000..b61245f2 --- /dev/null +++ b/src/gui/sysConf.cpp @@ -0,0 +1,371 @@ +/** + * Furnace Tracker - multi-system chiptune tracker + * Copyright (C) 2021-2022 tildearrow and contributors + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include "gui.h" + +void FurnaceGUI::drawSysConf(int i) { + unsigned int flags=e->song.systemFlags[i]; + bool restart=settings.restartOnFlagChange; + bool sysPal=flags&1; + switch (e->song.system[i]) { + case DIV_SYSTEM_YM2612: + case DIV_SYSTEM_YM2612_EXT: { + if (ImGui::RadioButton("NTSC (7.67MHz)",(flags&3)==0)) { + e->setSysFlags(i,(flags&0x80000000)|0,restart); + updateWindowTitle(); + } + if (ImGui::RadioButton("PAL (7.61MHz)",(flags&3)==1)) { + e->setSysFlags(i,(flags&0x80000000)|1,restart); + updateWindowTitle(); + } + if (ImGui::RadioButton("FM Towns (8MHz)",(flags&3)==2)) { + e->setSysFlags(i,(flags&0x80000000)|2,restart); + updateWindowTitle(); + } + if (ImGui::RadioButton("AtGames Genesis (6.13MHz)",(flags&3)==3)) { + e->setSysFlags(i,(flags&0x80000000)|3,restart); + updateWindowTitle(); + } + bool ladder=flags&0x80000000; + if (ImGui::Checkbox("Enable DAC distortion",&ladder)) { + e->setSysFlags(i,(flags&(~0x80000000))|(ladder?0x80000000:0),restart); + updateWindowTitle(); + } + break; + } + case DIV_SYSTEM_SMS: { + ImGui::Text("Clock rate:"); + if (ImGui::RadioButton("NTSC (3.58MHz)",(flags&3)==0)) { + e->setSysFlags(i,(flags&(~3))|0,restart); + updateWindowTitle(); + } + if (ImGui::RadioButton("PAL (3.55MHz)",(flags&3)==1)) { + e->setSysFlags(i,(flags&(~3))|1,restart); + updateWindowTitle(); + } + if (ImGui::RadioButton("BBC Micro (4MHz)",(flags&3)==2)) { + e->setSysFlags(i,(flags&(~3))|2,restart); + updateWindowTitle(); + } + if (ImGui::RadioButton("Half NTSC (1.79MHz)",(flags&3)==3)) { + e->setSysFlags(i,(flags&(~3))|3,restart); + updateWindowTitle(); + } + ImGui::Text("Chip type:"); + if (ImGui::RadioButton("Sega VDP/Master System",((flags>>2)&3)==0)) { + e->setSysFlags(i,(flags&(~12))|0,restart); + updateWindowTitle(); + } + if (ImGui::RadioButton("TI SN76489",((flags>>2)&3)==1)) { + e->setSysFlags(i,(flags&(~12))|4,restart); + updateWindowTitle(); + } + if (ImGui::RadioButton("TI SN76489 with Atari-like short noise",((flags>>2)&3)==2)) { + e->setSysFlags(i,(flags&(~12))|8,restart); + updateWindowTitle(); + } + /*if (ImGui::RadioButton("Game Gear",(flags>>2)==3)) { + e->setSysFlags(i,(flags&3)|12); + }*/ + + bool noPhaseReset=flags&16; + if (ImGui::Checkbox("Disable noise period change phase reset",&noPhaseReset)) { + e->setSysFlags(i,(flags&(~16))|(noPhaseReset<<4),restart); + updateWindowTitle(); + } + break; + } + case DIV_SYSTEM_OPLL: + case DIV_SYSTEM_OPLL_DRUMS: + case DIV_SYSTEM_VRC7: { + ImGui::Text("Clock rate:"); + if (ImGui::RadioButton("NTSC (3.58MHz)",(flags&15)==0)) { + e->setSysFlags(i,(flags&(~15))|0,restart); + updateWindowTitle(); + } + if (ImGui::RadioButton("PAL (3.55MHz)",(flags&15)==1)) { + e->setSysFlags(i,(flags&(~15))|1,restart); + updateWindowTitle(); + } + if (ImGui::RadioButton("BBC Micro (4MHz)",(flags&15)==2)) { + e->setSysFlags(i,(flags&(~15))|2,restart); + updateWindowTitle(); + } + if (ImGui::RadioButton("Half NTSC (1.79MHz)",(flags&15)==3)) { + e->setSysFlags(i,(flags&(~15))|3,restart); + updateWindowTitle(); + } + if (e->song.system[i]!=DIV_SYSTEM_VRC7) { + ImGui::Text("Patch set:"); + if (ImGui::RadioButton("Yamaha YM2413",((flags>>4)&15)==0)) { + e->setSysFlags(i,(flags&(~0xf0))|0,restart); + updateWindowTitle(); + } + if (ImGui::RadioButton("Yamaha YMF281",((flags>>4)&15)==1)) { + e->setSysFlags(i,(flags&(~0xf0))|0x10,restart); + updateWindowTitle(); + } + if (ImGui::RadioButton("Yamaha YM2423",((flags>>4)&15)==2)) { + e->setSysFlags(i,(flags&(~0xf0))|0x20,restart); + updateWindowTitle(); + } + if (ImGui::RadioButton("Konami VRC7",((flags>>4)&15)==3)) { + e->setSysFlags(i,(flags&(~0xf0))|0x30,restart); + updateWindowTitle(); + } + } + break; + } + case DIV_SYSTEM_YM2151: + if (ImGui::RadioButton("NTSC/X16 (3.58MHz)",flags==0)) { + e->setSysFlags(i,0,restart); + updateWindowTitle(); + } + if (ImGui::RadioButton("PAL (3.55MHz)",flags==1)) { + e->setSysFlags(i,1,restart); + updateWindowTitle(); + } + if (ImGui::RadioButton("X1/X68000 (4MHz)",flags==2)) { + e->setSysFlags(i,2,restart); + updateWindowTitle(); + } + break; + case DIV_SYSTEM_NES: + if (ImGui::RadioButton("NTSC (1.79MHz)",flags==0)) { + e->setSysFlags(i,0,restart); + updateWindowTitle(); + } + if (ImGui::RadioButton("PAL (1.67MHz)",flags==1)) { + e->setSysFlags(i,1,restart); + updateWindowTitle(); + } + if (ImGui::RadioButton("Dendy (1.77MHz)",flags==2)) { + e->setSysFlags(i,2,restart); + updateWindowTitle(); + } + break; + case DIV_SYSTEM_C64_8580: + case DIV_SYSTEM_C64_6581: + if (ImGui::RadioButton("NTSC (1.02MHz)",flags==0)) { + e->setSysFlags(i,0,restart); + updateWindowTitle(); + } + if (ImGui::RadioButton("PAL (0.99MHz)",flags==1)) { + e->setSysFlags(i,1,restart); + updateWindowTitle(); + } + if (ImGui::RadioButton("SSI 2001 (0.89MHz)",flags==2)) { + e->setSysFlags(i,2,restart); + updateWindowTitle(); + } + break; + case DIV_SYSTEM_AY8910: + case DIV_SYSTEM_AY8930: { + ImGui::Text("Clock rate:"); + if (ImGui::RadioButton("1.79MHz (ZX Spectrum NTSC/MSX)",(flags&15)==0)) { + e->setSysFlags(i,(flags&(~15))|0,restart); + updateWindowTitle(); + } + if (ImGui::RadioButton("1.77MHz (ZX Spectrum)",(flags&15)==1)) { + e->setSysFlags(i,(flags&(~15))|1,restart); + updateWindowTitle(); + } + if (ImGui::RadioButton("1.75MHz (ZX Spectrum)",(flags&15)==2)) { + e->setSysFlags(i,(flags&(~15))|2,restart); + updateWindowTitle(); + } + if (ImGui::RadioButton("2MHz (Atari ST/Sharp X1)",(flags&15)==3)) { + e->setSysFlags(i,(flags&(~15))|3,restart); + updateWindowTitle(); + } + if (ImGui::RadioButton("1.5MHz (Vectrex)",(flags&15)==4)) { + e->setSysFlags(i,(flags&(~15))|4,restart); + updateWindowTitle(); + } + if (ImGui::RadioButton("1MHz (Amstrad CPC)",(flags&15)==5)) { + e->setSysFlags(i,(flags&(~15))|5,restart); + updateWindowTitle(); + } + if (ImGui::RadioButton("0.89MHz (Sunsoft 5B)",(flags&15)==6)) { + e->setSysFlags(i,(flags&(~15))|6,restart); + updateWindowTitle(); + } + if (ImGui::RadioButton("1.67MHz (?)",(flags&15)==7)) { + e->setSysFlags(i,(flags&(~15))|7,restart); + updateWindowTitle(); + } + if (ImGui::RadioButton("0.83MHz (Sunsoft 5B on PAL)",(flags&15)==8)) { + e->setSysFlags(i,(flags&(~15))|8,restart); + updateWindowTitle(); + } + if (ImGui::RadioButton("1.10MHz (Gamate/VIC-20 PAL)",(flags&15)==9)) { + e->setSysFlags(i,(flags&(~15))|9,restart); + updateWindowTitle(); + } + if (ImGui::RadioButton("2^21Hz (Game Boy)",(flags&15)==10)) { + e->setSysFlags(i,(flags&(~15))|10,restart); + updateWindowTitle(); + } + if (e->song.system[i]==DIV_SYSTEM_AY8910) { + ImGui::Text("Chip type:"); + if (ImGui::RadioButton("AY-3-8910",(flags&0x30)==0)) { + e->setSysFlags(i,(flags&(~0x30))|0,restart); + updateWindowTitle(); + } + if (ImGui::RadioButton("YM2149(F)",(flags&0x30)==16)) { + e->setSysFlags(i,(flags&(~0x30))|16,restart); + updateWindowTitle(); + } + if (ImGui::RadioButton("Sunsoft 5B",(flags&0x30)==32)) { + e->setSysFlags(i,(flags&(~0x30))|32,restart); + updateWindowTitle(); + } + if (ImGui::RadioButton("AY-3-8914",(flags&0x30)==48)) { + e->setSysFlags(i,(flags&(~0x30))|48,restart); + updateWindowTitle(); + } + } + bool stereo=flags&0x40; + ImGui::BeginDisabled((flags&0x30)==32); + if (ImGui::Checkbox("Stereo##_AY_STEREO",&stereo)) { + e->setSysFlags(i,(flags&(~0x40))|(stereo?0x40:0),restart); + updateWindowTitle(); + } + ImGui::EndDisabled(); + break; + } + case DIV_SYSTEM_SAA1099: + if (ImGui::RadioButton("SAM Coupé (8MHz)",flags==0)) { + e->setSysFlags(i,0,restart); + updateWindowTitle(); + } + if (ImGui::RadioButton("NTSC (7.15MHz)",flags==1)) { + e->setSysFlags(i,1,restart); + updateWindowTitle(); + } + if (ImGui::RadioButton("PAL (7.09MHz)",flags==2)) { + e->setSysFlags(i,2,restart); + updateWindowTitle(); + } + break; + case DIV_SYSTEM_AMIGA: { + ImGui::Text("Stereo separation:"); + int stereoSep=(flags>>8)&127; + if (ImGui::SliderInt("##StereoSep",&stereoSep,0,127)) { + if (stereoSep<0) stereoSep=0; + if (stereoSep>127) stereoSep=127; + e->setSysFlags(i,(flags&(~0x7f00))|((stereoSep&127)<<8),restart); + updateWindowTitle(); + } rightClickable + if (ImGui::RadioButton("Amiga 500 (OCS)",(flags&2)==0)) { + e->setSysFlags(i,flags&(~2),restart); + } + if (ImGui::RadioButton("Amiga 1200 (AGA)",(flags&2)==2)) { + e->setSysFlags(i,(flags&(~2))|2,restart); + } + sysPal=flags&1; + if (ImGui::Checkbox("PAL",&sysPal)) { + e->setSysFlags(i,(flags&(~1))|sysPal,restart); + updateWindowTitle(); + } + bool bypassLimits=flags&4; + if (ImGui::Checkbox("Bypass frequency limits",&bypassLimits)) { + e->setSysFlags(i,(flags&(~4))|(bypassLimits<<2),restart); + updateWindowTitle(); + } + break; + } + case DIV_SYSTEM_PCSPKR: { + ImGui::Text("Speaker type:"); + if (ImGui::RadioButton("Unfiltered",(flags&3)==0)) { + e->setSysFlags(i,(flags&(~3))|0,restart); + updateWindowTitle(); + } + if (ImGui::RadioButton("Cone",(flags&3)==1)) { + e->setSysFlags(i,(flags&(~3))|1,restart); + updateWindowTitle(); + } + if (ImGui::RadioButton("Piezo",(flags&3)==2)) { + e->setSysFlags(i,(flags&(~3))|2,restart); + updateWindowTitle(); + } + if (ImGui::RadioButton("Use system beeper (Linux only!)",(flags&3)==3)) { + e->setSysFlags(i,(flags&(~3))|3,restart); + updateWindowTitle(); + } + break; + } + case DIV_SYSTEM_QSOUND: { + ImGui::Text("Echo delay:"); + int echoBufSize=2725 - (flags & 4095); + if (ImGui::SliderInt("##EchoBufSize",&echoBufSize,0,2725)) { + if (echoBufSize<0) echoBufSize=0; + if (echoBufSize>2725) echoBufSize=2725; + e->setSysFlags(i,(flags & ~4095) | ((2725 - echoBufSize) & 4095),restart); + updateWindowTitle(); + } rightClickable + ImGui::Text("Echo feedback:"); + int echoFeedback=(flags>>12)&255; + if (ImGui::SliderInt("##EchoFeedback",&echoFeedback,0,255)) { + if (echoFeedback<0) echoFeedback=0; + if (echoFeedback>255) echoFeedback=255; + e->setSysFlags(i,(flags & ~(255 << 12)) | ((echoFeedback & 255) << 12),restart); + updateWindowTitle(); + } rightClickable + break; + } + case DIV_SYSTEM_X1_010: { + ImGui::Text("Clock rate:"); + if (ImGui::RadioButton("16MHz (Seta 1)",(flags&15)==0)) { + e->setSysFlags(i,(flags&(~15))|0,restart); + updateWindowTitle(); + } + if (ImGui::RadioButton("16.67MHz (Seta 2)",(flags&15)==1)) { + e->setSysFlags(i,(flags&(~15))|1,restart); + updateWindowTitle(); + } + bool x1_010Stereo=flags&16; + if (ImGui::Checkbox("Stereo",&x1_010Stereo)) { + e->setSysFlags(i,(flags&(~16))|(x1_010Stereo<<4),restart); + updateWindowTitle(); + } + break; + } + case DIV_SYSTEM_GB: + case DIV_SYSTEM_SWAN: + case DIV_SYSTEM_VERA: + case DIV_SYSTEM_BUBSYS_WSG: + case DIV_SYSTEM_YM2610: + case DIV_SYSTEM_YM2610_EXT: + case DIV_SYSTEM_YM2610_FULL: + case DIV_SYSTEM_YM2610_FULL_EXT: + case DIV_SYSTEM_YM2610B: + case DIV_SYSTEM_YM2610B_EXT: + case DIV_SYSTEM_YMU759: + case DIV_SYSTEM_PET: + ImGui::Text("nothing to configure"); + break; + default: + if (ImGui::Checkbox("PAL",&sysPal)) { + e->setSysFlags(i,sysPal,restart); + updateWindowTitle(); + } + break; + } +} \ No newline at end of file diff --git a/src/gui/volMeter.cpp b/src/gui/volMeter.cpp new file mode 100644 index 00000000..29f62c96 --- /dev/null +++ b/src/gui/volMeter.cpp @@ -0,0 +1,111 @@ +/** + * Furnace Tracker - multi-system chiptune tracker + * Copyright (C) 2021-2022 tildearrow and contributors + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include "gui.h" +#include "imgui_internal.h" + +void FurnaceGUI::drawVolMeter() { + if (nextWindow==GUI_WINDOW_VOL_METER) { + volMeterOpen=true; + ImGui::SetNextWindowFocus(); + nextWindow=GUI_WINDOW_NOTHING; + } + if (!volMeterOpen) return; + if (--isClipping<0) isClipping=0; + ImGui::SetNextWindowSizeConstraints(ImVec2(6.0f*dpiScale,6.0f*dpiScale),ImVec2(scrW*dpiScale,scrH*dpiScale)); + ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding,ImVec2(0,0)); + ImGui::PushStyleVar(ImGuiStyleVar_FramePadding,ImVec2(0,0)); + ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing,ImVec2(0,0)); + ImGui::PushStyleVar(ImGuiStyleVar_ItemInnerSpacing,ImVec2(0,0)); + if (ImGui::Begin("Volume Meter",&volMeterOpen)) { + ImDrawList* dl=ImGui::GetWindowDrawList(); + bool aspectRatio=(ImGui::GetWindowSize().x/ImGui::GetWindowSize().y)>1.0; + + ImVec2 minArea=ImVec2( + ImGui::GetWindowPos().x+ImGui::GetCursorPos().x, + ImGui::GetWindowPos().y+ImGui::GetCursorPos().y + ); + ImVec2 maxArea=ImVec2( + ImGui::GetWindowPos().x+ImGui::GetCursorPos().x+ImGui::GetContentRegionAvail().x, + ImGui::GetWindowPos().y+ImGui::GetCursorPos().y+ImGui::GetContentRegionAvail().y + ); + ImRect rect=ImRect(minArea,maxArea); + ImGuiStyle& style=ImGui::GetStyle(); + ImGui::ItemSize(ImVec2(4.0f,4.0f),style.FramePadding.y); + ImU32 lowColor=ImGui::GetColorU32(uiColors[GUI_COLOR_VOLMETER_LOW]); + float peakDecay=0.05f*60.0f*ImGui::GetIO().DeltaTime; + if (ImGui::ItemAdd(rect,ImGui::GetID("volMeter"))) { + ImGui::RenderFrame(rect.Min,rect.Max,ImGui::GetColorU32(ImGuiCol_FrameBg),true,style.FrameRounding); + for (int i=0; i<2; i++) { + peak[i]*=1.0-peakDecay; + if (peak[i]<0.0001) peak[i]=0.0; + for (int j=0; joscSize; j++) { + if (fabs(e->oscBuf[i][j])>peak[i]) { + peak[i]=fabs(e->oscBuf[i][j]); + } + } + float logPeak=(20*log10(peak[i])/36.0); + if (logPeak==NAN) logPeak=0.0; + if (logPeak<-1.0) logPeak=-1.0; + if (logPeak>0.0) { + isClipping=8; + logPeak=0.0; + } + logPeak+=1.0; + ImU32 highColor=ImGui::GetColorU32( + ImLerp(uiColors[GUI_COLOR_VOLMETER_LOW],uiColors[GUI_COLOR_VOLMETER_HIGH],logPeak) + ); + ImRect s; + if (aspectRatio) { + s=ImRect( + ImLerp(rect.Min,rect.Max,ImVec2(0,float(i)*0.5)), + ImLerp(rect.Min,rect.Max,ImVec2(logPeak,float(i+1)*0.5)) + ); + if (i==0) s.Max.y-=dpiScale; + if (isClipping) { + dl->AddRectFilled(s.Min,s.Max,ImGui::GetColorU32(uiColors[GUI_COLOR_VOLMETER_PEAK])); + } else { + dl->AddRectFilledMultiColor(s.Min,s.Max,lowColor,highColor,highColor,lowColor); + } + } else { + s=ImRect( + ImLerp(rect.Min,rect.Max,ImVec2(float(i)*0.5,1.0-logPeak)), + ImLerp(rect.Min,rect.Max,ImVec2(float(i+1)*0.5,1.0)) + ); + if (i==0) s.Max.x-=dpiScale; + if (isClipping) { + dl->AddRectFilled(s.Min,s.Max,ImGui::GetColorU32(uiColors[GUI_COLOR_VOLMETER_PEAK])); + } else { + dl->AddRectFilledMultiColor(s.Min,s.Max,highColor,highColor,lowColor,lowColor); + } + } + } + if (ImGui::IsItemHovered()) { + if (aspectRatio) { + ImGui::SetTooltip("%.1fdB",36*((ImGui::GetMousePos().x-ImGui::GetItemRectMin().x)/(rect.Max.x-rect.Min.x)-1.0)); + } else { + ImGui::SetTooltip("%.1fdB",-(36+36*((ImGui::GetMousePos().y-ImGui::GetItemRectMin().y)/(rect.Max.y-rect.Min.y)-1.0))); + } + } + } + } + ImGui::PopStyleVar(4); + if (ImGui::IsWindowFocused(ImGuiFocusedFlags_ChildWindows)) curWindow=GUI_WINDOW_VOL_METER; + ImGui::End(); +} \ No newline at end of file diff --git a/src/gui/waveEdit.cpp b/src/gui/waveEdit.cpp index 09ec95b1..3fba43f7 100644 --- a/src/gui/waveEdit.cpp +++ b/src/gui/waveEdit.cpp @@ -1,3 +1,22 @@ +/** + * Furnace Tracker - multi-system chiptune tracker + * Copyright (C) 2021-2022 tildearrow and contributors + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + #include "gui.h" #include "plot_nolerp.h" #include "misc/cpp/imgui_stdlib.h" From 80927b80b12014205ff80fb47d14f1f07d106ec0 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Mon, 21 Mar 2022 17:38:11 -0500 Subject: [PATCH 20/40] fix build --- src/gui/about.cpp | 4 +++- src/gui/actionUtil.h | 5 +++-- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/src/gui/about.cpp b/src/gui/about.cpp index cd5888c6..4d1322a5 100644 --- a/src/gui/about.cpp +++ b/src/gui/about.cpp @@ -17,7 +17,9 @@ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ +#define _USE_MATH_DEFINES #include "gui.h" +#include const char* aboutLine[]={ "tildearrow", @@ -208,4 +210,4 @@ void FurnaceGUI::drawAbout() { } if (ImGui::IsWindowFocused(ImGuiFocusedFlags_ChildWindows)) curWindow=GUI_WINDOW_ABOUT; ImGui::End(); -} \ No newline at end of file +} diff --git a/src/gui/actionUtil.h b/src/gui/actionUtil.h index 38ed9a5c..4e9196af 100644 --- a/src/gui/actionUtil.h +++ b/src/gui/actionUtil.h @@ -5,7 +5,7 @@ firstChannel=i; \ break; \ } \ - } \ + } #define DETERMINE_LAST \ int lastChannel=0; \ @@ -18,4 +18,5 @@ #define DETERMINE_FIRST_LAST \ DETERMINE_FIRST \ - DETERMINE_LAST \ No newline at end of file + DETERMINE_LAST + From 6050fc72082ce3ad5e3f619895baf48336a380ab Mon Sep 17 00:00:00 2001 From: tildearrow Date: Mon, 21 Mar 2022 17:56:48 -0500 Subject: [PATCH 21/40] one more race condition fix --- src/engine/engine.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/engine/engine.cpp b/src/engine/engine.cpp index 2b328391..fb11f1df 100644 --- a/src/engine/engine.cpp +++ b/src/engine/engine.cpp @@ -614,9 +614,11 @@ void DivEngine::createNew(const int* description) { void DivEngine::changeSystem(int index, DivSystem which) { quitDispatch(); isBusy.lock(); + saveLock.lock(); song.system[index]=which; song.systemFlags[index]=0; recalcChans(); + saveLock.unlock(); isBusy.unlock(); initDispatch(); isBusy.lock(); @@ -637,11 +639,13 @@ bool DivEngine::addSystem(DivSystem which) { } quitDispatch(); isBusy.lock(); + saveLock.lock(); song.system[song.systemLen]=which; song.systemVol[song.systemLen]=64; song.systemPan[song.systemLen]=0; song.systemFlags[song.systemLen++]=0; recalcChans(); + saveLock.unlock(); isBusy.unlock(); initDispatch(); isBusy.lock(); @@ -662,12 +666,14 @@ bool DivEngine::removeSystem(int index) { } quitDispatch(); isBusy.lock(); + saveLock.lock(); song.system[index]=DIV_SYSTEM_NULL; song.systemLen--; for (int i=index; i Date: Mon, 21 Mar 2022 17:59:48 -0500 Subject: [PATCH 22/40] update README.md --- README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 15f0dcd2..b5e28cf6 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ ![screenshot](papers/screenshot1.png) -this is a work-in-progress chiptune tracker compatible with DefleMask modules (.dmf). +a multi-system chiptune tracker. [downloads](#downloads) | [discussion](#discussion) | [help](#help) | [developer info](#developer-info) @@ -23,6 +23,7 @@ this is a work-in-progress chiptune tracker compatible with DefleMask modules (. - Amiga - TIA (Atari 2600/7800) - multiple sound chips in a single song! +- DefleMask compatibility - loads .dmf modules, .dmp instruments and .dmw wavetables - clean-room design (guesswork and ABX tests only, no decompilation involved) - bug/quirk implementation for increased playback accuracy - VGM and audio file export From 322981b0215c60e2c1bfc7aad52e55811beb4af5 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Mon, 21 Mar 2022 21:26:36 -0500 Subject: [PATCH 23/40] maybe fix possible crash regarding smp loop points --- src/engine/platform/amiga.cpp | 2 +- src/engine/platform/genesis.cpp | 4 ++-- src/engine/platform/nes.cpp | 2 +- src/engine/platform/pce.cpp | 2 +- src/engine/platform/segapcm.cpp | 2 +- src/engine/platform/swan.cpp | 2 +- src/engine/platform/vera.cpp | 2 +- 7 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/engine/platform/amiga.cpp b/src/engine/platform/amiga.cpp index 49b5b59a..d160d112 100644 --- a/src/engine/platform/amiga.cpp +++ b/src/engine/platform/amiga.cpp @@ -75,7 +75,7 @@ void DivPlatformAmiga::acquire(short* bufL, short* bufR, size_t start, size_t le if (s->samples>0) { chan[i].audDat=s->data8[chan[i].audPos++]; if (chan[i].audPos>=s->samples || chan[i].audPos>=131071) { - if (s->loopStart>=0 && s->loopStart<=(int)s->samples) { + if (s->loopStart>=0 && s->loopStart<(int)s->samples) { chan[i].audPos=s->loopStart; } else { chan[i].sample=-1; diff --git a/src/engine/platform/genesis.cpp b/src/engine/platform/genesis.cpp index 75c54af6..46e24333 100644 --- a/src/engine/platform/genesis.cpp +++ b/src/engine/platform/genesis.cpp @@ -95,7 +95,7 @@ void DivPlatformGenesis::acquire_nuked(short* bufL, short* bufR, size_t start, s urgentWrite(0x2a,(unsigned char)s->data8[dacPos]+0x80); } if (++dacPos>=s->samples) { - if (s->loopStart>=0 && s->loopStart<=(int)s->samples) { + if (s->loopStart>=0 && s->loopStart<(int)s->samples) { dacPos=s->loopStart; } else { dacSample=-1; @@ -162,7 +162,7 @@ void DivPlatformGenesis::acquire_ymfm(short* bufL, short* bufR, size_t start, si urgentWrite(0x2a,(unsigned char)s->data8[dacPos]+0x80); } if (++dacPos>=s->samples) { - if (s->loopStart>=0 && s->loopStart<=(int)s->samples) { + if (s->loopStart>=0 && s->loopStart<(int)s->samples) { dacPos=s->loopStart; } else { dacSample=-1; diff --git a/src/engine/platform/nes.cpp b/src/engine/platform/nes.cpp index 06fbc725..d04edf4d 100644 --- a/src/engine/platform/nes.cpp +++ b/src/engine/platform/nes.cpp @@ -81,7 +81,7 @@ void DivPlatformNES::acquire(short* bufL, short* bufR, size_t start, size_t len) rWrite(0x4011,((unsigned char)s->data8[dacPos]+0x80)>>1); } if (++dacPos>=s->samples) { - if (s->loopStart>=0 && s->loopStart<=(int)s->samples) { + if (s->loopStart>=0 && s->loopStart<(int)s->samples) { dacPos=s->loopStart; } else { dacSample=-1; diff --git a/src/engine/platform/pce.cpp b/src/engine/platform/pce.cpp index 1af1741c..bf7d0d8d 100644 --- a/src/engine/platform/pce.cpp +++ b/src/engine/platform/pce.cpp @@ -91,7 +91,7 @@ void DivPlatformPCE::acquire(short* bufL, short* bufR, size_t start, size_t len) chWrite(i,0x06,(((unsigned char)s->data8[chan[i].dacPos]+0x80)>>3)); chan[i].dacPos++; if (chan[i].dacPos>=s->samples) { - if (s->loopStart>=0 && s->loopStart<=(int)s->samples) { + if (s->loopStart>=0 && s->loopStart<(int)s->samples) { chan[i].dacPos=s->loopStart; } else { chan[i].dacSample=-1; diff --git a/src/engine/platform/segapcm.cpp b/src/engine/platform/segapcm.cpp index 7872e0b6..c0366a18 100644 --- a/src/engine/platform/segapcm.cpp +++ b/src/engine/platform/segapcm.cpp @@ -54,7 +54,7 @@ void DivPlatformSegaPCM::acquire(short* bufL, short* bufR, size_t start, size_t } chan[i].pcm.pos+=chan[i].pcm.freq; if (chan[i].pcm.pos>=(s->samples<<8)) { - if (s->loopStart>=0 && s->loopStart<=(int)s->samples) { + if (s->loopStart>=0 && s->loopStart<(int)s->samples) { chan[i].pcm.pos=s->loopStart<<8; } else { chan[i].pcm.sample=-1; diff --git a/src/engine/platform/swan.cpp b/src/engine/platform/swan.cpp index de623647..0f32e9a0 100644 --- a/src/engine/platform/swan.cpp +++ b/src/engine/platform/swan.cpp @@ -84,7 +84,7 @@ void DivPlatformSwan::acquire(short* bufL, short* bufR, size_t start, size_t len } rWrite(0x09,(unsigned char)s->data8[dacPos++]+0x80); if (dacPos>=s->samples) { - if (s->loopStart>=0 && s->loopStart<=(int)s->samples) { + if (s->loopStart>=0 && s->loopStart<(int)s->samples) { dacPos=s->loopStart; } else { dacSample=-1; diff --git a/src/engine/platform/vera.cpp b/src/engine/platform/vera.cpp index a4d76aa0..522e4be6 100644 --- a/src/engine/platform/vera.cpp +++ b/src/engine/platform/vera.cpp @@ -97,7 +97,7 @@ void DivPlatformVERA::acquire(short* bufL, short* bufR, size_t start, size_t len } chan[16].pcm.pos++; if (chan[16].pcm.pos>=s->samples) { - if (s->loopStart>=0 && s->loopStart<=(int)s->samples) { + if (s->loopStart>=0 && s->loopStart<(int)s->samples) { chan[16].pcm.pos=s->loopStart; } else { chan[16].pcm.sample=-1; From cec5def34733a39f9b43f52deab5bf82ab7a4b10 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Mon, 21 Mar 2022 21:47:27 -0500 Subject: [PATCH 24/40] what? unacceptable. --- src/engine/fileOps.cpp | 7 +++++++ src/engine/sample.cpp | 1 + src/gui/gui.cpp | 2 +- 3 files changed, 9 insertions(+), 1 deletion(-) diff --git a/src/engine/fileOps.cpp b/src/engine/fileOps.cpp index 460f7d8d..b50eb74a 100644 --- a/src/engine/fileOps.cpp +++ b/src/engine/fileOps.cpp @@ -1030,6 +1030,7 @@ bool DivEngine::loadFur(unsigned char* file, size_t len) { reader.read(samplePtr,ds.sampleLen*4); for (int i=0; ireadInsData(reader,ds.version)!=DIV_DATA_SUCCESS) { @@ -1084,6 +1086,7 @@ bool DivEngine::loadFur(unsigned char* file, size_t len) { // read wavetables for (int i=0; ireadWaveData(reader,ds.version)!=DIV_DATA_SUCCESS) { @@ -1111,6 +1114,7 @@ bool DivEngine::loadFur(unsigned char* file, size_t len) { } reader.readI(); DivSample* sample=new DivSample; + logD("reading sample %d at %x...\n",i,samplePtr[i]); sample->name=reader.readString(); sample->samples=reader.readI(); @@ -1184,6 +1188,7 @@ bool DivEngine::loadFur(unsigned char* file, size_t len) { for (int i: patPtr) { reader.seek(i,SEEK_SET); reader.read(magic,4); + logD("reading pattern in %x...\n",i); if (strcmp(magic,"PATR")!=0) { logE("%x: invalid pattern header!\n",i); lastError="invalid pattern header!"; @@ -1196,6 +1201,8 @@ bool DivEngine::loadFur(unsigned char* file, size_t len) { int index=reader.readS(); reader.readI(); + logD("- %d, %d\n",chan,index); + DivPattern* pat=ds.pat[chan].getPattern(index,true); for (int j=0; jdata[j][0]=reader.readS(); diff --git a/src/engine/sample.cpp b/src/engine/sample.cpp index be9c5029..69e61e54 100644 --- a/src/engine/sample.cpp +++ b/src/engine/sample.cpp @@ -279,6 +279,7 @@ bool DivSample::trim(unsigned int begin, unsigned int end) { #define RESAMPLE_END \ samples=finalCount; \ + if (loopStart>=0) loopStart=(double)loopStart*(r/(double)rate); \ if (depth==16) { \ delete[] oldData16; \ } else if (depth==8) { \ diff --git a/src/gui/gui.cpp b/src/gui/gui.cpp index 2ba2131c..7a146425 100644 --- a/src/gui/gui.cpp +++ b/src/gui/gui.cpp @@ -2435,9 +2435,9 @@ bool FurnaceGUI::loop() { if (outFile!=NULL) { if (fwrite(w->getFinalBuf(),1,w->size(),outFile)!=w->size()) { logW("did not write backup entirely: %s!\n",strerror(errno)); - fclose(outFile); w->finish(); } + fclose(outFile); } else { logW("could not save backup: %s!\n",strerror(errno)); w->finish(); From bd84dc1c17bf66a161012672818c082a5579f471 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Mon, 21 Mar 2022 21:48:36 -0500 Subject: [PATCH 25/40] GUI: make clang happy --- src/gui/gui.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/gui/gui.cpp b/src/gui/gui.cpp index 7a146425..4af736ee 100644 --- a/src/gui/gui.cpp +++ b/src/gui/gui.cpp @@ -1745,7 +1745,7 @@ bool FurnaceGUI::loop() { } } if (macroDragActive || macroLoopDragActive || waveDragActive || sampleDragActive) { - int distance=fabs(motionXrel); + int distance=fabs((double)motionXrel); if (distance<1) distance=1; float start=motionX-motionXrel; float end=motionX; From e256efa6416cc99f5ea124fb0db78c44fd895e16 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Mon, 21 Mar 2022 23:36:17 -0500 Subject: [PATCH 26/40] GUI: add option to load Japanese chars issue #52 --- src/gui/fonts.h | 5 +++ src/gui/gui.cpp | 5 +++ src/gui/gui.h | 3 ++ src/gui/settings.cpp | 77 ++++++++++++++++++++++++++++++++++---------- 4 files changed, 73 insertions(+), 17 deletions(-) diff --git a/src/gui/fonts.h b/src/gui/fonts.h index 8f808a06..a0a42d25 100644 --- a/src/gui/fonts.h +++ b/src/gui/fonts.h @@ -46,3 +46,8 @@ extern const unsigned int builtinFontLen[]; extern const unsigned int* builtinFontM[]; extern const unsigned int builtinFontMLen[]; #endif + +// "(Glimmer of hope: the atlas system will be rewritten in the future to make scaling more flexible.)"" +// not just that. somebody rewrite it already so I can load these glyphs at run-time and support +// all languages at once, instead of having to load Unifont's 65k+ characters and blow the GPU up in the +// process! \ No newline at end of file diff --git a/src/gui/gui.cpp b/src/gui/gui.cpp index 4af736ee..ddb7433d 100644 --- a/src/gui/gui.cpp +++ b/src/gui/gui.cpp @@ -2699,6 +2699,11 @@ FurnaceGUI::FurnaceGUI(): aboutSin(0), aboutHue(0.0f), backupTimer(15.0), + mainFont(NULL), + iconFont(NULL), + patFont(NULL), + bigFont(NULL), + fontRange(NULL), curIns(0), curWave(0), curSample(0), diff --git a/src/gui/gui.h b/src/gui/gui.h index bfecdbb1..7433750e 100644 --- a/src/gui/gui.h +++ b/src/gui/gui.h @@ -530,6 +530,7 @@ class FurnaceGUI { ImFont* iconFont; ImFont* patFont; ImFont* bigFont; + ImWchar* fontRange; ImVec4 uiColors[GUI_COLOR_MAX]; ImVec4 volColors[128]; ImU32 pitchGrad[256]; @@ -584,6 +585,7 @@ class FurnaceGUI { int roundedWindows; int roundedButtons; int roundedMenus; + int loadJapanese; unsigned int maxUndoSteps; String mainFontPath; String patFontPath; @@ -633,6 +635,7 @@ class FurnaceGUI { roundedWindows(1), roundedButtons(1), roundedMenus(0), + loadJapanese(0), maxUndoSteps(100), mainFontPath(""), patFontPath(""), diff --git a/src/gui/settings.cpp b/src/gui/settings.cpp index a3fd347d..5348bfa0 100644 --- a/src/gui/settings.cpp +++ b/src/gui/settings.cpp @@ -346,6 +346,19 @@ void FurnaceGUI::drawSettings() { if (settings.patFontSize>96) settings.patFontSize=96; } + bool loadJapaneseB=settings.loadJapanese; + if (ImGui::Checkbox("Display Japanese characters",&loadJapaneseB)) { + settings.loadJapanese=loadJapaneseB; + } + if (ImGui::IsItemHovered()) { + ImGui::SetTooltip( + "Only toggle this option if you have enough graphics memory.\n" + "This is a temporary solution until dynamic font atlas is implemented in Dear ImGui.\n\n" + "このオプションは、十分なグラフィックメモリがある場合にのみ切り替えてください。\n" + "これは、Dear ImGuiにダイナミックフォントアトラスが実装されるまでの一時的な解決策です。" + ); + } + ImGui::Separator(); ImGui::Text("Orders row number format:"); @@ -927,6 +940,7 @@ void FurnaceGUI::syncSettings() { settings.roundedWindows=e->getConfInt("roundedWindows",1); settings.roundedButtons=e->getConfInt("roundedButtons",1); settings.roundedMenus=e->getConfInt("roundedMenus",0); + settings.loadJapanese=e->getConfInt("loadJapanese",0); clampSetting(settings.mainFontSize,2,96); clampSetting(settings.patFontSize,2,96); @@ -970,6 +984,7 @@ void FurnaceGUI::syncSettings() { clampSetting(settings.roundedWindows,0,1); clampSetting(settings.roundedButtons,0,1); clampSetting(settings.roundedMenus,0,1); + clampSetting(settings.loadJapanese,0,1); // keybinds LOAD_KEYBIND(GUI_ACTION_OPEN,FURKMOD_CMD|SDLK_o); @@ -1175,6 +1190,7 @@ void FurnaceGUI::commitSettings() { e->setConf("roundedWindows",settings.roundedWindows); e->setConf("roundedButtons",settings.roundedButtons); e->setConf("roundedMenus",settings.roundedMenus); + e->setConf("loadJapanese",settings.loadJapanese); PUT_UI_COLOR(GUI_COLOR_BACKGROUND); PUT_UI_COLOR(GUI_COLOR_FRAME_BACKGROUND); @@ -1710,7 +1726,24 @@ void FurnaceGUI::applyUISettings() { } // set to 800 for now due to problems with unifont - static const ImWchar loadEverything[]={0x20,0x800,0}; + static const ImWchar upTo800[]={0x20,0x7e,0xa0,0x800,0}; + ImFontGlyphRangesBuilder range; + ImVector outRange; + + range.AddRanges(upTo800); + if (settings.loadJapanese) { + range.AddRanges(ImGui::GetIO().Fonts->GetGlyphRangesJapanese()); + } + // I'm terribly sorry + range.UsedChars[0x80>>5]=0; + + range.BuildRanges(&outRange); + if (fontRange!=NULL) delete[] fontRange; + fontRange=new ImWchar[outRange.size()]; + int index=0; + for (ImWchar& i: outRange) { + fontRange[index++]=i; + } if (settings.mainFont<0 || settings.mainFont>6) settings.mainFont=0; if (settings.patFont<0 || settings.patFont>6) settings.patFont=0; @@ -1724,22 +1757,25 @@ void FurnaceGUI::applyUISettings() { settings.patFont=0; } + ImFontConfig fc1; + fc1.MergeMode=true; + if (settings.mainFont==6) { // custom font - if ((mainFont=ImGui::GetIO().Fonts->AddFontFromFileTTF(settings.mainFontPath.c_str(),e->getConfInt("mainFontSize",18)*dpiScale,NULL,loadEverything))==NULL) { + if ((mainFont=ImGui::GetIO().Fonts->AddFontFromFileTTF(settings.mainFontPath.c_str(),e->getConfInt("mainFontSize",18)*dpiScale,NULL,fontRange))==NULL) { logW("could not load UI font! reverting to default font\n"); settings.mainFont=0; - if ((mainFont=ImGui::GetIO().Fonts->AddFontFromMemoryCompressedTTF(builtinFont[settings.mainFont],builtinFontLen[settings.mainFont],e->getConfInt("mainFontSize",18)*dpiScale,NULL,loadEverything))==NULL) { + if ((mainFont=ImGui::GetIO().Fonts->AddFontFromMemoryCompressedTTF(builtinFont[settings.mainFont],builtinFontLen[settings.mainFont],e->getConfInt("mainFontSize",18)*dpiScale,NULL,fontRange))==NULL) { logE("could not load UI font! falling back to Proggy Clean.\n"); mainFont=ImGui::GetIO().Fonts->AddFontDefault(); } } } else if (settings.mainFont==5) { // system font - if ((mainFont=ImGui::GetIO().Fonts->AddFontFromFileTTF(SYSTEM_FONT_PATH_1,e->getConfInt("mainFontSize",18)*dpiScale,NULL,loadEverything))==NULL) { - if ((mainFont=ImGui::GetIO().Fonts->AddFontFromFileTTF(SYSTEM_FONT_PATH_2,e->getConfInt("mainFontSize",18)*dpiScale,NULL,loadEverything))==NULL) { - if ((mainFont=ImGui::GetIO().Fonts->AddFontFromFileTTF(SYSTEM_FONT_PATH_3,e->getConfInt("mainFontSize",18)*dpiScale,NULL,loadEverything))==NULL) { + if ((mainFont=ImGui::GetIO().Fonts->AddFontFromFileTTF(SYSTEM_FONT_PATH_1,e->getConfInt("mainFontSize",18)*dpiScale,NULL,fontRange))==NULL) { + if ((mainFont=ImGui::GetIO().Fonts->AddFontFromFileTTF(SYSTEM_FONT_PATH_2,e->getConfInt("mainFontSize",18)*dpiScale,NULL,fontRange))==NULL) { + if ((mainFont=ImGui::GetIO().Fonts->AddFontFromFileTTF(SYSTEM_FONT_PATH_3,e->getConfInt("mainFontSize",18)*dpiScale,NULL,fontRange))==NULL) { logW("could not load UI font! reverting to default font\n"); settings.mainFont=0; - if ((mainFont=ImGui::GetIO().Fonts->AddFontFromMemoryCompressedTTF(builtinFont[settings.mainFont],builtinFontLen[settings.mainFont],e->getConfInt("mainFontSize",18)*dpiScale,NULL,loadEverything))==NULL) { + if ((mainFont=ImGui::GetIO().Fonts->AddFontFromMemoryCompressedTTF(builtinFont[settings.mainFont],builtinFontLen[settings.mainFont],e->getConfInt("mainFontSize",18)*dpiScale,NULL,fontRange))==NULL) { logE("could not load UI font! falling back to Proggy Clean.\n"); mainFont=ImGui::GetIO().Fonts->AddFontDefault(); } @@ -1747,17 +1783,21 @@ void FurnaceGUI::applyUISettings() { } } } else { - if ((mainFont=ImGui::GetIO().Fonts->AddFontFromMemoryCompressedTTF(builtinFont[settings.mainFont],builtinFontLen[settings.mainFont],e->getConfInt("mainFontSize",18)*dpiScale,NULL,loadEverything))==NULL) { + if ((mainFont=ImGui::GetIO().Fonts->AddFontFromMemoryCompressedTTF(builtinFont[settings.mainFont],builtinFontLen[settings.mainFont],e->getConfInt("mainFontSize",18)*dpiScale,NULL,fontRange))==NULL) { logE("could not load UI font! falling back to Proggy Clean.\n"); mainFont=ImGui::GetIO().Fonts->AddFontDefault(); } } + // two fallback fonts + mainFont=ImGui::GetIO().Fonts->AddFontFromMemoryCompressedTTF(font_liberationSans_compressed_data,font_liberationSans_compressed_size,e->getConfInt("mainFontSize",18)*dpiScale,&fc1,fontRange); + mainFont=ImGui::GetIO().Fonts->AddFontFromMemoryCompressedTTF(font_unifont_compressed_data,font_unifont_compressed_size,e->getConfInt("mainFontSize",18)*dpiScale,&fc1,fontRange); + ImFontConfig fc; fc.MergeMode=true; fc.GlyphMinAdvanceX=e->getConfInt("iconSize",16)*dpiScale; - static const ImWchar fontRange[]={ICON_MIN_FA,ICON_MAX_FA,0}; - if ((iconFont=ImGui::GetIO().Fonts->AddFontFromMemoryCompressedTTF(iconFont_compressed_data,iconFont_compressed_size,e->getConfInt("iconSize",16)*dpiScale,&fc,fontRange))==NULL) { + static const ImWchar fontRangeIcon[]={ICON_MIN_FA,ICON_MAX_FA,0}; + if ((iconFont=ImGui::GetIO().Fonts->AddFontFromMemoryCompressedTTF(iconFont_compressed_data,iconFont_compressed_size,e->getConfInt("iconSize",16)*dpiScale,&fc,fontRangeIcon))==NULL) { logE("could not load icon font!\n"); } if (settings.mainFontSize==settings.patFontSize && settings.patFont<5 && builtinFontM[settings.patFont]==builtinFont[settings.mainFont]) { @@ -1765,21 +1805,21 @@ void FurnaceGUI::applyUISettings() { patFont=mainFont; } else { if (settings.patFont==6) { // custom font - if ((patFont=ImGui::GetIO().Fonts->AddFontFromFileTTF(settings.patFontPath.c_str(),e->getConfInt("patFontSize",18)*dpiScale,NULL,loadEverything))==NULL) { + if ((patFont=ImGui::GetIO().Fonts->AddFontFromFileTTF(settings.patFontPath.c_str(),e->getConfInt("patFontSize",18)*dpiScale,NULL,upTo800))==NULL) { logW("could not load pattern font! reverting to default font\n"); settings.patFont=0; - if ((patFont=ImGui::GetIO().Fonts->AddFontFromMemoryCompressedTTF(builtinFontM[settings.patFont],builtinFontMLen[settings.patFont],e->getConfInt("patFontSize",18)*dpiScale,NULL,loadEverything))==NULL) { + if ((patFont=ImGui::GetIO().Fonts->AddFontFromMemoryCompressedTTF(builtinFontM[settings.patFont],builtinFontMLen[settings.patFont],e->getConfInt("patFontSize",18)*dpiScale,NULL,upTo800))==NULL) { logE("could not load pattern font! falling back to Proggy Clean.\n"); patFont=ImGui::GetIO().Fonts->AddFontDefault(); } } } else if (settings.patFont==5) { // system font - if ((patFont=ImGui::GetIO().Fonts->AddFontFromFileTTF(SYSTEM_PAT_FONT_PATH_1,e->getConfInt("patFontSize",18)*dpiScale,NULL,loadEverything))==NULL) { - if ((patFont=ImGui::GetIO().Fonts->AddFontFromFileTTF(SYSTEM_PAT_FONT_PATH_2,e->getConfInt("patFontSize",18)*dpiScale,NULL,loadEverything))==NULL) { - if ((patFont=ImGui::GetIO().Fonts->AddFontFromFileTTF(SYSTEM_PAT_FONT_PATH_3,e->getConfInt("patFontSize",18)*dpiScale,NULL,loadEverything))==NULL) { + if ((patFont=ImGui::GetIO().Fonts->AddFontFromFileTTF(SYSTEM_PAT_FONT_PATH_1,e->getConfInt("patFontSize",18)*dpiScale,NULL,upTo800))==NULL) { + if ((patFont=ImGui::GetIO().Fonts->AddFontFromFileTTF(SYSTEM_PAT_FONT_PATH_2,e->getConfInt("patFontSize",18)*dpiScale,NULL,upTo800))==NULL) { + if ((patFont=ImGui::GetIO().Fonts->AddFontFromFileTTF(SYSTEM_PAT_FONT_PATH_3,e->getConfInt("patFontSize",18)*dpiScale,NULL,upTo800))==NULL) { logW("could not load pattern font! reverting to default font\n"); settings.patFont=0; - if ((patFont=ImGui::GetIO().Fonts->AddFontFromMemoryCompressedTTF(builtinFontM[settings.patFont],builtinFontMLen[settings.patFont],e->getConfInt("patFontSize",18)*dpiScale,NULL,loadEverything))==NULL) { + if ((patFont=ImGui::GetIO().Fonts->AddFontFromMemoryCompressedTTF(builtinFontM[settings.patFont],builtinFontMLen[settings.patFont],e->getConfInt("patFontSize",18)*dpiScale,NULL,upTo800))==NULL) { logE("could not load pattern font! falling back to Proggy Clean.\n"); patFont=ImGui::GetIO().Fonts->AddFontDefault(); } @@ -1787,7 +1827,7 @@ void FurnaceGUI::applyUISettings() { } } } else { - if ((patFont=ImGui::GetIO().Fonts->AddFontFromMemoryCompressedTTF(builtinFontM[settings.patFont],builtinFontMLen[settings.patFont],e->getConfInt("patFontSize",18)*dpiScale,NULL,loadEverything))==NULL) { + if ((patFont=ImGui::GetIO().Fonts->AddFontFromMemoryCompressedTTF(builtinFontM[settings.patFont],builtinFontMLen[settings.patFont],e->getConfInt("patFontSize",18)*dpiScale,NULL,upTo800))==NULL) { logE("could not load pattern font!\n"); patFont=ImGui::GetIO().Fonts->AddFontDefault(); } @@ -1797,6 +1837,9 @@ void FurnaceGUI::applyUISettings() { logE("could not load big UI font!\n"); } + mainFont->FallbackChar='?'; + mainFont->DotChar='.'; + // TODO: allow changing these colors. ImGuiFileDialog::Instance()->SetFileStyle(IGFD_FileStyleByTypeDir,"",ImVec4(0.0f,1.0f,1.0f,1.0f),ICON_FA_FOLDER_O); ImGuiFileDialog::Instance()->SetFileStyle(IGFD_FileStyleByTypeFile,"",ImVec4(0.7f,0.7f,0.7f,1.0f),ICON_FA_FILE_O); From 13eb0f96f1e34058cc31886dbdc75ab484874601 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Mon, 21 Mar 2022 23:48:18 -0500 Subject: [PATCH 27/40] double facepalm --- src/engine/sample.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/engine/sample.cpp b/src/engine/sample.cpp index 69e61e54..55982e59 100644 --- a/src/engine/sample.cpp +++ b/src/engine/sample.cpp @@ -460,7 +460,7 @@ bool DivSample::resampleBlep(double r) { } } } else if (depth==8) { - memset(data8,0,finalCount*sizeof(short)); + memset(data8,0,finalCount); for (int i=0; i Date: Mon, 21 Mar 2022 23:54:01 -0500 Subject: [PATCH 28/40] scale loop when resampling --- src/engine/sample.cpp | 13 ++----------- 1 file changed, 2 insertions(+), 11 deletions(-) diff --git a/src/engine/sample.cpp b/src/engine/sample.cpp index 55982e59..66e54d85 100644 --- a/src/engine/sample.cpp +++ b/src/engine/sample.cpp @@ -278,8 +278,9 @@ bool DivSample::trim(unsigned int begin, unsigned int end) { } #define RESAMPLE_END \ - samples=finalCount; \ if (loopStart>=0) loopStart=(double)loopStart*(r/(double)rate); \ + rate=r; \ + samples=finalCount; \ if (depth==16) { \ delete[] oldData16; \ } else if (depth==8) { \ @@ -309,8 +310,6 @@ bool DivSample::resampleNone(double r) { } } - rate=r; - RESAMPLE_END; return true; } @@ -350,8 +349,6 @@ bool DivSample::resampleLinear(double r) { } } - rate=r; - RESAMPLE_END; return true; } @@ -406,8 +403,6 @@ bool DivSample::resampleCubic(double r) { } } - rate=r; - RESAMPLE_END; return true; } @@ -497,8 +492,6 @@ bool DivSample::resampleBlep(double r) { } } - rate=r; - RESAMPLE_END; return true; } @@ -566,8 +559,6 @@ bool DivSample::resampleSinc(double r) { } } - rate=r; - RESAMPLE_END; return true; } From a1969d30f6c0a0476192acaceb0c878534999d29 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Tue, 22 Mar 2022 00:06:57 -0500 Subject: [PATCH 29/40] rename x1_010 to x1-010 --- papers/doc/7-systems/README.md | 2 +- papers/doc/7-systems/{x1_010.md => x1-010.md} | 0 2 files changed, 1 insertion(+), 1 deletion(-) rename papers/doc/7-systems/{x1_010.md => x1-010.md} (100%) diff --git a/papers/doc/7-systems/README.md b/papers/doc/7-systems/README.md index 5413b0b5..757a41f5 100644 --- a/papers/doc/7-systems/README.md +++ b/papers/doc/7-systems/README.md @@ -18,7 +18,7 @@ this is a list of systems that Furnace supports, including each system's effects - [Atari 2600](tia.md) - [Philips SAA1099](saa1099.md) - [Microchip AY8930](ay8930.md) -- [Seta/Allumer X1-010](x1_010.md) +- [Seta/Allumer X1-010](x1-010.md) - [WonderSwan](wonderswan.md) - [Bubble System WSG](bubblesystem.md) diff --git a/papers/doc/7-systems/x1_010.md b/papers/doc/7-systems/x1-010.md similarity index 100% rename from papers/doc/7-systems/x1_010.md rename to papers/doc/7-systems/x1-010.md From 848da5bcc52c1ea2c8ac79e0155528f443961f8e Mon Sep 17 00:00:00 2001 From: tildearrow Date: Tue, 22 Mar 2022 00:13:28 -0500 Subject: [PATCH 30/40] VERA: 0 to 3F --- src/engine/platform/vera.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/engine/platform/vera.cpp b/src/engine/platform/vera.cpp index 522e4be6..8965719f 100644 --- a/src/engine/platform/vera.cpp +++ b/src/engine/platform/vera.cpp @@ -57,7 +57,7 @@ const char* DivPlatformVERA::getEffectName(unsigned char effect) { return "20xx: Change waveform"; break; case 0x22: - return "22xx: Set duty cycle (0 to 63)"; + return "22xx: Set duty cycle (0 to 3F)"; break; } return NULL; From 2589709d1afbfa26a2270bd4bf7aa9a92f9da7f7 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Tue, 22 Mar 2022 00:16:19 -0500 Subject: [PATCH 31/40] add doc for VERA chip --- papers/doc/7-systems/vera.md | 15 +++++++++++++++ 1 file changed, 15 insertions(+) create mode 100644 papers/doc/7-systems/vera.md diff --git a/papers/doc/7-systems/vera.md b/papers/doc/7-systems/vera.md new file mode 100644 index 00000000..7d1b09c4 --- /dev/null +++ b/papers/doc/7-systems/vera.md @@ -0,0 +1,15 @@ +# VERA + +this is a video and sound generator chip used in the Commander X16, a modern 8-bit computer created by The 8-Bit Guy. +it has 16 channels of pulse/triangle/saw/noise and one stereo PCM channel. + +currently Furnace does not support the PCM channel's stereo mode, though. + +# effects + +- `20xx`: set waveform. the following values are accepted: + - 0: pulse + - 1: saw + - 2: triangle + - 3: noise +- `22xx`: set duty cycle. `xx` may go from 0 to 3F. From 8ae0796f0069fa405e1b778dc33d6f199a0a7db7 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Tue, 22 Mar 2022 00:17:00 -0500 Subject: [PATCH 32/40] now add it to the list --- papers/doc/7-systems/README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/papers/doc/7-systems/README.md b/papers/doc/7-systems/README.md index 757a41f5..c9ce873b 100644 --- a/papers/doc/7-systems/README.md +++ b/papers/doc/7-systems/README.md @@ -18,6 +18,7 @@ this is a list of systems that Furnace supports, including each system's effects - [Atari 2600](tia.md) - [Philips SAA1099](saa1099.md) - [Microchip AY8930](ay8930.md) +- [VERA](vera.md) - [Seta/Allumer X1-010](x1-010.md) - [WonderSwan](wonderswan.md) - [Bubble System WSG](bubblesystem.md) From ae3165dbb4bc9754414e1b0c17cde70e4d9b240e Mon Sep 17 00:00:00 2001 From: tildearrow Date: Tue, 22 Mar 2022 00:58:07 -0500 Subject: [PATCH 33/40] system doc work --- papers/doc/7-systems/README.md | 7 ++++- papers/doc/7-systems/arcade.md | 2 +- papers/doc/7-systems/bubblesystem.md | 2 +- papers/doc/7-systems/lynx.md | 6 ++-- papers/doc/7-systems/opl.md | 47 ++++++++++++++++++++++++++++ papers/doc/7-systems/opll.md | 47 ++++++++++++++++++---------- papers/doc/7-systems/pcspkr.md | 7 +++++ papers/doc/7-systems/pet.md | 11 +++++++ papers/doc/7-systems/segapcm.md | 12 +++++++ papers/doc/7-systems/x1-010.md | 6 ++-- papers/doc/7-systems/ym2151.md | 4 ++- papers/doc/7-systems/ym2610b.md | 5 +-- papers/doc/7-systems/ymu759.md | 3 +- 13 files changed, 130 insertions(+), 29 deletions(-) create mode 100644 papers/doc/7-systems/opl.md create mode 100644 papers/doc/7-systems/pcspkr.md create mode 100644 papers/doc/7-systems/pet.md create mode 100644 papers/doc/7-systems/segapcm.md diff --git a/papers/doc/7-systems/README.md b/papers/doc/7-systems/README.md index c9ce873b..31931440 100644 --- a/papers/doc/7-systems/README.md +++ b/papers/doc/7-systems/README.md @@ -4,6 +4,7 @@ this is a list of systems that Furnace supports, including each system's effects - [Sega Genesis/Mega Drive](genesis.md) - [Sega Master System](sms.md) +- [Yamaha OPLL](opll.md) - [Game Boy](game-boy.md) - [PC Engine/TurboGrafx-16](pce.md) - [NES](nes.md) @@ -15,6 +16,7 @@ this is a list of systems that Furnace supports, including each system's effects - [Amiga](amiga.md) - [Yamaha YM2612 standalone](ym2612.md) - [Yamaha YM2151 standalone](ym2151.md) +- [SegaPCM](segapcm.md) - [Atari 2600](tia.md) - [Philips SAA1099](saa1099.md) - [Microchip AY8930](ay8930.md) @@ -22,5 +24,8 @@ this is a list of systems that Furnace supports, including each system's effects - [Seta/Allumer X1-010](x1-010.md) - [WonderSwan](wonderswan.md) - [Bubble System WSG](bubblesystem.md) +- [Yamaha OPL](opl.md) +- [PC Speaker](pcspkr.md) +- [Commodore PET](pet.md) -Furnace also reads .dmf files with the [Yamaha YMU759](ymu759.md) system, but does not emulate the chip at all. +Furnace also reads .dmf files with the [Yamaha YMU759](ymu759.md) system, but... diff --git a/papers/doc/7-systems/arcade.md b/papers/doc/7-systems/arcade.md index fd17f91c..dc9496e6 100644 --- a/papers/doc/7-systems/arcade.md +++ b/papers/doc/7-systems/arcade.md @@ -1,7 +1,7 @@ # Arcade (Yamaha YM2151/PCM) this chip combination was used in the Sega OutRun, X and Y arcade boards, and perhaps a few others. -the actual PCM chip had 16 channels, but the number has been cut to 5 in DefleMask for seemingly arbitrary reasons. a system with all 16 channels may be coming soon. +the actual PCM chip had 16 channels, but the number has been cut to 5 in DefleMask for seemingly arbitrary reasons. # effects diff --git a/papers/doc/7-systems/bubblesystem.md b/papers/doc/7-systems/bubblesystem.md index 835fd622..0c363163 100644 --- a/papers/doc/7-systems/bubblesystem.md +++ b/papers/doc/7-systems/bubblesystem.md @@ -8,7 +8,7 @@ Also known as K005289, but that's just part of the logic used for pitch and wave Waveform select and Volume control are tied with single AY-3-8910 IO for both channels. Another AY-3-8910 IO is used for reading sound hardware status. -furnace emulates this configurations as single system, waveform format is 15 level and 32 width. +Furnace emulates this configurations as single system, waveform format is 15 level and 32 width. # effects diff --git a/papers/doc/7-systems/lynx.md b/papers/doc/7-systems/lynx.md index 2e2f3e3c..a92b2580 100644 --- a/papers/doc/7-systems/lynx.md +++ b/papers/doc/7-systems/lynx.md @@ -2,7 +2,7 @@ The Atari Lynx is a 16 bit handheld console developed by (obviously) Atari Corporation, and initially released in September of 1989, with the worldwide release being in 1990. -The Lynx, while being an incredible handheld for the time (and a lot more powerful than a Game Boy), unfortunately meant nothing in the end due to the Lynx being a market failiure, and ending up as one of the things that contributed to the downfall of Atari. +The Lynx, while being an incredible handheld for the time (and a lot more powerful than a Game Boy), unfortunately meant nothing in the end due to the Lynx being a market failure, and ending up as one of the things that contributed to the downfall of Atari. Although the Lynx is still getting (rather impressive) homebrew developed for it, it does not mean that the Lynx is a popular system at all. @@ -17,4 +17,6 @@ The Atari Lynx's custom sound chip and CPU (MIKEY) is a 6502-based 8 bit CPU run - The MIKEY also has a variety of pitches to choose from, and they go from 32Hz to "above the range of human hearing", according to Atari. ## Effect commands - - `3xxx`: Load LFSR (0 to FFF). For it to work, duty macro in instrument editor must be set to some value, without it LFSR will not be fed with any bits. + - `3xxx`: Load LFSR (0 to FFF). + - this is a bitmask. + - for it to work, duty macro in instrument editor must be set to some value, without it LFSR will not be fed with any bits. diff --git a/papers/doc/7-systems/opl.md b/papers/doc/7-systems/opl.md new file mode 100644 index 00000000..1459eee4 --- /dev/null +++ b/papers/doc/7-systems/opl.md @@ -0,0 +1,47 @@ +# Yamaha OPL + +a series of FM sound chips which were very popular in DOS land. it was so popular that even Yamaha made a logo for it! + +essentially a downgraded version of Yamaha's other FM chips, with only 2 operators per channel. +however, it also had a drums mode, and later chips in the series added more waveforms (than just the typical sine) and even a 4-operator mode. + +the original OPL was present as an expansion for the Commodore 64 and MSX computers (erm, a variant of it). it only had 9 channels and drums mode. + +its successor, the OPL2, added 3 more waveforms and was one of the more popular chips because it was present on the AdLib card for PC. +later Creative would borrow the chip to make the Sound Blaster, and totally destroyed AdLib's dominance. + +the OPL3 added 9 more channels, 4 more waveforms, 4-operator mode (pairing up to 12 channels to make up to six 4-operator channels), quadraphonic output (sadly Furnace only supports stereo) and some other things. +it was overkill. + +afterwards everyone moved to Windows and software mixing... + +# effects + +- 10xx: set AM depth. the following values are accepted: + - 0: 1dB (shallow) + - 1: 4.8dB (deep) + - this effect applies to all channels. +- `11xx`: set feedback of channel. +- `12xx`: set operator 1 level. +- `13xx`: set operator 2 level. +- `14xx`: set operator 3 level. + - only in 4-op mode (OPL3). +- `15xx`: set operator 4 level. + - only in 4-op mode (OPL3). +- `16xy`: set multiplier of operator. + - `x` is the operator (1-4; last 2 operators only in 4-op mode). + - `y` is the mutliplier. +- 17xx: set vibrato depth. the following values are accepted: + - 0: normal + - 1: double + - this effect applies to all channels. +- `18xx`: toggle drums mode. + - 0 disables it and 1 enables it. + - only in drums system. +- `19xx`: set attack of all operators. +- `1Axx`: set attack of operator 1. +- `1Bxx`: set attack of operator 2. +- `1Cxx`: set attack of operator 3. + - only in 4-op mode (OPL3). +- `1Dxx`: set attack of operator 4. + - only in 4-op mode (OPL3). diff --git a/papers/doc/7-systems/opll.md b/papers/doc/7-systems/opll.md index d760d413..880750b3 100644 --- a/papers/doc/7-systems/opll.md +++ b/papers/doc/7-systems/opll.md @@ -1,21 +1,34 @@ # Yamaha YM2413/OPLL -The YM2413, otherwise known as OPLL, is a cost-reduced FM synthesis sound chip manufactured by Yamaha Corporation and based on the Yamaha YM3812 sound chip (OPL2). -As of Furnace version 0.5.7pre4, the OPLL sound chip is not yet emulated. It is, however, emulated in Deflemask as of version 1.1.0. Support for loading .DMFs which contain the YM2413 was added in Furnace version 0.5.7pre4. +the YM2413, otherwise known as OPLL, is a cost-reduced FM synthesis sound chip manufactured by Yamaha Corporation and based on the Yamaha YM3812 sound chip (OPL2). thought OPL was downgraded enough? :p -## Technical specifications -The YM2413 is equipped with the following features: - - 9 channels of 2 operator FM synthesis - - A drum/percussion mode, replacing the last 3 voices with 3 rhythm channels - - 1 user-definable patch (this patch can be changed throughout the course of the song) - - 15 pre-defined patches which can all be used at the same time - - Support for ADSR on both the modulator and the carrier - - Sine and half-sine based FM synthesis - - 9 octave note control - - 4096 different frequencies for channels - - 16 unique volume levels (NOTE: Volume 0 is NOT silent.) - - Modulator and carrier key scaling - - Built-in hardware vibrato support +# technical specifications -## Effect commands -TODO: Add effect commands here when YM2413 emulation is added. +the YM2413 is equipped with the following features: + +- 9 channels of 2 operator FM synthesis +- A drum/percussion mode, replacing the last 3 voices with 3 rhythm channels +- 1 user-definable patch (this patch can be changed throughout the course of the song) +- 15 pre-defined patches which can all be used at the same time +- Support for ADSR on both the modulator and the carrier +- Sine and half-sine based FM synthesis +- 9 octave note control +- 4096 different frequencies for channels +- 16 unique volume levels (NOTE: Volume 0 is NOT silent.) +- Modulator and carrier key scaling +- Built-in hardware vibrato support + +# effects + +- `11xx`: set feedback of channel. +- `12xx`: set operator 1 level. +- `13xx`: set operator 2 level. +- `16xy`: set multiplier of operator. + - `x` is the operator (1 or 2). + - `y` is the mutliplier. +- `18xx`: toggle drums mode. + - 0 disables it and 1 enables it. + - only in drums system. +- `19xx`: set attack of all operators. +- `1Axx`: set attack of operator 1. +- `1Bxx`: set attack of operator 2. diff --git a/papers/doc/7-systems/pcspkr.md b/papers/doc/7-systems/pcspkr.md new file mode 100644 index 00000000..afaa4f82 --- /dev/null +++ b/papers/doc/7-systems/pcspkr.md @@ -0,0 +1,7 @@ +# PC Speaker + +40 years of one square beep - and still going! + +# effects + +ha! effects... diff --git a/papers/doc/7-systems/pet.md b/papers/doc/7-systems/pet.md new file mode 100644 index 00000000..f2c2c1c6 --- /dev/null +++ b/papers/doc/7-systems/pet.md @@ -0,0 +1,11 @@ +# Commodore PET + +a computer from 1977 which was leader on US schools back then. subsequently the Apple II took its throne. + +maybe no better than a computer terminal, but somebody discovered a way to update the screen at turbo rate - and eventually its sound "chip" (it was nothing more than an 8-bit shift register) was abused as well. + +some of these didn't even have sound... + +# effects + +- 10xx: set waveform. `xx` is a bitmask. diff --git a/papers/doc/7-systems/segapcm.md b/papers/doc/7-systems/segapcm.md new file mode 100644 index 00000000..1b3ed63f --- /dev/null +++ b/papers/doc/7-systems/segapcm.md @@ -0,0 +1,12 @@ +# SegaPCM + +16 channels of PCM? no way! + +yep, that's right! 16 channels of PCM! + +a chip used in the Sega OutRun/X/Y arcade boards. eventually the MultiPCM surpassed it with 24 channels, and later they joined the software mixing gang. + +# effects +- `20xx`: set PCM frequency. + - `xx` is a 256th fraction of 31250Hz. + - this effect exists for mostly DefleMask compatibility - it is otherwise recommended to use Sample type instruments. diff --git a/papers/doc/7-systems/x1-010.md b/papers/doc/7-systems/x1-010.md index 759d519c..1fde55e0 100644 --- a/papers/doc/7-systems/x1-010.md +++ b/papers/doc/7-systems/x1-010.md @@ -2,7 +2,7 @@ One of sound chip originally designed by Seta, mainly used at their arcade hardwares at late-80s to early-2000s. It has 2 output channels, but no known hardware using this feature for stereo sound. -Later hardware paired this with external bankswitching logic, but its logic is not emulated now in current furnace revision. +Later hardware paired this with external bankswitching logic, but this isn't emulated yet. Allumer one is just rebadged Seta's thing for use in their arcade hardwares. It has 16 channels, and all channels can be switchable to PCM sample or wavetable playback mode. @@ -26,9 +26,9 @@ In furnace, You can set envelope shape split mode. When it sets, its waveform wi # effects - `10xx`: change wave. -- `11xx`: change envelope shape. (also wavetable) +- `11xx`: change envelope shape (also wavetable). - `17xx`: toggle PCM mode. -- `20xx`: set PCM frequency. (1 to FF)* +- `20xx`: set PCM frequency (1 to FF). - `22xx`: set envelope mode. - bit 0 sets whether envelope will affect this channel. - bit 1 sets whether envelope one-shot mode. when it sets, channel is halted after envelope cycle is ended. diff --git a/papers/doc/7-systems/ym2151.md b/papers/doc/7-systems/ym2151.md index ff5a789d..bf8b0d6b 100644 --- a/papers/doc/7-systems/ym2151.md +++ b/papers/doc/7-systems/ym2151.md @@ -1,6 +1,8 @@ # Yamaha YM2151 -the sound chip powering the Arcade system, available for standalone use if you want to make X68000 music or pair it with a 16-channel Sega PCM when it comes. +the sound chip powering several arcade boards and the Sharp X1/X68000. + +it also was present on several pinball machines and synthesizers of the era, and later surpassed by the YM2414 (OPZ) present in the world-famous TX81Z. # effects diff --git a/papers/doc/7-systems/ym2610b.md b/papers/doc/7-systems/ym2610b.md index 2b2e5fea..e251122c 100644 --- a/papers/doc/7-systems/ym2610b.md +++ b/papers/doc/7-systems/ym2610b.md @@ -1,6 +1,7 @@ # Taito Arcade/Yamaha YM2610B -YM2610B is basically YM2610 with 2 extra FM channels used at some 90s Taito Arcade hardwares, it's backward compatible with non-B chip. +YM2610B is basically YM2610 with 2 extra FM channels used at some 90s Taito arcade hardware. +it is backward compatible with the original chip. # effects @@ -54,4 +55,4 @@ YM2610B is basically YM2610 with 2 extra FM channels used at some 90s Taito Arca - in this mode the envelope period is set to the channel's notes, multiplied by a fraction. - `x` is the numerator. - `y` is the denominator. - - if `x` or `y` are 0 this will disable auto-envelope mode. \ No newline at end of file + - if `x` or `y` are 0 this will disable auto-envelope mode. diff --git a/papers/doc/7-systems/ymu759.md b/papers/doc/7-systems/ymu759.md index 1ad80939..33984832 100644 --- a/papers/doc/7-systems/ymu759.md +++ b/papers/doc/7-systems/ymu759.md @@ -4,7 +4,8 @@ the Yamaha YMU759 is a sound chip designed for feature phones during the early 2 it is also known as MA-2. sadly Yamaha didn't care about these chips too much, and the register specs were completely unavailable, which means the YMU759 is totally unsupported and unemulated besides Yamaha's official emulator for it built into MidRadio. -hence Furnace does not emulate the chip (and doesn't even let you add it to a song). + +Furnace 0.6 loads DefleMask modules written for this system; however, it doesn't support any of its effects and is simulated using the OPL core. # effects From 3b63ca945527d4bd8e70fcf7d01fce91c9b2ea46 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Tue, 22 Mar 2022 01:06:13 -0500 Subject: [PATCH 34/40] update ay8930.md --- papers/doc/7-systems/ay8930.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/papers/doc/7-systems/ay8930.md b/papers/doc/7-systems/ay8930.md index 7344b8f0..6d9db10a 100644 --- a/papers/doc/7-systems/ay8930.md +++ b/papers/doc/7-systems/ay8930.md @@ -5,7 +5,7 @@ a backwards-compatible successor to the AY-3-8910, with increased volume resolut sadly, this soundchip has only ever observed minimal success, and has remained rather obscure since. it is known for being used in the Covox Sound Master, which didn't sell well either. -while emulation of this chip is mostly complete, the additional noise setup registers are not emulated (yet). whether it ever has been emulated at some point in a now-abandoned tracker with similar goal as Furnace is unknown. +while emulation of this chip is mostly complete, hardware comparison hasn't been performed yet due to its scarcity. it also was emulated in a now-abandoned tracker with similar goal as Furnace, which sparked interest on the chip. # effects From bfadb3b5cf11d64716b5d4f5fbc93648f0ea077b Mon Sep 17 00:00:00 2001 From: tildearrow Date: Tue, 22 Mar 2022 01:06:26 -0500 Subject: [PATCH 35/40] OPL: oopsie. 20xx effect doesn't exist --- src/engine/platform/opl.cpp | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/engine/platform/opl.cpp b/src/engine/platform/opl.cpp index 42ce1158..b0894fe7 100644 --- a/src/engine/platform/opl.cpp +++ b/src/engine/platform/opl.cpp @@ -189,9 +189,6 @@ const char* DivPlatformOPL::getEffectName(unsigned char effect) { case 0x1d: return "1Dxx: Set attack of operator 4 (0 to F; 4-op only)"; break; - case 0x20: - return "20xy: Set PSG noise mode (x: preset freq/ch3 freq; y: thin pulse/noise)"; - break; } return NULL; } From 6c10c269a100012443219164423d8e19e1575f5d Mon Sep 17 00:00:00 2001 From: tildearrow Date: Tue, 22 Mar 2022 01:18:22 -0500 Subject: [PATCH 36/40] VERA: Furnace does support stereo for panning --- papers/doc/7-systems/vera.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/papers/doc/7-systems/vera.md b/papers/doc/7-systems/vera.md index 7d1b09c4..be610a80 100644 --- a/papers/doc/7-systems/vera.md +++ b/papers/doc/7-systems/vera.md @@ -3,7 +3,7 @@ this is a video and sound generator chip used in the Commander X16, a modern 8-bit computer created by The 8-Bit Guy. it has 16 channels of pulse/triangle/saw/noise and one stereo PCM channel. -currently Furnace does not support the PCM channel's stereo mode, though. +currently Furnace does not support the PCM channel's stereo mode, though (except for panning). # effects From 6dbc46d50e323367d734d3f0e2850607c72dbac3 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Tue, 22 Mar 2022 01:21:13 -0500 Subject: [PATCH 37/40] GUI: prevent editing non-8/16-bit samples --- src/gui/sampleEdit.cpp | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/gui/sampleEdit.cpp b/src/gui/sampleEdit.cpp index 55d2e485..6b969412 100644 --- a/src/gui/sampleEdit.cpp +++ b/src/gui/sampleEdit.cpp @@ -135,6 +135,8 @@ void FurnaceGUI::drawSampleEdit() { */ ImGui::Separator(); + ImGui::BeginDisabled(sample->depth!=8 && sample->depth!=16); + if (ImGui::Button(ICON_FA_I_CURSOR "##SSelect")) { sampleDragMode=false; } @@ -862,6 +864,12 @@ void FurnaceGUI::drawSampleEdit() { updateSampleTex=true; } } + + if (sample->depth!=8 && sample->depth!=16) { + statusBar="Non-8/16-bit samples cannot be edited without prior conversion."; + } + + ImGui::EndDisabled(); ImGui::SetCursorPosY(ImGui::GetCursorPosY()+ImGui::GetStyle().ScrollbarSize); ImGui::Text("%s",statusBar.c_str()); From 73d2f97274b490ea237fa0ab726f2810c2e40e39 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Tue, 22 Mar 2022 01:34:53 -0500 Subject: [PATCH 38/40] GUI: fix visualizer option in menu --- src/gui/gui.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/gui/gui.cpp b/src/gui/gui.cpp index ddb7433d..ff0d4967 100644 --- a/src/gui/gui.cpp +++ b/src/gui/gui.cpp @@ -2011,6 +2011,9 @@ bool FurnaceGUI::loop() { if (ImGui::BeginMenu("settings")) { if (ImGui::MenuItem("visualizer",NULL,fancyPattern)) { fancyPattern=!fancyPattern; + e->enableCommandStream(fancyPattern); + e->getCommandStream(cmdStream); + cmdStream.clear(); } if (ImGui::MenuItem("reset layout")) { showWarning("Are you sure you want to reset the workspace layout?",GUI_WARN_RESET_LAYOUT); From f4f91ca49ebb46f4e51f6792a722559369ad088f Mon Sep 17 00:00:00 2001 From: tildearrow Date: Tue, 22 Mar 2022 01:57:06 -0500 Subject: [PATCH 39/40] GUI: ability to customize toggle button color --- src/gui/editControls.cpp | 30 +++++++++++++++++++----------- src/gui/gui.cpp | 4 +++- src/gui/gui.h | 6 ++++++ src/gui/insEdit.cpp | 16 ++++++++-------- src/gui/sampleEdit.cpp | 4 ++++ src/gui/settings.cpp | 6 ++++++ 6 files changed, 46 insertions(+), 20 deletions(-) diff --git a/src/gui/editControls.cpp b/src/gui/editControls.cpp index 68f0fee2..5e8d7257 100644 --- a/src/gui/editControls.cpp +++ b/src/gui/editControls.cpp @@ -56,9 +56,11 @@ void FurnaceGUI::drawEditControls() { } } + ImGui::PushStyleColor(ImGuiCol_Button,TOGGLE_COLOR(e->isPlaying())); if (ImGui::Button(ICON_FA_PLAY "##Play")) { play(); } + ImGui::PopStyleColor(); ImGui::SameLine(); if (ImGui::Button(ICON_FA_STOP "##Stop")) { stop(); @@ -95,9 +97,11 @@ void FurnaceGUI::drawEditControls() { stop(); } ImGui::SameLine(); + ImGui::PushStyleColor(ImGuiCol_Button,TOGGLE_COLOR(e->isPlaying())); if (ImGui::Button(ICON_FA_PLAY "##Play")) { play(); } + ImGui::PopStyleColor(); ImGui::SameLine(); if (ImGui::Button(ICON_FA_ARROW_DOWN "##StepOne")) { e->stepOne(cursor.y); @@ -105,14 +109,14 @@ void FurnaceGUI::drawEditControls() { ImGui::SameLine(); bool repeatPattern=e->getRepeatPattern(); - ImGui::PushStyleColor(ImGuiCol_Button,ImVec4(0.2f,(repeatPattern)?0.6f:0.2f,0.2f,1.0f)); + ImGui::PushStyleColor(ImGuiCol_Button,TOGGLE_COLOR(repeatPattern)); if (ImGui::Button(ICON_FA_REPEAT "##RepeatPattern")) { e->setRepeatPattern(!repeatPattern); } ImGui::PopStyleColor(); ImGui::SameLine(); - ImGui::PushStyleColor(ImGuiCol_Button,ImVec4(0.2f,(edit)?0.6f:0.2f,0.2f,1.0f)); + ImGui::PushStyleColor(ImGuiCol_Button,TOGGLE_COLOR(edit)); if (ImGui::Button(ICON_FA_CIRCLE "##Edit")) { edit=!edit; } @@ -120,7 +124,7 @@ void FurnaceGUI::drawEditControls() { ImGui::SameLine(); bool metro=e->getMetronome(); - ImGui::PushStyleColor(ImGuiCol_Button,ImVec4(0.2f,(metro)?0.6f:0.2f,0.2f,1.0f)); + ImGui::PushStyleColor(ImGuiCol_Button,TOGGLE_COLOR(metro)); if (ImGui::Button(ICON_FA_BELL_O "##Metronome")) { e->setMetronome(!metro); } @@ -168,9 +172,11 @@ void FurnaceGUI::drawEditControls() { break; case 2: // compact vertical if (ImGui::Begin("Play/Edit Controls",&editControlsOpen,ImGuiWindowFlags_NoScrollbar|ImGuiWindowFlags_NoScrollWithMouse)) { + ImGui::PushStyleColor(ImGuiCol_Button,TOGGLE_COLOR(e->isPlaying())); if (ImGui::Button(ICON_FA_PLAY "##Play")) { play(); } + ImGui::PopStyleColor(); if (ImGui::Button(ICON_FA_STOP "##Stop")) { stop(); } @@ -179,20 +185,20 @@ void FurnaceGUI::drawEditControls() { } bool repeatPattern=e->getRepeatPattern(); - ImGui::PushStyleColor(ImGuiCol_Button,ImVec4(0.2f,(repeatPattern)?0.6f:0.2f,0.2f,1.0f)); + ImGui::PushStyleColor(ImGuiCol_Button,TOGGLE_COLOR(repeatPattern)); if (ImGui::Button(ICON_FA_REPEAT "##RepeatPattern")) { e->setRepeatPattern(!repeatPattern); } ImGui::PopStyleColor(); - ImGui::PushStyleColor(ImGuiCol_Button,ImVec4(0.2f,(edit)?0.6f:0.2f,0.2f,1.0f)); + ImGui::PushStyleColor(ImGuiCol_Button,TOGGLE_COLOR(edit)); if (ImGui::Button(ICON_FA_CIRCLE "##Edit")) { edit=!edit; } ImGui::PopStyleColor(); bool metro=e->getMetronome(); - ImGui::PushStyleColor(ImGuiCol_Button,ImVec4(0.2f,(metro)?0.6f:0.2f,0.2f,1.0f)); + ImGui::PushStyleColor(ImGuiCol_Button,TOGGLE_COLOR(metro)); if (ImGui::Button(ICON_FA_BELL_O "##Metronome")) { e->setMetronome(!metro); } @@ -226,12 +232,12 @@ void FurnaceGUI::drawEditControls() { } ImGui::Text("Foll."); - ImGui::PushStyleColor(ImGuiCol_Button,ImVec4(0.2f,(followOrders)?0.6f:0.2f,0.2f,1.0f)); + ImGui::PushStyleColor(ImGuiCol_Button,TOGGLE_COLOR(followOrders)); if (ImGui::SmallButton("Ord##FollowOrders")) { handleUnimportant followOrders=!followOrders; } ImGui::PopStyleColor(); - ImGui::PushStyleColor(ImGuiCol_Button,ImVec4(0.2f,(followPattern)?0.6f:0.2f,0.2f,1.0f)); + ImGui::PushStyleColor(ImGuiCol_Button,TOGGLE_COLOR(followPattern)); if (ImGui::SmallButton("Pat##FollowPattern")) { handleUnimportant followPattern=!followPattern; } @@ -243,9 +249,11 @@ void FurnaceGUI::drawEditControls() { case 3: // split if (ImGui::Begin("Play Controls",&editControlsOpen,ImGuiWindowFlags_NoScrollbar|ImGuiWindowFlags_NoScrollWithMouse)) { if (e->isPlaying()) { + ImGui::PushStyleColor(ImGuiCol_Button,uiColors[GUI_COLOR_TOGGLE_ON]); if (ImGui::Button(ICON_FA_STOP "##Stop")) { stop(); } + ImGui::PopStyleColor(); } else { if (ImGui::Button(ICON_FA_PLAY "##Play")) { play(); @@ -261,7 +269,7 @@ void FurnaceGUI::drawEditControls() { } ImGui::SameLine(); - ImGui::PushStyleColor(ImGuiCol_Button,ImVec4(0.2f,(edit)?0.6f:0.2f,0.2f,1.0f)); + ImGui::PushStyleColor(ImGuiCol_Button,TOGGLE_COLOR(edit)); if (ImGui::Button(ICON_FA_CIRCLE "##Edit")) { edit=!edit; } @@ -269,7 +277,7 @@ void FurnaceGUI::drawEditControls() { bool metro=e->getMetronome(); ImGui::SameLine(); - ImGui::PushStyleColor(ImGuiCol_Button,ImVec4(0.2f,(metro)?0.6f:0.2f,0.2f,1.0f)); + ImGui::PushStyleColor(ImGuiCol_Button,TOGGLE_COLOR(metro)); if (ImGui::Button(ICON_FA_BELL_O "##Metronome")) { e->setMetronome(!metro); } @@ -277,7 +285,7 @@ void FurnaceGUI::drawEditControls() { ImGui::SameLine(); bool repeatPattern=e->getRepeatPattern(); - ImGui::PushStyleColor(ImGuiCol_Button,ImVec4(0.2f,(repeatPattern)?0.6f:0.2f,0.2f,1.0f)); + ImGui::PushStyleColor(ImGuiCol_Button,TOGGLE_COLOR(repeatPattern)); if (ImGui::Button(ICON_FA_REPEAT "##RepeatPattern")) { e->setRepeatPattern(!repeatPattern); } diff --git a/src/gui/gui.cpp b/src/gui/gui.cpp index ff0d4967..3db50d06 100644 --- a/src/gui/gui.cpp +++ b/src/gui/gui.cpp @@ -2848,7 +2848,9 @@ FurnaceGUI::FurnaceGUI(): sampleFilterRes(0.25f), sampleFilterCutStart(16000.0f), sampleFilterCutEnd(100.0f), - sampleFilterPower(1) { + sampleFilterPower(1), + sampleClipboard(NULL), + sampleClipboardLen(0) { // value keys valueKeys[SDLK_0]=0; valueKeys[SDLK_1]=1; diff --git a/src/gui/gui.h b/src/gui/gui.h index 7433750e..58539ab1 100644 --- a/src/gui/gui.h +++ b/src/gui/gui.h @@ -41,6 +41,8 @@ #define MARK_MODIFIED modified=true; +#define TOGGLE_COLOR(x) ((x)?uiColors[GUI_COLOR_TOGGLE_ON]:uiColors[GUI_COLOR_TOGGLE_OFF]) + enum FurnaceGUIColors { GUI_COLOR_BACKGROUND=0, GUI_COLOR_FRAME_BACKGROUND, @@ -49,6 +51,8 @@ enum FurnaceGUIColors { GUI_COLOR_TEXT, GUI_COLOR_ACCENT_PRIMARY, GUI_COLOR_ACCENT_SECONDARY, + GUI_COLOR_TOGGLE_OFF, + GUI_COLOR_TOGGLE_ON, GUI_COLOR_EDITING, GUI_COLOR_SONG_LOOP, @@ -774,6 +778,8 @@ class FurnaceGUI { unsigned int sampleDragLen; float sampleFilterL, sampleFilterB, sampleFilterH, sampleFilterRes, sampleFilterCutStart, sampleFilterCutEnd; unsigned char sampleFilterPower; + void* sampleClipboard; + size_t sampleClipboardLen; // visualizer float keyHit[DIV_MAX_CHANS]; diff --git a/src/gui/insEdit.cpp b/src/gui/insEdit.cpp index a1ba5f57..93b85a19 100644 --- a/src/gui/insEdit.cpp +++ b/src/gui/insEdit.cpp @@ -1370,25 +1370,25 @@ void FurnaceGUI::drawInsEdit() { if (ins->type==DIV_INS_C64) if (ImGui::BeginTabItem("C64")) { ImGui::Text("Waveform"); ImGui::SameLine(); - ImGui::PushStyleColor(ImGuiCol_Button,ImVec4(0.2f,(ins->c64.triOn)?0.6f:0.2f,0.2f,1.0f)); + ImGui::PushStyleColor(ImGuiCol_Button,TOGGLE_COLOR(ins->c64.triOn)); if (ImGui::Button("tri")) { PARAMETER ins->c64.triOn=!ins->c64.triOn; } ImGui::PopStyleColor(); ImGui::SameLine(); - ImGui::PushStyleColor(ImGuiCol_Button,ImVec4(0.2f,(ins->c64.sawOn)?0.6f:0.2f,0.2f,1.0f)); + ImGui::PushStyleColor(ImGuiCol_Button,TOGGLE_COLOR(ins->c64.sawOn)); if (ImGui::Button("saw")) { PARAMETER ins->c64.sawOn=!ins->c64.sawOn; } ImGui::PopStyleColor(); ImGui::SameLine(); - ImGui::PushStyleColor(ImGuiCol_Button,ImVec4(0.2f,(ins->c64.pulseOn)?0.6f:0.2f,0.2f,1.0f)); + ImGui::PushStyleColor(ImGuiCol_Button,TOGGLE_COLOR(ins->c64.pulseOn)); if (ImGui::Button("pulse")) { PARAMETER ins->c64.pulseOn=!ins->c64.pulseOn; } ImGui::PopStyleColor(); ImGui::SameLine(); - ImGui::PushStyleColor(ImGuiCol_Button,ImVec4(0.2f,(ins->c64.noiseOn)?0.6f:0.2f,0.2f,1.0f)); + ImGui::PushStyleColor(ImGuiCol_Button,TOGGLE_COLOR(ins->c64.noiseOn)); if (ImGui::Button("noise")) { PARAMETER ins->c64.noiseOn=!ins->c64.noiseOn; } @@ -1417,25 +1417,25 @@ void FurnaceGUI::drawInsEdit() { ImGui::Text("Filter Mode"); ImGui::SameLine(); - ImGui::PushStyleColor(ImGuiCol_Button,ImVec4(0.2f,(ins->c64.lp)?0.6f:0.2f,0.2f,1.0f)); + ImGui::PushStyleColor(ImGuiCol_Button,TOGGLE_COLOR(ins->c64.lp)); if (ImGui::Button("low")) { PARAMETER ins->c64.lp=!ins->c64.lp; } ImGui::PopStyleColor(); ImGui::SameLine(); - ImGui::PushStyleColor(ImGuiCol_Button,ImVec4(0.2f,(ins->c64.bp)?0.6f:0.2f,0.2f,1.0f)); + ImGui::PushStyleColor(ImGuiCol_Button,TOGGLE_COLOR(ins->c64.bp)); if (ImGui::Button("band")) { PARAMETER ins->c64.bp=!ins->c64.bp; } ImGui::PopStyleColor(); ImGui::SameLine(); - ImGui::PushStyleColor(ImGuiCol_Button,ImVec4(0.2f,(ins->c64.hp)?0.6f:0.2f,0.2f,1.0f)); + ImGui::PushStyleColor(ImGuiCol_Button,TOGGLE_COLOR(ins->c64.hp)); if (ImGui::Button("high")) { PARAMETER ins->c64.hp=!ins->c64.hp; } ImGui::PopStyleColor(); ImGui::SameLine(); - ImGui::PushStyleColor(ImGuiCol_Button,ImVec4(0.2f,(ins->c64.ch3off)?0.6f:0.2f,0.2f,1.0f)); + ImGui::PushStyleColor(ImGuiCol_Button,TOGGLE_COLOR(ins->c64.ch3off)); if (ImGui::Button("ch3off")) { PARAMETER ins->c64.ch3off=!ins->c64.ch3off; } diff --git a/src/gui/sampleEdit.cpp b/src/gui/sampleEdit.cpp index 6b969412..b33c1e80 100644 --- a/src/gui/sampleEdit.cpp +++ b/src/gui/sampleEdit.cpp @@ -137,16 +137,20 @@ void FurnaceGUI::drawSampleEdit() { ImGui::BeginDisabled(sample->depth!=8 && sample->depth!=16); + ImGui::PushStyleColor(ImGuiCol_Button,TOGGLE_COLOR(!sampleDragMode)); if (ImGui::Button(ICON_FA_I_CURSOR "##SSelect")) { sampleDragMode=false; } + ImGui::PopStyleColor(); if (ImGui::IsItemHovered()) { ImGui::SetTooltip("Edit mode: Select"); } ImGui::SameLine(); + ImGui::PushStyleColor(ImGuiCol_Button,TOGGLE_COLOR(sampleDragMode)); if (ImGui::Button(ICON_FA_PENCIL "##SDraw")) { sampleDragMode=true; } + ImGui::PopStyleColor(); if (ImGui::IsItemHovered()) { ImGui::SetTooltip("Edit mode: Draw"); } diff --git a/src/gui/settings.cpp b/src/gui/settings.cpp index 5348bfa0..b89ed593 100644 --- a/src/gui/settings.cpp +++ b/src/gui/settings.cpp @@ -497,6 +497,8 @@ void FurnaceGUI::drawSettings() { UI_COLOR_CONFIG(GUI_COLOR_TEXT,"Text"); UI_COLOR_CONFIG(GUI_COLOR_ACCENT_PRIMARY,"Primary"); UI_COLOR_CONFIG(GUI_COLOR_ACCENT_SECONDARY,"Secondary"); + UI_COLOR_CONFIG(GUI_COLOR_TOGGLE_ON,"Toggle on"); + UI_COLOR_CONFIG(GUI_COLOR_TOGGLE_OFF,"Toggle off"); UI_COLOR_CONFIG(GUI_COLOR_EDITING,"Editing"); UI_COLOR_CONFIG(GUI_COLOR_SONG_LOOP,"Song loop"); UI_COLOR_CONFIG(GUI_COLOR_PLAYBACK_STAT,"Playback status"); @@ -1199,6 +1201,8 @@ void FurnaceGUI::commitSettings() { PUT_UI_COLOR(GUI_COLOR_TEXT); PUT_UI_COLOR(GUI_COLOR_ACCENT_PRIMARY); PUT_UI_COLOR(GUI_COLOR_ACCENT_SECONDARY); + PUT_UI_COLOR(GUI_COLOR_TOGGLE_ON); + PUT_UI_COLOR(GUI_COLOR_TOGGLE_OFF); PUT_UI_COLOR(GUI_COLOR_EDITING); PUT_UI_COLOR(GUI_COLOR_SONG_LOOP); PUT_UI_COLOR(GUI_COLOR_VOLMETER_LOW); @@ -1537,6 +1541,8 @@ void FurnaceGUI::applyUISettings() { GET_UI_COLOR(GUI_COLOR_TEXT,ImVec4(1.0f,1.0f,1.0f,1.0f)); GET_UI_COLOR(GUI_COLOR_ACCENT_PRIMARY,ImVec4(0.06f,0.53f,0.98f,1.0f)); GET_UI_COLOR(GUI_COLOR_ACCENT_SECONDARY,ImVec4(0.26f,0.59f,0.98f,1.0f)); + GET_UI_COLOR(GUI_COLOR_TOGGLE_ON,ImVec4(0.2f,0.6f,0.2f,1.0f)); + GET_UI_COLOR(GUI_COLOR_TOGGLE_OFF,ImVec4(0.2f,0.2f,0.2f,1.0f)); GET_UI_COLOR(GUI_COLOR_EDITING,ImVec4(0.2f,0.1f,0.1f,1.0f)); GET_UI_COLOR(GUI_COLOR_SONG_LOOP,ImVec4(0.3f,0.5f,0.8f,0.4f)); GET_UI_COLOR(GUI_COLOR_VOLMETER_LOW,ImVec4(0.2f,0.6f,0.2f,1.0f)); From 705ba4273bbcd2148460e4a021dd2dd6e2b57ebb Mon Sep 17 00:00:00 2001 From: tildearrow Date: Tue, 22 Mar 2022 04:54:01 -0500 Subject: [PATCH 40/40] GUI: sample editor keybinds works --- src/engine/sample.cpp | 36 ++++ src/engine/sample.h | 9 + src/gui/actionUtil.h | 19 +++ src/gui/doAction.cpp | 366 +++++++++++++++++++++++++++++++++++++++++ src/gui/gui.cpp | 16 +- src/gui/gui.h | 33 +++- src/gui/sampleEdit.cpp | 232 +++----------------------- src/gui/sampleUtil.h | 31 ++++ src/gui/settings.cpp | 95 +++++++++++ 9 files changed, 629 insertions(+), 208 deletions(-) create mode 100644 src/gui/sampleUtil.h diff --git a/src/engine/sample.cpp b/src/engine/sample.cpp index 66e54d85..33532d43 100644 --- a/src/engine/sample.cpp +++ b/src/engine/sample.cpp @@ -258,6 +258,42 @@ bool DivSample::trim(unsigned int begin, unsigned int end) { return false; } +// TODO: for clipboard +bool DivSample::insert(unsigned int pos, unsigned int length) { + unsigned int count=samples+length; + if (depth==8) { + if (data8!=NULL) { + signed char* oldData8=data8; + data8=NULL; + initInternal(8,count); + if (pos>0) { + memcpy(data8,oldData8,pos); + } + if (count-pos-length>0) { + memcpy(data8+pos+length,oldData8+pos,count-pos-length); + } + delete[] oldData8; + } else { + initInternal(8,count); + } + samples=count; + return true; + } else if (depth==16) { + if (data16!=NULL) { + short* oldData16=data16; + data16=NULL; + initInternal(16,count); + memcpy(data16,oldData16,sizeof(short)*count); + delete[] oldData16; + } else { + initInternal(16,count); + } + samples=count; + return true; + } + return false; +} + #define RESAMPLE_BEGIN \ if (samples<1) return true; \ int finalCount=(double)samples*(r/(double)rate); \ diff --git a/src/engine/sample.h b/src/engine/sample.h index 7e4d76be..c697a995 100644 --- a/src/engine/sample.h +++ b/src/engine/sample.h @@ -120,6 +120,15 @@ struct DivSample { */ bool trim(unsigned int begin, unsigned int end); + /** + * insert silence at specified position. + * @warning do not attempt to do this outside of a synchronized block! + * @param pos the beginning. + * @param length how many samples to insert. + * @return whether it was successful. + */ + bool insert(unsigned int pos, unsigned int length); + /** * change the sample rate. * @warning do not attempt to resample outside of a synchronized block! diff --git a/src/gui/actionUtil.h b/src/gui/actionUtil.h index 4e9196af..752a5442 100644 --- a/src/gui/actionUtil.h +++ b/src/gui/actionUtil.h @@ -1,3 +1,22 @@ +/** + * Furnace Tracker - multi-system chiptune tracker + * Copyright (C) 2021-2022 tildearrow and contributors + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + #define DETERMINE_FIRST \ int firstChannel=0; \ for (int i=0; igetTotalChannelCount(); i++) { \ diff --git a/src/gui/doAction.cpp b/src/gui/doAction.cpp index 903f6edf..51dad892 100644 --- a/src/gui/doAction.cpp +++ b/src/gui/doAction.cpp @@ -19,8 +19,10 @@ #include "gui.h" #include +#include #include "actionUtil.h" +#include "sampleUtil.h" void FurnaceGUI::doAction(int what) { switch (what) { @@ -577,6 +579,370 @@ void FurnaceGUI::doAction(int what) { e->stopSamplePreview(); break; + case GUI_ACTION_SAMPLE_SELECT: + if (curSample<0 || curSample>=(int)e->song.sample.size()) break; + sampleDragMode=false; + break; + case GUI_ACTION_SAMPLE_DRAW: + if (curSample<0 || curSample>=(int)e->song.sample.size()) break; + sampleDragMode=true; + break; + case GUI_ACTION_SAMPLE_CUT: { + if (curSample<0 || curSample>=(int)e->song.sample.size()) break; + DivSample* sample=e->song.sample[curSample]; + SAMPLE_OP_BEGIN; + + if (end-start<1) break; + + if (sampleClipboard!=NULL) { + delete[] sampleClipboard; + } + sampleClipboard=new short[end-start]; + sampleClipboardLen=end-start; + memcpy(sampleClipboard,&(sample->data16[start]),end-start); + + e->lockEngine([this,sample,start,end]() { + sample->strip(start,end); + updateSampleTex=true; + + e->renderSamples(); + }); + sampleSelStart=-1; + sampleSelEnd=-1; + MARK_MODIFIED; + + break; + } + case GUI_ACTION_SAMPLE_COPY: { + if (curSample<0 || curSample>=(int)e->song.sample.size()) break; + DivSample* sample=e->song.sample[curSample]; + SAMPLE_OP_BEGIN; + + if (end-start<1) break; + + if (sampleClipboard!=NULL) { + delete[] sampleClipboard; + } + sampleClipboard=new short[end-start]; + sampleClipboardLen=end-start; + memcpy(sampleClipboard,&(sample->data16[start]),end-start); + break; + } + case GUI_ACTION_SAMPLE_PASTE: // TODO!!! + if (curSample<0 || curSample>=(int)e->song.sample.size()) break; + break; + case GUI_ACTION_SAMPLE_PASTE_REPLACE: + if (curSample<0 || curSample>=(int)e->song.sample.size()) break; + break; + case GUI_ACTION_SAMPLE_PASTE_MIX: + if (curSample<0 || curSample>=(int)e->song.sample.size()) break; + break; + case GUI_ACTION_SAMPLE_SELECT_ALL: { + if (curSample<0 || curSample>=(int)e->song.sample.size()) break; + DivSample* sample=e->song.sample[curSample]; + sampleDragActive=false; + sampleSelStart=0; + sampleSelEnd=sample->samples; + break; + } + case GUI_ACTION_SAMPLE_RESIZE: + if (curSample<0 || curSample>=(int)e->song.sample.size()) break; + openSampleResizeOpt=true; + break; + case GUI_ACTION_SAMPLE_RESAMPLE: + if (curSample<0 || curSample>=(int)e->song.sample.size()) break; + openSampleResampleOpt=true; + break; + case GUI_ACTION_SAMPLE_AMPLIFY: + if (curSample<0 || curSample>=(int)e->song.sample.size()) break; + openSampleAmplifyOpt=true; + break; + case GUI_ACTION_SAMPLE_NORMALIZE: { + if (curSample<0 || curSample>=(int)e->song.sample.size()) break; + DivSample* sample=e->song.sample[curSample]; + e->lockEngine([this,sample]() { + SAMPLE_OP_BEGIN; + float maxVal=0.0f; + + if (sample->depth==16) { + for (unsigned int i=start; idata16[i]/32767.0f); + if (val>maxVal) maxVal=val; + } + if (maxVal>1.0f) maxVal=1.0f; + if (maxVal>0.0f) { + float vol=1.0f/maxVal; + for (unsigned int i=start; idata16[i]*vol; + if (val<-32768) val=-32768; + if (val>32767) val=32767; + sample->data16[i]=val; + } + } + } else if (sample->depth==8) { + for (unsigned int i=start; idata8[i]/127.0f); + if (val>maxVal) maxVal=val; + } + if (maxVal>1.0f) maxVal=1.0f; + if (maxVal>0.0f) { + float vol=1.0f/maxVal; + for (unsigned int i=start; idata8[i]*vol; + if (val<-128) val=-128; + if (val>127) val=127; + sample->data8[i]=val; + } + } + } + + updateSampleTex=true; + + e->renderSamples(); + }); + MARK_MODIFIED; + break; + } + case GUI_ACTION_SAMPLE_FADE_IN: { + if (curSample<0 || curSample>=(int)e->song.sample.size()) break; + DivSample* sample=e->song.sample[curSample]; + e->lockEngine([this,sample]() { + SAMPLE_OP_BEGIN; + + if (sample->depth==16) { + for (unsigned int i=start; idata16[i]*float(i-start)/float(end-start); + if (val<-32768) val=-32768; + if (val>32767) val=32767; + sample->data16[i]=val; + } + } else if (sample->depth==8) { + for (unsigned int i=start; idata8[i]*float(i-start)/float(end-start); + if (val<-128) val=-128; + if (val>127) val=127; + sample->data8[i]=val; + } + } + + updateSampleTex=true; + + e->renderSamples(); + }); + MARK_MODIFIED; + break; + } + case GUI_ACTION_SAMPLE_FADE_OUT: { + if (curSample<0 || curSample>=(int)e->song.sample.size()) break; + DivSample* sample=e->song.sample[curSample]; + e->lockEngine([this,sample]() { + SAMPLE_OP_BEGIN; + + if (sample->depth==16) { + for (unsigned int i=start; idata16[i]*float(end-i)/float(end-start); + if (val<-32768) val=-32768; + if (val>32767) val=32767; + sample->data16[i]=val; + } + } else if (sample->depth==8) { + for (unsigned int i=start; idata8[i]*float(end-i)/float(end-start); + if (val<-128) val=-128; + if (val>127) val=127; + sample->data8[i]=val; + } + } + + updateSampleTex=true; + + e->renderSamples(); + }); + MARK_MODIFIED; + break; + } + case GUI_ACTION_SAMPLE_SILENCE: { + if (curSample<0 || curSample>=(int)e->song.sample.size()) break; + DivSample* sample=e->song.sample[curSample]; + e->lockEngine([this,sample]() { + SAMPLE_OP_BEGIN; + + if (sample->depth==16) { + for (unsigned int i=start; idata16[i]=0; + } + } else if (sample->depth==8) { + for (unsigned int i=start; idata8[i]=0; + } + } + + updateSampleTex=true; + + e->renderSamples(); + }); + MARK_MODIFIED; + break; + } + case GUI_ACTION_SAMPLE_DELETE: { + if (curSample<0 || curSample>=(int)e->song.sample.size()) break; + DivSample* sample=e->song.sample[curSample]; + e->lockEngine([this,sample]() { + SAMPLE_OP_BEGIN; + + sample->strip(start,end); + updateSampleTex=true; + + e->renderSamples(); + }); + sampleSelStart=-1; + sampleSelEnd=-1; + MARK_MODIFIED; + break; + } + case GUI_ACTION_SAMPLE_TRIM: { + if (curSample<0 || curSample>=(int)e->song.sample.size()) break; + DivSample* sample=e->song.sample[curSample]; + e->lockEngine([this,sample]() { + SAMPLE_OP_BEGIN; + + sample->trim(start,end); + updateSampleTex=true; + + e->renderSamples(); + }); + sampleSelStart=-1; + sampleSelEnd=-1; + MARK_MODIFIED; + break; + } + case GUI_ACTION_SAMPLE_REVERSE: { + if (curSample<0 || curSample>=(int)e->song.sample.size()) break; + DivSample* sample=e->song.sample[curSample]; + e->lockEngine([this,sample]() { + SAMPLE_OP_BEGIN; + + if (sample->depth==16) { + for (unsigned int i=start; idata16[i]^=sample->data16[ri]; + sample->data16[ri]^=sample->data16[i]; + sample->data16[i]^=sample->data16[ri]; + } + } else if (sample->depth==8) { + for (unsigned int i=start; idata8[i]^=sample->data8[ri]; + sample->data8[ri]^=sample->data8[i]; + sample->data8[i]^=sample->data8[ri]; + } + } + + updateSampleTex=true; + + e->renderSamples(); + }); + MARK_MODIFIED; + break; + } + case GUI_ACTION_SAMPLE_INVERT: { + if (curSample<0 || curSample>=(int)e->song.sample.size()) break; + DivSample* sample=e->song.sample[curSample]; + e->lockEngine([this,sample]() { + SAMPLE_OP_BEGIN; + + if (sample->depth==16) { + for (unsigned int i=start; idata16[i]=-sample->data16[i]; + if (sample->data16[i]==-32768) sample->data16[i]=32767; + } + } else if (sample->depth==8) { + for (unsigned int i=start; idata8[i]=-sample->data8[i]; + if (sample->data16[i]==-128) sample->data16[i]=127; + } + } + + updateSampleTex=true; + + e->renderSamples(); + }); + MARK_MODIFIED; + break; + } + case GUI_ACTION_SAMPLE_SIGN: { + if (curSample<0 || curSample>=(int)e->song.sample.size()) break; + DivSample* sample=e->song.sample[curSample]; + e->lockEngine([this,sample]() { + SAMPLE_OP_BEGIN; + + if (sample->depth==16) { + for (unsigned int i=start; idata16[i]^=0x8000; + } + } else if (sample->depth==8) { + for (unsigned int i=start; idata8[i]^=0x80; + } + } + + updateSampleTex=true; + + e->renderSamples(); + }); + MARK_MODIFIED; + break; + } + case GUI_ACTION_SAMPLE_FILTER: + if (curSample<0 || curSample>=(int)e->song.sample.size()) break; + openSampleFilterOpt=true; + break; + case GUI_ACTION_SAMPLE_PREVIEW: + if (curSample<0 || curSample>=(int)e->song.sample.size()) break; + e->previewSample(curSample); + break; + case GUI_ACTION_SAMPLE_STOP_PREVIEW: + if (curSample<0 || curSample>=(int)e->song.sample.size()) break; + e->stopSamplePreview(); + break; + case GUI_ACTION_SAMPLE_ZOOM_IN: { + if (curSample<0 || curSample>=(int)e->song.sample.size()) break; + double zoomPercent=100.0/sampleZoom; + zoomPercent+=10.0; + if (zoomPercent>10000.0) zoomPercent=10000.0; + if (zoomPercent<1.0) zoomPercent=1.0; + sampleZoom=100.0/zoomPercent; + if (sampleZoom<0.01) sampleZoom=0.01; + sampleZoomAuto=false; + updateSampleTex=true; + break; + } + case GUI_ACTION_SAMPLE_ZOOM_OUT: { + if (curSample<0 || curSample>=(int)e->song.sample.size()) break; + double zoomPercent=100.0/sampleZoom; + zoomPercent-=10.0; + if (zoomPercent>10000.0) zoomPercent=10000.0; + if (zoomPercent<1.0) zoomPercent=1.0; + sampleZoom=100.0/zoomPercent; + if (sampleZoom<0.01) sampleZoom=0.01; + sampleZoomAuto=false; + updateSampleTex=true; + break; + } + case GUI_ACTION_SAMPLE_ZOOM_AUTO: + if (curSample<0 || curSample>=(int)e->song.sample.size()) break; + if (sampleZoomAuto) { + sampleZoom=1.0; + sampleZoomAuto=false; + updateSampleTex=true; + } else { + sampleZoomAuto=true; + updateSampleTex=true; + } + break; + case GUI_ACTION_ORDERS_UP: if (e->getOrder()>0) { e->setOrder(e->getOrder()-1); diff --git a/src/gui/gui.cpp b/src/gui/gui.cpp index 3db50d06..f32e6eba 100644 --- a/src/gui/gui.cpp +++ b/src/gui/gui.cpp @@ -918,6 +918,16 @@ void FurnaceGUI::keyDown(SDL_Event& ev) { } } break; + case GUI_WINDOW_SAMPLE_EDIT: + try { + int action=actionMapSample.at(mapped); + if (action>0) { + doAction(action); + return; + } + } catch (std::out_of_range& e) { + } + break; case GUI_WINDOW_INS_LIST: try { int action=actionMapInsList.at(mapped); @@ -2850,7 +2860,11 @@ FurnaceGUI::FurnaceGUI(): sampleFilterCutEnd(100.0f), sampleFilterPower(1), sampleClipboard(NULL), - sampleClipboardLen(0) { + sampleClipboardLen(0), + openSampleResizeOpt(false), + openSampleResampleOpt(false), + openSampleAmplifyOpt(false), + openSampleFilterOpt(false) { // value keys valueKeys[SDLK_0]=0; valueKeys[SDLK_1]=1; diff --git a/src/gui/gui.h b/src/gui/gui.h index 58539ab1..aa661b3a 100644 --- a/src/gui/gui.h +++ b/src/gui/gui.h @@ -364,6 +364,35 @@ enum FurnaceGUIActions { GUI_ACTION_SAMPLE_LIST_STOP_PREVIEW, GUI_ACTION_SAMPLE_LIST_MAX, + GUI_ACTION_SAMPLE_MIN, + GUI_ACTION_SAMPLE_SELECT, + GUI_ACTION_SAMPLE_DRAW, + GUI_ACTION_SAMPLE_CUT, + GUI_ACTION_SAMPLE_COPY, + GUI_ACTION_SAMPLE_PASTE, + GUI_ACTION_SAMPLE_PASTE_REPLACE, + GUI_ACTION_SAMPLE_PASTE_MIX, + GUI_ACTION_SAMPLE_SELECT_ALL, + GUI_ACTION_SAMPLE_RESIZE, + GUI_ACTION_SAMPLE_RESAMPLE, + GUI_ACTION_SAMPLE_AMPLIFY, + GUI_ACTION_SAMPLE_NORMALIZE, + GUI_ACTION_SAMPLE_FADE_IN, + GUI_ACTION_SAMPLE_FADE_OUT, + GUI_ACTION_SAMPLE_SILENCE, + GUI_ACTION_SAMPLE_DELETE, + GUI_ACTION_SAMPLE_TRIM, + GUI_ACTION_SAMPLE_REVERSE, + GUI_ACTION_SAMPLE_INVERT, + GUI_ACTION_SAMPLE_SIGN, + GUI_ACTION_SAMPLE_FILTER, + GUI_ACTION_SAMPLE_PREVIEW, + GUI_ACTION_SAMPLE_STOP_PREVIEW, + GUI_ACTION_SAMPLE_ZOOM_IN, + GUI_ACTION_SAMPLE_ZOOM_OUT, + GUI_ACTION_SAMPLE_ZOOM_AUTO, + GUI_ACTION_SAMPLE_MAX, + GUI_ACTION_ORDERS_MIN, GUI_ACTION_ORDERS_UP, GUI_ACTION_ORDERS_DOWN, @@ -677,6 +706,7 @@ class FurnaceGUI { std::map actionMapGlobal; std::map actionMapPat; std::map actionMapOrders; + std::map actionMapSample; std::map actionMapInsList; std::map actionMapWaveList; std::map actionMapSampleList; @@ -778,8 +808,9 @@ class FurnaceGUI { unsigned int sampleDragLen; float sampleFilterL, sampleFilterB, sampleFilterH, sampleFilterRes, sampleFilterCutStart, sampleFilterCutEnd; unsigned char sampleFilterPower; - void* sampleClipboard; + short* sampleClipboard; size_t sampleClipboardLen; + bool openSampleResizeOpt, openSampleResampleOpt, openSampleAmplifyOpt, openSampleFilterOpt; // visualizer float keyHit[DIV_MAX_CHANS]; diff --git a/src/gui/sampleEdit.cpp b/src/gui/sampleEdit.cpp index b33c1e80..28e0f3d5 100644 --- a/src/gui/sampleEdit.cpp +++ b/src/gui/sampleEdit.cpp @@ -25,19 +25,7 @@ #include "misc/cpp/imgui_stdlib.h" #include #include "guiConst.h" - -#define SAMPLE_OP_BEGIN \ - unsigned int start=0; \ - unsigned int end=sample->samples; \ - if (sampleSelStart!=-1 && sampleSelEnd!=-1 && sampleSelStart!=sampleSelEnd) { \ - start=sampleSelStart; \ - end=sampleSelEnd; \ - if (start>end) { \ - start^=end; \ - end^=start; \ - start^=end; \ - } \ - } \ +#include "sampleUtil.h" void FurnaceGUI::drawSampleEdit() { if (nextWindow==GUI_WINDOW_SAMPLE_EDIT) { @@ -164,6 +152,10 @@ void FurnaceGUI::drawSampleEdit() { if (ImGui::IsItemHovered()) { ImGui::SetTooltip("Resize"); } + if (openSampleResizeOpt) { + openSampleResizeOpt=false; + ImGui::OpenPopup("SResizeOpt"); + } if (ImGui::BeginPopupContextItem("SResizeOpt",ImGuiPopupFlags_MouseButtonLeft)) { if (ImGui::InputInt("Samples",&resizeSize,1,64)) { if (resizeSize<0) resizeSize=0; @@ -194,6 +186,10 @@ void FurnaceGUI::drawSampleEdit() { if (ImGui::IsItemHovered()) { ImGui::SetTooltip("Resample"); } + if (openSampleResampleOpt) { + openSampleResampleOpt=false; + ImGui::OpenPopup("SResampleOpt"); + } if (ImGui::BeginPopupContextItem("SResampleOpt",ImGuiPopupFlags_MouseButtonLeft)) { ImGui::Text("Rate"); if (ImGui::InputDouble("##SRRate",&resampleTarget,1.0,50.0,"%g")) { @@ -243,6 +239,10 @@ void FurnaceGUI::drawSampleEdit() { if (ImGui::IsItemHovered()) { ImGui::SetTooltip("Amplify"); } + if (openSampleAmplifyOpt) { + openSampleAmplifyOpt=false; + ImGui::OpenPopup("SAmplifyOpt"); + } if (ImGui::BeginPopupContextItem("SAmplifyOpt",ImGuiPopupFlags_MouseButtonLeft)) { ImGui::Text("Volume"); if (ImGui::InputFloat("##SRVolume",&lifyVol,10.0,50.0,"%g%%")) { @@ -283,165 +283,42 @@ void FurnaceGUI::drawSampleEdit() { } ImGui::SameLine(); if (ImGui::Button(ICON_FA_ARROWS_V "##SNormalize")) { - e->lockEngine([this,sample]() { - SAMPLE_OP_BEGIN; - float maxVal=0.0f; - - if (sample->depth==16) { - for (unsigned int i=start; idata16[i]/32767.0f); - if (val>maxVal) maxVal=val; - } - if (maxVal>1.0f) maxVal=1.0f; - if (maxVal>0.0f) { - float vol=1.0f/maxVal; - for (unsigned int i=start; idata16[i]*vol; - if (val<-32768) val=-32768; - if (val>32767) val=32767; - sample->data16[i]=val; - } - } - } else if (sample->depth==8) { - for (unsigned int i=start; idata8[i]/127.0f); - if (val>maxVal) maxVal=val; - } - if (maxVal>1.0f) maxVal=1.0f; - if (maxVal>0.0f) { - float vol=1.0f/maxVal; - for (unsigned int i=start; idata8[i]*vol; - if (val<-128) val=-128; - if (val>127) val=127; - sample->data8[i]=val; - } - } - } - - updateSampleTex=true; - - e->renderSamples(); - }); - MARK_MODIFIED; + doAction(GUI_ACTION_SAMPLE_NORMALIZE); } if (ImGui::IsItemHovered()) { ImGui::SetTooltip("Normalize"); } ImGui::SameLine(); if (ImGui::Button(ICON_FA_ARROW_UP "##SFadeIn")) { - e->lockEngine([this,sample]() { - SAMPLE_OP_BEGIN; - - if (sample->depth==16) { - for (unsigned int i=start; idata16[i]*float(i-start)/float(end-start); - if (val<-32768) val=-32768; - if (val>32767) val=32767; - sample->data16[i]=val; - } - } else if (sample->depth==8) { - for (unsigned int i=start; idata8[i]*float(i-start)/float(end-start); - if (val<-128) val=-128; - if (val>127) val=127; - sample->data8[i]=val; - } - } - - updateSampleTex=true; - - e->renderSamples(); - }); - MARK_MODIFIED; + doAction(GUI_ACTION_SAMPLE_FADE_IN); } if (ImGui::IsItemHovered()) { ImGui::SetTooltip("Fade in"); } ImGui::SameLine(); if (ImGui::Button(ICON_FA_ARROW_DOWN "##SFadeOut")) { - e->lockEngine([this,sample]() { - SAMPLE_OP_BEGIN; - - if (sample->depth==16) { - for (unsigned int i=start; idata16[i]*float(end-i)/float(end-start); - if (val<-32768) val=-32768; - if (val>32767) val=32767; - sample->data16[i]=val; - } - } else if (sample->depth==8) { - for (unsigned int i=start; idata8[i]*float(end-i)/float(end-start); - if (val<-128) val=-128; - if (val>127) val=127; - sample->data8[i]=val; - } - } - - updateSampleTex=true; - - e->renderSamples(); - }); - MARK_MODIFIED; + doAction(GUI_ACTION_SAMPLE_FADE_OUT); } if (ImGui::IsItemHovered()) { ImGui::SetTooltip("Fade out"); } ImGui::SameLine(); if (ImGui::Button(ICON_FA_ERASER "##SSilence")) { - e->lockEngine([this,sample]() { - SAMPLE_OP_BEGIN; - - if (sample->depth==16) { - for (unsigned int i=start; idata16[i]=0; - } - } else if (sample->depth==8) { - for (unsigned int i=start; idata8[i]=0; - } - } - - updateSampleTex=true; - - e->renderSamples(); - }); - MARK_MODIFIED; + doAction(GUI_ACTION_SAMPLE_SILENCE); } if (ImGui::IsItemHovered()) { ImGui::SetTooltip("Apply silence"); } ImGui::SameLine(); if (ImGui::Button(ICON_FA_TIMES "##SDelete")) { - e->lockEngine([this,sample]() { - SAMPLE_OP_BEGIN; - - sample->strip(start,end); - updateSampleTex=true; - - e->renderSamples(); - }); - sampleSelStart=-1; - sampleSelEnd=-1; - MARK_MODIFIED; + doAction(GUI_ACTION_SAMPLE_DELETE); } if (ImGui::IsItemHovered()) { ImGui::SetTooltip("Delete"); } ImGui::SameLine(); if (ImGui::Button(ICON_FA_CROP "##STrim")) { - e->lockEngine([this,sample]() { - SAMPLE_OP_BEGIN; - - sample->trim(start,end); - updateSampleTex=true; - - e->renderSamples(); - }); - sampleSelStart=-1; - sampleSelEnd=-1; - MARK_MODIFIED; + doAction(GUI_ACTION_SAMPLE_TRIM); } if (ImGui::IsItemHovered()) { ImGui::SetTooltip("Trim"); @@ -450,82 +327,21 @@ void FurnaceGUI::drawSampleEdit() { ImGui::Dummy(ImVec2(4.0*dpiScale,dpiScale)); ImGui::SameLine(); if (ImGui::Button(ICON_FA_BACKWARD "##SReverse")) { - e->lockEngine([this,sample]() { - SAMPLE_OP_BEGIN; - - if (sample->depth==16) { - for (unsigned int i=start; idata16[i]^=sample->data16[ri]; - sample->data16[ri]^=sample->data16[i]; - sample->data16[i]^=sample->data16[ri]; - } - } else if (sample->depth==8) { - for (unsigned int i=start; idata8[i]^=sample->data8[ri]; - sample->data8[ri]^=sample->data8[i]; - sample->data8[i]^=sample->data8[ri]; - } - } - - updateSampleTex=true; - - e->renderSamples(); - }); - MARK_MODIFIED; + doAction(GUI_ACTION_SAMPLE_REVERSE); } if (ImGui::IsItemHovered()) { ImGui::SetTooltip("Reverse"); } ImGui::SameLine(); if (ImGui::Button(ICON_FA_SORT_AMOUNT_ASC "##SInvert")) { - e->lockEngine([this,sample]() { - SAMPLE_OP_BEGIN; - - if (sample->depth==16) { - for (unsigned int i=start; idata16[i]=-sample->data16[i]; - if (sample->data16[i]==-32768) sample->data16[i]=32767; - } - } else if (sample->depth==8) { - for (unsigned int i=start; idata8[i]=-sample->data8[i]; - if (sample->data16[i]==-128) sample->data16[i]=127; - } - } - - updateSampleTex=true; - - e->renderSamples(); - }); - MARK_MODIFIED; + doAction(GUI_ACTION_SAMPLE_INVERT); } if (ImGui::IsItemHovered()) { ImGui::SetTooltip("Invert"); } ImGui::SameLine(); if (ImGui::Button(ICON_FA_LEVEL_DOWN "##SSign")) { - e->lockEngine([this,sample]() { - SAMPLE_OP_BEGIN; - - if (sample->depth==16) { - for (unsigned int i=start; idata16[i]^=0x8000; - } - } else if (sample->depth==8) { - for (unsigned int i=start; idata8[i]^=0x80; - } - } - - updateSampleTex=true; - - e->renderSamples(); - }); - MARK_MODIFIED; + doAction(GUI_ACTION_SAMPLE_SIGN); } if (ImGui::IsItemHovered()) { ImGui::SetTooltip("Signed/unsigned exchange"); @@ -535,6 +351,10 @@ void FurnaceGUI::drawSampleEdit() { if (ImGui::IsItemHovered()) { ImGui::SetTooltip("Apply filter"); } + if (openSampleFilterOpt) { + openSampleFilterOpt=false; + ImGui::OpenPopup("SFilterOpt"); + } if (ImGui::BeginPopupContextItem("SFilterOpt",ImGuiPopupFlags_MouseButtonLeft)) { float lowP=sampleFilterL*100.0f; float bandP=sampleFilterB*100.0f; diff --git a/src/gui/sampleUtil.h b/src/gui/sampleUtil.h new file mode 100644 index 00000000..981a5604 --- /dev/null +++ b/src/gui/sampleUtil.h @@ -0,0 +1,31 @@ +/** + * Furnace Tracker - multi-system chiptune tracker + * Copyright (C) 2021-2022 tildearrow and contributors + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#define SAMPLE_OP_BEGIN \ + unsigned int start=0; \ + unsigned int end=sample->samples; \ + if (sampleSelStart!=-1 && sampleSelEnd!=-1 && sampleSelStart!=sampleSelEnd) { \ + start=sampleSelStart; \ + end=sampleSelEnd; \ + if (start>end) { \ + start^=end; \ + end^=start; \ + start^=end; \ + } \ + } diff --git a/src/gui/settings.cpp b/src/gui/settings.cpp index b89ed593..9a1a9eee 100644 --- a/src/gui/settings.cpp +++ b/src/gui/settings.cpp @@ -24,6 +24,7 @@ #include "ImGuiFileDialog.h" #include "IconsFontAwesome4.h" #include "misc/cpp/imgui_stdlib.h" +#include #include #include #include @@ -865,6 +866,39 @@ void FurnaceGUI::drawSettings() { KEYBIND_CONFIG_END; ImGui::TreePop(); } + if (ImGui::TreeNode("Sample editor")) { + KEYBIND_CONFIG_BEGIN("keysSampleEdit"); + + UI_KEYBIND_CONFIG(GUI_ACTION_SAMPLE_SELECT,"Edit mode: Select"); + UI_KEYBIND_CONFIG(GUI_ACTION_SAMPLE_DRAW,"Edit mode: Draw"); + UI_KEYBIND_CONFIG(GUI_ACTION_SAMPLE_CUT,"Cut"); + UI_KEYBIND_CONFIG(GUI_ACTION_SAMPLE_COPY,"Copy"); + UI_KEYBIND_CONFIG(GUI_ACTION_SAMPLE_PASTE,"Paste"); + UI_KEYBIND_CONFIG(GUI_ACTION_SAMPLE_PASTE_REPLACE,"Paste replace"); + UI_KEYBIND_CONFIG(GUI_ACTION_SAMPLE_PASTE_MIX,"Paste mix"); + UI_KEYBIND_CONFIG(GUI_ACTION_SAMPLE_SELECT_ALL,"Select all"); + UI_KEYBIND_CONFIG(GUI_ACTION_SAMPLE_RESIZE,"Resize"); + UI_KEYBIND_CONFIG(GUI_ACTION_SAMPLE_RESAMPLE,"Resample"); + UI_KEYBIND_CONFIG(GUI_ACTION_SAMPLE_AMPLIFY,"Amplify"); + UI_KEYBIND_CONFIG(GUI_ACTION_SAMPLE_NORMALIZE,"Normalize"); + UI_KEYBIND_CONFIG(GUI_ACTION_SAMPLE_FADE_IN,"Fade in"); + UI_KEYBIND_CONFIG(GUI_ACTION_SAMPLE_FADE_OUT,"Fade out"); + UI_KEYBIND_CONFIG(GUI_ACTION_SAMPLE_SILENCE,"Apply silence"); + UI_KEYBIND_CONFIG(GUI_ACTION_SAMPLE_DELETE,"Delete"); + UI_KEYBIND_CONFIG(GUI_ACTION_SAMPLE_TRIM,"Trim"); + UI_KEYBIND_CONFIG(GUI_ACTION_SAMPLE_REVERSE,"Reverse"); + UI_KEYBIND_CONFIG(GUI_ACTION_SAMPLE_INVERT,"Invert"); + UI_KEYBIND_CONFIG(GUI_ACTION_SAMPLE_SIGN,"Signed/unsigned exchange"); + UI_KEYBIND_CONFIG(GUI_ACTION_SAMPLE_FILTER,"Apply filter"); + UI_KEYBIND_CONFIG(GUI_ACTION_SAMPLE_PREVIEW,"Preview sample"); + UI_KEYBIND_CONFIG(GUI_ACTION_SAMPLE_STOP_PREVIEW,"Stop sample preview"); + UI_KEYBIND_CONFIG(GUI_ACTION_SAMPLE_ZOOM_IN,"Zoom in"); + UI_KEYBIND_CONFIG(GUI_ACTION_SAMPLE_ZOOM_OUT,"Zoom out"); + UI_KEYBIND_CONFIG(GUI_ACTION_SAMPLE_ZOOM_AUTO,"Toggle auto-zoom"); + + KEYBIND_CONFIG_END; + ImGui::TreePop(); + } ImGui::EndTabItem(); } ImGui::EndTabBar(); @@ -1119,6 +1153,33 @@ void FurnaceGUI::syncSettings() { LOAD_KEYBIND(GUI_ACTION_SAMPLE_LIST_PREVIEW,0); LOAD_KEYBIND(GUI_ACTION_SAMPLE_LIST_STOP_PREVIEW,0); + LOAD_KEYBIND(GUI_ACTION_SAMPLE_SELECT,FURKMOD_SHIFT|SDLK_i); + LOAD_KEYBIND(GUI_ACTION_SAMPLE_DRAW,FURKMOD_SHIFT|SDLK_d); + LOAD_KEYBIND(GUI_ACTION_SAMPLE_CUT,FURKMOD_CMD|SDLK_x); + LOAD_KEYBIND(GUI_ACTION_SAMPLE_COPY,FURKMOD_CMD|SDLK_c); + LOAD_KEYBIND(GUI_ACTION_SAMPLE_PASTE,FURKMOD_CMD|SDLK_v); + LOAD_KEYBIND(GUI_ACTION_SAMPLE_PASTE_REPLACE,FURKMOD_CMD|FURKMOD_SHIFT|SDLK_x); + LOAD_KEYBIND(GUI_ACTION_SAMPLE_PASTE_MIX,FURKMOD_CMD|FURKMOD_ALT|SDLK_x); + LOAD_KEYBIND(GUI_ACTION_SAMPLE_SELECT_ALL,FURKMOD_CMD|SDLK_a); + LOAD_KEYBIND(GUI_ACTION_SAMPLE_RESIZE,FURKMOD_CMD|SDLK_r); + LOAD_KEYBIND(GUI_ACTION_SAMPLE_RESAMPLE,FURKMOD_CMD|SDLK_e); + LOAD_KEYBIND(GUI_ACTION_SAMPLE_AMPLIFY,FURKMOD_CMD|SDLK_b); + LOAD_KEYBIND(GUI_ACTION_SAMPLE_NORMALIZE,FURKMOD_CMD|SDLK_n); + LOAD_KEYBIND(GUI_ACTION_SAMPLE_FADE_IN,FURKMOD_CMD|SDLK_i); + LOAD_KEYBIND(GUI_ACTION_SAMPLE_FADE_OUT,FURKMOD_CMD|SDLK_o); + LOAD_KEYBIND(GUI_ACTION_SAMPLE_SILENCE,FURKMOD_SHIFT|SDLK_DELETE); + LOAD_KEYBIND(GUI_ACTION_SAMPLE_DELETE,SDLK_DELETE); + LOAD_KEYBIND(GUI_ACTION_SAMPLE_TRIM,FURKMOD_CMD|SDLK_DELETE); + LOAD_KEYBIND(GUI_ACTION_SAMPLE_REVERSE,FURKMOD_CMD|SDLK_t); + LOAD_KEYBIND(GUI_ACTION_SAMPLE_INVERT,FURKMOD_CMD|FURKMOD_SHIFT|SDLK_t); + LOAD_KEYBIND(GUI_ACTION_SAMPLE_SIGN,FURKMOD_CMD|SDLK_u); + LOAD_KEYBIND(GUI_ACTION_SAMPLE_FILTER,FURKMOD_CMD|SDLK_f); + LOAD_KEYBIND(GUI_ACTION_SAMPLE_PREVIEW,0); + LOAD_KEYBIND(GUI_ACTION_SAMPLE_STOP_PREVIEW,0); + LOAD_KEYBIND(GUI_ACTION_SAMPLE_ZOOM_IN,FURKMOD_CMD|SDLK_EQUALS); + LOAD_KEYBIND(GUI_ACTION_SAMPLE_ZOOM_OUT,FURKMOD_CMD|SDLK_MINUS); + LOAD_KEYBIND(GUI_ACTION_SAMPLE_ZOOM_AUTO,FURKMOD_CMD|SDLK_0); + LOAD_KEYBIND(GUI_ACTION_ORDERS_UP,SDLK_UP); LOAD_KEYBIND(GUI_ACTION_ORDERS_DOWN,SDLK_DOWN); LOAD_KEYBIND(GUI_ACTION_ORDERS_LEFT,SDLK_LEFT); @@ -1404,6 +1465,33 @@ void FurnaceGUI::commitSettings() { SAVE_KEYBIND(GUI_ACTION_SAMPLE_LIST_PREVIEW); SAVE_KEYBIND(GUI_ACTION_SAMPLE_LIST_STOP_PREVIEW); + SAVE_KEYBIND(GUI_ACTION_SAMPLE_SELECT); + SAVE_KEYBIND(GUI_ACTION_SAMPLE_DRAW); + SAVE_KEYBIND(GUI_ACTION_SAMPLE_CUT); + SAVE_KEYBIND(GUI_ACTION_SAMPLE_COPY); + SAVE_KEYBIND(GUI_ACTION_SAMPLE_PASTE); + SAVE_KEYBIND(GUI_ACTION_SAMPLE_PASTE_REPLACE); + SAVE_KEYBIND(GUI_ACTION_SAMPLE_PASTE_MIX); + SAVE_KEYBIND(GUI_ACTION_SAMPLE_SELECT_ALL); + SAVE_KEYBIND(GUI_ACTION_SAMPLE_RESIZE); + SAVE_KEYBIND(GUI_ACTION_SAMPLE_RESAMPLE); + SAVE_KEYBIND(GUI_ACTION_SAMPLE_AMPLIFY); + SAVE_KEYBIND(GUI_ACTION_SAMPLE_NORMALIZE); + SAVE_KEYBIND(GUI_ACTION_SAMPLE_FADE_IN); + SAVE_KEYBIND(GUI_ACTION_SAMPLE_FADE_OUT); + SAVE_KEYBIND(GUI_ACTION_SAMPLE_SILENCE); + SAVE_KEYBIND(GUI_ACTION_SAMPLE_DELETE); + SAVE_KEYBIND(GUI_ACTION_SAMPLE_TRIM); + SAVE_KEYBIND(GUI_ACTION_SAMPLE_REVERSE); + SAVE_KEYBIND(GUI_ACTION_SAMPLE_INVERT); + SAVE_KEYBIND(GUI_ACTION_SAMPLE_SIGN); + SAVE_KEYBIND(GUI_ACTION_SAMPLE_FILTER); + SAVE_KEYBIND(GUI_ACTION_SAMPLE_PREVIEW); + SAVE_KEYBIND(GUI_ACTION_SAMPLE_STOP_PREVIEW); + SAVE_KEYBIND(GUI_ACTION_SAMPLE_ZOOM_IN); + SAVE_KEYBIND(GUI_ACTION_SAMPLE_ZOOM_OUT); + SAVE_KEYBIND(GUI_ACTION_SAMPLE_ZOOM_AUTO); + SAVE_KEYBIND(GUI_ACTION_ORDERS_UP); SAVE_KEYBIND(GUI_ACTION_ORDERS_DOWN); SAVE_KEYBIND(GUI_ACTION_ORDERS_LEFT); @@ -1454,6 +1542,7 @@ void FurnaceGUI::parseKeybinds() { actionMapInsList.clear(); actionMapWaveList.clear(); actionMapSampleList.clear(); + actionMapSample.clear(); actionMapOrders.clear(); for (int i=GUI_ACTION_GLOBAL_MIN+1; i