furnace/src/gui/sampleEdit.cpp

873 lines
30 KiB
C++
Raw Normal View History

/**
* 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.
*/
#include "gui.h"
#include <imgui.h>
#include <imgui_internal.h>
2022-03-17 23:30:28 +00:00
#include <math.h>
2022-03-17 08:43:02 +00:00
#include "../ta-log.h"
#include "IconsFontAwesome4.h"
#include "misc/cpp/imgui_stdlib.h"
2022-03-20 09:28:57 +00:00
#include <fmt/printf.h>
#include "guiConst.h"
2022-03-22 09:54:01 +00:00
#include "sampleUtil.h"
#include "util.h"
void FurnaceGUI::drawSampleEdit() {
if (nextWindow==GUI_WINDOW_SAMPLE_EDIT) {
sampleEditOpen=true;
ImGui::SetNextWindowFocus();
nextWindow=GUI_WINDOW_NOTHING;
}
if (!sampleEditOpen) return;
if (ImGui::Begin("Sample Editor",&sampleEditOpen,settings.allowEditDocking?0:ImGuiWindowFlags_NoDocking)) {
if (curSample<0 || curSample>=(int)e->song.sample.size()) {
ImGui::Text("no sample selected");
} else {
DivSample* sample=e->song.sample[curSample];
String sampleType="Invalid";
if (sample->depth<17) {
if (sampleDepths[sample->depth]!=NULL) {
sampleType=sampleDepths[sample->depth];
}
}
2022-03-17 08:43:02 +00:00
ImGui::Text("Name");
2022-03-17 23:08:59 +00:00
ImGui::SameLine();
2022-03-17 08:43:02 +00:00
ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x);
if (ImGui::InputText("##SampleName",&sample->name)) {
MARK_MODIFIED;
}
2022-03-17 08:43:02 +00:00
2022-04-08 04:01:42 +00:00
if (ImGui::BeginTable("SampleProps",2,ImGuiTableFlags_SizingStretchSame)) {
2022-03-17 08:43:02 +00:00
ImGui::TableNextRow();
ImGui::TableNextColumn();
2022-03-19 08:42:44 +00:00
ImGui::Text("Type");
ImGui::SameLine();
ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x);
if (ImGui::BeginCombo("##SampleType",sampleType.c_str())) {
2022-03-17 08:43:02 +00:00
for (int i=0; i<17; i++) {
if (sampleDepths[i]==NULL) continue;
if (ImGui::Selectable(sampleDepths[i])) {
sample->prepareUndo(true);
2022-03-17 08:43:02 +00:00
sample->depth=i;
e->renderSamplesP();
2022-03-17 23:28:56 +00:00
updateSampleTex=true;
MARK_MODIFIED;
2022-03-17 08:43:02 +00:00
}
}
2022-03-17 08:43:02 +00:00
ImGui::EndCombo();
}
2022-03-17 08:43:02 +00:00
ImGui::TableNextColumn();
2022-03-19 08:42:44 +00:00
ImGui::Text("Rate (Hz)");
ImGui::SameLine();
ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x);
if (ImGui::InputInt("##SampleRate",&sample->rate,10,200)) { MARK_MODIFIED
2022-03-17 08:43:02 +00:00
if (sample->rate<100) sample->rate=100;
if (sample->rate>96000) sample->rate=96000;
}
2022-03-17 08:43:02 +00:00
2022-04-08 04:01:42 +00:00
ImGui::TableNextRow();
2022-03-17 08:43:02 +00:00
ImGui::TableNextColumn();
2022-03-19 08:42:44 +00:00
ImGui::Text("C-4 (Hz)");
ImGui::SameLine();
ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x);
if (ImGui::InputInt("##SampleCenter",&sample->centerRate,10,200)) { MARK_MODIFIED
2022-03-17 08:43:02 +00:00
if (sample->centerRate<100) sample->centerRate=100;
if (sample->centerRate>65535) sample->centerRate=65535;
}
ImGui::TableNextColumn();
bool doLoop=(sample->loopStart>=0);
if (ImGui::Checkbox("Loop",&doLoop)) { MARK_MODIFIED
2022-03-17 08:43:02 +00:00
if (doLoop) {
sample->loopStart=0;
2022-03-17 08:43:02 +00:00
} else {
sample->loopStart=-1;
}
2022-03-17 23:28:56 +00:00
updateSampleTex=true;
}
2022-03-17 08:43:02 +00:00
if (doLoop) {
ImGui::SameLine();
2022-03-19 08:42:44 +00:00
ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x);
if (ImGui::InputInt("##LoopPosition",&sample->loopStart,1,10)) { MARK_MODIFIED
2022-03-17 08:43:02 +00:00
if (sample->loopStart<0 || sample->loopStart>=(int)sample->samples) {
sample->loopStart=0;
}
2022-03-17 23:28:56 +00:00
updateSampleTex=true;
2022-03-17 08:43:02 +00:00
}
}
ImGui::EndTable();
}
2022-03-17 08:43:02 +00:00
/*
if (ImGui::Button("Apply")) {
e->renderSamplesP();
}
ImGui::SameLine();
*/
ImGui::Separator();
2022-03-17 08:43:02 +00:00
ImGui::BeginDisabled(sample->depth!=8 && sample->depth!=16);
ImGui::PushStyleColor(ImGuiCol_Button,TOGGLE_COLOR(!sampleDragMode));
2022-03-20 09:28:57 +00:00
if (ImGui::Button(ICON_FA_I_CURSOR "##SSelect")) {
sampleDragMode=false;
}
ImGui::PopStyleColor();
2022-03-19 08:42:44 +00:00
if (ImGui::IsItemHovered()) {
ImGui::SetTooltip("Edit mode: Select");
}
ImGui::SameLine();
ImGui::PushStyleColor(ImGuiCol_Button,TOGGLE_COLOR(sampleDragMode));
2022-03-20 09:28:57 +00:00
if (ImGui::Button(ICON_FA_PENCIL "##SDraw")) {
sampleDragMode=true;
}
ImGui::PopStyleColor();
2022-03-19 08:42:44 +00:00
if (ImGui::IsItemHovered()) {
ImGui::SetTooltip("Edit mode: Draw");
}
ImGui::SameLine();
2022-04-08 04:01:42 +00:00
ImGui::Dummy(ImVec2(7.0*dpiScale,dpiScale));
2022-03-19 08:42:44 +00:00
ImGui::SameLine();
ImGui::Button(ICON_FA_ARROWS_H "##SResize");
if (ImGui::IsItemClicked()) {
resizeSize=sample->samples;
}
if (ImGui::IsItemHovered()) {
ImGui::SetTooltip("Resize");
}
2022-03-22 09:54:01 +00:00
if (openSampleResizeOpt) {
openSampleResizeOpt=false;
ImGui::OpenPopup("SResizeOpt");
}
2022-03-19 08:42:44 +00:00
if (ImGui::BeginPopupContextItem("SResizeOpt",ImGuiPopupFlags_MouseButtonLeft)) {
if (ImGui::InputInt("Samples",&resizeSize,1,64)) {
if (resizeSize<0) resizeSize=0;
if (resizeSize>16777215) resizeSize=16777215;
}
if (ImGui::Button("Resize")) {
sample->prepareUndo(true);
e->lockEngine([this,sample]() {
2022-03-20 08:14:00 +00:00
if (!sample->resize(resizeSize)) {
showError("couldn't resize! make sure your sample is 8 or 16-bit.");
}
2022-03-19 08:42:44 +00:00
e->renderSamples();
});
updateSampleTex=true;
sampleSelStart=-1;
sampleSelEnd=-1;
MARK_MODIFIED;
2022-03-19 08:42:44 +00:00
ImGui::CloseCurrentPopup();
}
ImGui::EndPopup();
} else {
resizeSize=sample->samples;
}
ImGui::SameLine();
ImGui::Button(ICON_FA_EXPAND "##SResample");
if (ImGui::IsItemClicked()) {
resampleTarget=sample->rate;
}
if (ImGui::IsItemHovered()) {
ImGui::SetTooltip("Resample");
}
2022-03-22 09:54:01 +00:00
if (openSampleResampleOpt) {
openSampleResampleOpt=false;
ImGui::OpenPopup("SResampleOpt");
}
2022-03-19 08:42:44 +00:00
if (ImGui::BeginPopupContextItem("SResampleOpt",ImGuiPopupFlags_MouseButtonLeft)) {
ImGui::Text("Rate");
if (ImGui::InputDouble("##SRRate",&resampleTarget,1.0,50.0,"%g")) {
if (resampleTarget<0) resampleTarget=0;
if (resampleTarget>96000) resampleTarget=96000;
}
ImGui::SameLine();
if (ImGui::Button("0.5x")) {
resampleTarget*=0.5;
}
ImGui::SameLine();
if (ImGui::Button("==")) {
resampleTarget=sample->rate;
}
ImGui::SameLine();
if (ImGui::Button("2.0x")) {
resampleTarget*=2.0;
}
double factor=resampleTarget/(double)sample->rate;
if (ImGui::InputDouble("Factor",&factor,0.125,0.5,"%g")) {
resampleTarget=(double)sample->rate*factor;
if (resampleTarget<0) resampleTarget=0;
if (resampleTarget>96000) resampleTarget=96000;
}
ImGui::Combo("Filter",&resampleStrat,resampleStrats,6);
2022-03-20 08:14:00 +00:00
if (ImGui::Button("Resample")) {
sample->prepareUndo(true);
e->lockEngine([this,sample]() {
2022-03-20 08:14:00 +00:00
if (!sample->resample(resampleTarget,resampleStrat)) {
showError("couldn't resample! make sure your sample is 8 or 16-bit.");
}
e->renderSamples();
});
updateSampleTex=true;
sampleSelStart=-1;
sampleSelEnd=-1;
MARK_MODIFIED;
2022-03-20 08:14:00 +00:00
ImGui::CloseCurrentPopup();
}
2022-03-19 08:42:44 +00:00
ImGui::EndPopup();
} else {
resampleTarget=sample->rate;
}
ImGui::SameLine();
2022-04-08 04:01:42 +00:00
ImGui::Dummy(ImVec2(0.5*dpiScale,dpiScale));
ImGui::SameLine();
2022-03-19 08:42:44 +00:00
ImGui::Button(ICON_FA_VOLUME_UP "##SAmplify");
if (ImGui::IsItemHovered()) {
ImGui::SetTooltip("Amplify");
}
2022-03-22 09:54:01 +00:00
if (openSampleAmplifyOpt) {
openSampleAmplifyOpt=false;
ImGui::OpenPopup("SAmplifyOpt");
}
2022-03-19 08:42:44 +00:00
if (ImGui::BeginPopupContextItem("SAmplifyOpt",ImGuiPopupFlags_MouseButtonLeft)) {
ImGui::Text("Volume");
if (ImGui::InputFloat("##SRVolume",&amplifyVol,10.0,50.0,"%g%%")) {
if (amplifyVol<0) amplifyVol=0;
if (amplifyVol>10000) amplifyVol=10000;
}
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;
if (sample->depth==16) {
for (unsigned int i=start; i<end; i++) {
float val=sample->data16[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; i<end; i++) {
float val=sample->data8[i]*vol;
if (val<-128) val=-128;
if (val>127) val=127;
sample->data8[i]=val;
}
}
updateSampleTex=true;
e->renderSamples();
});
MARK_MODIFIED;
ImGui::CloseCurrentPopup();
}
2022-03-19 08:42:44 +00:00
ImGui::EndPopup();
}
ImGui::SameLine();
if (ImGui::Button(ICON_FA_ARROWS_V "##SNormalize")) {
2022-03-22 09:54:01 +00:00
doAction(GUI_ACTION_SAMPLE_NORMALIZE);
}
2022-03-19 08:42:44 +00:00
if (ImGui::IsItemHovered()) {
ImGui::SetTooltip("Normalize");
}
ImGui::SameLine();
if (ImGui::Button(ICON_FA_ARROW_UP "##SFadeIn")) {
2022-03-22 09:54:01 +00:00
doAction(GUI_ACTION_SAMPLE_FADE_IN);
}
if (ImGui::IsItemHovered()) {
ImGui::SetTooltip("Fade in");
}
ImGui::SameLine();
if (ImGui::Button(ICON_FA_ARROW_DOWN "##SFadeOut")) {
2022-03-22 09:54:01 +00:00
doAction(GUI_ACTION_SAMPLE_FADE_OUT);
}
if (ImGui::IsItemHovered()) {
ImGui::SetTooltip("Fade out");
}
ImGui::SameLine();
2022-04-08 04:01:42 +00:00
ImGui::Dummy(ImVec2(0.5*dpiScale,dpiScale));
2022-03-19 08:42:44 +00:00
ImGui::SameLine();
ImGui::Button(ICON_FA_INDUSTRY "##SFilter");
if (ImGui::IsItemHovered()) {
ImGui::SetTooltip("Apply filter");
}
2022-03-22 09:54:01 +00:00
if (openSampleFilterOpt) {
openSampleFilterOpt=false;
ImGui::OpenPopup("SFilterOpt");
}
if (ImGui::BeginPopupContextItem("SFilterOpt",ImGuiPopupFlags_MouseButtonLeft)) {
float lowP=sampleFilterL*100.0f;
float bandP=sampleFilterB*100.0f;
float highP=sampleFilterH*100.0f;
float resP=sampleFilterRes*100.0f;
ImGui::Text("Cutoff:");
if (ImGui::SliderFloat("From",&sampleFilterCutStart,0.0f,sample->rate*0.5,"%.0fHz")) {
if (sampleFilterCutStart<0.0) sampleFilterCutStart=0.0;
if (sampleFilterCutStart>sample->rate*0.5) sampleFilterCutStart=sample->rate*0.5;
}
if (ImGui::SliderFloat("To",&sampleFilterCutEnd,0.0f,sample->rate*0.5,"%.0fHz")) {
if (sampleFilterCutEnd<0.0) sampleFilterCutEnd=0.0;
if (sampleFilterCutEnd>sample->rate*0.5) sampleFilterCutEnd=sample->rate*0.5;
}
ImGui::Separator();
if (ImGui::SliderFloat("Resonance",&resP,0.0f,99.0f,"%.1f%%")) {
sampleFilterRes=resP/100.0f;
if (sampleFilterRes<0.0f) sampleFilterRes=0.0f;
if (sampleFilterRes>0.99f) sampleFilterRes=0.99f;
}
ImGui::Text("Power");
ImGui::SameLine();
if (ImGui::RadioButton("1x",sampleFilterPower==1)) {
sampleFilterPower=1;
}
ImGui::SameLine();
if (ImGui::RadioButton("2x",sampleFilterPower==2)) {
sampleFilterPower=2;
}
ImGui::SameLine();
if (ImGui::RadioButton("3x",sampleFilterPower==3)) {
sampleFilterPower=3;
}
ImGui::Separator();
if (ImGui::SliderFloat("Low-pass",&lowP,0.0f,100.0f,"%.1f%%")) {
sampleFilterL=lowP/100.0f;
if (sampleFilterL<0.0f) sampleFilterL=0.0f;
if (sampleFilterL>1.0f) sampleFilterL=1.0f;
}
if (ImGui::SliderFloat("Band-pass",&bandP,0.0f,100.0f,"%.1f%%")) {
sampleFilterB=bandP/100.0f;
if (sampleFilterB<0.0f) sampleFilterB=0.0f;
if (sampleFilterB>1.0f) sampleFilterB=1.0f;
}
if (ImGui::SliderFloat("High-pass",&highP,0.0f,100.0f,"%.1f%%")) {
sampleFilterH=highP/100.0f;
if (sampleFilterH<0.0f) sampleFilterH=0.0f;
if (sampleFilterH>1.0f) sampleFilterH=1.0f;
}
if (ImGui::Button("Apply")) {
sample->prepareUndo(true);
e->lockEngine([this,sample]() {
SAMPLE_OP_BEGIN;
float res=1.0-pow(sampleFilterRes,0.5f);
float low=0;
float band=0;
float high=0;
double power=(sampleFilterCutStart>sampleFilterCutEnd)?0.5:2.0;
if (sample->depth==16) {
for (unsigned int i=start; i<end; i++) {
double freq=sampleFilterCutStart+(sampleFilterCutEnd-sampleFilterCutStart)*pow(double(i-start)/double(end-start),power);
double cut=sin((freq/double(sample->rate))*M_PI);
2022-03-19 08:42:44 +00:00
for (int j=0; j<sampleFilterPower; j++) {
low=low+cut*band;
high=float(sample->data16[i])-low-(res*band);
band=cut*high+band;
}
float val=low*sampleFilterL+band*sampleFilterB+high*sampleFilterH;
if (val<-32768) val=-32768;
if (val>32767) val=32767;
sample->data16[i]=val;
}
} else if (sample->depth==8) {
for (unsigned int i=start; i<end; i++) {
double freq=sampleFilterCutStart+(sampleFilterCutEnd-sampleFilterCutStart)*pow(double(i-start)/double(end-start),power);
double cut=sin((freq/double(sample->rate))*M_PI);
for (int j=0; j<sampleFilterPower; j++) {
low=low+cut*band;
high=float(sample->data8[i])-low-(res*band);
band=cut*high+band;
}
float val=low*sampleFilterL+band*sampleFilterB+high*sampleFilterH;
if (val<-128) val=-128;
if (val>127) val=127;
sample->data8[i]=val;
}
}
updateSampleTex=true;
e->renderSamples();
});
MARK_MODIFIED;
ImGui::CloseCurrentPopup();
}
ImGui::EndPopup();
}
2022-04-08 04:01:42 +00:00
if (ImGui::Button(ICON_FA_UNDO "##SUndo")) {
doUndoSample();
}
if (ImGui::IsItemHovered()) {
ImGui::SetTooltip("Undo");
}
ImGui::SameLine();
2022-04-08 04:01:42 +00:00
if (ImGui::Button(ICON_FA_REPEAT "##SRedo")) {
doRedoSample();
}
if (ImGui::IsItemHovered()) {
ImGui::SetTooltip("Redo");
}
ImGui::SameLine();
ImGui::Dummy(ImVec2(7.0*dpiScale,dpiScale));
ImGui::SameLine();
2022-04-08 04:01:42 +00:00
if (ImGui::Button(ICON_FA_BACKWARD "##SReverse")) {
doAction(GUI_ACTION_SAMPLE_REVERSE);
}
if (ImGui::IsItemHovered()) {
ImGui::SetTooltip("Reverse");
}
ImGui::SameLine();
if (ImGui::Button(ICON_FA_SORT_AMOUNT_ASC "##SInvert")) {
doAction(GUI_ACTION_SAMPLE_INVERT);
}
if (ImGui::IsItemHovered()) {
ImGui::SetTooltip("Invert");
}
ImGui::SameLine();
if (ImGui::Button(ICON_FA_LEVEL_DOWN "##SSign")) {
doAction(GUI_ACTION_SAMPLE_SIGN);
}
if (ImGui::IsItemHovered()) {
ImGui::SetTooltip("Signed/unsigned exchange");
}
ImGui::SameLine();
ImGui::Dummy(ImVec2(7.0*dpiScale,dpiScale));
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;
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.");
}
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);
}
if (ImGui::IsItemHovered()) {
ImGui::SetTooltip("Apply silence");
}
ImGui::SameLine();
if (ImGui::Button(ICON_FA_TIMES "##SDelete")) {
doAction(GUI_ACTION_SAMPLE_DELETE);
}
if (ImGui::IsItemHovered()) {
ImGui::SetTooltip("Delete");
}
ImGui::SameLine();
if (ImGui::Button(ICON_FA_CROP "##STrim")) {
doAction(GUI_ACTION_SAMPLE_TRIM);
}
if (ImGui::IsItemHovered()) {
ImGui::SetTooltip("Trim");
}
if (ImGui::Button(ICON_FA_PLAY "##PreviewSample")) {
e->previewSample(curSample);
}
if (ImGui::IsItemHovered()) {
ImGui::SetTooltip("Preview sample");
}
ImGui::SameLine();
2022-04-08 04:01:42 +00:00
if (ImGui::Button(ICON_FA_STOP "##StopSample")) {
e->stopSamplePreview();
}
if (ImGui::IsItemHovered()) {
ImGui::SetTooltip("Stop sample preview");
}
ImGui::SameLine();
double zoomPercent=100.0/sampleZoom;
ImGui::Text("Zoom");
ImGui::SameLine();
ImGui::SetNextItemWidth(150.0f*dpiScale);
if (ImGui::InputDouble("##SZoom",&zoomPercent,5.0,20.0,"%g%%")) {
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;
}
ImGui::SameLine();
if (sampleZoomAuto) {
if (ImGui::Button("100%")) {
sampleZoom=1.0;
sampleZoomAuto=false;
updateSampleTex=true;
}
} else {
if (ImGui::Button("Auto")) {
sampleZoomAuto=true;
updateSampleTex=true;
}
}
2022-03-19 08:42:44 +00:00
ImGui::Separator();
2022-03-17 08:43:02 +00:00
ImVec2 avail=ImGui::GetContentRegionAvail();
avail.y-=ImGui::GetFontSize()+ImGui::GetStyle().ItemSpacing.y+ImGui::GetStyle().ScrollbarSize;
2022-03-17 23:08:59 +00:00
int availX=avail.x;
2022-03-17 08:43:02 +00:00
int availY=avail.y;
if (sampleZoomAuto) {
samplePos=0;
if (sample->samples<1 || avail.x<=0) {
sampleZoom=1.0;
} else {
sampleZoom=(double)sample->samples/avail.x;
}
if (sampleZoom!=prevSampleZoom) {
prevSampleZoom=sampleZoom;
updateSampleTex=true;
}
}
2022-03-17 08:43:02 +00:00
if (sampleTex==NULL || sampleTexW!=avail.x || sampleTexH!=avail.y) {
if (sampleTex!=NULL) {
SDL_DestroyTexture(sampleTex);
sampleTex=NULL;
}
if (avail.x>=1 && avail.y>=1) {
logD("recreating sample texture.\n");
2022-03-17 23:08:59 +00:00
sampleTex=SDL_CreateTexture(sdlRend,SDL_PIXELFORMAT_ABGR8888,SDL_TEXTUREACCESS_STREAMING,avail.x,avail.y);
2022-03-17 08:43:02 +00:00
sampleTexW=avail.x;
sampleTexH=avail.y;
if (sampleTex==NULL) {
logE("error while creating sample texture! %s\n",SDL_GetError());
} else {
updateSampleTex=true;
}
}
}
if (sampleTex!=NULL) {
if (updateSampleTex) {
2022-03-17 23:08:59 +00:00
unsigned int* data=NULL;
2022-03-17 08:43:02 +00:00
int pitch=0;
logD("updating sample texture.\n");
if (SDL_LockTexture(sampleTex,NULL,(void**)&data,&pitch)!=0) {
logE("error while locking sample texture! %s\n",SDL_GetError());
} else {
2022-03-17 23:08:59 +00:00
ImU32 bgColor=ImGui::GetColorU32(ImGuiCol_FrameBg);
ImU32 bgColorLoop=ImAlphaBlendColors(bgColor,ImGui::GetColorU32(ImGuiCol_FrameBgHovered,0.5));
2022-03-17 23:08:59 +00:00
ImU32 lineColor=ImGui::GetColorU32(ImGuiCol_PlotLines);
2022-03-22 22:33:54 +00:00
ImU32 centerLineColor=ImAlphaBlendColors(bgColor,ImGui::GetColorU32(ImGuiCol_PlotLines,0.25));
for (int i=0; i<availY; i++) {
for (int j=0; j<availX; j++) {
if (sample->loopStart>=0 && sample->loopStart<(int)sample->samples && j-samplePos>sample->loopStart) {
data[i*availX+j]=bgColorLoop;
} else {
data[i*availX+j]=bgColor;
}
}
2022-03-17 23:08:59 +00:00
}
2022-03-22 22:33:54 +00:00
if (availY>0) {
for (int i=availX*(availY>>1); i<availX*(1+(availY>>1)); i++) {
data[i]=centerLineColor;
}
}
2022-03-17 23:08:59 +00:00
unsigned int xCoarse=samplePos;
unsigned int xFine=0;
unsigned int xAdvanceCoarse=sampleZoom;
unsigned int xAdvanceFine=fmod(sampleZoom,1.0)*16777216;
for (unsigned int i=0; i<(unsigned int)availX; i++) {
if (xCoarse>=sample->samples) break;
int y1, y2;
int totalAdvance=0;
2022-03-20 23:25:48 +00:00
if (sample->depth==8) {
y1=((unsigned char)sample->data8[xCoarse]^0x80)*availY/256;
} else {
y1=((unsigned short)sample->data16[xCoarse]^0x8000)*availY/65536;
}
2022-03-17 23:08:59 +00:00
xFine+=xAdvanceFine;
if (xFine>=16777216) {
xFine-=16777216;
totalAdvance++;
}
totalAdvance+=xAdvanceCoarse;
if (xCoarse>=sample->samples) break;
do {
2022-03-20 23:25:48 +00:00
if (sample->depth==8) {
y2=((unsigned char)sample->data8[xCoarse]^0x80)*availY/256;
} else {
y2=((unsigned short)sample->data16[xCoarse]^0x8000)*availY/65536;
}
2022-03-17 23:08:59 +00:00
if (y1>y2) {
y2^=y1;
y1^=y2;
y2^=y1;
}
2022-03-20 23:25:48 +00:00
if (y1<0) y1=0;
if (y1>=availY) y1=availY-1;
if (y2<0) y2=0;
if (y2>=availY) y2=availY-1;
2022-03-17 23:08:59 +00:00
for (int j=y1; j<=y2; j++) {
2022-03-20 23:25:48 +00:00
data[i+availX*(availY-j-1)]=lineColor;
2022-03-17 23:08:59 +00:00
}
if (totalAdvance>0) xCoarse++;
} while ((totalAdvance--)>0);
2022-03-17 08:43:02 +00:00
}
SDL_UnlockTexture(sampleTex);
}
updateSampleTex=false;
}
2022-03-19 08:42:44 +00:00
ImGui::ImageButton(sampleTex,avail,ImVec2(0,0),ImVec2(1,1),0);
ImVec2 rectMin=ImGui::GetItemRectMin();
ImVec2 rectMax=ImGui::GetItemRectMax();
ImVec2 rectSize=ImGui::GetItemRectSize();
2022-03-19 08:42:44 +00:00
if (ImGui::IsItemClicked()) {
if (ImGui::IsMouseDoubleClicked(ImGuiMouseButton_Left)) {
sampleDragActive=false;
sampleSelStart=0;
sampleSelEnd=sample->samples;
} else {
if (sample->samples>0 && (sample->depth==16 || sample->depth==8)) {
sampleDragStart=rectMin;
sampleDragAreaSize=rectSize;
sampleDrag16=(sample->depth==16);
sampleDragTarget=(sample->depth==16)?((void*)sample->data16):((void*)sample->data8);
sampleDragLen=sample->samples;
sampleDragActive=true;
sampleSelStart=-1;
sampleSelEnd=-1;
if (sampleDragMode) sample->prepareUndo(true);
processDrags(ImGui::GetMousePos().x,ImGui::GetMousePos().y);
}
2022-03-20 23:25:48 +00:00
}
2022-03-19 08:42:44 +00:00
}
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();
}
2022-03-20 09:28:57 +00:00
String statusBar=sampleDragMode?"Draw":"Select";
bool drawSelection=false;
2022-03-20 09:28:57 +00:00
if (!sampleDragMode) {
2022-03-22 22:33:54 +00:00
if (sampleSelStart>=0 && sampleSelEnd>=0) {
2022-03-20 10:19:23 +00:00
int start=sampleSelStart;
int end=sampleSelEnd;
if (start>end) {
start^=end;
end^=start;
start^=end;
}
statusBar+=fmt::sprintf(" (%d-%d)",start,end);
drawSelection=true;
2022-03-20 09:28:57 +00:00
}
}
2022-03-20 09:28:57 +00:00
if (ImGui::IsItemHovered()) {
int posX=-1;
int posY=0;
ImVec2 pos=ImGui::GetMousePos();
pos.x-=rectMin.x;
pos.y-=rectMin.y;
2022-03-20 09:28:57 +00:00
if (sampleZoom>0) {
posX=samplePos+pos.x*sampleZoom;
if (posX>(int)sample->samples) posX=-1;
}
posY=(0.5-pos.y/rectSize.y)*((sample->depth==8)?255:32767);
2022-03-20 09:28:57 +00:00
if (posX>=0) {
statusBar+=fmt::sprintf(" | (%d, %d)",posX,posY);
}
}
if (drawSelection) {
int start=sampleSelStart;
int end=sampleSelEnd;
if (start>end) {
start^=end;
end^=start;
start^=end;
}
ImDrawList* dl=ImGui::GetWindowDrawList();
ImVec2 p1=rectMin;
2022-03-22 22:33:54 +00:00
p1.x+=(start-samplePos)/sampleZoom;
2022-03-22 22:33:54 +00:00
ImVec2 p2=ImVec2(rectMin.x+(end-samplePos)/sampleZoom,rectMax.y);
ImVec4 boundColor=uiColors[GUI_COLOR_ACCENT_PRIMARY];
ImVec4 selColor=uiColors[GUI_COLOR_ACCENT_SECONDARY];
2022-03-22 22:33:54 +00:00
boundColor.w*=0.5;
selColor.w*=0.25;
2022-03-22 22:33:54 +00:00
if (p1.x<rectMin.x) p1.x=rectMin.x;
if (p1.x>rectMax.x) p1.x=rectMax.x;
if (p2.x<rectMin.x) p2.x=rectMin.x;
if (p2.x>rectMax.x) p2.x=rectMax.x;
dl->AddRectFilled(p1,p2,ImGui::GetColorU32(selColor));
2022-03-22 22:33:54 +00:00
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;
ImS64 availV=round(rectSize.x*sampleZoom);
ImS64 contentsV=MAX(sample->samples,MAX(availV,1));
if (ImGui::ScrollbarEx(ImRect(ImVec2(rectMin.x,rectMax.y),ImVec2(rectMax.x,rectMax.y+ImGui::GetStyle().ScrollbarSize)),ImGui::GetID("sampleScroll"),ImGuiAxis_X,&scrollV,availV,contentsV,0)) {
if (!sampleZoomAuto && samplePos!=scrollV) {
samplePos=scrollV;
updateSampleTex=true;
}
}
if (sample->depth!=8 && sample->depth!=16) {
statusBar="Non-8/16-bit samples cannot be edited without prior conversion.";
}
ImGui::EndDisabled();
2022-03-20 09:28:57 +00:00
ImGui::SetCursorPosY(ImGui::GetCursorPosY()+ImGui::GetStyle().ScrollbarSize);
2022-03-20 09:28:57 +00:00
ImGui::Text("%s",statusBar.c_str());
2022-03-17 08:43:02 +00:00
}
/*
bool considerations=false;
ImGui::Text("notes:");
if (sample->loopStart>=0) {
considerations=true;
ImGui::Text("- sample won't loop on Neo Geo ADPCM-A and X1-010");
if (sample->loopStart&1) {
ImGui::Text("- sample loop start will be aligned to the nearest even sample on Amiga");
}
if (sample->loopStart>0) {
ImGui::Text("- sample loop start will be ignored on Neo Geo ADPCM-B");
}
}
if (sample->samples&1) {
considerations=true;
ImGui::Text("- sample length will be aligned to the nearest even sample on Amiga");
}
if (sample->samples&511) {
considerations=true;
ImGui::Text("- sample length will be aligned and padded to 512 sample units on Neo Geo ADPCM.");
}
if (sample->samples&4095) {
considerations=true;
ImGui::Text("- sample length will be aligned and padded to 4096 sample units on X1-010.");
}
if (sample->samples>65535) {
considerations=true;
ImGui::Text("- maximum sample length on Sega PCM and QSound is 65536 samples");
}
if (sample->samples>131071) {
considerations=true;
ImGui::Text("- maximum sample length on X1-010 is 131072 samples");
}
if (sample->samples>2097151) {
considerations=true;
ImGui::Text("- maximum sample length on Neo Geo ADPCM is 2097152 samples");
}
if (!considerations) {
ImGui::Text("- none");
2022-03-17 08:43:02 +00:00
}*/
}
}
if (ImGui::IsWindowFocused(ImGuiFocusedFlags_ChildWindows)) curWindow=GUI_WINDOW_SAMPLE_EDIT;
ImGui::End();
2022-03-17 23:30:28 +00:00
}
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;
}
});
}