From dc87bc489fa55357a47b0aaf0c7cbcb7ae8fb454 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Sat, 4 Oct 2025 18:39:07 -0500 Subject: [PATCH] AY/YM Timer FX bug fixes and improvements pull request #2547 manual merge due to modification of unrelated files according to GitHub's files view Co-authored-by: host12prog --- src/engine/platform/ay.cpp | 61 +++++++++++++++++++++----------------- src/gui/insEdit.cpp | 30 +++++++++++++------ 2 files changed, 54 insertions(+), 37 deletions(-) diff --git a/src/engine/platform/ay.cpp b/src/engine/platform/ay.cpp index 30e0e26d3..00e02905b 100644 --- a/src/engine/platform/ay.cpp +++ b/src/engine/platform/ay.cpp @@ -166,17 +166,8 @@ void DivPlatformAY8910::runTFX(int runRate, int advance) { if (runRate!=0) counterRatio=(double)rate/(double)runRate; int timerPeriod, output; for (int i=0; i<3; i++) { - if (chan[i].active && (chan[i].curPSGMode.val&16) && !(chan[i].curPSGMode.val&8) && chan[i].tfx.mode!=-1) { + if (chan[i].active && (chan[i].curPSGMode.val&16) && !(chan[i].curPSGMode.val&8)) { if (chan[i].tfx.mode == -1 && !isMuted[i]) { - /* - bug: if in the timer FX macro the user enables - and then disables PWM while there is no volume macro - there is now a random chance that the resulting output - is silent or has volume set incorrectly - i've tried to implement a fix, but it seems to be - ineffective, so... - TODO: actually implement a proper fix - */ if (intellivision && chan[i].curPSGMode.getEnvelope()) { immWrite(0x08+i,(chan[i].outVol&0xc)<<2); continue; @@ -186,12 +177,40 @@ void DivPlatformAY8910::runTFX(int runRate, int advance) { } } chan[i].tfx.counter += counterRatio; - if (chan[i].tfx.counter >= chan[i].tfx.period && chan[i].tfx.mode == 0) { + if (chan[i].tfx.counter >= chan[i].tfx.period) { chan[i].tfx.counter -= chan[i].tfx.period; - chan[i].tfx.out ^= 1; + switch (chan[i].tfx.mode) { + case 0: + // pwm + // we will handle the modulator gen after this switch... if we don't, crackling happens + chan[i].tfx.out ^= 1; + break; + case 1: + // syncbuzzer + if (!isMuted[i]) { + if (intellivision && chan[i].curPSGMode.getEnvelope()) { + immWrite(0x08 + i, (chan[i].outVol & 0xc) << 2); + } + else { + immWrite(0x08 + i, (chan[i].outVol & 15) | ((chan[i].curPSGMode.getEnvelope()) << 2)); + } + } + if (intellivision && selCore) { + immWrite(0xa, ayEnvMode); + } + else { + immWrite(0xd, ayEnvMode); + } + break; + case 2: + default: + // unimplemented, or invalid effects here + break; + } + } + if (chan[i].tfx.mode == 0) { + // pwm output = ((chan[i].tfx.out) ? chan[i].outVol : (chan[i].tfx.lowBound-(15-chan[i].outVol))); - // TODO: fix this stupid crackling noise that happens - // everytime the volume changes output = (output <= 0) ? 0 : output; // underflow output = (output >= 15) ? 15 : output; // overflow output &= 15; // i don't know if i need this but i'm too scared to remove it @@ -204,20 +223,6 @@ void DivPlatformAY8910::runTFX(int runRate, int advance) { } } } - if (chan[i].tfx.counter >= chan[i].tfx.period && chan[i].tfx.mode == 1) { - chan[i].tfx.counter -= chan[i].tfx.period; - if (!isMuted[i]) { - // TODO: ??????? - if (intellivision && selCore) { - immWrite(0xa, ayEnvMode); - } else { - immWrite(0xd, ayEnvMode); - } - } - } - if (chan[i].tfx.counter >= chan[i].tfx.period && chan[i].tfx.mode == 2) { - chan[i].tfx.counter -= chan[i].tfx.period; - } } if (chan[i].tfx.num > 0) { timerPeriod = chan[i].freq*chan[i].tfx.den/chan[i].tfx.num; diff --git a/src/gui/insEdit.cpp b/src/gui/insEdit.cpp index 60a23187d..667d99ff2 100644 --- a/src/gui/insEdit.cpp +++ b/src/gui/insEdit.cpp @@ -838,6 +838,22 @@ String macroSoundUnitWaves(int id, float val, void* u) { return fmt::sprintf("%d: %s",id,label); } +String macroTFXModes(int id, float val, void* u) { + switch (((int)val)&3) { + case 0: + return _("Disabled"); + case 1: + return _("PWM"); + case 2: + return _("SyncBuzzer"); + case 3: + return _("Reserved"); + default: + return "???"; + } + return "???"; +} + String macroSID3SpecialWaves(int id, float val, void* u) { if ((int)val<0 || (int)val>=SID3_NUM_SPECIAL_WAVES) return "???"; @@ -2099,12 +2115,12 @@ void FurnaceGUI::drawMacroEdit(FurnaceGUIMacroDesc& i, int totalFit, float avail } } ImGui::PushStyleVar(ImGuiStyleVar_FramePadding,ImVec2(0.0f,0.0f)); - if (MACRO_VZOOM<1) { if (i.macro->macroType==DIV_MACRO_ARP || i.isArp) { MACRO_VZOOM=24; MACRO_VSCROLL=120-12; - } else if (i.macro->macroType==DIV_MACRO_PITCH || i.isPitch) { + } + else if ((i.macro->macroType == DIV_MACRO_PITCH || i.isPitch) || (i.macro->macroType == DIV_MACRO_EX7 && i.isPitch)) { MACRO_VZOOM=128; MACRO_VSCROLL=2048-64; } else { @@ -8762,16 +8778,12 @@ void FurnaceGUI::drawInsEdit() { { if (ImGui::BeginTabItem(_("Timer Macros"))) { - ImGui::Text(_("warning: timer effects are not supported by VGM export!")); - macroList.push_back(FurnaceGUIMacroDesc(_("Timer FX"),&ins->std.ex6Macro,0,3,64,uiColors[GUI_COLOR_MACRO_OTHER])); - macroList.push_back(FurnaceGUIMacroDesc(_("TFX Offset"),&ins->std.ex7Macro,-2048,2047,160,uiColors[GUI_COLOR_MACRO_PITCH],true)); + ImGui::Text(_("warning: timer effects require direct stream mode to be enabled during VGM export!")); + macroList.push_back(FurnaceGUIMacroDesc(_("Timer FX"),&ins->std.ex6Macro,0,2,64,uiColors[GUI_COLOR_MACRO_OTHER],false,NULL,macroTFXModes)); + macroList.push_back(FurnaceGUIMacroDesc(_("Timer Offset"),&ins->std.ex7Macro,-2048,2047,160,uiColors[GUI_COLOR_MACRO_PITCH],true,macroRelativeMode,NULL,false,NULL,false,NULL,false,true)); macroList.push_back(FurnaceGUIMacroDesc(_("Timer Num"),&ins->std.ex8Macro,0,15,64,uiColors[GUI_COLOR_MACRO_OTHER])); macroList.push_back(FurnaceGUIMacroDesc(_("Timer Den"),&ins->std.fmsMacro,0,15,64,uiColors[GUI_COLOR_MACRO_OTHER])); macroList.push_back(FurnaceGUIMacroDesc(_("PWM Boundary"),&ins->std.amsMacro,0,15,64,uiColors[GUI_COLOR_MACRO_OTHER])); - // workaround, because the gui will not set - // zoom or scroll if we're not in macros tab - ins->temp.vZoom[DIV_MACRO_EX7]=128; - ins->temp.vScroll[DIV_MACRO_EX7]=2048-64; drawMacros(macroList,macroEditStateMacros,ins); ImGui::EndTabItem(); }