From 3ec02994fb42b095b084d3e22933812e29c3cd64 Mon Sep 17 00:00:00 2001 From: Demur Rumed Date: Fri, 25 Apr 2025 20:05:50 +0000 Subject: [PATCH] store sfx as raw data, removes explicit dr_wav dependency --- .gitmodules | 3 - soh/include/dr_libs | 1 - .../AccessibleAudioEngine.cpp | 31 +++--- .../accessible-actors/AccessibleAudioEngine.h | 14 +-- .../accessible-actors/ActorAccessibility.cpp | 19 ++-- .../accessible-actors/SfxExtractor.cpp | 94 ++++++------------- .../accessible-actors/SfxExtractor.h | 28 +++++- soh/soh/Enhancements/audio/AudioDecoder.cpp | 56 ++++++----- soh/soh/Enhancements/audio/AudioDecoder.h | 3 +- soh/soh/Enhancements/audio/miniaudio.h | 9 ++ soh/soh/OTRGlobals.cpp | 2 +- 11 files changed, 124 insertions(+), 136 deletions(-) delete mode 160000 soh/include/dr_libs create mode 100644 soh/soh/Enhancements/audio/miniaudio.h diff --git a/.gitmodules b/.gitmodules index ec4585a8d..1a4f40487 100644 --- a/.gitmodules +++ b/.gitmodules @@ -7,9 +7,6 @@ [submodule "OTRExporter"] path = OTRExporter url = https://github.com/harbourmasters/OTRExporter -[submodule "dr_libs"] - path = soh/include/dr_libs - url = https://github.com/mackron/dr_libs [submodule "miniaudio"] path = soh/include/miniaudio url = https://github.com/mackron/miniaudio diff --git a/soh/include/dr_libs b/soh/include/dr_libs deleted file mode 160000 index 9cb7092ac..000000000 --- a/soh/include/dr_libs +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 9cb7092ac8c75a82b5c6ea72652ca8d0091d7ffa diff --git a/soh/soh/Enhancements/accessible-actors/AccessibleAudioEngine.cpp b/soh/soh/Enhancements/accessible-actors/AccessibleAudioEngine.cpp index 160835fe4..f63ed5ca9 100644 --- a/soh/soh/Enhancements/accessible-actors/AccessibleAudioEngine.cpp +++ b/soh/soh/Enhancements/accessible-actors/AccessibleAudioEngine.cpp @@ -1,10 +1,3 @@ -#define MINIAUDIO_IMPLEMENTATION -#define MA_NO_THREADING -#define MA_NO_DEVICE_IO -#define MA_NO_GENERATION -#define MA_NO_FLAC -#define MA_NO_MP3 -#define MA_ENABLE_ONLY_SPECIFIC_BACKENDS #define AAE_CHANNELS 2 #define AAE_SAMPLE_RATE 44100 #define AAE_MAX_BUFFER_SIZE AAE_SAMPLE_RATE / 10 @@ -23,6 +16,8 @@ int AudioPlayer_GetDesiredBuffered(); #include #include #include +#include + enum AAE_COMMANDS { AAE_START = 0, AAE_STOP, @@ -123,10 +118,9 @@ uint32_t AccessibleAudioEngine::retrieve(float* buffer, uint32_t nFrames) { if (nFrames == 0) return 0; uint32_t ogNFrames = nFrames; - uint32_t framesObtained = 0; while (nFrames > 0) { void* readBuffer; - framesObtained = nFrames; + uint32_t framesObtained = nFrames; ma_pcm_rb_acquire_read(&preparedOutput, &framesObtained, (void**)&readBuffer); if (framesObtained > nFrames) framesObtained = nFrames; @@ -246,6 +240,7 @@ void AccessibleAudioEngine::runThread() { } } } + SoundSlot* AccessibleAudioEngine::findSound(SoundAction& action) { if (action.slot < 0 || action.slot >= AAE_SLOTS_PER_HANDLE) return NULL; @@ -257,6 +252,7 @@ SoundSlot* AccessibleAudioEngine::findSound(SoundAction& action) { return NULL; return ⌖ } + void AccessibleAudioEngine::doPlaySound(SoundAction& action) { SoundSlot* sound; if (sounds.contains(action.handle)) { @@ -266,7 +262,6 @@ void AccessibleAudioEngine::doPlaySound(SoundAction& action) { destroySound(sound); } } - else { SoundSlots temp; for (int i = 0; i < AAE_SLOTS_PER_HANDLE; i++) @@ -275,10 +270,14 @@ void AccessibleAudioEngine::doPlaySound(SoundAction& action) { sounds[action.handle] = temp; sound = &sounds[action.handle][action.slot]; } - if (ma_sound_init_from_file(&engine, action.path.c_str(), + + ma_result result = ma_sound_init_from_file(&engine, action.path.c_str(), MA_SOUND_FLAG_NO_SPATIALIZATION | MA_SOUND_FLAG_NO_DEFAULT_ATTACHMENT, NULL, NULL, - &sound->sound) != MA_SUCCESS) + &sound->sound); + if (result != MA_SUCCESS) { + SPDLOG_ERROR("failed to play sound: {}", ma_result_description(result)); return; + } initSoundExtras(sound); ma_sound_set_looping(&sound->sound, action.looping); @@ -289,6 +288,7 @@ void AccessibleAudioEngine::doPlaySound(SoundAction& action) { sound->active = true; } + void AccessibleAudioEngine::doStopSound(SoundAction& action) { SoundSlot* slot = findSound(action); if (slot == NULL) @@ -416,6 +416,7 @@ bool AccessibleAudioEngine::initSoundExtras(SoundSlot* slot) { ma_node_attach_output_bus(&slot->sound, 0, &slot->extras, 0); return true; } + void AccessibleAudioEngine::destroySound(SoundSlot* slot) { ma_node_detach_all_output_buses(&slot->extras); ma_sound_uninit(&slot->sound); @@ -461,7 +462,6 @@ AccessibleAudioEngine::~AccessibleAudioEngine() { destroy(); } void AccessibleAudioEngine::mix(int16_t* ogBuffer, uint32_t nFrames) { - uint32_t framesAvailable = ma_pcm_rb_available_read(&preparedOutput); float sourceChunk[AAE_MIX_CHUNK_SIZE * AAE_CHANNELS]; float mixedChunk[AAE_MIX_CHUNK_SIZE * AAE_CHANNELS]; while (nFrames > 0) { @@ -596,9 +596,4 @@ void AccessibleAudioEngine::prepare() { action.command = AAE_PREPARE; // This is called once at the end of every frame, so now is the time to post all of the accumulated commands. postSoundActions(); -} - -void AccessibleAudioEngine::cacheDecodedSample(const char* path, void* data, size_t size) { - // data stored as wave, so we register it with MiniAudio as an encoded asset as opposed to a decoded one - ma_resource_manager_register_encoded_data(&resourceManager, path, data, size); } \ No newline at end of file diff --git a/soh/soh/Enhancements/accessible-actors/AccessibleAudioEngine.h b/soh/soh/Enhancements/accessible-actors/AccessibleAudioEngine.h index c7a841205..16f6826b4 100644 --- a/soh/soh/Enhancements/accessible-actors/AccessibleAudioEngine.h +++ b/soh/soh/Enhancements/accessible-actors/AccessibleAudioEngine.h @@ -1,11 +1,4 @@ #pragma once - -#define MA_NO_FLAC -#define MA_NO_MP3 -#define MA_NO_THREADING -#define MA_NO_DEVICE_IO -#define MA_NO_GENERATION -#include "miniaudio/miniaudio.h" #include #include #include @@ -14,6 +7,9 @@ #include #include #include + +#include "soh/Enhancements/audio/miniaudio.h" + #define AAE_SOUND_ACTION_BATCH_SIZE 64 #define AAE_SLOTS_PER_HANDLE 16 class IResource; @@ -71,7 +67,6 @@ typedef std::array SoundSlots; class AccessibleAudioEngine { int initialized; - ma_resource_manager resourceManager; ma_engine engine; ma_pcm_rb preparedOutput; // Lock-free single producer single consumer. std::deque soundActions; // A command cue. @@ -152,5 +147,6 @@ class AccessibleAudioEngine { float maxDistance); // Schedule the preparation of output for delivery. void prepare(); - void cacheDecodedSample(const char* path, void* data, size_t size); + + ma_resource_manager resourceManager; }; diff --git a/soh/soh/Enhancements/accessible-actors/ActorAccessibility.cpp b/soh/soh/Enhancements/accessible-actors/ActorAccessibility.cpp index 85a8d197e..99d1f0c35 100644 --- a/soh/soh/Enhancements/accessible-actors/ActorAccessibility.cpp +++ b/soh/soh/Enhancements/accessible-actors/ActorAccessibility.cpp @@ -90,7 +90,7 @@ class ActorAccessibility { // Maps internal sfx to external (prerendered) resources. std::unordered_map sfxMap; // Similar to above, but this one maps raw audio samples as opposed to SFX. - std::unordered_set sampleMap; + std::unordered_map> sampleMap; int extractSfx = 0; s16 currentScene = -1; s8 currentRoom = -1; @@ -706,16 +706,17 @@ const char* ActorAccessibility_MapSfxToExternalAudio(s16 sfxId) { std::stringstream ss; ss << std::setw(4) << std::setfill('0') << std::hex << sfxId; tempRecord.path = ss.str(); - aa->sfxMap[sfxId] = tempRecord; - record = &aa->sfxMap[sfxId]; - aa->audioEngine->cacheDecodedSample(record->path.c_str(), record->resource->Buffer->data(), - record->resource->Buffer->size()); + auto pair = aa->sfxMap.insert({ sfxId, tempRecord }); + record = &pair.first->second; + ma_resource_manager_register_decoded_data(&aa->audioEngine->resourceManager, record->path.c_str(), + record->resource->Buffer->data(), record->resource->Buffer->size() / 2, ma_format_s16, 1, 44100); } else { record = &it->second; } return record->path.c_str(); } + // Map the path to a raw sample to the external audio engine. const char* ActorAccessibility_MapRawSampleToExternalAudio(const char* name) { auto it = aa->sampleMap.find(name); @@ -728,11 +729,9 @@ const char* ActorAccessibility_MapRawSampleToExternalAudio(const char* name) { return NULL; // Resource doesn't exist, user's gotta run the extractor. AudioDecoder decoder; decoder.setSample((SOH::AudioSample*)res.get()); - // TODO track wav somehow & free it with drwav_free - s16* wav; - size_t wavSize = decoder.decodeToWav(&wav); - aa->sampleMap.insert(name); - aa->audioEngine->cacheDecodedSample(name, wav, wavSize); + auto pair = aa->sampleMap.insert({ name, decoder.decodeToWav() }); + ma_resource_manager_register_encoded_data(&aa->audioEngine->resourceManager, name, + pair.first->second.data(), pair.first->second.size()); } return name; diff --git a/soh/soh/Enhancements/accessible-actors/SfxExtractor.cpp b/soh/soh/Enhancements/accessible-actors/SfxExtractor.cpp index ebaf32b52..e92c9d2e3 100644 --- a/soh/soh/Enhancements/accessible-actors/SfxExtractor.cpp +++ b/soh/soh/Enhancements/accessible-actors/SfxExtractor.cpp @@ -1,14 +1,8 @@ #include "SfxExtractor.h" +#include "soh/Enhancements/audio/AudioDecoder.h" +#include "soh/Enhancements/audio/miniaudio.h" #include "soh/Enhancements/speechsynthesizer/SpeechSynthesizer.h" #include "soh/Enhancements/tts/tts.h" -#include "dr_libs/dr_wav.h" -#define MA_NO_FLAC -#define MA_NO_MP3 -#define MA_NO_THREADING -#define MA_NO_DEVICE_IO -#define MA_NO_GENERATION -#define MA_ENABLE_ONLY_SPECIFIC_BACKENDS -#include "miniaudio/miniaudio.h" #include "soh/OTRGlobals.h" #include "SfxTable.h" #include @@ -19,23 +13,7 @@ extern "C" { void AudioMgr_CreateNextAudioBuffer(s16* samples, u32 num_samples); extern bool freezeGame; } -enum { - STEP_SETUP = 0, - STEP_MAIN, - STEP_FINISHED, - STEP_ERROR, - STEP_ERROR_OTR, // File exists. -} SFX_EXTRACTION_STEPS; -enum { - CT_WAITING, // for a sound to start ripping. - CT_PRIMING, - CT_READY, // to start ripping a sound. - CT_FINISHED, // ripping the current sound. - CT_SHUTDOWN, -} CAPTURE_THREAD_STATES; -#define SFX_EXTRACTION_BUFFER_SIZE 44100 * 15 -#define SFX_EXTRACTION_ONE_FRAME 736 bool SfxExtractor::isAllZero(int16_t* buffer, size_t count) { for (size_t i = 0; i < count; i++) { if (buffer[i] != 0) @@ -71,34 +49,22 @@ bool SfxExtractor::renderOutput(size_t endOfInput) { ma_channel_converter_config config = ma_channel_converter_config_init(ma_format_s16, 2, NULL, 1, NULL, ma_channel_mix_mode_default); ma_channel_converter converter; - if (ma_channel_converter_init(&config, NULL, &converter) != MA_SUCCESS) - throw std::runtime_error("SfxExtractor: Unable to initialize channel converter."); - drwav_data_format format; - format.bitsPerSample = 16; - format.channels = 1; - format.container = drwav_container_riff; - format.format = DR_WAVE_FORMAT_PCM; - format.sampleRate = 44100; - drwav wav; + if (ma_channel_converter_init(&config, NULL, &converter) != MA_SUCCESS) { + return false; + } + std::vector fileData; std::string fileName = getExternalFileName(currentSfx); - void* mem = NULL; - size_t size = 0; - - if (!drwav_init_memory_write(&wav, &mem, &size, &format, NULL)) - throw std::runtime_error("SfxExtractor: Unable to initialize wave writer."); int16_t chunk[64]; int16_t* mark = tempBuffer + startOfInput; - size_t samplesLeft = endOfInput - startOfInput; - while (samplesLeft > 0) { - size_t thisChunk = std::min(64, samplesLeft); - ma_channel_converter_process_pcm_frames(&converter, chunk, mark, thisChunk / 2); - drwav_write_pcm_frames(&wav, thisChunk / 2, chunk); - samplesLeft -= thisChunk; - mark += thisChunk; + while (mark < tempBuffer + endOfInput) { + size_t chunkSize = std::min(64, ((tempBuffer + endOfInput) - mark) / 2); + ma_result converter_result = ma_channel_converter_process_pcm_frames(&converter, chunk, mark, chunkSize); + if (converter_result != MA_SUCCESS) { + return false; + } + fileData.insert(fileData.end(), (uint8_t*)chunk, (uint8_t*)(chunk + chunkSize)); + mark += chunkSize * 2; } - drwav_uninit(&wav); - std::vector fileData((uint8_t*)mem, (uint8_t*)mem + size); - drwav_free(mem, nullptr); return archive->WriteFile(fileName.c_str(), fileData); } @@ -110,18 +76,15 @@ void SfxExtractor::setup() { captureThreadState = CT_WAITING; OTRAudio_InstallSfxCaptureThread(); // Make sure we're starting from a clean slate. - std::string sohAccessibilityPath = Ship::Context::GetPathRelativeToAppDirectory("accessibility.o2r"); + std::string sohAccessibilityPath = Ship::Context::GetPathRelativeToAppBundle("accessibility.o2r"); if (std::filesystem::exists(sohAccessibilityPath)) { - currentStep = STEP_ERROR_OTR; + currentStep = STEP_ERROR_FILE_EXISTS; return; } - // Over-allocated just a tad because otherwise we'll overrun if the last frame is short. - tempStorage.resize((SFX_EXTRACTION_BUFFER_SIZE + (SFX_EXTRACTION_ONE_FRAME * 3)) * 2, 0); - tempBuffer = tempStorage.data(); sfxToRip = 0; currentStep = STEP_MAIN; - archive = std::make_shared("accessibility.o2r"); + archive = std::make_shared(sohAccessibilityPath); archive->Open(); } catch (...) { currentStep = STEP_ERROR; } } @@ -166,7 +129,7 @@ void SfxExtractor::finished() { Audio_QueueSeqCmd(NA_BGM_TITLE); - if (currentStep == STEP_ERROR || currentStep == STEP_ERROR_OTR) { + if (currentStep >= STEP_ERROR) { Audio_PlaySoundGeneral(NA_SE_SY_ERROR, &gSfxDefaultPos, 4, &gSfxDefaultFreqAndVolScale, &gSfxDefaultFreqAndVolScale, &gSfxDefaultReverb); Audio_PlaySoundGeneral(NA_SE_EN_GANON_LAUGH, &gSfxDefaultPos, 4, &gSfxDefaultFreqAndVolScale, @@ -174,7 +137,7 @@ void SfxExtractor::finished() { std::stringstream ss; ss << "Sorry, we tried to extract the sound effects, but Ganondorf overruled us with an iron fist." << std::endl; - if (currentStep == STEP_ERROR_OTR) + if (currentStep == STEP_ERROR_FILE_EXISTS) ss << "In all seriousness, please delete accessibility.o2r and try again."; SpeechSynthesizer::Instance->Speak(ss.str().c_str(), "en-US"); } else @@ -209,8 +172,8 @@ void SfxExtractor::frameCallback() { void SfxExtractor::prime() { while (true) { - AudioMgr_CreateNextAudioBuffer(tempBuffer, SFX_EXTRACTION_ONE_FRAME); - if (isAllZero(tempBuffer, SFX_EXTRACTION_ONE_FRAME * 2)) + AudioMgr_CreateNextAudioBuffer(tempBuffer + 0, SFX_EXTRACTION_ONE_FRAME); + if (isAllZero(tempBuffer + 0, SFX_EXTRACTION_ONE_FRAME * 2)) break; } captureThreadState = CT_FINISHED; @@ -222,10 +185,9 @@ void SfxExtractor::captureCallback() { if (captureThreadState != CT_READY) return; // No work to do at the moment. memset(tempBuffer, 0, SFX_EXTRACTION_BUFFER_SIZE * 4); - int16_t* mark = tempBuffer; + int16_t* mark = tempBuffer + 0; size_t samplesLeft = SFX_EXTRACTION_BUFFER_SIZE; bool outputStarted = false; - size_t endOfInput = 0; int waitTime = 0; while (samplesLeft > 0) { AudioMgr_CreateNextAudioBuffer(mark, SFX_EXTRACTION_ONE_FRAME); @@ -241,19 +203,19 @@ void SfxExtractor::captureCallback() { } outputStarted = true; - mark += (SFX_EXTRACTION_ONE_FRAME * 2); - endOfInput += (SFX_EXTRACTION_ONE_FRAME * 2); - samplesLeft -= std::min(SFX_EXTRACTION_ONE_FRAME, samplesLeft); + size_t samples = std::min(SFX_EXTRACTION_ONE_FRAME, samplesLeft); + mark += samples * 2; + samplesLeft -= samples; } - if (renderOutput(endOfInput)) { + if (renderOutput(mark - tempBuffer)) { captureThreadState = CT_FINISHED; } else { SPDLOG_ERROR("failed to write file to archive, trying again"); } } + std::string SfxExtractor::getExternalFileName(int16_t sfxId) { std::stringstream ss; - ss << "accessibility/audio/"; - ss << std::hex << std::setw(4) << std::setfill('0') << sfxId << ".wav"; + ss << "accessibility/audio/" << std::hex << std::setw(4) << std::setfill('0') << sfxId << ".wav"; return ss.str(); } diff --git a/soh/soh/Enhancements/accessible-actors/SfxExtractor.h b/soh/soh/Enhancements/accessible-actors/SfxExtractor.h index ce1b80e03..34d62a043 100644 --- a/soh/soh/Enhancements/accessible-actors/SfxExtractor.h +++ b/soh/soh/Enhancements/accessible-actors/SfxExtractor.h @@ -1,13 +1,33 @@ #pragma once #include "libultraship/libultraship.h" + +#define SFX_EXTRACTION_BUFFER_SIZE 44100 * 15 +#define SFX_EXTRACTION_ONE_FRAME 736 + +enum CaptureThreadStates { + CT_WAITING, // for a sound to start ripping. + CT_PRIMING, + CT_READY, // to start ripping a sound. + CT_FINISHED, // ripping the current sound. + CT_SHUTDOWN, +}; + +enum SfxExtractionSteps { + STEP_SETUP = 0, + STEP_MAIN, + STEP_FINISHED, + STEP_ERROR, + STEP_ERROR_FILE_EXISTS, +}; + class SfxExtractor { std::shared_ptr archive; - int currentStep; - int captureThreadState; + SfxExtractionSteps currentStep; + CaptureThreadStates captureThreadState; int sfxToRip; s16 currentSfx; - std::vector tempStorage; // Stores raw audio data for the sfx currently being ripped. - int16_t* tempBuffer; // Raw pointer to the above vector. + // Stores raw audio data for the sfx currently being ripped. + int16_t tempBuffer[(SFX_EXTRACTION_BUFFER_SIZE + SFX_EXTRACTION_ONE_FRAME * 3) * 2]; // Check if a buffer contains meaningful audio output. bool isAllZero(int16_t* buffer, size_t count); size_t adjustedStartOfInput(); diff --git a/soh/soh/Enhancements/audio/AudioDecoder.cpp b/soh/soh/Enhancements/audio/AudioDecoder.cpp index 710d2ebe8..80f7b753e 100644 --- a/soh/soh/Enhancements/audio/AudioDecoder.cpp +++ b/soh/soh/Enhancements/audio/AudioDecoder.cpp @@ -1,7 +1,6 @@ +#define MINIAUDIO_IMPLEMENTATION #include "AudioDecoder.h" #include "z64audio.h" -#define DR_WAV_IMPLEMENTATION -#include "dr_libs/dr_wav.h" #include #define WAV_DECODE_CHUNK_SIZE 64 // A handful of definitions need to be copied from mixer.c. @@ -23,6 +22,7 @@ AudioDecoder::AudioDecoder() { } AudioDecoder::~AudioDecoder() { } + void AudioDecoder::setSample(SOH::AudioSample* sample) { this->sample.codec = sample->sample.codec; this->sample.loop.start = sample->sample.loop->start; @@ -40,6 +40,7 @@ void AudioDecoder::setSample(SOH::AudioSample* sample) { inStart = in; inEnd = in + sample->sample.size; } + void AudioDecoder::setSample(SoundFontSample* sample) { this->sample.codec = sample->codec; this->sample.loop.start = sample->loop->start; @@ -56,6 +57,7 @@ void AudioDecoder::setSample(SoundFontSample* sample) { inStart = in; inEnd = in + sample->size; } + size_t AudioDecoder::decode(int16_t* out, size_t nSamples) { size_t samplesOut = 0; size_t nbytes = nSamples * 2; @@ -101,26 +103,39 @@ size_t AudioDecoder::decode(int16_t* out, size_t nSamples) { return samplesOut; } -size_t AudioDecoder::decodeToWav(int16_t** buffer) { - int16_t* wavOut = nullptr; +ma_result wavWrite(ma_encoder* pEncoder, const void* pBufferIn, size_t bytesToWrite, size_t* pBytesWritten) { + auto fileData = (std::vector*)pEncoder->pUserData; + fileData->insert(fileData->end(), (uint8_t*)pBufferIn, (uint8_t*)pBufferIn + bytesToWrite); + if (pBytesWritten != NULL) { + *pBytesWritten = bytesToWrite; + } + return MA_SUCCESS; +} - drwav_data_format format; - format.bitsPerSample = 16; - format.channels = 1; - format.container = drwav_container_riff; - format.format = DR_WAVE_FORMAT_PCM; +ma_result wavSeek(ma_encoder* pEncoder, ma_int64 offset, ma_seek_origin origin) { + return MA_ERROR; +} + +std::vector AudioDecoder::decodeToWav() { + std::vector fileData; + + ma_uint32 sampleRate; // Todo: figure out how to really determine the sample rate. CODEC_ADPCM tends to stream at higher rates (usually // 20KHZ) while CODEC_SMALL_ADPCM is usually around 14000. They're still not consistent though. if (sample.codec == CODEC_ADPCM) - format.sampleRate = 20000; + sampleRate = 20000; else if (sample.codec = CODEC_SMALL_ADPCM) - format.sampleRate = 14000; + sampleRate = 14000; else throw std::runtime_error("AudioDecoder: Unsupported codec."); - drwav wav; - size_t wavSize; - if (!drwav_init_memory_write(&wav, (void**)&wavOut, &wavSize, &format, nullptr)) - throw std::runtime_error("AudioDecoder: Unable to initialize wave writer."); + + ma_encoder_config maconfig = ma_encoder_config_init(ma_encoding_format_wav, ma_format_s16, 1, sampleRate); + ma_encoder wavEncoder; + ma_result init_result = ma_encoder_init(wavWrite, wavSeek, &fileData, &maconfig, &wavEncoder); + if (init_result != MA_SUCCESS) { + return fileData; + } + int16_t chunk[WAV_DECODE_CHUNK_SIZE]; // Don't decode past the end of the loop. size_t samplesLeft = sample.loop.end; @@ -135,14 +150,9 @@ size_t AudioDecoder::decodeToWav(int16_t** buffer) { if (samplesRead == 0) break; - if (drwav_write_pcm_frames(&wav, samplesRead, chunk) != samplesRead) { - drwav_uninit(&wav); - drwav_free(wavOut, nullptr); - throw std::runtime_error("AudioDecoder: Unable to write wave data."); - } + ma_encoder_write_pcm_frames(&wavEncoder, chunk, samplesRead, NULL); samplesLeft -= samplesRead; } - drwav_uninit(&wav); - *buffer = wavOut; - return wavSize; + ma_encoder_uninit(&wavEncoder); + return fileData; } diff --git a/soh/soh/Enhancements/audio/AudioDecoder.h b/soh/soh/Enhancements/audio/AudioDecoder.h index fb2c021f2..8d611787a 100644 --- a/soh/soh/Enhancements/audio/AudioDecoder.h +++ b/soh/soh/Enhancements/audio/AudioDecoder.h @@ -2,6 +2,7 @@ // A standalone, incremental audio sample decoder. // Based on the ADPCM decoding routines in mixer.c. +#include "miniaudio.h" #include "libultraship/libultraship.h" #include "soh/resource/type/AudioSample.h" #include "z64audio.h" @@ -28,5 +29,5 @@ class AudioDecoder { void setSample(SoundFontSample* sample); size_t decode(int16_t* out, size_t nSamples); - size_t decodeToWav(int16_t** buffer); + std::vector decodeToWav(); }; \ No newline at end of file diff --git a/soh/soh/Enhancements/audio/miniaudio.h b/soh/soh/Enhancements/audio/miniaudio.h new file mode 100644 index 000000000..4c7e80aef --- /dev/null +++ b/soh/soh/Enhancements/audio/miniaudio.h @@ -0,0 +1,9 @@ +#pragma once +#define MA_NO_FLAC +#define MA_NO_MP3 +#define MA_NO_THREADING +#define MA_NO_DEVICE_IO +#define MA_NO_GENERATION +#define MA_NO_STDIO +#define MA_ENABLE_ONLY_SPECIFIC_BACKENDS +#include "miniaudio/miniaudio.h" \ No newline at end of file diff --git a/soh/soh/OTRGlobals.cpp b/soh/soh/OTRGlobals.cpp index 9ff253c3c..528c02f5d 100644 --- a/soh/soh/OTRGlobals.cpp +++ b/soh/soh/OTRGlobals.cpp @@ -294,7 +294,7 @@ OTRGlobals::OTRGlobals() { } } - std::string sohAccessibilityPath = Ship::Context::GetPathRelativeToAppDirectory("accessibility.o2r"); + std::string sohAccessibilityPath = Ship::Context::GetPathRelativeToAppBundle("accessibility.o2r"); if (std::filesystem::exists(sohAccessibilityPath)) { OTRFiles.push_back(sohAccessibilityPath); }