diff --git a/src/engine/sample.h b/src/engine/sample.h index 253fe347c..118fe7776 100644 --- a/src/engine/sample.h +++ b/src/engine/sample.h @@ -287,6 +287,7 @@ struct DivSample { */ void convert(DivSampleDepth newDepth); + /** * initialize the rest of sample formats for this sample. */ diff --git a/src/gui/doAction.cpp b/src/gui/doAction.cpp index 0d1bcf623..afd6de957 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_XFADE_LOOP: + if (curSample<0 || curSample>=(int)e->song.sample.size()) break; + openSampleXFadeOpt=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 3f585047e..50808e689 100644 --- a/src/gui/gui.cpp +++ b/src/gui/gui.cpp @@ -7529,6 +7529,8 @@ FurnaceGUI::FurnaceGUI(): sampleFilterRes(0.25f), sampleFilterCutStart(16000.0f), sampleFilterCutEnd(100.0f), + sampleXFadeLoopLength(0), + sampleXFadeLoopLaw(50000), sampleFilterPower(1), sampleClipboard(NULL), sampleClipboardLen(0), @@ -7537,6 +7539,7 @@ FurnaceGUI::FurnaceGUI(): openSampleAmplifyOpt(false), openSampleSilenceOpt(false), openSampleFilterOpt(false), + openSampleXFadeOpt(false), selectedPortSet(0x1fff), selectedSubPort(-1), hoveredPortSet(0x1fff), diff --git a/src/gui/gui.h b/src/gui/gui.h index 67b83484c..686f9b966 100644 --- a/src/gui/gui.h +++ b/src/gui/gui.h @@ -722,6 +722,7 @@ enum FurnaceGUIActions { GUI_ACTION_SAMPLE_INVERT, GUI_ACTION_SAMPLE_SIGN, GUI_ACTION_SAMPLE_FILTER, + GUI_ACTION_SAMPLE_XFADE_LOOP, GUI_ACTION_SAMPLE_PREVIEW, GUI_ACTION_SAMPLE_STOP_PREVIEW, GUI_ACTION_SAMPLE_ZOOM_IN, @@ -2103,10 +2104,11 @@ class FurnaceGUI { ImVec2 sampleDragAreaSize; unsigned int sampleDragLen; float sampleFilterL, sampleFilterB, sampleFilterH, sampleFilterRes, sampleFilterCutStart, sampleFilterCutEnd; + int sampleXFadeLoopLength, sampleXFadeLoopLaw; unsigned char sampleFilterPower; short* sampleClipboard; size_t sampleClipboardLen; - bool openSampleResizeOpt, openSampleResampleOpt, openSampleAmplifyOpt, openSampleSilenceOpt, openSampleFilterOpt; + bool openSampleResizeOpt, openSampleResampleOpt, openSampleAmplifyOpt, openSampleSilenceOpt, openSampleFilterOpt, openSampleXFadeOpt; // mixer // 0xxx: output diff --git a/src/gui/guiConst.cpp b/src/gui/guiConst.cpp index 0411d8a3c..561dcffe3 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_XFADE_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 5a57483dd..373521e05 100644 --- a/src/gui/sampleEdit.cpp +++ b/src/gui/sampleEdit.cpp @@ -1226,6 +1226,63 @@ void FurnaceGUI::drawSampleEdit() { ImGui::SameLine(); ImGui::Dummy(ImVec2(4.0*dpiScale,dpiScale)); sameLineMaybe(); + ImGui::Button("Crossfade"); + if (ImGui::IsItemHovered()) { + ImGui::SetTooltip("Crossfade loop points."); + } + if (openSampleXFadeOpt) { + openSampleXFadeOpt=false; + ImGui::OpenPopup("SXFadeOpt"); + } + if (ImGui::BeginPopupContextItem("SXFadeÓpt",ImGuiPopupFlags_MouseButtonLeft)) { + if (ImGui::SliderInt("Number of samples", &sampleXFadeLoopLength, 0, 100000)) { + if (sampleXFadeLoopLength<0) sampleXFadeLoopLength=0; + if (sampleXFadeLoopLength>sample->loopStart) sampleXFadeLoopLength=sample->loopStart; + if (sampleXFadeLoopLength>100000) sampleXFadeLoopLength=100000; + } + if (ImGui::SliderInt("Linear <-> Equal power", &sampleXFadeLoopLaw, 0, 100000)) { + if (sampleXFadeLoopLaw<0) sampleXFadeLoopLaw=0; + if (sampleXFadeLoopLaw>100000) sampleXFadeLoopLaw=100000; + } + if (ImGui::Button("Apply")) { + sample->prepareUndo(true); + e->lockEngine([this,sample]{ + SAMPLE_OP_BEGIN; + double l=1.0/(double)sampleXFadeLoopLength; + double evar=1.0-sampleXFadeLoopLaw/200000.0; + if (sample->depth==DIV_SAMPLE_DEPTH_8BIT) { + unsigned int xfadeInput=sample->loopStart-sampleXFadeLoopLength; + unsigned int xfadeOutput=sample->loopEnd-sampleXFadeLoopLength; + for (int i=0;idata8[xfadeInput])*f1+((double)sample->data8[xfadeOutput])*f2); + sample->data8[xfadeOutput]=out; + xfadeInput++; + xfadeOutput++; + } + } else if (sample->depth==DIV_SAMPLE_DEPTH_16BIT) { + unsigned int xfadeInput=sample->loopStart-sampleXFadeLoopLength; + unsigned int xfadeOutput=sample->loopEnd-sampleXFadeLoopLength; + for (int i=0;idata16[xfadeInput])*f1+((double)sample->data16[xfadeOutput])*f2); + sample->data16[xfadeOutput]=out; + xfadeInput++; + xfadeOutput++; + } + } + updateSampleTex=true; + + e->renderSamples(curSample); + }); + MARK_MODIFIED; + ImGui::CloseCurrentPopup(); + } + ImGui::EndPopup(); + } + ImGui::SameLine(); if (ImGui::Button(ICON_FA_PLAY "##PreviewSample")) { e->previewSample(curSample); }