diff --git a/src/engine/sample.cpp b/src/engine/sample.cpp index d5e1844c8..5fab8b2b7 100644 --- a/src/engine/sample.cpp +++ b/src/engine/sample.cpp @@ -790,7 +790,7 @@ unsigned int DivSample::getCurBufLen() { return 0; } -DivSampleHistory* DivSample::prepareUndo(bool data) { +DivSampleHistory* DivSample::prepareUndo(bool data, bool doNotPush) { DivSampleHistory* h; if (data) { unsigned char* duplicate; @@ -804,33 +804,77 @@ DivSampleHistory* DivSample::prepareUndo(bool data) { } else { h=new DivSampleHistory(depth,rate,centerRate,loopStart); } - while (!redoHist.empty()) { - DivSampleHistory* h=redoHist.front(); - delete h; - redoHist.pop_front(); + if (!doNotPush) { + while (!redoHist.empty()) { + DivSampleHistory* h=redoHist.back(); + delete h; + redoHist.pop_back(); + } + if (undoHist.size()>100) undoHist.pop_front(); + undoHist.push_back(h); } - undoHist.push_back(h); return h; } +#define applyHistory \ + depth=h->depth; \ + if (h->hasSample) { \ + initInternal(h->depth,h->samples); \ + samples=h->samples; \ +\ + if (h->length!=getCurBufLen()) logW("undo buffer length not equal to current buffer length! %d != %d\n",h->length,getCurBufLen()); \ +\ + void* buf=getCurBuf(); \ +\ + if (buf!=NULL && h->data!=NULL) { \ + memcpy(buf,h->data,h->length); \ + } \ + } \ + rate=h->rate; \ + centerRate=h->centerRate; \ + loopStart=h->loopStart; + + int DivSample::undo() { - return 0; + if (undoHist.empty()) return 0; + DivSampleHistory* h=undoHist.back(); + DivSampleHistory* redo=prepareUndo(h->hasSample,true); + + int ret=h->hasSample?2:1; + + applyHistory; + + redoHist.push_back(redo); + delete h; + undoHist.pop_back(); + return ret; } int DivSample::redo() { - return 0; + if (redoHist.empty()) return 0; + DivSampleHistory* h=redoHist.back(); + DivSampleHistory* undo=prepareUndo(h->hasSample,true); + + int ret=h->hasSample?2:1; + + applyHistory; + + undoHist.push_back(undo); + delete h; + redoHist.pop_back(); + return ret; } DivSample::~DivSample() { while (!undoHist.empty()) { - DivSampleHistory* h=undoHist.front(); + DivSampleHistory* h=undoHist.back(); delete h; - undoHist.pop_front(); + undoHist.pop_back(); } while (!redoHist.empty()) { - DivSampleHistory* h=redoHist.front(); + DivSampleHistory* h=redoHist.back(); delete h; - redoHist.pop_front(); + redoHist.pop_back(); } if (data8) delete[] data8; if (data16) delete[] data16; diff --git a/src/engine/sample.h b/src/engine/sample.h index f07c7ef91..1a9e53d32 100644 --- a/src/engine/sample.h +++ b/src/engine/sample.h @@ -189,9 +189,10 @@ struct DivSample { /** * prepare an undo step for this sample. * @param data whether to include sample data. + * @param doNotPush if this is true, don't push the DivSampleHistory to the undo history. * @return the undo step. */ - DivSampleHistory* prepareUndo(bool data); + DivSampleHistory* prepareUndo(bool data, bool doNotPush=false); /** * undo. you may need to call DivEngine::renderSamples afterwards. diff --git a/src/gui/doAction.cpp b/src/gui/doAction.cpp index 66a05fb3a..3866624ac 100644 --- a/src/gui/doAction.cpp +++ b/src/gui/doAction.cpp @@ -55,10 +55,18 @@ void FurnaceGUI::doAction(int what) { openFileDialog(GUI_FILE_SAVE); break; case GUI_ACTION_UNDO: - doUndo(); + if (curWindow==GUI_WINDOW_SAMPLE_EDIT) { + doUndoSample(); + } else { + doUndo(); + } break; case GUI_ACTION_REDO: - doRedo(); + if (curWindow==GUI_WINDOW_SAMPLE_EDIT) { + doRedoSample(); + } else { + doRedo(); + } break; case GUI_ACTION_PLAY_TOGGLE: if (e->isPlaying() && !e->isStepping()) { @@ -594,6 +602,8 @@ void FurnaceGUI::doAction(int what) { if (end-start<1) break; + sample->prepareUndo(true); + if (sampleClipboard!=NULL) { delete[] sampleClipboard; } @@ -632,6 +642,7 @@ void FurnaceGUI::doAction(int what) { if (curSample<0 || curSample>=(int)e->song.sample.size()) break; if (sampleClipboard==NULL || sampleClipboardLen<1) break; DivSample* sample=e->song.sample[curSample]; + sample->prepareUndo(true); int pos=(sampleSelStart==-1 || sampleSelStart==sampleSelEnd)?sample->samples:sampleSelStart; e->lockEngine([this,sample,pos]() { @@ -658,6 +669,7 @@ void FurnaceGUI::doAction(int what) { if (curSample<0 || curSample>=(int)e->song.sample.size()) break; if (sampleClipboard==NULL || sampleClipboardLen<1) break; DivSample* sample=e->song.sample[curSample]; + sample->prepareUndo(true); int pos=(sampleSelStart==-1 || sampleSelStart==sampleSelEnd)?0:sampleSelStart; e->lockEngine([this,sample,pos]() { @@ -685,6 +697,7 @@ void FurnaceGUI::doAction(int what) { if (curSample<0 || curSample>=(int)e->song.sample.size()) break; if (sampleClipboard==NULL || sampleClipboardLen<1) break; DivSample* sample=e->song.sample[curSample]; + sample->prepareUndo(true); int pos=(sampleSelStart==-1 || sampleSelStart==sampleSelEnd)?0:sampleSelStart; e->lockEngine([this,sample,pos]() { @@ -737,6 +750,7 @@ void FurnaceGUI::doAction(int what) { case GUI_ACTION_SAMPLE_NORMALIZE: { if (curSample<0 || curSample>=(int)e->song.sample.size()) break; DivSample* sample=e->song.sample[curSample]; + sample->prepareUndo(true); e->lockEngine([this,sample]() { SAMPLE_OP_BEGIN; float maxVal=0.0f; @@ -783,6 +797,7 @@ void FurnaceGUI::doAction(int what) { case GUI_ACTION_SAMPLE_FADE_IN: { if (curSample<0 || curSample>=(int)e->song.sample.size()) break; DivSample* sample=e->song.sample[curSample]; + sample->prepareUndo(true); e->lockEngine([this,sample]() { SAMPLE_OP_BEGIN; @@ -812,6 +827,7 @@ void FurnaceGUI::doAction(int what) { case GUI_ACTION_SAMPLE_FADE_OUT: { if (curSample<0 || curSample>=(int)e->song.sample.size()) break; DivSample* sample=e->song.sample[curSample]; + sample->prepareUndo(true); e->lockEngine([this,sample]() { SAMPLE_OP_BEGIN; @@ -845,6 +861,7 @@ void FurnaceGUI::doAction(int what) { case GUI_ACTION_SAMPLE_SILENCE: { if (curSample<0 || curSample>=(int)e->song.sample.size()) break; DivSample* sample=e->song.sample[curSample]; + sample->prepareUndo(true); e->lockEngine([this,sample]() { SAMPLE_OP_BEGIN; @@ -868,6 +885,7 @@ void FurnaceGUI::doAction(int what) { case GUI_ACTION_SAMPLE_DELETE: { if (curSample<0 || curSample>=(int)e->song.sample.size()) break; DivSample* sample=e->song.sample[curSample]; + sample->prepareUndo(true); e->lockEngine([this,sample]() { SAMPLE_OP_BEGIN; @@ -884,6 +902,7 @@ void FurnaceGUI::doAction(int what) { case GUI_ACTION_SAMPLE_TRIM: { if (curSample<0 || curSample>=(int)e->song.sample.size()) break; DivSample* sample=e->song.sample[curSample]; + sample->prepareUndo(true); e->lockEngine([this,sample]() { SAMPLE_OP_BEGIN; @@ -900,6 +919,7 @@ void FurnaceGUI::doAction(int what) { case GUI_ACTION_SAMPLE_REVERSE: { if (curSample<0 || curSample>=(int)e->song.sample.size()) break; DivSample* sample=e->song.sample[curSample]; + sample->prepareUndo(true); e->lockEngine([this,sample]() { SAMPLE_OP_BEGIN; @@ -931,6 +951,7 @@ void FurnaceGUI::doAction(int what) { case GUI_ACTION_SAMPLE_INVERT: { if (curSample<0 || curSample>=(int)e->song.sample.size()) break; DivSample* sample=e->song.sample[curSample]; + sample->prepareUndo(true); e->lockEngine([this,sample]() { SAMPLE_OP_BEGIN; @@ -956,6 +977,7 @@ void FurnaceGUI::doAction(int what) { case GUI_ACTION_SAMPLE_SIGN: { if (curSample<0 || curSample>=(int)e->song.sample.size()) break; DivSample* sample=e->song.sample[curSample]; + sample->prepareUndo(true); e->lockEngine([this,sample]() { SAMPLE_OP_BEGIN; diff --git a/src/gui/gui.h b/src/gui/gui.h index a4feed9b5..315ce7421 100644 --- a/src/gui/gui.h +++ b/src/gui/gui.h @@ -897,6 +897,9 @@ class FurnaceGUI { void doRedo(); void editOptions(bool topMenu); + void doUndoSample(); + void doRedoSample(); + void play(int row=0); void stop(); diff --git a/src/gui/sampleEdit.cpp b/src/gui/sampleEdit.cpp index 3bdfa6c9f..0998856cc 100644 --- a/src/gui/sampleEdit.cpp +++ b/src/gui/sampleEdit.cpp @@ -63,14 +63,12 @@ void FurnaceGUI::drawSampleEdit() { for (int i=0; i<17; i++) { if (sampleDepths[i]==NULL) continue; if (ImGui::Selectable(sampleDepths[i])) { + sample->prepareUndo(true); sample->depth=i; e->renderSamplesP(); updateSampleTex=true; MARK_MODIFIED; } - if (ImGui::IsItemHovered()) { - ImGui::SetTooltip("no undo for sample type change operations!"); - } } ImGui::EndCombo(); } @@ -163,6 +161,7 @@ void FurnaceGUI::drawSampleEdit() { if (resizeSize>16777215) resizeSize=16777215; } if (ImGui::Button("Resize")) { + sample->prepareUndo(true); e->lockEngine([this,sample]() { if (!sample->resize(resizeSize)) { showError("couldn't resize! make sure your sample is 8 or 16-bit."); @@ -217,6 +216,7 @@ void FurnaceGUI::drawSampleEdit() { } ImGui::Combo("Filter",&resampleStrat,resampleStrats,6); if (ImGui::Button("Resample")) { + sample->prepareUndo(true); e->lockEngine([this,sample]() { if (!sample->resample(resampleTarget,resampleStrat)) { showError("couldn't resample! make sure your sample is 8 or 16-bit."); @@ -253,6 +253,7 @@ void FurnaceGUI::drawSampleEdit() { 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; @@ -319,6 +320,7 @@ void FurnaceGUI::drawSampleEdit() { } 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."); @@ -437,6 +439,7 @@ void FurnaceGUI::drawSampleEdit() { } if (ImGui::Button("Apply")) { + sample->prepareUndo(true); e->lockEngine([this,sample]() { SAMPLE_OP_BEGIN; float res=1.0-pow(sampleFilterRes,0.5f); @@ -665,6 +668,7 @@ void FurnaceGUI::drawSampleEdit() { sampleDragActive=true; sampleSelStart=-1; sampleSelEnd=-1; + if (sampleDragMode) sample->prepareUndo(true); processDrags(ImGui::GetMousePos().x,ImGui::GetMousePos().y); } } @@ -827,3 +831,27 @@ void FurnaceGUI::drawSampleEdit() { if (ImGui::IsWindowFocused(ImGuiFocusedFlags_ChildWindows)) curWindow=GUI_WINDOW_SAMPLE_EDIT; ImGui::End(); } + +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; + } + }); +} \ No newline at end of file