From 92906a3269793004bbc6c44b9ac0b5ddb12ad44e Mon Sep 17 00:00:00 2001 From: PJB3005 Date: Mon, 27 Jul 2026 15:41:08 +0200 Subject: [PATCH] Audio replacement priority & lifecycle handling --- libs/JSystem/src/JAudio2/JASBank.cpp | 3 +- sdk/include/mods/svc/audio_res.h | 2 + src/dusk/mods/svc/audio_res/audio_res.cpp | 85 ++++++++++++++++++----- src/dusk/mods/svc/audio_res/audio_res.hpp | 6 +- 4 files changed, 75 insertions(+), 21 deletions(-) diff --git a/libs/JSystem/src/JAudio2/JASBank.cpp b/libs/JSystem/src/JAudio2/JASBank.cpp index 1dc66b377e..b85258bb8b 100644 --- a/libs/JSystem/src/JAudio2/JASBank.cpp +++ b/libs/JSystem/src/JAudio2/JASBank.cpp @@ -34,9 +34,10 @@ JASChannel* JASBank::noteOn(JASBank const* param_0, int param_1, u8 param_2, u8 } #if TARGET_PC auto const key = AudioWaveKey(static_cast(waveBank->bankId), stack_60.field_0x1a); + std::lock_guard lock(s_replacements_mutex); auto const found_replacement = s_replacements.find(key); if (found_replacement != s_replacements.end()) { - waveHandle = found_replacement->second.get(); + waveHandle = &found_replacement->second; } auto const aramBase = waveHandle->getAramBaseAddress(); diff --git a/sdk/include/mods/svc/audio_res.h b/sdk/include/mods/svc/audio_res.h index f5ec3693dc..0d126c1912 100644 --- a/sdk/include/mods/svc/audio_res.h +++ b/sdk/include/mods/svc/audio_res.h @@ -51,6 +51,8 @@ typedef struct AudioResService { char const* file_name, AudioWaveInfo const* wave_info, AudioWaveHandle* out_handle); + + ModResult (*remove_wave)(ModContext* ctx, AudioWaveHandle handle); } AudioResService; #ifdef __cplusplus diff --git a/src/dusk/mods/svc/audio_res/audio_res.cpp b/src/dusk/mods/svc/audio_res/audio_res.cpp index d95aba652a..dc7b2337fd 100644 --- a/src/dusk/mods/svc/audio_res/audio_res.cpp +++ b/src/dusk/mods/svc/audio_res/audio_res.cpp @@ -6,6 +6,7 @@ #include "../registry.hpp" #include "../slot_map.hpp" #include "aurora/lib/logging.hpp" +#include "dusk/audio/DuskAudioSystem.h" #include "dusk/mods/loader/loader.hpp" namespace dusk::mods::svc { @@ -15,6 +16,8 @@ namespace { using namespace dusk::helpers::cast; +bool audio_replacements_dirty = false; + aurora::Module Log("dusk::mods::svc::audio_res"); SlotMap s_waveReplacements; @@ -163,14 +166,7 @@ ModResult insert_replace_wave( slot.loop_end_sample = slot.sample_count; } - // TODO: Handle conflicts - - audio_res::AudioWaveReplacementValue value; - value.data = slot.data; - value.wave_info = wave_info_from_slot(slot); - audio_res::s_replacements.emplace( - audio_res::AudioWaveKey{.bank = bank, .wave_id = wave_id}, - std::make_unique(std::move(value))); + audio_replacements_dirty = true; const auto handle = s_waveReplacements.emplace(*mod, std::move(slot)); if (out_handle) { @@ -180,13 +176,64 @@ ModResult insert_replace_wave( return MOD_OK; } +bool wave_remove(LoadedMod const& mod, AudioWaveHandle const handle) { + auto const result = s_waveReplacements.erase_owned(handle, mod); + audio_replacements_dirty |= result; + return result; +} + +ModResult remove_wave(ModContext* ctx, AudioWaveHandle handle) { + auto* mod = mod_from_context(ctx); + if (mod == nullptr || handle == 0) { + return MOD_INVALID_ARGUMENT; + } + if (!wave_remove(*mod, handle)) { + Log.error("[{}] remove wave failed: unknown handle {}", mod->metadata.id, handle); + return MOD_INVALID_ARGUMENT; + } + return MOD_OK; +} + constexpr AudioResService s_audioResService{ .header = SERVICE_HEADER(AudioResService, AUDIO_RES_SERVICE_MAJOR, AUDIO_RES_SERVICE_MINOR), - .replace_wave = &audio_res::insert_replace_wave}; + .replace_wave = &insert_replace_wave, + .remove_wave = &remove_wave, +}; + +void sync_audio_replacements() { + audio_replacements_dirty = false; + + absl::flat_hash_map new_map; + + for (auto const& mod : ModLoader::instance().active_mods()) { + for (auto const& slot : s_waveReplacements.take_all(mod)) { + auto const wave_info = wave_info_from_slot(slot.value); + new_map.emplace( + AudioWaveKey(slot.value.bank, slot.value.wave_id), + AudioWaveReplacementValue(wave_info, slot.value.data)); + } + } + + // Log.info("new: {}, old: {}", new_map.size(), s_replacements.size()); + + std::lock_guard lock(s_replacements_mutex); + // Note: new_map will contain the old contents, and is dropped *outside* the lock. + // As to avoid holding the lock any longer than necessary. + std::exchange(s_replacements, std::move(new_map)); + + // Log.info("new: {}, old: {}", new_map.size(), s_replacements.size()); +} + +void replacements_remove_mod(LoadedMod& mod) { + s_waveReplacements.erase_all(mod); + + audio_replacements_dirty = true; +} } -absl::flat_hash_map> s_replacements; +absl::flat_hash_map s_replacements; +std::mutex s_replacements_mutex; AudioWaveReplacementValue::~AudioWaveReplacementValue() = default; const JASWaveInfo* AudioWaveReplacementValue::getWaveInfo() const { @@ -218,14 +265,14 @@ constinit const ServiceModule g_audioResModule{ .majorVersion = AUDIO_RES_SERVICE_MAJOR, .minorVersion = AUDIO_RES_SERVICE_MINOR, .service = &audio_res::s_audioResService, - // .modDetached = overlay_remove_mod, - // .lifecycleApplied = overlay_sync_files, - // .frameEnd = - // [] { - // if (consume_overlays_dirty()) { - // overlay_sync_files(); - // } - // }, + .modDetached = audio_res::replacements_remove_mod, + .lifecycleApplied = audio_res::sync_audio_replacements, + .frameEnd = + [] { + if (audio_res::audio_replacements_dirty) { + audio_res::sync_audio_replacements(); + } + } + }; }; -} diff --git a/src/dusk/mods/svc/audio_res/audio_res.hpp b/src/dusk/mods/svc/audio_res/audio_res.hpp index bed4567af8..4ae3e0dd1c 100644 --- a/src/dusk/mods/svc/audio_res/audio_res.hpp +++ b/src/dusk/mods/svc/audio_res/audio_res.hpp @@ -51,6 +51,8 @@ struct AudioWaveKey { AudioWaveBank bank; u32 wave_id; + AudioWaveKey(AudioWaveBank bank, u32 wave_id) : bank(bank), wave_id(wave_id) {}; + template friend H AbslHashValue(H h, const AudioWaveKey& k) { return H::combine(std::move(h), k.bank, k.wave_id); @@ -62,6 +64,7 @@ struct AudioWaveKey { }; struct AudioWaveReplacementValue : JASWaveHandle { + AudioWaveReplacementValue(const JASWaveInfo& wave_info, std::shared_ptr data) : wave_info(wave_info), data(std::move(data)) {}; ~AudioWaveReplacementValue() override; [[nodiscard]] const JASWaveInfo* getWaveInfo() const override; [[nodiscard]] intptr_t getWavePtr() const override; @@ -72,7 +75,8 @@ struct AudioWaveReplacementValue : JASWaveHandle { std::shared_ptr data; }; -extern absl::flat_hash_map> s_replacements; +extern absl::flat_hash_map s_replacements; +extern std::mutex s_replacements_mutex; struct RuntimeWaveReplacementSlot { std::string bundle_path;