From 5f0bd6e9f052821ab8d0d8394b9df4467176b6a0 Mon Sep 17 00:00:00 2001 From: KiritoDv Date: Sun, 2 Feb 2025 23:34:11 -0600 Subject: [PATCH] First implementation of louist system --- .github/workflows/linux.yml | 2 +- .github/workflows/mac.yml | 2 +- .github/workflows/main.yml | 4 +- CMakeLists.txt | 26 +- assets/yaml/cn/rev0/ast_audio.yaml | 31 +++ cmake/modules/FindOgg.cmake | 61 +++++ cmake/modules/FindVorbis.cmake | 197 ++++++++++++++ docs/BUILDING.md | 18 +- src/audio/audio_synthesis.c | 3 +- src/port/Engine.cpp | 5 +- .../importers/audio/SampleFactory.cpp | 244 ++++++++++++++++-- .../resource/importers/audio/SampleFactory.h | 12 +- tools/Torch | 2 +- 13 files changed, 567 insertions(+), 40 deletions(-) create mode 100644 assets/yaml/cn/rev0/ast_audio.yaml create mode 100644 cmake/modules/FindOgg.cmake create mode 100644 cmake/modules/FindVorbis.cmake diff --git a/.github/workflows/linux.yml b/.github/workflows/linux.yml index 251ddcc8..c0b68d29 100644 --- a/.github/workflows/linux.yml +++ b/.github/workflows/linux.yml @@ -14,7 +14,7 @@ jobs: - name: Update machine run: sudo apt update - name: Install dependencies - run: sudo apt-get install gcc g++ git cmake ninja-build lsb-release libsdl2-dev libpng-dev libsdl2-net-dev libzip-dev zipcmp zipmerge ziptool nlohmann-json3-dev libtinyxml2-dev libspdlog-dev libboost-dev libopengl-dev + run: sudo apt-get install gcc g++ git cmake ninja-build lsb-release libsdl2-dev libpng-dev libsdl2-net-dev libzip-dev zipcmp zipmerge ziptool nlohmann-json3-dev libtinyxml2-dev libspdlog-dev libboost-dev libopengl-dev libogg-dev libvorbis-dev - name: Install latest SDL run: | export PATH="/usr/lib/ccache:/usr/local/opt/ccache/libexec:$PATH" diff --git a/.github/workflows/mac.yml b/.github/workflows/mac.yml index 2cc8c768..f70c52e2 100644 --- a/.github/workflows/mac.yml +++ b/.github/workflows/mac.yml @@ -12,7 +12,7 @@ jobs: with: submodules: recursive - name: Install dependencies - run: brew install sdl2 libpng glew ninja cmake libzip nlohmann-json tinyxml2 spdlog + run: brew install sdl2 libpng glew ninja cmake libzip nlohmann-json tinyxml2 spdlog vorbis-tools - name: Build run: | cmake -H. -Bbuild-cmake -GNinja -DCMAKE_BUILD_TYPE=Release diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 160e17e2..54ac983f 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -81,7 +81,7 @@ jobs: with: submodules: recursive - name: Install dependencies - run: brew install sdl2 libpng glew ninja cmake libzip nlohmann-json tinyxml2 spdlog + run: brew install sdl2 libpng glew ninja cmake libzip nlohmann-json tinyxml2 spdlog vorbis-tools - name: Build run: | cmake -H. -Bbuild-cmake -GNinja -DCMAKE_BUILD_TYPE=Release @@ -115,7 +115,7 @@ jobs: - name: Update machine run: sudo apt update - name: Install dependencies - run: sudo apt-get install gcc g++ git cmake ninja-build lsb-release libsdl2-dev libpng-dev libsdl2-net-dev libzip-dev zipcmp zipmerge ziptool nlohmann-json3-dev libtinyxml2-dev libspdlog-dev libboost-dev libopengl-dev + run: sudo apt-get install gcc g++ git cmake ninja-build lsb-release libsdl2-dev libpng-dev libsdl2-net-dev libzip-dev zipcmp zipmerge ziptool nlohmann-json3-dev libtinyxml2-dev libspdlog-dev libboost-dev libopengl-dev libogg-dev libvorbis-dev - name: ccache uses: hendrikmuhs/ccache-action@v1.2.14 with: diff --git a/CMakeLists.txt b/CMakeLists.txt index 557a23c4..ffe5a58f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -2,6 +2,7 @@ cmake_minimum_required(VERSION 3.16.0 FATAL_ERROR) # Set the project version and language project(Starship VERSION 0.1.0 LANGUAGES C CXX ASM) +include(FetchContent) if(APPLE) enable_language(OBJCXX) @@ -26,7 +27,7 @@ include(cmake/automate-vcpkg.cmake) set(VCPKG_TRIPLET x64-windows-static) set(VCPKG_TARGET_TRIPLET x64-windows-static) vcpkg_bootstrap() -vcpkg_install_packages(zlib bzip2 libzip libpng sdl2 glew glfw3 nlohmann-json tinyxml2 spdlog) +vcpkg_install_packages(zlib bzip2 libzip libpng sdl2 glew glfw3 nlohmann-json tinyxml2 spdlog libogg libvorbis) set_property(DIRECTORY ${CMAKE_SOURCE_DIR} PROPERTY VS_STARTUP_PROJECT ${PROJECT_NAME}) set_property(DIRECTORY ${CMAKE_SOURCE_DIR} PROPERTY VS_DEBUGGER_WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}) @@ -188,6 +189,13 @@ if (MSVC) endif() endif() +FetchContent_Declare( + dr_libs + GIT_REPOSITORY https://github.com/mackron/dr_libs.git + GIT_TAG da35f9d6c7374a95353fd1df1d394d44ab66cf01 +) +FetchContent_MakeAvailable(dr_libs) + #==============================================================================# # Libultraship Integration # #==============================================================================# @@ -224,6 +232,7 @@ include_directories( ${CMAKE_CURRENT_SOURCE_DIR}/libultraship/src/graphic ${SDL2_INCLUDE_DIRS} ${GLEW_INCLUDE_DIRS} + ${dr_libs_SOURCE_DIR} ) add_subdirectory(libultraship ${CMAKE_CURRENT_SOURCE_DIR}/libultraship) @@ -280,8 +289,17 @@ endif() if (CMAKE_SYSTEM_NAME STREQUAL "Windows") + find_package(Ogg CONFIG REQUIRED) + link_libraries(Ogg::ogg) + + find_package(Vorbis CONFIG REQUIRED) + link_libraries(Vorbis::vorbisfile) set(ADDITIONAL_LIBRARY_DEPENDENCIES "$<$:SDL2_net::SDL2_net-static>" + "Ogg::ogg" + "Vorbis::vorbis" + "Vorbis::vorbisenc" + "Vorbis::vorbisfile" ) elseif(CMAKE_SYSTEM_NAME STREQUAL "NintendoSwitch") set(ADDITIONAL_LIBRARY_DEPENDENCIES @@ -295,8 +313,14 @@ elseif(CMAKE_SYSTEM_NAME STREQUAL "CafeOS") ${DEVKITPRO}/portlibs/wiiu/include/ ) else() + find_package(Ogg REQUIRED) + find_package(Vorbis REQUIRED) set(ADDITIONAL_LIBRARY_DEPENDENCIES "$<$:SDL2_net::SDL2_net>" + "Ogg::ogg" + "Vorbis::vorbis" + "Vorbis::vorbisenc" + "Vorbis::vorbisfile" ) endif() diff --git a/assets/yaml/cn/rev0/ast_audio.yaml b/assets/yaml/cn/rev0/ast_audio.yaml new file mode 100644 index 00000000..e6bb775a --- /dev/null +++ b/assets/yaml/cn/rev0/ast_audio.yaml @@ -0,0 +1,31 @@ +:config: + force: true + header: + code: + - '#include "sys.h"' + - '#include "sf64audio_provisional.h"' + +audio_setup: + type: NAUDIO:V1:AUDIO_SETUP + driver: SF64 + audio_seq: + size: 0x3AFD0 + offset: 0xE9950 + audio_bank: + size: 0x1CB20 + offset: 0x1183A0 + audio_table: + size: 0x691AF0 + offset: 0x134EC0 + +audio_sample_bank_table: + { type: NAUDIO:V1:AUDIO_TABLE, format: SAMPLE, offset: 0xC1460, symbol: gSampleBankTableInit } + +audio_seq_table: + { type: NAUDIO:V1:AUDIO_TABLE, format: SEQUENCE, offset: 0xC14A0, symbol: gSeqTableInit } + +audio_soundfont_table: + { type: NAUDIO:V1:AUDIO_TABLE, format: SOUNDFONT, offset: 0xC18D0, symbol: gSoundFontTableInit } + +audio_seq_font_table: + { type: ARRAY, count: 283, array_type: u8, offset: 0xC1AF0, symbol: gSeqFontTableInit } \ No newline at end of file diff --git a/cmake/modules/FindOgg.cmake b/cmake/modules/FindOgg.cmake new file mode 100644 index 00000000..f606144f --- /dev/null +++ b/cmake/modules/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) \ No newline at end of file diff --git a/cmake/modules/FindVorbis.cmake b/cmake/modules/FindVorbis.cmake new file mode 100644 index 00000000..0d3d6624 --- /dev/null +++ b/cmake/modules/FindVorbis.cmake @@ -0,0 +1,197 @@ +#[=======================================================================[.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) \ No newline at end of file diff --git a/docs/BUILDING.md b/docs/BUILDING.md index d042a25c..aac581e6 100644 --- a/docs/BUILDING.md +++ b/docs/BUILDING.md @@ -83,34 +83,34 @@ C:\Program Files\CMake\bin\cmake.exe --build build-cmake --target clean #### Debian/Ubuntu ```sh # using gcc -apt-get install gcc g++ git cmake ninja-build lsb-release libsdl2-dev libpng-dev libsdl2-net-dev libzip-dev zipcmp zipmerge ziptool nlohmann-json3-dev libtinyxml2-dev libspdlog-dev libboost-dev libopengl-dev +apt-get install gcc g++ git cmake ninja-build lsb-release libsdl2-dev libpng-dev libsdl2-net-dev libzip-dev zipcmp zipmerge ziptool nlohmann-json3-dev libtinyxml2-dev libspdlog-dev libboost-dev libopengl-dev libogg-dev ibvorbis-dev # or using clang -apt-get install clang git cmake ninja-build lsb-release libsdl2-dev libpng-dev libsdl2-net-dev libzip-dev zipcmp zipmerge ziptool nlohmann-json3-dev libtinyxml2-dev libspdlog-dev libboost-dev libopengl-dev +apt-get install clang git cmake ninja-build lsb-release libsdl2-dev libpng-dev libsdl2-net-dev libzip-dev zipcmp zipmerge ziptool nlohmann-json3-dev libtinyxml2-dev libspdlog-dev libboost-dev libopengl-dev libogg-dev libvorbis-dev ``` #### Arch ```sh # using gcc -pacman -S gcc git cmake ninja lsb-release sdl2 libpng libzip nlohmann-json tinyxml2 spdlog sdl2_net boost +pacman -S gcc git cmake ninja lsb-release sdl2 libpng libzip nlohmann-json tinyxml2 spdlog sdl2_net boost libogg libvorbis # or using clang -pacman -S clang git cmake ninja lsb-release sdl2 libpng libzip nlohmann-json tinyxml2 spdlog sdl2_net boost +pacman -S clang git cmake ninja lsb-release sdl2 libpng libzip nlohmann-json tinyxml2 spdlog sdl2_net boost libogg libvorbis ``` #### Fedora ```sh # using gcc -dnf install gcc gcc-c++ git cmake ninja-build lsb_release SDL2-devel libpng-devel libzip-devel libzip-tools nlohmann-json-devel tinyxml2-devel spdlog-devel boost-devel +dnf install gcc gcc-c++ git cmake ninja-build lsb_release SDL2-devel libpng-devel libzip-devel libzip-tools nlohmann-json-devel tinyxml2-devel spdlog-devel boost-devel libogg-devel libvorbis-devel # or using clang -dnf install clang git cmake ninja-build lsb_release SDL2-devel libpng-devel libzip-devel libzip-tools nlohmann-json-devel tinyxml2-devel spdlog-devel boost-devel +dnf install clang git cmake ninja-build lsb_release SDL2-devel libpng-devel libzip-devel libzip-tools nlohmann-json-devel tinyxml2-devel spdlog-devel boost-devel libogg-devel libvorbis-devel ``` #### openSUSE ```sh # using gcc -zypper in gcc gcc-c++ git cmake ninja SDL2-devel libpng16-devel libzip-devel libzip-tools nlohmann_json-devel tinyxml2-devel spdlog-devel +zypper in gcc gcc-c++ git cmake ninja SDL2-devel libpng16-devel libzip-devel libzip-tools nlohmann_json-devel tinyxml2-devel spdlog-devel libogg-devel libvorbis-devel # or using clang -zypper in clang libstdc++-devel git cmake ninja SDL2-devel libpng16-devel libzip-devel libzip-tools nlohmann_json-devel tinyxml2-devel spdlog-devel +zypper in clang libstdc++-devel git cmake ninja SDL2-devel libpng16-devel libzip-devel libzip-tools nlohmann_json-devel tinyxml2-devel spdlog-devel libogg-devel libvorbis-devel ``` ### Build @@ -160,7 +160,7 @@ cmake --build build-cmake --target clean ``` ## macOS -Requires Xcode (or xcode-tools) && `sdl2, libpng, glew, ninja, cmake, nlohmann-json, libzip` (can be installed via homebrew, macports, etc) +Requires Xcode (or xcode-tools) && `sdl2, libpng, glew, ninja, cmake, nlohmann-json, libzip, vorbis-tools` (can be installed via homebrew, macports, etc) **Important: For maximum performance make sure you have ninja build tools installed!** diff --git a/src/audio/audio_synthesis.c b/src/audio/audio_synthesis.c index 090e5128..f360229a 100644 --- a/src/audio/audio_synthesis.c +++ b/src/audio/audio_synthesis.c @@ -1036,13 +1036,14 @@ Acmd* AudioSynth_ProcessNote(s32 noteIndex, NoteSubEu* noteSub, NoteSynthesisSta goto skip; case CODEC_S16: + flags = A_CONTINUE; skipBytes = 0; size_t bytesToRead; numSamplesProcessed += numSamplesToLoadAdj; dmemUncompressedAddrOffset1 = numSamplesToLoadAdj; if (((synthState->samplePosInt * 2) + (numSamplesToLoadAdj)*SAMPLE_SIZE) < bookSample->size) { - bytesToRead = (numSamplesToLoadAdj)*SAMPLE_SIZE; + bytesToRead = (numSamplesToLoadAdj) * SAMPLE_SIZE; } else { bytesToRead = bookSample->size - (synthState->samplePosInt * 2); } diff --git a/src/port/Engine.cpp b/src/port/Engine.cpp index 98492a67..aaebe67a 100644 --- a/src/port/Engine.cpp +++ b/src/port/Engine.cpp @@ -237,8 +237,9 @@ GameEngine::GameEngine() { loader->RegisterResourceFactory(std::make_shared(), RESOURCE_FORMAT_BINARY, "Sample", static_cast(SF64::ResourceType::Sample), 1); - loader->RegisterResourceFactory(std::make_shared(), RESOURCE_FORMAT_BINARY, - "Sample", static_cast(SF64::ResourceType::Sample), 2); + + loader->RegisterResourceFactory(std::make_shared(), RESOURCE_FORMAT_XML, + "Sample", static_cast(SF64::ResourceType::Sample), 0); loader->RegisterResourceFactory(std::make_shared(), RESOURCE_FORMAT_BINARY, "SoundFont", static_cast(SF64::ResourceType::SoundFont), 0); diff --git a/src/port/resource/importers/audio/SampleFactory.cpp b/src/port/resource/importers/audio/SampleFactory.cpp index cac380d2..8289266a 100644 --- a/src/port/resource/importers/audio/SampleFactory.cpp +++ b/src/port/resource/importers/audio/SampleFactory.cpp @@ -1,6 +1,14 @@ #include "SampleFactory.h" #include "../ResourceUtil.h" #include "port/resource/type/audio/Sample.h" +#include "sf64audio_provisional.h" +#define DR_WAV_IMPLEMENTATION +#include + +#define DR_MP3_IMPLEMENTATION +#include + +#include "vorbis/vorbisfile.h" namespace SF64 { std::shared_ptr ResourceFactoryBinarySampleV1::ReadResource(std::shared_ptr file) { @@ -23,7 +31,7 @@ std::shared_ptr ResourceFactoryBinarySampleV1::ReadResource(std if(sample->mSample.codec == 2){ sample->mSample.medium = 2; for(size_t i = 0; i < sample->mSample.size / 2; i++){ - int16_t* sampleData = (int16_t*) sample->mSample.sampleAddr; + auto sampleData = (int16_t*) sample->mSample.sampleAddr; sampleData[i] = BSWAP16(sampleData[i]); } } else { @@ -35,36 +43,230 @@ std::shared_ptr ResourceFactoryBinarySampleV1::ReadResource(std return sample; } -std::shared_ptr ResourceFactoryBinarySampleV2::ReadResource(std::shared_ptr file) { +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 sample, 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; + + sample->mSample.sampleAddr = new uint8_t[numFrames * channels * 2]; + drmp3_read_pcm_frames_s16(&mp3, numFrames, (int16_t*)sample->mSample.sampleAddr); +} + +static void OggDecoderWorker(std::shared_ptr sample, 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; + sample->mSample.sampleAddr = new uint8_t[toRead]; + do { + read = ov_read(&vf, dataBuff, 4096, 0, 2, 1, &bitStream); + memcpy(sample->mSample.sampleAddr + pos, dataBuff, read); + pos += read; + } while (read != 0); + ov_clear(&vf); +} + +std::shared_ptr ResourceFactoryXMLSampleV0::ReadResource(std::shared_ptr file) { if (!FileHasValidFormatAndReader(file)) { return nullptr; } auto sample = std::make_shared(file->InitData); - auto reader = std::get>(file->Reader); + auto child = std::get>(file->Reader)->FirstChildElement(); + std::shared_ptr initData = std::make_shared(); + const char* customFormatStr = child->Attribute("CustomFormat"); + memset(&sample->mSample, 0, sizeof(sample->mSample)); + sample->mSample.isRelocated = 0; + sample->mSample.codec = CodecStrToInt(child->Attribute("Codec"), file->InitData->Path.c_str()); + sample->mSample.medium = MediumStrToInt(child->Attribute("Medium")); + sample->mSample.unk = child->IntAttribute("bit26"); - sample->mSample.codec = reader->ReadUByte(); - sample->mSample.medium = reader->ReadUByte(); - sample->mSample.unk = reader->ReadUByte(); - sample->mSample.size = reader->ReadUInt32(); - sample->mSample.tuning = reader->ReadFloat(); - sample->mSample.loop = LoadChild(reader->ReadUInt64()); - sample->mSample.book = LoadChild(reader->ReadUInt64()); - sample->mSample.sampleAddr = new uint8_t[sample->mSample.size]; - reader->Read((char*) sample->mSample.sampleAddr, sample->mSample.size); - - if(sample->mSample.codec == 2){ - sample->mSample.medium = 2; - for(size_t i = 0; i < sample->mSample.size / 2; i++){ - int16_t* sampleData = (int16_t*) sample->mSample.sampleAddr; - sampleData[i] = BSWAP16(sampleData[i]); + tinyxml2::XMLElement* loopRoot = child->FirstChildElement("ADPCMLoop"); + if (loopRoot != nullptr) { + size_t i = 0; + sample->mSample.loop = new AdpcmLoopData(); + sample->mSample.loop->start = loopRoot->UnsignedAttribute("Start"); + sample->mSample.loop->end = loopRoot->UnsignedAttribute("End"); + sample->mSample.loop->count = loopRoot->UnsignedAttribute("Count"); + tinyxml2::XMLElement* predictor = loopRoot->FirstChildElement("Predictor"); + while (predictor != nullptr) { + sample->mSample.loop->predictorState[i++] = predictor->IntAttribute("State"); + predictor = predictor->NextSiblingElement(); } - } else { - sample->mSample.medium = 0; } - sample->mSample.isRelocated = 1; + tinyxml2::XMLElement* bookRoot = child->FirstChildElement("ADPCMBook"); + if (bookRoot != nullptr) { + size_t i = 0; + sample->mSample.book = new AdpcmBookData(); + sample->mSample.book->numPredictors = bookRoot->IntAttribute("Npredictors"); + sample->mSample.book->order = bookRoot->IntAttribute("Order"); + tinyxml2::XMLElement* book = bookRoot->FirstChildElement("Book"); + size_t numBooks = sample->mSample.book->numPredictors * sample->mSample.book->order * 8; + sample->mSample.book->book = new int16_t[numBooks]; + while (book != nullptr) { + sample->mSample.book->book[i++] = book->IntAttribute("Page"); + book = book->NextSiblingElement(); + } + } + + size_t size = child->Int64Attribute("Size"); + sample->mSample.size = size; + + const char* path = child->Attribute("Path"); + 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); + + drwav_get_length_in_pcm_frames(&wav, &numFrames); + + sample->mSample.tuning = (wav.sampleRate * wav.channels) / 32000.0f; + sample->mSample.sampleAddr = new uint8_t[numFrames * wav.channels * 2]; + + drwav_read_pcm_frames_s16(&wav, numFrames, (int16_t*)sample->mSample.sampleAddr); + return sample; + } else if (strcmp(customFormatStr, "ogg") == 0) { + std::thread fileDecoderThread = std::thread(OggDecoderWorker, sample, sampleFile); + fileDecoderThread.detach(); + return sample; + } else if (strcmp(customFormatStr, "mp3") == 0) { + std::thread fileDecoderThread = std::thread(Mp3DecoderWorker, sample, sampleFile); + fileDecoderThread.detach(); + return sample; + } + } + // Not a normal streamed sample. Fallback to the original ADPCM sample to be decoded by the audio engine. + sample->mSample.sampleAddr = new uint8_t[size]; + // Can't use memcpy due to endianness issues. + for (uint32_t i = 0; i < size; i++) { + sample->mSample.sampleAddr[i] = (*sampleFile->Buffer)[i]; + } return sample; } + +uint8_t ResourceFactoryXMLSampleV0::CodecStrToInt(const char* str, const char* file) { + 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 { + char buff[2048]; + snprintf(buff, 2048, + "Invalid codec in %s. Got %s, expected ADPCM, S8, S16MEM, ADPCMSMALL, REVERB, S16, UNK6, UNK7.", file, + str); + throw std::runtime_error(buff); + } +} + +uint32_t ResourceFactoryXMLSampleV0::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 { + char buff[2048]; + snprintf(buff, 2048, + "Bad medium value. Got %s, expected Ram, Unk, Cart, or Disk.", str); + throw std::runtime_error(buff); + } +} } // namespace LUS diff --git a/src/port/resource/importers/audio/SampleFactory.h b/src/port/resource/importers/audio/SampleFactory.h index 4b2017eb..01fbf97e 100644 --- a/src/port/resource/importers/audio/SampleFactory.h +++ b/src/port/resource/importers/audio/SampleFactory.h @@ -1,16 +1,26 @@ #pragma once #include "Resource.h" +#include "ResourceFactoryXML.h" #include "ResourceFactoryBinary.h" namespace SF64 { +struct OggFileData { + void* data; + size_t pos; + size_t size; +}; + class ResourceFactoryBinarySampleV1 : public Ship::ResourceFactoryBinary { public: std::shared_ptr ReadResource(std::shared_ptr file) override; }; -class ResourceFactoryBinarySampleV2 : public Ship::ResourceFactoryBinary { +class ResourceFactoryXMLSampleV0 : public Ship::ResourceFactoryXML { public: std::shared_ptr ReadResource(std::shared_ptr file) override; + private: + static uint8_t CodecStrToInt(const char* str, const char* file); + static uint32_t MediumStrToInt(const char* str); }; }; // namespace LUS diff --git a/tools/Torch b/tools/Torch index 28dcd128..053d97a4 160000 --- a/tools/Torch +++ b/tools/Torch @@ -1 +1 @@ -Subproject commit 28dcd128b0406a43ab7ef9718213f7ab7d3736f8 +Subproject commit 053d97a433f3cfc9607b7cedb512d2e7ee1dc78a