From f5ce58ca7f818bfc7100929381324b86923ae646 Mon Sep 17 00:00:00 2001 From: madeline Date: Sat, 4 Apr 2026 01:38:28 -0700 Subject: [PATCH 1/4] reverb proof of concept --- CMakeLists.txt | 8 +- include/dusk/settings.h | 2 +- libs/freeverb/CMakeLists.txt | 13 ++ libs/freeverb/allpass.cpp | 36 +++++ libs/freeverb/allpass.hpp | 48 ++++++ libs/freeverb/comb.cpp | 48 ++++++ libs/freeverb/comb.hpp | 55 +++++++ libs/freeverb/denormals.h | 15 ++ libs/freeverb/revmodel.cpp | 252 +++++++++++++++++++++++++++++++ libs/freeverb/revmodel.hpp | 87 +++++++++++ libs/freeverb/tuning.h | 60 ++++++++ src/dusk/audio/DuskDsp.cpp | 54 +++++-- src/dusk/audio/DuskDsp.hpp | 4 + src/dusk/imgui/ImGuiMenuGame.cpp | 4 +- src/dusk/settings.cpp | 1 + 15 files changed, 668 insertions(+), 19 deletions(-) create mode 100644 libs/freeverb/CMakeLists.txt create mode 100644 libs/freeverb/allpass.cpp create mode 100644 libs/freeverb/allpass.hpp create mode 100644 libs/freeverb/comb.cpp create mode 100644 libs/freeverb/comb.hpp create mode 100644 libs/freeverb/denormals.h create mode 100644 libs/freeverb/revmodel.cpp create mode 100644 libs/freeverb/revmodel.hpp create mode 100644 libs/freeverb/tuning.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 248dfcf6b1..272ff76db3 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -17,6 +17,8 @@ set(AURORA_ENABLE_DVD ON CACHE BOOL "Enable DVD API support" FORCE) set(AURORA_ENABLE_CARD ON CACHE BOOL "Enable CARD API support" FORCE) add_subdirectory(extern/aurora EXCLUDE_FROM_ALL) +add_subdirectory(libs/freeverb) + option(DUSK_BUILD_WARNINGS "If off, compiler warnings will be suppressed") option(DUSK_SELECTED_OPT "If on, selected parts of the project will be compiled with optimizations on Debug, intending to make the game run at 30 FPS. Note for MSVC: you will need to remove '/RTC1' from your debug flags in CMake.") option(DUSK_MOVIE_SUPPORT "If on, compile against libjpeg-turbo to enable THP file decoding" ON) @@ -108,14 +110,14 @@ target_include_directories(game_debug PUBLIC extern ${CMAKE_SOURCE_DIR}/build/${DUSK_TP_VERSION}/include build/${DUSK_TP_VERSION}/include) -target_link_libraries(game_debug PUBLIC aurora::core aurora::gx aurora::gd aurora::si aurora::vi aurora::pad aurora::mtx aurora::os aurora::dvd aurora::card) +target_link_libraries(game_debug PUBLIC aurora::core aurora::gx aurora::gd aurora::si aurora::vi aurora::pad aurora::mtx aurora::os aurora::dvd aurora::card freeverb) set(CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS ON) add_library(game SHARED ${DOLZEL_FILES} ${Z2AUDIOLIB_FILES} ${SSYSTEM_FILES} ${JSYSTEM_FILES} ${REL_FILES} ${DUSK_FILES} ${DOLPHIN_FILES} src/dusk/imgui/ImGuiStubLog.cpp src/dusk/imgui/ImGuiAudio.cpp) -target_link_libraries(game PRIVATE game_debug cxxopts::cxxopts absl::flat_hash_map) +target_link_libraries(game PRIVATE game_debug cxxopts::cxxopts absl::flat_hash_map freeverb) if (DUSK_MOVIE_SUPPORT_REAL) if (TARGET libjpeg-turbo::turbojpeg-static) message(STATUS "dusk: Linking against libjpeg-turbo static library") @@ -132,7 +134,7 @@ target_precompile_headers(game PRIVATE "$<$:${CMAKE_SOURCE add_executable(dusk src/dusk/main.cpp) target_compile_definitions(dusk PRIVATE TARGET_PC AVOID_UB=1 VERSION=0) target_include_directories(dusk PRIVATE include) -target_link_libraries(dusk PRIVATE game aurora::main) +target_link_libraries(dusk PRIVATE game aurora::main freeverb) add_custom_command(TARGET dusk POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy_directory diff --git a/include/dusk/settings.h b/include/dusk/settings.h index daba1287d8..64fee289ba 100644 --- a/include/dusk/settings.h +++ b/include/dusk/settings.h @@ -20,6 +20,7 @@ struct UserSettings { float subMusicVolume; float soundEffectsVolume; float fanfareVolume; + bool enableReverb; } audio; // Game settings @@ -68,4 +69,3 @@ TransientSettings& getTransientSettings(); } #endif // DUSK_CONFIG_H - diff --git a/libs/freeverb/CMakeLists.txt b/libs/freeverb/CMakeLists.txt new file mode 100644 index 0000000000..c4f4d4ed95 --- /dev/null +++ b/libs/freeverb/CMakeLists.txt @@ -0,0 +1,13 @@ +cmake_minimum_required(VERSION 3.10) + +project(freeverb LANGUAGES CXX) + +add_library(freeverb + comb.cpp + allpass.cpp + revmodel.cpp +) + +target_include_directories(freeverb PUBLIC + ${CMAKE_CURRENT_SOURCE_DIR} +) diff --git a/libs/freeverb/allpass.cpp b/libs/freeverb/allpass.cpp new file mode 100644 index 0000000000..5d80eda2bd --- /dev/null +++ b/libs/freeverb/allpass.cpp @@ -0,0 +1,36 @@ +// Allpass filter implementation +// +// Written by Jezar at Dreampoint, June 2000 +// http://www.dreampoint.co.uk +// This code is public domain + +#include "allpass.hpp" + +allpass::allpass() +{ + bufidx = 0; +} + +void allpass::setbuffer(float *buf, int size) +{ + buffer = buf; + bufsize = size; +} + +void allpass::mute() +{ + for (int i=0; i=bufsize) bufidx = 0; + + return output; +} + +#endif//_allpass + +//ends \ No newline at end of file diff --git a/libs/freeverb/comb.cpp b/libs/freeverb/comb.cpp new file mode 100644 index 0000000000..c05f5069c8 --- /dev/null +++ b/libs/freeverb/comb.cpp @@ -0,0 +1,48 @@ +// Comb filter implementation +// +// Written by Jezar at Dreampoint, June 2000 +// http://www.dreampoint.co.uk +// This code is public domain + +#include "comb.hpp" + +comb::comb() +{ + filterstore = 0; + bufidx = 0; +} + +void comb::setbuffer(float *buf, int size) +{ + buffer = buf; + bufsize = size; +} + +void comb::mute() +{ + for (int i=0; i=bufsize) bufidx = 0; + + return output; +} + +#endif //_comb_ + +//ends diff --git a/libs/freeverb/denormals.h b/libs/freeverb/denormals.h new file mode 100644 index 0000000000..f871412714 --- /dev/null +++ b/libs/freeverb/denormals.h @@ -0,0 +1,15 @@ +// Macro for killing denormalled numbers +// +// Written by Jezar at Dreampoint, June 2000 +// http://www.dreampoint.co.uk +// Based on IS_DENORMAL macro by Jon Watte +// This code is public domain + +#ifndef _denormals_ +#define _denormals_ + +#define undenormalise(sample) if(((*(unsigned int*)&sample)&0x7f800000)==0) sample=0.0f + +#endif//_denormals_ + +//ends diff --git a/libs/freeverb/revmodel.cpp b/libs/freeverb/revmodel.cpp new file mode 100644 index 0000000000..feaa3bc04c --- /dev/null +++ b/libs/freeverb/revmodel.cpp @@ -0,0 +1,252 @@ +// Reverb model implementation +// +// Written by Jezar at Dreampoint, June 2000 +// http://www.dreampoint.co.uk +// This code is public domain + +#include "revmodel.hpp" + +revmodel::revmodel() +{ + // Tie the components to their buffers + combL[0].setbuffer(bufcombL1,combtuningL1); + combR[0].setbuffer(bufcombR1,combtuningR1); + combL[1].setbuffer(bufcombL2,combtuningL2); + combR[1].setbuffer(bufcombR2,combtuningR2); + combL[2].setbuffer(bufcombL3,combtuningL3); + combR[2].setbuffer(bufcombR3,combtuningR3); + combL[3].setbuffer(bufcombL4,combtuningL4); + combR[3].setbuffer(bufcombR4,combtuningR4); + combL[4].setbuffer(bufcombL5,combtuningL5); + combR[4].setbuffer(bufcombR5,combtuningR5); + combL[5].setbuffer(bufcombL6,combtuningL6); + combR[5].setbuffer(bufcombR6,combtuningR6); + combL[6].setbuffer(bufcombL7,combtuningL7); + combR[6].setbuffer(bufcombR7,combtuningR7); + combL[7].setbuffer(bufcombL8,combtuningL8); + combR[7].setbuffer(bufcombR8,combtuningR8); + allpassL[0].setbuffer(bufallpassL1,allpasstuningL1); + allpassR[0].setbuffer(bufallpassR1,allpasstuningR1); + allpassL[1].setbuffer(bufallpassL2,allpasstuningL2); + allpassR[1].setbuffer(bufallpassR2,allpasstuningR2); + allpassL[2].setbuffer(bufallpassL3,allpasstuningL3); + allpassR[2].setbuffer(bufallpassR3,allpasstuningR3); + allpassL[3].setbuffer(bufallpassL4,allpasstuningL4); + allpassR[3].setbuffer(bufallpassR4,allpasstuningR4); + + // Set default values + allpassL[0].setfeedback(0.5f); + allpassR[0].setfeedback(0.5f); + allpassL[1].setfeedback(0.5f); + allpassR[1].setfeedback(0.5f); + allpassL[2].setfeedback(0.5f); + allpassR[2].setfeedback(0.5f); + allpassL[3].setfeedback(0.5f); + allpassR[3].setfeedback(0.5f); + setwet(initialwet); + setroomsize(initialroom); + setdry(initialdry); + setdamp(initialdamp); + setwidth(initialwidth); + setmode(initialmode); + + // Buffer will be full of rubbish - so we MUST mute them + mute(); +} + +void revmodel::mute() +{ + if (getmode() >= freezemode) + return; + + for (int i=0;i 0) + { + outL = outR = 0; + input = (*inputL + *inputR) * gain; + + // Accumulate comb filters in parallel + for(int i=0; i 0) + { + outL = outR = 0; + input = (*inputL + *inputR) * gain; + + // Accumulate comb filters in parallel + for(int i=0; i= freezemode) + { + roomsize1 = 1; + damp1 = 0; + gain = muted; + } + else + { + roomsize1 = roomsize; + damp1 = damp; + gain = fixedgain; + } + + for(i=0; i= freezemode) + return 1; + else + return 0; +} + +//ends diff --git a/libs/freeverb/revmodel.hpp b/libs/freeverb/revmodel.hpp new file mode 100644 index 0000000000..10fe7c67d5 --- /dev/null +++ b/libs/freeverb/revmodel.hpp @@ -0,0 +1,87 @@ +// Reverb model declaration +// +// Written by Jezar at Dreampoint, June 2000 +// http://www.dreampoint.co.uk +// This code is public domain + +#ifndef _revmodel_ +#define _revmodel_ + +#include "comb.hpp" +#include "allpass.hpp" +#include "tuning.h" + +class revmodel +{ +public: + revmodel(); + void mute(); + void processmix(float *inputL, float *inputR, float *outputL, float *outputR, long numsamples, int skip); + void processreplace(float *inputL, float *inputR, float *outputL, float *outputR, long numsamples, int skip); + void setroomsize(float value); + float getroomsize(); + void setdamp(float value); + float getdamp(); + void setwet(float value); + float getwet(); + void setdry(float value); + float getdry(); + void setwidth(float value); + float getwidth(); + void setmode(float value); + float getmode(); +private: + void update(); +private: + float gain; + float roomsize,roomsize1; + float damp,damp1; + float wet,wet1,wet2; + float dry; + float width; + float mode; + + // The following are all declared inline + // to remove the need for dynamic allocation + // with its subsequent error-checking messiness + + // Comb filters + comb combL[numcombs]; + comb combR[numcombs]; + + // Allpass filters + allpass allpassL[numallpasses]; + allpass allpassR[numallpasses]; + + // Buffers for the combs + float bufcombL1[combtuningL1]; + float bufcombR1[combtuningR1]; + float bufcombL2[combtuningL2]; + float bufcombR2[combtuningR2]; + float bufcombL3[combtuningL3]; + float bufcombR3[combtuningR3]; + float bufcombL4[combtuningL4]; + float bufcombR4[combtuningR4]; + float bufcombL5[combtuningL5]; + float bufcombR5[combtuningR5]; + float bufcombL6[combtuningL6]; + float bufcombR6[combtuningR6]; + float bufcombL7[combtuningL7]; + float bufcombR7[combtuningR7]; + float bufcombL8[combtuningL8]; + float bufcombR8[combtuningR8]; + + // Buffers for the allpasses + float bufallpassL1[allpasstuningL1]; + float bufallpassR1[allpasstuningR1]; + float bufallpassL2[allpasstuningL2]; + float bufallpassR2[allpasstuningR2]; + float bufallpassL3[allpasstuningL3]; + float bufallpassR3[allpasstuningR3]; + float bufallpassL4[allpasstuningL4]; + float bufallpassR4[allpasstuningR4]; +}; + +#endif//_revmodel_ + +//ends \ No newline at end of file diff --git a/libs/freeverb/tuning.h b/libs/freeverb/tuning.h new file mode 100644 index 0000000000..baaa9ce004 --- /dev/null +++ b/libs/freeverb/tuning.h @@ -0,0 +1,60 @@ +// Reverb model tuning values +// +// Written by Jezar at Dreampoint, June 2000 +// http://www.dreampoint.co.uk +// This code is public domain + +#ifndef _tuning_ +#define _tuning_ + +const int numcombs = 8; +const int numallpasses = 4; +const float muted = 0; +const float fixedgain = 0.015f; +const float scalewet = 3; +const float scaledry = 2; +const float scaledamp = 0.4f; +const float scaleroom = 0.28f; +const float offsetroom = 0.7f; +const float initialroom = 0.5f; +const float initialdamp = 0.5f; +const float initialwet = 1/scalewet; +const float initialdry = 0; +const float initialwidth = 1; +const float initialmode = 0; +const float freezemode = 0.5f; +const int stereospread = 23; + +// These values assume 44.1KHz sample rate +// they will probably be OK for 48KHz sample rate +// but would need scaling for 96KHz (or other) sample rates. +// The values were obtained by listening tests. +const int combtuningL1 = 1116; +const int combtuningR1 = 1116+stereospread; +const int combtuningL2 = 1188; +const int combtuningR2 = 1188+stereospread; +const int combtuningL3 = 1277; +const int combtuningR3 = 1277+stereospread; +const int combtuningL4 = 1356; +const int combtuningR4 = 1356+stereospread; +const int combtuningL5 = 1422; +const int combtuningR5 = 1422+stereospread; +const int combtuningL6 = 1491; +const int combtuningR6 = 1491+stereospread; +const int combtuningL7 = 1557; +const int combtuningR7 = 1557+stereospread; +const int combtuningL8 = 1617; +const int combtuningR8 = 1617+stereospread; +const int allpasstuningL1 = 556; +const int allpasstuningR1 = 556+stereospread; +const int allpasstuningL2 = 441; +const int allpasstuningR2 = 441+stereospread; +const int allpasstuningL3 = 341; +const int allpasstuningR3 = 341+stereospread; +const int allpasstuningL4 = 225; +const int allpasstuningR4 = 225+stereospread; + +#endif//_tuning_ + +//ends + diff --git a/src/dusk/audio/DuskDsp.cpp b/src/dusk/audio/DuskDsp.cpp index 7cf1c44cd8..fd1ae66620 100644 --- a/src/dusk/audio/DuskDsp.cpp +++ b/src/dusk/audio/DuskDsp.cpp @@ -19,6 +19,7 @@ ChannelAuxData dusk::audio::ChannelAux[DSP_CHANNELS] = {}; f32 dusk::audio::MasterVolume = 1.0f; f32 dusk::audio::PrevMasterVolume = 1.0f; +bool dusk::audio::EnableReverb = true; /** * Validate that a DSP channel's format is actually something we know how to play. @@ -112,32 +113,47 @@ void dusk::audio::DspRender(OutputSubframe& subframe) { auto& channel = channels[i]; auto& channelAux = ChannelAux[i]; - if (!channel.mIsActive) { - continue; - } + bool skipRender = false; - if (channel.mPauseFlag) { + if (!channel.mIsActive) { + skipRender = true; + } + else if (channel.mPauseFlag) { // Not really sure what the practical difference between pause and // deactivation is. Either avoids clearing state or allows the DSP to avoid popping? - continue; + skipRender = true; } - - if (channel.mForcedStop) { + else if (channel.mForcedStop) { channel.mIsFinished = true; - continue; + skipRender = true; } - - if (channel.mWaveAramAddress == 0) { + else if (channel.mWaveAramAddress == 0) { // I think these are oscillator channels? Not backed by audio. // No idea how to implement these yet, so skip them. channel.mIsFinished = true; - continue; + skipRender = true; } - ValidateChannel(channel); - OutputSubframe channelSubframe = {}; - RenderChannel(channel, channelAux, channelSubframe); + + if (!skipRender) { + ValidateChannel(channel); + RenderChannel(channel, channelAux, channelSubframe); + // todo figure out an actual way to convert this accurately to dry/wet + channelAux.reverb.setwet(((channel.mAutoMixerFxMix >> 8) / 255.0f)); + channelAux.reverb.setdry(1.0f - channelAux.reverb.getwet()); + } + + if (EnableReverb) { + channelAux.reverb.processreplace( + channelSubframe.channels[0].data(), + channelSubframe.channels[1].data(), + channelSubframe.channels[0].data(), + channelSubframe.channels[1].data(), + DSP_SUBFRAME_SIZE, + 1 + ); + } for (int o = 0; o < subframe.channels.size(); o++) { MixSubframe(subframe.channels[o], channelSubframe.channels[o]); @@ -463,6 +479,16 @@ static void RenderChannel( } void dusk::audio::DspInit() { + for (int i = 0; i < DSP_CHANNELS; i++) { + auto& channelAux = ChannelAux[i]; + channelAux.reverb.setwet(0.0f); + channelAux.reverb.setdry(1.0f); + channelAux.reverb.setroomsize(0.2f); + channelAux.reverb.setdamp(0.3f); + channelAux.reverb.setwidth(1.0f); + channelAux.reverb.setmode(0.0f); + channelAux.reverb.mute(); + } } void dusk::audio::ApplyVolume( diff --git a/src/dusk/audio/DuskDsp.hpp b/src/dusk/audio/DuskDsp.hpp index 35b6ccabc9..3c414375f3 100644 --- a/src/dusk/audio/DuskDsp.hpp +++ b/src/dusk/audio/DuskDsp.hpp @@ -8,6 +8,8 @@ #include "SDL3/SDL_audio.h" #include +#include "revmodel.hpp" + // ReSharper disable once CppUnusedIncludeDirective #include "global.h" @@ -29,6 +31,7 @@ namespace dusk::audio { // Used for debugging tools. u32 resetCount; + revmodel reverb; /** * Previous volume values, per output channel. @@ -118,4 +121,5 @@ namespace dusk::audio { extern f32 MasterVolume; extern f32 PrevMasterVolume; + extern bool EnableReverb; } diff --git a/src/dusk/imgui/ImGuiMenuGame.cpp b/src/dusk/imgui/ImGuiMenuGame.cpp index 91aa0314df..7bdea47862 100644 --- a/src/dusk/imgui/ImGuiMenuGame.cpp +++ b/src/dusk/imgui/ImGuiMenuGame.cpp @@ -6,6 +6,7 @@ #include #include "JSystem/JUtility/JUTGamePad.h" +#include "dusk/audio/DuskDsp.hpp" #include "dusk/audio/DuskAudioSystem.h" #include "dusk/hotkeys.h" #include "dusk/settings.h" @@ -34,7 +35,7 @@ namespace dusk { if (ImGui::BeginMenu("Audio")) { ImGui::Text("Master Volume"); ImGui::SliderFloat("##masterVolume", &getSettings().audio.masterVolume, 0.0f, 1.0f, ""); - + ImGui::Checkbox("Enable Reverb", &getSettings().audio.enableReverb); /* // TODO: implement additional settings ImGui::Text("Main Music Volume"); @@ -55,6 +56,7 @@ namespace dusk { */ audio::SetMasterVolume(getSettings().audio.masterVolume); + audio::EnableReverb = getSettings().audio.enableReverb; ImGui::EndMenu(); } diff --git a/src/dusk/settings.cpp b/src/dusk/settings.cpp index 9a5bb20c8c..ed2071ca30 100644 --- a/src/dusk/settings.cpp +++ b/src/dusk/settings.cpp @@ -17,6 +17,7 @@ UserSettings g_userSettings = { .subMusicVolume = 1.0f, .soundEffectsVolume = 1.0f, .fanfareVolume = 1.0f, + .enableReverb = true }, // Game settings From ebc37b934d5c08e597e0134f65a217314a0bb9ed Mon Sep 17 00:00:00 2001 From: madeline Date: Sat, 4 Apr 2026 15:59:18 -0700 Subject: [PATCH 2/4] better reverb --- src/dusk/audio/DuskDsp.cpp | 36 ++++++++++++++++++++++++------------ src/dusk/audio/DuskDsp.hpp | 2 +- src/dusk/settings.cpp | 2 +- 3 files changed, 26 insertions(+), 14 deletions(-) diff --git a/src/dusk/audio/DuskDsp.cpp b/src/dusk/audio/DuskDsp.cpp index fd1ae66620..3ca34bfa20 100644 --- a/src/dusk/audio/DuskDsp.cpp +++ b/src/dusk/audio/DuskDsp.cpp @@ -139,20 +139,32 @@ void dusk::audio::DspRender(OutputSubframe& subframe) { if (!skipRender) { ValidateChannel(channel); RenderChannel(channel, channelAux, channelSubframe); - // todo figure out an actual way to convert this accurately to dry/wet - channelAux.reverb.setwet(((channel.mAutoMixerFxMix >> 8) / 255.0f)); - channelAux.reverb.setdry(1.0f - channelAux.reverb.getwet()); } if (EnableReverb) { + // scale the input to the reverb rather than using wet/dry on the output. + // this way the reverb's internal buffers accumulate energy proportional to mAutoMixerFxMix, + // so any tail always decays at the correct level regardless of mAutoMixerFxMix changes + // prevents transients when the next sound starts playing with a different reverb level + // 700.0f was pulled out of my ass and just sounds good enough for console + f32 inputGain = (!skipRender) ? (channel.mAutoMixerFxMix >> 8) / 700.0f : 0.0f; + + OutputSubframe reverbSubframe = {}; + for (int j = 0; j < DSP_SUBFRAME_SIZE; j++) { + reverbSubframe.channels[0][j] = channelSubframe.channels[0][j] * inputGain; + reverbSubframe.channels[1][j] = channelSubframe.channels[1][j] * inputGain; + } + channelAux.reverb.processreplace( - channelSubframe.channels[0].data(), - channelSubframe.channels[1].data(), - channelSubframe.channels[0].data(), - channelSubframe.channels[1].data(), - DSP_SUBFRAME_SIZE, - 1 + reverbSubframe.channels[0].data(), reverbSubframe.channels[1].data(), + reverbSubframe.channels[0].data(), reverbSubframe.channels[1].data(), + DSP_SUBFRAME_SIZE, 1 ); + + for (int j = 0; j < DSP_SUBFRAME_SIZE; j++) { + channelSubframe.channels[0][j] += reverbSubframe.channels[0][j]; + channelSubframe.channels[1][j] += reverbSubframe.channels[1][j]; + } } for (int o = 0; o < subframe.channels.size(); o++) { @@ -481,10 +493,10 @@ static void RenderChannel( void dusk::audio::DspInit() { for (int i = 0; i < DSP_CHANNELS; i++) { auto& channelAux = ChannelAux[i]; - channelAux.reverb.setwet(0.0f); - channelAux.reverb.setdry(1.0f); + channelAux.reverb.setwet(1.0f); + channelAux.reverb.setdry(0.0f); channelAux.reverb.setroomsize(0.2f); - channelAux.reverb.setdamp(0.3f); + channelAux.reverb.setdamp(0.7f); channelAux.reverb.setwidth(1.0f); channelAux.reverb.setmode(0.0f); channelAux.reverb.mute(); diff --git a/src/dusk/audio/DuskDsp.hpp b/src/dusk/audio/DuskDsp.hpp index 3c414375f3..520a948208 100644 --- a/src/dusk/audio/DuskDsp.hpp +++ b/src/dusk/audio/DuskDsp.hpp @@ -54,7 +54,7 @@ namespace dusk::audio { // basically stores our position between resamplePrev and decodeBuf[0] so we don't lose that fractional resampler position next subframe f32 resamplePos; // last consumed sample from decodeBuf - s16 resamplePrev; + s16 resamplePrev; }; extern ChannelAuxData ChannelAux[DSP_CHANNELS]; diff --git a/src/dusk/settings.cpp b/src/dusk/settings.cpp index ed2071ca30..14269e833b 100644 --- a/src/dusk/settings.cpp +++ b/src/dusk/settings.cpp @@ -12,7 +12,7 @@ UserSettings g_userSettings = { // Audio .audio = { - .masterVolume = 1.0f, + .masterVolume =0.8f, .mainMusicVolume = 1.0f, .subMusicVolume = 1.0f, .soundEffectsVolume = 1.0f, From 37ab09ec1d2f3264036d87c7fdea7ab57525a81d Mon Sep 17 00:00:00 2001 From: madeline Date: Sat, 4 Apr 2026 16:07:36 -0700 Subject: [PATCH 3/4] h --- src/dusk/settings.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/dusk/settings.cpp b/src/dusk/settings.cpp index 14269e833b..db1e9e8b92 100644 --- a/src/dusk/settings.cpp +++ b/src/dusk/settings.cpp @@ -12,7 +12,7 @@ UserSettings g_userSettings = { // Audio .audio = { - .masterVolume =0.8f, + .masterVolume = 0.8f, .mainMusicVolume = 1.0f, .subMusicVolume = 1.0f, .soundEffectsVolume = 1.0f, From 29ea44fc1d934ae5bf5e9f35b40576b8e014edad Mon Sep 17 00:00:00 2001 From: madeline Date: Sat, 4 Apr 2026 17:14:09 -0700 Subject: [PATCH 4/4] dump audio and bigger room size --- src/dusk/audio/DuskAudioSystem.cpp | 16 ------------ src/dusk/audio/DuskDsp.cpp | 40 +++++++++++++++++++++++++++++- src/dusk/audio/DuskDsp.hpp | 1 + src/dusk/imgui/ImGuiAudio.cpp | 2 ++ 4 files changed, 42 insertions(+), 17 deletions(-) diff --git a/src/dusk/audio/DuskAudioSystem.cpp b/src/dusk/audio/DuskAudioSystem.cpp index 93e161512f..c7313749c2 100644 --- a/src/dusk/audio/DuskAudioSystem.cpp +++ b/src/dusk/audio/DuskAudioSystem.cpp @@ -3,8 +3,6 @@ #include #include #include -#include -#include #include #include "JSystem/JAudio2/JASAiCtrl.h" @@ -17,8 +15,6 @@ #include "JSystem/JAudio2/JASAudioThread.h" #include "JSystem/JAudio2/JASDriverIF.h" -// #define DUSK_DUMP_AUDIO - using namespace dusk::audio; static OutputSubframe OutBuffer; @@ -91,10 +87,6 @@ void SDLCALL GetNewAudio( } } -#if defined(DUSK_DUMP_AUDIO) -static std::ofstream outRaw("guh.raw", std::ios_base::out | std::ios_base::binary); -#endif - int RenderNewAudioFrame() { JASCriticalSection section; const u32 countSubframes = JASDriver::getSubFrames(); @@ -107,10 +99,6 @@ int RenderNewAudioFrame() { JASAudioThread::snIntCount -= 1; } -#if defined(DUSK_DUMP_AUDIO) - outRaw.flush(); -#endif - return static_cast(countSubframes) * DSP_SUBFRAME_SIZE; } @@ -147,10 +135,6 @@ void RenderAudioSubframe() { } } -#if defined(DUSK_DUMP_AUDIO) - outRaw.write((const char*)OutInterleaveBuffer.data(), sizeof(OutInterleaveBuffer)); -#endif - SDL_PutAudioStreamData(PlaybackStream, &OutInterleaveBuffer, sizeof(OutInterleaveBuffer)); } diff --git a/src/dusk/audio/DuskDsp.cpp b/src/dusk/audio/DuskDsp.cpp index 3ca34bfa20..e104749991 100644 --- a/src/dusk/audio/DuskDsp.cpp +++ b/src/dusk/audio/DuskDsp.cpp @@ -5,6 +5,7 @@ #include #include +#include #include #include "Adpcm.hpp" @@ -17,9 +18,30 @@ using namespace dusk::audio; ChannelAuxData dusk::audio::ChannelAux[DSP_CHANNELS] = {}; +static bool sDumpWasActive = false; +static FILE* sChannelDumpFiles[DSP_CHANNELS] = {}; + +static void OpenChannelDumpFiles() { + char name[32]; + for (int i = 0; i < DSP_CHANNELS; i++) { + snprintf(name, sizeof(name), "channel_%02d.raw", i); + sChannelDumpFiles[i] = fopen(name, "wb"); + } +} + +static void CloseChannelDumpFiles() { + for (int i = 0; i < DSP_CHANNELS; i++) { + if (sChannelDumpFiles[i]) { + fclose(sChannelDumpFiles[i]); + sChannelDumpFiles[i] = nullptr; + } + } +} + f32 dusk::audio::MasterVolume = 1.0f; f32 dusk::audio::PrevMasterVolume = 1.0f; bool dusk::audio::EnableReverb = true; +bool dusk::audio::DumpAudio = false; /** * Validate that a DSP channel's format is actually something we know how to play. @@ -107,6 +129,15 @@ static void MixSubframe(DspSubframe& dst, const DspSubframe& src) { } void dusk::audio::DspRender(OutputSubframe& subframe) { + if (DumpAudio != sDumpWasActive) { + sDumpWasActive = DumpAudio; + if (DumpAudio) { + OpenChannelDumpFiles(); + } else { + CloseChannelDumpFiles(); + } + } + std::span channels(JASDsp::CH_BUF, DSP_CHANNELS); for (int i = 0; i < channels.size(); i++) { @@ -167,6 +198,13 @@ void dusk::audio::DspRender(OutputSubframe& subframe) { } } + if (DumpAudio && sChannelDumpFiles[i]) { + for (int j = 0; j < DSP_SUBFRAME_SIZE; j++) { + fwrite(&channelSubframe.channels[0][j], sizeof(f32), 1, sChannelDumpFiles[i]); + fwrite(&channelSubframe.channels[1][j], sizeof(f32), 1, sChannelDumpFiles[i]); + } + } + for (int o = 0; o < subframe.channels.size(); o++) { MixSubframe(subframe.channels[o], channelSubframe.channels[o]); } @@ -495,7 +533,7 @@ void dusk::audio::DspInit() { auto& channelAux = ChannelAux[i]; channelAux.reverb.setwet(1.0f); channelAux.reverb.setdry(0.0f); - channelAux.reverb.setroomsize(0.2f); + channelAux.reverb.setroomsize(0.4f); channelAux.reverb.setdamp(0.7f); channelAux.reverb.setwidth(1.0f); channelAux.reverb.setmode(0.0f); diff --git a/src/dusk/audio/DuskDsp.hpp b/src/dusk/audio/DuskDsp.hpp index 520a948208..036a4bb9f0 100644 --- a/src/dusk/audio/DuskDsp.hpp +++ b/src/dusk/audio/DuskDsp.hpp @@ -122,4 +122,5 @@ namespace dusk::audio { extern f32 MasterVolume; extern f32 PrevMasterVolume; extern bool EnableReverb; + extern bool DumpAudio; } diff --git a/src/dusk/imgui/ImGuiAudio.cpp b/src/dusk/imgui/ImGuiAudio.cpp index ae01f119f4..3328d1b97c 100644 --- a/src/dusk/imgui/ImGuiAudio.cpp +++ b/src/dusk/imgui/ImGuiAudio.cpp @@ -8,6 +8,7 @@ #include "JSystem/JAudio2/JASDSPInterface.h" #include "JSystem/JAudio2/JASTrack.h" #include "dusk/audio/DuskAudioSystem.h" +#include "dusk/audio/DuskDsp.hpp" static std::array channelSortIndices = {}; static std::array lastResetCounts = {}; @@ -102,6 +103,7 @@ static void ShowAllDspChannels() { } ImGui::Text("Active channels: %d", activeChannels); + ImGui::Checkbox("Dump channels to disk", &dusk::audio::DumpAudio); ImGui::Checkbox("Sort by update count", &sortUpdateCount); if (sortUpdateCount) {