From 705ba4273bbcd2148460e4a021dd2dd6e2b57ebb Mon Sep 17 00:00:00 2001 From: tildearrow Date: Tue, 22 Mar 2022 04:54:01 -0500 Subject: [PATCH] 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 66e54d857..33532d43d 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 7e4d76bec..c697a9951 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 4e9196afc..752a54423 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 903f6edf0..51dad892e 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 3db50d06e..f32e6eba2 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 58539ab10..aa661b3a4 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 b33c1e802..28e0f3d59 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 000000000..981a56046 --- /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 b89ed593f..9a1a9eee2 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