From 3d90888bdcc3e0ae62358446a6dbeea4f4aa4b68 Mon Sep 17 00:00:00 2001 From: Louis <35883445+louist103@users.noreply.github.com> Date: Wed, 27 Nov 2024 20:25:08 -0500 Subject: [PATCH 1/9] Backport 2ship streamed audio --- CMake/FindOgg.cmake | 61 +++ CMake/FindVorbis.cmake | 210 +++++++++ CMakeLists.txt | 4 +- soh/CMakeLists.txt | 62 +-- soh/soh/OTRGlobals.cpp | 7 + soh/soh/OTRGlobals.h | 2 + .../resource/importer/AudioSampleFactory.cpp | 296 ++++++++++--- .../resource/importer/AudioSampleFactory.h | 10 + .../importer/AudioSequenceFactory.cpp | 399 +++++++++++++++++- .../resource/importer/AudioSequenceFactory.h | 11 + .../importer/AudioSoundFontFactory.cpp | 363 +++++++++++++--- .../resource/importer/AudioSoundFontFactory.h | 16 + soh/soh/resource/type/AudioSample.h | 102 ++--- soh/soh/resource/type/AudioSequence.h | 4 +- soh/soh/resource/type/AudioSoundFont.cpp | 17 + soh/soh/resource/type/AudioSoundFont.h | 13 +- soh/src/code/audio_heap.c | 4 +- soh/src/code/audio_load.c | 109 +++-- soh/src/code/audio_playback.c | 13 +- soh/src/code/audio_seqplayer.c | 2 +- soh/src/code/audio_synthesis.c | 30 +- soh/src/code/code_800E4FE0.c | 2 +- 22 files changed, 1461 insertions(+), 276 deletions(-) create mode 100644 CMake/FindOgg.cmake create mode 100644 CMake/FindVorbis.cmake diff --git a/CMake/FindOgg.cmake b/CMake/FindOgg.cmake new file mode 100644 index 00000000000..9cf5ce43099 --- /dev/null +++ b/CMake/FindOgg.cmake @@ -0,0 +1,61 @@ +# - Find ogg +# Find the native ogg includes and libraries +# +# OGG_INCLUDE_DIRS - where to find ogg.h, etc. +# OGG_LIBRARIES - List of libraries when using ogg. +# OGG_FOUND - True if ogg found. + +if (OGG_INCLUDE_DIR) + # Already in cache, be silent + set(OGG_FIND_QUIETLY TRUE) +endif () + +find_package (PkgConfig QUIET) +pkg_check_modules (PC_OGG QUIET ogg>=1.3.0) + +set (OGG_VERSION ${PC_OGG_VERSION}) + +find_path (OGG_INCLUDE_DIR ogg/ogg.h + HINTS + ${PC_OGG_INCLUDEDIR} + ${PC_OGG_INCLUDE_DIRS} + ${OGG_ROOT} + ) +# MSVC built ogg may be named ogg_static. +# The provided project files name the library with the lib prefix. +find_library (OGG_LIBRARY + NAMES + ogg + ogg_static + libogg + libogg_static + HINTS + ${PC_OGG_LIBDIR} + ${PC_OGG_LIBRARY_DIRS} + ${OGG_ROOT} + ) +# Handle the QUIETLY and REQUIRED arguments and set OGG_FOUND +# to TRUE if all listed variables are TRUE. +include (FindPackageHandleStandardArgs) +find_package_handle_standard_args (Ogg + REQUIRED_VARS + OGG_LIBRARY + OGG_INCLUDE_DIR + VERSION_VAR + OGG_VERSION + ) + +if (OGG_FOUND) + set (OGG_LIBRARIES ${OGG_LIBRARY}) + set (OGG_INCLUDE_DIRS ${OGG_INCLUDE_DIR}) + + if(NOT TARGET Ogg::ogg) + add_library(Ogg::ogg UNKNOWN IMPORTED) + set_target_properties(Ogg::ogg PROPERTIES + INTERFACE_INCLUDE_DIRECTORIES "${OGG_INCLUDE_DIRS}" + IMPORTED_LOCATION "${OGG_LIBRARIES}" + ) + endif () +endif () + +mark_as_advanced (OGG_INCLUDE_DIR OGG_LIBRARY) diff --git a/CMake/FindVorbis.cmake b/CMake/FindVorbis.cmake new file mode 100644 index 00000000000..57e60557f71 --- /dev/null +++ b/CMake/FindVorbis.cmake @@ -0,0 +1,210 @@ +#[=======================================================================[.rst: +FindVorbis +---------- + +Finds the native vorbis, vorbisenc amd vorbisfile includes and libraries. + +Imported Targets +^^^^^^^^^^^^^^^^ + +This module provides the following imported targets, if found: + +``Vorbis::vorbis`` + The Vorbis library +``Vorbis::vorbisenc`` + The VorbisEnc library +``Vorbis::vorbisfile`` + The VorbisFile library + +Result Variables +^^^^^^^^^^^^^^^^ + +This will define the following variables: + +``Vorbis_Vorbis_INCLUDE_DIRS`` + List of include directories when using vorbis. +``Vorbis_Enc_INCLUDE_DIRS`` + List of include directories when using vorbisenc. +``Vorbis_File_INCLUDE_DIRS`` + List of include directories when using vorbisfile. +``Vorbis_Vorbis_LIBRARIES`` + List of libraries when using vorbis. +``Vorbis_Enc_LIBRARIES`` + List of libraries when using vorbisenc. +``Vorbis_File_LIBRARIES`` + List of libraries when using vorbisfile. +``Vorbis_FOUND`` + True if vorbis and requested components found. +``Vorbis_Vorbis_FOUND`` + True if vorbis found. +``Vorbis_Enc_FOUND`` + True if vorbisenc found. +``Vorbis_Enc_FOUND`` + True if vorbisfile found. + +Cache variables +^^^^^^^^^^^^^^^ + +The following cache variables may also be set: + +``Vorbis_Vorbis_INCLUDE_DIR`` + The directory containing ``vorbis/vorbis.h``. +``Vorbis_Enc_INCLUDE_DIR`` + The directory containing ``vorbis/vorbisenc.h``. +``Vorbis_File_INCLUDE_DIR`` + The directory containing ``vorbis/vorbisenc.h``. +``Vorbis_Vorbis_LIBRARY`` + The path to the vorbis library. +``Vorbis_Enc_LIBRARY`` + The path to the vorbisenc library. +``Vorbis_File_LIBRARY`` + The path to the vorbisfile library. + +Hints +^^^^^ + +A user may set ``Vorbis_ROOT`` to a vorbis installation root to tell this module where to look. + +#]=======================================================================] + +if (Vorbis_Vorbis_INCLUDE_DIR) + # Already in cache, be silent + set (Vorbis_FIND_QUIETLY TRUE) +endif () + +set (Vorbis_Vorbis_FIND_QUIETLY TRUE) +set (Vorbis_Enc_FIND_QUIETLY TRUE) +set (Vorbis_File_FIND_QUIETLY TRUE) + +find_package (Ogg QUIET) + +find_package (PkgConfig QUIET) +pkg_check_modules (PC_Vorbis_Vorbis QUIET vorbis) +pkg_check_modules (PC_Vorbis_Enc QUIET vorbisenc) +pkg_check_modules (PC_Vorbis_File QUIET vorbisfile) + +set (Vorbis_VERSION ${PC_Vorbis_Vorbis_VERSION}) + +find_path (Vorbis_Vorbis_INCLUDE_DIR vorbis/codec.h + HINTS + ${PC_Vorbis_Vorbis_INCLUDEDIR} + ${PC_Vorbis_Vorbis_INCLUDE_DIRS} + ${Vorbis_ROOT} + ) + +find_path (Vorbis_Enc_INCLUDE_DIR vorbis/vorbisenc.h + HINTS + ${PC_Vorbis_Enc_INCLUDEDIR} + ${PC_Vorbis_Enc_INCLUDE_DIRS} + ${Vorbis_ROOT} + ) + +find_path (Vorbis_File_INCLUDE_DIR vorbis/vorbisfile.h + HINTS + ${PC_Vorbis_File_INCLUDEDIR} + ${PC_Vorbis_File_INCLUDE_DIRS} + ${Vorbis_ROOT} + ) + +find_library (Vorbis_Vorbis_LIBRARY + NAMES + vorbis + vorbis_static + libvorbis + libvorbis_static + HINTS + ${PC_Vorbis_Vorbis_LIBDIR} + ${PC_Vorbis_Vorbis_LIBRARY_DIRS} + ${Vorbis_ROOT} + ) + +find_library (Vorbis_Enc_LIBRARY + NAMES + vorbisenc + vorbisenc_static + libvorbisenc + libvorbisenc_static + HINTS + ${PC_Vorbis_Enc_LIBDIR} + ${PC_Vorbis_Enc_LIBRARY_DIRS} + ${Vorbis_ROOT} + ) + +find_library (Vorbis_File_LIBRARY + NAMES + vorbisfile + vorbisfile_static + libvorbisfile + libvorbisfile_static + HINTS + ${PC_Vorbis_File_LIBDIR} + ${PC_Vorbis_File_LIBRARY_DIRS} + ${Vorbis_ROOT} + ) + +include (FindPackageHandleStandardArgs) + +if (Vorbis_Vorbis_LIBRARY AND Vorbis_Vorbis_INCLUDE_DIR AND Ogg_FOUND) + set (Vorbis_Vorbis_FOUND TRUE) +endif () + +if (Vorbis_Enc_LIBRARY AND Vorbis_Enc_INCLUDE_DIR AND Vorbis_Vorbis_FOUND) + set (Vorbis_Enc_FOUND TRUE) +endif () + +if (Vorbis_Vorbis_FOUND AND Vorbis_File_LIBRARY AND Vorbis_File_INCLUDE_DIR) + set (Vorbis_File_FOUND TRUE) +endif () + +find_package_handle_standard_args (Vorbis + REQUIRED_VARS + Vorbis_Vorbis_LIBRARY + Vorbis_Vorbis_INCLUDE_DIR + Ogg_FOUND + HANDLE_COMPONENTS + VERSION_VAR Vorbis_VERSION) + + +if (Vorbis_Vorbis_FOUND) + set (Vorbis_Vorbis_INCLUDE_DIRS ${VORBIS_INCLUDE_DIR}) + set (Vorbis_Vorbis_LIBRARIES ${VORBIS_LIBRARY} ${OGG_LIBRARIES}) + if (NOT TARGET Vorbis::vorbis) + add_library (Vorbis::vorbis UNKNOWN IMPORTED) + set_target_properties (Vorbis::vorbis PROPERTIES + INTERFACE_INCLUDE_DIRECTORIES "${Vorbis_Vorbis_INCLUDE_DIR}" + IMPORTED_LOCATION "${Vorbis_Vorbis_LIBRARY}" + INTERFACE_LINK_LIBRARIES Ogg::ogg + ) + endif () + + if (Vorbis_Enc_FOUND) + set (Vorbis_Enc_INCLUDE_DIRS ${Vorbis_Enc_INCLUDE_DIR}) + set (Vorbis_Enc_LIBRARIES ${Vorbis_Enc_LIBRARY} ${Vorbis_Enc_LIBRARIES}) + if (NOT TARGET Vorbis::vorbisenc) + add_library (Vorbis::vorbisenc UNKNOWN IMPORTED) + set_target_properties (Vorbis::vorbisenc PROPERTIES + INTERFACE_INCLUDE_DIRECTORIES "${Vorbis_Enc_INCLUDE_DIR}" + IMPORTED_LOCATION "${Vorbis_Enc_LIBRARY}" + INTERFACE_LINK_LIBRARIES Vorbis::vorbis + ) + endif () + endif () + + if (Vorbis_File_FOUND) + set (Vorbis_File_INCLUDE_DIRS ${Vorbis_File_INCLUDE_DIR}) + set (Vorbis_File_LIBRARIES ${Vorbis_File_LIBRARY} ${Vorbis_File_LIBRARIES}) + if (NOT TARGET Vorbis::vorbisfile) + add_library (Vorbis::vorbisfile UNKNOWN IMPORTED) + set_target_properties (Vorbis::vorbisfile PROPERTIES + INTERFACE_INCLUDE_DIRECTORIES "${Vorbis_File_INCLUDE_DIR}" + IMPORTED_LOCATION "${Vorbis_File_LIBRARY}" + INTERFACE_LINK_LIBRARIES Vorbis::vorbis + ) + endif () + endif () + +endif () + +mark_as_advanced (Vorbis_Vorbis_INCLUDE_DIR Vorbis_Vorbis_LIBRARY) +mark_as_advanced (Vorbis_Enc_INCLUDE_DIR Vorbis_Enc_LIBRARY) +mark_as_advanced (Vorbis_File_INCLUDE_DIR Vorbis_File_LIBRARY) diff --git a/CMakeLists.txt b/CMakeLists.txt index 2bdaf1de99f..6e200ac6ad1 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -79,13 +79,15 @@ if (CMAKE_SYSTEM_NAME STREQUAL "Windows") set(VCPKG_TARGET_TRIPLET x64-windows-static) vcpkg_bootstrap() - vcpkg_install_packages(zlib bzip2 libzip libpng sdl2 sdl2-net glew glfw3 nlohmann-json tinyxml2 spdlog) + vcpkg_install_packages(zlib bzip2 libzip libpng sdl2 sdl2-net glew glfw3 nlohmann-json tinyxml2 spdlog libogg libvorbis) if (CMAKE_C_COMPILER_LAUNCHER MATCHES "ccache|sccache") set(CMAKE_MSVC_DEBUG_INFORMATION_FORMAT Embedded) endif() endif() +list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/CMake") + ################################################################################ # Set target arch type if empty. Visual studio solution generator provides it. ################################################################################ diff --git a/soh/CMakeLists.txt b/soh/CMakeLists.txt index 935f32ea1ee..77bd280d636 100644 --- a/soh/CMakeLists.txt +++ b/soh/CMakeLists.txt @@ -29,6 +29,13 @@ if (CMAKE_SYSTEM_NAME STREQUAL "Windows") endif() endif() +FetchContent_Declare( + dr_libs + GIT_REPOSITORY https://github.com/mackron/dr_libs.git + GIT_TAG da35f9d6c7374a95353fd1df1d394d44ab66cf01 +) +FetchContent_MakeAvailable(dr_libs) + ################################################################################ # Global configuration types ################################################################################ @@ -350,6 +357,7 @@ target_include_directories(${PROJECT_NAME} PRIVATE assets ${SDL2-NET-INCLUDE} ${BOOST-INCLUDE} ${CMAKE_CURRENT_SOURCE_DIR}/assets/ + ${dr_libs_SOURCE_DIR} . ) @@ -639,34 +647,28 @@ endif() if (CMAKE_SYSTEM_NAME STREQUAL "Windows") find_package(glfw3 REQUIRED) - if("${CMAKE_VS_PLATFORM_NAME}" STREQUAL "x64") - set(ADDITIONAL_LIBRARY_DEPENDENCIES - "libultraship;" - "ZAPDLib;" - "glu32;" - "SDL2::SDL2;" - "SDL2::SDL2main;" - "$<$:SDL2_net::SDL2_net-static>" - "glfw;" - "winmm;" - "imm32;" - "version;" - "setupapi" - ) - elseif("${CMAKE_VS_PLATFORM_NAME}" STREQUAL "Win32") - set(ADDITIONAL_LIBRARY_DEPENDENCIES - "libultraship;" - "ZAPDLib;" - "glu32;" - "SDL2::SDL2;" - "SDL2::SDL2main;" - "glfw;" - "winmm;" - "imm32;" - "version;" - "setupapi" - ) - endif() + find_package(Ogg CONFIG REQUIRED) + link_libraries(Ogg::ogg) + + find_package(Vorbis CONFIG REQUIRED) + link_libraries(Vorbis::vorbisfile) + set(ADDITIONAL_LIBRARY_DEPENDENCIES + "libultraship;" + "ZAPDLib;" + "glu32;" + "SDL2::SDL2;" + "SDL2::SDL2main;" + "$<$:SDL2_net::SDL2_net-static>" + "glfw;" + "winmm;" + "imm32;" + "version;" + "setupapi" + "Ogg::ogg" + "Vorbis::vorbis" + "Vorbis::vorbisenc" + "Vorbis::vorbisfile" + ) elseif(CMAKE_SYSTEM_NAME STREQUAL "NintendoSwitch") find_package(SDL2) set(THREADS_PREFER_PTHREAD_FLAG ON) @@ -695,6 +697,10 @@ else() set(ADDITIONAL_LIBRARY_DEPENDENCIES "libultraship;" "ZAPDLib;" + "Ogg::ogg" + "Vorbis::vorbis" + "Vorbis::vorbisenc" + "Vorbis::vorbisfile" SDL2::SDL2 "$<$:SDL2_net::SDL2_net>" ${CMAKE_DL_LIBS} diff --git a/soh/soh/OTRGlobals.cpp b/soh/soh/OTRGlobals.cpp index 8fd8177402d..13855f92f8d 100644 --- a/soh/soh/OTRGlobals.cpp +++ b/soh/soh/OTRGlobals.cpp @@ -382,8 +382,11 @@ OTRGlobals::OTRGlobals() { loader->RegisterResourceFactory(std::make_shared(), RESOURCE_FORMAT_BINARY, "Text", static_cast(SOH::ResourceType::SOH_Text), 0); loader->RegisterResourceFactory(std::make_shared(), RESOURCE_FORMAT_XML, "Text", static_cast(SOH::ResourceType::SOH_Text), 0); loader->RegisterResourceFactory(std::make_shared(), RESOURCE_FORMAT_BINARY, "AudioSample", static_cast(SOH::ResourceType::SOH_AudioSample), 2); + loader->RegisterResourceFactory(std::make_shared(), RESOURCE_FORMAT_XML, "Sample", static_cast(SOH::ResourceType::SOH_AudioSample), 0); loader->RegisterResourceFactory(std::make_shared(), RESOURCE_FORMAT_BINARY, "AudioSoundFont", static_cast(SOH::ResourceType::SOH_AudioSoundFont), 2); + loader->RegisterResourceFactory(std::make_shared(), RESOURCE_FORMAT_XML, "SoundFont", static_cast(SOH::ResourceType::SOH_AudioSoundFont), 0); loader->RegisterResourceFactory(std::make_shared(), RESOURCE_FORMAT_BINARY, "AudioSequence", static_cast(SOH::ResourceType::SOH_AudioSequence), 2); + loader->RegisterResourceFactory(std::make_shared(), RESOURCE_FORMAT_XML, "Sequence", static_cast(SOH::ResourceType::SOH_AudioSequence), 0); loader->RegisterResourceFactory(std::make_shared(), RESOURCE_FORMAT_BINARY, "Background", static_cast(SOH::ResourceType::SOH_Background), 0); gSaveStateMgr = std::make_shared(); @@ -1832,6 +1835,10 @@ extern "C" void AudioPlayer_Play(const uint8_t* buf, uint32_t len) { AudioPlayerPlayFrame(buf, len); } +extern "C" void Messagebox_ShowErrorBox(char* title, char* body) { + Extractor::ShowErrorBox(title, body); +} + extern "C" int Controller_ShouldRumble(size_t slot) { for (auto [id, mapping] : Ship::Context::GetInstance() ->GetControlDeck() diff --git a/soh/soh/OTRGlobals.h b/soh/soh/OTRGlobals.h index 854ef5b8fc2..be029f3b4a3 100644 --- a/soh/soh/OTRGlobals.h +++ b/soh/soh/OTRGlobals.h @@ -155,6 +155,8 @@ void Gfx_TextureCacheDelete(const uint8_t* addr); void SaveManager_ThreadPoolWait(); void CheckTracker_OnMessageClose(); +void Messagebox_ShowErrorBox(char* title, char* body); + GetItemID RetrieveGetItemIDFromItemID(ItemID itemID); RandomizerGet RetrieveRandomizerGetFromItemID(ItemID itemID); #endif diff --git a/soh/soh/resource/importer/AudioSampleFactory.cpp b/soh/soh/resource/importer/AudioSampleFactory.cpp index 0ec3f526018..a66e9fc998e 100644 --- a/soh/soh/resource/importer/AudioSampleFactory.cpp +++ b/soh/soh/resource/importer/AudioSampleFactory.cpp @@ -1,6 +1,132 @@ #include "soh/resource/importer/AudioSampleFactory.h" #include "soh/resource/type/AudioSample.h" -#include "spdlog/spdlog.h" +#include "soh/resource/importer/AudioSoundFontFactory.h" +#include "StringHelper.h" +#include "libultraship/libultraship.h" +#include "z64audio.h" +#define DR_WAV_IMPLEMENTATION +#include + +#define DR_MP3_IMPLEMENTATION +#include + +#define DR_FLAC_IMPLEMENTATION +#include + +#include "vorbis/vorbisfile.h" + +struct OggFileData { + void* data; + size_t pos; + size_t size; +}; + +static size_t VorbisReadCallback(void* out, size_t size, size_t elems, void* src) { + OggFileData* data = static_cast(src); + size_t toRead = size * elems; + + if (toRead > data->size - data->pos) { + toRead = data->size - data->pos; + } + + memcpy(out, static_cast(data->data) + data->pos, toRead); + data->pos += toRead; + + return toRead / size; +} + +static int VorbisSeekCallback(void* src, ogg_int64_t pos, int whence) { + OggFileData* data = static_cast(src); + size_t newPos; + + switch (whence) { + case SEEK_SET: + newPos = pos; + break; + case SEEK_CUR: + newPos = data->pos + pos; + break; + case SEEK_END: + newPos = data->size + pos; + break; + default: + return -1; + } + if (newPos > data->size) { + return -1; + } + data->pos = newPos; + return 0; +} + +static int VorbisCloseCallback([[maybe_unused]] void* src) { + return 0; +} + +static long VorbisTellCallback(void* src) { + OggFileData* data = static_cast(src); + return data->pos; +} + +static const ov_callbacks vorbisCallbacks = { + VorbisReadCallback, + VorbisSeekCallback, + VorbisCloseCallback, + VorbisTellCallback, +}; + +static void Mp3DecoderWorker(std::shared_ptr audioSample, std::shared_ptr sampleFile) { + drmp3 mp3; + drwav_uint64 numFrames; + drmp3_bool32 ret = + drmp3_init_memory(&mp3, sampleFile->Buffer.get()->data(), sampleFile->Buffer.get()->size(), nullptr); + numFrames = drmp3_get_pcm_frame_count(&mp3); + drwav_uint64 channels = mp3.channels; + drwav_uint64 sampleRate = mp3.sampleRate; + + audioSample->audioSampleData.reserve(numFrames * channels * 2); // 2 for sizeof(s16) + drmp3_read_pcm_frames_s16(&mp3, numFrames, (int16_t*)audioSample->audioSampleData.data()); + audioSample->sample.sampleAddr = audioSample->audioSampleData.data(); +} + +static void FlacDecoderWorker(std::shared_ptr audioSample, std::shared_ptr sampleFile) { + drflac* flac = drflac_open_memory(sampleFile->Buffer.get()->data(), sampleFile->Buffer.get()->size(), nullptr); + drflac_uint64 numFrames = flac->totalPCMFrameCount; + audioSample->audioSampleData.reserve(numFrames * flac->channels * 2); + drflac_read_pcm_frames_s16(flac, numFrames, (int16_t*)audioSample->audioSampleData.data()); + audioSample->sample.sampleAddr = audioSample->audioSampleData.data(); + drflac_close(flac); +} + +static void OggDecoderWorker(std::shared_ptr audioSample, std::shared_ptr sampleFile) { + OggVorbis_File vf; + char dataBuff[4096]; + long read = 0; + size_t pos = 0; + + OggFileData fileData = { + .data = sampleFile->Buffer.get()->data(), + .pos = 0, + .size = sampleFile->Buffer.get()->size(), + }; + int ret = ov_open_callbacks(&fileData, &vf, nullptr, 0, vorbisCallbacks); + + vorbis_info* vi = ov_info(&vf, -1); + + uint64_t numFrames = ov_pcm_total(&vf, -1); + uint64_t sampleRate = vi->rate; + uint64_t numChannels = vi->channels; + int bitStream = 0; + size_t toRead = numFrames * numChannels * 2; + audioSample->audioSampleData.reserve(toRead); + do { + read = ov_read(&vf, dataBuff, 4096, 0, 2, 1, &bitStream); + memcpy(audioSample->audioSampleData.data() + pos, dataBuff, read); + pos += read; + } while (read != 0); + audioSample->sample.sampleAddr = audioSample->audioSampleData.data(); + ov_clear(&vf); +} namespace SOH { std::shared_ptr ResourceFactoryBinaryAudioSampleV2::ReadResource(std::shared_ptr file) { @@ -49,75 +175,117 @@ std::shared_ptr ResourceFactoryBinaryAudioSampleV2::ReadResourc return audioSample; } -} // namespace SOH -/* -in ResourceMgr_LoadAudioSample we used to have --------------- - if (cachedCustomSFs.find(path) != cachedCustomSFs.end()) - return cachedCustomSFs[path]; +std::shared_ptr ResourceFactoryXMLAudioSampleV0::ReadResource(std::shared_ptr file) { + if (!FileHasValidFormatAndReader(file)) { + return nullptr; + } - SoundFontSample* cSample = ReadCustomSample(path); + auto audioSample = std::make_shared(file->InitData); + auto child = std::get>(file->Reader)->FirstChildElement(); + std::shared_ptr initData = std::make_shared(); + const char* customFormatStr = child->Attribute("CustomFormat"); + memset(&audioSample->sample, 0, sizeof(audioSample->sample)); + audioSample->sample.codec = CodecStrToInt(child->Attribute("Codec")); + audioSample->sample.medium = ResourceFactoryXMLSoundFontV0::MediumStrToInt(child->Attribute("Medium")); + audioSample->sample.unk_bit25 = child->IntAttribute("Relocated"); + audioSample->sample.unk_bit26 = child->IntAttribute("bit26"); + audioSample->loop.start = child->IntAttribute("LoopStart"); + audioSample->loop.end = child->IntAttribute("LoopEnd"); + audioSample->loop.count = child->IntAttribute("LoopCount"); - if (cSample != nullptr) - return cSample; --------------- -before the rest of the standard sample reading, this is the ReadCustomSample code we used to have + // CODEC_ADPCM || CODEC_SMALL_ADPCM + memset(audioSample->loop.state, 0, sizeof(audioSample->loop.state)); + if (audioSample->sample.codec == 0 || audioSample->sample.codec == 3) { + tinyxml2::XMLElement* loopElement = child->FirstChildElement(); + size_t i = 0; + if (loopElement != nullptr) { + while (strcmp(loopElement->Name(), "LoopState") == 0) { + audioSample->loop.state[i] = loopElement->IntAttribute("Loop"); + loopElement = loopElement->NextSiblingElement(); + i++; + } + audioSample->loop.count = i; + i = 0; + while (loopElement != nullptr && strcmp(loopElement->Name(), "Books") == 0) { + audioSample->bookData.push_back(loopElement->IntAttribute("Book")); + loopElement = loopElement->NextSiblingElement(); + i++; + } + } + audioSample->book.npredictors = child->IntAttribute("Npredictors"); + audioSample->book.order = child->IntAttribute("Order"); -extern "C" SoundFontSample* ReadCustomSample(const char* path) { + audioSample->book.book = audioSample->bookData.data(); + audioSample->sample.book = &audioSample->book; + } + audioSample->sample.loop = &audioSample->loop; + size_t size = child->Int64Attribute("SampleSize"); + audioSample->sample.size = size; - if (!ExtensionCache.contains(path)) - return nullptr; + const char* path = child->Attribute("SamplePath"); + initData->Path = path; + initData->IsCustom = false; + initData->ByteOrder = Ship::Endianness::Native; + auto sampleFile = Ship::Context::GetInstance()->GetResourceManager()->GetArchiveManager()->LoadFile(path, initData); + if (customFormatStr != nullptr) { + // Compressed files can take a really long time to decode (~250ms per). + // This worked when we tested it (09/04/2024) (Works on my machine) + if (strcmp(customFormatStr, "wav") == 0) { + drwav wav; + drwav_uint64 numFrames; + + drwav_bool32 ret = + drwav_init_memory(&wav, sampleFile->Buffer.get()->data(), sampleFile->Buffer.get()->size(), nullptr); - ExtensionEntry entry = ExtensionCache[path]; - - auto sampleRaw = Ship::Context::GetInstance()->GetResourceManager()->LoadFile(entry.path); - uint32_t* strem = (uint32_t*)sampleRaw->Buffer.get(); - uint8_t* strem2 = (uint8_t*)strem; - - SoundFontSample* sampleC = new SoundFontSample; - - if (entry.ext == "wav") { - drwav_uint32 channels; - drwav_uint32 sampleRate; - drwav_uint64 totalPcm; - drmp3_int16* pcmData = - drwav_open_memory_and_read_pcm_frames_s16(strem2, sampleRaw->BufferSize, &channels, &sampleRate, &totalPcm, NULL); - sampleC->size = totalPcm; - sampleC->sampleAddr = (uint8_t*)pcmData; - sampleC->codec = CODEC_S16; - - sampleC->loop = new AdpcmLoop; - sampleC->loop->start = 0; - sampleC->loop->end = sampleC->size - 1; - sampleC->loop->count = 0; - sampleC->sampleRateMagicValue = 'RIFF'; - sampleC->sampleRate = sampleRate; - - cachedCustomSFs[path] = sampleC; - return sampleC; - } else if (entry.ext == "mp3") { - drmp3_config mp3Info; - drmp3_uint64 totalPcm; - drmp3_int16* pcmData = - drmp3_open_memory_and_read_pcm_frames_s16(strem2, sampleRaw->BufferSize, &mp3Info, &totalPcm, NULL); - - sampleC->size = totalPcm * mp3Info.channels * sizeof(short); - sampleC->sampleAddr = (uint8_t*)pcmData; - sampleC->codec = CODEC_S16; - - sampleC->loop = new AdpcmLoop; - sampleC->loop->start = 0; - sampleC->loop->end = sampleC->size; - sampleC->loop->count = 0; - sampleC->sampleRateMagicValue = 'RIFF'; - sampleC->sampleRate = mp3Info.sampleRate; - - cachedCustomSFs[path] = sampleC; - return sampleC; + drwav_get_length_in_pcm_frames(&wav, &numFrames); + + audioSample->tuning = (wav.sampleRate * wav.channels) / 32000.0f; + audioSample->audioSampleData.reserve(numFrames * wav.channels * 2); + + drwav_read_pcm_frames_s16(&wav, numFrames, (int16_t*)audioSample->audioSampleData.data()); + audioSample->sample.sampleAddr = audioSample->audioSampleData.data(); + return audioSample; + } else if (strcmp(customFormatStr, "mp3") == 0) { + std::thread fileDecoderThread = std::thread(Mp3DecoderWorker, audioSample, sampleFile); + fileDecoderThread.detach(); + return audioSample; + } else if (strcmp(customFormatStr, "ogg") == 0) { + std::thread fileDecoderThread = std::thread(OggDecoderWorker, audioSample, sampleFile); + fileDecoderThread.detach(); + return audioSample; + } else if (strcmp(customFormatStr, "flac") == 0) { + std::thread fileDecoderThread = std::thread(FlacDecoderWorker, audioSample, sampleFile); + fileDecoderThread.detach(); + return audioSample; + } + } + // Not a normal streamed sample. Fallback to the original ADPCM sample to be decoded by the audio engine. + audioSample->audioSampleData.resize(size); + // Can't use memcpy due to endianness issues. + for (uint32_t i = 0; i < size; i++) { + audioSample->audioSampleData[i] = sampleFile->Buffer.get()->data()[i]; } + audioSample->sample.sampleAddr = audioSample->audioSampleData.data(); - return nullptr; + return audioSample; } - -*/ +#include +uint8_t ResourceFactoryXMLAudioSampleV0::CodecStrToInt(const char* str) { + if (strcmp("ADPCM", str) == 0) { + return CODEC_ADPCM; + } else if (strcmp("S8", str) == 0) { + return CODEC_S8; + } else if (strcmp("S16MEM", str) == 0) { + return CODEC_S16_INMEMORY; + } else if (strcmp("ADPCMSMALL", str) == 0) { + return CODEC_SMALL_ADPCM; + } else if (strcmp("REVERB", str) == 0) { + return CODEC_REVERB; + } else if (strcmp("S16", str) == 0) { + return CODEC_S16; + } else { + assert(0); + } +} +} // namespace SOH diff --git a/soh/soh/resource/importer/AudioSampleFactory.h b/soh/soh/resource/importer/AudioSampleFactory.h index 372e8a31041..1d241e8478a 100644 --- a/soh/soh/resource/importer/AudioSampleFactory.h +++ b/soh/soh/resource/importer/AudioSampleFactory.h @@ -2,10 +2,20 @@ #include "Resource.h" #include "ResourceFactoryBinary.h" +#include "ResourceFactoryXML.h" namespace SOH { class ResourceFactoryBinaryAudioSampleV2 : public Ship::ResourceFactoryBinary { public: std::shared_ptr ReadResource(std::shared_ptr file) override; }; + +class ResourceFactoryXMLAudioSampleV0 : public Ship::ResourceFactoryXML { + public: + std::shared_ptr ReadResource(std::shared_ptr file) override; + + private: + uint8_t CodecStrToInt(const char* str); +}; + } // namespace SOH diff --git a/soh/soh/resource/importer/AudioSequenceFactory.cpp b/soh/soh/resource/importer/AudioSequenceFactory.cpp index 35da7f798db..b190a6af74c 100644 --- a/soh/soh/resource/importer/AudioSequenceFactory.cpp +++ b/soh/soh/resource/importer/AudioSequenceFactory.cpp @@ -1,6 +1,10 @@ #include "soh/resource/importer/AudioSequenceFactory.h" #include "soh/resource/type/AudioSequence.h" -#include "spdlog/spdlog.h" +#include "BinaryWriter.h" +#include "StringHelper.h" +#include "libultraship/libultraship.h" +#include "BinaryWriter.h" +#include namespace SOH { std::shared_ptr ResourceFactoryBinaryAudioSequenceV2::ReadResource(std::shared_ptr file) { @@ -11,13 +15,13 @@ std::shared_ptr ResourceFactoryBinaryAudioSequenceV2::ReadResou auto audioSequence = std::make_shared(file->InitData); auto reader = std::get>(file->Reader); - audioSequence->sequence.seqDataSize = reader->ReadInt32(); + audioSequence->sequence.seqDataSize = reader->ReadUInt32(); audioSequence->sequenceData.reserve(audioSequence->sequence.seqDataSize); for (uint32_t i = 0; i < audioSequence->sequence.seqDataSize; i++) { audioSequence->sequenceData.push_back(reader->ReadChar()); } audioSequence->sequence.seqData = audioSequence->sequenceData.data(); - + audioSequence->sequence.seqNumber = reader->ReadUByte(); audioSequence->sequence.medium = reader->ReadUByte(); audioSequence->sequence.cachePolicy = reader->ReadUByte(); @@ -32,4 +36,393 @@ std::shared_ptr ResourceFactoryBinaryAudioSequenceV2::ReadResou return audioSequence; } + +int8_t SOH::ResourceFactoryXMLAudioSequenceV0::MediumStrToInt(const char* str) { + if (!strcmp("Ram", str)) { + return 0; + } else if (!strcmp("Unk", str)) { + return 1; + } else if (!strcmp("Cart", str)) { + return 2; + } else if (!strcmp("Disk", str)) { + return 3; + // 4 is skipped + } else if (!strcmp("RamUnloaded", str)) { + return 5; + } else { + throw std::runtime_error( + StringHelper::Sprintf("Bad medium value. Got %s, expected Ram, Unk, Cart, or Disk.", str)); + } +} + +int8_t ResourceFactoryXMLAudioSequenceV0::CachePolicyToInt(const char* str) { + if (!strcmp("Temporary", str)) { + return 0; + } else if (!strcmp("Persistent", str)) { + return 1; + } else if (!strcmp("Either", str)) { + return 2; + } else if (!strcmp("Permanent", str)) { + return 3; + } else { + throw std::runtime_error(StringHelper::Sprintf( + "Bad cache policy value. Got %s, expected Temporary, Persistent, Either, or Permanent.", str)); + } +} + +template static void WriteInsnOneArg(Ship::BinaryWriter* writer, uint8_t opcode, T arg) { + static_assert(std::is_fundamental::value); + writer->Write(opcode); + writer->Write(arg); +} + +template +static void WriteInsnTwoArg(Ship::BinaryWriter* writer, uint8_t opcode, T1 arg1, T2 arg2) { + static_assert(std::is_fundamental::value && std::is_fundamental::value); + writer->Write(opcode); + writer->Write(arg1); + writer->Write(arg2); +} + +template +static void WriteInsnThreeArg(Ship::BinaryWriter* writer, uint8_t opcode, T1 arg1, T2 arg2, T3 arg3) { + static_assert(std::is_fundamental::value && std::is_fundamental::value); + writer->Write(opcode); + writer->Write(arg1); + writer->Write(arg2); + writer->Write(arg3); +} + +static void WriteInsnNoArg(Ship::BinaryWriter* writer, uint8_t opcode) { + writer->Write(opcode); +} + +static void WriteLegato(Ship::BinaryWriter* writer) { + WriteInsnNoArg(writer, 0xC4); +} + +static void WriteNoLegato(Ship::BinaryWriter* writer) { + WriteInsnNoArg(writer, 0xC5); +} + +static void WriteMuteBhv(Ship::BinaryWriter* writer, uint8_t arg) { + WriteInsnOneArg(writer, 0xD3, arg); +} + +static void WriteMuteScale(Ship::BinaryWriter* writer, uint8_t arg) { + WriteInsnOneArg(writer, 0xD5, arg); +} + +static void WriteInitchan(Ship::BinaryWriter* writer, uint16_t channels) { + WriteInsnOneArg(writer, 0xD7, channels); +} + +static void WriteLdchan(Ship::BinaryWriter* writer, uint8_t channel, uint16_t offset) { + WriteInsnOneArg(writer, 0x90 | channel, offset); +} + +static void WriteVolSHeader(Ship::BinaryWriter* writer, uint8_t vol) { + WriteInsnOneArg(writer, 0xDB, vol); +} + +static void WriteVolCHeader(Ship::BinaryWriter* writer, uint8_t vol) { + WriteInsnOneArg(writer, 0xDF, vol); +} + +static void WriteTempo(Ship::BinaryWriter* writer, uint8_t tempo) { + WriteInsnOneArg(writer, 0xDD, tempo); +} + +static void WriteJump(Ship::BinaryWriter* writer, uint16_t offset) { + WriteInsnOneArg(writer, 0xFB, offset); +} + +static void WriteDisablecan(Ship::BinaryWriter* writer, uint16_t channels) { + WriteInsnOneArg(writer, 0xD6, channels); +} + +static void WriteNoshort(Ship::BinaryWriter* writer) { + WriteInsnNoArg(writer, 0xC4); +} + +static void WriteLdlayer(Ship::BinaryWriter* writer, uint8_t layer, uint16_t offset) { + WriteInsnOneArg(writer, 0x88 | layer, offset); +} + +static void WritePan(Ship::BinaryWriter* writer, uint8_t pan) { + WriteInsnOneArg(writer, 0xDD, pan); +} + +static void WriteBend(Ship::BinaryWriter* writer, uint8_t bend) { + WriteInsnOneArg(writer, 0xD3, bend); +} + +static void WriteInstrument(Ship::BinaryWriter* writer, uint8_t instrument) { + WriteInsnOneArg(writer, 0xC1, instrument); +} + +static void WriteTranspose(Ship::BinaryWriter* writer, int8_t transpose) { + WriteInsnOneArg(writer, 0xC2, transpose); +} + +static void WriteDelay(Ship::BinaryWriter* writer, uint16_t delay) { + if (delay > 0x7F) { + WriteInsnOneArg(writer, 0xFD, static_cast(delay | 0x8000)); + } else { + WriteInsnOneArg(writer, 0xFD, static_cast(delay)); + } +} + +template static void WriteLDelay(Ship::BinaryWriter* writer, T delay) { + WriteInsnOneArg(writer, 0xC0, delay); +} + +template static void WriteNotedv(Ship::BinaryWriter* writer, uint8_t note, T delay, uint8_t velocity) { + WriteInsnTwoArg(writer, note, delay, velocity); +} + +static void WriteNotedvg(Ship::BinaryWriter* writer, uint8_t note, uint16_t delay, uint8_t velocity, uint8_t gateTime) { + if (delay > 0x7F) { + WriteInsnThreeArg(writer, note, static_cast(delay | 0x8000), velocity, gateTime); + } else { + WriteInsnThreeArg(writer, note, static_cast(delay), velocity, gateTime); + } +} + +static void WriteMonoSingleSeq(Ship::BinaryWriter* writer, uint16_t delay, uint8_t tempo, bool looped) { + uint16_t channelStart; + uint16_t channelPlaceholderOff; + uint16_t loopPoint; + uint16_t layerPlaceholderOff; + uint16_t layerStart; + if (looped) { + delay = 0x7FFF; + } + // Write seq header + + // These two values are always the same in OOT and MM + WriteMuteBhv(writer, 0x20); + WriteMuteScale(writer, 0x32); + + // We only have one channel + WriteInitchan(writer, 0b11); + // Store the current position so we can write the address of the channel when we are ready. + channelPlaceholderOff = writer->GetBaseAddress(); + // Store the current position so we can loop here after the song ends. + loopPoint = writer->GetBaseAddress(); + WriteLdchan(writer, 0, 0); // Fill in the actual address later + + WriteVolSHeader(writer, 127); // Max volume + WriteTempo(writer, tempo); + + WriteDelay(writer, delay); + if (looped) { + WriteJump(writer, loopPoint); + } + WriteDisablecan(writer, 0b11); + writer->Write(static_cast(0xFF)); + + // Fill in the ldchan from before + channelStart = writer->GetBaseAddress(); + writer->Seek(channelPlaceholderOff, Ship::SeekOffsetType::Start); + WriteLdchan(writer, 0, channelStart); + writer->Seek(channelStart, Ship::SeekOffsetType::Start); + + // Channel header + layerPlaceholderOff = writer->GetBaseAddress(); + WriteNoshort(writer); + WriteLdlayer(writer, 0, 0); + WritePan(writer, 64); + WriteVolCHeader(writer, 127); // Max volume + WriteBend(writer, 0); + WriteInstrument(writer, 0); + WriteDelay(writer, delay); + writer->Write(static_cast(0xFF)); + + layerStart = writer->GetBaseAddress(); + writer->Seek(layerPlaceholderOff, Ship::SeekOffsetType::Start); + WriteLdlayer(writer, 0, layerStart); + writer->Seek(layerStart, Ship::SeekOffsetType::Start); + + // Note layer + WriteLegato(writer); + WriteNotedvg(writer, 39, 0x7FFF - 1, static_cast(0x7F), static_cast(1)); + writer->Write(static_cast(0xFF)); +} + +static void WriteStereoSingleSeq(Ship::BinaryWriter* writer, uint16_t delay, uint8_t tempo, bool looped) { + uint16_t lChannelStart; + uint16_t rChannelStart; + uint16_t channelPlaceholderOff; + uint16_t loopPoint; + uint16_t lLayerPlaceholderOff; + uint16_t rLayerPlaceholderOff; + uint16_t lLayerOffset; + uint16_t rLayerOffset; + + uint16_t layerStart; + // Write seq header + if (looped) { + delay = 0x7FFF; + } + // These two values are always the same in OOT and MM + WriteMuteBhv(writer, 0x20); + WriteMuteScale(writer, 0x32); + + // We only have one channel + WriteInitchan(writer, 0b11); + // Store the current position so we can write the address of the channel when we are ready. + channelPlaceholderOff = writer->GetBaseAddress(); + // Store the current position so we can loop here after the song ends. + loopPoint = writer->GetBaseAddress(); + // Left note channel + WriteLdchan(writer, 0, 0); // Fill in the actual address later + // Right note channel + WriteLdchan(writer, 1, 0); // Fill in the actual address later + + WriteVolSHeader(writer, 127); // Max volume + WriteTempo(writer, tempo); + + WriteDelay(writer, delay); + if (looped) { + WriteJump(writer, loopPoint); + } + WriteDisablecan(writer, 0b11); + writer->Write(static_cast(0xFF)); + + lChannelStart = writer->GetBaseAddress(); + // Left Channel header + WriteNoshort(writer); + lLayerPlaceholderOff = writer->GetBaseAddress(); + WriteLdlayer(writer, 0, 0); + WritePan(writer, 0); + WriteVolCHeader(writer, 127); // Max volume + WriteBend(writer, 0); + WriteInstrument(writer, 0); + WriteDelay(writer, delay); + writer->Write(static_cast(0xFF)); + + rChannelStart = writer->GetBaseAddress(); + // Right Channel header + WriteNoshort(writer); + rLayerPlaceholderOff = writer->GetBaseAddress(); + WriteLdlayer(writer, 1, 0); + WritePan(writer, 127); + WriteVolCHeader(writer, 127); // Max volume + WriteBend(writer, 0); + WriteInstrument(writer, 1); + WriteDelay(writer, delay); + writer->Write(static_cast(0xFF)); + uint16_t placeHolder = writer->GetBaseAddress(); + writer->Seek(channelPlaceholderOff, Ship::SeekOffsetType::Start); + WriteLdchan(writer, 0, lChannelStart); + WriteLdchan(writer, 1, rChannelStart); + writer->Seek(placeHolder, Ship::SeekOffsetType::Start); + + // Left Note layer + lLayerOffset = writer->GetBaseAddress(); + WriteLegato(writer); + WriteNotedvg(writer, 39, 0x7FFF - 1, static_cast(0x7F), static_cast(1)); + writer->Write(static_cast(0xFF)); + + // Right Note layer + rLayerOffset = writer->GetBaseAddress(); + WriteLegato(writer); + WriteNotedvg(writer, 39, 0x7FFF - 1, static_cast(0x7F), static_cast(1)); + writer->Write(static_cast(0xFF)); + + writer->Seek(lLayerPlaceholderOff, Ship::SeekOffsetType::Start); + WriteLdlayer(writer, 0, lLayerOffset); + writer->Seek(rLayerPlaceholderOff, Ship::SeekOffsetType::Start); + WriteLdlayer(writer, 1, rLayerOffset); +} + +std::shared_ptr ResourceFactoryXMLAudioSequenceV0::ReadResource(std::shared_ptr file) { + if (!FileHasValidFormatAndReader(file)) { + return nullptr; + } + + auto sequence = std::make_shared(file->InitData); + auto child = std::get>(file->Reader)->FirstChildElement(); + unsigned int i = 0; + std::shared_ptr initData = std::make_shared(); + + sequence->sequence.medium = MediumStrToInt(child->Attribute("Medium")); + sequence->sequence.cachePolicy = CachePolicyToInt(child->Attribute("CachePolicy")); + sequence->sequence.seqDataSize = child->IntAttribute("Size"); + sequence->sequence.seqNumber = child->IntAttribute("Index"); + bool streamed = child->BoolAttribute("Streamed"); + + memset(sequence->sequence.fonts, 0, sizeof(sequence->sequence.fonts)); + + tinyxml2::XMLElement* fontsElement = child->FirstChildElement(); + tinyxml2::XMLElement* fontElement = fontsElement->FirstChildElement(); + while (fontElement != nullptr) { + sequence->sequence.fonts[i] = fontElement->IntAttribute("FontIdx"); + fontElement = fontElement->NextSiblingElement(); + i++; + } + sequence->sequence.numFonts = i; + + const char* path = child->Attribute("Path"); + std::shared_ptr seqFile; + if (path != nullptr) { + initData->Path = path; + initData->IsCustom = false; + initData->ByteOrder = Ship::Endianness::Native; + seqFile = Ship::Context::GetInstance()->GetResourceManager()->GetArchiveManager()->LoadFile(path, initData); + } + + if (!streamed) { + sequence->sequenceData = *seqFile->Buffer.get(); + sequence->sequence.seqData = sequence->sequenceData.data(); + } else { + // setting numFonts to -1 tells the game's audio engine the sound font to used is CRC64 encoded in the font + // indicies. + sequence->sequence.numFonts = -1; + if (path != nullptr) { + sequence->sequenceData = *seqFile->Buffer.get(); + sequence->sequence.seqData = sequence->sequenceData.data(); + sequence->sequence.seqDataSize = seqFile->Buffer.get()->size(); + } else { + unsigned int length = child->UnsignedAttribute("Length"); + bool looped = child->BoolAttribute("Looped", true); + bool stereo = child->BoolAttribute("Stereo", false); + Ship::BinaryWriter writer = Ship::BinaryWriter(); + writer.SetEndianness(Ship::Endianness::Big); + // Placeholder off is the offset of the instruction to be replaced. The second variable is the target adress + // of what we need to load of jump to + uint16_t loopPoint; + uint16_t channelPlaceholderOff, channelStart; + uint16_t layerPlaceholderOff, layerStart; + + // 1 second worth of ticks can be found by using `ticks = 60 / (bpm * 48)` + // Get the number of ticks per second and then divide the length by this number to get the number of ticks + // for the song. + constexpr uint8_t TEMPO = 1; + constexpr float TEMPO_F = TEMPO; + // Use floats for this first calculation so we can round up + float delayF = length / (60.0f / (TEMPO_F * 48.0f)); + // Convert to u16. This way this value is encoded changes depending on the value. + // It can be at most 0xFFFF so store it in a u16 for now. + uint16_t delay; + if (delayF >= 65535.0f) { + delay = 0x7FFF; + } else { + delay = delayF; + } + if (stereo) { + WriteStereoSingleSeq(&writer, delay, TEMPO, looped); + } else { + WriteMonoSingleSeq(&writer, delay, TEMPO, looped); + } + + sequence->sequenceData = writer.ToVector(); + sequence->sequence.seqData = sequence->sequenceData.data(); + sequence->sequence.seqDataSize = writer.ToVector().size(); + } + } + + return sequence; +} } // namespace SOH diff --git a/soh/soh/resource/importer/AudioSequenceFactory.h b/soh/soh/resource/importer/AudioSequenceFactory.h index 35fd1ebef54..58d5de975eb 100644 --- a/soh/soh/resource/importer/AudioSequenceFactory.h +++ b/soh/soh/resource/importer/AudioSequenceFactory.h @@ -2,10 +2,21 @@ #include "Resource.h" #include "ResourceFactoryBinary.h" +#include "ResourceFactoryXML.h" +#include "BinaryWriter.h" namespace SOH { class ResourceFactoryBinaryAudioSequenceV2 : public Ship::ResourceFactoryBinary { public: std::shared_ptr ReadResource(std::shared_ptr file) override; }; + +class ResourceFactoryXMLAudioSequenceV0 : public Ship::ResourceFactoryXML { + public: + std::shared_ptr ReadResource(std::shared_ptr file) override; + + private: + int8_t MediumStrToInt(const char* str); + int8_t CachePolicyToInt(const char* str); +}; } // namespace SOH diff --git a/soh/soh/resource/importer/AudioSoundFontFactory.cpp b/soh/soh/resource/importer/AudioSoundFontFactory.cpp index 534a7914c85..63715bb65b9 100644 --- a/soh/soh/resource/importer/AudioSoundFontFactory.cpp +++ b/soh/soh/resource/importer/AudioSoundFontFactory.cpp @@ -2,6 +2,8 @@ #include "soh/resource/type/AudioSoundFont.h" #include "spdlog/spdlog.h" #include "libultraship/libultraship.h" +#include "StringHelper.h" +#include "z64audio.h" namespace SOH { std::shared_ptr ResourceFactoryBinaryAudioSoundFontV2::ReadResource(std::shared_ptr file) { @@ -15,7 +17,7 @@ std::shared_ptr ResourceFactoryBinaryAudioSoundFontV2::ReadReso audioSoundFont->soundFont.fntIndex = reader->ReadInt32(); audioSoundFont->medium = reader->ReadInt8(); audioSoundFont->cachePolicy = reader->ReadInt8(); - + audioSoundFont->data1 = reader->ReadUInt16(); audioSoundFont->soundFont.sampleBankId1 = audioSoundFont->data1 >> 8; audioSoundFont->soundFont.sampleBankId2 = audioSoundFont->data1 & 0xFF; @@ -25,128 +27,114 @@ std::shared_ptr ResourceFactoryBinaryAudioSoundFontV2::ReadReso uint32_t drumCount = reader->ReadUInt32(); audioSoundFont->soundFont.numDrums = drumCount; - + uint32_t instrumentCount = reader->ReadUInt32(); audioSoundFont->soundFont.numInstruments = instrumentCount; - + uint32_t soundEffectCount = reader->ReadUInt32(); audioSoundFont->soundFont.numSfx = soundEffectCount; // πŸ₯ DRUMS πŸ₯ - audioSoundFont->drums.reserve(audioSoundFont->soundFont.numDrums); audioSoundFont->drumAddresses.reserve(audioSoundFont->soundFont.numDrums); for (uint32_t i = 0; i < audioSoundFont->soundFont.numDrums; i++) { - Drum drum; - drum.releaseRate = reader->ReadUByte(); - drum.pan = reader->ReadUByte(); - drum.loaded = reader->ReadUByte(); - drum.loaded = 0; // this was always getting set to zero in ResourceMgr_LoadAudioSoundFont + Drum* drum = new Drum; + drum->releaseRate = reader->ReadUByte(); + drum->pan = reader->ReadUByte(); + drum->loaded = reader->ReadUByte(); + drum->loaded = 0; // this was always getting set to zero in ResourceMgr_LoadAudioSoundFontByName uint32_t envelopeCount = reader->ReadUInt32(); - audioSoundFont->drumEnvelopeCounts.push_back(envelopeCount); - std::vector drumEnvelopes; - drumEnvelopes.reserve(audioSoundFont->drumEnvelopeCounts[i]); - for (uint32_t j = 0; j < audioSoundFont->drumEnvelopeCounts.back(); j++) { - AdsrEnvelope env; - + drum->envelope = new AdsrEnvelope[envelopeCount]; + for (uint32_t j = 0; j < envelopeCount; j++) { int16_t delay = reader->ReadInt16(); int16_t arg = reader->ReadInt16(); - env.delay = BE16SWAP(delay); - env.arg = BE16SWAP(arg); - - drumEnvelopes.push_back(env); + drum->envelope[j].delay = BE16SWAP(delay); + drum->envelope[j].arg = BE16SWAP(arg); } - audioSoundFont->drumEnvelopeArrays.push_back(drumEnvelopes); - drum.envelope = audioSoundFont->drumEnvelopeArrays.back().data(); bool hasSample = reader->ReadInt8(); std::string sampleFileName = reader->ReadString(); - drum.sound.tuning = reader->ReadFloat(); + drum->sound.tuning = reader->ReadFloat(); if (sampleFileName.empty()) { - drum.sound.sample = nullptr; + drum->sound.sample = nullptr; } else { auto res = Ship::Context::GetInstance()->GetResourceManager()->LoadResourceProcess(sampleFileName.c_str()); - drum.sound.sample = static_cast(res ? res->GetRawPointer() : nullptr); + drum->sound.sample = static_cast(res ? res->GetRawPointer() : nullptr); } - audioSoundFont->drums.push_back(drum); - audioSoundFont->drumAddresses.push_back(&audioSoundFont->drums.back()); + audioSoundFont->drumAddresses.push_back(drum); } audioSoundFont->soundFont.drums = audioSoundFont->drumAddresses.data(); // 🎺🎻🎷🎸🎹 INSTRUMENTS 🎹🎸🎷🎻🎺 - audioSoundFont->instruments.reserve(audioSoundFont->soundFont.numInstruments); for (uint32_t i = 0; i < audioSoundFont->soundFont.numInstruments; i++) { - Instrument instrument; + Instrument* instrument = new Instrument; uint8_t isValidEntry = reader->ReadUByte(); - instrument.loaded = reader->ReadUByte(); - instrument.loaded = 0; // this was always getting set to zero in ResourceMgr_LoadAudioSoundFont + instrument->loaded = reader->ReadUByte(); + instrument->loaded = 0; // this was always getting set to zero in ResourceMgr_LoadAudioSoundFontByName - instrument.normalRangeLo = reader->ReadUByte(); - instrument.normalRangeHi = reader->ReadUByte(); - instrument.releaseRate = reader->ReadUByte(); + instrument->normalRangeLo = reader->ReadUByte(); + instrument->normalRangeHi = reader->ReadUByte(); + instrument->releaseRate = reader->ReadUByte(); uint32_t envelopeCount = reader->ReadInt32(); - audioSoundFont->instrumentEnvelopeCounts.push_back(envelopeCount); - std::vector instrumentEnvelopes; - for (uint32_t j = 0; j < audioSoundFont->instrumentEnvelopeCounts.back(); j++) { - AdsrEnvelope env; + instrument->envelope = new AdsrEnvelope[envelopeCount]; + for (uint32_t j = 0; j < envelopeCount; j++) { int16_t delay = reader->ReadInt16(); int16_t arg = reader->ReadInt16(); - env.delay = BE16SWAP(delay); - env.arg = BE16SWAP(arg); - - instrumentEnvelopes.push_back(env); + instrument->envelope[j].delay = BE16SWAP(delay); + instrument->envelope[j].arg = BE16SWAP(arg); } - audioSoundFont->instrumentEnvelopeArrays.push_back(instrumentEnvelopes); - instrument.envelope = audioSoundFont->instrumentEnvelopeArrays.back().data(); bool hasLowNoteSoundFontEntry = reader->ReadInt8(); if (hasLowNoteSoundFontEntry) { bool hasSampleRef = reader->ReadInt8(); std::string sampleFileName = reader->ReadString(); - instrument.lowNotesSound.tuning = reader->ReadFloat(); + instrument->lowNotesSound.tuning = reader->ReadFloat(); auto res = Ship::Context::GetInstance()->GetResourceManager()->LoadResourceProcess(sampleFileName.c_str()); - instrument.lowNotesSound.sample = static_cast(res ? res->GetRawPointer() : nullptr); + instrument->lowNotesSound.sample = static_cast(res ? res->GetRawPointer() : nullptr); } else { - instrument.lowNotesSound.sample = nullptr; - instrument.lowNotesSound.tuning = 0; + instrument->lowNotesSound.sample = nullptr; + instrument->lowNotesSound.tuning = 0; } bool hasNormalNoteSoundFontEntry = reader->ReadInt8(); if (hasNormalNoteSoundFontEntry) { + // BENTODO remove in V3 bool hasSampleRef = reader->ReadInt8(); std::string sampleFileName = reader->ReadString(); - instrument.normalNotesSound.tuning = reader->ReadFloat(); + instrument->normalNotesSound.tuning = reader->ReadFloat(); auto res = Ship::Context::GetInstance()->GetResourceManager()->LoadResourceProcess(sampleFileName.c_str()); - instrument.normalNotesSound.sample = static_cast(res ? res->GetRawPointer() : nullptr); + instrument->normalNotesSound.sample = static_cast(res ? res->GetRawPointer() : nullptr); } else { - instrument.normalNotesSound.sample = nullptr; - instrument.normalNotesSound.tuning = 0; + instrument->normalNotesSound.sample = nullptr; + instrument->normalNotesSound.tuning = 0; } bool hasHighNoteSoundFontEntry = reader->ReadInt8(); if (hasHighNoteSoundFontEntry) { bool hasSampleRef = reader->ReadInt8(); std::string sampleFileName = reader->ReadString(); - instrument.highNotesSound.tuning = reader->ReadFloat(); + instrument->highNotesSound.tuning = reader->ReadFloat(); auto res = Ship::Context::GetInstance()->GetResourceManager()->LoadResourceProcess(sampleFileName.c_str()); - instrument.highNotesSound.sample = static_cast(res ? res->GetRawPointer() : nullptr); + instrument->highNotesSound.sample = static_cast(res ? res->GetRawPointer() : nullptr); } else { - instrument.highNotesSound.sample = nullptr; - instrument.highNotesSound.tuning = 0; + instrument->highNotesSound.sample = nullptr; + instrument->highNotesSound.tuning = 0; } - - audioSoundFont->instruments.push_back(instrument); - audioSoundFont->instrumentAddresses.push_back(isValidEntry ? - &audioSoundFont->instruments.back() : - nullptr); + if (isValidEntry) { + audioSoundFont->instrumentAddresses.push_back(instrument); + } else { + delete[] instrument->envelope; + delete instrument; + audioSoundFont->instrumentAddresses.push_back(nullptr); + } } audioSoundFont->soundFont.instruments = audioSoundFont->instrumentAddresses.data(); @@ -163,11 +151,264 @@ std::shared_ptr ResourceFactoryBinaryAudioSoundFontV2::ReadReso auto res = Ship::Context::GetInstance()->GetResourceManager()->LoadResourceProcess(sampleFileName.c_str()); soundEffect.sample = static_cast(res ? res->GetRawPointer() : nullptr); } - + audioSoundFont->soundEffects.push_back(soundEffect); } audioSoundFont->soundFont.soundEffects = audioSoundFont->soundEffects.data(); return audioSoundFont; } + +int8_t ResourceFactoryXMLSoundFontV0::MediumStrToInt(const char* str) { + if (!strcmp("Ram", str)) { + return MEDIUM_RAM; + } else if (!strcmp("Unk", str)) { + return MEDIUM_UNK; + } else if (!strcmp("Cart", str)) { + return MEDIUM_CART; + } else if (!strcmp("Disk", str)) { + return MEDIUM_DISK_DRIVE; + // 4 is skipped + } else { + throw std::runtime_error( + StringHelper::Sprintf("Bad medium value. Got %s, expected Ram, Unk, Cart, or Disk.", str)); + } +} + +int8_t ResourceFactoryXMLSoundFontV0::CachePolicyToInt(const char* str) { + if (!strcmp("Temporary", str)) { + return CACHE_TEMPORARY; + } else if (!strcmp("Persistent", str)) { + return CACHE_PERSISTENT; + } else if (!strcmp("Either", str)) { + return CACHE_EITHER; + } else if (!strcmp("Permanent", str)) { + return CACHE_PERMANENT; + } else { + throw std::runtime_error(StringHelper::Sprintf( + "Bad cache policy value. Got %s, expected Temporary, Persistent, Either, or Permanent.", str)); + } +} + +void ResourceFactoryXMLSoundFontV0::ParseDrums(AudioSoundFont* soundFont, tinyxml2::XMLElement* element) { + element = (tinyxml2::XMLElement*)element->FirstChildElement(); + // No drums + if (element == nullptr) { + soundFont->soundFont.drums = nullptr; + soundFont->soundFont.numDrums = 0; + return; + } + + do { + Drum* drum = new Drum; + std::vector envelopes; + drum->releaseRate = element->IntAttribute("ReleaseRate"); + drum->pan = element->IntAttribute("Pan"); + drum->loaded = element->IntAttribute("Loaded"); + drum->sound.tuning = element->FloatAttribute("Tuning"); + const char* sampleStr = element->Attribute("SampleRef"); + + if (sampleStr != nullptr && sampleStr[0] != 0) { + auto res = Ship::Context::GetInstance()->GetResourceManager()->LoadResourceProcess(sampleStr); + drum->sound.sample = static_cast(res ? res->GetRawPointer() : nullptr); + } else { + drum->sound.sample = nullptr; + } + + element = (tinyxml2::XMLElement*)element->FirstChildElement(); + if (!strcmp(element->Name(), "Envelopes")) { + // element = (tinyxml2::XMLElement*)element->FirstChildElement(); + unsigned int envCount = 0; + envelopes = ParseEnvelopes(soundFont, element, &envCount); + element = (tinyxml2::XMLElement*)element->Parent(); + soundFont->drumEnvelopeArrays.push_back(envelopes); + drum->envelope = new AdsrEnvelope[envelopes.size()]; + memcpy(drum->envelope, envelopes.data(), envelopes.size() * sizeof(AdsrEnvelope)); + } else { + drum->envelope = nullptr; + } + + if (drum->sound.sample == nullptr) { + soundFont->drumAddresses.push_back(nullptr); + } else { + soundFont->drumAddresses.push_back(drum); + } + + element = element->NextSiblingElement(); + } while (element != nullptr); + + soundFont->soundFont.numDrums = soundFont->drumAddresses.size(); + soundFont->soundFont.drums = soundFont->drumAddresses.data(); +} + +void SOH::ResourceFactoryXMLSoundFontV0::ParseInstruments(AudioSoundFont* soundFont, tinyxml2::XMLElement* element) { + element = element->FirstChildElement(); + do { + Instrument* instrument = new Instrument; + memset(instrument, 0, sizeof(Instrument)); + unsigned int envCount = 0; + std::vector envelopes; + + int isValid = element->BoolAttribute("IsValid"); + instrument->loaded = element->IntAttribute("Loaded"); + instrument->normalRangeLo = element->IntAttribute("NormalRangeLo"); + instrument->normalRangeHi = element->IntAttribute("NormalRangeHi"); + instrument->releaseRate = element->IntAttribute("ReleaseRate"); + tinyxml2::XMLElement* instrumentElement = element->FirstChildElement(); + tinyxml2::XMLElement* instrumentElementCopy = instrumentElement; + + if (instrumentElement != nullptr && !strcmp(instrumentElement->Name(), "Envelopes")) { + envelopes = ParseEnvelopes(soundFont, instrumentElement, &envCount); + instrument->envelope = new AdsrEnvelope[envelopes.size()]; + memcpy(instrument->envelope, envelopes.data(), envelopes.size() * sizeof(AdsrEnvelope)); + instrumentElement = instrumentElement->NextSiblingElement(); + } + + if (instrumentElement != nullptr && !strcmp("LowNotesSound", instrumentElement->Name())) { + instrument->lowNotesSound.tuning = instrumentElement->FloatAttribute("Tuning"); + const char* sampleStr = instrumentElement->Attribute("SampleRef"); + if (sampleStr != nullptr && sampleStr[0] != 0) { + std::shared_ptr res = static_pointer_cast( + Ship::Context::GetInstance()->GetResourceManager()->LoadResourceProcess(sampleStr)); + if (res->tuning != -1.0f) { + instrument->lowNotesSound.tuning = res->tuning; + } + instrument->lowNotesSound.sample = static_cast(res ? res->GetRawPointer() : nullptr); + } + instrumentElement = instrumentElement->NextSiblingElement(); + } + + if (instrumentElement != nullptr && !strcmp("NormalNotesSound", instrumentElement->Name())) { + instrument->normalNotesSound.tuning = instrumentElement->FloatAttribute("Tuning"); + const char* sampleStr = instrumentElement->Attribute("SampleRef"); + if (sampleStr != nullptr && sampleStr[0] != 0) { + std::shared_ptr res = static_pointer_cast( + Ship::Context::GetInstance()->GetResourceManager()->LoadResourceProcess(sampleStr)); + if (res->tuning != -1.0f) { + instrument->normalNotesSound.tuning = res->tuning; + } + instrument->normalNotesSound.sample = static_cast(res ? res->GetRawPointer() : nullptr); + } + instrumentElement = instrumentElement->NextSiblingElement(); + } + + if (instrumentElement != nullptr && !strcmp("HighNotesSound", instrumentElement->Name())) { + instrument->highNotesSound.tuning = instrumentElement->FloatAttribute("Tuning"); + const char* sampleStr = instrumentElement->Attribute("SampleRef"); + if (sampleStr != nullptr && sampleStr[0] != 0) { + std::shared_ptr res = static_pointer_cast( + Ship::Context::GetInstance()->GetResourceManager()->LoadResourceProcess(sampleStr)); + if (res->tuning != -1.0f) { + instrument->highNotesSound.tuning = res->tuning; + } + instrument->highNotesSound.sample = static_cast(res ? res->GetRawPointer() : nullptr); + } + instrumentElement = instrumentElement->NextSiblingElement(); + } + + soundFont->instrumentAddresses.push_back(instrument); + + element = instrumentElementCopy; + element = (tinyxml2::XMLElement*)element->Parent(); + element = element->NextSiblingElement(); + } while (element != nullptr); + + soundFont->soundFont.instruments = soundFont->instrumentAddresses.data(); + soundFont->soundFont.numInstruments = soundFont->instrumentAddresses.size(); +} + +void SOH::ResourceFactoryXMLSoundFontV0::ParseSfxTable(AudioSoundFont* soundFont, tinyxml2::XMLElement* element) { + size_t count = element->IntAttribute("Count"); + + element = (tinyxml2::XMLElement*)element->FirstChildElement(); + + while (element != nullptr) { + SoundFontSound sound = { 0 }; + const char* sampleStr = element->Attribute("SampleRef"); + // Insert an empty sound effect. The game assumes the empty slots are + // filled so we can't just skip them + if (sampleStr == nullptr) + goto skip; + + sound.tuning = element->FloatAttribute("Tuning"); + if (sampleStr != nullptr && sampleStr[0] != 0) { + auto res = static_pointer_cast( + Ship::Context::GetInstance()->GetResourceManager()->LoadResourceProcess(sampleStr)); + if (res->tuning != -1.0f) { + sound.tuning = res->tuning; + } + sound.sample = static_cast(res ? res->GetRawPointer() : nullptr); + } + skip: + element = element->NextSiblingElement(); + soundFont->soundEffects.push_back(sound); + } + soundFont->soundFont.soundEffects = soundFont->soundEffects.data(); + soundFont->soundFont.numSfx = soundFont->soundEffects.size(); +} + +std::vector SOH::ResourceFactoryXMLSoundFontV0::ParseEnvelopes(AudioSoundFont* soundFont, + tinyxml2::XMLElement* element, + unsigned int* count) { + std::vector envelopes; + unsigned int total = 0; + element = element->FirstChildElement("Envelope"); + while (element != nullptr) { + AdsrEnvelope env = { + .delay = (s16)element->IntAttribute("Delay"), + .arg = (s16)element->IntAttribute("Arg"), + }; + env.delay = BSWAP16(env.delay); + env.arg = BSWAP16(env.arg); + envelopes.emplace_back(env); + element = element->NextSiblingElement("Envelope"); + total++; + } + *count = total; + return envelopes; +} + +std::shared_ptr ResourceFactoryXMLSoundFontV0::ReadResource(std::shared_ptr file) { + if (!FileHasValidFormatAndReader(file)) { + return nullptr; + } + auto audioSoundFont = std::make_shared(file->InitData); + auto child = std::get>(file->Reader)->FirstChildElement(); + // Header data + memset(&audioSoundFont->soundFont, 0, sizeof(audioSoundFont->soundFont)); + audioSoundFont->soundFont.fntIndex = child->IntAttribute("Num", 0); + + const char* mediumStr = child->Attribute("Medium"); + audioSoundFont->medium = MediumStrToInt(mediumStr); + + const char* cachePolicyStr = child->Attribute("CachePolicy"); + audioSoundFont->cachePolicy = CachePolicyToInt(cachePolicyStr); + if (audioSoundFont->soundFont.fntIndex == 1) { + int bp = 1; + } + + audioSoundFont->data1 = child->IntAttribute("Data1"); + audioSoundFont->data2 = child->IntAttribute("Data2"); + audioSoundFont->data3 = child->IntAttribute("Data3"); + + audioSoundFont->soundFont.sampleBankId1 = audioSoundFont->data1 >> 8; + audioSoundFont->soundFont.sampleBankId2 = audioSoundFont->data1 & 0xFF; + + child = (tinyxml2::XMLElement*)child->FirstChildElement(); + + while (child != nullptr) { + const char* name = child->Name(); + + if (!strcmp(name, "Drums")) { + ParseDrums(audioSoundFont.get(), child); + } else if (!strcmp(name, "Instruments")) { + ParseInstruments(audioSoundFont.get(), child); + } else if (!strcmp(name, "SfxTable")) { + ParseSfxTable(audioSoundFont.get(), child); + } + child = child->NextSiblingElement(); + } + return audioSoundFont; +} + } // namespace SOH diff --git a/soh/soh/resource/importer/AudioSoundFontFactory.h b/soh/soh/resource/importer/AudioSoundFontFactory.h index a80b8fe9726..c5f2a93bf43 100644 --- a/soh/soh/resource/importer/AudioSoundFontFactory.h +++ b/soh/soh/resource/importer/AudioSoundFontFactory.h @@ -2,10 +2,26 @@ #include "Resource.h" #include "ResourceFactoryBinary.h" +#include "ResourceFactoryXML.h" +#include "soh/resource/type/AudioSoundFont.h" namespace SOH { class ResourceFactoryBinaryAudioSoundFontV2 : public Ship::ResourceFactoryBinary { public: std::shared_ptr ReadResource(std::shared_ptr file) override; }; + +class ResourceFactoryXMLSoundFontV0 : public Ship::ResourceFactoryXML { + public: + std::shared_ptr ReadResource(std::shared_ptr file) override; + static int8_t MediumStrToInt(const char* str); + static int8_t CachePolicyToInt(const char* str); + + private: + void ParseDrums(AudioSoundFont* soundFont, tinyxml2::XMLElement* element); + void ParseInstruments(AudioSoundFont* soundFont, tinyxml2::XMLElement* element); + void ParseSfxTable(AudioSoundFont* soundFont, tinyxml2::XMLElement* element); + std::vector ParseEnvelopes(AudioSoundFont* soundFont, tinyxml2::XMLElement* element, + unsigned int* count); +}; } // namespace SOH diff --git a/soh/soh/resource/type/AudioSample.h b/soh/soh/resource/type/AudioSample.h index f3765909128..e47ea58325e 100644 --- a/soh/soh/resource/type/AudioSample.h +++ b/soh/soh/resource/type/AudioSample.h @@ -6,56 +6,56 @@ #include namespace SOH { - typedef struct { - /* 0x00 */ uintptr_t start; - /* 0x04 */ uintptr_t end; - /* 0x08 */ u32 count; - /* 0x0C */ char unk_0C[0x4]; - /* 0x10 */ s16 state[16]; // only exists if count != 0. 8-byte aligned - } AdpcmLoop; // size = 0x30 (or 0x10) - - typedef struct { - /* 0x00 */ s32 order; - /* 0x04 */ s32 npredictors; - /* 0x08 */ s16* book; // size 8 * order * npredictors. 8-byte aligned - } AdpcmBook; // s - - typedef struct { - union { - struct { - /* 0x00 */ u32 codec : 4; - /* 0x00 */ u32 medium : 2; - /* 0x00 */ u32 unk_bit26 : 1; - /* 0x00 */ u32 unk_bit25 : 1; // this has been named isRelocated in zret - /* 0x01 */ u32 size : 24; - }; - u32 asU32; +typedef struct { + /* 0x00 */ uintptr_t start; + /* 0x04 */ uintptr_t end; + /* 0x08 */ u32 count; + /* 0x0C */ char unk_0C[0x4]; + /* 0x10 */ s16 state[16]; // only exists if count != 0. 8-byte aligned +} AdpcmLoop; // size = 0x30 (or 0x10) + +typedef struct { + /* 0x00 */ s32 order; + /* 0x04 */ s32 npredictors; + /* 0x08 */ s16* book; // size 8 * order * npredictors. 8-byte aligned +} AdpcmBook; // s + +typedef struct { + union { + struct { + /* 0x00 */ u32 codec : 4; + /* 0x00 */ u32 medium : 2; + /* 0x00 */ u32 unk_bit26 : 1; + /* 0x00 */ u32 unk_bit25 : 1; // this has been named isRelocated in zret }; - - /* 0x04 */ u8* sampleAddr; - /* 0x08 */ AdpcmLoop* loop; - /* 0x0C */ AdpcmBook* book; - u32 sampleRateMagicValue; // For wav samples only... - s32 sampleRate; // For wav samples only... - } Sample; // size = 0x10 - - class AudioSample : public Ship::Resource { - public: - using Resource::Resource; - - AudioSample() : Resource(std::shared_ptr()) {} - - Sample* GetPointer(); - size_t GetPointerSize(); - - Sample sample; - std::vector audioSampleData; - - AdpcmLoop loop; - uint32_t loopStateCount; - - AdpcmBook book; - uint32_t bookDataCount; - std::vector bookData; + u32 asU32; }; -}; // namespace LUS + /* 0x01 */ u32 size; + /* 0x04 */ u8* sampleAddr; + /* 0x08 */ AdpcmLoop* loop; + /* 0x0C */ AdpcmBook* book; +} Sample; // size = 0x10 + +class AudioSample : public Ship::Resource { + public: + using Resource::Resource; + + AudioSample() : Resource(std::shared_ptr()) { + } + + Sample* GetPointer(); + size_t GetPointerSize(); + + Sample sample; + std::vector audioSampleData; + + AdpcmLoop loop; + uint32_t loopStateCount; + + AdpcmBook book; + uint32_t bookDataCount; + std::vector bookData; + // Only applies to streamed audio + float tuning = -1.0f; +}; +}; // namespace SOH diff --git a/soh/soh/resource/type/AudioSequence.h b/soh/soh/resource/type/AudioSequence.h index d7a8820a398..883dfd79cbd 100644 --- a/soh/soh/resource/type/AudioSequence.h +++ b/soh/soh/resource/type/AudioSequence.h @@ -9,11 +9,11 @@ namespace SOH { typedef struct { char* seqData; - int32_t seqDataSize; + uint32_t seqDataSize; uint16_t seqNumber; uint8_t medium; uint8_t cachePolicy; - int32_t numFonts; + uint32_t numFonts; uint8_t fonts[16]; } Sequence; diff --git a/soh/soh/resource/type/AudioSoundFont.cpp b/soh/soh/resource/type/AudioSoundFont.cpp index 12218cb648b..7f323ddf480 100644 --- a/soh/soh/resource/type/AudioSoundFont.cpp +++ b/soh/soh/resource/type/AudioSoundFont.cpp @@ -1,6 +1,23 @@ #include "AudioSoundFont.h" namespace SOH { + +AudioSoundFont::~AudioSoundFont() { + for (auto i : instrumentAddresses) { + if (i != nullptr) { + delete[] i->envelope; + delete i; + } + } + + for (auto d : drumAddresses) { + if (d != nullptr) { + delete[] d->envelope; + delete d; + } + } +} + SoundFont* AudioSoundFont::GetPointer() { return &soundFont; } diff --git a/soh/soh/resource/type/AudioSoundFont.h b/soh/soh/resource/type/AudioSoundFont.h index 12d66b2d244..be4a86c21b9 100644 --- a/soh/soh/resource/type/AudioSoundFont.h +++ b/soh/soh/resource/type/AudioSoundFont.h @@ -17,7 +17,7 @@ typedef struct { /* 0x00 */ Sample* sample; /* 0x04 */ union { u32 tuningAsU32; - f32 tuning;// frequency scale factor + f32 tuning; // frequency scale factor }; } SoundFontSound; // size = 0x8 @@ -53,10 +53,12 @@ typedef struct { } SoundFont; // size = 0x14 class AudioSoundFont : public Ship::Resource { -public: + public: using Resource::Resource; - AudioSoundFont() : Resource(std::shared_ptr()) {} + AudioSoundFont() : Resource(std::shared_ptr()) { + } + ~AudioSoundFont(); SoundFont* GetPointer(); size_t GetPointerSize(); @@ -67,18 +69,15 @@ class AudioSoundFont : public Ship::Resource { uint16_t data2; uint16_t data3; - std::vector drums; std::vector drumAddresses; std::vector drumEnvelopeCounts; std::vector> drumEnvelopeArrays; - std::vector instruments; std::vector instrumentAddresses; - std::vector instrumentEnvelopeCounts; std::vector> instrumentEnvelopeArrays; std::vector soundEffects; SoundFont soundFont; }; -}; // namespace LUS +}; // namespace SOH diff --git a/soh/src/code/audio_heap.c b/soh/src/code/audio_heap.c index 8d0e97fa373..5e51b4c7873 100644 --- a/soh/src/code/audio_heap.c +++ b/soh/src/code/audio_heap.c @@ -41,7 +41,7 @@ void func_800DDE3C(void) { void AudioHeap_ResetLoadStatus(void) { s32 i; - for (i = 0; i < 0x30; i++) { + for (i = 0; i < fontMapSize; i++) { if (gAudioContext.fontLoadStatus[i] != 5) { gAudioContext.fontLoadStatus[i] = 0; } @@ -940,7 +940,7 @@ void AudioHeap_Init(void) { reverb->sample.sampleAddr = (u8*)reverb->leftRingBuf; reverb->loop.start = 0; reverb->loop.count = 1; - reverb->loop.end = reverb->windowSize; + reverb->loop.loopEnd = reverb->windowSize; if (reverb->downsampleRate != 1) { reverb->unk_0E = 0x8000 / reverb->downsampleRate; diff --git a/soh/src/code/audio_load.c b/soh/src/code/audio_load.c index 3c722461273..6646649ef5c 100644 --- a/soh/src/code/audio_load.c +++ b/soh/src/code/audio_load.c @@ -9,6 +9,11 @@ #include "soh/Enhancements/audio/AudioEditor.h" #include "soh/ResourceManagerHelpers.h" +// Windows deprecated the use of `strdup` it uses _strdup. Linux/Unix doesn't have _strdup. +#ifdef _MSC_VER +#define strdup _strdup +#endif + #define MK_ASYNC_MSG(retData, tableType, id, status) (((retData) << 24) | ((tableType) << 16) | ((id) << 8) | (status)) #define ASYNC_TBLTYPE(v) ((u8)(v >> 16)) #define ASYNC_ID(v) ((u8)(v >> 8)) @@ -81,9 +86,10 @@ s32 gAudioContextInitalized = false; char** sequenceMap; size_t sequenceMapSize; +size_t fontMapSize; // A map of authentic sequence IDs to their cache policies, for use with sequence swapping. u8 seqCachePolicyMap[MAX_AUTHENTIC_SEQID]; -char* fontMap[256]; +char** fontMap; uintptr_t fontStart; uint32_t fontOffsets[8192]; @@ -707,7 +713,7 @@ SoundFontData* AudioLoad_SyncLoadFont(u32 fontId) { return NULL; } - SoundFont* sf = ResourceMgr_LoadAudioSoundFont(fontMap[fontId]); + SoundFont* sf = ResourceMgr_LoadAudioSoundFontByName(fontMap[fontId]); sampleBankId1 = sf->sampleBankId1; sampleBankId2 = sf->sampleBankId2; @@ -768,7 +774,7 @@ uintptr_t AudioLoad_SyncLoad(u32 tableType, u32 id, s32* didAllocate) { } else if (tableType == FONT_TABLE) { - fnt = ResourceMgr_LoadAudioSoundFont(fontMap[id]); + fnt = ResourceMgr_LoadAudioSoundFontByName(fontMap[id]); size = sizeof(SoundFont); medium = 2; cachePolicy = 0; @@ -911,7 +917,7 @@ void AudioLoad_RelocateFont(s32 fontId, SoundFontData* mem, RelocInfo* relocInfo s32 numInstruments = 0; s32 numSfx = 0; - sf = ResourceMgr_LoadAudioSoundFont(fontMap[fontId]); + sf = ResourceMgr_LoadAudioSoundFontByName(fontMap[fontId]); numDrums = sf->numDrums; numInstruments = sf->numInstruments; numSfx = sf->numSfx; @@ -1354,19 +1360,39 @@ void AudioLoad_Init(void* heap, size_t heapSize) { sequenceMap = malloc(sequenceMapSize * sizeof(char*)); gAudioContext.seqLoadStatus = malloc(sequenceMapSize * sizeof(char*)); - for (size_t i = 0; i < seqListSize; i++) - { + for (size_t i = 0; i < seqListSize; i++) { SequenceData sDat = ResourceMgr_LoadSeqByName(seqList[i]); - - char* str = malloc(strlen(seqList[i]) + 1); - strcpy(str, seqList[i]); - - sequenceMap[sDat.seqNumber] = str; + sequenceMap[sDat.seqNumber] = strdup(seqList[i]); seqCachePolicyMap[sDat.seqNumber] = sDat.cachePolicy; } free(seqList); + // SOH [Port] [Streamed Audio] We need to load the custom songs after the fonts because streamed songs will use a hash to + // find its soundfont + int fntListSize = 0; + int customFntListSize = 0; + char** fntList = ResourceMgr_ListFiles("audio/fonts*", &fntListSize); + char** customFntList = ResourceMgr_ListFiles("custom/fonts/*", &customFntListSize); + + gAudioContext.fontLoadStatus = malloc(customFntListSize + fntListSize); + fontMapSize = customFntListSize + fntListSize; + fontMap = calloc(customFntListSize + fntListSize, sizeof(char*)); + for (int i = 0; i < fntListSize; i++) { + SoundFont* sf = ResourceMgr_LoadAudioSoundFontByName(fntList[i]); + fontMap[sf->fntIndex] = strdup(fntList[i]); + } + + free(fntList); + + int customFontStart = fntListSize; + for (int i = customFontStart; i < customFntListSize + fntListSize; i++) { + SoundFont* sf = ResourceMgr_LoadAudioSoundFontByName(customFntList[i - customFontStart]); + sf->fntIndex = i; + fontMap[i] = strdup(customFntList[i - customFontStart]); + } + free(customFntList); + int startingSeqNum = MAX_AUTHENTIC_SEQID; // 109 is the highest vanilla sequence qsort(customSeqList, customSeqListSize, sizeof(char*), strcmp_sort); @@ -1378,40 +1404,49 @@ void AudioLoad_Init(void* heap, size_t heapSize) { for (size_t i = startingSeqNum; i < startingSeqNum + customSeqListSize; i++) { // ensure that what would be the next sequence number is actually unassigned in AudioCollection + int j = i - startingSeqNum; + SequenceData* sDat = ResourceMgr_LoadSeqPtrByName(customSeqList[j]); + + if (sDat->numFonts == -1) { + uint64_t crc; + + memcpy(&crc, sDat->fonts, sizeof(uint64_t)); + const char* res = ResourceGetNameByCrc(crc); + if (res == NULL) { + // Passing a null buffer and length of 0 to snprintf will return the required numbers of characters the + // buffer needs to be. + int len = + snprintf(NULL, 0, "Could not find sound font for sequence %s. It will not be in the audio editor.", + customSeqList[j]); + char* error = malloc(len + 1); + snprintf(error, len, "Could not find sound font for sequence %s. It will not be in the audio editor.", + customSeqList[j]); + LUSLOG_ERROR("%s", error); + Messagebox_ShowErrorBox("Invalid Sequence", error); + free(error); + continue; + } + SoundFont* sf = ResourceMgr_LoadAudioSoundFontByName(res); + memset(&sDat->fonts[0], 0, sizeof(sDat->fonts)); + sDat->fonts[0] = sf->fntIndex; + sDat->numFonts = 1; + } + while (AudioCollection_HasSequenceNum(seqNum)) { seqNum++; } - int j = i - startingSeqNum; - AudioCollection_AddToCollection(customSeqList[j], seqNum); - SequenceData sDat = ResourceMgr_LoadSeqByName(customSeqList[j]); - sDat.seqNumber = seqNum; - char* str = malloc(strlen(customSeqList[j]) + 1); - strcpy(str, customSeqList[j]); + AudioCollection_AddToCollection(customSeqList[j], seqNum); - sequenceMap[sDat.seqNumber] = str; + sDat->seqNumber = seqNum; + sequenceMap[sDat->seqNumber] = strdup(customSeqList[j]); seqNum++; } free(customSeqList); - int fntListSize = 0; - char** fntList = ResourceMgr_ListFiles("audio/fonts*", &fntListSize); - - for (int i = 0; i < fntListSize; i++) - { - SoundFont* sf = ResourceMgr_LoadAudioSoundFont(fntList[i]); - - char* str = malloc(strlen(fntList[i]) + 1); - strcpy(str, fntList[i]); - - fontMap[sf->fntIndex] = str; - } - - numFonts = fntListSize; - - free(fntList); - gAudioContext.soundFonts = AudioHeap_Alloc(&gAudioContext.audioInitPool, numFonts * sizeof(SoundFont)); + numFonts = fntListSize; + gAudioContext.soundFonts = AudioHeap_Alloc(&gAudioContext.audioInitPool, numFonts * sizeof(SoundFont)); if (temp_v0_3 = AudioHeap_Alloc(&gAudioContext.audioInitPool, D_8014A6C4.permanentPoolSize), temp_v0_3 == NULL) { @@ -2080,7 +2115,7 @@ void AudioLoad_PreloadSamplesForFont(s32 fontId, s32 async, RelocInfo* relocInfo gAudioContext.numUsedSamples = 0; - SoundFont* sf = ResourceMgr_LoadAudioSoundFont(fontMap[fontId]); + SoundFont* sf = ResourceMgr_LoadAudioSoundFontByName(fontMap[fontId]); numDrums = sf->numDrums; numInstruments = sf->numInstruments; @@ -2210,7 +2245,7 @@ void AudioLoad_LoadPermanentSamples(void) { fontId = AudioLoad_GetRealTableIndex(FONT_TABLE, gAudioContext.permanentCache[i].id); //fontId = gAudioContext.permanentCache[i].id; - SoundFont* sf = ResourceMgr_LoadAudioSoundFont(fontMap[fontId]); + SoundFont* sf = ResourceMgr_LoadAudioSoundFontByName(fontMap[fontId]); relocInfo.sampleBankId1 = sf->sampleBankId1; relocInfo.sampleBankId2 = sf->sampleBankId2; diff --git a/soh/src/code/audio_playback.c b/soh/src/code/audio_playback.c index 3b3089e2c02..55a21e7babf 100644 --- a/soh/src/code/audio_playback.c +++ b/soh/src/code/audio_playback.c @@ -293,13 +293,6 @@ void Audio_ProcessNotes(void) { f32 resampRate = gAudioContext.audioBufferParameters.resampleRate; - // CUSTOM SAMPLE CHECK - if (!noteSubEu2->bitField1.isSyntheticWave && noteSubEu2->sound.soundFontSound != NULL && - noteSubEu2->sound.soundFontSound->sample != NULL && - noteSubEu2->sound.soundFontSound->sample->sampleRateMagicValue == 'RIFF') { - resampRate = CALC_RESAMPLE_FREQ(noteSubEu2->sound.soundFontSound->sample->sampleRate); - } - subAttrs.frequency *= resampRate; @@ -336,7 +329,7 @@ Instrument* Audio_GetInstrumentInner(s32 fontId, s32 instId) { } int instCnt = 0; - SoundFont* sf = ResourceMgr_LoadAudioSoundFont(fontMap[fontId]); + SoundFont* sf = ResourceMgr_LoadAudioSoundFontByName(fontMap[fontId]); if (instId >= sf->numInstruments) return NULL; @@ -364,7 +357,7 @@ Drum* Audio_GetDrum(s32 fontId, s32 drumId) { } - SoundFont* sf = ResourceMgr_LoadAudioSoundFont(fontMap[fontId]); + SoundFont* sf = ResourceMgr_LoadAudioSoundFontByName(fontMap[fontId]); if (drumId < sf->numDrums) { drum = sf->drums[drumId]; } @@ -388,7 +381,7 @@ SoundFontSound* Audio_GetSfx(s32 fontId, s32 sfxId) { return NULL; } - SoundFont* sf = ResourceMgr_LoadAudioSoundFont(fontMap[fontId]); + SoundFont* sf = ResourceMgr_LoadAudioSoundFontByName(fontMap[fontId]); if (sfxId < sf->numSfx) { sfx = &sf->soundEffects[sfxId]; } diff --git a/soh/src/code/audio_seqplayer.c b/soh/src/code/audio_seqplayer.c index e194fd28b7e..3b15dcd68d4 100644 --- a/soh/src/code/audio_seqplayer.c +++ b/soh/src/code/audio_seqplayer.c @@ -789,7 +789,7 @@ s32 AudioSeq_SeqLayerProcessScriptStep4(SequenceLayer* layer, s32 cmd) { layer->freqScale *= layer->unk_34; if (layer->delay == 0) { if (layer->sound != NULL) { - time = (f32)layer->sound->sample->loop->end; + time = (f32)layer->sound->sample->loop->loopEnd; } else { time = 0.0f; } diff --git a/soh/src/code/audio_synthesis.c b/soh/src/code/audio_synthesis.c index 7044a48c464..ad07bd6b594 100644 --- a/soh/src/code/audio_synthesis.c +++ b/soh/src/code/audio_synthesis.c @@ -761,7 +761,7 @@ Acmd* AudioSynth_ProcessNote(s32 noteIndex, NoteSubEu* noteSubEu, NoteSynthesisS audioFontSample = noteSubEu->sound.soundFontSound->sample; loopInfo = audioFontSample->loop; - loopEndPos = loopInfo->end; + loopEndPos = loopInfo->loopEnd; sampleAddr = audioFontSample->sampleAddr; resampledTempLen = 0; @@ -854,14 +854,28 @@ Acmd* AudioSynth_ProcessNote(s32 noteIndex, NoteSubEu* noteSubEu, NoteSynthesisS s5 = samplesLenAdjusted; goto skip; case CODEC_S16: - AudioSynth_ClearBuffer(cmd++, DMEM_UNCOMPRESSED_NOTE, (samplesLenAdjusted * 2) + 0x20); - AudioSynth_LoadBuffer(cmd++, DMEM_UNCOMPRESSED_NOTE, ALIGN16(nSamplesToLoad * 2), - audioFontSample->sampleAddr + (synthState->samplePosInt * 2)); - + // SoH [Port] [Custom Audio] This was not finished originally. + AudioSynth_ClearBuffer(cmd++, DMEM_UNCOMPRESSED_NOTE, (samplesLenAdjusted + 16) * 2); flags = A_CONTINUE; skipBytes = 0; - nSamplesProcessed = samplesLenAdjusted; - s5 = samplesLenAdjusted; + nSamplesProcessed += samplesLenAdjusted; + phi_s4 = samplesLenAdjusted; + size_t bytesToRead; + + if (((synthState->samplePosInt * 2) + (samplesLenAdjusted + 16) * 2) < + audioFontSample->size) { + bytesToRead = (samplesLenAdjusted + 16) * 2; + } else { + bytesToRead = audioFontSample->size - (synthState->samplePosInt * 2); + } + // SoH [Port] [Custom audio] + // TLDR samples are loaded async and might be null the first time they are played. + // See note in AudioSampleFactory.cpp + if (sampleAddr != NULL) { + aLoadBuffer(cmd++, sampleAddr + (synthState->samplePosInt * 2), DMEM_UNCOMPRESSED_NOTE, + bytesToRead); + } + goto skip; case CODEC_REVERB: break; @@ -895,7 +909,7 @@ Acmd* AudioSynth_ProcessNote(s32 noteIndex, NoteSubEu* noteSubEu, NoteSynthesisS } if (synthState->restart) { - aSetLoop(cmd++, audioFontSample->loop->state); + aSetLoop(cmd++, audioFontSample->loop->predictorState); flags = A_LOOP; synthState->restart = false; } diff --git a/soh/src/code/code_800E4FE0.c b/soh/src/code/code_800E4FE0.c index 6eb3ae3695b..e3d590eac19 100644 --- a/soh/src/code/code_800E4FE0.c +++ b/soh/src/code/code_800E4FE0.c @@ -794,7 +794,7 @@ s32 func_800E6590(s32 playerIdx, s32 arg1, s32 arg2) { if (sound == NULL) { return 0; } - loopEnd = sound->sample->loop->end; + loopEnd = sound->sample->loop->loopEnd; samplePos = note->synthesisState.samplePosInt; return loopEnd - samplePos; } From fe6320c8566a7a31b9ade94d6101e1e0c474b054 Mon Sep 17 00:00:00 2001 From: louist103 <35883445+louist103@users.noreply.github.com> Date: Wed, 27 Nov 2024 20:39:00 -0500 Subject: [PATCH 2/9] Rename resourcemgr helpers --- soh/soh/OTRGlobals.cpp | 60 ------------------------------ soh/soh/ResourceManagerHelpers.cpp | 2 +- soh/soh/ResourceManagerHelpers.h | 2 +- 3 files changed, 2 insertions(+), 62 deletions(-) diff --git a/soh/soh/OTRGlobals.cpp b/soh/soh/OTRGlobals.cpp index 13855f92f8d..787140fc361 100644 --- a/soh/soh/OTRGlobals.cpp +++ b/soh/soh/OTRGlobals.cpp @@ -1479,66 +1479,6 @@ extern "C" uint8_t GetSeedIconIndex(uint8_t index) { return OTRGlobals::Instance->gRandoContext->hashIconIndexes[index]; } -std::map cachedCustomSFs; - -extern "C" SoundFontSample* ReadCustomSample(const char* path) { - return nullptr; -/* - if (!ExtensionCache.contains(path)) - return nullptr; - - ExtensionEntry entry = ExtensionCache[path]; - - auto sampleRaw = Ship::Context::GetInstance()->GetResourceManager()->LoadFile(entry.path); - uint32_t* strem = (uint32_t*)sampleRaw->Buffer.get(); - uint8_t* strem2 = (uint8_t*)strem; - - SoundFontSample* sampleC = new SoundFontSample; - - if (entry.ext == "wav") { - drwav_uint32 channels; - drwav_uint32 sampleRate; - drwav_uint64 totalPcm; - drmp3_int16* pcmData = - drwav_open_memory_and_read_pcm_frames_s16(strem2, sampleRaw->BufferSize, &channels, &sampleRate, &totalPcm, NULL); - sampleC->size = totalPcm; - sampleC->sampleAddr = (uint8_t*)pcmData; - sampleC->codec = CODEC_S16; - - sampleC->loop = new AdpcmLoop; - sampleC->loop->start = 0; - sampleC->loop->end = sampleC->size - 1; - sampleC->loop->count = 0; - sampleC->sampleRateMagicValue = 'RIFF'; - sampleC->sampleRate = sampleRate; - - cachedCustomSFs[path] = sampleC; - return sampleC; - } else if (entry.ext == "mp3") { - drmp3_config mp3Info; - drmp3_uint64 totalPcm; - drmp3_int16* pcmData = - drmp3_open_memory_and_read_pcm_frames_s16(strem2, sampleRaw->BufferSize, &mp3Info, &totalPcm, NULL); - - sampleC->size = totalPcm * mp3Info.channels * sizeof(short); - sampleC->sampleAddr = (uint8_t*)pcmData; - sampleC->codec = CODEC_S16; - - sampleC->loop = new AdpcmLoop; - sampleC->loop->start = 0; - sampleC->loop->end = sampleC->size; - sampleC->loop->count = 0; - sampleC->sampleRateMagicValue = 'RIFF'; - sampleC->sampleRate = mp3Info.sampleRate; - - cachedCustomSFs[path] = sampleC; - return sampleC; - } - - return nullptr; -*/ -} - std::filesystem::path GetSaveFile(std::shared_ptr Conf) { const std::string fileName = Conf->GetString("Game.SaveName", Ship::Context::GetPathRelativeToAppDirectory("oot_save.sav")); std::filesystem::path saveFile = std::filesystem::absolute(fileName); diff --git a/soh/soh/ResourceManagerHelpers.cpp b/soh/soh/ResourceManagerHelpers.cpp index 45ed269f7f5..130137fb174 100644 --- a/soh/soh/ResourceManagerHelpers.cpp +++ b/soh/soh/ResourceManagerHelpers.cpp @@ -426,7 +426,7 @@ extern "C" SoundFontSample* ResourceMgr_LoadAudioSample(const char* path) { return (SoundFontSample*) ResourceGetDataByName(path); } -extern "C" SoundFont* ResourceMgr_LoadAudioSoundFont(const char* path) { +extern "C" SoundFont* ResourceMgr_LoadAudioSoundFontByName(const char* path) { return (SoundFont*) ResourceGetDataByName(path); } diff --git a/soh/soh/ResourceManagerHelpers.h b/soh/soh/ResourceManagerHelpers.h index 3b14188b759..d2188e028a7 100644 --- a/soh/soh/ResourceManagerHelpers.h +++ b/soh/soh/ResourceManagerHelpers.h @@ -50,7 +50,7 @@ extern "C" { char* ResourceMgr_LoadArrayByNameAsVec3s(const char* path); Vtx* ResourceMgr_LoadVtxByCRC(uint64_t crc); Vtx* ResourceMgr_LoadVtxByName(char* path); - SoundFont* ResourceMgr_LoadAudioSoundFont(const char* path); + SoundFont* ResourceMgr_LoadAudioSoundFontByName(const char* path); SequenceData ResourceMgr_LoadSeqByName(const char* path); SoundFontSample* ResourceMgr_LoadAudioSample(const char* path); CollisionHeader* ResourceMgr_LoadColByName(const char* path); From 92ac982b82cd06829e1b6f4e45872d3a05624284 Mon Sep 17 00:00:00 2001 From: Louis <35883445+louist103@users.noreply.github.com> Date: Wed, 27 Nov 2024 20:50:41 -0500 Subject: [PATCH 3/9] fix deps --- .github/workflows/apt-deps.txt | 2 +- .github/workflows/macports-deps.txt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/apt-deps.txt b/.github/workflows/apt-deps.txt index c06068ab467..916846fa39d 100644 --- a/.github/workflows/apt-deps.txt +++ b/.github/workflows/apt-deps.txt @@ -1 +1 @@ -libusb-dev libusb-1.0-0-dev libsdl2-dev libsdl2-net-dev libpng-dev libglew-dev libzip-dev zipcmp zipmerge ziptool nlohmann-json3-dev libtinyxml2-dev libspdlog-dev ninja-build +libusb-dev libusb-1.0-0-dev libsdl2-dev libsdl2-net-dev libpng-dev libglew-dev libzip-dev zipcmp zipmerge ziptool nlohmann-json3-dev libtinyxml2-dev libspdlog-dev ninja-build libogg-dev libvorbis-dev diff --git a/.github/workflows/macports-deps.txt b/.github/workflows/macports-deps.txt index d150390781c..ed9f85af8bc 100644 --- a/.github/workflows/macports-deps.txt +++ b/.github/workflows/macports-deps.txt @@ -1 +1 @@ -libsdl2 +universal libsdl2_net +universal libpng +universal glew +universal libzip +universal nlohmann-json +universal tinyxml2 +universal \ No newline at end of file +libsdl2 +universal libsdl2_net +universal libpng +universal glew +universal libzip +universal nlohmann-json +universal tinyxml2 +universal libogg +universal libvorbis +universal \ No newline at end of file From ec7eb5d727c1d926a1ad6b5ae0abc0d2c44d6a16 Mon Sep 17 00:00:00 2001 From: Louis <35883445+louist103@users.noreply.github.com> Date: Wed, 27 Nov 2024 21:01:17 -0500 Subject: [PATCH 4/9] Fix cmake lists --- soh/CMakeLists.txt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/soh/CMakeLists.txt b/soh/CMakeLists.txt index 77bd280d636..4154c848751 100644 --- a/soh/CMakeLists.txt +++ b/soh/CMakeLists.txt @@ -694,6 +694,8 @@ else() find_package(SDL2) set(THREADS_PREFER_PTHREAD_FLAG ON) find_package(Threads REQUIRED) + find_package(Ogg REQUIRED) + find_package(Vorbis REQUIRED) set(ADDITIONAL_LIBRARY_DEPENDENCIES "libultraship;" "ZAPDLib;" From 9d642a1b79657f2a09f9ef4b935a08eb9f25f8cf Mon Sep 17 00:00:00 2001 From: Louis <35883445+louist103@users.noreply.github.com> Date: Wed, 27 Nov 2024 22:14:13 -0500 Subject: [PATCH 5/9] submodules --- soh/include/z64audio.h | 24 +++++++++++++----------- soh/soh/ResourceManagerHelpers.cpp | 5 +++++ soh/soh/ResourceManagerHelpers.h | 1 + 3 files changed, 19 insertions(+), 11 deletions(-) diff --git a/soh/include/z64audio.h b/soh/include/z64audio.h index 81775feba3a..a074d1fe4c6 100644 --- a/soh/include/z64audio.h +++ b/soh/include/z64audio.h @@ -24,8 +24,9 @@ //#define MAX_SEQUENCES 0x800 extern size_t sequenceMapSize; +extern size_t fontMapSize; -extern char* fontMap[256]; +extern char** fontMap; #define MAX_AUTHENTIC_SEQID 110 @@ -117,13 +118,15 @@ typedef struct { /* 0x2 */ s16 arg; } AdsrEnvelope; // size = 0x4 -typedef struct { +typedef struct AdpcmLoop { /* 0x00 */ uintptr_t start; - /* 0x04 */ uintptr_t end; - /* 0x08 */ u32 count; - /* 0x0C */ char unk_0C[0x4]; - /* 0x10 */ s16 state[16]; // only exists if count != 0. 8-byte aligned -} AdpcmLoop; // size = 0x30 (or 0x10) + /* 0x04 */ uintptr_t loopEnd; // numSamples position into the sample where the loop ends + /* 0x08 */ u32 count; // The number of times the loop is played before the sound completes. Setting count to -1 + // indicates that the loop should play indefinitely. + /* 0x0C */ u32 sampleEnd; // total number of s16-samples in the sample audio clip + /* 0x10 */ s16 predictorState[16]; // only exists if count != 0. 8-byte aligned +} AdpcmLoop; // size = 0x30 (or 0x10) + typedef struct { /* 0x00 */ s32 order; @@ -139,16 +142,15 @@ typedef struct /* 0x00 */ u32 medium : 2; /* 0x00 */ u32 unk_bit26 : 1; /* 0x00 */ u32 unk_bit25 : 1; // this has been named isRelocated in zret - /* 0x01 */ u32 size : 24; }; u32 asU32; }; + // SOH [Port][Custom Audio] Custom sample sizes will be too big to fit in the 24 bits given originally. + /* 0x01 */ u32 size; /* 0x04 */ u8* sampleAddr; /* 0x08 */ AdpcmLoop* loop; /* 0x0C */ AdpcmBook* book; - u32 sampleRateMagicValue; // For wav samples only... - s32 sampleRate; // For wav samples only... } SoundFontSample; // size = 0x10 typedef struct { @@ -917,7 +919,7 @@ typedef struct { /* 0x3420 */ AudioPoolSplit3 persistentCommonPoolSplit; /* 0x342C */ AudioPoolSplit3 temporaryCommonPoolSplit; /* 0x3438 */ u8 sampleFontLoadStatus[0x30]; - /* 0x3468 */ u8 fontLoadStatus[0x30]; + /* 0x3468 */ u8* fontLoadStatus; /* 0x3498 */ u8* seqLoadStatus; /* 0x3518 */ volatile u8 resetStatus; /* 0x3519 */ u8 audioResetSpecIdToLoad; diff --git a/soh/soh/ResourceManagerHelpers.cpp b/soh/soh/ResourceManagerHelpers.cpp index 130137fb174..9799ba15cf1 100644 --- a/soh/soh/ResourceManagerHelpers.cpp +++ b/soh/soh/ResourceManagerHelpers.cpp @@ -422,6 +422,11 @@ extern "C" SequenceData ResourceMgr_LoadSeqByName(const char* path) { return *sequence; } +extern "C" SequenceData* ResourceMgr_LoadSeqPtrByName(const char* path) { + SequenceData* sequence = (SequenceData*)ResourceGetDataByName(path); + return sequence; +} + extern "C" SoundFontSample* ResourceMgr_LoadAudioSample(const char* path) { return (SoundFontSample*) ResourceGetDataByName(path); } diff --git a/soh/soh/ResourceManagerHelpers.h b/soh/soh/ResourceManagerHelpers.h index d2188e028a7..e70b3119b5c 100644 --- a/soh/soh/ResourceManagerHelpers.h +++ b/soh/soh/ResourceManagerHelpers.h @@ -52,6 +52,7 @@ extern "C" { Vtx* ResourceMgr_LoadVtxByName(char* path); SoundFont* ResourceMgr_LoadAudioSoundFontByName(const char* path); SequenceData ResourceMgr_LoadSeqByName(const char* path); + SequenceData* ResourceMgr_LoadSeqPtrByName(const char* path); SoundFontSample* ResourceMgr_LoadAudioSample(const char* path); CollisionHeader* ResourceMgr_LoadColByName(const char* path); bool ResourceMgr_IsAltAssetsEnabled(); From a965d4a08895d0e8a77f4136173f418b5c828ba0 Mon Sep 17 00:00:00 2001 From: Louis <35883445+louist103@users.noreply.github.com> Date: Wed, 27 Nov 2024 23:20:50 -0500 Subject: [PATCH 6/9] stdio for sprintf --- soh/src/code/audio_load.c | 1 + 1 file changed, 1 insertion(+) diff --git a/soh/src/code/audio_load.c b/soh/src/code/audio_load.c index 6646649ef5c..e04c133c722 100644 --- a/soh/src/code/audio_load.c +++ b/soh/src/code/audio_load.c @@ -8,6 +8,7 @@ #include "soh/Enhancements/audio/AudioCollection.h" #include "soh/Enhancements/audio/AudioEditor.h" #include "soh/ResourceManagerHelpers.h" +#include // Windows deprecated the use of `strdup` it uses _strdup. Linux/Unix doesn't have _strdup. #ifdef _MSC_VER From 6c8c63f11ea9fc2f013cab26781c5681a13b8c25 Mon Sep 17 00:00:00 2001 From: Louis <35883445+louist103@users.noreply.github.com> Date: Thu, 28 Nov 2024 11:38:46 -0500 Subject: [PATCH 7/9] fix underscore logic in audio collection --- .../Enhancements/audio/AudioCollection.cpp | 31 +++++++++++-------- 1 file changed, 18 insertions(+), 13 deletions(-) diff --git a/soh/soh/Enhancements/audio/AudioCollection.cpp b/soh/soh/Enhancements/audio/AudioCollection.cpp index 5b0165637dd..ff799530f42 100644 --- a/soh/soh/Enhancements/audio/AudioCollection.cpp +++ b/soh/soh/Enhancements/audio/AudioCollection.cpp @@ -343,21 +343,26 @@ std::string AudioCollection::GetCvarLockKey(std::string sfxKey) { void AudioCollection::AddToCollection(char* otrPath, uint16_t seqNum) { std::string fileName = std::filesystem::path(otrPath).filename().string(); - std::vector splitFileName = StringHelper::Split(fileName, "_"); - std::string sequenceName = splitFileName[0]; + size_t underscorePos = fileName.find_last_of('_') + 1; SeqType type = SEQ_BGM_CUSTOM; - std::string typeString = splitFileName[splitFileName.size() - 1]; - std::locale loc; - for (size_t i = 0; i < typeString.length(); i++) { - typeString[i] = std::tolower(typeString[i], loc); - } - if (typeString == "fanfare") { - type = SEQ_FANFARE; + if (underscorePos != std::string::npos) { + std::string typeString = fileName.substr(underscorePos); + std::locale loc; + for (size_t i = 0; i < typeString.length(); i++) { + typeString[i] = std::tolower(typeString[i], loc); + } + if (typeString == "fanfare") { + type = SEQ_FANFARE; + } } - SequenceInfo info = {seqNum, - sequenceName, - StringHelper::Replace(StringHelper::Replace(StringHelper::Replace(sequenceName, " ", "_"), "~", "-"),".", ""), - type, false, true}; + std::string sequenceName = fileName.substr(0, underscorePos - 1); + SequenceInfo info = { seqNum, + sequenceName, + StringHelper::Replace( + StringHelper::Replace(StringHelper::Replace(sequenceName, " ", "_"), "~", "-"), ".", ""), + type, + false, + true }; sequenceMap.emplace(seqNum, info); } From 746f329786b513bd09a38bd83c5944010c9f4fa6 Mon Sep 17 00:00:00 2001 From: Louis <35883445+louist103@users.noreply.github.com> Date: Thu, 28 Nov 2024 11:39:42 -0500 Subject: [PATCH 8/9] Use torch sample xml format --- .../resource/importer/AudioSampleFactory.cpp | 99 +++++++++---------- soh/soh/resource/type/AudioSample.cpp | 8 ++ soh/soh/resource/type/AudioSample.h | 16 +-- 3 files changed, 60 insertions(+), 63 deletions(-) diff --git a/soh/soh/resource/importer/AudioSampleFactory.cpp b/soh/soh/resource/importer/AudioSampleFactory.cpp index a66e9fc998e..175e38676a8 100644 --- a/soh/soh/resource/importer/AudioSampleFactory.cpp +++ b/soh/soh/resource/importer/AudioSampleFactory.cpp @@ -84,17 +84,15 @@ static void Mp3DecoderWorker(std::shared_ptr audioSample, std: drwav_uint64 channels = mp3.channels; drwav_uint64 sampleRate = mp3.sampleRate; - audioSample->audioSampleData.reserve(numFrames * channels * 2); // 2 for sizeof(s16) - drmp3_read_pcm_frames_s16(&mp3, numFrames, (int16_t*)audioSample->audioSampleData.data()); - audioSample->sample.sampleAddr = audioSample->audioSampleData.data(); + audioSample->sample.sampleAddr = new uint8_t[numFrames * channels * 2]; + drmp3_read_pcm_frames_s16(&mp3, numFrames, (int16_t*)audioSample->sample.sampleAddr); } static void FlacDecoderWorker(std::shared_ptr audioSample, std::shared_ptr sampleFile) { drflac* flac = drflac_open_memory(sampleFile->Buffer.get()->data(), sampleFile->Buffer.get()->size(), nullptr); drflac_uint64 numFrames = flac->totalPCMFrameCount; - audioSample->audioSampleData.reserve(numFrames * flac->channels * 2); - drflac_read_pcm_frames_s16(flac, numFrames, (int16_t*)audioSample->audioSampleData.data()); - audioSample->sample.sampleAddr = audioSample->audioSampleData.data(); + audioSample->sample.sampleAddr = new uint8_t[numFrames * flac->channels * 2]; + drflac_read_pcm_frames_s16(flac, numFrames, (int16_t*)audioSample->sample.sampleAddr); drflac_close(flac); } @@ -118,13 +116,12 @@ static void OggDecoderWorker(std::shared_ptr audioSample, std: uint64_t numChannels = vi->channels; int bitStream = 0; size_t toRead = numFrames * numChannels * 2; - audioSample->audioSampleData.reserve(toRead); + audioSample->sample.sampleAddr = new uint8_t[toRead]; do { read = ov_read(&vf, dataBuff, 4096, 0, 2, 1, &bitStream); - memcpy(audioSample->audioSampleData.data() + pos, dataBuff, read); + memcpy(audioSample->sample.sampleAddr + pos, dataBuff, read); pos += read; } while (read != 0); - audioSample->sample.sampleAddr = audioSample->audioSampleData.data(); ov_clear(&vf); } @@ -143,34 +140,34 @@ std::shared_ptr ResourceFactoryBinaryAudioSampleV2::ReadResourc audioSample->sample.unk_bit25 = reader->ReadUByte(); audioSample->sample.size = reader->ReadUInt32(); - audioSample->audioSampleData.reserve(audioSample->sample.size); + audioSample->sample.sampleAddr = new uint8_t[audioSample->sample.size]; for (uint32_t i = 0; i < audioSample->sample.size; i++) { - audioSample->audioSampleData.push_back(reader->ReadUByte()); + audioSample->sample.sampleAddr[i] = reader->ReadUByte(); } - audioSample->sample.sampleAddr = audioSample->audioSampleData.data(); audioSample->loop.start = reader->ReadUInt32(); audioSample->loop.end = reader->ReadUInt32(); audioSample->loop.count = reader->ReadUInt32(); - audioSample->loopStateCount = reader->ReadUInt32(); + // This always seems to be 16. Can it be removed in V3? + uint32_t loopStateCount = reader->ReadUInt32(); for (int i = 0; i < 16; i++) { audioSample->loop.state[i] = 0; } - for (uint32_t i = 0; i < audioSample->loopStateCount; i++) { + for (uint32_t i = 0; i < loopStateCount; i++) { audioSample->loop.state[i] = reader->ReadInt16(); } audioSample->sample.loop = &audioSample->loop; audioSample->book.order = reader->ReadInt32(); audioSample->book.npredictors = reader->ReadInt32(); - audioSample->bookDataCount = reader->ReadUInt32(); + uint32_t bookDataCount = reader->ReadUInt32(); - audioSample->bookData.reserve(audioSample->bookDataCount); - for (uint32_t i = 0; i < audioSample->bookDataCount; i++) { - audioSample->bookData.push_back(reader->ReadInt16()); + audioSample->book.book = new int16_t[bookDataCount]; + + for (uint32_t i = 0; i < bookDataCount; i++) { + audioSample->book.book[i] = reader->ReadInt16(); } - audioSample->book.book = audioSample->bookData.data(); audioSample->sample.book = &audioSample->book; return audioSample; @@ -186,44 +183,44 @@ std::shared_ptr ResourceFactoryXMLAudioSampleV0::ReadResource(s std::shared_ptr initData = std::make_shared(); const char* customFormatStr = child->Attribute("CustomFormat"); memset(&audioSample->sample, 0, sizeof(audioSample->sample)); + audioSample->sample.unk_bit25 = 0; audioSample->sample.codec = CodecStrToInt(child->Attribute("Codec")); audioSample->sample.medium = ResourceFactoryXMLSoundFontV0::MediumStrToInt(child->Attribute("Medium")); - audioSample->sample.unk_bit25 = child->IntAttribute("Relocated"); audioSample->sample.unk_bit26 = child->IntAttribute("bit26"); - audioSample->loop.start = child->IntAttribute("LoopStart"); - audioSample->loop.end = child->IntAttribute("LoopEnd"); - audioSample->loop.count = child->IntAttribute("LoopCount"); - - // CODEC_ADPCM || CODEC_SMALL_ADPCM - memset(audioSample->loop.state, 0, sizeof(audioSample->loop.state)); - if (audioSample->sample.codec == 0 || audioSample->sample.codec == 3) { - tinyxml2::XMLElement* loopElement = child->FirstChildElement(); + + tinyxml2::XMLElement* loopRoot = child->FirstChildElement("ADPCMLoop"); + if (loopRoot != nullptr) { size_t i = 0; - if (loopElement != nullptr) { - while (strcmp(loopElement->Name(), "LoopState") == 0) { - audioSample->loop.state[i] = loopElement->IntAttribute("Loop"); - loopElement = loopElement->NextSiblingElement(); - i++; - } - audioSample->loop.count = i; - i = 0; - while (loopElement != nullptr && strcmp(loopElement->Name(), "Books") == 0) { - audioSample->bookData.push_back(loopElement->IntAttribute("Book")); - loopElement = loopElement->NextSiblingElement(); - i++; - } + audioSample->loop.start = loopRoot->UnsignedAttribute("Start"); + audioSample->loop.end = loopRoot->UnsignedAttribute("End"); + audioSample->loop.count = loopRoot->UnsignedAttribute("Count"); + tinyxml2::XMLElement* predictor = loopRoot->FirstChildElement("Predictor"); + while (predictor != nullptr) { + audioSample->loop.state[i++] = predictor->IntAttribute("State"); + predictor = predictor->NextSiblingElement(); } - audioSample->book.npredictors = child->IntAttribute("Npredictors"); - audioSample->book.order = child->IntAttribute("Order"); + } - audioSample->book.book = audioSample->bookData.data(); + tinyxml2::XMLElement* bookRoot = child->FirstChildElement("ADPCMBook"); + if (bookRoot != nullptr) { + size_t i = 0; + audioSample->book.npredictors = bookRoot->IntAttribute("Npredictors"); + audioSample->book.order = bookRoot->IntAttribute("Order"); + tinyxml2::XMLElement* book = bookRoot->FirstChildElement("Book"); + size_t numBooks = audioSample->book.npredictors * audioSample->book.order * 8; + audioSample->book.book = new int16_t[numBooks]; + while (book != nullptr) { + audioSample->book.book[i] = book->IntAttribute("Page"); + book = book->NextSiblingElement(); + } audioSample->sample.book = &audioSample->book; } + audioSample->sample.loop = &audioSample->loop; - size_t size = child->Int64Attribute("SampleSize"); + size_t size = child->Int64Attribute("Size"); audioSample->sample.size = size; - const char* path = child->Attribute("SamplePath"); + const char* path = child->Attribute("Path"); initData->Path = path; initData->IsCustom = false; initData->ByteOrder = Ship::Endianness::Native; @@ -241,10 +238,9 @@ std::shared_ptr ResourceFactoryXMLAudioSampleV0::ReadResource(s drwav_get_length_in_pcm_frames(&wav, &numFrames); audioSample->tuning = (wav.sampleRate * wav.channels) / 32000.0f; - audioSample->audioSampleData.reserve(numFrames * wav.channels * 2); + audioSample->sample.sampleAddr = new uint8_t[numFrames * wav.channels * 2]; - drwav_read_pcm_frames_s16(&wav, numFrames, (int16_t*)audioSample->audioSampleData.data()); - audioSample->sample.sampleAddr = audioSample->audioSampleData.data(); + drwav_read_pcm_frames_s16(&wav, numFrames, (int16_t*)audioSample->sample.sampleAddr); return audioSample; } else if (strcmp(customFormatStr, "mp3") == 0) { std::thread fileDecoderThread = std::thread(Mp3DecoderWorker, audioSample, sampleFile); @@ -261,12 +257,11 @@ std::shared_ptr ResourceFactoryXMLAudioSampleV0::ReadResource(s } } // Not a normal streamed sample. Fallback to the original ADPCM sample to be decoded by the audio engine. - audioSample->audioSampleData.resize(size); + audioSample->sample.sampleAddr = new uint8_t[size]; // Can't use memcpy due to endianness issues. for (uint32_t i = 0; i < size; i++) { - audioSample->audioSampleData[i] = sampleFile->Buffer.get()->data()[i]; + audioSample->sample.sampleAddr[i] = sampleFile->Buffer.get()->data()[i]; } - audioSample->sample.sampleAddr = audioSample->audioSampleData.data(); return audioSample; } diff --git a/soh/soh/resource/type/AudioSample.cpp b/soh/soh/resource/type/AudioSample.cpp index 2887658224b..4a499cff7b0 100644 --- a/soh/soh/resource/type/AudioSample.cpp +++ b/soh/soh/resource/type/AudioSample.cpp @@ -1,6 +1,14 @@ #include "AudioSample.h" namespace SOH { +AudioSample::~AudioSample() { + if (sample.book->book != nullptr) { + delete[] sample.book->book; + } + if (sample.sampleAddr != nullptr) { + delete[] sample.sampleAddr; + } +} Sample* AudioSample::GetPointer() { return &sample; } diff --git a/soh/soh/resource/type/AudioSample.h b/soh/soh/resource/type/AudioSample.h index e47ea58325e..dad42c4b8a9 100644 --- a/soh/soh/resource/type/AudioSample.h +++ b/soh/soh/resource/type/AudioSample.h @@ -1,24 +1,23 @@ #pragma once #include -#include #include "Resource.h" #include namespace SOH { typedef struct { - /* 0x00 */ uintptr_t start; - /* 0x04 */ uintptr_t end; + /* 0x00 */ u32 start; + /* 0x04 */ u32 end; /* 0x08 */ u32 count; /* 0x0C */ char unk_0C[0x4]; /* 0x10 */ s16 state[16]; // only exists if count != 0. 8-byte aligned -} AdpcmLoop; // size = 0x30 (or 0x10) +} AdpcmLoop; // size = 0x30 (or 0x10) typedef struct { /* 0x00 */ s32 order; /* 0x04 */ s32 npredictors; /* 0x08 */ s16* book; // size 8 * order * npredictors. 8-byte aligned -} AdpcmBook; // s +} AdpcmBook; // s typedef struct { union { @@ -42,19 +41,14 @@ class AudioSample : public Ship::Resource { AudioSample() : Resource(std::shared_ptr()) { } + ~AudioSample(); Sample* GetPointer(); size_t GetPointerSize(); Sample sample; - std::vector audioSampleData; - AdpcmLoop loop; - uint32_t loopStateCount; - AdpcmBook book; - uint32_t bookDataCount; - std::vector bookData; // Only applies to streamed audio float tuning = -1.0f; }; From 158ac7112085ace5bd53a9109ea1df99e3367802 Mon Sep 17 00:00:00 2001 From: Louis <35883445+louist103@users.noreply.github.com> Date: Thu, 28 Nov 2024 13:52:50 -0500 Subject: [PATCH 9/9] fix --- soh/include/z64audio.h | 4 ++-- soh/soh/resource/importer/AudioSampleFactory.cpp | 2 +- soh/soh/resource/type/AudioSample.cpp | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/soh/include/z64audio.h b/soh/include/z64audio.h index a074d1fe4c6..76f31b0734f 100644 --- a/soh/include/z64audio.h +++ b/soh/include/z64audio.h @@ -119,8 +119,8 @@ typedef struct { } AdsrEnvelope; // size = 0x4 typedef struct AdpcmLoop { - /* 0x00 */ uintptr_t start; - /* 0x04 */ uintptr_t loopEnd; // numSamples position into the sample where the loop ends + /* 0x00 */ u32 start; + /* 0x04 */ u32 loopEnd; // numSamples position into the sample where the loop ends /* 0x08 */ u32 count; // The number of times the loop is played before the sound completes. Setting count to -1 // indicates that the loop should play indefinitely. /* 0x0C */ u32 sampleEnd; // total number of s16-samples in the sample audio clip diff --git a/soh/soh/resource/importer/AudioSampleFactory.cpp b/soh/soh/resource/importer/AudioSampleFactory.cpp index 175e38676a8..614f1a83fca 100644 --- a/soh/soh/resource/importer/AudioSampleFactory.cpp +++ b/soh/soh/resource/importer/AudioSampleFactory.cpp @@ -210,7 +210,7 @@ std::shared_ptr ResourceFactoryXMLAudioSampleV0::ReadResource(s size_t numBooks = audioSample->book.npredictors * audioSample->book.order * 8; audioSample->book.book = new int16_t[numBooks]; while (book != nullptr) { - audioSample->book.book[i] = book->IntAttribute("Page"); + audioSample->book.book[i++] = book->IntAttribute("Page"); book = book->NextSiblingElement(); } audioSample->sample.book = &audioSample->book; diff --git a/soh/soh/resource/type/AudioSample.cpp b/soh/soh/resource/type/AudioSample.cpp index 4a499cff7b0..b4a53975768 100644 --- a/soh/soh/resource/type/AudioSample.cpp +++ b/soh/soh/resource/type/AudioSample.cpp @@ -2,7 +2,7 @@ namespace SOH { AudioSample::~AudioSample() { - if (sample.book->book != nullptr) { + if (sample.book != nullptr && sample.book->book != nullptr) { delete[] sample.book->book; } if (sample.sampleAddr != nullptr) {