Move wsys replacements into their own .cpp file

This is gonna be a big PR
This commit is contained in:
PJB3005
2026-08-06 01:21:13 +02:00
parent d4ca7b696b
commit 756f8d5fb9
8 changed files with 404 additions and 349 deletions
+2
View File
@@ -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
+2 -2
View File
@@ -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,
+14 -244
View File
@@ -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<RuntimeWaveReplacementSlot> 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<SampleDataPcm16>(std::move(pcm16));
} else {
slot.data = std::make_shared<SampleDataU8>(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<u8>(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<AudioWaveKey, AudioWaveReplacementValue> 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<AudioWaveKey, AudioWaveReplacementValue> 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<JASSampleDataReference> AudioWaveReplacementValue::getSampleReference() const {
return std::make_unique<SampleReference>(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
};
};
-97
View File
@@ -1,106 +1,9 @@
#pragma once
#include <memory>
#include <mutex>
#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<std::byte const> 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<u8>
// because I don't want aliasing or alignment to eat my face.
struct SampleDataPcm16 : SampleDataBuf {
std::vector<s16> data;
[[nodiscard]] std::span<std::byte const> get_data() const override {
return as_bytes(std::span(data));
}
void be_swap();
};
struct SampleDataU8 : SampleDataBuf {
std::vector<u8> data;
explicit SampleDataU8(std::vector<u8> data) : data(std::move(data)) {}
[[nodiscard]] std::span<std::byte const> get_data() const override {
return as_bytes(std::span(data));
}
};
struct SampleReference : JASSampleDataReference {
explicit SampleReference(std::shared_ptr<SampleDataBuf> data) : data(std::move(data)) {}
std::shared_ptr<SampleDataBuf> data;
};
struct AudioWaveKey {
AudioWaveBank bank;
u32 wave_id;
AudioWaveKey(AudioWaveBank bank, u32 wave_id) : bank(bank), wave_id(wave_id) {};
template <typename H>
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<SampleDataBuf> 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<JASSampleDataReference> getSampleReference() const override;
JASWaveInfo wave_info;
std::shared_ptr<SampleDataBuf> data;
};
extern absl::flat_hash_map<AudioWaveKey, AudioWaveReplacementValue> 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<audio_res::SampleDataBuf> data;
};
using ContainerLoadFunction = ModResult (*)(LoadedMod const& mod, RuntimeWaveReplacementSlot& slot, std::span<u8 const> fileData);
ModResult load_wav(LoadedMod const& mod, RuntimeWaveReplacementSlot& slot, std::span<u8 const> fileData);
ModResult load_opus(LoadedMod const& mod, RuntimeWaveReplacementSlot& slot, std::span<u8 const> fileData);
}
+3 -3
View File
@@ -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<u8 const> fileData) {
OpusHead head;
const int result = op_test(&head, fileData.data(), fileData.size());
+3 -3
View File
@@ -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<u8 const> fileData) {
using namespace helpers::alignment;
+260
View File
@@ -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<RuntimeWaveReplacementSlot> 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<SampleDataPcm16>(std::move(pcm16));
} else {
slot.data = std::make_shared<SampleDataU8>(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<u8>(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<AudioWaveKey, AudioWaveReplacementValue> 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<AudioWaveKey, AudioWaveReplacementValue> 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<JASSampleDataReference> AudioWaveReplacementValue::getSampleReference() const {
return std::make_unique<SampleReference>(data);
}
void SampleDataPcm16::be_swap() {
for (auto& sample : data) {
::be_swap(sample);
}
}
}
+120
View File
@@ -0,0 +1,120 @@
#pragma once
#include <memory>
#include <mutex>
#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<std::byte const> 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<u8>
// because I don't want aliasing or alignment to eat my face.
struct SampleDataPcm16 : SampleDataBuf {
std::vector<s16> data;
[[nodiscard]] std::span<std::byte const> get_data() const override {
return as_bytes(std::span(data));
}
void be_swap();
};
struct SampleDataU8 : SampleDataBuf {
std::vector<u8> data;
explicit SampleDataU8(std::vector<u8> data) : data(std::move(data)) {}
[[nodiscard]] std::span<std::byte const> get_data() const override {
return as_bytes(std::span(data));
}
};
struct SampleReference : JASSampleDataReference {
explicit SampleReference(std::shared_ptr<SampleDataBuf> data) : data(std::move(data)) {}
std::shared_ptr<SampleDataBuf> data;
};
struct AudioWaveKey {
AudioWaveBank bank;
u32 wave_id;
AudioWaveKey(AudioWaveBank bank, u32 wave_id) : bank(bank), wave_id(wave_id) {};
template <typename H>
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<SampleDataBuf> 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<JASSampleDataReference> getSampleReference() const override;
JASWaveInfo wave_info;
std::shared_ptr<SampleDataBuf> data;
};
extern absl::flat_hash_map<AudioWaveKey, AudioWaveReplacementValue> 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<SampleDataBuf> data;
};
using ContainerLoadFunction = ModResult (*)(LoadedMod const& mod, RuntimeWaveReplacementSlot& slot, std::span<u8 const> fileData);
ModResult load_wav(LoadedMod const& mod, RuntimeWaveReplacementSlot& slot, std::span<u8 const> fileData);
ModResult load_opus(LoadedMod const& mod, RuntimeWaveReplacementSlot& slot, std::span<u8 const> 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);
}