From 310ad1391655b58c43ac443cc4567a918334eddf Mon Sep 17 00:00:00 2001 From: Adam Lederer Date: Fri, 23 Aug 2024 10:39:14 -0700 Subject: [PATCH 01/14] volume portamento (vol porta) on D3xx and D4xx (D4 is fast version, rate * 256) --- src/engine/cmdStream.cpp | 15 +++++++++- src/engine/cmdStream.h | 3 +- src/engine/cmdStreamOps.cpp | 5 ++++ src/engine/dispatch.h | 1 + src/engine/engine.cpp | 4 +++ src/engine/engine.h | 3 +- src/engine/playback.cpp | 55 +++++++++++++++++++++++++++++++++++-- src/gui/csPlayer.cpp | 4 +++ src/gui/debugWindow.cpp | 1 + src/gui/guiConst.cpp | 4 +-- src/gui/pattern.cpp | 1 + 11 files changed, 88 insertions(+), 8 deletions(-) diff --git a/src/engine/cmdStream.cpp b/src/engine/cmdStream.cpp index d8c77030f..5f369b211 100644 --- a/src/engine/cmdStream.cpp +++ b/src/engine/cmdStream.cpp @@ -215,6 +215,10 @@ bool DivCSPlayer::tick() { case DIV_CMD_HINT_VOL_SLIDE: arg0=(short)stream.readS(); break; + case DIV_CMD_HINT_VOL_SLIDE_TARGET: + arg0=(short)stream.readS(); + arg1=(short)stream.readS(); + break; case DIV_CMD_HINT_LEGATO: arg0=(unsigned char)stream.readC(); if (arg0==0xff) { @@ -321,6 +325,11 @@ bool DivCSPlayer::tick() { break; case DIV_CMD_HINT_VOL_SLIDE: chan[i].volSpeed=arg0; + chan[i].volSpeedTarget=-1; + break; + case DIV_CMD_HINT_VOL_SLIDE_TARGET: + chan[i].volSpeed=arg0; + chan[i].volSpeedTarget=arg0==0 ? -1 : arg1; break; case DIV_CMD_HINT_PITCH: chan[i].pitch=arg0; @@ -356,13 +365,17 @@ bool DivCSPlayer::tick() { if (sendVolume || chan[i].volSpeed!=0) { chan[i].volume+=chan[i].volSpeed; + if (chan[i].volSpeedTarget!=-1 && (chan[i].volume==chan[i].volSpeedTarget || (chan[i].volume>chan[i].volSpeedTarget)==(chan[i].volSpeed>0))) { + chan[i].volume=chan[i].volSpeedTarget; + chan[i].volSpeed=0; + chan[i].volSpeedTarget=-1; + } if (chan[i].volume<0) { chan[i].volume=0; } if (chan[i].volume>chan[i].volMax) { chan[i].volume=chan[i].volMax; } - e->dispatchCmd(DivCommand(DIV_CMD_VOLUME,i,chan[i].volume>>8)); } diff --git a/src/engine/cmdStream.h b/src/engine/cmdStream.h index 4704b41b9..33fcdd2cf 100644 --- a/src/engine/cmdStream.h +++ b/src/engine/cmdStream.h @@ -34,7 +34,7 @@ struct DivCSChannelState { int lastWaitLen; int note, pitch; - int volume, volMax, volSpeed; + int volume, volMax, volSpeed, volSpeedTarget; int vibratoDepth, vibratoRate, vibratoPos; int portaTarget, portaSpeed; unsigned char arp, arpStage, arpTicks; @@ -56,6 +56,7 @@ struct DivCSChannelState { volume(0x7f00), volMax(0), volSpeed(0), + volSpeedTarget(0), vibratoDepth(0), vibratoRate(0), vibratoPos(0), diff --git a/src/engine/cmdStreamOps.cpp b/src/engine/cmdStreamOps.cpp index 0710e650e..1f3d81ee2 100644 --- a/src/engine/cmdStreamOps.cpp +++ b/src/engine/cmdStreamOps.cpp @@ -59,6 +59,7 @@ void writePackedCommandValues(SafeWriter* w, const DivCommand& c) { case DIV_CMD_HINT_VOLUME: case DIV_CMD_HINT_PORTA: case DIV_CMD_HINT_VOL_SLIDE: + case DIV_CMD_HINT_VOL_SLIDE_TARGET: case DIV_CMD_HINT_LEGATO: w->writeC((unsigned char)c.cmd+0xb4); break; @@ -100,6 +101,10 @@ void writePackedCommandValues(SafeWriter* w, const DivCommand& c) { case DIV_CMD_HINT_VOL_SLIDE: w->writeS(c.value); break; + case DIV_CMD_HINT_VOL_SLIDE_TARGET: + w->writeS(c.value); + w->writeS(c.value2); + break; case DIV_CMD_SAMPLE_MODE: case DIV_CMD_SAMPLE_FREQ: case DIV_CMD_SAMPLE_BANK: diff --git a/src/engine/dispatch.h b/src/engine/dispatch.h index e077d8f4d..517adc662 100644 --- a/src/engine/dispatch.h +++ b/src/engine/dispatch.h @@ -67,6 +67,7 @@ enum DivDispatchCmds { DIV_CMD_HINT_ARPEGGIO, // (note1, note2) DIV_CMD_HINT_VOLUME, // (vol) DIV_CMD_HINT_VOL_SLIDE, // (amount, oneTick) + DIV_CMD_HINT_VOL_SLIDE_TARGET, // (amount, target) DIV_CMD_HINT_PORTA, // (target, speed) DIV_CMD_HINT_LEGATO, // (note) diff --git a/src/engine/engine.cpp b/src/engine/engine.cpp index fbbf0edc8..18189a29b 100644 --- a/src/engine/engine.cpp +++ b/src/engine/engine.cpp @@ -98,6 +98,10 @@ const char* DivEngine::getEffectDesc(unsigned char effect, int chan, bool notNul break; case 0xc0: case 0xc1: case 0xc2: case 0xc3: return _("Cxxx: Set tick rate (hz)"); + case 0xd3: + return _("D3xx: Volume portamento"); + case 0xd4: + return _("D4xx: Volume portamento (fast)"); case 0xdc: return _("DCxx: Delayed mute"); case 0xe0: diff --git a/src/engine/engine.h b/src/engine/engine.h index 64d406964..e7788149e 100644 --- a/src/engine/engine.h +++ b/src/engine/engine.h @@ -133,7 +133,7 @@ struct DivAudioExportOptions { struct DivChannelState { std::vector delayed; int note, oldNote, lastIns, pitch, portaSpeed, portaNote; - int volume, volSpeed, cut, volCut, legatoDelay, legatoTarget, rowDelay, volMax; + int volume, volSpeed, volSpeedTarget, cut, volCut, legatoDelay, legatoTarget, rowDelay, volMax; int delayOrder, delayRow, retrigSpeed, retrigTick; int vibratoDepth, vibratoRate, vibratoPos, vibratoPosGiant, vibratoShape, vibratoFine; int tremoloDepth, tremoloRate, tremoloPos; @@ -157,6 +157,7 @@ struct DivChannelState { portaNote(-1), volume(0x7f00), volSpeed(0), + volSpeedTarget(0), cut(-1), volCut(-1), legatoDelay(-1), diff --git a/src/engine/playback.cpp b/src/engine/playback.cpp index 3b3234934..9c157073a 100644 --- a/src/engine/playback.cpp +++ b/src/engine/playback.cpp @@ -67,6 +67,7 @@ const char* cmdName[]={ "HINT_ARPEGGIO", "HINT_VOLUME", "HINT_VOL_SLIDE", + "HINT_VOL_SLIDE_TARGET", "HINT_PORTA", "HINT_LEGATO", @@ -639,11 +640,26 @@ void DivEngine::processRow(int i, bool afterDelay) { } // volume + int volPortaTarget=-1; + bool useVolPorta = false; + for (int j=0; jdata[whatRow][4+(j<<1)]; + short effectVal=pat->data[whatRow][5+(j<<1)]; + if (effectVal==-1) effectVal=0; + if ((effect==0xd3||effect==0xd4) && effectVal!=0) { // vol porta + useVolPorta=true; + break; + } + } + if (pat->data[whatRow][3]!=-1) { - if (!song.oldAlwaysSetVolume || disCont[dispatchOfChan[i]].dispatch->getLegacyAlwaysSetVolume() || (MIN(chan[i].volMax,chan[i].volume)>>8)!=pat->data[whatRow][3]) { + if (useVolPorta) { + volPortaTarget=pat->data[whatRow][3]<<8; + } else if (!song.oldAlwaysSetVolume || disCont[dispatchOfChan[i]].dispatch->getLegacyAlwaysSetVolume() || (MIN(chan[i].volMax,chan[i].volume)>>8)!=pat->data[whatRow][3]) { if (pat->data[whatRow][0]==0 && pat->data[whatRow][1]==0) { chan[i].midiAftertouch=true; } + chan[i].volume=pat->data[whatRow][3]<<8; dispatchCmd(DivCommand(DIV_CMD_VOLUME,i,chan[i].volume>>8)); dispatchCmd(DivCommand(DIV_CMD_HINT_VOLUME,i,chan[i].volume>>8)); @@ -828,6 +844,7 @@ void DivEngine::processRow(int i, bool afterDelay) { } else { chan[i].volSpeed=0; } + chan[i].volSpeedTarget=-1; dispatchCmd(DivCommand(DIV_CMD_HINT_VOL_SLIDE,i,chan[i].volSpeed)); break; case 0x06: // vol slide + porta @@ -869,6 +886,7 @@ void DivEngine::processRow(int i, bool afterDelay) { } else { chan[i].volSpeed=0; } + chan[i].volSpeedTarget=-1; dispatchCmd(DivCommand(DIV_CMD_HINT_VOL_SLIDE,i,chan[i].volSpeed)); break; case 0x07: // tremolo @@ -879,6 +897,7 @@ void DivEngine::processRow(int i, bool afterDelay) { chan[i].tremoloRate=effectVal>>4; if (chan[i].tremoloDepth!=0) { chan[i].volSpeed=0; + chan[i].volSpeedTarget=-1; } else { dispatchCmd(DivCommand(DIV_CMD_VOLUME,i,chan[i].volume>>8)); dispatchCmd(DivCommand(DIV_CMD_HINT_VOLUME,i,chan[i].volume>>8)); @@ -898,6 +917,7 @@ void DivEngine::processRow(int i, bool afterDelay) { } else { chan[i].volSpeed=0; } + chan[i].volSpeedTarget=-1; dispatchCmd(DivCommand(DIV_CMD_HINT_VOL_SLIDE,i,chan[i].volSpeed)); break; case 0x00: // arpeggio @@ -1077,6 +1097,7 @@ void DivEngine::processRow(int i, bool afterDelay) { chan[i].tremoloDepth=0; chan[i].tremoloRate=0; chan[i].volSpeed=effectVal; + chan[i].volSpeedTarget=-1; dispatchCmd(DivCommand(DIV_CMD_HINT_VOL_SLIDE,i,chan[i].volSpeed)); break; case 0xf4: // fine volume ramp down @@ -1084,6 +1105,7 @@ void DivEngine::processRow(int i, bool afterDelay) { chan[i].tremoloDepth=0; chan[i].tremoloRate=0; chan[i].volSpeed=-effectVal; + chan[i].volSpeedTarget=-1; dispatchCmd(DivCommand(DIV_CMD_HINT_VOL_SLIDE,i,chan[i].volSpeed)); break; case 0xf5: // disable macro @@ -1097,12 +1119,14 @@ void DivEngine::processRow(int i, bool afterDelay) { break; case 0xf8: // single volume ramp up chan[i].volSpeed=0; // add compat flag? + chan[i].volSpeedTarget=-1; chan[i].volume=MIN(chan[i].volume+effectVal*256,chan[i].volMax); dispatchCmd(DivCommand(DIV_CMD_VOLUME,i,chan[i].volume>>8)); dispatchCmd(DivCommand(DIV_CMD_HINT_VOLUME,i,chan[i].volume>>8)); break; case 0xf9: // single volume ramp down chan[i].volSpeed=0; // add compat flag? + chan[i].volSpeedTarget=-1; chan[i].volume=MAX(chan[i].volume-effectVal*256,0); dispatchCmd(DivCommand(DIV_CMD_VOLUME,i,chan[i].volume>>8)); dispatchCmd(DivCommand(DIV_CMD_HINT_VOLUME,i,chan[i].volume>>8)); @@ -1120,9 +1144,25 @@ void DivEngine::processRow(int i, bool afterDelay) { } else { chan[i].volSpeed=0; } + chan[i].volSpeedTarget=-1; dispatchCmd(DivCommand(DIV_CMD_HINT_VOL_SLIDE,i,chan[i].volSpeed)); break; - + case 0xd3: // volume portamento (vol porta) + // tremolo and vol slides are incompatible + chan[i].tremoloDepth=0; + chan[i].tremoloRate=0; + chan[i].volSpeed=volPortaTarget>chan[i].volume ? effectVal : -effectVal; + chan[i].volSpeedTarget=chan[i].volSpeed==0 ? -1 : volPortaTarget; + dispatchCmd(DivCommand(DIV_CMD_HINT_VOL_SLIDE_TARGET,i,chan[i].volSpeed,chan[i].volSpeedTarget)); + break; + case 0xd4: // volume portamento fast (vol porta fast) + // tremolo and vol slides are incompatible + chan[i].tremoloDepth=0; + chan[i].tremoloRate=0; + chan[i].volSpeed=volPortaTarget>chan[i].volume ? 256*effectVal : -256*effectVal; + chan[i].volSpeedTarget=chan[i].volSpeed==0 ? -1 : volPortaTarget; + dispatchCmd(DivCommand(DIV_CMD_HINT_VOL_SLIDE_TARGET,i,chan[i].volSpeed,chan[i].volSpeedTarget)); + break; case 0xfc: // delayed note release if (song.delayBehavior==2 || effectValchan[i].volMax) { + if (chan[i].volSpeedTarget!=-1 && (chan[i].volume==chan[i].volSpeedTarget || (chan[i].volume>chan[i].volSpeedTarget)==(chan[i].volSpeed>0))) { + chan[i].volume=chan[i].volSpeedTarget; + chan[i].volSpeed=0; + chan[i].volSpeedTarget=-1; + dispatchCmd(DivCommand(DIV_CMD_HINT_VOLUME,i,chan[i].volume>>8)); + dispatchCmd(DivCommand(DIV_CMD_VOLUME,i,chan[i].volume>>8)); + dispatchCmd(DivCommand(DIV_CMD_HINT_VOL_SLIDE,i,0)); + } else if (chan[i].volume>chan[i].volMax) { chan[i].volume=chan[i].volMax; chan[i].volSpeed=0; + chan[i].volSpeedTarget=-1; dispatchCmd(DivCommand(DIV_CMD_HINT_VOLUME,i,chan[i].volume>>8)); dispatchCmd(DivCommand(DIV_CMD_VOLUME,i,chan[i].volume>>8)); dispatchCmd(DivCommand(DIV_CMD_HINT_VOL_SLIDE,i,0)); @@ -1604,6 +1652,7 @@ bool DivEngine::nextTick(bool noAccum, bool inhibitLowLat) { } else { chan[i].volume=0; } + chan[i].volSpeedTarget=-1; dispatchCmd(DivCommand(DIV_CMD_VOLUME,i,chan[i].volume>>8)); dispatchCmd(DivCommand(DIV_CMD_HINT_VOLUME,i,chan[i].volume>>8)); } else { diff --git a/src/gui/csPlayer.cpp b/src/gui/csPlayer.cpp index 6bbae43ce..7be1f4e1d 100644 --- a/src/gui/csPlayer.cpp +++ b/src/gui/csPlayer.cpp @@ -161,6 +161,8 @@ void FurnaceGUI::drawCSPlayer() { ImGui::TableNextColumn(); ImGui::Text(_("vols")); ImGui::TableNextColumn(); + ImGui::Text(_("volst")); + ImGui::TableNextColumn(); ImGui::Text(_("vib")); ImGui::TableNextColumn(); ImGui::Text(_("porta")); @@ -189,6 +191,8 @@ void FurnaceGUI::drawCSPlayer() { ImGui::TableNextColumn(); ImGui::Text("%+d",state->volSpeed); ImGui::TableNextColumn(); + ImGui::Text("%+d",state->volSpeedTarget); + ImGui::TableNextColumn(); ImGui::Text("%d/%d (%d)",state->vibratoDepth,state->vibratoRate,state->vibratoPos); ImGui::TableNextColumn(); ImGui::Text("-> %d (%d)",state->portaTarget,state->portaSpeed); diff --git a/src/gui/debugWindow.cpp b/src/gui/debugWindow.cpp index b08c60532..b28126fc5 100644 --- a/src/gui/debugWindow.cpp +++ b/src/gui/debugWindow.cpp @@ -145,6 +145,7 @@ void FurnaceGUI::drawDebug() { ImGui::Text("- portaNote = %d",ch->portaNote); ImGui::Text("- volume = %.4x",ch->volume); ImGui::Text("- volSpeed = %d",ch->volSpeed); + ImGui::Text("- volSpeedTarget = %d",ch->volSpeedTarget); ImGui::Text("- cut = %d",ch->cut); ImGui::Text("- rowDelay = %d",ch->rowDelay); ImGui::Text("- volMax = %.4x",ch->volMax); diff --git a/src/gui/guiConst.cpp b/src/gui/guiConst.cpp index 8fd662824..fbfdadc0c 100644 --- a/src/gui/guiConst.cpp +++ b/src/gui/guiConst.cpp @@ -473,8 +473,8 @@ const FurnaceGUIColors fxColors[256]={ GUI_COLOR_PATTERN_EFFECT_INVALID, GUI_COLOR_PATTERN_EFFECT_INVALID, GUI_COLOR_PATTERN_EFFECT_INVALID, - GUI_COLOR_PATTERN_EFFECT_INVALID, - GUI_COLOR_PATTERN_EFFECT_INVALID, + GUI_COLOR_PATTERN_EFFECT_VOLUME, + GUI_COLOR_PATTERN_EFFECT_VOLUME, GUI_COLOR_PATTERN_EFFECT_INVALID, GUI_COLOR_PATTERN_EFFECT_INVALID, GUI_COLOR_PATTERN_EFFECT_INVALID, diff --git a/src/gui/pattern.cpp b/src/gui/pattern.cpp index a2568bd31..c32171161 100644 --- a/src/gui/pattern.cpp +++ b/src/gui/pattern.cpp @@ -1500,6 +1500,7 @@ void FurnaceGUI::drawPattern() { i.cmd==DIV_CMD_HINT_PORTA || i.cmd==DIV_CMD_HINT_LEGATO || i.cmd==DIV_CMD_HINT_VOL_SLIDE || + i.cmd==DIV_CMD_HINT_VOL_SLIDE_TARGET || i.cmd==DIV_CMD_HINT_ARPEGGIO || i.cmd==DIV_CMD_HINT_PITCH || i.cmd==DIV_CMD_HINT_VIBRATO || From ae4adececd48c21d8440c71f2e963eaf5c073437 Mon Sep 17 00:00:00 2001 From: Adam Lederer Date: Fri, 23 Aug 2024 11:16:22 -0700 Subject: [PATCH 02/14] do nothing if volume column empty --- src/engine/playback.cpp | 32 ++++++++++++++++++-------------- 1 file changed, 18 insertions(+), 14 deletions(-) diff --git a/src/engine/playback.cpp b/src/engine/playback.cpp index 9c157073a..e56489f05 100644 --- a/src/engine/playback.cpp +++ b/src/engine/playback.cpp @@ -641,21 +641,23 @@ void DivEngine::processRow(int i, bool afterDelay) { // volume int volPortaTarget=-1; - bool useVolPorta = false; + bool noApplyVolume=false; for (int j=0; jdata[whatRow][4+(j<<1)]; - short effectVal=pat->data[whatRow][5+(j<<1)]; - if (effectVal==-1) effectVal=0; - if ((effect==0xd3||effect==0xd4) && effectVal!=0) { // vol porta - useVolPorta=true; - break; + if (effect==0xd3 || effect==0xd4) { // vol porta + volPortaTarget=pat->data[whatRow][3]<<8; // can be -256 + + short effectVal=pat->data[whatRow][5+(j<<1)]; + if (effectVal==-1) effectVal=0; + effectVal&=255; + + noApplyVolume=effectVal>0; // "D3.." or "D300" shouldn't stop volume from applying + break; // technically you could have both D3 and D4... let's not care } } - if (pat->data[whatRow][3]!=-1) { - if (useVolPorta) { - volPortaTarget=pat->data[whatRow][3]<<8; - } else if (!song.oldAlwaysSetVolume || disCont[dispatchOfChan[i]].dispatch->getLegacyAlwaysSetVolume() || (MIN(chan[i].volMax,chan[i].volume)>>8)!=pat->data[whatRow][3]) { + if (pat->data[whatRow][3]!=-1 && !noApplyVolume) { + if (!song.oldAlwaysSetVolume || disCont[dispatchOfChan[i]].dispatch->getLegacyAlwaysSetVolume() || (MIN(chan[i].volMax,chan[i].volume)>>8)!=pat->data[whatRow][3]) { if (pat->data[whatRow][0]==0 && pat->data[whatRow][1]==0) { chan[i].midiAftertouch=true; } @@ -1147,22 +1149,24 @@ void DivEngine::processRow(int i, bool afterDelay) { chan[i].volSpeedTarget=-1; dispatchCmd(DivCommand(DIV_CMD_HINT_VOL_SLIDE,i,chan[i].volSpeed)); break; - case 0xd3: // volume portamento (vol porta) + case 0xd3: { // volume portamento (vol porta) // tremolo and vol slides are incompatible chan[i].tremoloDepth=0; chan[i].tremoloRate=0; - chan[i].volSpeed=volPortaTarget>chan[i].volume ? effectVal : -effectVal; + chan[i].volSpeed=volPortaTarget<0 ? 0 : volPortaTarget>chan[i].volume ? effectVal : -effectVal; chan[i].volSpeedTarget=chan[i].volSpeed==0 ? -1 : volPortaTarget; dispatchCmd(DivCommand(DIV_CMD_HINT_VOL_SLIDE_TARGET,i,chan[i].volSpeed,chan[i].volSpeedTarget)); break; - case 0xd4: // volume portamento fast (vol porta fast) + } + case 0xd4: { // volume portamento fast (vol porta fast) // tremolo and vol slides are incompatible chan[i].tremoloDepth=0; chan[i].tremoloRate=0; - chan[i].volSpeed=volPortaTarget>chan[i].volume ? 256*effectVal : -256*effectVal; + chan[i].volSpeed=volPortaTarget<0 ? 0 : volPortaTarget>chan[i].volume ? 256*effectVal : -256*effectVal; chan[i].volSpeedTarget=chan[i].volSpeed==0 ? -1 : volPortaTarget; dispatchCmd(DivCommand(DIV_CMD_HINT_VOL_SLIDE_TARGET,i,chan[i].volSpeed,chan[i].volSpeedTarget)); break; + } case 0xfc: // delayed note release if (song.delayBehavior==2 || effectVal Date: Fri, 23 Aug 2024 11:24:24 -0700 Subject: [PATCH 03/14] minor cleanup --- src/engine/cmdStream.h | 2 +- src/engine/engine.h | 2 +- src/engine/playback.cpp | 35 ++++++++++++++++------------------- 3 files changed, 18 insertions(+), 21 deletions(-) diff --git a/src/engine/cmdStream.h b/src/engine/cmdStream.h index 33fcdd2cf..2d8e6e97e 100644 --- a/src/engine/cmdStream.h +++ b/src/engine/cmdStream.h @@ -56,7 +56,7 @@ struct DivCSChannelState { volume(0x7f00), volMax(0), volSpeed(0), - volSpeedTarget(0), + volSpeedTarget(-1), vibratoDepth(0), vibratoRate(0), vibratoPos(0), diff --git a/src/engine/engine.h b/src/engine/engine.h index e7788149e..308c6f5cd 100644 --- a/src/engine/engine.h +++ b/src/engine/engine.h @@ -157,7 +157,7 @@ struct DivChannelState { portaNote(-1), volume(0x7f00), volSpeed(0), - volSpeedTarget(0), + volSpeedTarget(-1), cut(-1), volCut(-1), legatoDelay(-1), diff --git a/src/engine/playback.cpp b/src/engine/playback.cpp index e56489f05..2c7e97e0e 100644 --- a/src/engine/playback.cpp +++ b/src/engine/playback.cpp @@ -661,7 +661,6 @@ void DivEngine::processRow(int i, bool afterDelay) { if (pat->data[whatRow][0]==0 && pat->data[whatRow][1]==0) { chan[i].midiAftertouch=true; } - chan[i].volume=pat->data[whatRow][3]<<8; dispatchCmd(DivCommand(DIV_CMD_VOLUME,i,chan[i].volume>>8)); dispatchCmd(DivCommand(DIV_CMD_HINT_VOLUME,i,chan[i].volume>>8)); @@ -962,6 +961,22 @@ void DivEngine::processRow(int i, bool afterDelay) { chan[i].cutType=0; } break; + case 0xd3: // volume portamento (vol porta) + // tremolo and vol slides are incompatible + chan[i].tremoloDepth=0; + chan[i].tremoloRate=0; + chan[i].volSpeed=volPortaTarget<0 ? 0 : volPortaTarget>chan[i].volume ? effectVal : -effectVal; + chan[i].volSpeedTarget=chan[i].volSpeed==0 ? -1 : volPortaTarget; + dispatchCmd(DivCommand(DIV_CMD_HINT_VOL_SLIDE_TARGET,i,chan[i].volSpeed,chan[i].volSpeedTarget)); + break; + case 0xd4: // volume portamento fast (vol porta fast) + // tremolo and vol slides are incompatible + chan[i].tremoloDepth=0; + chan[i].tremoloRate=0; + chan[i].volSpeed=volPortaTarget<0 ? 0 : volPortaTarget>chan[i].volume ? 256*effectVal : -256*effectVal; + chan[i].volSpeedTarget=chan[i].volSpeed==0 ? -1 : volPortaTarget; + dispatchCmd(DivCommand(DIV_CMD_HINT_VOL_SLIDE_TARGET,i,chan[i].volSpeed,chan[i].volSpeedTarget)); + break; case 0xe0: // arp speed if (effectVal>0) { curSubSong->arpLen=effectVal; @@ -1149,24 +1164,6 @@ void DivEngine::processRow(int i, bool afterDelay) { chan[i].volSpeedTarget=-1; dispatchCmd(DivCommand(DIV_CMD_HINT_VOL_SLIDE,i,chan[i].volSpeed)); break; - case 0xd3: { // volume portamento (vol porta) - // tremolo and vol slides are incompatible - chan[i].tremoloDepth=0; - chan[i].tremoloRate=0; - chan[i].volSpeed=volPortaTarget<0 ? 0 : volPortaTarget>chan[i].volume ? effectVal : -effectVal; - chan[i].volSpeedTarget=chan[i].volSpeed==0 ? -1 : volPortaTarget; - dispatchCmd(DivCommand(DIV_CMD_HINT_VOL_SLIDE_TARGET,i,chan[i].volSpeed,chan[i].volSpeedTarget)); - break; - } - case 0xd4: { // volume portamento fast (vol porta fast) - // tremolo and vol slides are incompatible - chan[i].tremoloDepth=0; - chan[i].tremoloRate=0; - chan[i].volSpeed=volPortaTarget<0 ? 0 : volPortaTarget>chan[i].volume ? 256*effectVal : -256*effectVal; - chan[i].volSpeedTarget=chan[i].volSpeed==0 ? -1 : volPortaTarget; - dispatchCmd(DivCommand(DIV_CMD_HINT_VOL_SLIDE_TARGET,i,chan[i].volSpeed,chan[i].volSpeedTarget)); - break; - } case 0xfc: // delayed note release if (song.delayBehavior==2 || effectVal Date: Fri, 23 Aug 2024 13:17:19 -0700 Subject: [PATCH 04/14] Instrument editor undo (minus FixedQueue change) (#2094) * add undo to instrument editor (check for diffs on the current DivInstrument in insEdit, record them in a stack) * style fixes * accidentally left some logs in * typo in style fix * cheat to avoid warning -Werror=class-memaccess on linux * warn instead of assert on case where MemPatch application would exceed size of target buffer (which should never happen, if you're applying the patch to the same type it was generated from) * instrument editor undo: don't check delta if no user input has come in that could potentially have dirtied the editor * don't run a delta against cached instrument if not insEditOpen * revert fixedQueue to before my 'fix' (if i touch it again i'll add unit tests) * explicitly cast to (DivInstrumentPOD*) when memsetting DivInstrumentPOD in DivInstrument constructor, rather than relying on implicit memory layout * use delete[] instead of free (whoops) * MemPatch/DivInstrumentUndoStep -- remove clear() function (ambiguous whether it should free data, it only existed to set data to null after the swap, so just do that directly now). Also set data to null after delete. * DivInstrument -- fix dangling undo-step pointers being created on duplicate (potentially leading to use-after-free), fix undo-step objects being shamelessly leaked --------- Co-authored-by: Adam Lederer --- src/engine/instrument.cpp | 160 ++++++++++++++++++++++++++++++++++++++ src/engine/instrument.h | 80 +++++++++++++++++-- src/fixedQueue.h | 5 ++ src/gui/doAction.cpp | 4 + src/gui/gui.cpp | 13 ++++ src/gui/gui.h | 7 ++ src/gui/insEdit.cpp | 61 ++++++++++++++- 7 files changed, 323 insertions(+), 7 deletions(-) diff --git a/src/engine/instrument.cpp b/src/engine/instrument.cpp index ad5ebe8e1..4d8a8bf63 100644 --- a/src/engine/instrument.cpp +++ b/src/engine/instrument.cpp @@ -363,6 +363,141 @@ void DivInstrument::writeFeatureFM(SafeWriter* w, bool fui) { FEATURE_END; } +bool MemPatch::calcDiff(const void* pre, const void* post, size_t inputSize) { + bool diffValid=false; + size_t firstDiff=0; + size_t lastDiff=0; + const unsigned char* preBytes=(const unsigned char*)pre; + const unsigned char* postBytes=(const unsigned char*)post; + + // @NOTE: consider/profile using a memcmp==0 check to early-out, if it's potentially faster + // for the common case, which is no change + for (size_t ii=0; iitargetSize) { + logW("MemPatch (offset %d, size %d) exceeds target size (%d), can't apply!",offset,size,targetSize); + return; + } + + unsigned char* targetBytes=(unsigned char*)target; + + // swap this->data and its segment on target + for (size_t ii=0; iiname); + } + podPatch.applyAndReverse((DivInstrumentPOD*)target, sizeof(DivInstrumentPOD)); +} + +bool DivInstrumentUndoStep::makeUndoPatch(size_t processTime_, const DivInstrument* pre, const DivInstrument* post) { + processTime=processTime_; + + // create the patch that will make post into pre + podPatch.calcDiff((const DivInstrumentPOD*)post, (const DivInstrumentPOD*)pre, sizeof(DivInstrumentPOD)); + if (pre->name!=post->name) { + nameValid=true; + name=pre->name; + } + + return nameValid || podPatch.isValid(); +} + +bool DivInstrument::recordUndoStepIfChanged(size_t processTime, const DivInstrument* old) { + DivInstrumentUndoStep step; + + // generate a patch to go back to old + if (step.makeUndoPatch(processTime, old, this)) { + + // make room + if (undoHist.size()>=undoHist.capacity()) { + delete undoHist.front(); + undoHist.pop_front(); + } + + // clear redo + while (!redoHist.empty()) { + delete redoHist.back(); + redoHist.pop_back(); + } + + DivInstrumentUndoStep* stepPtr=new DivInstrumentUndoStep; + *stepPtr=step; + step.podPatch.data=NULL; // don't let it delete the data ptr that's been copied! + undoHist.push_back(stepPtr); + + // logI("DivInstrument::undoHist push (%u off, %u size)", stepPtr->podPatch.offset, stepPtr->podPatch.size); + return true; + } + + return false; +} + +int DivInstrument::undo() { + if (undoHist.empty()) return 0; + + DivInstrumentUndoStep* step=undoHist.back(); + undoHist.pop_back(); + // logI("DivInstrument::undo (%u off, %u size)", step->podPatch.offset, step->podPatch.size); + step->applyAndReverse(this); + + // make room + if (redoHist.size()>=redoHist.capacity()) { + DivInstrumentUndoStep* step=redoHist.front(); + delete step; + redoHist.pop_front(); + } + redoHist.push_back(step); + + return 1; +} + +int DivInstrument::redo() { + if (redoHist.empty()) return 0; + + DivInstrumentUndoStep* step = redoHist.back(); + redoHist.pop_back(); + // logI("DivInstrument::redo (%u off, %u size)", step->podPatch.offset, step->podPatch.size); + step->applyAndReverse(this); + + // make room + if (undoHist.size()>=undoHist.capacity()) { + DivInstrumentUndoStep* step=undoHist.front(); + delete step; + undoHist.pop_front(); + } + undoHist.push_back(step); + + return 1; +} + void DivInstrument::writeMacro(SafeWriter* w, const DivInstrumentMacro& m) { if (!m.len) return; @@ -3327,3 +3462,28 @@ bool DivInstrument::saveDMP(const char* path) { w->finish(); return true; } + +DivInstrument::~DivInstrument() { + // free undoHist/redoHist + while (!undoHist.empty()) { + delete undoHist.back(); + undoHist.pop_back(); + } + while (!redoHist.empty()) { + delete redoHist.back(); + redoHist.pop_back(); + } +} + +DivInstrument::DivInstrument( const DivInstrument& ins ) { + // undo/redo history is specifically not copied + *(DivInstrumentPOD*)this=ins; + name=ins.name; +} + +DivInstrument& DivInstrument::operator=( const DivInstrument& ins ) { + // undo/redo history is specifically not copied + *(DivInstrumentPOD*)this=ins; + name=ins.name; + return *this; +} \ No newline at end of file diff --git a/src/engine/instrument.h b/src/engine/instrument.h index 49b2991d4..43fa691b6 100644 --- a/src/engine/instrument.h +++ b/src/engine/instrument.h @@ -23,8 +23,10 @@ #include "dataErrors.h" #include "../ta-utils.h" #include "../pch.h" +#include "../fixedQueue.h" struct DivSong; +struct DivInstrument; // NOTICE! // before adding new instrument types to this struct, please ask me first. @@ -860,8 +862,7 @@ struct DivInstrumentSID2 { noiseMode(0) {} }; -struct DivInstrument { - String name; +struct DivInstrumentPOD { DivInstrumentType type; DivInstrumentFM fm; DivInstrumentSTD std; @@ -880,6 +881,77 @@ struct DivInstrument { DivInstrumentPowerNoise powernoise; DivInstrumentSID2 sid2; + DivInstrumentPOD() : + type(DIV_INS_FM) { + } +}; + +struct MemPatch { + MemPatch() : + data(NULL) + , offset(0) + , size(0) { + } + + ~MemPatch() { + if (data) { + delete[] data; + data=NULL; + } + } + + bool calcDiff(const void* pre, const void* post, size_t size); + void applyAndReverse(void* target, size_t inputSize); + bool isValid() const { return size>0; } + + unsigned char* data; + size_t offset; + size_t size; +}; + +struct DivInstrumentUndoStep { + DivInstrumentUndoStep() : + name(""), + nameValid(false), + processTime(0) { + } + + MemPatch podPatch; + String name; + bool nameValid; + size_t processTime; + + void applyAndReverse(DivInstrument* target); + bool makeUndoPatch(size_t processTime_, const DivInstrument* pre, const DivInstrument* post); +}; + +struct DivInstrument : DivInstrumentPOD { + String name; + + DivInstrument() : + name("") { + // clear and construct DivInstrumentPOD so it doesn't have any garbage in the padding + memset((unsigned char*)(DivInstrumentPOD*)this, 0, sizeof(DivInstrumentPOD)); + new ((DivInstrumentPOD*)this) DivInstrumentPOD; + } + + ~DivInstrument(); + + /** + * copy/assignment to specifically avoid leaking or dangling pointers to undo step + */ + DivInstrument( const DivInstrument& ins ); + DivInstrument& operator=( const DivInstrument& ins ); + + /** + * undo stuff + */ + FixedQueue undoHist; + FixedQueue redoHist; + bool recordUndoStepIfChanged(size_t processTime, const DivInstrument* old); + int undo(); + int redo(); + /** * these are internal functions. */ @@ -964,9 +1036,5 @@ struct DivInstrument { * @return whether it was successful. */ bool saveDMP(const char* path); - DivInstrument(): - name(""), - type(DIV_INS_FM) { - } }; #endif diff --git a/src/fixedQueue.h b/src/fixedQueue.h index 68f883edc..f67f70946 100644 --- a/src/fixedQueue.h +++ b/src/fixedQueue.h @@ -42,6 +42,7 @@ template struct FixedQueue { void clear(); bool empty(); size_t size(); + size_t capacity(); FixedQueue(): readPos(0), writePos(0) {} @@ -177,4 +178,8 @@ template size_t FixedQueue::size() { return writePos-readPos; } +template size_t FixedQueue::capacity() { + return items-1; +} + #endif diff --git a/src/gui/doAction.cpp b/src/gui/doAction.cpp index 00bfe95ea..9e0ec7fd5 100644 --- a/src/gui/doAction.cpp +++ b/src/gui/doAction.cpp @@ -73,6 +73,8 @@ void FurnaceGUI::doAction(int what) { case GUI_ACTION_UNDO: if (curWindow==GUI_WINDOW_SAMPLE_EDIT) { doUndoSample(); + } else if (curWindow==GUI_WINDOW_INS_EDIT) { + doUndoInstrument(); } else { doUndo(); } @@ -80,6 +82,8 @@ void FurnaceGUI::doAction(int what) { case GUI_ACTION_REDO: if (curWindow==GUI_WINDOW_SAMPLE_EDIT) { doRedoSample(); + } else if (curWindow==GUI_WINDOW_INS_EDIT) { + doRedoInstrument(); } else { doRedo(); } diff --git a/src/gui/gui.cpp b/src/gui/gui.cpp index 254065992..24c198bf5 100644 --- a/src/gui/gui.cpp +++ b/src/gui/gui.cpp @@ -3739,6 +3739,7 @@ bool FurnaceGUI::loop() { ImGui::GetIO().AddKeyEvent(ImGuiKey_Backspace,false); injectBackUp=false; } + while (SDL_PollEvent(&ev)) { WAKE_UP; ImGui_ImplSDL2_ProcessEvent(&ev); @@ -3755,13 +3756,16 @@ bool FurnaceGUI::loop() { } case SDL_MOUSEBUTTONUP: pointUp(ev.button.x,ev.button.y,ev.button.button); + insEditMayBeDirty=true; break; case SDL_MOUSEBUTTONDOWN: pointDown(ev.button.x,ev.button.y,ev.button.button); + insEditMayBeDirty=true; break; case SDL_MOUSEWHEEL: wheelX+=ev.wheel.x; wheelY+=ev.wheel.y; + insEditMayBeDirty=true; break; case SDL_WINDOWEVENT: switch (ev.window.event) { @@ -3838,12 +3842,14 @@ bool FurnaceGUI::loop() { if (!ImGui::GetIO().WantCaptureKeyboard) { keyDown(ev); } + insEditMayBeDirty=true; #ifdef IS_MOBILE injectBackUp=true; #endif break; case SDL_KEYUP: // for now + insEditMayBeDirty=true; break; case SDL_DROPFILE: if (ev.drop.file!=NULL) { @@ -7241,6 +7247,11 @@ bool FurnaceGUI::loop() { willCommit=false; } + // To check for instrument editor modification, we need an up-to-date `insEditMayBeDirty` + // (based on incoming user input events), and we want any possible instrument modifications + // to already have been made. + checkRecordInstrumentUndoStep(); + if (shallDetectScale) { if (--shallDetectScale<1) { if (settings.dpiScale<0.5f) { @@ -8414,6 +8425,8 @@ FurnaceGUI::FurnaceGUI(): localeRequiresChineseTrad(false), localeRequiresKorean(false), prevInsData(NULL), + cachedCurInsPtr(NULL), + insEditMayBeDirty(false), pendingLayoutImport(NULL), pendingLayoutImportLen(0), pendingLayoutImportStep(0), diff --git a/src/gui/gui.h b/src/gui/gui.h index 9c84cacb0..8108c74f0 100644 --- a/src/gui/gui.h +++ b/src/gui/gui.h @@ -2269,6 +2269,9 @@ class FurnaceGUI { std::vector localeExtraRanges; DivInstrument* prevInsData; + DivInstrument cachedCurIns; + DivInstrument* cachedCurInsPtr; + bool insEditMayBeDirty; unsigned char* pendingLayoutImport; size_t pendingLayoutImportLen; @@ -2935,6 +2938,10 @@ class FurnaceGUI { void doUndoSample(); void doRedoSample(); + void checkRecordInstrumentUndoStep(); + void doUndoInstrument(); + void doRedoInstrument(); + void play(int row=0); void setOrder(unsigned char order, bool forced=false); void stop(); diff --git a/src/gui/insEdit.cpp b/src/gui/insEdit.cpp index 99dbf26dc..581251dc7 100644 --- a/src/gui/insEdit.cpp +++ b/src/gui/insEdit.cpp @@ -5250,6 +5250,7 @@ void FurnaceGUI::drawInsEdit() { ImGui::SetNextWindowSizeConstraints(ImVec2(440.0f*dpiScale,400.0f*dpiScale),ImVec2(canvasW,canvasH)); } if (ImGui::Begin("Instrument Editor",&insEditOpen,globalWinFlags|(settings.allowEditDocking?0:ImGuiWindowFlags_NoDocking),_("Instrument Editor"))) { + DivInstrument* ins=NULL; if (curIns==-2) { ImGui::SetCursorPosY(ImGui::GetCursorPosY()+(ImGui::GetContentRegionAvail().y-ImGui::GetFrameHeightWithSpacing()+ImGui::GetStyle().ItemSpacing.y)*0.5f); CENTER_TEXT(_("waiting...")); @@ -5277,6 +5278,7 @@ void FurnaceGUI::drawInsEdit() { curIns=i; wavePreviewInit=true; updateFMPreview=true; + ins = e->song.ins[curIns]; } } ImGui::EndCombo(); @@ -5299,7 +5301,7 @@ void FurnaceGUI::drawInsEdit() { ImGui::EndTable(); } } else { - DivInstrument* ins=e->song.ins[curIns]; + ins=e->song.ins[curIns]; if (updateFMPreview) { renderFMPreview(ins); updateFMPreview=false; @@ -7739,6 +7741,63 @@ void FurnaceGUI::drawInsEdit() { ImGui::EndPopup(); } } + if (ImGui::IsWindowFocused(ImGuiFocusedFlags_ChildWindows)) curWindow=GUI_WINDOW_INS_EDIT; ImGui::End(); } + +void FurnaceGUI::checkRecordInstrumentUndoStep() { + if (insEditOpen && curIns>=0 && curIns<(int)e->song.ins.size()) { + DivInstrument* ins=e->song.ins[curIns]; + + // invalidate cachedCurIns/any possible changes if the cachedCurIns was referencing a different + // instrument altgoether + bool insChanged=ins!=cachedCurInsPtr; + if (insChanged) { + insEditMayBeDirty=false; + cachedCurInsPtr=ins; + cachedCurIns=*ins; + } + + cachedCurInsPtr=ins; + + // check against the last cached to see if diff -- note that modifications to instruments + // happen outside drawInsEdit (e.g. cursor inputs are processed and can directly modify + // macro data). but don't check until we think the user input is complete. + bool delayDiff=ImGui::IsMouseDown(ImGuiMouseButton_Left) || ImGui::IsMouseDown(ImGuiMouseButton_Right) || ImGui::GetIO().WantCaptureKeyboard; + if (!delayDiff && insEditMayBeDirty) { + bool hasChange=ins->recordUndoStepIfChanged(e->processTime, &cachedCurIns); + if (hasChange) { + cachedCurIns=*ins; + } + insEditMayBeDirty=false; + } + } else { + cachedCurInsPtr=NULL; + insEditMayBeDirty=false; + } +} + +void FurnaceGUI::doUndoInstrument() { + if (!insEditOpen) return; + if (curIns<0 || curIns>=(int)e->song.ins.size()) return; + DivInstrument* ins=e->song.ins[curIns]; + // is locking the engine necessary? copied from doUndoSample + e->lockEngine([this,ins]() { + ins->undo(); + cachedCurInsPtr=ins; + cachedCurIns=*ins; + }); +} + +void FurnaceGUI::doRedoInstrument() { + if (!insEditOpen) return; + if (curIns<0 || curIns>=(int)e->song.ins.size()) return; + DivInstrument* ins=e->song.ins[curIns]; + // is locking the engine necessary? copied from doRedoSample + e->lockEngine([this,ins]() { + ins->redo(); + cachedCurInsPtr=ins; + cachedCurIns=*ins; + }); +} From d0f3e0fa5899991f640e616d00983596468c61d0 Mon Sep 17 00:00:00 2001 From: Adam Lederer Date: Thu, 22 Aug 2024 02:43:30 -0700 Subject: [PATCH 05/14] GUI_ACTION_PAT_ABSORB_INSTRUMENT search through prior orders (also set instrument to none if no instrument found --- src/gui/doAction.cpp | 25 +----------------------- src/gui/editing.cpp | 46 ++++++++++++++++++++++++++++++++++++++++++++ src/gui/gui.h | 1 + 3 files changed, 48 insertions(+), 24 deletions(-) diff --git a/src/gui/doAction.cpp b/src/gui/doAction.cpp index 9e0ec7fd5..48edf51e2 100644 --- a/src/gui/doAction.cpp +++ b/src/gui/doAction.cpp @@ -681,30 +681,7 @@ void FurnaceGUI::doAction(int what) { latchNibble=false; break; case GUI_ACTION_PAT_ABSORB_INSTRUMENT: { - DivPattern* pat=e->curPat[cursor.xCoarse].getPattern(e->curOrders->ord[cursor.xCoarse][curOrder],false); - if (!pat) break; - bool foundIns=false; - bool foundOctave=false; - for (int i=cursor.y; i>=0 && !(foundIns && foundOctave); i--) { - // absorb most recent instrument - if (!foundIns && pat->data[i][2] >= 0) { - curIns=pat->data[i][2]; - foundIns=true; - } - // absorb most recent octave (i.e. set curOctave such that the "main row" (QWERTY) of notes - // will result in an octave number equal to the previous note). - if (!foundOctave && pat->data[i][0] != 0) { - // decode octave data (was signed cast to unsigned char) - int octave=pat->data[i][1]; - if (octave>128) octave-=256; - // @NOTE the special handling when note==12, which is really an octave above what's - // stored in the octave data. without this handling, if you press Q, then - // "ABSORB_INSTRUMENT", then Q again, you'd get a different octave! - if (pat->data[i][0]==12) octave++; - curOctave=CLAMP(octave-1, GUI_EDIT_OCTAVE_MIN, GUI_EDIT_OCTAVE_MAX); - foundOctave=true; - } - } + doAbsorbInstrument(); break; } diff --git a/src/gui/editing.cpp b/src/gui/editing.cpp index 1a4449098..8a49f4ecf 100644 --- a/src/gui/editing.cpp +++ b/src/gui/editing.cpp @@ -1823,6 +1823,52 @@ void FurnaceGUI::doExpandSong(int multiplier) { if (e->isPlaying()) e->play(); } +void FurnaceGUI::doAbsorbInstrument() { + bool foundIns=false; + bool foundOctave=false; + auto foundAll = [&]() { return foundIns && foundOctave; }; + + // search this order and all prior until we find all the data we need + int orderIdx=curOrder; + for (; orderIdx>=0 && !foundAll(); orderIdx--) { + DivPattern* pat=e->curPat[cursor.xCoarse].getPattern(e->curOrders->ord[cursor.xCoarse][orderIdx],false); + if (!pat) continue; + + // start on current row when searching current order, but start from end when searching + // prior orders. + int searchStartRow=orderIdx==curOrder ? cursor.y : e->curSubSong->patLen-1; + for (int i=searchStartRow; i>=0 && !foundAll(); i--) { + + // absorb most recent instrument + if (!foundIns && pat->data[i][2] >= 0) { + foundIns=true; + curIns=pat->data[i][2]; + } + + // absorb most recent octave (i.e. set curOctave such that the "main row" (QWERTY) of + // notes will result in an octave number equal to the previous note). + if (!foundOctave && pat->data[i][0] != 0) { + foundOctave=true; + + // decode octave data (was signed cast to unsigned char) + int octave=pat->data[i][1]; + if (octave>128) octave-=256; + + // @NOTE the special handling when note==12, which is really an octave above what's + // stored in the octave data. without this handling, if you press Q, then + // "ABSORB_INSTRUMENT", then Q again, you'd get a different octave! + if (pat->data[i][0]==12) octave++; + curOctave=CLAMP(octave-1, GUI_EDIT_OCTAVE_MIN, GUI_EDIT_OCTAVE_MAX); + } + } + } + + // if no instrument has been set at this point, the only way to match it is to use "none" + if (!foundIns) curIns=-1; + + logD("doAbsorbInstrument -- searched %d orders", curOrder-orderIdx); +} + void FurnaceGUI::doDrag() { int len=dragEnd.xCoarse-dragStart.xCoarse+1; diff --git a/src/gui/gui.h b/src/gui/gui.h index 8108c74f0..83d412b7f 100644 --- a/src/gui/gui.h +++ b/src/gui/gui.h @@ -2922,6 +2922,7 @@ class FurnaceGUI { void doExpand(int multiplier, const SelectionPoint& sStart, const SelectionPoint& sEnd); void doCollapseSong(int divider); void doExpandSong(int multiplier); + void doAbsorbInstrument(); void doUndo(); void doRedo(); void doFind(); From 99ae6a2b38af0b3af4a6110afe27bd058ee4464f Mon Sep 17 00:00:00 2001 From: tildearrow Date: Fri, 23 Aug 2024 17:46:09 -0500 Subject: [PATCH 06/14] VGM export: include song comments also show song comments on mobile --- src/engine/vgmOps.cpp | 3 ++- src/gui/editControls.cpp | 6 +++++- src/gui/gui.h | 2 +- src/gui/songNotes.cpp | 15 ++++++++++----- 4 files changed, 18 insertions(+), 8 deletions(-) diff --git a/src/engine/vgmOps.cpp b/src/engine/vgmOps.cpp index 9529631a1..0ddf7f602 100644 --- a/src/engine/vgmOps.cpp +++ b/src/engine/vgmOps.cpp @@ -2679,7 +2679,8 @@ SafeWriter* DivEngine::saveVGM(bool* sysToExport, bool loop, int version, bool p w->writeWString(ws,false); // japanese author name w->writeS(0); // date w->writeWString(L"Furnace (chiptune tracker)",false); // ripper - w->writeS(0); // notes + ws=utf8To16(song.notes.c_str()); + w->writeWString(ws,false); // notes int gd3Len=w->tell()-gd3Off-12; diff --git a/src/gui/editControls.cpp b/src/gui/editControls.cpp index 34beff48b..40269259e 100644 --- a/src/gui/editControls.cpp +++ b/src/gui/editControls.cpp @@ -508,7 +508,7 @@ void FurnaceGUI::drawMobileControls() { mobileMenuOpen=false; doAction(GUI_ACTION_SAVE_AS); } - + ImGui::SameLine(); if (ImGui::Button(_("Export"))) { doAction(GUI_ACTION_EXPORT); } @@ -533,6 +533,10 @@ void FurnaceGUI::drawMobileControls() { drawSpeed(true); ImGui::EndTabItem(); } + if (ImGui::BeginTabItem(_("Comments"))) { + drawNotes(true); + ImGui::EndTabItem(); + } ImGui::EndTabBar(); } break; diff --git a/src/gui/gui.h b/src/gui/gui.h index 83d412b7f..72436f071 100644 --- a/src/gui/gui.h +++ b/src/gui/gui.h @@ -2832,7 +2832,7 @@ class FurnaceGUI { void drawMemory(); void drawCompatFlags(); void drawPiano(); - void drawNotes(); + void drawNotes(bool asChild=false); void drawChannels(); void drawPatManager(); void drawSysManager(); diff --git a/src/gui/songNotes.cpp b/src/gui/songNotes.cpp index cf55b7aef..15ecb2a82 100644 --- a/src/gui/songNotes.cpp +++ b/src/gui/songNotes.cpp @@ -22,18 +22,23 @@ // NOTE: please don't ask me to enable text wrap. // Dear ImGui doesn't have that feature. D: -void FurnaceGUI::drawNotes() { +void FurnaceGUI::drawNotes(bool asChild) { if (nextWindow==GUI_WINDOW_NOTES) { notesOpen=true; ImGui::SetNextWindowFocus(); nextWindow=GUI_WINDOW_NOTHING; } - if (!notesOpen) return; - if (ImGui::Begin("Song Comments",¬esOpen,globalWinFlags,_("Song Comments"))) { + if (!notesOpen && !asChild) return; + bool began=asChild?ImGui::BeginChild("Song Info##Song Information"):ImGui::Begin("Song Comments",¬esOpen,globalWinFlags,_("Song Comments")); + if (began) { if (ImGui::InputTextMultiline("##SongNotes",&e->song.notes,ImGui::GetContentRegionAvail(),ImGuiInputTextFlags_UndoRedo)) { MARK_MODIFIED; } } - if (ImGui::IsWindowFocused(ImGuiFocusedFlags_ChildWindows)) curWindow=GUI_WINDOW_NOTES; - ImGui::End(); + if (!asChild && ImGui::IsWindowFocused(ImGuiFocusedFlags_ChildWindows)) curWindow=GUI_WINDOW_NOTES; + if (asChild) { + ImGui::EndChild(); + } else { + ImGui::End(); + } } From 5a79a709805694e0c422ac68cffeeae38c82b80e Mon Sep 17 00:00:00 2001 From: tildearrow Date: Fri, 23 Aug 2024 20:10:23 -0500 Subject: [PATCH 07/14] GUI: missing WonderSwan pan macros --- src/gui/gui.cpp | 2 ++ src/gui/insEdit.cpp | 2 ++ 2 files changed, 4 insertions(+) diff --git a/src/gui/gui.cpp b/src/gui/gui.cpp index 24c198bf5..d0310daa5 100644 --- a/src/gui/gui.cpp +++ b/src/gui/gui.cpp @@ -5861,7 +5861,9 @@ bool FurnaceGUI::loop() { MEASURE_BEGIN(popup); centerNextWindow(_("Rendering..."),canvasW,canvasH); + ImGui::SetNextWindowSize(ImVec2(640.0f*dpiScale,0.0f),ImGuiCond_Always); if (ImGui::BeginPopupModal(_("Rendering..."),NULL,ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoMove)) { + WAKE_UP; if(audioExportOptions.mode != DIV_EXPORT_MODE_MANY_CHAN) { ImGui::Text(_("Please wait...")); diff --git a/src/gui/insEdit.cpp b/src/gui/insEdit.cpp index 581251dc7..120eac9e7 100644 --- a/src/gui/insEdit.cpp +++ b/src/gui/insEdit.cpp @@ -7185,6 +7185,8 @@ void FurnaceGUI::drawInsEdit() { macroList.push_back(FurnaceGUIMacroDesc(_("Noise"),&ins->std.dutyMacro,0,8,160,uiColors[GUI_COLOR_MACRO_NOISE])); } macroList.push_back(FurnaceGUIMacroDesc(_("Waveform"),&ins->std.waveMacro,0,waveCount,160,uiColors[GUI_COLOR_MACRO_WAVE],false,NULL,NULL,false,NULL)); + macroList.push_back(FurnaceGUIMacroDesc(_("Panning (left)"),&ins->std.panLMacro,0,15,46,uiColors[GUI_COLOR_MACRO_OTHER],false,NULL)); + macroList.push_back(FurnaceGUIMacroDesc(_("Panning (right)"),&ins->std.panRMacro,0,15,46,uiColors[GUI_COLOR_MACRO_OTHER])); macroList.push_back(FurnaceGUIMacroDesc(_("Pitch"),&ins->std.pitchMacro,-2048,2047,160,uiColors[GUI_COLOR_MACRO_PITCH],true,macroRelativeMode)); macroList.push_back(FurnaceGUIMacroDesc(_("Phase Reset"),&ins->std.phaseResetMacro,0,1,32,uiColors[GUI_COLOR_MACRO_OTHER],false,NULL,NULL,true)); break; From a458e105d9a5853f6bd32fd97ca13f764ae82bc0 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Fri, 23 Aug 2024 20:16:48 -0500 Subject: [PATCH 08/14] VGM export: use QSound ADPCM size --- src/engine/vgmOps.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/engine/vgmOps.cpp b/src/engine/vgmOps.cpp index 0ddf7f602..0050ab6e6 100644 --- a/src/engine/vgmOps.cpp +++ b/src/engine/vgmOps.cpp @@ -2169,8 +2169,8 @@ SafeWriter* DivEngine::saveVGM(bool* sysToExport, bool loop, int version, bool p w->writeI(0); w->write(writeADPCM_Y8950[i]->getSampleMem(0),writeADPCM_Y8950[i]->getSampleMemUsage(0)); } - if (writeQSound[i]!=NULL && writeQSound[i]->getSampleMemUsage()>0) { - unsigned int blockSize=(writeQSound[i]->getSampleMemUsage()+0xffff)&(~0xffff); + if (writeQSound[i]!=NULL && writeQSound[i]->getSampleMemUsage(1)>0) { + unsigned int blockSize=(writeQSound[i]->getSampleMemUsage(1)+0xffff)&(~0xffff); if (blockSize > 0x1000000) { blockSize = 0x1000000; } @@ -2178,7 +2178,7 @@ SafeWriter* DivEngine::saveVGM(bool* sysToExport, bool loop, int version, bool p w->writeC(0x66); w->writeC(0x8F); w->writeI((blockSize+8)|(i*0x80000000)); - w->writeI(writeQSound[i]->getSampleMemCapacity()); + w->writeI(writeQSound[i]->getSampleMemCapacity(1)); w->writeI(0); w->write(writeQSound[i]->getSampleMem(),blockSize); } From bcdad3c53879e9fc0efc1d68ff44e35cfa5238c7 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Sat, 24 Aug 2024 02:47:51 -0500 Subject: [PATCH 09/14] AY: remove all ffff writes --- src/engine/platform/ay.cpp | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/src/engine/platform/ay.cpp b/src/engine/platform/ay.cpp index 4e5357722..11ce83389 100644 --- a/src/engine/platform/ay.cpp +++ b/src/engine/platform/ay.cpp @@ -423,11 +423,11 @@ void DivPlatformAY8910::tick(bool sysTick) { chan[i].tfx.counter = 0; chan[i].tfx.out = 0; if (chan[i].nextPSGMode.val&8) { - if (dumpWrites) addWrite(0xffff0002+(i<<8),0); + //if (dumpWrites) addWrite(0xffff0002+(i<<8),0); if (chan[i].dac.sample<0 || chan[i].dac.sample>=parent->song.sampleLen) { if (dumpWrites) { rWrite(0x08+i,0); - addWrite(0xffff0000+(i<<8),chan[i].dac.sample); + //addWrite(0xffff0000+(i<<8),chan[i].dac.sample); } if (chan[i].dac.setPos) { chan[i].dac.setPos=false; @@ -517,7 +517,7 @@ void DivPlatformAY8910::tick(bool sysTick) { } } chan[i].dac.rate=((double)rate*((sunsoft||clockSel)?8.0:16.0))/(double)(MAX(1,off*chan[i].freq)); - if (dumpWrites) addWrite(0xffff0001+(i<<8),chan[i].dac.rate); + //if (dumpWrites) addWrite(0xffff0001+(i<<8),chan[i].dac.rate); } if (chan[i].freq<0) chan[i].freq=0; if (chan[i].freq>4095) chan[i].freq=4095; @@ -604,12 +604,12 @@ int DivPlatformAY8910::dispatch(DivCommand c) { } if (chan[c.chan].dac.sample<0 || chan[c.chan].dac.sample>=parent->song.sampleLen) { chan[c.chan].dac.sample=-1; - if (dumpWrites) addWrite(0xffff0002+(c.chan<<8),0); + //if (dumpWrites) addWrite(0xffff0002+(c.chan<<8),0); break; } else { if (dumpWrites) { rWrite(0x08+c.chan,0); - addWrite(0xffff0000+(c.chan<<8),chan[c.chan].dac.sample); + //addWrite(0xffff0000+(c.chan<<8),chan[c.chan].dac.sample); } } if (chan[c.chan].dac.setPos) { @@ -637,10 +637,10 @@ int DivPlatformAY8910::dispatch(DivCommand c) { chan[c.chan].dac.sample=12*sampleBank+chan[c.chan].note%12; if (chan[c.chan].dac.sample>=parent->song.sampleLen) { chan[c.chan].dac.sample=-1; - if (dumpWrites) addWrite(0xffff0002+(c.chan<<8),0); + //if (dumpWrites) addWrite(0xffff0002+(c.chan<<8),0); break; } else { - if (dumpWrites) addWrite(0xffff0000+(c.chan<<8),chan[c.chan].dac.sample); + //if (dumpWrites) addWrite(0xffff0000+(c.chan<<8),chan[c.chan].dac.sample); } if (chan[c.chan].dac.setPos) { chan[c.chan].dac.setPos=false; @@ -651,7 +651,7 @@ int DivPlatformAY8910::dispatch(DivCommand c) { chan[c.chan].dac.rate=parent->getSample(chan[c.chan].dac.sample)->rate*2048; if (dumpWrites) { rWrite(0x08+c.chan,0); - addWrite(0xffff0001+(c.chan<<8),chan[c.chan].dac.rate); + //addWrite(0xffff0001+(c.chan<<8),chan[c.chan].dac.rate); } chan[c.chan].dac.furnaceDAC=false; } @@ -686,7 +686,7 @@ int DivPlatformAY8910::dispatch(DivCommand c) { } case DIV_CMD_NOTE_OFF: chan[c.chan].dac.sample=-1; - if (dumpWrites) addWrite(0xffff0002+(c.chan<<8),0); + //if (dumpWrites) addWrite(0xffff0002+(c.chan<<8),0); chan[c.chan].nextPSGMode.val&=~8; chan[c.chan].keyOff=true; chan[c.chan].active=false; @@ -867,7 +867,7 @@ int DivPlatformAY8910::dispatch(DivCommand c) { case DIV_CMD_SAMPLE_POS: chan[c.chan].dac.pos=c.value; chan[c.chan].dac.setPos=true; - if (dumpWrites) addWrite(0xffff0005,chan[c.chan].dac.pos); + //if (dumpWrites) addWrite(0xffff0005,chan[c.chan].dac.pos); break; case DIV_CMD_MACRO_OFF: chan[c.chan].std.mask(c.value,true); From 2982a401104402769d694b134633c7f60758ee72 Mon Sep 17 00:00:00 2001 From: Adam Lederer Date: Sat, 24 Aug 2024 00:59:03 -0700 Subject: [PATCH 10/14] update vol-porta-ending logic to be more readable --- src/engine/cmdStream.cpp | 20 ++++++++++++++++---- src/engine/playback.cpp | 29 +++++++++++++++++++++-------- 2 files changed, 37 insertions(+), 12 deletions(-) diff --git a/src/engine/cmdStream.cpp b/src/engine/cmdStream.cpp index 5f369b211..40542ef98 100644 --- a/src/engine/cmdStream.cpp +++ b/src/engine/cmdStream.cpp @@ -365,10 +365,22 @@ bool DivCSPlayer::tick() { if (sendVolume || chan[i].volSpeed!=0) { chan[i].volume+=chan[i].volSpeed; - if (chan[i].volSpeedTarget!=-1 && (chan[i].volume==chan[i].volSpeedTarget || (chan[i].volume>chan[i].volSpeedTarget)==(chan[i].volSpeed>0))) { - chan[i].volume=chan[i].volSpeedTarget; - chan[i].volSpeed=0; - chan[i].volSpeedTarget=-1; + if (chan[i].volSpeedTarget!=-1) { + bool atTarget=false; + if (chan[i].volSpeed>0) { + atTarget=(chan[i].volume>=chan[i].volSpeedTarget); + } else if (chan[i].volSpeed<0) { + atTarget=(chan[i].volume<=chan[i].volSpeedTarget); + } else { + atTarget=true; + chan[i].volSpeedTarget=chan[i].volume; + } + + if (atTarget) { + chan[i].volume=chan[i].volSpeedTarget; + chan[i].volSpeed=0; + chan[i].volSpeedTarget=-1; + } } if (chan[i].volume<0) { chan[i].volume=0; diff --git a/src/engine/playback.cpp b/src/engine/playback.cpp index 2c7e97e0e..296183f8c 100644 --- a/src/engine/playback.cpp +++ b/src/engine/playback.cpp @@ -1631,14 +1631,27 @@ bool DivEngine::nextTick(bool noAccum, bool inhibitLowLat) { if (chan[i].volSpeed!=0) { chan[i].volume=(chan[i].volume&0xff)|(dispatchCmd(DivCommand(DIV_CMD_GET_VOLUME,i))<<8); chan[i].volume+=chan[i].volSpeed; - if (chan[i].volSpeedTarget!=-1 && (chan[i].volume==chan[i].volSpeedTarget || (chan[i].volume>chan[i].volSpeedTarget)==(chan[i].volSpeed>0))) { - chan[i].volume=chan[i].volSpeedTarget; - chan[i].volSpeed=0; - chan[i].volSpeedTarget=-1; - dispatchCmd(DivCommand(DIV_CMD_HINT_VOLUME,i,chan[i].volume>>8)); - dispatchCmd(DivCommand(DIV_CMD_VOLUME,i,chan[i].volume>>8)); - dispatchCmd(DivCommand(DIV_CMD_HINT_VOL_SLIDE,i,0)); - } else if (chan[i].volume>chan[i].volMax) { + if (chan[i].volSpeedTarget!=-1) { + bool atTarget=false; + if (chan[i].volSpeed>0) { + atTarget=(chan[i].volume>=chan[i].volSpeedTarget); + } else if (chan[i].volSpeed<0) { + atTarget=(chan[i].volume<=chan[i].volSpeedTarget); + } else { + atTarget=true; + chan[i].volSpeedTarget=chan[i].volume; + } + + if (atTarget) { + chan[i].volume=chan[i].volSpeedTarget; + chan[i].volSpeed=0; + chan[i].volSpeedTarget=-1; + dispatchCmd(DivCommand(DIV_CMD_HINT_VOLUME,i,chan[i].volume>>8)); + dispatchCmd(DivCommand(DIV_CMD_VOLUME,i,chan[i].volume>>8)); + dispatchCmd(DivCommand(DIV_CMD_HINT_VOL_SLIDE,i,0)); + } + } + if (chan[i].volume>chan[i].volMax) { chan[i].volume=chan[i].volMax; chan[i].volSpeed=0; chan[i].volSpeedTarget=-1; From 977c464e7bb207d9f4f08a2fa746c233b7858492 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Sat, 24 Aug 2024 03:08:32 -0500 Subject: [PATCH 11/14] GUI: fix audio export progress window --- src/gui/gui.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/gui/gui.cpp b/src/gui/gui.cpp index d0310daa5..27b452697 100644 --- a/src/gui/gui.cpp +++ b/src/gui/gui.cpp @@ -5861,7 +5861,6 @@ bool FurnaceGUI::loop() { MEASURE_BEGIN(popup); centerNextWindow(_("Rendering..."),canvasW,canvasH); - ImGui::SetNextWindowSize(ImVec2(640.0f*dpiScale,0.0f),ImGuiCond_Always); if (ImGui::BeginPopupModal(_("Rendering..."),NULL,ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoMove)) { WAKE_UP; if(audioExportOptions.mode != DIV_EXPORT_MODE_MANY_CHAN) @@ -5924,7 +5923,7 @@ bool FurnaceGUI::loop() { ImGui::Text(_("Channel %d of %d"), curFile + 1, totalFiles); } - ImGui::ProgressBar(curProgress,ImVec2(-FLT_MIN,0), fmt::sprintf("%.2f%%", curProgress * 100.0f).c_str()); + ImGui::ProgressBar(curProgress,ImVec2(320.0f*dpiScale,0), fmt::sprintf("%.2f%%", curProgress * 100.0f).c_str()); if (ImGui::Button(_("Abort"))) { if (e->haltAudioFile()) { From db19b13ce4a660a08d0deb1419fafcbe4e5e06f3 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Sat, 24 Aug 2024 03:08:46 -0500 Subject: [PATCH 12/14] GUI: update credits --- src/gui/about.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/gui/about.cpp b/src/gui/about.cpp index e3093cde3..efa95465b 100644 --- a/src/gui/about.cpp +++ b/src/gui/about.cpp @@ -35,6 +35,7 @@ const char* aboutLine[]={ _N("-- program --"), "tildearrow", _N("A M 4 N (intro tune)"), + "Adam Lederer", "akumanatt", "cam900", "djtuBIG-MaliceX", From 46e48e42eaf0b517f0e69673f0cde1a603a3fa93 Mon Sep 17 00:00:00 2001 From: freq-mod Date: Sat, 24 Aug 2024 11:24:58 +0200 Subject: [PATCH 13/14] backport default sys picker from B --- src/gui/gui.cpp | 4 ++-- src/gui/gui.h | 2 +- src/gui/settings.cpp | 20 ++++++++++++-------- src/gui/sysManager.cpp | 4 ++-- src/gui/sysPicker.cpp | 4 ++-- src/gui/userPresets.cpp | 4 ++-- 6 files changed, 21 insertions(+), 17 deletions(-) diff --git a/src/gui/gui.cpp b/src/gui/gui.cpp index 27b452697..608be1e56 100644 --- a/src/gui/gui.cpp +++ b/src/gui/gui.cpp @@ -4488,7 +4488,7 @@ bool FurnaceGUI::loop() { } else { if (ImGui::BeginMenu(_("add chip..."))) { exitDisabledTimer=1; - DivSystem picked=systemPicker(); + DivSystem picked=systemPicker(false); if (picked!=DIV_SYSTEM_NULL) { if (!e->addSystem(picked)) { showError(fmt::sprintf(_("cannot add chip! (%s)"),e->getLastError())); @@ -4519,7 +4519,7 @@ bool FurnaceGUI::loop() { ImGui::Checkbox(_("Preserve channel positions"),&preserveChanPos); for (int i=0; isong.systemLen; i++) { if (ImGui::BeginMenu(fmt::sprintf("%d. %s##_SYSC%d",i+1,getSystemName(e->song.system[i]),i).c_str())) { - DivSystem picked=systemPicker(); + DivSystem picked=systemPicker(false); if (picked!=DIV_SYSTEM_NULL) { if (e->changeSystem(i,picked,preserveChanPos)) { MARK_MODIFIED; diff --git a/src/gui/gui.h b/src/gui/gui.h index 72436f071..31a287ef9 100644 --- a/src/gui/gui.h +++ b/src/gui/gui.h @@ -2929,7 +2929,7 @@ class FurnaceGUI { void doReplace(); void doDrag(); void editOptions(bool topMenu); - DivSystem systemPicker(); + DivSystem systemPicker(bool full_width); void noteInput(int num, int key, int vol=-1); void valueInput(int num, bool direct=false, int target=-1); void orderInput(int num); diff --git a/src/gui/settings.cpp b/src/gui/settings.cpp index 16414eab0..e2efb91e7 100644 --- a/src/gui/settings.cpp +++ b/src/gui/settings.cpp @@ -1084,15 +1084,19 @@ void FurnaceGUI::drawSettings() { ImGui::PushID(i); ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x-ImGui::CalcTextSize(_("Invert")).x-ImGui::GetFrameHeightWithSpacing()*2.0-ImGui::GetStyle().ItemSpacing.x*2.0); - if (ImGui::BeginCombo("##System",getSystemName(sysID))) { - for (int j=0; availableSystems[j]; j++) { - if (ImGui::Selectable(getSystemName((DivSystem)availableSystems[j]),sysID==availableSystems[j])) { - sysID=(DivSystem)availableSystems[j]; - settings.initialSys.set(fmt::sprintf("id%d",i),(int)e->systemToFileFur(sysID)); - settings.initialSys.set(fmt::sprintf("flags%d",i),""); - settingsChanged=true; - } + if (ImGui::BeginCombo("##System",getSystemName(sysID),ImGuiComboFlags_HeightLargest)) { + + sysID=systemPicker(true); + + if (sysID!=DIV_SYSTEM_NULL) + { + settings.initialSys.set(fmt::sprintf("id%d",i),(int)e->systemToFileFur(sysID)); + settings.initialSys.set(fmt::sprintf("flags%d",i),""); + settingsChanged=true; + + ImGui::CloseCurrentPopup(); } + ImGui::EndCombo(); } diff --git a/src/gui/sysManager.cpp b/src/gui/sysManager.cpp index b4e08fc36..c118c1d77 100644 --- a/src/gui/sysManager.cpp +++ b/src/gui/sysManager.cpp @@ -102,7 +102,7 @@ void FurnaceGUI::drawSysManager() { ImGui::SameLine(); ImGui::Button(_("Change##SysChange")); if (ImGui::BeginPopupContextItem("SysPickerC",ImGuiPopupFlags_MouseButtonLeft)) { - DivSystem picked=systemPicker(); + DivSystem picked=systemPicker(false); if (picked!=DIV_SYSTEM_NULL) { if (e->changeSystem(i,picked,preserveChanPos)) { MARK_MODIFIED; @@ -138,7 +138,7 @@ void FurnaceGUI::drawSysManager() { ImGui::TableNextColumn(); ImGui::Button(ICON_FA_PLUS "##SysAdd"); if (ImGui::BeginPopupContextItem("SysPickerA",ImGuiPopupFlags_MouseButtonLeft)) { - DivSystem picked=systemPicker(); + DivSystem picked=systemPicker(false); if (picked!=DIV_SYSTEM_NULL) { if (!e->addSystem(picked)) { showError(fmt::sprintf(_("cannot add chip! (%s)"),e->getLastError())); diff --git a/src/gui/sysPicker.cpp b/src/gui/sysPicker.cpp index 3eb7f5015..fd0046121 100644 --- a/src/gui/sysPicker.cpp +++ b/src/gui/sysPicker.cpp @@ -23,7 +23,7 @@ #include "guiConst.h" #include -DivSystem FurnaceGUI::systemPicker() { +DivSystem FurnaceGUI::systemPicker(bool full_width) { DivSystem ret=DIV_SYSTEM_NULL; DivSystem hoveredSys=DIV_SYSTEM_NULL; bool reissueSearch=false; @@ -61,7 +61,7 @@ DivSystem FurnaceGUI::systemPicker() { } } } - if (ImGui::BeginTable("SysList",1,ImGuiTableFlags_ScrollY,ImVec2(500.0f*dpiScale,200.0*dpiScale))) { + if (ImGui::BeginTable("SysList",1,ImGuiTableFlags_ScrollY,ImVec2(full_width ? ImGui::GetContentRegionAvail().x : 500.0f*dpiScale,200.0f*dpiScale))) { if (sysSearchQuery.empty()) { // display chip list for (int j=0; curSysSection[j]; j++) { diff --git a/src/gui/userPresets.cpp b/src/gui/userPresets.cpp index 14e6f9d7b..d2999c6cc 100644 --- a/src/gui/userPresets.cpp +++ b/src/gui/userPresets.cpp @@ -392,7 +392,7 @@ void FurnaceGUI::drawUserPresets() { tempID=fmt::sprintf("%s##USystem",getSystemName(chip.sys)); ImGui::Button(tempID.c_str(),ImVec2(ImGui::GetContentRegionAvail().x-ImGui::CalcTextSize(_("Invert")).x-ImGui::GetFrameHeightWithSpacing()*2.0-ImGui::GetStyle().ItemSpacing.x*2.0,0)); if (ImGui::BeginPopupContextItem("SysPickerCU",ImGuiPopupFlags_MouseButtonLeft)) { - DivSystem picked=systemPicker(); + DivSystem picked=systemPicker(false); if (picked!=DIV_SYSTEM_NULL) { chip.sys=picked; mustBake=true; @@ -456,7 +456,7 @@ void FurnaceGUI::drawUserPresets() { ImGui::Button(ICON_FA_PLUS "##SysAddU"); if (ImGui::BeginPopupContextItem("SysPickerU",ImGuiPopupFlags_MouseButtonLeft)) { - DivSystem picked=systemPicker(); + DivSystem picked=systemPicker(false); if (picked!=DIV_SYSTEM_NULL) { preset->orig.push_back(FurnaceGUISysDefChip(picked,1.0f,0.0f,"")); mustBake=true; From 332b449f0eb3463b2a72eccfb6129658c6a740f6 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Sat, 24 Aug 2024 06:03:28 -0500 Subject: [PATCH 14/14] deGNUize --- src/gui/gui.h | 2 +- src/gui/sysPicker.cpp | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/gui/gui.h b/src/gui/gui.h index 31a287ef9..1cdfb4f3f 100644 --- a/src/gui/gui.h +++ b/src/gui/gui.h @@ -2929,7 +2929,7 @@ class FurnaceGUI { void doReplace(); void doDrag(); void editOptions(bool topMenu); - DivSystem systemPicker(bool full_width); + DivSystem systemPicker(bool fullWidth); void noteInput(int num, int key, int vol=-1); void valueInput(int num, bool direct=false, int target=-1); void orderInput(int num); diff --git a/src/gui/sysPicker.cpp b/src/gui/sysPicker.cpp index fd0046121..6d10ac754 100644 --- a/src/gui/sysPicker.cpp +++ b/src/gui/sysPicker.cpp @@ -23,7 +23,7 @@ #include "guiConst.h" #include -DivSystem FurnaceGUI::systemPicker(bool full_width) { +DivSystem FurnaceGUI::systemPicker(bool fullWidth) { DivSystem ret=DIV_SYSTEM_NULL; DivSystem hoveredSys=DIV_SYSTEM_NULL; bool reissueSearch=false; @@ -61,7 +61,7 @@ DivSystem FurnaceGUI::systemPicker(bool full_width) { } } } - if (ImGui::BeginTable("SysList",1,ImGuiTableFlags_ScrollY,ImVec2(full_width ? ImGui::GetContentRegionAvail().x : 500.0f*dpiScale,200.0f*dpiScale))) { + if (ImGui::BeginTable("SysList",1,ImGuiTableFlags_ScrollY,ImVec2(fullWidth ? ImGui::GetContentRegionAvail().x : 500.0f*dpiScale,200.0f*dpiScale))) { if (sysSearchQuery.empty()) { // display chip list for (int j=0; curSysSection[j]; j++) {