diff --git a/files.cmake b/files.cmake index af29738867..90a0dcd4f8 100644 --- a/files.cmake +++ b/files.cmake @@ -1487,6 +1487,8 @@ set(DUSK_FILES src/dusk/mods/manifest.hpp src/dusk/mods/svc/audio_res/audio_res.hpp src/dusk/mods/svc/audio_res/audio_res.cpp + src/dusk/mods/svc/audio_res/wsys.cpp + src/dusk/mods/svc/audio_res/wsys.hpp src/dusk/mods/svc/audio_res/wave.cpp src/dusk/mods/svc/audio_res/opus.cpp src/dusk/mods/svc/camera.cpp diff --git a/libs/JSystem/src/JAudio2/JASBank.cpp b/libs/JSystem/src/JAudio2/JASBank.cpp index b36b076dc8..0acc5b9ed4 100644 --- a/libs/JSystem/src/JAudio2/JASBank.cpp +++ b/libs/JSystem/src/JAudio2/JASBank.cpp @@ -2,13 +2,13 @@ #include "JSystem/JAudio2/JASBank.h" -#include "dusk/mods/svc/audio_res/audio_res.hpp" +#include "dusk/mods/svc/audio_res/wsys.hpp" #include "JSystem/JAudio2/JASAiCtrl.h" #include "JSystem/JAudio2/JASBasicInst.h" #include "JSystem/JAudio2/JASBasicWaveBank.h" #include "JSystem/JAudio2/JASChannel.h" -using namespace dusk::mods::svc::audio_res; +using namespace dusk::mods::svc::audio_res::wsys; // NONMATCHING JASPoolAllocObject_MultiThreaded<_> locations JASChannel* JASBank::noteOn(JASBank const* bank, int program, u8 key, u8 velocity, u16 priority, diff --git a/src/dusk/mods/svc/audio_res/audio_res.cpp b/src/dusk/mods/svc/audio_res/audio_res.cpp index dc7b2337fd..28a18453ca 100644 --- a/src/dusk/mods/svc/audio_res/audio_res.cpp +++ b/src/dusk/mods/svc/audio_res/audio_res.cpp @@ -4,10 +4,7 @@ #include "mods/svc/audio_res.h" #include "../registry.hpp" -#include "../slot_map.hpp" -#include "aurora/lib/logging.hpp" -#include "dusk/audio/DuskAudioSystem.h" -#include "dusk/mods/loader/loader.hpp" +#include "wsys.hpp" namespace dusk::mods::svc { @@ -16,246 +13,24 @@ namespace { using namespace dusk::helpers::cast; -bool audio_replacements_dirty = false; - -aurora::Module Log("dusk::mods::svc::audio_res"); - -SlotMap s_waveReplacements; - -constexpr ContainerLoadFunction s_containerLoadFunctions[] = { - load_wav, -#if DUSK_OPUS - load_opus, -#endif -}; - -bool validate_raw_size(LoadedMod const& mod, std::string const& path, uintptr_t actual_size, AudioRawWave const& raw, u32& sample_count) { - u32 samples_per_block; - u32 bytes_per_block; - switch (raw.format) { - case Adpcm4: - samples_per_block = 16; - bytes_per_block = 9; - break; - case Pcm16: - samples_per_block = 1; - bytes_per_block = 2; - break; - default: - Log.error("[{}] unimplemented wave format (ADPCM2/PCM8): '{}'", mod.metadata.id, path); - return false; - } - - if (actual_size % bytes_per_block != 0) { - Log.error("[{}] raw file is not divisible by format block size: '{}'", mod.metadata.id, path); - return false; - } - - sample_count = (actual_size / bytes_per_block) * samples_per_block; - return true; -} - -ModResult load_raw( - LoadedMod const& mod, RuntimeWaveReplacementSlot& slot, AudioRawWave const& raw) { - auto file_contents = mod.bundle->readFile(slot.bundle_path); - u32 sample_count = 0; - - if (!validate_raw_size(mod, slot.bundle_path, file_contents.size(), raw, sample_count)) { - Log.error( - "[{}] replace_wave: file '{}' is a raw .abf file, but raw_wave data was not specified", - mod.metadata.id, slot.bundle_path); - - return MOD_INVALID_ARGUMENT; - } - - if (slot.format == Pcm16) { - SampleDataPcm16 pcm16; - pcm16.data.resize(sample_count); - memcpy(pcm16.data.data(), file_contents.data(), file_contents.size()); - slot.data = std::make_shared(std::move(pcm16)); - } else { - slot.data = std::make_shared(std::move(file_contents)); - } - - slot.sample_count = sample_count; - slot.sample_rate = raw.sample_rate; - slot.format = raw.format; - slot.sample_value_penult = raw.sample_value_penult; - slot.sample_value_last = raw.sample_value_last; - return MOD_OK; -} - -/** - * Load an audio file stored in some sort of container format. - * File type is determined by looking at contents, not extension. - */ -ModResult load_container(LoadedMod const& mod, RuntimeWaveReplacementSlot& slot) { - auto file_contents = mod.bundle->readFile(slot.bundle_path); - - for (auto const loader : s_containerLoadFunctions) { - auto const result = loader(mod, slot, file_contents); - if (result == MOD_OK) { - return MOD_OK; - } - - if (result != MOD_UNSUPPORTED) { - return result; - } - } - - return MOD_UNSUPPORTED; -} - -JASWaveInfo wave_info_from_slot(RuntimeWaveReplacementSlot const& slot) { - JASWaveInfo info; - info.mWaveFormat = static_cast(slot.format); - info.mBaseKey = slot.base_key; - info.mLoopFlag = slot.loop ? 0xFF : 0; - info.mSampleRate = slot.sample_rate; - info.mOffsetStart = 0; // Unused but just init it ig. - info.mOffsetLength = bounded_cast(slot.data->size()); - info.mLoopStartSample = slot.loop_start_sample; - info.mLoopEndSample = bounded_cast(slot.loop_end_sample); - info.mSampleCount = bounded_cast(slot.sample_count); - info.mpLast = slot.sample_value_last; - info.mpPenult = slot.sample_value_penult; - // mpLoaded is initialized to point to a 1, so we're good there. - return info; -} - -ModResult insert_replace_wave( - ModContext* ctx, - AudioWaveBank bank, - u16 wave_id, - char const* file_name, - AudioWaveInfo const* wave_info, - AudioWaveHandle* out_handle) { - if (out_handle != nullptr) { - *out_handle = 0; - } - - auto mod = mod_from_context(ctx); - if (mod == nullptr || file_name == nullptr || !is_safe_resource_path(file_name)) { - return MOD_INVALID_ARGUMENT; - } - - RuntimeWaveReplacementSlot slot{}; - slot.bundle_path = file_name; - slot.bank = bank; - slot.wave_id = wave_id; - - ModResult result; - if (wave_info && wave_info->raw_wave) { - result = load_raw(*mod, slot, *wave_info->raw_wave); - } else { - result = load_container(*mod, slot); - } - if (result != MOD_OK) { - return result; - } - - if (wave_info != nullptr) { - slot.base_key = wave_info->base_key; - slot.loop = wave_info->loop; - slot.loop_start_sample = wave_info->loop_start_sample; - slot.loop_end_sample = wave_info->loop_end_sample; - } else { - slot.base_key = AUDIO_RES_DEFAULT_KEY; - slot.loop = false; - slot.loop_start_sample = 0; - slot.loop_end_sample = slot.sample_count; - } - - audio_replacements_dirty = true; - - const auto handle = s_waveReplacements.emplace(*mod, std::move(slot)); - if (out_handle) { - *out_handle = handle; - } - - 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 = &insert_replace_wave, - .remove_wave = &remove_wave, + .replace_wave = &wsys::insert_replace_wave, + .remove_wave = &wsys::remove_wave, }; +void frame_end() { + wsys::frame_end(); +} + +void mod_detached(LoadedMod& mod) { + wsys::remove_mod(mod); +} + 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()); + wsys::sync_audio_replacements(); } -void replacements_remove_mod(LoadedMod& mod) { - s_waveReplacements.erase_all(mod); - - audio_replacements_dirty = true; -} - -} - -absl::flat_hash_map s_replacements; -std::mutex s_replacements_mutex; - -AudioWaveReplacementValue::~AudioWaveReplacementValue() = default; -const JASWaveInfo* AudioWaveReplacementValue::getWaveInfo() const { - return &wave_info; -} - -void const* AudioWaveReplacementValue::getAramBaseAddress() const { - return data->get_data().data(); -} - -intptr_t AudioWaveReplacementValue::getWavePtr() const { - return 0; -} - -std::unique_ptr AudioWaveReplacementValue::getSampleReference() const { - return std::make_unique(data); -} - -void SampleDataPcm16::be_swap() { - for (auto& sample : data) { - ::be_swap(sample); - } } } // namespace audio_res @@ -265,14 +40,9 @@ constinit const ServiceModule g_audioResModule{ .majorVersion = AUDIO_RES_SERVICE_MAJOR, .minorVersion = AUDIO_RES_SERVICE_MINOR, .service = &audio_res::s_audioResService, - .modDetached = audio_res::replacements_remove_mod, + .modDetached = audio_res::mod_detached, .lifecycleApplied = audio_res::sync_audio_replacements, - .frameEnd = - [] { - if (audio_res::audio_replacements_dirty) { - audio_res::sync_audio_replacements(); - } - } + .frameEnd = audio_res::frame_end }; }; diff --git a/src/dusk/mods/svc/audio_res/audio_res.hpp b/src/dusk/mods/svc/audio_res/audio_res.hpp index ce46db0721..5b2ae0a87a 100644 --- a/src/dusk/mods/svc/audio_res/audio_res.hpp +++ b/src/dusk/mods/svc/audio_res/audio_res.hpp @@ -1,106 +1,9 @@ #pragma once -#include -#include -#include "JSystem/JAudio2/JASChannel.h" -#include "JSystem/JAudio2/JASWaveInfo.h" -#include "absl/container/flat_hash_map.h" -#include "mods/svc/audio_res.h" namespace dusk::mods { struct LoadedMod; } namespace dusk::mods::svc::audio_res { -struct SampleDataBuf { - virtual ~SampleDataBuf() = default; - [[nodiscard]] virtual std::span get_data() const = 0; - [[nodiscard]] size_t size() const { - return get_data().size(); - } -}; - -// Making a separate type for this rather than just using a vector -// because I don't want aliasing or alignment to eat my face. -struct SampleDataPcm16 : SampleDataBuf { - std::vector data; - - [[nodiscard]] std::span get_data() const override { - return as_bytes(std::span(data)); - } - - void be_swap(); -}; - -struct SampleDataU8 : SampleDataBuf { - std::vector data; - - explicit SampleDataU8(std::vector data) : data(std::move(data)) {} - - [[nodiscard]] std::span get_data() const override { - return as_bytes(std::span(data)); - } -}; - -struct SampleReference : JASSampleDataReference { - explicit SampleReference(std::shared_ptr data) : data(std::move(data)) {} - - std::shared_ptr data; -}; - -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); - } - - [[nodiscard]] bool operator==(const AudioWaveKey& other) const { - return other.bank == bank && other.wave_id == wave_id; - } -}; - -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; - [[nodiscard]] void const* getAramBaseAddress() const override; - [[nodiscard]] std::unique_ptr getSampleReference() const override; - - JASWaveInfo wave_info; - std::shared_ptr data; -}; - -extern absl::flat_hash_map s_replacements; -extern std::mutex s_replacements_mutex; - -struct RuntimeWaveReplacementSlot { - std::string bundle_path; - AudioWaveBank bank; - u16 wave_id; - - u8 base_key; - bool loop; - u32 loop_start_sample; - u32 loop_end_sample; - - AudioWaveFormat format; - f32 sample_rate; - u32 sample_count; - s16 sample_value_last; - s16 sample_value_penult; - - std::shared_ptr data; -}; - -using ContainerLoadFunction = ModResult (*)(LoadedMod const& mod, RuntimeWaveReplacementSlot& slot, std::span fileData); - -ModResult load_wav(LoadedMod const& mod, RuntimeWaveReplacementSlot& slot, std::span fileData); -ModResult load_opus(LoadedMod const& mod, RuntimeWaveReplacementSlot& slot, std::span fileData); - } diff --git a/src/dusk/mods/svc/audio_res/opus.cpp b/src/dusk/mods/svc/audio_res/opus.cpp index 3541f6be81..aa4e6bea99 100644 --- a/src/dusk/mods/svc/audio_res/opus.cpp +++ b/src/dusk/mods/svc/audio_res/opus.cpp @@ -1,6 +1,6 @@ #if DUSK_OPUS -#include "audio_res.hpp" +#include "wsys.hpp" #include "aurora/lib/logging.hpp" #include "dusk/mod_loader.hpp" #include "helpers/cast.hpp" @@ -8,7 +8,7 @@ namespace { -aurora::Module Log("dusk::mods::svc::audio_res::opus"); +aurora::Module Log("dusk::mods::svc::audio_res::wsys::opus"); struct OpusHandle { OggOpusFile* file; @@ -24,7 +24,7 @@ struct OpusHandle { } -namespace dusk::mods::svc::audio_res { +namespace dusk::mods::svc::audio_res::wsys { ModResult load_opus(LoadedMod const& mod, RuntimeWaveReplacementSlot& slot, std::span fileData) { OpusHead head; const int result = op_test(&head, fileData.data(), fileData.size()); diff --git a/src/dusk/mods/svc/audio_res/wave.cpp b/src/dusk/mods/svc/audio_res/wave.cpp index 69e1e2e10f..ae97a8f81b 100644 --- a/src/dusk/mods/svc/audio_res/wave.cpp +++ b/src/dusk/mods/svc/audio_res/wave.cpp @@ -1,11 +1,11 @@ -#include "audio_res.hpp" +#include "wsys.hpp" #include "aurora/lib/logging.hpp" #include "dusk/mod_loader.hpp" #include "helpers/alignment.hpp" namespace { -aurora::Module Log("dusk::mods::svc::audio_res::wav"); +aurora::Module Log("dusk::mods::svc::audio_res::wsys::wav"); struct ChunkHeader { char magic[4]; @@ -37,7 +37,7 @@ bool check_four_cc(char const (&field)[4], char const (&expected)[5]) { } -namespace dusk::mods::svc::audio_res { +namespace dusk::mods::svc::audio_res::wsys { ModResult load_wav(LoadedMod const& mod, RuntimeWaveReplacementSlot& slot, std::span fileData) { using namespace helpers::alignment; diff --git a/src/dusk/mods/svc/audio_res/wsys.cpp b/src/dusk/mods/svc/audio_res/wsys.cpp new file mode 100644 index 0000000000..5abe9cea71 --- /dev/null +++ b/src/dusk/mods/svc/audio_res/wsys.cpp @@ -0,0 +1,260 @@ +#include "wsys.hpp" +#include "../registry.hpp" +#include "../slot_map.hpp" +#include "aurora/lib/logging.hpp" +#include "dusk/audio/DuskAudioSystem.h" +#include "dusk/mods/loader/loader.hpp" +#include "helpers/alignment.hpp" +#include "helpers/cast.hpp" + +namespace dusk::mods::svc::audio_res::wsys { + +namespace { + +using namespace dusk::helpers::cast; + +bool wave_replacements_dirty = false; + +aurora::Module Log("dusk::mods::svc::audio_res"); + +SlotMap s_waveReplacements; + +constexpr ContainerLoadFunction s_containerLoadFunctions[] = { + load_wav, +#if DUSK_OPUS + load_opus, +#endif +}; + + +bool validate_raw_size(LoadedMod const& mod, std::string const& path, uintptr_t actual_size, AudioRawWave const& raw, u32& sample_count) { + u32 samples_per_block; + u32 bytes_per_block; + switch (raw.format) { + case Adpcm4: + samples_per_block = 16; + bytes_per_block = 9; + break; + case Pcm16: + samples_per_block = 1; + bytes_per_block = 2; + break; + default: + Log.error("[{}] unimplemented wave format (ADPCM2/PCM8): '{}'", mod.metadata.id, path); + return false; + } + + if (actual_size % bytes_per_block != 0) { + Log.error("[{}] raw file is not divisible by format block size: '{}'", mod.metadata.id, path); + return false; + } + + sample_count = (actual_size / bytes_per_block) * samples_per_block; + return true; +} + +ModResult load_raw( + LoadedMod const& mod, RuntimeWaveReplacementSlot& slot, AudioRawWave const& raw) { + auto file_contents = mod.bundle->readFile(slot.bundle_path); + u32 sample_count = 0; + + if (!validate_raw_size(mod, slot.bundle_path, file_contents.size(), raw, sample_count)) { + Log.error( + "[{}] replace_wave: file '{}' is a raw .abf file, but raw_wave data was not specified", + mod.metadata.id, slot.bundle_path); + + return MOD_INVALID_ARGUMENT; + } + + if (slot.format == Pcm16) { + SampleDataPcm16 pcm16; + pcm16.data.resize(sample_count); + memcpy(pcm16.data.data(), file_contents.data(), file_contents.size()); + slot.data = std::make_shared(std::move(pcm16)); + } else { + slot.data = std::make_shared(std::move(file_contents)); + } + + slot.sample_count = sample_count; + slot.sample_rate = raw.sample_rate; + slot.format = raw.format; + slot.sample_value_penult = raw.sample_value_penult; + slot.sample_value_last = raw.sample_value_last; + return MOD_OK; +} + +/** + * Load an audio file stored in some sort of container format. + * File type is determined by looking at contents, not extension. + */ +ModResult load_container(LoadedMod const& mod, RuntimeWaveReplacementSlot& slot) { + auto file_contents = mod.bundle->readFile(slot.bundle_path); + + for (auto const loader : s_containerLoadFunctions) { + auto const result = loader(mod, slot, file_contents); + if (result == MOD_OK) { + return MOD_OK; + } + + if (result != MOD_UNSUPPORTED) { + return result; + } + } + + return MOD_UNSUPPORTED; +} + +JASWaveInfo wave_info_from_slot(RuntimeWaveReplacementSlot const& slot) { + JASWaveInfo info; + info.mWaveFormat = static_cast(slot.format); + info.mBaseKey = slot.base_key; + info.mLoopFlag = slot.loop ? 0xFF : 0; + info.mSampleRate = slot.sample_rate; + info.mOffsetStart = 0; // Unused but just init it ig. + info.mOffsetLength = bounded_cast(slot.data->size()); + info.mLoopStartSample = slot.loop_start_sample; + info.mLoopEndSample = bounded_cast(slot.loop_end_sample); + info.mSampleCount = bounded_cast(slot.sample_count); + info.mpLast = slot.sample_value_last; + info.mpPenult = slot.sample_value_penult; + // mpLoaded is initialized to point to a 1, so we're good there. + return info; +} + +bool wave_remove(LoadedMod const& mod, AudioWaveHandle const handle) { + auto const result = s_waveReplacements.erase_owned(handle, mod); + wave_replacements_dirty |= result; + return result; +} + +} + +absl::flat_hash_map s_replacements; +std::mutex s_replacements_mutex; + +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; +} + +ModResult insert_replace_wave( + ModContext* ctx, + AudioWaveBank bank, + u16 wave_id, + char const* file_name, + AudioWaveInfo const* wave_info, + AudioWaveHandle* out_handle) { + if (out_handle != nullptr) { + *out_handle = 0; + } + + auto mod = mod_from_context(ctx); + if (mod == nullptr || file_name == nullptr || !is_safe_resource_path(file_name)) { + return MOD_INVALID_ARGUMENT; + } + + RuntimeWaveReplacementSlot slot{}; + slot.bundle_path = file_name; + slot.bank = bank; + slot.wave_id = wave_id; + + ModResult result; + if (wave_info && wave_info->raw_wave) { + result = load_raw(*mod, slot, *wave_info->raw_wave); + } else { + result = load_container(*mod, slot); + } + if (result != MOD_OK) { + return result; + } + + if (wave_info != nullptr) { + slot.base_key = wave_info->base_key; + slot.loop = wave_info->loop; + slot.loop_start_sample = wave_info->loop_start_sample; + slot.loop_end_sample = wave_info->loop_end_sample; + } else { + slot.base_key = AUDIO_RES_DEFAULT_KEY; + slot.loop = false; + slot.loop_start_sample = 0; + slot.loop_end_sample = slot.sample_count; + } + + wave_replacements_dirty = true; + + const auto handle = s_waveReplacements.emplace(*mod, std::move(slot)); + if (out_handle) { + *out_handle = handle; + } + + return MOD_OK; +} + +void remove_mod(LoadedMod& mod) { + s_waveReplacements.erase_all(mod); + + wave_replacements_dirty = true; +} + +void sync_audio_replacements() { + wave_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 frame_end() { + if (wave_replacements_dirty) { + sync_audio_replacements(); + } +} + +AudioWaveReplacementValue::~AudioWaveReplacementValue() = default; +const JASWaveInfo* AudioWaveReplacementValue::getWaveInfo() const { + return &wave_info; +} + +void const* AudioWaveReplacementValue::getAramBaseAddress() const { + return data->get_data().data(); +} + +intptr_t AudioWaveReplacementValue::getWavePtr() const { + return 0; +} + +std::unique_ptr AudioWaveReplacementValue::getSampleReference() const { + return std::make_unique(data); +} + +void SampleDataPcm16::be_swap() { + for (auto& sample : data) { + ::be_swap(sample); + } +} + + +} \ No newline at end of file diff --git a/src/dusk/mods/svc/audio_res/wsys.hpp b/src/dusk/mods/svc/audio_res/wsys.hpp new file mode 100644 index 0000000000..76b966f65a --- /dev/null +++ b/src/dusk/mods/svc/audio_res/wsys.hpp @@ -0,0 +1,120 @@ +#pragma once + +#include +#include +#include "JSystem/JAudio2/JASWaveInfo.h" +#include "absl/container/flat_hash_map.h" +#include "mods/svc/audio_res.h" + +namespace dusk::mods { +struct LoadedMod; +} + +namespace dusk::mods::svc::audio_res::wsys { + +struct SampleDataBuf { + virtual ~SampleDataBuf() = default; + [[nodiscard]] virtual std::span get_data() const = 0; + [[nodiscard]] size_t size() const { + return get_data().size(); + } +}; + +// Making a separate type for this rather than just using a vector +// because I don't want aliasing or alignment to eat my face. +struct SampleDataPcm16 : SampleDataBuf { + std::vector data; + + [[nodiscard]] std::span get_data() const override { + return as_bytes(std::span(data)); + } + + void be_swap(); +}; + +struct SampleDataU8 : SampleDataBuf { + std::vector data; + + explicit SampleDataU8(std::vector data) : data(std::move(data)) {} + + [[nodiscard]] std::span get_data() const override { + return as_bytes(std::span(data)); + } +}; + +struct SampleReference : JASSampleDataReference { + explicit SampleReference(std::shared_ptr data) : data(std::move(data)) {} + + std::shared_ptr data; +}; + +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); + } + + [[nodiscard]] bool operator==(const AudioWaveKey& other) const { + return other.bank == bank && other.wave_id == wave_id; + } +}; + +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; + [[nodiscard]] void const* getAramBaseAddress() const override; + [[nodiscard]] std::unique_ptr getSampleReference() const override; + + JASWaveInfo wave_info; + std::shared_ptr data; +}; + +extern absl::flat_hash_map s_replacements; +extern std::mutex s_replacements_mutex; + +struct RuntimeWaveReplacementSlot { + std::string bundle_path; + AudioWaveBank bank; + u16 wave_id; + + u8 base_key; + bool loop; + u32 loop_start_sample; + u32 loop_end_sample; + + AudioWaveFormat format; + f32 sample_rate; + u32 sample_count; + s16 sample_value_last; + s16 sample_value_penult; + + std::shared_ptr data; +}; + +using ContainerLoadFunction = ModResult (*)(LoadedMod const& mod, RuntimeWaveReplacementSlot& slot, std::span fileData); + +ModResult load_wav(LoadedMod const& mod, RuntimeWaveReplacementSlot& slot, std::span fileData); +ModResult load_opus(LoadedMod const& mod, RuntimeWaveReplacementSlot& slot, std::span fileData); + +void frame_end(); +void remove_mod(LoadedMod& mod); +void sync_audio_replacements(); + +ModResult insert_replace_wave( + ModContext* ctx, + AudioWaveBank bank, + u16 wave_id, + char const* file_name, + AudioWaveInfo const* wave_info, + AudioWaveHandle* out_handle); + +ModResult remove_wave(ModContext* ctx, AudioWaveHandle handle); + +} \ No newline at end of file