From 7253bf66cd2c796c454301ac97a8963fdabd0b53 Mon Sep 17 00:00:00 2001 From: Eknous-P Date: Tue, 30 Sep 2025 15:15:42 +0400 Subject: [PATCH 1/7] really fix nes loop end warning --- src/gui/sampleEdit.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/gui/sampleEdit.cpp b/src/gui/sampleEdit.cpp index 4e4c11dec..7f1a594aa 100644 --- a/src/gui/sampleEdit.cpp +++ b/src/gui/sampleEdit.cpp @@ -293,10 +293,11 @@ void FurnaceGUI::drawSampleEdit() { String alignHint=fmt::sprintf(_("NES: loop start must be a multiple of 512 (try with %d)"),tryWith); SAMPLE_WARN(warnLoopStart,alignHint); } - if ((sample->loopEnd>0) && ((sample->loopEnd-8)&127)) { + if ((sample->loopEnd-8)&127) { int tryWith=(sample->loopEnd-8)&(~127); if (tryWith>(int)sample->samples) tryWith-=128; tryWith+=8; // +1 bc of how sample length is treated: https://www.nesdev.org/wiki/APU_DMC + if (tryWith<8) tryWith=8; String alignHint=fmt::sprintf(_("NES: loop end must be a multiple of 128 + 8 (try with %d)"),tryWith); SAMPLE_WARN(warnLoopEnd,alignHint); } From 1b712e03ee7f15aaf032d0d138483d456cd2648e Mon Sep 17 00:00:00 2001 From: cam900 Date: Mon, 22 Sep 2025 15:54:48 +0900 Subject: [PATCH 2/7] Add notifySampleChanged in dispatch and engine: This method/variables are for notify sample is changed/altered/added/removed. can be called together with updateSampleTex for sample update. multipcm: Fix possible desync with instrument and sample parameter opl4: Split sample table render and sample data render, Add notifySampleChange for refresh sample parameters --- src/engine/dispatch.h | 5 ++ src/engine/engine.cpp | 8 +++ src/engine/engine.h | 2 + src/engine/platform/abstract.cpp | 6 +- src/engine/platform/multipcm.cpp | 7 ++- src/engine/platform/multipcm.h | 1 + src/engine/platform/opl.cpp | 98 +++++++++++++++++++------------- src/engine/platform/opl.h | 3 + src/gui/dataList.cpp | 2 + src/gui/doAction.cpp | 22 +++++++ src/gui/gui.cpp | 23 +++++--- src/gui/gui.h | 2 +- src/gui/insEdit.cpp | 2 + src/gui/newSong.cpp | 1 + src/gui/sampleEdit.cpp | 16 ++++++ 15 files changed, 146 insertions(+), 52 deletions(-) diff --git a/src/engine/dispatch.h b/src/engine/dispatch.h index 9837102e7..843a78d8d 100644 --- a/src/engine/dispatch.h +++ b/src/engine/dispatch.h @@ -917,6 +917,11 @@ class DivDispatch { */ virtual void notifyWaveChange(int wave); + /** + * notify sample change. + */ + virtual void notifySampleChange(int sample); + /** * notify addition of an instrument. */ diff --git a/src/engine/engine.cpp b/src/engine/engine.cpp index 8735f1b81..065d0b426 100644 --- a/src/engine/engine.cpp +++ b/src/engine/engine.cpp @@ -283,6 +283,14 @@ void DivEngine::notifyWaveChange(int wave) { BUSY_END; } +void DivEngine::notifySampleChange(int sample) { + BUSY_BEGIN; + for (int i=0; inotifySampleChange(sample); + } + BUSY_END; +} + int DivEngine::loadSampleROM(String path, ssize_t expectedSize, unsigned char*& ret) { ret=NULL; if (path.empty()) { diff --git a/src/engine/engine.h b/src/engine/engine.h index 2c7c2ccb7..48e53fcbd 100644 --- a/src/engine/engine.h +++ b/src/engine/engine.h @@ -748,6 +748,8 @@ class DivEngine { void notifyInsChange(int ins); // notify wavetable change void notifyWaveChange(int wave); + // notify sample change + void notifySampleChange(int sample); // dispatch a command int dispatchCmd(DivCommand c); diff --git a/src/engine/platform/abstract.cpp b/src/engine/platform/abstract.cpp index b51cf5966..bb3b40030 100644 --- a/src/engine/platform/abstract.cpp +++ b/src/engine/platform/abstract.cpp @@ -160,7 +160,11 @@ void DivDispatch::notifyInsChange(int ins) { } -void DivDispatch::notifyWaveChange(int ins) { +void DivDispatch::notifyWaveChange(int wave) { + +} + +void DivDispatch::notifySampleChange(int sample) { } diff --git a/src/engine/platform/multipcm.cpp b/src/engine/platform/multipcm.cpp index 1f03199b5..7e1a0ce29 100644 --- a/src/engine/platform/multipcm.cpp +++ b/src/engine/platform/multipcm.cpp @@ -516,6 +516,10 @@ void DivPlatformMultiPCM::notifyInsChange(int ins) { } } +void DivPlatformMultiPCM::notifySampleChange(int sample) { + renderInstruments(); +} + void DivPlatformMultiPCM::notifyInsAddition(int sysID) { renderInstruments(); } @@ -654,8 +658,6 @@ void DivPlatformMultiPCM::renderSamples(int sysID) { memCompo=DivMemoryComposition(); memCompo.name="Sample Memory"; - renderInstruments(); - size_t memPos=0x1800; int sampleCount=parent->song.sampleLen; if (sampleCount>512) { @@ -709,6 +711,7 @@ void DivPlatformMultiPCM::renderSamples(int sysID) { pcmMemLen=memPos+256; memCompo.used=pcmMemLen; } + renderInstruments(); memCompo.capacity=getSampleMemCapacity(0); } diff --git a/src/engine/platform/multipcm.h b/src/engine/platform/multipcm.h index f03c3cefe..36fab35bd 100644 --- a/src/engine/platform/multipcm.h +++ b/src/engine/platform/multipcm.h @@ -120,6 +120,7 @@ class DivPlatformMultiPCM: public DivDispatch { void toggleRegisterDump(bool enable); void setFlags(const DivConfig& flags); void notifyInsChange(int ins); + void notifySampleChange(int sample); void notifyInsAddition(int sysID); void notifyInsDeletion(void* ins); int getPortaFloor(int ch); diff --git a/src/engine/platform/opl.cpp b/src/engine/platform/opl.cpp index f16ad7c2d..1d2c95257 100644 --- a/src/engine/platform/opl.cpp +++ b/src/engine/platform/opl.cpp @@ -2993,6 +2993,12 @@ void DivPlatformOPL::notifyInsChange(int ins) { } } +void DivPlatformOPL::notifySampleChange(int sample) { + if (pcmChanOffs>=0) { + renderInstruments(); + } +} + void DivPlatformOPL::notifyInsDeletion(void* ins) { for (int i=0; i=0) { + const int maxSample=PCM_IN_RAM?128:512; + int sampleCount=parent->song.sampleLen; + if (sampleCount>maxSample) { + sampleCount=maxSample; + } + // instrument table + for (int i=0; isong.sample[i]; + unsigned int insAddr=(i*12)+(PCM_IN_RAM?0x200000:0); + unsigned char bitDepth; + int endPos=CLAMP(s->isLoopable()?s->loopEnd:(s->samples+1),1,0x10000); + int loop=s->isLoopable()?CLAMP(s->loopStart,0,endPos-2):(endPos-2); + switch (s->depth) { + case DIV_SAMPLE_DEPTH_8BIT: + bitDepth=0; + break; + case DIV_SAMPLE_DEPTH_12BIT: + bitDepth=1; + if (!s->isLoopable()) { + endPos++; + loop++; + } + break; + case DIV_SAMPLE_DEPTH_16BIT: + bitDepth=2; + break; + default: + bitDepth=0; + break; + } + pcmMem[insAddr]=(bitDepth<<6)|((sampleOffPCM[i]>>16)&0x3f); + pcmMem[1+insAddr]=(sampleOffPCM[i]>>8)&0xff; + pcmMem[2+insAddr]=(sampleOffPCM[i])&0xff; + pcmMem[3+insAddr]=(loop>>8)&0xff; + pcmMem[4+insAddr]=(loop)&0xff; + pcmMem[5+insAddr]=((~(endPos-1))>>8)&0xff; + pcmMem[6+insAddr]=(~(endPos-1))&0xff; + // on MultiPCM this consists of instrument params, but on OPL4 this is not used + pcmMem[7+insAddr]=0; // LFO, VIB + pcmMem[8+insAddr]=(0xf<<4)|(0xf<<0); // AR, D1R + pcmMem[9+insAddr]=0; // DL, D2R + pcmMem[10+insAddr]=(0xf<<4)|(0xf<<0); // RC, RR + pcmMem[11+insAddr]=0; // AM + } + } +} + void DivPlatformOPL::renderSamples(int sysID) { if (adpcmChan<0 && pcmChanOffs<0) return; if (adpcmChan>=0 && adpcmBMem!=NULL) { @@ -3349,45 +3407,7 @@ void DivPlatformOPL::renderSamples(int sysID) { } pcmMemLen=memPos+256; - // instrument table - for (int i=0; isong.sample[i]; - unsigned int insAddr=(i*12)+(PCM_IN_RAM?0x200000:0); - unsigned char bitDepth; - int endPos=CLAMP(s->isLoopable()?s->loopEnd:(s->samples+1),1,0x10000); - int loop=s->isLoopable()?CLAMP(s->loopStart,0,endPos-2):(endPos-2); - switch (s->depth) { - case DIV_SAMPLE_DEPTH_8BIT: - bitDepth=0; - break; - case DIV_SAMPLE_DEPTH_12BIT: - bitDepth=1; - if (!s->isLoopable()) { - endPos++; - loop++; - } - break; - case DIV_SAMPLE_DEPTH_16BIT: - bitDepth=2; - break; - default: - bitDepth=0; - break; - } - pcmMem[insAddr]=(bitDepth<<6)|((sampleOffPCM[i]>>16)&0x3f); - pcmMem[1+insAddr]=(sampleOffPCM[i]>>8)&0xff; - pcmMem[2+insAddr]=(sampleOffPCM[i])&0xff; - pcmMem[3+insAddr]=(loop>>8)&0xff; - pcmMem[4+insAddr]=(loop)&0xff; - pcmMem[5+insAddr]=((~(endPos-1))>>8)&0xff; - pcmMem[6+insAddr]=(~(endPos-1))&0xff; - // on MultiPCM this consists of instrument params, but on OPL4 this is not used - pcmMem[7+insAddr]=0; // LFO, VIB - pcmMem[8+insAddr]=(0xf<<4)|(0xf<<0); // AR, D1R - pcmMem[9+insAddr]=0; // DL, D2R - pcmMem[10+insAddr]=(0xf<<4)|(0xf<<0); // RC, RR - pcmMem[11+insAddr]=0; // AM - } + renderInstruments(); if (PCM_IN_RAM) { memCompo.entries.push_back(DivMemoryEntry(DIV_MEMORY_RESERVED,"ROM data",0,0,0x200000)); } diff --git a/src/engine/platform/opl.h b/src/engine/platform/opl.h index 7d456a6e2..9ba5dd3fe 100644 --- a/src/engine/platform/opl.h +++ b/src/engine/platform/opl.h @@ -186,6 +186,8 @@ class DivPlatformOPL: public DivDispatch { void acquire_ymfm2(short** buf, size_t len); void acquire_ymfm1(short** buf, size_t len); + void renderInstruments(); + public: void acquire(short** buf, size_t len); int dispatch(DivCommand c); @@ -211,6 +213,7 @@ class DivPlatformOPL: public DivDispatch { void toggleRegisterDump(bool enable); void setFlags(const DivConfig& flags); void notifyInsChange(int ins); + void notifySampleChange(int sample); void notifyInsDeletion(void* ins); int getPortaFloor(int ch); void poke(unsigned int addr, unsigned short val); diff --git a/src/gui/dataList.cpp b/src/gui/dataList.cpp index 400fd6e6c..f8a38f1d6 100644 --- a/src/gui/dataList.cpp +++ b/src/gui/dataList.cpp @@ -279,6 +279,7 @@ void FurnaceGUI::sampleListItem(int i, int dir, int asset) { curSample=i; samplePos=0; updateSampleTex=true; + notifySampleChange=true; lastAssetType=2; } if (ImGui::IsItemHovered() && !mobileUI) { @@ -309,6 +310,7 @@ void FurnaceGUI::sampleListItem(int i, int dir, int asset) { curSample=i; samplePos=0; updateSampleTex=true; + notifySampleChange=true; lastAssetType=2; ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_TEXT]); if (ImGui::MenuItem(_("edit"))) { diff --git a/src/gui/doAction.cpp b/src/gui/doAction.cpp index 3ef117ed0..a9326016b 100644 --- a/src/gui/doAction.cpp +++ b/src/gui/doAction.cpp @@ -946,6 +946,7 @@ void FurnaceGUI::doAction(int what) { MARK_MODIFIED; } updateSampleTex=true; + notifySampleChange=true; } break; case GUI_ACTION_WAVE_LIST_MOVE_UP: @@ -1003,6 +1004,7 @@ void FurnaceGUI::doAction(int what) { MARK_MODIFIED; } updateSampleTex=true; + notifySampleChange=true; break; case GUI_ACTION_SAMPLE_LIST_DUPLICATE: if (curSample>=0 && curSample<(int)e->song.sample.size()) { @@ -1037,6 +1039,7 @@ void FurnaceGUI::doAction(int what) { MARK_MODIFIED; } updateSampleTex=true; + notifySampleChange=true; } break; case GUI_ACTION_SAMPLE_LIST_OPEN: @@ -1062,6 +1065,7 @@ void FurnaceGUI::doAction(int what) { curSample--; wantScrollListSample=true; updateSampleTex=true; + notifySampleChange=true; MARK_MODIFIED; } break; @@ -1070,6 +1074,7 @@ void FurnaceGUI::doAction(int what) { curSample++; wantScrollListSample=true; updateSampleTex=true; + notifySampleChange=true; MARK_MODIFIED; } break; @@ -1081,6 +1086,7 @@ void FurnaceGUI::doAction(int what) { curSample--; } updateSampleTex=true; + notifySampleChange=true; break; case GUI_ACTION_SAMPLE_LIST_EDIT: sampleEditOpen=true; @@ -1089,11 +1095,13 @@ void FurnaceGUI::doAction(int what) { if (--curSample<0) curSample=0; wantScrollListSample=true; updateSampleTex=true; + notifySampleChange=true; break; case GUI_ACTION_SAMPLE_LIST_DOWN: if (++curSample>=(int)e->song.sample.size()) curSample=((int)e->song.sample.size())-1; wantScrollListSample=true; updateSampleTex=true; + notifySampleChange=true; break; case GUI_ACTION_SAMPLE_LIST_PREVIEW: e->previewSample(curSample); @@ -1183,6 +1191,7 @@ void FurnaceGUI::doAction(int what) { e->lockEngine([this,sample,start,end]() { sample->strip(start,end); updateSampleTex=true; + notifySampleChange=true; e->renderSamples(curSample); }); @@ -1235,6 +1244,7 @@ void FurnaceGUI::doAction(int what) { sampleSelStart=pos; sampleSelEnd=pos+sampleClipboardLen; updateSampleTex=true; + notifySampleChange=true; MARK_MODIFIED; break; } @@ -1266,6 +1276,7 @@ void FurnaceGUI::doAction(int what) { sampleSelEnd=pos+sampleClipboardLen; if (sampleSelEnd>(int)sample->samples) sampleSelEnd=sample->samples; updateSampleTex=true; + notifySampleChange=true; MARK_MODIFIED; break; } @@ -1303,6 +1314,7 @@ void FurnaceGUI::doAction(int what) { sampleSelEnd=pos+sampleClipboardLen; if (sampleSelEnd>(int)sample->samples) sampleSelEnd=sample->samples; updateSampleTex=true; + notifySampleChange=true; MARK_MODIFIED; break; } @@ -1368,6 +1380,7 @@ void FurnaceGUI::doAction(int what) { } updateSampleTex=true; + notifySampleChange=true; e->renderSamples(curSample); }); @@ -1399,6 +1412,7 @@ void FurnaceGUI::doAction(int what) { } updateSampleTex=true; + notifySampleChange=true; e->renderSamples(curSample); }); @@ -1430,6 +1444,7 @@ void FurnaceGUI::doAction(int what) { } updateSampleTex=true; + notifySampleChange=true; e->renderSamples(curSample); }); @@ -1459,6 +1474,7 @@ void FurnaceGUI::doAction(int what) { } updateSampleTex=true; + notifySampleChange=true; e->renderSamples(curSample); }); @@ -1475,6 +1491,7 @@ void FurnaceGUI::doAction(int what) { sample->strip(start,end); updateSampleTex=true; + notifySampleChange=true; e->renderSamples(curSample); }); @@ -1493,6 +1510,7 @@ void FurnaceGUI::doAction(int what) { sample->trim(start,end); updateSampleTex=true; + notifySampleChange=true; e->renderSamples(curSample); }); @@ -1528,6 +1546,7 @@ void FurnaceGUI::doAction(int what) { } updateSampleTex=true; + notifySampleChange=true; e->renderSamples(curSample); }); @@ -1555,6 +1574,7 @@ void FurnaceGUI::doAction(int what) { } updateSampleTex=true; + notifySampleChange=true; e->renderSamples(curSample); }); @@ -1580,6 +1600,7 @@ void FurnaceGUI::doAction(int what) { } updateSampleTex=true; + notifySampleChange=true; e->renderSamples(curSample); }); @@ -1713,6 +1734,7 @@ void FurnaceGUI::doAction(int what) { sample->loopEnd=end; sample->loop=true; updateSampleTex=true; + notifySampleChange=true; e->renderSamples(curSample); }); diff --git a/src/gui/gui.cpp b/src/gui/gui.cpp index d17efa94d..c3e8dedc6 100644 --- a/src/gui/gui.cpp +++ b/src/gui/gui.cpp @@ -2915,6 +2915,7 @@ void FurnaceGUI::processDrags(int dragX, int dragY) { for (int i=x; i<=x1; i++) ((signed char*)sampleDragTarget)[i]=val; } updateSampleTex=true; + notifySampleChange=true; } } else { // select if (sampleSelStart<0) { @@ -4019,26 +4020,22 @@ bool FurnaceGUI::loop() { } nextWindow=GUI_WINDOW_WAVE_LIST; MARK_MODIFIED; - } - else if (!samples.empty()) - { + } else if (!samples.empty()) { if (e->song.sampleLen!=sampleCountBefore) { //e->renderSamplesP(); } - if (!e->getWarnings().empty()) - { + if (!e->getWarnings().empty()) { showWarning(e->getWarnings(),GUI_WARN_GENERIC); } int sampleCount=-1; - for (DivSample* s: samples) - { + for (DivSample* s: samples) { sampleCount=e->addSamplePtr(s); } //sampleCount=e->addSamplePtr(droppedSample); - if (sampleCount>=0 && settings.selectAssetOnLoad) - { + if (sampleCount>=0 && settings.selectAssetOnLoad) { curSample=sampleCount; updateSampleTex=true; + notifySampleChange=true; } nextWindow=GUI_WINDOW_SAMPLE_LIST; MARK_MODIFIED; @@ -4277,6 +4274,11 @@ bool FurnaceGUI::loop() { e->notifyWaveChange(curWave); } + if (notifySampleChange) { + notifySampleChange=false; + e->notifySampleChange(curSample); + } + eventTimeEnd=SDL_GetPerformanceCounter(); if (SDL_GetWindowFlags(sdlWin)&SDL_WINDOW_MINIMIZED) { @@ -5523,6 +5525,7 @@ bool FurnaceGUI::loop() { MARK_MODIFIED; }); updateSampleTex=true; + notifySampleChange=true; } else { showError(_("...but you haven't selected a sample!")); delete samples[0]; @@ -7146,6 +7149,7 @@ bool FurnaceGUI::loop() { MARK_MODIFIED; }); updateSampleTex=true; + notifySampleChange=true; } else { showError(_("...but you haven't selected a sample!")); delete s; @@ -8511,6 +8515,7 @@ FurnaceGUI::FurnaceGUI(): sysDupEnd(false), noteInputPoly(true), notifyWaveChange(false), + notifySampleChange(false), wantScrollListIns(false), wantScrollListWave(false), wantScrollListSample(false), diff --git a/src/gui/gui.h b/src/gui/gui.h index dc866ea33..ccda00a27 100644 --- a/src/gui/gui.h +++ b/src/gui/gui.h @@ -1707,7 +1707,7 @@ class FurnaceGUI { bool vgmExportDirectStream, displayInsTypeList, displayWaveSizeList; bool portrait, injectBackUp, mobileMenuOpen, warnColorPushed; bool wantCaptureKeyboard, oldWantCaptureKeyboard, displayMacroMenu; - bool displayNew, displayExport, displayPalette, fullScreen, preserveChanPos, sysDupCloneChannels, sysDupEnd, noteInputPoly, notifyWaveChange; + bool displayNew, displayExport, displayPalette, fullScreen, preserveChanPos, sysDupCloneChannels, sysDupEnd, noteInputPoly, notifyWaveChange, notifySampleChange; bool wantScrollListIns, wantScrollListWave, wantScrollListSample; bool displayPendingIns, pendingInsSingle, displayPendingRawSample, snesFilterHex, modTableHex, displayEditString; bool displayPendingSamples, replacePendingSample; diff --git a/src/gui/insEdit.cpp b/src/gui/insEdit.cpp index 6efdc8892..510b9011d 100644 --- a/src/gui/insEdit.cpp +++ b/src/gui/insEdit.cpp @@ -3465,6 +3465,7 @@ void FurnaceGUI::insTabSample(DivInstrument* ins) { if (ImGui::InputInt(_("Sample bank slot##BANKSLOT"),&ins->x1_010.bankSlot,1,4)) { PARAMETER if (ins->x1_010.bankSlot<0) ins->x1_010.bankSlot=0; if (ins->x1_010.bankSlot>=7) ins->x1_010.bankSlot=7; + notifySampleChange=true; } } } @@ -3478,6 +3479,7 @@ void FurnaceGUI::insTabSample(DivInstrument* ins) { id=fmt::sprintf("%d: %s",i,e->song.sample[i]->name); if (ImGui::Selectable(id.c_str(),ins->amiga.initSample==i)) { PARAMETER ins->amiga.initSample=i; + notifySampleChange=true; } } ImGui::EndCombo(); diff --git a/src/gui/newSong.cpp b/src/gui/newSong.cpp index 7034f8a01..bb92c81d1 100644 --- a/src/gui/newSong.cpp +++ b/src/gui/newSong.cpp @@ -289,6 +289,7 @@ void FurnaceGUI::drawNewSong() { orderCursor=-1; samplePos=0; updateSampleTex=true; + notifySampleChange=true; selStart=SelectionPoint(); selEnd=SelectionPoint(); cursor=SelectionPoint(); diff --git a/src/gui/sampleEdit.cpp b/src/gui/sampleEdit.cpp index 7f1a594aa..4415130d9 100644 --- a/src/gui/sampleEdit.cpp +++ b/src/gui/sampleEdit.cpp @@ -656,6 +656,7 @@ void FurnaceGUI::drawSampleEdit() { sample->loopEnd=sample->samples;*/ } updateSampleTex=true; + notifySampleChange=true; REFRESH_SAMPLE } popWarningColor(); @@ -688,6 +689,7 @@ void FurnaceGUI::drawSampleEdit() { e->renderSamples(curSample); }); updateSampleTex=true; + notifySampleChange=true; MARK_MODIFIED; } } @@ -708,6 +710,7 @@ void FurnaceGUI::drawSampleEdit() { sample->brrEmphasis=be; e->renderSamplesP(curSample); updateSampleTex=true; + notifySampleChange=true; MARK_MODIFIED; } if (ImGui::IsItemHovered()) { @@ -725,6 +728,7 @@ void FurnaceGUI::drawSampleEdit() { sample->brrNoFilter=bf; e->renderSamplesP(curSample); updateSampleTex=true; + notifySampleChange=true; MARK_MODIFIED; } if (ImGui::IsItemHovered()) { @@ -738,6 +742,7 @@ void FurnaceGUI::drawSampleEdit() { sample->dither=di; e->renderSamplesP(curSample); updateSampleTex=true; + notifySampleChange=true; MARK_MODIFIED; } if (ImGui::IsItemHovered()) { @@ -870,6 +875,7 @@ void FurnaceGUI::drawSampleEdit() { sample->loopMode=(DivSampleLoopMode)i; e->renderSamplesP(curSample); updateSampleTex=true; + notifySampleChange=true; MARK_MODIFIED; } } @@ -893,6 +899,7 @@ void FurnaceGUI::drawSampleEdit() { sample->loopStart=sample->loopEnd; } updateSampleTex=true; + notifySampleChange=true; REFRESH_SAMPLE } if (ImGui::IsItemActive()) { @@ -934,6 +941,7 @@ void FurnaceGUI::drawSampleEdit() { sample->loopEnd=sample->samples; } updateSampleTex=true; + notifySampleChange=true; REFRESH_SAMPLE } if (ImGui::IsItemActive()) { @@ -1108,6 +1116,7 @@ void FurnaceGUI::drawSampleEdit() { e->renderSamples(curSample); }); updateSampleTex=true; + notifySampleChange=true; sampleSelStart=-1; sampleSelEnd=-1; MARK_MODIFIED; @@ -1172,6 +1181,7 @@ void FurnaceGUI::drawSampleEdit() { e->renderSamples(curSample); }); updateSampleTex=true; + notifySampleChange=true; sampleSelStart=-1; sampleSelEnd=-1; MARK_MODIFIED; @@ -1239,6 +1249,7 @@ void FurnaceGUI::drawSampleEdit() { } updateSampleTex=true; + notifySampleChange=true; e->renderSamples(curSample); }); @@ -1292,6 +1303,7 @@ void FurnaceGUI::drawSampleEdit() { e->renderSamples(curSample); }); updateSampleTex=true; + notifySampleChange=true; sampleSelStart=pos; sampleSelEnd=pos+silenceSize; MARK_MODIFIED; @@ -1467,6 +1479,7 @@ void FurnaceGUI::drawSampleEdit() { } updateSampleTex=true; + notifySampleChange=true; e->renderSamples(curSample); }); @@ -1546,6 +1559,7 @@ void FurnaceGUI::drawSampleEdit() { } } updateSampleTex=true; + notifySampleChange=true; e->renderSamples(curSample); }); @@ -2508,6 +2522,7 @@ void FurnaceGUI::doUndoSample() { if (sample->undo()==2) { e->renderSamples(curSample); updateSampleTex=true; + notifySampleChange=true; } }); } @@ -2520,6 +2535,7 @@ void FurnaceGUI::doRedoSample() { if (sample->redo()==2) { e->renderSamples(curSample); updateSampleTex=true; + notifySampleChange=true; } }); } From 52eac7e3c6dd87bb1c991defd1d86726128aa12a Mon Sep 17 00:00:00 2001 From: Eknous-P Date: Wed, 1 Oct 2025 23:08:38 +0400 Subject: [PATCH 3/7] sample import detune load setting --- src/engine/fileOpsSample.cpp | 13 ++++++------- src/gui/gui.h | 2 ++ src/gui/settings.cpp | 16 ++++++++++++++-- 3 files changed, 22 insertions(+), 9 deletions(-) diff --git a/src/engine/fileOpsSample.cpp b/src/engine/fileOpsSample.cpp index 8c89d451d..f522160ff 100644 --- a/src/engine/fileOpsSample.cpp +++ b/src/engine/fileOpsSample.cpp @@ -402,13 +402,12 @@ std::vector DivEngine::sampleFromFile(const char* path) { { // There's no documentation on libsndfile detune range, but the code // implies -50..50. Yet when loading a file you can get a >50 value. - // disabled for now - /* - if(inst.detune > 50) - inst.detune = inst.detune - 100; - short pitch = ((0x3c-inst.basenote)*100) + inst.detune; - sample->centerRate=si.samplerate*pow(2.0,pitch/(12.0 * 100.0)); - */ + if (getConfInt("sampleImportInstDetune", 0)) { + if(inst.detune > 50) + inst.detune = inst.detune - 100; + short pitch = ((0x3c-inst.basenote)*100) + inst.detune; + sample->centerRate=si.samplerate*pow(2.0,pitch/(12.0 * 100.0)); + } if(inst.loop_count && inst.loops[0].mode >= SF_LOOP_FORWARD) { sample->loop=true; diff --git a/src/gui/gui.h b/src/gui/gui.h index ccda00a27..47d8048c0 100644 --- a/src/gui/gui.h +++ b/src/gui/gui.h @@ -2058,6 +2058,7 @@ class FurnaceGUI { int s3mOPL3; int songNotesWrap; int rackShowLEDs; + int sampleImportInstDetune; String mainFontPath; String headFontPath; String patFontPath; @@ -2311,6 +2312,7 @@ class FurnaceGUI { s3mOPL3(1), songNotesWrap(0), rackShowLEDs(1), + sampleImportInstDetune(0), mainFontPath(""), headFontPath(""), patFontPath(""), diff --git a/src/gui/settings.cpp b/src/gui/settings.cpp index 595f7330d..5758f8367 100644 --- a/src/gui/settings.cpp +++ b/src/gui/settings.cpp @@ -1162,9 +1162,10 @@ void FurnaceGUI::drawSettings() { settings.newSongBehavior=1; settingsChanged=true; } - if (ImGui::InputText(_("Default author name"), &settings.defaultAuthorName)) settingsChanged=true; ImGui::Unindent(); + if (ImGui::InputText(_("Default author name"), &settings.defaultAuthorName)) settingsChanged=true; + // SUBSECTION START-UP CONFIG_SUBSECTION(_("Start-up")); #ifndef NO_INTRO @@ -1230,6 +1231,14 @@ void FurnaceGUI::drawSettings() { settings.s3mOPL3=s3mOPL3B; settingsChanged=true; } + bool sampleImportInstDetuneB=settings.sampleImportInstDetune; + if (ImGui::Checkbox(_("Load sample fine tuning when importing a sample"), &sampleImportInstDetuneB)) { + settings.sampleImportInstDetune=sampleImportInstDetuneB; + settingsChanged=true; + } + if (ImGui::IsItemHovered()) { + ImGui::SetTooltip(_("this may result in glitches with some samples.")); + } #ifdef ANDROID // SUBSECTION ANDROID @@ -3844,7 +3853,7 @@ void FurnaceGUI::drawSettings() { settingsChanged=true; } - // SUBSECTION SONG COMMENTS + // SUBSECTION CHIP MANAGER CONFIG_SUBSECTION(_("Chip Manager")); bool rackShowLEDsB=settings.rackShowLEDs; if (ImGui::Checkbox(_("Show channel indicators"), &rackShowLEDsB)) { @@ -4881,6 +4890,7 @@ void FurnaceGUI::readConfig(DivConfig& conf, FurnaceGUISettingGroups groups) { settings.vibrationLength=conf.getInt("vibrationLength",20); settings.s3mOPL3=conf.getInt("s3mOPL3",1); + settings.sampleImportInstDetune=conf.getInt("sampleImportInstDetune",0); settings.backupEnable=conf.getInt("backupEnable",1); settings.backupInterval=conf.getInt("backupInterval",30); @@ -5396,6 +5406,7 @@ void FurnaceGUI::readConfig(DivConfig& conf, FurnaceGUISettingGroups groups) { clampSetting(settings.autoFillSave,0,1); clampSetting(settings.autoMacroStepSize,0,2); clampSetting(settings.s3mOPL3,0,1); + clampSetting(settings.sampleImportInstDetune,0,1); clampSetting(settings.backgroundPlay,0,1); clampSetting(settings.noMaximizeWorkaround,0,1); @@ -5478,6 +5489,7 @@ void FurnaceGUI::writeConfig(DivConfig& conf, FurnaceGUISettingGroups groups) { conf.set("vibrationLength",settings.vibrationLength); conf.set("s3mOPL3",settings.s3mOPL3); + conf.set("sampleImportInstDetune",settings.sampleImportInstDetune); conf.set("backupEnable",settings.backupEnable); conf.set("backupInterval",settings.backupInterval); From e6c98506d1b53244610aaefa751bdeafdb65c50f Mon Sep 17 00:00:00 2001 From: Electric Keet Date: Fri, 3 Oct 2025 14:01:38 -0700 Subject: [PATCH 4/7] Proper tick rate for Supervision. This is the actual framerate for the SV's screen. --- src/gui/presets.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/gui/presets.cpp b/src/gui/presets.cpp index 972d77e9c..c8d8ca449 100644 --- a/src/gui/presets.cpp +++ b/src/gui/presets.cpp @@ -328,7 +328,8 @@ void FurnaceGUI::initSystemPresets() { ENTRY( "Watara Supervision", { CH(DIV_SYSTEM_SUPERVISION, 1.0f, 0, "") - } + }, + "tickRate=50.81300813008130081301" ); CATEGORY_END; From 1099c79ec89708d4da614664b7486bcd2010cb74 Mon Sep 17 00:00:00 2001 From: yohannd1 Date: Sun, 28 Sep 2025 12:43:47 -0300 Subject: [PATCH 5/7] channel drag copy: initial impl --- src/engine/engine.cpp | 35 ++++++++++++++++++++++++++++++++++- src/engine/engine.h | 4 ++++ src/gui/channels.cpp | 9 ++++++--- 3 files changed, 44 insertions(+), 4 deletions(-) diff --git a/src/engine/engine.cpp b/src/engine/engine.cpp index 065d0b426..10f33cd33 100644 --- a/src/engine/engine.cpp +++ b/src/engine/engine.cpp @@ -603,10 +603,33 @@ void DivEngine::createNewFromDefaults() { BUSY_END; } +void DivEngine::copyChannel(int src, int dest) { + logV("copying channel %d to %d",src,dest); + if (src==dest) { + logV("not copying because it's the same channel!"); + return; + } + + for (int i=0; iord[dest][i]=curOrders->ord[src][i]; + if (curPat[src].data[i]!=NULL && curPat[dest].data[i]!=NULL) { + curPat[src].data[i]->copyOn(curPat[dest].data[i]); + } + } + + curPat[dest].effectCols=curPat[src].effectCols; + + curSubSong->chanName[dest]=curSubSong->chanName[src]; + curSubSong->chanShortName[dest]=curSubSong->chanShortName[src]; + curSubSong->chanShow[dest]=curSubSong->chanShow[src]; + curSubSong->chanShowChanOsc[dest]=curSubSong->chanShowChanOsc[src]; + curSubSong->chanCollapse[dest]=curSubSong->chanCollapse[src]; +} + void DivEngine::swapChannels(int src, int dest) { logV("swapping channel %d with %d",src,dest); if (src==dest) { - logV("not swapping channels because it's the same channel!",src,dest); + logV("not swapping channels because it's the same channel!"); return; } @@ -746,6 +769,16 @@ void DivEngine::checkAssetDir(std::vector& dir, size_t entries) { delete[] inAssetDir; } +void DivEngine::copyChannelP(int src, int dest) { + if (src<0 || src>=chans) return; + if (dest<0 || dest>=chans) return; + BUSY_BEGIN; + saveLock.lock(); + copyChannel(src,dest); + saveLock.unlock(); + BUSY_END; +} + void DivEngine::swapChannelsP(int src, int dest) { if (src<0 || src>=chans) return; if (dest<0 || dest>=chans) return; diff --git a/src/engine/engine.h b/src/engine/engine.h index 48e53fcbd..a0e181c5d 100644 --- a/src/engine/engine.h +++ b/src/engine/engine.h @@ -649,6 +649,7 @@ class DivEngine { void exchangeWave(int one, int two); void exchangeSample(int one, int two); + void copyChannel(int src, int dest); void swapChannels(int src, int dest); void stompChannel(int ch); @@ -1262,6 +1263,9 @@ class DivEngine { // >=0: render specific sample void renderSamplesP(int whichSample=-1); + // public copy channel + void copyChannelP(int src, int dest); + // public swap channels void swapChannelsP(int src, int dest); diff --git a/src/gui/channels.cpp b/src/gui/channels.cpp index 6ac636a03..0b4ced5ac 100644 --- a/src/gui/channels.cpp +++ b/src/gui/channels.cpp @@ -50,7 +50,7 @@ void FurnaceGUI::drawChannels() { ImGui::TableNextColumn(); ImGui::Text(_("Osc")); ImGui::TableNextColumn(); - ImGui::Text(_("Swap")); + ImGui::Text(_("Drg")); ImGui::TableNextColumn(); ImGui::Text(_("Name")); for (int i=0; igetTotalChannelCount(); i++) { @@ -79,14 +79,17 @@ void FurnaceGUI::drawChannels() { ImGui::Button(ICON_FA_ARROWS "##ChanDrag"); ImGui::EndDragDropSource(); } else if (ImGui::IsItemHovered()) { - ImGui::SetTooltip(_("%s #%d\n(drag to swap channels)"),e->getSystemName(e->sysOfChan[i]),e->dispatchChanOfChan[i]); + ImGui::SetTooltip(_("%s #%d\n(drag to swap channels)\n(shift+drag to copy channel contents)"),e->getSystemName(e->sysOfChan[i]),e->dispatchChanOfChan[i]); } if (ImGui::BeginDragDropTarget()) { const ImGuiPayload* dragItem=ImGui::AcceptDragDropPayload("FUR_CHAN"); if (dragItem!=NULL) { if (dragItem->IsDataType("FUR_CHAN")) { if (chanToMove!=i && chanToMove>=0) { - e->swapChannelsP(chanToMove,i); + if (ImGui::IsKeyDown(ImGuiKey_LeftShift)) + e->copyChannelP(chanToMove,i); + else + e->swapChannelsP(chanToMove,i); MARK_MODIFIED; } chanToMove=-1; From b8ce3219eb2466cd67ed7cf5a832616c28d6d83c Mon Sep 17 00:00:00 2001 From: yohannd1 Date: Sun, 28 Sep 2025 19:55:46 -0300 Subject: [PATCH 6/7] channel drag copy: fix channel copying code (thanks tildearrow & eknous) --- src/engine/engine.cpp | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/src/engine/engine.cpp b/src/engine/engine.cpp index 10f33cd33..3dd9d85c1 100644 --- a/src/engine/engine.cpp +++ b/src/engine/engine.cpp @@ -612,8 +612,16 @@ void DivEngine::copyChannel(int src, int dest) { for (int i=0; iord[dest][i]=curOrders->ord[src][i]; - if (curPat[src].data[i]!=NULL && curPat[dest].data[i]!=NULL) { - curPat[src].data[i]->copyOn(curPat[dest].data[i]); + + DivPattern* srcPat=curPat[src].data[i]; + DivPattern* destPat=curPat[dest].data[i]; + if (srcPat==NULL) { + if (destPat!=NULL) { + delete destPat; + curPat[dest].data[i]=NULL; + } + } else { + curPat[src].data[i]->copyOn(curPat[dest].getPattern(i, true)); } } From e7e001f956371577d20969d8d2345222494fee1e Mon Sep 17 00:00:00 2001 From: yohannd1 Date: Sun, 28 Sep 2025 20:32:49 -0300 Subject: [PATCH 7/7] channel drag copy: remove column header text --- src/gui/channels.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/gui/channels.cpp b/src/gui/channels.cpp index 0b4ced5ac..2e8c4d276 100644 --- a/src/gui/channels.cpp +++ b/src/gui/channels.cpp @@ -50,7 +50,7 @@ void FurnaceGUI::drawChannels() { ImGui::TableNextColumn(); ImGui::Text(_("Osc")); ImGui::TableNextColumn(); - ImGui::Text(_("Drg")); + ImGui::Text("%s", ""); ImGui::TableNextColumn(); ImGui::Text(_("Name")); for (int i=0; igetTotalChannelCount(); i++) {