From a199c102ce7626611222feca841a5e22981febdf Mon Sep 17 00:00:00 2001 From: tildearrow Date: Tue, 22 Mar 2022 15:37:36 -0500 Subject: [PATCH 1/9] GUI: hopefully better workaround to macOS #4 --- src/gui/gui.cpp | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/gui/gui.cpp b/src/gui/gui.cpp index f32e6eba2..11d8875b2 100644 --- a/src/gui/gui.cpp +++ b/src/gui/gui.cpp @@ -2165,6 +2165,9 @@ bool FurnaceGUI::loop() { if (firstFrame) { firstFrame=false; if (patternOpen) nextWindow=GUI_WINDOW_PATTERN; +#ifdef __APPLE__ + SDL_RaiseWindow(sdlWin); +#endif } if (fileDialog->render(ImVec2(600.0f*dpiScale,400.0f*dpiScale),ImVec2(scrW*dpiScale,scrH*dpiScale))) { @@ -2624,10 +2627,6 @@ bool FurnaceGUI::init() { firstFrame=true; -#ifdef __APPLE__ - SDL_RaiseWindow(sdlWin); -#endif - return true; } From 7ebc63a5144547498c69201cca8b55a432b06865 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Tue, 22 Mar 2022 17:01:06 -0500 Subject: [PATCH 2/9] GUI: implement sample paste --- src/engine/sample.cpp | 8 +++- src/gui/doAction.cpp | 88 +++++++++++++++++++++++++++++++++++++++--- src/gui/gui.cpp | 2 + src/gui/gui.h | 5 ++- src/gui/sampleEdit.cpp | 30 ++++++++++++++ src/gui/settings.cpp | 7 +++- 6 files changed, 129 insertions(+), 11 deletions(-) diff --git a/src/engine/sample.cpp b/src/engine/sample.cpp index 33532d43d..19768fcf7 100644 --- a/src/engine/sample.cpp +++ b/src/engine/sample.cpp @@ -258,7 +258,6 @@ 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) { @@ -283,7 +282,12 @@ bool DivSample::insert(unsigned int pos, unsigned int length) { short* oldData16=data16; data16=NULL; initInternal(16,count); - memcpy(data16,oldData16,sizeof(short)*count); + if (pos>0) { + memcpy(data16,oldData16,sizeof(short)*pos); + } + if (count-pos-length>0) { + memcpy(&(data16[pos+length]),&(oldData16[pos]),sizeof(short)*(count-pos-length)); + } delete[] oldData16; } else { initInternal(16,count); diff --git a/src/gui/doAction.cpp b/src/gui/doAction.cpp index 51dad892e..c89c957c4 100644 --- a/src/gui/doAction.cpp +++ b/src/gui/doAction.cpp @@ -599,7 +599,7 @@ void FurnaceGUI::doAction(int what) { } sampleClipboard=new short[end-start]; sampleClipboardLen=end-start; - memcpy(sampleClipboard,&(sample->data16[start]),end-start); + memcpy(sampleClipboard,&(sample->data16[start]),sizeof(short)*(end-start)); e->lockEngine([this,sample,start,end]() { sample->strip(start,end); @@ -625,18 +625,92 @@ void FurnaceGUI::doAction(int what) { } sampleClipboard=new short[end-start]; sampleClipboardLen=end-start; - memcpy(sampleClipboard,&(sample->data16[start]),end-start); + memcpy(sampleClipboard,&(sample->data16[start]),sizeof(short)*(end-start)); break; } - case GUI_ACTION_SAMPLE_PASTE: // TODO!!! + case GUI_ACTION_SAMPLE_PASTE: { if (curSample<0 || curSample>=(int)e->song.sample.size()) break; + if (sampleClipboard==NULL || sampleClipboardLen<1) break; + DivSample* sample=e->song.sample[curSample]; + int pos=(sampleSelStart==-1 || sampleSelStart==sampleSelEnd)?sample->samples:sampleSelStart; + + e->lockEngine([this,sample,pos]() { + if (!sample->insert(pos,sampleClipboardLen)) { + showError("couldn't paste! make sure your sample is 8 or 16-bit."); + } else { + if (sample->depth==8) { + for (size_t i=0; idata8[pos+i]=sampleClipboard[i]>>8; + } + } else { + memcpy(&(sample->data16[pos]),sampleClipboard,sizeof(short)*sampleClipboardLen); + } + } + e->renderSamples(); + }); + sampleSelStart=pos; + sampleSelEnd=pos+sampleClipboardLen; + MARK_MODIFIED; break; - case GUI_ACTION_SAMPLE_PASTE_REPLACE: + } + case GUI_ACTION_SAMPLE_PASTE_REPLACE: { if (curSample<0 || curSample>=(int)e->song.sample.size()) break; + if (sampleClipboard==NULL || sampleClipboardLen<1) break; + DivSample* sample=e->song.sample[curSample]; + int pos=(sampleSelStart==-1 || sampleSelStart==sampleSelEnd)?0:sampleSelStart; + + e->lockEngine([this,sample,pos]() { + if (sample->depth==8) { + for (size_t i=0; i=sample->samples) break; + sample->data8[pos+i]=sampleClipboard[i]>>8; + } + } else { + for (size_t i=0; i=sample->samples) break; + sample->data16[pos+i]=sampleClipboard[i]; + } + } + e->renderSamples(); + }); + sampleSelStart=pos; + sampleSelEnd=pos+sampleClipboardLen; + if (sampleSelEnd>(int)sample->samples) sampleSelEnd=sample->samples; + MARK_MODIFIED; break; - case GUI_ACTION_SAMPLE_PASTE_MIX: + } + case GUI_ACTION_SAMPLE_PASTE_MIX: { if (curSample<0 || curSample>=(int)e->song.sample.size()) break; + if (sampleClipboard==NULL || sampleClipboardLen<1) break; + DivSample* sample=e->song.sample[curSample]; + int pos=(sampleSelStart==-1 || sampleSelStart==sampleSelEnd)?0:sampleSelStart; + + e->lockEngine([this,sample,pos]() { + if (sample->depth==8) { + for (size_t i=0; i=sample->samples) break; + int val=sample->data8[pos+i]+(sampleClipboard[i]>>8); + if (val>127) val=127; + if (val<-128) val=-128; + sample->data8[pos+i]=val; + } + } else { + for (size_t i=0; i=sample->samples) break; + int val=sample->data16[pos+i]+sampleClipboard[i]; + if (val>32767) val=32767; + if (val<-32768) val=-32768; + sample->data16[pos+i]=val; + } + } + e->renderSamples(); + }); + sampleSelStart=pos; + sampleSelEnd=pos+sampleClipboardLen; + if (sampleSelEnd>(int)sample->samples) sampleSelEnd=sample->samples; + MARK_MODIFIED; break; + } case GUI_ACTION_SAMPLE_SELECT_ALL: { if (curSample<0 || curSample>=(int)e->song.sample.size()) break; DivSample* sample=e->song.sample[curSample]; @@ -761,6 +835,10 @@ void FurnaceGUI::doAction(int what) { MARK_MODIFIED; break; } + case GUI_ACTION_SAMPLE_INSERT: + if (curSample<0 || curSample>=(int)e->song.sample.size()) break; + openSampleSilenceOpt=true; + break; case GUI_ACTION_SAMPLE_SILENCE: { if (curSample<0 || curSample>=(int)e->song.sample.size()) break; DivSample* sample=e->song.sample[curSample]; diff --git a/src/gui/gui.cpp b/src/gui/gui.cpp index 11d8875b2..fedf53d15 100644 --- a/src/gui/gui.cpp +++ b/src/gui/gui.cpp @@ -2838,6 +2838,7 @@ FurnaceGUI::FurnaceGUI(): prevSampleZoom(1.0), samplePos(0), resizeSize(1024), + silenceSize(1024), resampleTarget(32000), resampleStrat(5), amplifyVol(100.0), @@ -2863,6 +2864,7 @@ FurnaceGUI::FurnaceGUI(): openSampleResizeOpt(false), openSampleResampleOpt(false), openSampleAmplifyOpt(false), + openSampleSilenceOpt(false), openSampleFilterOpt(false) { // value keys valueKeys[SDLK_0]=0; diff --git a/src/gui/gui.h b/src/gui/gui.h index aa661b3a4..f92efd959 100644 --- a/src/gui/gui.h +++ b/src/gui/gui.h @@ -380,6 +380,7 @@ enum FurnaceGUIActions { GUI_ACTION_SAMPLE_FADE_IN, GUI_ACTION_SAMPLE_FADE_OUT, GUI_ACTION_SAMPLE_SILENCE, + GUI_ACTION_SAMPLE_INSERT, GUI_ACTION_SAMPLE_DELETE, GUI_ACTION_SAMPLE_TRIM, GUI_ACTION_SAMPLE_REVERSE, @@ -796,7 +797,7 @@ class FurnaceGUI { double sampleZoom; double prevSampleZoom; int samplePos; - int resizeSize; + int resizeSize, silenceSize; double resampleTarget; int resampleStrat; float amplifyVol; @@ -810,7 +811,7 @@ class FurnaceGUI { unsigned char sampleFilterPower; short* sampleClipboard; size_t sampleClipboardLen; - bool openSampleResizeOpt, openSampleResampleOpt, openSampleAmplifyOpt, openSampleFilterOpt; + bool openSampleResizeOpt, openSampleResampleOpt, openSampleAmplifyOpt, openSampleSilenceOpt, openSampleFilterOpt; // visualizer float keyHit[DIV_MAX_CHANS]; diff --git a/src/gui/sampleEdit.cpp b/src/gui/sampleEdit.cpp index 28e0f3d59..81bab4489 100644 --- a/src/gui/sampleEdit.cpp +++ b/src/gui/sampleEdit.cpp @@ -303,6 +303,36 @@ void FurnaceGUI::drawSampleEdit() { ImGui::SetTooltip("Fade out"); } ImGui::SameLine(); + ImGui::Button(ICON_FA_ADJUST "##SInsertSilence"); + if (ImGui::IsItemHovered()) { + ImGui::SetTooltip("Insert silence"); + } + if (openSampleSilenceOpt) { + openSampleSilenceOpt=false; + ImGui::OpenPopup("SSilenceOpt"); + } + if (ImGui::BeginPopupContextItem("SSilenceOpt",ImGuiPopupFlags_MouseButtonLeft)) { + if (ImGui::InputInt("Samples",&silenceSize,1,64)) { + if (silenceSize<0) silenceSize=0; + if (silenceSize>16777215) silenceSize=16777215; + } + if (ImGui::Button("Resize")) { + int pos=(sampleSelStart==-1 || sampleSelStart==sampleSelEnd)?sample->samples:sampleSelStart; + e->lockEngine([this,sample,pos]() { + if (!sample->insert(pos,silenceSize)) { + showError("couldn't insert! make sure your sample is 8 or 16-bit."); + } + e->renderSamples(); + }); + updateSampleTex=true; + sampleSelStart=pos; + sampleSelEnd=pos+silenceSize; + MARK_MODIFIED; + ImGui::CloseCurrentPopup(); + } + ImGui::EndPopup(); + } + ImGui::SameLine(); if (ImGui::Button(ICON_FA_ERASER "##SSilence")) { doAction(GUI_ACTION_SAMPLE_SILENCE); } diff --git a/src/gui/settings.cpp b/src/gui/settings.cpp index 9a1a9eee2..9f1a8f5b1 100644 --- a/src/gui/settings.cpp +++ b/src/gui/settings.cpp @@ -883,6 +883,7 @@ void FurnaceGUI::drawSettings() { 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_INSERT,"Insert silence"); 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"); @@ -1158,8 +1159,8 @@ void FurnaceGUI::syncSettings() { 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_PASTE_REPLACE,FURKMOD_CMD|FURKMOD_SHIFT|SDLK_v); + LOAD_KEYBIND(GUI_ACTION_SAMPLE_PASTE_MIX,FURKMOD_CMD|FURKMOD_ALT|SDLK_v); 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); @@ -1167,6 +1168,7 @@ void FurnaceGUI::syncSettings() { 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_INSERT,SDLK_INSERT); 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); @@ -1479,6 +1481,7 @@ void FurnaceGUI::commitSettings() { 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_INSERT); SAVE_KEYBIND(GUI_ACTION_SAMPLE_SILENCE); SAVE_KEYBIND(GUI_ACTION_SAMPLE_DELETE); SAVE_KEYBIND(GUI_ACTION_SAMPLE_TRIM); From 97926c4e5c21f723259557bf878ec5000daf3b52 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Tue, 22 Mar 2022 17:04:36 -0500 Subject: [PATCH 3/9] GUI: update sample texture --- src/gui/doAction.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/gui/doAction.cpp b/src/gui/doAction.cpp index c89c957c4..66a05fb3a 100644 --- a/src/gui/doAction.cpp +++ b/src/gui/doAction.cpp @@ -650,6 +650,7 @@ void FurnaceGUI::doAction(int what) { }); sampleSelStart=pos; sampleSelEnd=pos+sampleClipboardLen; + updateSampleTex=true; MARK_MODIFIED; break; } @@ -676,6 +677,7 @@ void FurnaceGUI::doAction(int what) { sampleSelStart=pos; sampleSelEnd=pos+sampleClipboardLen; if (sampleSelEnd>(int)sample->samples) sampleSelEnd=sample->samples; + updateSampleTex=true; MARK_MODIFIED; break; } @@ -708,6 +710,7 @@ void FurnaceGUI::doAction(int what) { sampleSelStart=pos; sampleSelEnd=pos+sampleClipboardLen; if (sampleSelEnd>(int)sample->samples) sampleSelEnd=sample->samples; + updateSampleTex=true; MARK_MODIFIED; break; } From 8e0119b2d30852012a9e551d3f6bc05022927bc7 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Tue, 22 Mar 2022 17:33:54 -0500 Subject: [PATCH 4/9] GUI: more sample editor fixes --- src/gui/sampleEdit.cpp | 24 +++++++++++++++++++++--- 1 file changed, 21 insertions(+), 3 deletions(-) diff --git a/src/gui/sampleEdit.cpp b/src/gui/sampleEdit.cpp index 81bab4489..f8515db54 100644 --- a/src/gui/sampleEdit.cpp +++ b/src/gui/sampleEdit.cpp @@ -581,9 +581,15 @@ void FurnaceGUI::drawSampleEdit() { } else { ImU32 bgColor=ImGui::GetColorU32(ImGuiCol_FrameBg); ImU32 lineColor=ImGui::GetColorU32(ImGuiCol_PlotLines); + ImU32 centerLineColor=ImAlphaBlendColors(bgColor,ImGui::GetColorU32(ImGuiCol_PlotLines,0.25)); for (int i=0; i0) { + for (int i=availX*(availY>>1); i>1)); i++) { + data[i]=centerLineColor; + } + } unsigned int xCoarse=samplePos; unsigned int xFine=0; unsigned int xAdvanceCoarse=sampleZoom; @@ -659,7 +665,7 @@ void FurnaceGUI::drawSampleEdit() { bool drawSelection=false; if (!sampleDragMode) { - if (sampleSelStart>=0 && sampleSelEnd>=0 && sampleSelStart!=sampleSelEnd) { + if (sampleSelStart>=0 && sampleSelEnd>=0) { int start=sampleSelStart; int end=sampleSelEnd; if (start>end) { @@ -699,13 +705,25 @@ void FurnaceGUI::drawSampleEdit() { } ImDrawList* dl=ImGui::GetWindowDrawList(); ImVec2 p1=rectMin; - p1.x+=start/sampleZoom-samplePos; + p1.x+=(start-samplePos)/sampleZoom; - ImVec2 p2=ImVec2(rectMin.x+end/sampleZoom-samplePos,rectMax.y); + ImVec2 p2=ImVec2(rectMin.x+(end-samplePos)/sampleZoom,rectMax.y); + ImVec4 boundColor=uiColors[GUI_COLOR_ACCENT_PRIMARY]; ImVec4 selColor=uiColors[GUI_COLOR_ACCENT_SECONDARY]; + boundColor.w*=0.5; selColor.w*=0.25; + if (p1.xrectMax.x) p1.x=rectMax.x; + + if (p2.xrectMax.x) p2.x=rectMax.x; + dl->AddRectFilled(p1,p2,ImGui::GetColorU32(selColor)); + dl->AddLine(ImVec2(p1.x,p1.y),ImVec2(p1.x,p2.y),ImGui::GetColorU32(boundColor)); + if (start!=end) { + dl->AddLine(ImVec2(p2.x,p1.y),ImVec2(p2.x,p2.y),ImGui::GetColorU32(boundColor)); + } } ImS64 scrollV=samplePos; From 519dd7f2dd66b26c9cd5fb074059d1ae6028e6cb Mon Sep 17 00:00:00 2001 From: tildearrow Date: Tue, 22 Mar 2022 18:05:32 -0500 Subject: [PATCH 5/9] GUI: aaand more sample editor work now it has a right click menu --- src/gui/gui.cpp | 2 -- src/gui/gui.h | 2 ++ src/gui/sampleEdit.cpp | 39 +++++++++++++++++++++++++++++++++++++-- 3 files changed, 39 insertions(+), 4 deletions(-) diff --git a/src/gui/gui.cpp b/src/gui/gui.cpp index fedf53d15..9f869e57d 100644 --- a/src/gui/gui.cpp +++ b/src/gui/gui.cpp @@ -1539,8 +1539,6 @@ void FurnaceGUI::processDrags(int dragX, int dragY) { fileName+=fallback; \ } -#define BIND_FOR(x) getKeyName(actionKeys[x],true).c_str() - void FurnaceGUI::editOptions(bool topMenu) { char id[4096]; if (ImGui::MenuItem("cut",BIND_FOR(GUI_ACTION_PAT_CUT))) doCopy(true); diff --git a/src/gui/gui.h b/src/gui/gui.h index f92efd959..a4feed9b5 100644 --- a/src/gui/gui.h +++ b/src/gui/gui.h @@ -43,6 +43,8 @@ #define TOGGLE_COLOR(x) ((x)?uiColors[GUI_COLOR_TOGGLE_ON]:uiColors[GUI_COLOR_TOGGLE_OFF]) +#define BIND_FOR(x) getKeyName(actionKeys[x],true).c_str() + enum FurnaceGUIColors { GUI_COLOR_BACKGROUND=0, GUI_COLOR_FRAME_BACKGROUND, diff --git a/src/gui/sampleEdit.cpp b/src/gui/sampleEdit.cpp index f8515db54..3bdfa6c9f 100644 --- a/src/gui/sampleEdit.cpp +++ b/src/gui/sampleEdit.cpp @@ -26,6 +26,7 @@ #include #include "guiConst.h" #include "sampleUtil.h" +#include "util.h" void FurnaceGUI::drawSampleEdit() { if (nextWindow==GUI_WINDOW_SAMPLE_EDIT) { @@ -580,10 +581,17 @@ void FurnaceGUI::drawSampleEdit() { logE("error while locking sample texture! %s\n",SDL_GetError()); } else { ImU32 bgColor=ImGui::GetColorU32(ImGuiCol_FrameBg); + ImU32 bgColorLoop=ImAlphaBlendColors(bgColor,ImGui::GetColorU32(ImGuiCol_FrameBgHovered,0.5)); ImU32 lineColor=ImGui::GetColorU32(ImGuiCol_PlotLines); ImU32 centerLineColor=ImAlphaBlendColors(bgColor,ImGui::GetColorU32(ImGuiCol_PlotLines,0.25)); - for (int i=0; iloopStart>=0 && sample->loopStart<(int)sample->samples && j-samplePos>sample->loopStart) { + data[i*availX+j]=bgColorLoop; + } else { + data[i*availX+j]=bgColor; + } + } } if (availY>0) { for (int i=availX*(availY>>1); i>1)); i++) { @@ -661,6 +669,33 @@ void FurnaceGUI::drawSampleEdit() { } } } + + if (!sampleDragMode && ImGui::IsItemClicked(ImGuiMouseButton_Right)) { + ImGui::OpenPopup("SRightClick"); + } + + if (ImGui::BeginPopup("SRightClick",ImGuiWindowFlags_NoTitleBar|ImGuiWindowFlags_NoMove|ImGuiWindowFlags_AlwaysAutoResize)) { + if (ImGui::MenuItem("cut",BIND_FOR(GUI_ACTION_SAMPLE_CUT))) { + doAction(GUI_ACTION_SAMPLE_CUT); + } + if (ImGui::MenuItem("copy",BIND_FOR(GUI_ACTION_SAMPLE_COPY))) { + doAction(GUI_ACTION_SAMPLE_COPY); + } + if (ImGui::MenuItem("paste",BIND_FOR(GUI_ACTION_SAMPLE_PASTE))) { + doAction(GUI_ACTION_SAMPLE_PASTE); + } + if (ImGui::MenuItem("paste (replace)",BIND_FOR(GUI_ACTION_SAMPLE_PASTE_REPLACE))) { + doAction(GUI_ACTION_SAMPLE_PASTE_REPLACE); + } + if (ImGui::MenuItem("paste (mix)",BIND_FOR(GUI_ACTION_SAMPLE_PASTE_MIX))) { + doAction(GUI_ACTION_SAMPLE_PASTE_MIX); + } + if (ImGui::MenuItem("select all",BIND_FOR(GUI_ACTION_SAMPLE_SELECT_ALL))) { + doAction(GUI_ACTION_SAMPLE_SELECT_ALL); + } + ImGui::EndPopup(); + } + String statusBar=sampleDragMode?"Draw":"Select"; bool drawSelection=false; From 2c05d56ab22f5476fdd9573c79bc91110e3f046f Mon Sep 17 00:00:00 2001 From: tildearrow Date: Tue, 22 Mar 2022 18:36:29 -0500 Subject: [PATCH 6/9] prepare for sample undo/redo --- src/engine/sample.cpp | 45 +++++++++++++++++++++++++++++++++++++ src/engine/sample.h | 52 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 97 insertions(+) diff --git a/src/engine/sample.cpp b/src/engine/sample.cpp index 19768fcf7..d5e1844c8 100644 --- a/src/engine/sample.cpp +++ b/src/engine/sample.cpp @@ -32,6 +32,10 @@ extern "C" { #include "../../extern/adpcm/ymz_codec.h" } +DivSampleHistory::~DivSampleHistory() { + if (data!=NULL) delete[] data; +} + bool DivSample::save(const char* path) { SNDFILE* f; SF_INFO si; @@ -786,7 +790,48 @@ unsigned int DivSample::getCurBufLen() { return 0; } +DivSampleHistory* DivSample::prepareUndo(bool data) { + DivSampleHistory* h; + if (data) { + unsigned char* duplicate; + if (getCurBuf()==NULL) { + duplicate=NULL; + } else { + duplicate=new unsigned char[getCurBufLen()]; + memcpy(duplicate,getCurBuf(),getCurBufLen()); + } + h=new DivSampleHistory(duplicate,getCurBufLen(),samples,depth,rate,centerRate,loopStart); + } else { + h=new DivSampleHistory(depth,rate,centerRate,loopStart); + } + while (!redoHist.empty()) { + DivSampleHistory* h=redoHist.front(); + delete h; + redoHist.pop_front(); + } + undoHist.push_back(h); + return h; +} + +int DivSample::undo() { + return 0; +} + +int DivSample::redo() { + return 0; +} + DivSample::~DivSample() { + while (!undoHist.empty()) { + DivSampleHistory* h=undoHist.front(); + delete h; + undoHist.pop_front(); + } + while (!redoHist.empty()) { + DivSampleHistory* h=redoHist.front(); + delete h; + redoHist.pop_front(); + } if (data8) delete[] data8; if (data16) delete[] data16; if (data1) delete[] data1; diff --git a/src/engine/sample.h b/src/engine/sample.h index c697a9951..f07c7ef91 100644 --- a/src/engine/sample.h +++ b/src/engine/sample.h @@ -18,6 +18,7 @@ */ #include "../ta-utils.h" +#include enum DivResampleFilters { DIV_RESAMPLE_NONE=0, @@ -28,6 +29,33 @@ enum DivResampleFilters { DIV_RESAMPLE_BEST }; +struct DivSampleHistory { + unsigned char* data; + unsigned int length, samples; + unsigned char depth; + int rate, centerRate, loopStart; + bool hasSample; + DivSampleHistory(void* d, unsigned int l, unsigned int s, unsigned char de, int r, int cr, int ls): + data((unsigned char*)d), + length(l), + samples(s), + depth(de), + rate(r), + centerRate(cr), + loopStart(ls), + hasSample(true) {} + DivSampleHistory(unsigned char de, int r, int cr, int ls): + data(NULL), + length(0), + samples(0), + depth(de), + rate(r), + centerRate(cr), + loopStart(ls), + hasSample(false) {} + ~DivSampleHistory(); +}; + struct DivSample { String name; int rate, centerRate, loopStart, loopOffP; @@ -62,6 +90,9 @@ struct DivSample { unsigned int samples; + std::deque undoHist; + std::deque redoHist; + /** * @warning DO NOT USE - internal functions */ @@ -154,6 +185,27 @@ struct DivSample { * @return the sample data length. */ unsigned int getCurBufLen(); + + /** + * prepare an undo step for this sample. + * @param data whether to include sample data. + * @return the undo step. + */ + DivSampleHistory* prepareUndo(bool data); + + /** + * undo. you may need to call DivEngine::renderSamples afterwards. + * @warning do not attempt to undo outside of a synchronized block! + * @return 0 on failure; 1 on success and 2 on success (data changed). + */ + int undo(); + + /** + * redo. you may need to call DivEngine::renderSamples afterwards. + * @warning do not attempt to redo outside of a synchronized block! + * @return 0 on failure; 1 on success and 2 on success (data changed). + */ + int redo(); DivSample(): name(""), rate(32000), From f45273c89ccafc908d40510e69743e6a40c5c3d2 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Wed, 23 Mar 2022 00:04:47 -0500 Subject: [PATCH 7/9] OPL: better 4-op channel naming --- src/engine/sysDef.cpp | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/src/engine/sysDef.cpp b/src/engine/sysDef.cpp index 95e1a7b05..bcfbf1c63 100644 --- a/src/engine/sysDef.cpp +++ b/src/engine/sysDef.cpp @@ -924,15 +924,15 @@ const char* chanNames[40][32]={ {"Pulse 1", "Pulse 2", "PCM"}, // MMC5 {"FM 1", "FM 2", "FM 3", "PSG 1", "PSG 2", "PSG 3"}, // OPN {"FM 1", "FM 2", "FM 3", "FM 4", "FM 5", "FM 6", "Square 1", "Square 2", "Square 3", "Kick", "Snare", "Top", "HiHat", "Tom", "Rim", "ADPCM"}, // PC-98 - {"FM 1", "FM 2", "FM 3", "FM 4", "FM 5", "FM 6", "FM 7", "FM 8", "FM 9", "FM 10", "FM 11", "FM 12", "FM 13", "FM 14", "FM 15", "FM 16", "FM 17", "FM 18"}, // OPL3 + {"4OP 1", "FM 2", "4OP 3", "FM 4", "4OP 5", "FM 6", "4OP 7", "FM 8", "4OP 9", "FM 10", "4OP 11", "FM 12", "FM 13", "FM 14", "FM 15", "FM 16", "FM 17", "FM 18"}, // OPL3 {"Channel 1", "Channel 2", "Channel 3", "Channel 4", "Channel 5", "Channel 6", "Channel 7", "Channel 8", "Channel 9", "Channel 10", "Channel 11", "Channel 12", "Channel 13", "Channel 14", "Channel 15", "Channel 16", "Channel 17", "Channel 18", "Channel 19", "Channel 20", "Channel 21", "Channel 22", "Channel 23", "Channel 24", "Channel 25", "Channel 26", "Channel 27", "Channel 28"}, // MultiPCM {"Square"}, // PC Speaker/Pokémon Mini {"Channel 1", "Channel 2", "Channel 3", "Channel 4", "Channel 5", "Noise"}, // Virtual Boy/SCC {"FM 1", "FM 2", "FM 3", "FM 4", "FM 5", "FM 6", "PSG 1", "PSG 2", "PSG 3", "ADPCM-A 1", "ADPCM-A 2", "ADPCM-A 3", "ADPCM-A 4", "ADPCM-A 5", "ADPCM-A 6", "ADPCM-B"}, // YM2610B {"FM 1", "FM 2", "FM 3", "FM 4", "FM 5", "FM 6", "Kick", "Snare", "Tom", "Top", "HiHat"}, // OPLL/OPL/OPL2 drums - {"FM 1", "FM 2", "FM 3", "FM 4", "FM 5", "FM 6", "FM 7", "FM 8", "FM 9", "FM 10", "FM 11", "FM 12", "FM 13", "FM 14", "FM 15", "Kick", "Snare", "Tom", "Top", "HiHat"}, // OPL3 drums - {"FM 1", "FM 2", "FM 3", "FM 4", "FM 5", "FM 6", "4OP 1", "4OP 2", "4OP 3", "4OP 4", "4OP 5", "4OP 6"}, // OPL3 4-op - {"FM 1", "FM 2", "FM 3", "4OP 1", "4OP 2", "4OP 3", "4OP 4", "4OP 5", "4OP 6", "Kick", "Snare", "Tom", "Top", "HiHat"}, // OPL3 4-op + drums + {"4OP 1", "FM 2", "4OP 3", "FM 4", "4OP 5", "FM 6", "4OP 7", "FM 8", "4OP 9", "FM 10", "4OP 11", "FM 12", "FM 13", "FM 14", "FM 15", "Kick", "Snare", "Tom", "Top", "HiHat"}, // OPL3 drums + {"FM 1", "FM 2", "FM 3", "FM 4", "FM 5", "FM 6", "4OP 1", "4OP 2", "4OP 3", "4OP 4", "4OP 5", "4OP 6"}, // OPL3 4-op (UNUSED) + {"FM 1", "FM 2", "FM 3", "4OP 1", "4OP 2", "4OP 3", "4OP 4", "4OP 5", "4OP 6", "Kick", "Snare", "Tom", "Top", "HiHat"}, // OPL3 4-op + drums (UNUSED) {"PCM 1", "PCM 2", "PCM 3", "PCM 4", "PCM 5", "PCM 6", "PCM 7", "PCM 8", "PCM 9", "PCM 10", "PCM 11", "PCM 12", "PCM 13", "PCM 14", "PCM 15", "PCM 16", "ADPCM 1", "ADPCM 2", "ADPCM 3"}, // QSound {"FM 1", "FM 2", "FM 3 OP1", "FM 3 OP2", "FM 3 OP3", "FM 3 OP4", "FM 4", "FM 5", "FM 6", "PSG 1", "PSG 2", "PSG 3", "ADPCM-A 1", "ADPCM-A 2", "ADPCM-A 3", "ADPCM-A 4", "ADPCM-A 5", "ADPCM-A 6", "ADPCM-B"}, // YM2610B (extended channel 3) {"Wave", "Wave/PCM", "Wave", "Wave/Noise"}, // Swan @@ -974,8 +974,8 @@ const char* chanShortNames[38][32]={ {"F1", "F2", "F3", "F4", "F5", "F6", "S1", "S2", "S3", "P1", "P2", "P3", "P4", "P5", "P6", "B"}, // YM2610B {"F1", "F2", "F3", "F4", "F5", "F6", "BD", "SD", "TM", "TP", "HH"}, // OPLL/OPL/OPL2 drums {"1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12", "13", "14", "15", "BD", "SD", "TM", "TP", "HH"}, // OPL3 drums - {"F1", "F2", "F3", "F4", "F5", "F6", "Q1", "Q2", "Q3", "Q4", "Q5", "Q6"}, // OPL3 4-op - {"F1", "F2", "F3", "Q1", "Q2", "Q3", "Q4", "Q5", "Q6", "BD", "SD", "TM", "TP", "HH"}, // OPL3 4-op + drums + {"F1", "F2", "F3", "F4", "F5", "F6", "Q1", "Q2", "Q3", "Q4", "Q5", "Q6"}, // OPL3 4-op (UNUSED) + {"F1", "F2", "F3", "Q1", "Q2", "Q3", "Q4", "Q5", "Q6", "BD", "SD", "TM", "TP", "HH"}, // OPL3 4-op + drums (UNUSED) {"1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12", "13", "14", "15", "16", "A1", "A2", "A3"}, // QSound {"F1", "F2", "O1", "O2", "O3", "O4", "F4", "F5", "F6", "S1", "S2", "S3", "P1", "P2", "P3", "P4", "P5", "P6", "B"}, // YM2610B (extended channel 3) }; @@ -1008,15 +1008,15 @@ const int chanTypes[41][32]={ {1, 1, 4}, // MMC5 {0, 0, 0, 1, 1, 1}, // OPN {0, 0, 0, 0, 0, 0, 1, 1, 1, 2, 2, 2, 2, 2, 2, 4}, // PC-98 - {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, // OPL3 + {5, 0, 5, 0, 5, 0, 5, 0, 5, 0, 5, 0, 0, 0, 0, 0, 0, 0}, // OPL3 {4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4}, // MultiPCM/QSound {1}, // PC Speaker/Pokémon Mini {3, 3, 3, 3, 3, 2}, // Virtual Boy/SCC {0, 0, 0, 0, 0, 0, 1, 1, 1, 4, 4, 4, 4, 4, 4, 4}, // YM2610B {0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 2}, // OPLL/OPL/OPL2 drums - {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 2}, // OPL3 drums - {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, // OPL3 4-op - {0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 2}, // OPL3 4-op + drums + {5, 0, 5, 0, 5, 0, 5, 0, 5, 0, 5, 0, 0, 0, 0, 2, 2, 2, 2, 2}, // OPL3 drums + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, // OPL3 4-op (UNUSED) + {0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 2}, // OPL3 4-op + drums (UNUSED) {3, 3, 3, 3}, // Lynx {0, 0, 5, 5, 5, 5, 0, 0, 0, 1, 1, 1, 4, 4, 4, 4, 4, 4, 4}, // YM2610B (extended channel 3) {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 4}, // VERA From afc701b0b9c1cbe43fdeaf6efab124b09fae0f36 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Wed, 23 Mar 2022 00:42:59 -0500 Subject: [PATCH 8/9] GUI: sample edit undo/redo! it seems to work but if you find bugs/crashes tell me --- src/engine/sample.cpp | 68 ++++++++++++++++++++++++++++++++++-------- src/engine/sample.h | 3 +- src/gui/doAction.cpp | 26 ++++++++++++++-- src/gui/gui.h | 3 ++ src/gui/sampleEdit.cpp | 34 +++++++++++++++++++-- 5 files changed, 116 insertions(+), 18 deletions(-) diff --git a/src/engine/sample.cpp b/src/engine/sample.cpp index d5e1844c8..5fab8b2b7 100644 --- a/src/engine/sample.cpp +++ b/src/engine/sample.cpp @@ -790,7 +790,7 @@ unsigned int DivSample::getCurBufLen() { return 0; } -DivSampleHistory* DivSample::prepareUndo(bool data) { +DivSampleHistory* DivSample::prepareUndo(bool data, bool doNotPush) { DivSampleHistory* h; if (data) { unsigned char* duplicate; @@ -804,33 +804,77 @@ DivSampleHistory* DivSample::prepareUndo(bool data) { } else { h=new DivSampleHistory(depth,rate,centerRate,loopStart); } - while (!redoHist.empty()) { - DivSampleHistory* h=redoHist.front(); - delete h; - redoHist.pop_front(); + if (!doNotPush) { + while (!redoHist.empty()) { + DivSampleHistory* h=redoHist.back(); + delete h; + redoHist.pop_back(); + } + if (undoHist.size()>100) undoHist.pop_front(); + undoHist.push_back(h); } - undoHist.push_back(h); return h; } +#define applyHistory \ + depth=h->depth; \ + if (h->hasSample) { \ + initInternal(h->depth,h->samples); \ + samples=h->samples; \ +\ + if (h->length!=getCurBufLen()) logW("undo buffer length not equal to current buffer length! %d != %d\n",h->length,getCurBufLen()); \ +\ + void* buf=getCurBuf(); \ +\ + if (buf!=NULL && h->data!=NULL) { \ + memcpy(buf,h->data,h->length); \ + } \ + } \ + rate=h->rate; \ + centerRate=h->centerRate; \ + loopStart=h->loopStart; + + int DivSample::undo() { - return 0; + if (undoHist.empty()) return 0; + DivSampleHistory* h=undoHist.back(); + DivSampleHistory* redo=prepareUndo(h->hasSample,true); + + int ret=h->hasSample?2:1; + + applyHistory; + + redoHist.push_back(redo); + delete h; + undoHist.pop_back(); + return ret; } int DivSample::redo() { - return 0; + if (redoHist.empty()) return 0; + DivSampleHistory* h=redoHist.back(); + DivSampleHistory* undo=prepareUndo(h->hasSample,true); + + int ret=h->hasSample?2:1; + + applyHistory; + + undoHist.push_back(undo); + delete h; + redoHist.pop_back(); + return ret; } DivSample::~DivSample() { while (!undoHist.empty()) { - DivSampleHistory* h=undoHist.front(); + DivSampleHistory* h=undoHist.back(); delete h; - undoHist.pop_front(); + undoHist.pop_back(); } while (!redoHist.empty()) { - DivSampleHistory* h=redoHist.front(); + DivSampleHistory* h=redoHist.back(); delete h; - redoHist.pop_front(); + redoHist.pop_back(); } if (data8) delete[] data8; if (data16) delete[] data16; diff --git a/src/engine/sample.h b/src/engine/sample.h index f07c7ef91..1a9e53d32 100644 --- a/src/engine/sample.h +++ b/src/engine/sample.h @@ -189,9 +189,10 @@ struct DivSample { /** * prepare an undo step for this sample. * @param data whether to include sample data. + * @param doNotPush if this is true, don't push the DivSampleHistory to the undo history. * @return the undo step. */ - DivSampleHistory* prepareUndo(bool data); + DivSampleHistory* prepareUndo(bool data, bool doNotPush=false); /** * undo. you may need to call DivEngine::renderSamples afterwards. diff --git a/src/gui/doAction.cpp b/src/gui/doAction.cpp index 66a05fb3a..3866624ac 100644 --- a/src/gui/doAction.cpp +++ b/src/gui/doAction.cpp @@ -55,10 +55,18 @@ void FurnaceGUI::doAction(int what) { openFileDialog(GUI_FILE_SAVE); break; case GUI_ACTION_UNDO: - doUndo(); + if (curWindow==GUI_WINDOW_SAMPLE_EDIT) { + doUndoSample(); + } else { + doUndo(); + } break; case GUI_ACTION_REDO: - doRedo(); + if (curWindow==GUI_WINDOW_SAMPLE_EDIT) { + doRedoSample(); + } else { + doRedo(); + } break; case GUI_ACTION_PLAY_TOGGLE: if (e->isPlaying() && !e->isStepping()) { @@ -594,6 +602,8 @@ void FurnaceGUI::doAction(int what) { if (end-start<1) break; + sample->prepareUndo(true); + if (sampleClipboard!=NULL) { delete[] sampleClipboard; } @@ -632,6 +642,7 @@ void FurnaceGUI::doAction(int what) { if (curSample<0 || curSample>=(int)e->song.sample.size()) break; if (sampleClipboard==NULL || sampleClipboardLen<1) break; DivSample* sample=e->song.sample[curSample]; + sample->prepareUndo(true); int pos=(sampleSelStart==-1 || sampleSelStart==sampleSelEnd)?sample->samples:sampleSelStart; e->lockEngine([this,sample,pos]() { @@ -658,6 +669,7 @@ void FurnaceGUI::doAction(int what) { if (curSample<0 || curSample>=(int)e->song.sample.size()) break; if (sampleClipboard==NULL || sampleClipboardLen<1) break; DivSample* sample=e->song.sample[curSample]; + sample->prepareUndo(true); int pos=(sampleSelStart==-1 || sampleSelStart==sampleSelEnd)?0:sampleSelStart; e->lockEngine([this,sample,pos]() { @@ -685,6 +697,7 @@ void FurnaceGUI::doAction(int what) { if (curSample<0 || curSample>=(int)e->song.sample.size()) break; if (sampleClipboard==NULL || sampleClipboardLen<1) break; DivSample* sample=e->song.sample[curSample]; + sample->prepareUndo(true); int pos=(sampleSelStart==-1 || sampleSelStart==sampleSelEnd)?0:sampleSelStart; e->lockEngine([this,sample,pos]() { @@ -737,6 +750,7 @@ void FurnaceGUI::doAction(int what) { case GUI_ACTION_SAMPLE_NORMALIZE: { if (curSample<0 || curSample>=(int)e->song.sample.size()) break; DivSample* sample=e->song.sample[curSample]; + sample->prepareUndo(true); e->lockEngine([this,sample]() { SAMPLE_OP_BEGIN; float maxVal=0.0f; @@ -783,6 +797,7 @@ void FurnaceGUI::doAction(int what) { case GUI_ACTION_SAMPLE_FADE_IN: { if (curSample<0 || curSample>=(int)e->song.sample.size()) break; DivSample* sample=e->song.sample[curSample]; + sample->prepareUndo(true); e->lockEngine([this,sample]() { SAMPLE_OP_BEGIN; @@ -812,6 +827,7 @@ void FurnaceGUI::doAction(int what) { case GUI_ACTION_SAMPLE_FADE_OUT: { if (curSample<0 || curSample>=(int)e->song.sample.size()) break; DivSample* sample=e->song.sample[curSample]; + sample->prepareUndo(true); e->lockEngine([this,sample]() { SAMPLE_OP_BEGIN; @@ -845,6 +861,7 @@ void FurnaceGUI::doAction(int what) { case GUI_ACTION_SAMPLE_SILENCE: { if (curSample<0 || curSample>=(int)e->song.sample.size()) break; DivSample* sample=e->song.sample[curSample]; + sample->prepareUndo(true); e->lockEngine([this,sample]() { SAMPLE_OP_BEGIN; @@ -868,6 +885,7 @@ void FurnaceGUI::doAction(int what) { case GUI_ACTION_SAMPLE_DELETE: { if (curSample<0 || curSample>=(int)e->song.sample.size()) break; DivSample* sample=e->song.sample[curSample]; + sample->prepareUndo(true); e->lockEngine([this,sample]() { SAMPLE_OP_BEGIN; @@ -884,6 +902,7 @@ void FurnaceGUI::doAction(int what) { case GUI_ACTION_SAMPLE_TRIM: { if (curSample<0 || curSample>=(int)e->song.sample.size()) break; DivSample* sample=e->song.sample[curSample]; + sample->prepareUndo(true); e->lockEngine([this,sample]() { SAMPLE_OP_BEGIN; @@ -900,6 +919,7 @@ void FurnaceGUI::doAction(int what) { case GUI_ACTION_SAMPLE_REVERSE: { if (curSample<0 || curSample>=(int)e->song.sample.size()) break; DivSample* sample=e->song.sample[curSample]; + sample->prepareUndo(true); e->lockEngine([this,sample]() { SAMPLE_OP_BEGIN; @@ -931,6 +951,7 @@ void FurnaceGUI::doAction(int what) { case GUI_ACTION_SAMPLE_INVERT: { if (curSample<0 || curSample>=(int)e->song.sample.size()) break; DivSample* sample=e->song.sample[curSample]; + sample->prepareUndo(true); e->lockEngine([this,sample]() { SAMPLE_OP_BEGIN; @@ -956,6 +977,7 @@ void FurnaceGUI::doAction(int what) { case GUI_ACTION_SAMPLE_SIGN: { if (curSample<0 || curSample>=(int)e->song.sample.size()) break; DivSample* sample=e->song.sample[curSample]; + sample->prepareUndo(true); e->lockEngine([this,sample]() { SAMPLE_OP_BEGIN; diff --git a/src/gui/gui.h b/src/gui/gui.h index a4feed9b5..315ce7421 100644 --- a/src/gui/gui.h +++ b/src/gui/gui.h @@ -897,6 +897,9 @@ class FurnaceGUI { void doRedo(); void editOptions(bool topMenu); + void doUndoSample(); + void doRedoSample(); + void play(int row=0); void stop(); diff --git a/src/gui/sampleEdit.cpp b/src/gui/sampleEdit.cpp index 3bdfa6c9f..0998856cc 100644 --- a/src/gui/sampleEdit.cpp +++ b/src/gui/sampleEdit.cpp @@ -63,14 +63,12 @@ void FurnaceGUI::drawSampleEdit() { for (int i=0; i<17; i++) { if (sampleDepths[i]==NULL) continue; if (ImGui::Selectable(sampleDepths[i])) { + sample->prepareUndo(true); sample->depth=i; e->renderSamplesP(); updateSampleTex=true; MARK_MODIFIED; } - if (ImGui::IsItemHovered()) { - ImGui::SetTooltip("no undo for sample type change operations!"); - } } ImGui::EndCombo(); } @@ -163,6 +161,7 @@ void FurnaceGUI::drawSampleEdit() { if (resizeSize>16777215) resizeSize=16777215; } if (ImGui::Button("Resize")) { + sample->prepareUndo(true); e->lockEngine([this,sample]() { if (!sample->resize(resizeSize)) { showError("couldn't resize! make sure your sample is 8 or 16-bit."); @@ -217,6 +216,7 @@ void FurnaceGUI::drawSampleEdit() { } ImGui::Combo("Filter",&resampleStrat,resampleStrats,6); if (ImGui::Button("Resample")) { + sample->prepareUndo(true); e->lockEngine([this,sample]() { if (!sample->resample(resampleTarget,resampleStrat)) { showError("couldn't resample! make sure your sample is 8 or 16-bit."); @@ -253,6 +253,7 @@ void FurnaceGUI::drawSampleEdit() { ImGui::SameLine(); ImGui::Text("(%.1fdB)",20.0*log10(amplifyVol/100.0f)); if (ImGui::Button("Apply")) { + sample->prepareUndo(true); e->lockEngine([this,sample]() { SAMPLE_OP_BEGIN; float vol=amplifyVol/100.0f; @@ -319,6 +320,7 @@ void FurnaceGUI::drawSampleEdit() { } if (ImGui::Button("Resize")) { int pos=(sampleSelStart==-1 || sampleSelStart==sampleSelEnd)?sample->samples:sampleSelStart; + sample->prepareUndo(true); e->lockEngine([this,sample,pos]() { if (!sample->insert(pos,silenceSize)) { showError("couldn't insert! make sure your sample is 8 or 16-bit."); @@ -437,6 +439,7 @@ void FurnaceGUI::drawSampleEdit() { } if (ImGui::Button("Apply")) { + sample->prepareUndo(true); e->lockEngine([this,sample]() { SAMPLE_OP_BEGIN; float res=1.0-pow(sampleFilterRes,0.5f); @@ -665,6 +668,7 @@ void FurnaceGUI::drawSampleEdit() { sampleDragActive=true; sampleSelStart=-1; sampleSelEnd=-1; + if (sampleDragMode) sample->prepareUndo(true); processDrags(ImGui::GetMousePos().x,ImGui::GetMousePos().y); } } @@ -827,3 +831,27 @@ void FurnaceGUI::drawSampleEdit() { if (ImGui::IsWindowFocused(ImGuiFocusedFlags_ChildWindows)) curWindow=GUI_WINDOW_SAMPLE_EDIT; ImGui::End(); } + +void FurnaceGUI::doUndoSample() { + if (!sampleEditOpen) return; + if (curSample<0 || curSample>=(int)e->song.sample.size()) return; + DivSample* sample=e->song.sample[curSample]; + e->lockEngine([this,sample]() { + if (sample->undo()==2) { + e->renderSamples(); + updateSampleTex=true; + } + }); +} + +void FurnaceGUI::doRedoSample() { + if (!sampleEditOpen) return; + if (curSample<0 || curSample>=(int)e->song.sample.size()) return; + DivSample* sample=e->song.sample[curSample]; + e->lockEngine([this,sample]() { + if (sample->redo()==2) { + e->renderSamples(); + updateSampleTex=true; + } + }); +} \ No newline at end of file From ac79e7d6af862b511623f8640d08f51eaa80d1c6 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Wed, 23 Mar 2022 01:35:57 -0500 Subject: [PATCH 9/9] add broken speed alternation flag - dev70 --- papers/format.md | 4 ++++ src/engine/engine.h | 4 ++-- src/engine/fileOps.cpp | 15 +++++++++++++++ src/engine/playback.cpp | 22 ++++++++++++++++------ src/engine/song.h | 4 +++- src/gui/compatFlags.cpp | 4 ++++ 6 files changed, 44 insertions(+), 9 deletions(-) diff --git a/papers/format.md b/papers/format.md index 51dc8dd93..af2c4a5c3 100644 --- a/papers/format.md +++ b/papers/format.md @@ -29,6 +29,7 @@ furthermore, an `or reserved` indicates this field is always present, but is res the format versions are: +- 70: Furnace dev70 - 69: Furnace dev69 - 68: Furnace dev68 - 67: Furnace dev67 @@ -235,6 +236,9 @@ size | description STR | song comment 4f | master volume, 1.0f=100% (>=59) | this is 2.0f for modules before 59 + --- | **extended compatibility flags** (>=70) + 1 | broken speed selection + 31 | reserved ``` # instrument diff --git a/src/engine/engine.h b/src/engine/engine.h index 9f7464e37..911a33c7c 100644 --- a/src/engine/engine.h +++ b/src/engine/engine.h @@ -38,8 +38,8 @@ warnings+=(String("\n")+x); \ } -#define DIV_VERSION "dev69" -#define DIV_ENGINE_VERSION 69 +#define DIV_VERSION "dev70" +#define DIV_ENGINE_VERSION 70 // for imports #define DIV_VERSION_MOD 0xff01 diff --git a/src/engine/fileOps.cpp b/src/engine/fileOps.cpp index b50eb74ae..f267e2c23 100644 --- a/src/engine/fileOps.cpp +++ b/src/engine/fileOps.cpp @@ -144,6 +144,7 @@ bool DivEngine::loadDMF(unsigned char* file, size_t len) { ds.oneTickCut=false; ds.newInsTriggersInPorta=true; ds.arp0Reset=true; + ds.brokenSpeedSel=true; // 1.1 compat flags if (ds.version>24) { @@ -1067,6 +1068,14 @@ bool DivEngine::loadFur(unsigned char* file, size_t len) { ds.masterVol=2.0f; } + if (ds.version>=70) { + // extended compat flags + ds.brokenSpeedSel=reader.readC(); + for (int i=0; i<31; i++) { + reader.readC(); + } + } + // read instruments for (int i=0; iwriteF(song.masterVol); + // extended compat flags + w->writeC(song.brokenSpeedSel); + for (int i=0; i<31; i++) { + w->writeC(0); + } + /// INSTRUMENT for (int i=0; i ins; @@ -373,7 +374,8 @@ struct DivSong { brokenDACMode(false), oneTickCut(false), newInsTriggersInPorta(true), - arp0Reset(true) { + arp0Reset(true), + brokenSpeedSel(false) { for (int i=0; i<32; i++) { system[i]=DIV_SYSTEM_NULL; systemVol[i]=64; diff --git a/src/gui/compatFlags.cpp b/src/gui/compatFlags.cpp index 46424fc52..b49c251f8 100644 --- a/src/gui/compatFlags.cpp +++ b/src/gui/compatFlags.cpp @@ -81,6 +81,10 @@ void FurnaceGUI::drawCompatFlags() { 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::Checkbox("Broken speed alternation",&e->song.brokenSpeedSel); + if (ImGui::IsItemHovered()) { + ImGui::SetTooltip("determines next speed based on whether the row is odd/even instead of alternating between speeds."); + } ImGui::Text("Loop modality:"); if (ImGui::RadioButton("Reset channels",e->song.loopModality==0)) {