Add loop point crossfading in the sample editor.
This commit is contained in:
parent
d7ca3469a4
commit
4bd0dc2c63
|
@ -287,6 +287,7 @@ struct DivSample {
|
||||||
*/
|
*/
|
||||||
void convert(DivSampleDepth newDepth);
|
void convert(DivSampleDepth newDepth);
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* initialize the rest of sample formats for this sample.
|
* initialize the rest of sample formats for this sample.
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -1336,6 +1336,10 @@ void FurnaceGUI::doAction(int what) {
|
||||||
MARK_MODIFIED;
|
MARK_MODIFIED;
|
||||||
break;
|
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:
|
case GUI_ACTION_SAMPLE_FILTER:
|
||||||
if (curSample<0 || curSample>=(int)e->song.sample.size()) break;
|
if (curSample<0 || curSample>=(int)e->song.sample.size()) break;
|
||||||
openSampleFilterOpt=true;
|
openSampleFilterOpt=true;
|
||||||
|
|
|
@ -7529,6 +7529,8 @@ FurnaceGUI::FurnaceGUI():
|
||||||
sampleFilterRes(0.25f),
|
sampleFilterRes(0.25f),
|
||||||
sampleFilterCutStart(16000.0f),
|
sampleFilterCutStart(16000.0f),
|
||||||
sampleFilterCutEnd(100.0f),
|
sampleFilterCutEnd(100.0f),
|
||||||
|
sampleXFadeLoopLength(0),
|
||||||
|
sampleXFadeLoopLaw(50000),
|
||||||
sampleFilterPower(1),
|
sampleFilterPower(1),
|
||||||
sampleClipboard(NULL),
|
sampleClipboard(NULL),
|
||||||
sampleClipboardLen(0),
|
sampleClipboardLen(0),
|
||||||
|
@ -7537,6 +7539,7 @@ FurnaceGUI::FurnaceGUI():
|
||||||
openSampleAmplifyOpt(false),
|
openSampleAmplifyOpt(false),
|
||||||
openSampleSilenceOpt(false),
|
openSampleSilenceOpt(false),
|
||||||
openSampleFilterOpt(false),
|
openSampleFilterOpt(false),
|
||||||
|
openSampleXFadeOpt(false),
|
||||||
selectedPortSet(0x1fff),
|
selectedPortSet(0x1fff),
|
||||||
selectedSubPort(-1),
|
selectedSubPort(-1),
|
||||||
hoveredPortSet(0x1fff),
|
hoveredPortSet(0x1fff),
|
||||||
|
|
|
@ -722,6 +722,7 @@ enum FurnaceGUIActions {
|
||||||
GUI_ACTION_SAMPLE_INVERT,
|
GUI_ACTION_SAMPLE_INVERT,
|
||||||
GUI_ACTION_SAMPLE_SIGN,
|
GUI_ACTION_SAMPLE_SIGN,
|
||||||
GUI_ACTION_SAMPLE_FILTER,
|
GUI_ACTION_SAMPLE_FILTER,
|
||||||
|
GUI_ACTION_SAMPLE_XFADE_LOOP,
|
||||||
GUI_ACTION_SAMPLE_PREVIEW,
|
GUI_ACTION_SAMPLE_PREVIEW,
|
||||||
GUI_ACTION_SAMPLE_STOP_PREVIEW,
|
GUI_ACTION_SAMPLE_STOP_PREVIEW,
|
||||||
GUI_ACTION_SAMPLE_ZOOM_IN,
|
GUI_ACTION_SAMPLE_ZOOM_IN,
|
||||||
|
@ -2103,10 +2104,11 @@ class FurnaceGUI {
|
||||||
ImVec2 sampleDragAreaSize;
|
ImVec2 sampleDragAreaSize;
|
||||||
unsigned int sampleDragLen;
|
unsigned int sampleDragLen;
|
||||||
float sampleFilterL, sampleFilterB, sampleFilterH, sampleFilterRes, sampleFilterCutStart, sampleFilterCutEnd;
|
float sampleFilterL, sampleFilterB, sampleFilterH, sampleFilterRes, sampleFilterCutStart, sampleFilterCutEnd;
|
||||||
|
int sampleXFadeLoopLength, sampleXFadeLoopLaw;
|
||||||
unsigned char sampleFilterPower;
|
unsigned char sampleFilterPower;
|
||||||
short* sampleClipboard;
|
short* sampleClipboard;
|
||||||
size_t sampleClipboardLen;
|
size_t sampleClipboardLen;
|
||||||
bool openSampleResizeOpt, openSampleResampleOpt, openSampleAmplifyOpt, openSampleSilenceOpt, openSampleFilterOpt;
|
bool openSampleResizeOpt, openSampleResampleOpt, openSampleAmplifyOpt, openSampleSilenceOpt, openSampleFilterOpt, openSampleXFadeOpt;
|
||||||
|
|
||||||
// mixer
|
// mixer
|
||||||
// 0xxx: output
|
// 0xxx: output
|
||||||
|
|
|
@ -743,6 +743,7 @@ const FurnaceGUIActionDef guiActions[GUI_ACTION_MAX]={
|
||||||
D("SAMPLE_INVERT", "Invert", FURKMOD_CMD|FURKMOD_SHIFT|SDLK_t),
|
D("SAMPLE_INVERT", "Invert", FURKMOD_CMD|FURKMOD_SHIFT|SDLK_t),
|
||||||
D("SAMPLE_SIGN", "Signed/unsigned exchange", FURKMOD_CMD|SDLK_u),
|
D("SAMPLE_SIGN", "Signed/unsigned exchange", FURKMOD_CMD|SDLK_u),
|
||||||
D("SAMPLE_FILTER", "Apply filter", FURKMOD_CMD|SDLK_f),
|
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_PREVIEW", "Preview sample", 0),
|
||||||
D("SAMPLE_STOP_PREVIEW", "Stop sample preview", 0),
|
D("SAMPLE_STOP_PREVIEW", "Stop sample preview", 0),
|
||||||
D("SAMPLE_ZOOM_IN", "Zoom in", FURKMOD_CMD|SDLK_EQUALS),
|
D("SAMPLE_ZOOM_IN", "Zoom in", FURKMOD_CMD|SDLK_EQUALS),
|
||||||
|
|
|
@ -1226,6 +1226,63 @@ void FurnaceGUI::drawSampleEdit() {
|
||||||
ImGui::SameLine();
|
ImGui::SameLine();
|
||||||
ImGui::Dummy(ImVec2(4.0*dpiScale,dpiScale));
|
ImGui::Dummy(ImVec2(4.0*dpiScale,dpiScale));
|
||||||
sameLineMaybe();
|
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;i<sampleXFadeLoopLength;i++) {
|
||||||
|
double f1=std::pow(i*l,evar);
|
||||||
|
double f2=std::pow((sampleXFadeLoopLength-i)*l, evar);
|
||||||
|
signed char out=(signed char)(((double)sample->data8[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;i<sampleXFadeLoopLength;i++) {
|
||||||
|
double f1=std::pow(i*l,evar);
|
||||||
|
double f2=std::pow((sampleXFadeLoopLength-i)*l,evar);
|
||||||
|
short out = (short)(((double)sample->data16[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")) {
|
if (ImGui::Button(ICON_FA_PLAY "##PreviewSample")) {
|
||||||
e->previewSample(curSample);
|
e->previewSample(curSample);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue