diff --git a/CHANGES b/CHANGES index 4aacbb2bb..f0747acad 100644 --- a/CHANGES +++ b/CHANGES @@ -1,6 +1,7 @@ 0.3.0: (Future) Features: - Ability to hide individual background layers, or OBJs + - Ability to mute individual audio channels Bugfixes: - GBA: Fix timers not updating timing when writing to only the reload register - All: Fix sanitize-deb script not cleaning up after itself diff --git a/src/gba/audio.c b/src/gba/audio.c index 974132801..90d0bab20 100644 --- a/src/gba/audio.c +++ b/src/gba/audio.c @@ -41,6 +41,13 @@ void GBAAudioInit(struct GBAAudio* audio, size_t samples) { #endif CircleBufferInit(&audio->chA.fifo, GBA_AUDIO_FIFO_SIZE); CircleBufferInit(&audio->chB.fifo, GBA_AUDIO_FIFO_SIZE); + + audio->forceDisableCh[0] = false; + audio->forceDisableCh[1] = false; + audio->forceDisableCh[2] = false; + audio->forceDisableCh[3] = false; + audio->forceDisableChA = false; + audio->forceDisableChB = false; } void GBAAudioReset(struct GBAAudio* audio) { @@ -750,55 +757,67 @@ static void _sample(struct GBAAudio* audio) { int16_t sampleRight = 0; int psgShift = 5 - audio->volume; - if (audio->ch1Left) { - sampleLeft += audio->ch1.sample; + if (audio->playingCh1 && !audio->forceDisableCh[0]) { + if (audio->ch1Left) { + sampleLeft += audio->ch1.sample; + } + + if (audio->ch1Right) { + sampleRight += audio->ch1.sample; + } } - if (audio->ch1Right) { - sampleRight += audio->ch1.sample; + if (audio->playingCh2 && !audio->forceDisableCh[1]) { + if (audio->ch2Left) { + sampleLeft += audio->ch2.sample; + } + + if (audio->ch2Right) { + sampleRight += audio->ch2.sample; + } } - if (audio->ch2Left) { - sampleLeft += audio->ch2.sample; + if (audio->playingCh3 && !audio->forceDisableCh[2]) { + if (audio->ch3Left) { + sampleLeft += audio->ch3.sample; + } + + if (audio->ch3Right) { + sampleRight += audio->ch3.sample; + } } - if (audio->ch2Right) { - sampleRight += audio->ch2.sample; - } + if (audio->playingCh4 && !audio->forceDisableCh[3]) { + if (audio->ch4Left) { + sampleLeft += audio->ch4.sample; + } - if (audio->ch3Left) { - sampleLeft += audio->ch3.sample; - } - - if (audio->ch3Right) { - sampleRight += audio->ch3.sample; - } - - if (audio->ch4Left) { - sampleLeft += audio->ch4.sample; - } - - if (audio->ch4Right) { - sampleRight += audio->ch4.sample; + if (audio->ch4Right) { + sampleRight += audio->ch4.sample; + } } sampleLeft = (sampleLeft * (1 + audio->volumeLeft)) >> psgShift; sampleRight = (sampleRight * (1 + audio->volumeRight)) >> psgShift; - if (audio->chALeft) { - sampleLeft += (audio->chA.sample << 2) >> !audio->volumeChA; + if (!audio->forceDisableChA) { + if (audio->chALeft) { + sampleLeft += (audio->chA.sample << 2) >> !audio->volumeChA; + } + + if (audio->chARight) { + sampleRight += (audio->chA.sample << 2) >> !audio->volumeChA; + } } - if (audio->chARight) { - sampleRight += (audio->chA.sample << 2) >> !audio->volumeChA; - } + if (!audio->forceDisableChB) { + if (audio->chBLeft) { + sampleLeft += (audio->chB.sample << 2) >> !audio->volumeChB; + } - if (audio->chBLeft) { - sampleLeft += (audio->chB.sample << 2) >> !audio->volumeChB; - } - - if (audio->chBRight) { - sampleRight += (audio->chB.sample << 2) >> !audio->volumeChB; + if (audio->chBRight) { + sampleRight += (audio->chB.sample << 2) >> !audio->volumeChB; + } } sampleLeft = _applyBias(audio, sampleLeft); diff --git a/src/gba/audio.h b/src/gba/audio.h index 91cc84b1b..52662a984 100644 --- a/src/gba/audio.h +++ b/src/gba/audio.h @@ -236,6 +236,10 @@ struct GBAAudio { int32_t nextSample; int32_t sampleInterval; + + bool forceDisableCh[4]; + bool forceDisableChA; + bool forceDisableChB; }; struct GBAStereoSample { diff --git a/src/platform/qt/Window.cpp b/src/platform/qt/Window.cpp index 01e21d221..b1c214a94 100644 --- a/src/platform/qt/Window.cpp +++ b/src/platform/qt/Window.cpp @@ -818,6 +818,31 @@ void Window::setupMenu(QMenuBar* menubar) { m_gameActions.append(enableObj); addControlledAction(videoLayers, enableObj, "enableOBJ"); + QMenu* audioChannels = avMenu->addMenu(tr("Audio channels")); + + for (int i = 0; i < 4; ++i) { + QAction* enableCh = new QAction(tr("Channel %0").arg(i + 1), audioChannels); + enableCh->setCheckable(true); + enableCh->setChecked(true); + connect(enableCh, &QAction::triggered, [this, i](bool enable) { m_controller->thread()->gba->audio.forceDisableCh[i] = !enable; }); + m_gameActions.append(enableCh); + addControlledAction(audioChannels, enableCh, QString("enableCh%0").arg(i + 1)); + } + + QAction* enableChA = new QAction(tr("Channel A"), audioChannels); + enableChA->setCheckable(true); + enableChA->setChecked(true); + connect(enableChA, &QAction::triggered, [this, i](bool enable) { m_controller->thread()->gba->audio.forceDisableChA = !enable; }); + m_gameActions.append(enableChA); + addControlledAction(audioChannels, enableChA, QString("enableChA")); + + QAction* enableChB = new QAction(tr("Channel B"), audioChannels); + enableChB->setCheckable(true); + enableChB->setChecked(true); + connect(enableChB, &QAction::triggered, [this, i](bool enable) { m_controller->thread()->gba->audio.forceDisableChB = !enable; }); + m_gameActions.append(enableChB); + addControlledAction(audioChannels, enableChB, QString("enableChB")); + QMenu* toolsMenu = menubar->addMenu(tr("&Tools")); m_shortcutController->addMenu(toolsMenu); QAction* viewLogs = new QAction(tr("View &logs..."), toolsMenu);