diff --git a/src/gui/doAction.cpp b/src/gui/doAction.cpp index 0d1bcf623a..4db68d93b0 100644 --- a/src/gui/doAction.cpp +++ b/src/gui/doAction.cpp @@ -1336,6 +1336,10 @@ void FurnaceGUI::doAction(int what) { MARK_MODIFIED; break; } + case GUI_ACTION_SAMPLE_CROSSFADE_LOOP: + if (curSample<0 || curSample>=(int)e->song.sample.size()) break; + openSampleCrossFadeOpt=true; + break; case GUI_ACTION_SAMPLE_FILTER: if (curSample<0 || curSample>=(int)e->song.sample.size()) break; openSampleFilterOpt=true; diff --git a/src/gui/gui.cpp b/src/gui/gui.cpp index 86cf09a86a..bf35f058e5 100644 --- a/src/gui/gui.cpp +++ b/src/gui/gui.cpp @@ -7570,6 +7570,8 @@ FurnaceGUI::FurnaceGUI(): sampleFilterRes(0.25f), sampleFilterCutStart(16000.0f), sampleFilterCutEnd(100.0f), + sampleCrossFadeLoopLength(0), + sampleCrossFadeLoopLaw(50), sampleFilterPower(1), sampleClipboard(NULL), sampleClipboardLen(0), @@ -7578,6 +7580,7 @@ FurnaceGUI::FurnaceGUI(): openSampleAmplifyOpt(false), openSampleSilenceOpt(false), openSampleFilterOpt(false), + openSampleCrossFadeOpt(false), selectedPortSet(0x1fff), selectedSubPort(-1), hoveredPortSet(0x1fff), diff --git a/src/gui/gui.h b/src/gui/gui.h index 05e6896631..645495a2d6 100644 --- a/src/gui/gui.h +++ b/src/gui/gui.h @@ -723,6 +723,7 @@ enum FurnaceGUIActions { GUI_ACTION_SAMPLE_INVERT, GUI_ACTION_SAMPLE_SIGN, GUI_ACTION_SAMPLE_FILTER, + GUI_ACTION_SAMPLE_CROSSFADE_LOOP, GUI_ACTION_SAMPLE_PREVIEW, GUI_ACTION_SAMPLE_STOP_PREVIEW, GUI_ACTION_SAMPLE_ZOOM_IN, @@ -2104,10 +2105,11 @@ class FurnaceGUI { ImVec2 sampleDragAreaSize; unsigned int sampleDragLen; float sampleFilterL, sampleFilterB, sampleFilterH, sampleFilterRes, sampleFilterCutStart, sampleFilterCutEnd; + int sampleCrossFadeLoopLength, sampleCrossFadeLoopLaw; unsigned char sampleFilterPower; short* sampleClipboard; size_t sampleClipboardLen; - bool openSampleResizeOpt, openSampleResampleOpt, openSampleAmplifyOpt, openSampleSilenceOpt, openSampleFilterOpt; + bool openSampleResizeOpt, openSampleResampleOpt, openSampleAmplifyOpt, openSampleSilenceOpt, openSampleFilterOpt, openSampleCrossFadeOpt; // mixer // 0xxx: output diff --git a/src/gui/guiConst.cpp b/src/gui/guiConst.cpp index 0411d8a3cf..75decd374e 100644 --- a/src/gui/guiConst.cpp +++ b/src/gui/guiConst.cpp @@ -743,6 +743,7 @@ const FurnaceGUIActionDef guiActions[GUI_ACTION_MAX]={ D("SAMPLE_INVERT", "Invert", FURKMOD_CMD|FURKMOD_SHIFT|SDLK_t), D("SAMPLE_SIGN", "Signed/unsigned exchange", FURKMOD_CMD|SDLK_u), D("SAMPLE_FILTER", "Apply filter", FURKMOD_CMD|SDLK_f), + D("SAMPLE_CROSSFADE_LOOP", "Crossfade loop points", NOT_AN_ACTION), D("SAMPLE_PREVIEW", "Preview sample", 0), D("SAMPLE_STOP_PREVIEW", "Stop sample preview", 0), D("SAMPLE_ZOOM_IN", "Zoom in", FURKMOD_CMD|SDLK_EQUALS), diff --git a/src/gui/sampleEdit.cpp b/src/gui/sampleEdit.cpp index 5a57483ddf..5c070eebc0 100644 --- a/src/gui/sampleEdit.cpp +++ b/src/gui/sampleEdit.cpp @@ -1226,6 +1226,74 @@ void FurnaceGUI::drawSampleEdit() { ImGui::SameLine(); ImGui::Dummy(ImVec2(4.0*dpiScale,dpiScale)); sameLineMaybe(); + ImGui::Button(ICON_FUR_CROSSFADE "##CrossFade"); + if (ImGui::IsItemHovered()) { + ImGui::SetTooltip("Crossfade loop points"); + } + if (openSampleCrossFadeOpt) { + openSampleCrossFadeOpt=false; + ImGui::OpenPopup("SCrossFadeOpt"); + } + if (ImGui::BeginPopupContextItem("SCrossFadeOpt",ImGuiPopupFlags_MouseButtonLeft)) { + if (sampleCrossFadeLoopLength>sample->loopStart) sampleCrossFadeLoopLength=sample->loopStart; + if (sampleCrossFadeLoopLength>(sample->loopEnd-sample->loopStart)) sampleCrossFadeLoopLength=sample->loopEnd-sample->loopStart; + if (ImGui::SliderInt("Number of samples",&sampleCrossFadeLoopLength,0,100000)) { + if (sampleCrossFadeLoopLength<0) sampleCrossFadeLoopLength=0; + if (sampleCrossFadeLoopLength>sample->loopStart) sampleCrossFadeLoopLength=sample->loopStart; + if (sampleCrossFadeLoopLength>(sample->loopEnd-sample->loopStart)) sampleCrossFadeLoopLength=sample->loopEnd-sample->loopStart; + if (sampleCrossFadeLoopLength>100000) sampleCrossFadeLoopLength=100000; + } + if (ImGui::SliderInt("Linear <-> Equal power",&sampleCrossFadeLoopLaw,0,100)) { + if (sampleCrossFadeLoopLaw<0) sampleCrossFadeLoopLaw=0; + if (sampleCrossFadeLoopLaw>100) sampleCrossFadeLoopLaw=100; + } + if (ImGui::Button("Apply")) { + if (sampleCrossFadeLoopLength>sample->loopStart) { + showError("Crossfade: length would go out of bounds. Aborted..."); + ImGui::CloseCurrentPopup(); + } else if (sampleCrossFadeLoopLength>(sample->loopEnd-sample->loopStart)) { + showError("Crossfade: length would overflow loopStart. Try a smaller random value."); + ImGui::CloseCurrentPopup(); + } else { + sample->prepareUndo(true); + e->lockEngine([this,sample] { + SAMPLE_OP_BEGIN; + double l=1.0/(double)sampleCrossFadeLoopLength; + double evar=1.0-sampleCrossFadeLoopLaw/200.0; + if (sample->depth==DIV_SAMPLE_DEPTH_8BIT) { + unsigned int crossFadeInput=sample->loopStart-sampleCrossFadeLoopLength; + unsigned int crossFadeOutput=sample->loopEnd-sampleCrossFadeLoopLength; + for (int i=0; idata8[crossFadeInput])*f1+((double)sample->data8[crossFadeOutput])*f2); + sample->data8[crossFadeOutput]=out; + crossFadeInput++; + crossFadeOutput++; + } + } else if (sample->depth==DIV_SAMPLE_DEPTH_16BIT) { + unsigned int crossFadeInput=sample->loopStart-sampleCrossFadeLoopLength; + unsigned int crossFadeOutput=sample->loopEnd-sampleCrossFadeLoopLength; + for (int i=0; idata16[crossFadeInput])*f1+((double)sample->data16[crossFadeOutput])*f2); + sample->data16[crossFadeOutput]=out; + crossFadeInput++; + crossFadeOutput++; + } + } + updateSampleTex=true; + + e->renderSamples(curSample); + }); + MARK_MODIFIED; + ImGui::CloseCurrentPopup(); + } + } + ImGui::EndPopup(); + } + ImGui::SameLine(); if (ImGui::Button(ICON_FA_PLAY "##PreviewSample")) { e->previewSample(curSample); } diff --git a/src/icon/furIcons.h b/src/icon/furIcons.h index 718e6822be..5d5cec4cbf 100644 --- a/src/icon/furIcons.h +++ b/src/icon/furIcons.h @@ -1,7 +1,7 @@ // not auto-generated. update every time you change icons.ttf! #define ICON_MIN_FUR 0xe0f0 -#define ICON_MAX_FUR 0xe158 +#define ICON_MAX_FUR 0xe159 // test #define ICON_FUR_TEST0 u8"\ue0f0" @@ -105,4 +105,5 @@ #define ICON_FUR_INC_LINEAR u8"\ue14b" #define ICON_FUR_INC_BENT u8"\ue14c" #define ICON_FUR_VOL_DIRECT u8"\ue14d" -#define ICON_FUR_ADSR u8"\ue14e" \ No newline at end of file +#define ICON_FUR_ADSR u8"\ue14e" +#define ICON_FUR_CROSSFADE u8"\ue159"