diff --git a/CMakeLists.txt b/CMakeLists.txt index f4d0cc74..2bcee73f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -15,8 +15,8 @@ set(CMAKE_CXX_STANDARD 20 CACHE STRING "The C++ standard to use") set(CMAKE_C_STANDARD 11 CACHE STRING "The C standard to use") add_compile_options($<$:/MP>) add_compile_options($<$:/MT>) -# add_compile_options(-fsanitize=address) -# add_link_options(-fsanitize=address) +#add_compile_options(-fsanitize=address) +#add_link_options(-fsanitize=address) # Add a custom module path to locate additional CMake modules set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_SOURCE_DIR}/cmake/modules/") @@ -507,99 +507,6 @@ if (CMAKE_CXX_COMPILER_ID MATCHES "GNU|Clang|AppleClang") endif() endif() -#==============================================================================# -# Sound File Generation # -#==============================================================================# - -execute_process( - OUTPUT_VARIABLE endian_and_bitwidth - COMMAND ${Python3_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/tools/determine-endian-bitwidth.py - WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} -) -string(REPLACE " " ";" endian_and_bitwidth ${endian_and_bitwidth}) - -set (TABLE_FILES "") -file(GLOB_RECURSE AIFF_FILES RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} CONFIGURE_DEPENDS "*.aiff") -foreach(aifffile ${AIFF_FILES}) - get_filename_component(OUTPUT_FILE_WLE ${aifffile} NAME_WLE) - get_filename_component(OUTPUT_DIR ${aifffile} DIRECTORY) - set(OUTPUT_FILE ${CMAKE_CURRENT_BINARY_DIR}/${OUTPUT_DIR}/${OUTPUT_FILE_WLE}.table) - list(APPEND TABLE_FILES ${OUTPUT_FILE}) - add_custom_command( - OUTPUT ${OUTPUT_FILE} - COMMAND ${CMAKE_COMMAND} -E make_directory ${OUTPUT_DIR} - VERBATIM COMMAND ${CMAKE_COMMAND} -E env "TOOLS_PATH=$" $ ${CMAKE_CURRENT_SOURCE_DIR}/${aifffile} > ${OUTPUT_FILE} - DEPENDS aiff_extract_cookbook - DEPENDS ${aifffile} - ) -endforeach(aifffile) - -set(AIFC_FILES "") -foreach(aifffile ${AIFF_FILES}) - get_filename_component(OUTPUT_FILE_WLE ${aifffile} NAME_WLE) - get_filename_component(OUTPUT_DIR ${aifffile} DIRECTORY) - set(OUTPUT_FILE ${CMAKE_CURRENT_BINARY_DIR}/${OUTPUT_DIR}/${OUTPUT_FILE_WLE}.aifc) - list(APPEND AIFC_FILES ${OUTPUT_FILE}) - add_custom_command( - OUTPUT ${OUTPUT_FILE} - COMMAND ${CMAKE_COMMAND} -E make_directory ${OUTPUT_DIR} - VERBATIM COMMAND $ -c ${CMAKE_CURRENT_BINARY_DIR}/${OUTPUT_DIR}/${OUTPUT_FILE_WLE}.table ${CMAKE_CURRENT_SOURCE_DIR}/${aifffile} ${OUTPUT_FILE} - DEPENDS vadpcm_enc - DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/${OUTPUT_DIR}/${OUTPUT_FILE_WLE}.table ${CMAKE_CURRENT_SOURCE_DIR}/${aifffile} - ) -endforeach(aifffile) - -add_custom_command( - OUTPUT sound/sound_data.ctl.inc.c sound/sound_data.tbl.inc.c - COMMAND ${Python3_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/tools/assemble_sound.py ${CMAKE_CURRENT_BINARY_DIR}/sound/samples/ ${CMAKE_CURRENT_SOURCE_DIR}/sound/sound_banks ${CMAKE_CURRENT_BINARY_DIR}/sound/sound_data.ctl ${CMAKE_CURRENT_BINARY_DIR}/sound/ctl_header ${CMAKE_CURRENT_BINARY_DIR}/sound/sound_data.tbl ${CMAKE_CURRENT_BINARY_DIR}/sound/tbl_header -DVERSION_US ${endian_and_bitwidth} - COMMAND ${Python3_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/tools/conv2hex.py ${CMAKE_CURRENT_BINARY_DIR}/sound/sound_data.ctl > ${CMAKE_CURRENT_BINARY_DIR}/sound/sound_data.ctl.inc.c - COMMAND ${Python3_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/tools/conv2hex.py ${CMAKE_CURRENT_BINARY_DIR}/sound/sound_data.tbl > ${CMAKE_CURRENT_BINARY_DIR}/sound/sound_data.tbl.inc.c - WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} - DEPENDS ${TABLE_FILES} - DEPENDS ${AIFC_FILES} -) -list(APPEND INC_C_FILES ${CMAKE_CURRENT_BINARY_DIR}/sound/sound_data.ctl.inc.c ${CMAKE_CURRENT_BINARY_DIR}/sound/sound_data.tbl.inc.c) - -file(GLOB_RECURSE SEQUENCES_FILES RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} "sound/*.m64") - -file(GLOB_RECURSE SEQUENCES_S_FILES RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} "sound/*.s") -foreach(sequence_s ${SEQUENCES_S_FILES}) - get_filename_component(OUTPUT_FILE_WLE ${sequence_s} NAME_WLE) - get_filename_component(OUTPUT_DIR ${sequence_s} DIRECTORY) - set(OUTPUT_FILE ${CMAKE_CURRENT_BINARY_DIR}/${OUTPUT_DIR}/${OUTPUT_FILE_WLE}.m64) - - if(APPLE) - set(COPY_COMMAND i686-w64-mingw32-objcopy) - else() - set(COPY_COMMAND objcopy) - endif() - - if(APPLE) - set(AS_COMMAND i686-w64-mingw32-as) - else() - set(AS_COMMAND as) - endif() - - add_custom_command( - OUTPUT ${OUTPUT_FILE} - COMMAND ${CMAKE_COMMAND} -E make_directory ${OUTPUT_DIR} - COMMAND ${CPP} ${CMAKE_CURRENT_SOURCE_DIR}/${sequence_s} -I${CMAKE_CURRENT_SOURCE_DIR}/include -DVERSION_US | ${AS_COMMAND} -o ${CMAKE_CURRENT_BINARY_DIR}/${OUTPUT_DIR}/${OUTPUT_FILE_WLE}.o - VERBATIM COMMAND ${COPY_COMMAND} -j .rodata ${CMAKE_CURRENT_BINARY_DIR}/${OUTPUT_DIR}/${OUTPUT_FILE_WLE}.o -O binary ${OUTPUT_FILE} - DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/${sequence_s} - ) - list(APPEND SEQUENCES_FILES ${OUTPUT_FILE}) -endforeach(sequence_s) - -add_custom_command( - OUTPUT sound/sequences.bin.inc.c sound/bank_sets.inc.c - COMMAND ${Python3_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/tools/assemble_sound.py --sequences ${CMAKE_CURRENT_BINARY_DIR}/sound/sequences.bin ${CMAKE_CURRENT_BINARY_DIR}/sound/sequences_header ${CMAKE_CURRENT_BINARY_DIR}/sound/bank_sets ${CMAKE_CURRENT_SOURCE_DIR}/sound/sound_banks ${CMAKE_CURRENT_SOURCE_DIR}/sound/sequences.json ${SEQUENCES_FILES} -DVERSION_US ${endian_and_bitwidth} - COMMAND ${Python3_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/tools/conv2hex.py ${CMAKE_CURRENT_BINARY_DIR}/sound/sequences.bin > ${CMAKE_CURRENT_BINARY_DIR}/sound/sequences.bin.inc.c - COMMAND ${Python3_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/tools/conv2hex.py ${CMAKE_CURRENT_BINARY_DIR}/sound/bank_sets > ${CMAKE_CURRENT_BINARY_DIR}/sound/bank_sets.inc.c - WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} - DEPENDS ${SEQUENCES_FILES} -) -list(APPEND INC_C_FILES ${CMAKE_CURRENT_BINARY_DIR}/sound/sequences.bin.inc.c ${CMAKE_CURRENT_BINARY_DIR}/sound/bank_sets.inc.c) - add_custom_target( ConvertIncCFiles DEPENDS ${INC_C_FILES} diff --git a/assets.json b/assets.json index 691735f2..0719a80c 100644 --- a/assets.json +++ b/assets.json @@ -1086,6 +1086,7 @@ "sound/sequences/sh/20_cutscene_ending.m64": [1887,{"sh":[7981824]}], "sound/sequences/sh/21_menu_file_select.m64": [786,{"sh":[7983712]}], "sound/sequences/sh/22_cutscene_lakitu.m64": [317,{"sh":[7984512]}], +"sound/sequences/us/00_sound_player.m64": [13456,{"us":[8063360]}], "sound/sequences/us/01_cutscene_collect_star.m64": [619,{"us":[8076816]}], "sound/sequences/us/02_menu_title_screen.m64": [8254,{"us":[8077440]}], "sound/sequences/us/03_level_grass.m64": [5122,{"us":[8085696]}], diff --git a/include/banks_table.h b/include/banks_table.h new file mode 100644 index 00000000..1c05cb14 --- /dev/null +++ b/include/banks_table.h @@ -0,0 +1,129 @@ +#pragma once +#include "align_asset_macro.h" + +#define dgBank00 "__OTR__sound/banks/us/00_bank" +static const ALIGN_ASSET(2) char gBank00[] = dgBank00; + +#define dgBank01 "__OTR__sound/banks/us/01_bank" +static const ALIGN_ASSET(2) char gBank01[] = dgBank01; + +#define dgBank02 "__OTR__sound/banks/us/02_bank" +static const ALIGN_ASSET(2) char gBank02[] = dgBank02; + +#define dgBank03 "__OTR__sound/banks/us/03_bank" +static const ALIGN_ASSET(2) char gBank03[] = dgBank03; + +#define dgBank04 "__OTR__sound/banks/us/04_bank" +static const ALIGN_ASSET(2) char gBank04[] = dgBank04; + +#define dgBank05 "__OTR__sound/banks/us/05_bank" +static const ALIGN_ASSET(2) char gBank05[] = dgBank05; + +#define dgBank06 "__OTR__sound/banks/us/06_bank" +static const ALIGN_ASSET(2) char gBank06[] = dgBank06; + +#define dgBank07 "__OTR__sound/banks/us/07_bank" +static const ALIGN_ASSET(2) char gBank07[] = dgBank07; + +#define dgBank08 "__OTR__sound/banks/us/08_bank" +static const ALIGN_ASSET(2) char gBank08[] = dgBank08; + +#define dgBank09 "__OTR__sound/banks/us/09_bank" +static const ALIGN_ASSET(2) char gBank09[] = dgBank09; + +#define dgBank0A "__OTR__sound/banks/us/0A_bank" +static const ALIGN_ASSET(2) char gBank0A[] = dgBank0A; + +#define dgBank0B "__OTR__sound/banks/us/0B_bank" +static const ALIGN_ASSET(2) char gBank0B[] = dgBank0B; + +#define dgBank0C "__OTR__sound/banks/us/0C_bank" +static const ALIGN_ASSET(2) char gBank0C[] = dgBank0C; + +#define dgBank0D "__OTR__sound/banks/us/0D_bank" +static const ALIGN_ASSET(2) char gBank0D[] = dgBank0D; + +#define dgBank0E "__OTR__sound/banks/us/0E_bank" +static const ALIGN_ASSET(2) char gBank0E[] = dgBank0E; + +#define dgBank0F "__OTR__sound/banks/us/0F_bank" +static const ALIGN_ASSET(2) char gBank0F[] = dgBank0F; + +#define dgBank10 "__OTR__sound/banks/us/10_bank" +static const ALIGN_ASSET(2) char gBank10[] = dgBank10; + +#define dgBank11 "__OTR__sound/banks/us/11_bank" +static const ALIGN_ASSET(2) char gBank11[] = dgBank11; + +#define dgBank12 "__OTR__sound/banks/us/12_bank" +static const ALIGN_ASSET(2) char gBank12[] = dgBank12; + +#define dgBank13 "__OTR__sound/banks/us/13_bank" +static const ALIGN_ASSET(2) char gBank13[] = dgBank13; + +#define dgBank14 "__OTR__sound/banks/us/14_bank" +static const ALIGN_ASSET(2) char gBank14[] = dgBank14; + +#define dgBank15 "__OTR__sound/banks/us/15_bank" +static const ALIGN_ASSET(2) char gBank15[] = dgBank15; + +#define dgBank16 "__OTR__sound/banks/us/16_bank" +static const ALIGN_ASSET(2) char gBank16[] = dgBank16; + +#define dgBank17 "__OTR__sound/banks/us/17_bank" +static const ALIGN_ASSET(2) char gBank17[] = dgBank17; + +#define dgBank18 "__OTR__sound/banks/us/18_bank" +static const ALIGN_ASSET(2) char gBank18[] = dgBank18; + +#define dgBank19 "__OTR__sound/banks/us/19_bank" +static const ALIGN_ASSET(2) char gBank19[] = dgBank19; + +#define dgBank1A "__OTR__sound/banks/us/1A_bank" +static const ALIGN_ASSET(2) char gBank1A[] = dgBank1A; + +#define dgBank1B "__OTR__sound/banks/us/1B_bank" +static const ALIGN_ASSET(2) char gBank1B[] = dgBank1B; + +#define dgBank1C "__OTR__sound/banks/us/1C_bank" +static const ALIGN_ASSET(2) char gBank1C[] = dgBank1C; + +#define dgBank1D "__OTR__sound/banks/us/1D_bank" +static const ALIGN_ASSET(2) char gBank1D[] = dgBank1D; + +#define dgBank1E "__OTR__sound/banks/us/1E_bank" +static const ALIGN_ASSET(2) char gBank1E[] = dgBank1E; + +#define dgBank1F "__OTR__sound/banks/us/1F_bank" +static const ALIGN_ASSET(2) char gBank1F[] = dgBank1F; + +#define dgBank20 "__OTR__sound/banks/us/20_bank" +static const ALIGN_ASSET(2) char gBank20[] = dgBank20; + +#define dgBank21 "__OTR__sound/banks/us/21_bank" +static const ALIGN_ASSET(2) char gBank21[] = dgBank21; + +#define dgBank22 "__OTR__sound/banks/us/22_bank" +static const ALIGN_ASSET(2) char gBank22[] = dgBank22; + +#define dgBank23 "__OTR__sound/banks/us/23_bank" +static const ALIGN_ASSET(2) char gBank23[] = dgBank23; + +#define dgBank24 "__OTR__sound/banks/us/24_bank" +static const ALIGN_ASSET(2) char gBank24[] = dgBank24; + +#define dgBank25 "__OTR__sound/banks/us/25_bank" +static const ALIGN_ASSET(2) char gBank25[] = dgBank25; + +static const char* gBankTable[] = { + gBank00, gBank01, gBank02, gBank03, + gBank04, gBank05, gBank06, gBank07, + gBank08, gBank09, gBank0A, gBank0B, + gBank0C, gBank0D, gBank0E, gBank0F, + gBank10, gBank11, gBank12, gBank13, + gBank14, gBank15, gBank16, gBank17, + gBank18, gBank19, gBank1A, gBank1B, + gBank1C, gBank1D, gBank1E, gBank1F, + gBank20, gBank21, gBank22, gBank23, + gBank24, gBank25, +}; diff --git a/include/dr_mp3.h b/include/dr_mp3.h new file mode 100644 index 00000000..b4d606c2 --- /dev/null +++ b/include/dr_mp3.h @@ -0,0 +1,4831 @@ +/* +MP3 audio decoder. Choice of public domain or MIT-0. See license statements at the end of this file. +dr_mp3 - v0.6.37 - 2023-07-07 + +David Reid - mackron@gmail.com + +GitHub: https://github.com/mackron/dr_libs + +Based on minimp3 (https://github.com/lieff/minimp3) which is where the real work was done. See the bottom of this file for differences between minimp3 and dr_mp3. +*/ + +/* +RELEASE NOTES - VERSION 0.6 +=========================== +Version 0.6 includes breaking changes with the configuration of decoders. The ability to customize the number of output channels and the sample rate has been +removed. You must now use the channel count and sample rate reported by the MP3 stream itself, and all channel and sample rate conversion must be done +yourself. + + +Changes to Initialization +------------------------- +Previously, `drmp3_init()`, etc. took a pointer to a `drmp3_config` object that allowed you to customize the output channels and sample rate. This has been +removed. If you need the old behaviour you will need to convert the data yourself or just not upgrade. The following APIs have changed. + + `drmp3_init()` + `drmp3_init_memory()` + `drmp3_init_file()` + + +Miscellaneous Changes +--------------------- +Support for loading a file from a `wchar_t` string has been added via the `drmp3_init_file_w()` API. +*/ + +/* +Introducation +============= +dr_mp3 is a single file library. To use it, do something like the following in one .c file. + + ```c + #define DR_MP3_IMPLEMENTATION + #include "dr_mp3.h" + ``` + +You can then #include this file in other parts of the program as you would with any other header file. To decode audio data, do something like the following: + + ```c + drmp3 mp3; + if (!drmp3_init_file(&mp3, "MySong.mp3", NULL)) { + // Failed to open file + } + + ... + + drmp3_uint64 framesRead = drmp3_read_pcm_frames_f32(pMP3, framesToRead, pFrames); + ``` + +The drmp3 object is transparent so you can get access to the channel count and sample rate like so: + + ``` + drmp3_uint32 channels = mp3.channels; + drmp3_uint32 sampleRate = mp3.sampleRate; + ``` + +The example above initializes a decoder from a file, but you can also initialize it from a block of memory and read and seek callbacks with +`drmp3_init_memory()` and `drmp3_init()` respectively. + +You do not need to do any annoying memory management when reading PCM frames - this is all managed internally. You can request any number of PCM frames in each +call to `drmp3_read_pcm_frames_f32()` and it will return as many PCM frames as it can, up to the requested amount. + +You can also decode an entire file in one go with `drmp3_open_and_read_pcm_frames_f32()`, `drmp3_open_memory_and_read_pcm_frames_f32()` and +`drmp3_open_file_and_read_pcm_frames_f32()`. + + +Build Options +============= +#define these options before including this file. + +#define DR_MP3_NO_STDIO + Disable drmp3_init_file(), etc. + +#define DR_MP3_NO_SIMD + Disable SIMD optimizations. +*/ + +#ifndef dr_mp3_h +#define dr_mp3_h + +#ifdef __cplusplus +extern "C" { +#endif + +#define DRMP3_STRINGIFY(x) #x +#define DRMP3_XSTRINGIFY(x) DRMP3_STRINGIFY(x) + +#define DRMP3_VERSION_MAJOR 0 +#define DRMP3_VERSION_MINOR 6 +#define DRMP3_VERSION_REVISION 37 +#define DRMP3_VERSION_STRING DRMP3_XSTRINGIFY(DRMP3_VERSION_MAJOR) "." DRMP3_XSTRINGIFY(DRMP3_VERSION_MINOR) "." DRMP3_XSTRINGIFY(DRMP3_VERSION_REVISION) + +#include /* For size_t. */ + +/* Sized Types */ +typedef signed char drmp3_int8; +typedef unsigned char drmp3_uint8; +typedef signed short drmp3_int16; +typedef unsigned short drmp3_uint16; +typedef signed int drmp3_int32; +typedef unsigned int drmp3_uint32; +#if defined(_MSC_VER) && !defined(__clang__) +typedef signed __int64 drmp3_int64; + typedef unsigned __int64 drmp3_uint64; +#else +#if defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6))) +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wlong-long" +#if defined(__clang__) +#pragma GCC diagnostic ignored "-Wc++11-long-long" +#endif +#endif +typedef signed long long drmp3_int64; +typedef unsigned long long drmp3_uint64; +#if defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6))) +#pragma GCC diagnostic pop +#endif +#endif +#if defined(__LP64__) || defined(_WIN64) || (defined(__x86_64__) && !defined(__ILP32__)) || defined(_M_X64) || defined(__ia64) || defined (_M_IA64) || defined(__aarch64__) || defined(_M_ARM64) || defined(__powerpc64__) +typedef drmp3_uint64 drmp3_uintptr; +#else +typedef drmp3_uint32 drmp3_uintptr; +#endif +typedef drmp3_uint8 drmp3_bool8; +typedef drmp3_uint32 drmp3_bool32; +#define DRMP3_TRUE 1 +#define DRMP3_FALSE 0 +/* End Sized Types */ + +/* Decorations */ +#if !defined(DRMP3_API) +#if defined(DRMP3_DLL) +#if defined(_WIN32) + #define DRMP3_DLL_IMPORT __declspec(dllimport) + #define DRMP3_DLL_EXPORT __declspec(dllexport) + #define DRMP3_DLL_PRIVATE static + #else + #if defined(__GNUC__) && __GNUC__ >= 4 + #define DRMP3_DLL_IMPORT __attribute__((visibility("default"))) + #define DRMP3_DLL_EXPORT __attribute__((visibility("default"))) + #define DRMP3_DLL_PRIVATE __attribute__((visibility("hidden"))) + #else + #define DRMP3_DLL_IMPORT + #define DRMP3_DLL_EXPORT + #define DRMP3_DLL_PRIVATE static + #endif + #endif + + #if defined(DR_MP3_IMPLEMENTATION) || defined(DRMP3_IMPLEMENTATION) + #define DRMP3_API DRMP3_DLL_EXPORT + #else + #define DRMP3_API DRMP3_DLL_IMPORT + #endif + #define DRMP3_PRIVATE DRMP3_DLL_PRIVATE +#else +#define DRMP3_API extern +#define DRMP3_PRIVATE static +#endif +#endif +/* End Decorations */ + +/* Result Codes */ +typedef drmp3_int32 drmp3_result; +#define DRMP3_SUCCESS 0 +#define DRMP3_ERROR -1 /* A generic error. */ +#define DRMP3_INVALID_ARGS -2 +#define DRMP3_INVALID_OPERATION -3 +#define DRMP3_OUT_OF_MEMORY -4 +#define DRMP3_OUT_OF_RANGE -5 +#define DRMP3_ACCESS_DENIED -6 +#define DRMP3_DOES_NOT_EXIST -7 +#define DRMP3_ALREADY_EXISTS -8 +#define DRMP3_TOO_MANY_OPEN_FILES -9 +#define DRMP3_INVALID_FILE -10 +#define DRMP3_TOO_BIG -11 +#define DRMP3_PATH_TOO_LONG -12 +#define DRMP3_NAME_TOO_LONG -13 +#define DRMP3_NOT_DIRECTORY -14 +#define DRMP3_IS_DIRECTORY -15 +#define DRMP3_DIRECTORY_NOT_EMPTY -16 +#define DRMP3_END_OF_FILE -17 +#define DRMP3_NO_SPACE -18 +#define DRMP3_BUSY -19 +#define DRMP3_IO_ERROR -20 +#define DRMP3_INTERRUPT -21 +#define DRMP3_UNAVAILABLE -22 +#define DRMP3_ALREADY_IN_USE -23 +#define DRMP3_BAD_ADDRESS -24 +#define DRMP3_BAD_SEEK -25 +#define DRMP3_BAD_PIPE -26 +#define DRMP3_DEADLOCK -27 +#define DRMP3_TOO_MANY_LINKS -28 +#define DRMP3_NOT_IMPLEMENTED -29 +#define DRMP3_NO_MESSAGE -30 +#define DRMP3_BAD_MESSAGE -31 +#define DRMP3_NO_DATA_AVAILABLE -32 +#define DRMP3_INVALID_DATA -33 +#define DRMP3_TIMEOUT -34 +#define DRMP3_NO_NETWORK -35 +#define DRMP3_NOT_UNIQUE -36 +#define DRMP3_NOT_SOCKET -37 +#define DRMP3_NO_ADDRESS -38 +#define DRMP3_BAD_PROTOCOL -39 +#define DRMP3_PROTOCOL_UNAVAILABLE -40 +#define DRMP3_PROTOCOL_NOT_SUPPORTED -41 +#define DRMP3_PROTOCOL_FAMILY_NOT_SUPPORTED -42 +#define DRMP3_ADDRESS_FAMILY_NOT_SUPPORTED -43 +#define DRMP3_SOCKET_NOT_SUPPORTED -44 +#define DRMP3_CONNECTION_RESET -45 +#define DRMP3_ALREADY_CONNECTED -46 +#define DRMP3_NOT_CONNECTED -47 +#define DRMP3_CONNECTION_REFUSED -48 +#define DRMP3_NO_HOST -49 +#define DRMP3_IN_PROGRESS -50 +#define DRMP3_CANCELLED -51 +#define DRMP3_MEMORY_ALREADY_MAPPED -52 +#define DRMP3_AT_END -53 +/* End Result Codes */ + +#define DRMP3_MAX_PCM_FRAMES_PER_MP3_FRAME 1152 +#define DRMP3_MAX_SAMPLES_PER_FRAME (DRMP3_MAX_PCM_FRAMES_PER_MP3_FRAME*2) + +/* Inline */ +#ifdef _MSC_VER +#define DRMP3_INLINE __forceinline +#elif defined(__GNUC__) +/* +I've had a bug report where GCC is emitting warnings about functions possibly not being inlineable. This warning happens when +the __attribute__((always_inline)) attribute is defined without an "inline" statement. I think therefore there must be some +case where "__inline__" is not always defined, thus the compiler emitting these warnings. When using -std=c89 or -ansi on the +command line, we cannot use the "inline" keyword and instead need to use "__inline__". In an attempt to work around this issue +I am using "__inline__" only when we're compiling in strict ANSI mode. +*/ +#if defined(__STRICT_ANSI__) +#define DRMP3_GNUC_INLINE_HINT __inline__ +#else +#define DRMP3_GNUC_INLINE_HINT inline +#endif + +#if (__GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 2)) || defined(__clang__) +#define DRMP3_INLINE DRMP3_GNUC_INLINE_HINT __attribute__((always_inline)) +#else +#define DRMP3_INLINE DRMP3_GNUC_INLINE_HINT +#endif +#elif defined(__WATCOMC__) +#define DRMP3_INLINE __inline +#else + #define DRMP3_INLINE +#endif +/* End Inline */ + + +DRMP3_API void drmp3_version(drmp3_uint32* pMajor, drmp3_uint32* pMinor, drmp3_uint32* pRevision); +DRMP3_API const char* drmp3_version_string(void); + + +/* Allocation Callbacks */ +typedef struct +{ + void* pUserData; + void* (* onMalloc)(size_t sz, void* pUserData); + void* (* onRealloc)(void* p, size_t sz, void* pUserData); + void (* onFree)(void* p, void* pUserData); +} drmp3_allocation_callbacks; +/* End Allocation Callbacks */ + + +/* +Low Level Push API +================== +*/ +typedef struct +{ + int frame_bytes, channels, hz, layer, bitrate_kbps; +} drmp3dec_frame_info; + +typedef struct +{ + float mdct_overlap[2][9*32], qmf_state[15*2*32]; + int reserv, free_format_bytes; + drmp3_uint8 header[4], reserv_buf[511]; +} drmp3dec; + +/* Initializes a low level decoder. */ +DRMP3_API void drmp3dec_init(drmp3dec *dec); + +/* Reads a frame from a low level decoder. */ +DRMP3_API int drmp3dec_decode_frame(drmp3dec *dec, const drmp3_uint8 *mp3, int mp3_bytes, void *pcm, drmp3dec_frame_info *info); + +/* Helper for converting between f32 and s16. */ +DRMP3_API void drmp3dec_f32_to_s16(const float *in, drmp3_int16 *out, size_t num_samples); + + + +/* +Main API (Pull API) +=================== +*/ +typedef enum +{ + drmp3_seek_origin_start, + drmp3_seek_origin_current +} drmp3_seek_origin; + +typedef struct +{ + drmp3_uint64 seekPosInBytes; /* Points to the first byte of an MP3 frame. */ + drmp3_uint64 pcmFrameIndex; /* The index of the PCM frame this seek point targets. */ + drmp3_uint16 mp3FramesToDiscard; /* The number of whole MP3 frames to be discarded before pcmFramesToDiscard. */ + drmp3_uint16 pcmFramesToDiscard; /* The number of leading samples to read and discard. These are discarded after mp3FramesToDiscard. */ +} drmp3_seek_point; + +/* +Callback for when data is read. Return value is the number of bytes actually read. + +pUserData [in] The user data that was passed to drmp3_init(), drmp3_open() and family. +pBufferOut [out] The output buffer. +bytesToRead [in] The number of bytes to read. + +Returns the number of bytes actually read. + +A return value of less than bytesToRead indicates the end of the stream. Do _not_ return from this callback until +either the entire bytesToRead is filled or you have reached the end of the stream. +*/ +typedef size_t (* drmp3_read_proc)(void* pUserData, void* pBufferOut, size_t bytesToRead); + +/* +Callback for when data needs to be seeked. + +pUserData [in] The user data that was passed to drmp3_init(), drmp3_open() and family. +offset [in] The number of bytes to move, relative to the origin. Will never be negative. +origin [in] The origin of the seek - the current position or the start of the stream. + +Returns whether or not the seek was successful. + +Whether or not it is relative to the beginning or current position is determined by the "origin" parameter which +will be either drmp3_seek_origin_start or drmp3_seek_origin_current. +*/ +typedef drmp3_bool32 (* drmp3_seek_proc)(void* pUserData, int offset, drmp3_seek_origin origin); + +typedef struct +{ + drmp3_uint32 channels; + drmp3_uint32 sampleRate; +} drmp3_config; + +typedef struct +{ + drmp3dec decoder; + drmp3_uint32 channels; + drmp3_uint32 sampleRate; + drmp3_read_proc onRead; + drmp3_seek_proc onSeek; + void* pUserData; + drmp3_allocation_callbacks allocationCallbacks; + drmp3_uint32 mp3FrameChannels; /* The number of channels in the currently loaded MP3 frame. Internal use only. */ + drmp3_uint32 mp3FrameSampleRate; /* The sample rate of the currently loaded MP3 frame. Internal use only. */ + drmp3_uint32 pcmFramesConsumedInMP3Frame; + drmp3_uint32 pcmFramesRemainingInMP3Frame; + drmp3_uint8 pcmFrames[sizeof(float)*DRMP3_MAX_SAMPLES_PER_FRAME]; /* <-- Multipled by sizeof(float) to ensure there's enough room for DR_MP3_FLOAT_OUTPUT. */ + drmp3_uint64 currentPCMFrame; /* The current PCM frame, globally, based on the output sample rate. Mainly used for seeking. */ + drmp3_uint64 streamCursor; /* The current byte the decoder is sitting on in the raw stream. */ + drmp3_seek_point* pSeekPoints; /* NULL by default. Set with drmp3_bind_seek_table(). Memory is owned by the client. dr_mp3 will never attempt to free this pointer. */ + drmp3_uint32 seekPointCount; /* The number of items in pSeekPoints. When set to 0 assumes to no seek table. Defaults to zero. */ + size_t dataSize; + size_t dataCapacity; + size_t dataConsumed; + drmp3_uint8* pData; + drmp3_bool32 atEnd : 1; + struct + { + const drmp3_uint8* pData; + size_t dataSize; + size_t currentReadPos; + } memory; /* Only used for decoders that were opened against a block of memory. */ +} drmp3; + +/* +Initializes an MP3 decoder. + +onRead [in] The function to call when data needs to be read from the client. +onSeek [in] The function to call when the read position of the client data needs to move. +pUserData [in, optional] A pointer to application defined data that will be passed to onRead and onSeek. + +Returns true if successful; false otherwise. + +Close the loader with drmp3_uninit(). + +See also: drmp3_init_file(), drmp3_init_memory(), drmp3_uninit() +*/ +DRMP3_API drmp3_bool32 drmp3_init(drmp3* pMP3, drmp3_read_proc onRead, drmp3_seek_proc onSeek, void* pUserData, const drmp3_allocation_callbacks* pAllocationCallbacks); + +/* +Initializes an MP3 decoder from a block of memory. + +This does not create a copy of the data. It is up to the application to ensure the buffer remains valid for +the lifetime of the drmp3 object. + +The buffer should contain the contents of the entire MP3 file. +*/ +DRMP3_API drmp3_bool32 drmp3_init_memory(drmp3* pMP3, const void* pData, size_t dataSize, const drmp3_allocation_callbacks* pAllocationCallbacks); + +#ifndef DR_MP3_NO_STDIO +/* +Initializes an MP3 decoder from a file. + +This holds the internal FILE object until drmp3_uninit() is called. Keep this in mind if you're caching drmp3 +objects because the operating system may restrict the number of file handles an application can have open at +any given time. +*/ +DRMP3_API drmp3_bool32 drmp3_init_file(drmp3* pMP3, const char* pFilePath, const drmp3_allocation_callbacks* pAllocationCallbacks); +DRMP3_API drmp3_bool32 drmp3_init_file_w(drmp3* pMP3, const wchar_t* pFilePath, const drmp3_allocation_callbacks* pAllocationCallbacks); +#endif + +/* +Uninitializes an MP3 decoder. +*/ +DRMP3_API void drmp3_uninit(drmp3* pMP3); + +/* +Reads PCM frames as interleaved 32-bit IEEE floating point PCM. + +Note that framesToRead specifies the number of PCM frames to read, _not_ the number of MP3 frames. +*/ +DRMP3_API drmp3_uint64 drmp3_read_pcm_frames_f32(drmp3* pMP3, drmp3_uint64 framesToRead, float* pBufferOut); + +/* +Reads PCM frames as interleaved signed 16-bit integer PCM. + +Note that framesToRead specifies the number of PCM frames to read, _not_ the number of MP3 frames. +*/ +DRMP3_API drmp3_uint64 drmp3_read_pcm_frames_s16(drmp3* pMP3, drmp3_uint64 framesToRead, drmp3_int16* pBufferOut); + +/* +Seeks to a specific frame. + +Note that this is _not_ an MP3 frame, but rather a PCM frame. +*/ +DRMP3_API drmp3_bool32 drmp3_seek_to_pcm_frame(drmp3* pMP3, drmp3_uint64 frameIndex); + +/* +Calculates the total number of PCM frames in the MP3 stream. Cannot be used for infinite streams such as internet +radio. Runs in linear time. Returns 0 on error. +*/ +DRMP3_API drmp3_uint64 drmp3_get_pcm_frame_count(drmp3* pMP3); + +/* +Calculates the total number of MP3 frames in the MP3 stream. Cannot be used for infinite streams such as internet +radio. Runs in linear time. Returns 0 on error. +*/ +DRMP3_API drmp3_uint64 drmp3_get_mp3_frame_count(drmp3* pMP3); + +/* +Calculates the total number of MP3 and PCM frames in the MP3 stream. Cannot be used for infinite streams such as internet +radio. Runs in linear time. Returns 0 on error. + +This is equivalent to calling drmp3_get_mp3_frame_count() and drmp3_get_pcm_frame_count() except that it's more efficient. +*/ +DRMP3_API drmp3_bool32 drmp3_get_mp3_and_pcm_frame_count(drmp3* pMP3, drmp3_uint64* pMP3FrameCount, drmp3_uint64* pPCMFrameCount); + +/* +Calculates the seekpoints based on PCM frames. This is slow. + +pSeekpoint count is a pointer to a uint32 containing the seekpoint count. On input it contains the desired count. +On output it contains the actual count. The reason for this design is that the client may request too many +seekpoints, in which case dr_mp3 will return a corrected count. + +Note that seektable seeking is not quite sample exact when the MP3 stream contains inconsistent sample rates. +*/ +DRMP3_API drmp3_bool32 drmp3_calculate_seek_points(drmp3* pMP3, drmp3_uint32* pSeekPointCount, drmp3_seek_point* pSeekPoints); + +/* +Binds a seek table to the decoder. + +This does _not_ make a copy of pSeekPoints - it only references it. It is up to the application to ensure this +remains valid while it is bound to the decoder. + +Use drmp3_calculate_seek_points() to calculate the seek points. +*/ +DRMP3_API drmp3_bool32 drmp3_bind_seek_table(drmp3* pMP3, drmp3_uint32 seekPointCount, drmp3_seek_point* pSeekPoints); + + +/* +Opens an decodes an entire MP3 stream as a single operation. + +On output pConfig will receive the channel count and sample rate of the stream. + +Free the returned pointer with drmp3_free(). +*/ +DRMP3_API float* drmp3_open_and_read_pcm_frames_f32(drmp3_read_proc onRead, drmp3_seek_proc onSeek, void* pUserData, drmp3_config* pConfig, drmp3_uint64* pTotalFrameCount, const drmp3_allocation_callbacks* pAllocationCallbacks); +DRMP3_API drmp3_int16* drmp3_open_and_read_pcm_frames_s16(drmp3_read_proc onRead, drmp3_seek_proc onSeek, void* pUserData, drmp3_config* pConfig, drmp3_uint64* pTotalFrameCount, const drmp3_allocation_callbacks* pAllocationCallbacks); + +DRMP3_API float* drmp3_open_memory_and_read_pcm_frames_f32(const void* pData, size_t dataSize, drmp3_config* pConfig, drmp3_uint64* pTotalFrameCount, const drmp3_allocation_callbacks* pAllocationCallbacks); +DRMP3_API drmp3_int16* drmp3_open_memory_and_read_pcm_frames_s16(const void* pData, size_t dataSize, drmp3_config* pConfig, drmp3_uint64* pTotalFrameCount, const drmp3_allocation_callbacks* pAllocationCallbacks); + +#ifndef DR_MP3_NO_STDIO +DRMP3_API float* drmp3_open_file_and_read_pcm_frames_f32(const char* filePath, drmp3_config* pConfig, drmp3_uint64* pTotalFrameCount, const drmp3_allocation_callbacks* pAllocationCallbacks); +DRMP3_API drmp3_int16* drmp3_open_file_and_read_pcm_frames_s16(const char* filePath, drmp3_config* pConfig, drmp3_uint64* pTotalFrameCount, const drmp3_allocation_callbacks* pAllocationCallbacks); +#endif + +/* +Allocates a block of memory on the heap. +*/ +DRMP3_API void* drmp3_malloc(size_t sz, const drmp3_allocation_callbacks* pAllocationCallbacks); + +/* +Frees any memory that was allocated by a public drmp3 API. +*/ +DRMP3_API void drmp3_free(void* p, const drmp3_allocation_callbacks* pAllocationCallbacks); + +#ifdef __cplusplus +} +#endif +#endif /* dr_mp3_h */ + + +/************************************************************************************************************************************************************ + ************************************************************************************************************************************************************ + + IMPLEMENTATION + + ************************************************************************************************************************************************************ + ************************************************************************************************************************************************************/ +#if defined(DR_MP3_IMPLEMENTATION) || defined(DRMP3_IMPLEMENTATION) +#ifndef dr_mp3_c +#define dr_mp3_c + +#include +#include +#include /* For INT_MAX */ + +DRMP3_API void drmp3_version(drmp3_uint32* pMajor, drmp3_uint32* pMinor, drmp3_uint32* pRevision) +{ + if (pMajor) { + *pMajor = DRMP3_VERSION_MAJOR; + } + + if (pMinor) { + *pMinor = DRMP3_VERSION_MINOR; + } + + if (pRevision) { + *pRevision = DRMP3_VERSION_REVISION; + } +} + +DRMP3_API const char* drmp3_version_string(void) +{ + return DRMP3_VERSION_STRING; +} + +/* Disable SIMD when compiling with TCC for now. */ +#if defined(__TINYC__) +#define DR_MP3_NO_SIMD +#endif + +#define DRMP3_OFFSET_PTR(p, offset) ((void*)((drmp3_uint8*)(p) + (offset))) + +#define DRMP3_MAX_FREE_FORMAT_FRAME_SIZE 2304 /* more than ISO spec's */ +#ifndef DRMP3_MAX_FRAME_SYNC_MATCHES +#define DRMP3_MAX_FRAME_SYNC_MATCHES 10 +#endif + +#define DRMP3_MAX_L3_FRAME_PAYLOAD_BYTES DRMP3_MAX_FREE_FORMAT_FRAME_SIZE /* MUST be >= 320000/8/32000*1152 = 1440 */ + +#define DRMP3_MAX_BITRESERVOIR_BYTES 511 +#define DRMP3_SHORT_BLOCK_TYPE 2 +#define DRMP3_STOP_BLOCK_TYPE 3 +#define DRMP3_MODE_MONO 3 +#define DRMP3_MODE_JOINT_STEREO 1 +#define DRMP3_HDR_SIZE 4 +#define DRMP3_HDR_IS_MONO(h) (((h[3]) & 0xC0) == 0xC0) +#define DRMP3_HDR_IS_MS_STEREO(h) (((h[3]) & 0xE0) == 0x60) +#define DRMP3_HDR_IS_FREE_FORMAT(h) (((h[2]) & 0xF0) == 0) +#define DRMP3_HDR_IS_CRC(h) (!((h[1]) & 1)) +#define DRMP3_HDR_TEST_PADDING(h) ((h[2]) & 0x2) +#define DRMP3_HDR_TEST_MPEG1(h) ((h[1]) & 0x8) +#define DRMP3_HDR_TEST_NOT_MPEG25(h) ((h[1]) & 0x10) +#define DRMP3_HDR_TEST_I_STEREO(h) ((h[3]) & 0x10) +#define DRMP3_HDR_TEST_MS_STEREO(h) ((h[3]) & 0x20) +#define DRMP3_HDR_GET_STEREO_MODE(h) (((h[3]) >> 6) & 3) +#define DRMP3_HDR_GET_STEREO_MODE_EXT(h) (((h[3]) >> 4) & 3) +#define DRMP3_HDR_GET_LAYER(h) (((h[1]) >> 1) & 3) +#define DRMP3_HDR_GET_BITRATE(h) ((h[2]) >> 4) +#define DRMP3_HDR_GET_SAMPLE_RATE(h) (((h[2]) >> 2) & 3) +#define DRMP3_HDR_GET_MY_SAMPLE_RATE(h) (DRMP3_HDR_GET_SAMPLE_RATE(h) + (((h[1] >> 3) & 1) + ((h[1] >> 4) & 1))*3) +#define DRMP3_HDR_IS_FRAME_576(h) ((h[1] & 14) == 2) +#define DRMP3_HDR_IS_LAYER_1(h) ((h[1] & 6) == 6) + +#define DRMP3_BITS_DEQUANTIZER_OUT -1 +#define DRMP3_MAX_SCF (255 + DRMP3_BITS_DEQUANTIZER_OUT*4 - 210) +#define DRMP3_MAX_SCFI ((DRMP3_MAX_SCF + 3) & ~3) + +#define DRMP3_MIN(a, b) ((a) > (b) ? (b) : (a)) +#define DRMP3_MAX(a, b) ((a) < (b) ? (b) : (a)) + +#if !defined(DR_MP3_NO_SIMD) + +#if !defined(DR_MP3_ONLY_SIMD) && (defined(_M_X64) || defined(__x86_64__) || defined(__aarch64__) || defined(_M_ARM64)) +/* x64 always have SSE2, arm64 always have neon, no need for generic code */ +#define DR_MP3_ONLY_SIMD +#endif + +#if ((defined(_MSC_VER) && _MSC_VER >= 1400) && defined(_M_X64)) || ((defined(__i386) || defined(_M_IX86) || defined(__i386__) || defined(__x86_64__)) && ((defined(_M_IX86_FP) && _M_IX86_FP == 2) || defined(__SSE2__))) +#if defined(_MSC_VER) +#include +#endif +#include +#define DRMP3_HAVE_SSE 1 +#define DRMP3_HAVE_SIMD 1 +#define DRMP3_VSTORE _mm_storeu_ps +#define DRMP3_VLD _mm_loadu_ps +#define DRMP3_VSET _mm_set1_ps +#define DRMP3_VADD _mm_add_ps +#define DRMP3_VSUB _mm_sub_ps +#define DRMP3_VMUL _mm_mul_ps +#define DRMP3_VMAC(a, x, y) _mm_add_ps(a, _mm_mul_ps(x, y)) +#define DRMP3_VMSB(a, x, y) _mm_sub_ps(a, _mm_mul_ps(x, y)) +#define DRMP3_VMUL_S(x, s) _mm_mul_ps(x, _mm_set1_ps(s)) +#define DRMP3_VREV(x) _mm_shuffle_ps(x, x, _MM_SHUFFLE(0, 1, 2, 3)) +typedef __m128 drmp3_f4; +#if defined(_MSC_VER) || defined(DR_MP3_ONLY_SIMD) +#define drmp3_cpuid __cpuid +#else +static __inline__ __attribute__((always_inline)) void drmp3_cpuid(int CPUInfo[], const int InfoType) +{ +#if defined(__PIC__) + __asm__ __volatile__( +#if defined(__x86_64__) + "push %%rbx\n" + "cpuid\n" + "xchgl %%ebx, %1\n" + "pop %%rbx\n" +#else + "xchgl %%ebx, %1\n" + "cpuid\n" + "xchgl %%ebx, %1\n" +#endif + : "=a" (CPUInfo[0]), "=r" (CPUInfo[1]), "=c" (CPUInfo[2]), "=d" (CPUInfo[3]) + : "a" (InfoType)); +#else + __asm__ __volatile__( + "cpuid" + : "=a" (CPUInfo[0]), "=b" (CPUInfo[1]), "=c" (CPUInfo[2]), "=d" (CPUInfo[3]) + : "a" (InfoType)); +#endif +} +#endif +static int drmp3_have_simd(void) +{ +#ifdef DR_MP3_ONLY_SIMD + return 1; +#else + static int g_have_simd; + int CPUInfo[4]; +#ifdef MINIMP3_TEST + static int g_counter; + if (g_counter++ > 100) + return 0; +#endif + if (g_have_simd) + goto end; + drmp3_cpuid(CPUInfo, 0); + if (CPUInfo[0] > 0) + { + drmp3_cpuid(CPUInfo, 1); + g_have_simd = (CPUInfo[3] & (1 << 26)) + 1; /* SSE2 */ + return g_have_simd - 1; + } + +end: + return g_have_simd - 1; +#endif +} +#elif defined(__ARM_NEON) || defined(__aarch64__) || defined(_M_ARM64) +#include +#define DRMP3_HAVE_SSE 0 +#define DRMP3_HAVE_SIMD 1 +#define DRMP3_VSTORE vst1q_f32 +#define DRMP3_VLD vld1q_f32 +#define DRMP3_VSET vmovq_n_f32 +#define DRMP3_VADD vaddq_f32 +#define DRMP3_VSUB vsubq_f32 +#define DRMP3_VMUL vmulq_f32 +#define DRMP3_VMAC(a, x, y) vmlaq_f32(a, x, y) +#define DRMP3_VMSB(a, x, y) vmlsq_f32(a, x, y) +#define DRMP3_VMUL_S(x, s) vmulq_f32(x, vmovq_n_f32(s)) +#define DRMP3_VREV(x) vcombine_f32(vget_high_f32(vrev64q_f32(x)), vget_low_f32(vrev64q_f32(x))) +typedef float32x4_t drmp3_f4; +static int drmp3_have_simd(void) +{ /* TODO: detect neon for !DR_MP3_ONLY_SIMD */ + return 1; +} +#else +#define DRMP3_HAVE_SSE 0 +#define DRMP3_HAVE_SIMD 0 +#ifdef DR_MP3_ONLY_SIMD +#error DR_MP3_ONLY_SIMD used, but SSE/NEON not enabled +#endif +#endif + +#else + +#define DRMP3_HAVE_SIMD 0 + +#endif + +#if defined(__ARM_ARCH) && (__ARM_ARCH >= 6) && !defined(__aarch64__) && !defined(_M_ARM64) +#define DRMP3_HAVE_ARMV6 1 +static __inline__ __attribute__((always_inline)) drmp3_int32 drmp3_clip_int16_arm(drmp3_int32 a) +{ + drmp3_int32 x = 0; + __asm__ ("ssat %0, #16, %1" : "=r"(x) : "r"(a)); + return x; +} +#else +#define DRMP3_HAVE_ARMV6 0 +#endif + + +/* Standard library stuff. */ +#ifndef DRMP3_ASSERT +#include +#define DRMP3_ASSERT(expression) assert(expression) +#endif +#ifndef DRMP3_COPY_MEMORY +#define DRMP3_COPY_MEMORY(dst, src, sz) memcpy((dst), (src), (sz)) +#endif +#ifndef DRMP3_MOVE_MEMORY +#define DRMP3_MOVE_MEMORY(dst, src, sz) memmove((dst), (src), (sz)) +#endif +#ifndef DRMP3_ZERO_MEMORY +#define DRMP3_ZERO_MEMORY(p, sz) memset((p), 0, (sz)) +#endif +#define DRMP3_ZERO_OBJECT(p) DRMP3_ZERO_MEMORY((p), sizeof(*(p))) +#ifndef DRMP3_MALLOC +#define DRMP3_MALLOC(sz) malloc((sz)) +#endif +#ifndef DRMP3_REALLOC +#define DRMP3_REALLOC(p, sz) realloc((p), (sz)) +#endif +#ifndef DRMP3_FREE +#define DRMP3_FREE(p) free((p)) +#endif + +typedef struct +{ + const drmp3_uint8 *buf; + int pos, limit; +} drmp3_bs; + +typedef struct +{ + float scf[3*64]; + drmp3_uint8 total_bands, stereo_bands, bitalloc[64], scfcod[64]; +} drmp3_L12_scale_info; + +typedef struct +{ + drmp3_uint8 tab_offset, code_tab_width, band_count; +} drmp3_L12_subband_alloc; + +typedef struct +{ + const drmp3_uint8 *sfbtab; + drmp3_uint16 part_23_length, big_values, scalefac_compress; + drmp3_uint8 global_gain, block_type, mixed_block_flag, n_long_sfb, n_short_sfb; + drmp3_uint8 table_select[3], region_count[3], subblock_gain[3]; + drmp3_uint8 preflag, scalefac_scale, count1_table, scfsi; +} drmp3_L3_gr_info; + +typedef struct +{ + drmp3_bs bs; + drmp3_uint8 maindata[DRMP3_MAX_BITRESERVOIR_BYTES + DRMP3_MAX_L3_FRAME_PAYLOAD_BYTES]; + drmp3_L3_gr_info gr_info[4]; + float grbuf[2][576], scf[40], syn[18 + 15][2*32]; + drmp3_uint8 ist_pos[2][39]; +} drmp3dec_scratch; + +static void drmp3_bs_init(drmp3_bs *bs, const drmp3_uint8 *data, int bytes) +{ + bs->buf = data; + bs->pos = 0; + bs->limit = bytes*8; +} + +static drmp3_uint32 drmp3_bs_get_bits(drmp3_bs *bs, int n) +{ + drmp3_uint32 next, cache = 0, s = bs->pos & 7; + int shl = n + s; + const drmp3_uint8 *p = bs->buf + (bs->pos >> 3); + if ((bs->pos += n) > bs->limit) + return 0; + next = *p++ & (255 >> s); + while ((shl -= 8) > 0) + { + cache |= next << shl; + next = *p++; + } + return cache | (next >> -shl); +} + +static int drmp3_hdr_valid(const drmp3_uint8 *h) +{ + return h[0] == 0xff && + ((h[1] & 0xF0) == 0xf0 || (h[1] & 0xFE) == 0xe2) && + (DRMP3_HDR_GET_LAYER(h) != 0) && + (DRMP3_HDR_GET_BITRATE(h) != 15) && + (DRMP3_HDR_GET_SAMPLE_RATE(h) != 3); +} + +static int drmp3_hdr_compare(const drmp3_uint8 *h1, const drmp3_uint8 *h2) +{ + return drmp3_hdr_valid(h2) && + ((h1[1] ^ h2[1]) & 0xFE) == 0 && + ((h1[2] ^ h2[2]) & 0x0C) == 0 && + !(DRMP3_HDR_IS_FREE_FORMAT(h1) ^ DRMP3_HDR_IS_FREE_FORMAT(h2)); +} + +static unsigned drmp3_hdr_bitrate_kbps(const drmp3_uint8 *h) +{ + static const drmp3_uint8 halfrate[2][3][15] = { + { { 0,4,8,12,16,20,24,28,32,40,48,56,64,72,80 }, { 0,4,8,12,16,20,24,28,32,40,48,56,64,72,80 }, { 0,16,24,28,32,40,48,56,64,72,80,88,96,112,128 } }, + { { 0,16,20,24,28,32,40,48,56,64,80,96,112,128,160 }, { 0,16,24,28,32,40,48,56,64,80,96,112,128,160,192 }, { 0,16,32,48,64,80,96,112,128,144,160,176,192,208,224 } }, + }; + return 2*halfrate[!!DRMP3_HDR_TEST_MPEG1(h)][DRMP3_HDR_GET_LAYER(h) - 1][DRMP3_HDR_GET_BITRATE(h)]; +} + +static unsigned drmp3_hdr_sample_rate_hz(const drmp3_uint8 *h) +{ + static const unsigned g_hz[3] = { 44100, 48000, 32000 }; + return g_hz[DRMP3_HDR_GET_SAMPLE_RATE(h)] >> (int)!DRMP3_HDR_TEST_MPEG1(h) >> (int)!DRMP3_HDR_TEST_NOT_MPEG25(h); +} + +static unsigned drmp3_hdr_frame_samples(const drmp3_uint8 *h) +{ + return DRMP3_HDR_IS_LAYER_1(h) ? 384 : (1152 >> (int)DRMP3_HDR_IS_FRAME_576(h)); +} + +static int drmp3_hdr_frame_bytes(const drmp3_uint8 *h, int free_format_size) +{ + int frame_bytes = drmp3_hdr_frame_samples(h)*drmp3_hdr_bitrate_kbps(h)*125/drmp3_hdr_sample_rate_hz(h); + if (DRMP3_HDR_IS_LAYER_1(h)) + { + frame_bytes &= ~3; /* slot align */ + } + return frame_bytes ? frame_bytes : free_format_size; +} + +static int drmp3_hdr_padding(const drmp3_uint8 *h) +{ + return DRMP3_HDR_TEST_PADDING(h) ? (DRMP3_HDR_IS_LAYER_1(h) ? 4 : 1) : 0; +} + +#ifndef DR_MP3_ONLY_MP3 +static const drmp3_L12_subband_alloc *drmp3_L12_subband_alloc_table(const drmp3_uint8 *hdr, drmp3_L12_scale_info *sci) +{ + const drmp3_L12_subband_alloc *alloc; + int mode = DRMP3_HDR_GET_STEREO_MODE(hdr); + int nbands, stereo_bands = (mode == DRMP3_MODE_MONO) ? 0 : (mode == DRMP3_MODE_JOINT_STEREO) ? (DRMP3_HDR_GET_STEREO_MODE_EXT(hdr) << 2) + 4 : 32; + + if (DRMP3_HDR_IS_LAYER_1(hdr)) + { + static const drmp3_L12_subband_alloc g_alloc_L1[] = { { 76, 4, 32 } }; + alloc = g_alloc_L1; + nbands = 32; + } else if (!DRMP3_HDR_TEST_MPEG1(hdr)) + { + static const drmp3_L12_subband_alloc g_alloc_L2M2[] = { { 60, 4, 4 }, { 44, 3, 7 }, { 44, 2, 19 } }; + alloc = g_alloc_L2M2; + nbands = 30; + } else + { + static const drmp3_L12_subband_alloc g_alloc_L2M1[] = { { 0, 4, 3 }, { 16, 4, 8 }, { 32, 3, 12 }, { 40, 2, 7 } }; + int sample_rate_idx = DRMP3_HDR_GET_SAMPLE_RATE(hdr); + unsigned kbps = drmp3_hdr_bitrate_kbps(hdr) >> (int)(mode != DRMP3_MODE_MONO); + if (!kbps) /* free-format */ + { + kbps = 192; + } + + alloc = g_alloc_L2M1; + nbands = 27; + if (kbps < 56) + { + static const drmp3_L12_subband_alloc g_alloc_L2M1_lowrate[] = { { 44, 4, 2 }, { 44, 3, 10 } }; + alloc = g_alloc_L2M1_lowrate; + nbands = sample_rate_idx == 2 ? 12 : 8; + } else if (kbps >= 96 && sample_rate_idx != 1) + { + nbands = 30; + } + } + + sci->total_bands = (drmp3_uint8)nbands; + sci->stereo_bands = (drmp3_uint8)DRMP3_MIN(stereo_bands, nbands); + + return alloc; +} + +static void drmp3_L12_read_scalefactors(drmp3_bs *bs, drmp3_uint8 *pba, drmp3_uint8 *scfcod, int bands, float *scf) +{ + static const float g_deq_L12[18*3] = { +#define DRMP3_DQ(x) 9.53674316e-07f/x, 7.56931807e-07f/x, 6.00777173e-07f/x + DRMP3_DQ(3),DRMP3_DQ(7),DRMP3_DQ(15),DRMP3_DQ(31),DRMP3_DQ(63),DRMP3_DQ(127),DRMP3_DQ(255),DRMP3_DQ(511),DRMP3_DQ(1023),DRMP3_DQ(2047),DRMP3_DQ(4095),DRMP3_DQ(8191),DRMP3_DQ(16383),DRMP3_DQ(32767),DRMP3_DQ(65535),DRMP3_DQ(3),DRMP3_DQ(5),DRMP3_DQ(9) + }; + int i, m; + for (i = 0; i < bands; i++) + { + float s = 0; + int ba = *pba++; + int mask = ba ? 4 + ((19 >> scfcod[i]) & 3) : 0; + for (m = 4; m; m >>= 1) + { + if (mask & m) + { + int b = drmp3_bs_get_bits(bs, 6); + s = g_deq_L12[ba*3 - 6 + b % 3]*(int)(1 << 21 >> b/3); + } + *scf++ = s; + } + } +} + +static void drmp3_L12_read_scale_info(const drmp3_uint8 *hdr, drmp3_bs *bs, drmp3_L12_scale_info *sci) +{ + static const drmp3_uint8 g_bitalloc_code_tab[] = { + 0,17, 3, 4, 5,6,7, 8,9,10,11,12,13,14,15,16, + 0,17,18, 3,19,4,5, 6,7, 8, 9,10,11,12,13,16, + 0,17,18, 3,19,4,5,16, + 0,17,18,16, + 0,17,18,19, 4,5,6, 7,8, 9,10,11,12,13,14,15, + 0,17,18, 3,19,4,5, 6,7, 8, 9,10,11,12,13,14, + 0, 2, 3, 4, 5,6,7, 8,9,10,11,12,13,14,15,16 + }; + const drmp3_L12_subband_alloc *subband_alloc = drmp3_L12_subband_alloc_table(hdr, sci); + + int i, k = 0, ba_bits = 0; + const drmp3_uint8 *ba_code_tab = g_bitalloc_code_tab; + + for (i = 0; i < sci->total_bands; i++) + { + drmp3_uint8 ba; + if (i == k) + { + k += subband_alloc->band_count; + ba_bits = subband_alloc->code_tab_width; + ba_code_tab = g_bitalloc_code_tab + subband_alloc->tab_offset; + subband_alloc++; + } + ba = ba_code_tab[drmp3_bs_get_bits(bs, ba_bits)]; + sci->bitalloc[2*i] = ba; + if (i < sci->stereo_bands) + { + ba = ba_code_tab[drmp3_bs_get_bits(bs, ba_bits)]; + } + sci->bitalloc[2*i + 1] = sci->stereo_bands ? ba : 0; + } + + for (i = 0; i < 2*sci->total_bands; i++) + { + sci->scfcod[i] = (drmp3_uint8)(sci->bitalloc[i] ? DRMP3_HDR_IS_LAYER_1(hdr) ? 2 : drmp3_bs_get_bits(bs, 2) : 6); + } + + drmp3_L12_read_scalefactors(bs, sci->bitalloc, sci->scfcod, sci->total_bands*2, sci->scf); + + for (i = sci->stereo_bands; i < sci->total_bands; i++) + { + sci->bitalloc[2*i + 1] = 0; + } +} + +static int drmp3_L12_dequantize_granule(float *grbuf, drmp3_bs *bs, drmp3_L12_scale_info *sci, int group_size) +{ + int i, j, k, choff = 576; + for (j = 0; j < 4; j++) + { + float *dst = grbuf + group_size*j; + for (i = 0; i < 2*sci->total_bands; i++) + { + int ba = sci->bitalloc[i]; + if (ba != 0) + { + if (ba < 17) + { + int half = (1 << (ba - 1)) - 1; + for (k = 0; k < group_size; k++) + { + dst[k] = (float)((int)drmp3_bs_get_bits(bs, ba) - half); + } + } else + { + unsigned mod = (2 << (ba - 17)) + 1; /* 3, 5, 9 */ + unsigned code = drmp3_bs_get_bits(bs, mod + 2 - (mod >> 3)); /* 5, 7, 10 */ + for (k = 0; k < group_size; k++, code /= mod) + { + dst[k] = (float)((int)(code % mod - mod/2)); + } + } + } + dst += choff; + choff = 18 - choff; + } + } + return group_size*4; +} + +static void drmp3_L12_apply_scf_384(drmp3_L12_scale_info *sci, const float *scf, float *dst) +{ + int i, k; + DRMP3_COPY_MEMORY(dst + 576 + sci->stereo_bands*18, dst + sci->stereo_bands*18, (sci->total_bands - sci->stereo_bands)*18*sizeof(float)); + for (i = 0; i < sci->total_bands; i++, dst += 18, scf += 6) + { + for (k = 0; k < 12; k++) + { + dst[k + 0] *= scf[0]; + dst[k + 576] *= scf[3]; + } + } +} +#endif + +static int drmp3_L3_read_side_info(drmp3_bs *bs, drmp3_L3_gr_info *gr, const drmp3_uint8 *hdr) +{ + static const drmp3_uint8 g_scf_long[8][23] = { + { 6,6,6,6,6,6,8,10,12,14,16,20,24,28,32,38,46,52,60,68,58,54,0 }, + { 12,12,12,12,12,12,16,20,24,28,32,40,48,56,64,76,90,2,2,2,2,2,0 }, + { 6,6,6,6,6,6,8,10,12,14,16,20,24,28,32,38,46,52,60,68,58,54,0 }, + { 6,6,6,6,6,6,8,10,12,14,16,18,22,26,32,38,46,54,62,70,76,36,0 }, + { 6,6,6,6,6,6,8,10,12,14,16,20,24,28,32,38,46,52,60,68,58,54,0 }, + { 4,4,4,4,4,4,6,6,8,8,10,12,16,20,24,28,34,42,50,54,76,158,0 }, + { 4,4,4,4,4,4,6,6,6,8,10,12,16,18,22,28,34,40,46,54,54,192,0 }, + { 4,4,4,4,4,4,6,6,8,10,12,16,20,24,30,38,46,56,68,84,102,26,0 } + }; + static const drmp3_uint8 g_scf_short[8][40] = { + { 4,4,4,4,4,4,4,4,4,6,6,6,8,8,8,10,10,10,12,12,12,14,14,14,18,18,18,24,24,24,30,30,30,40,40,40,18,18,18,0 }, + { 8,8,8,8,8,8,8,8,8,12,12,12,16,16,16,20,20,20,24,24,24,28,28,28,36,36,36,2,2,2,2,2,2,2,2,2,26,26,26,0 }, + { 4,4,4,4,4,4,4,4,4,6,6,6,6,6,6,8,8,8,10,10,10,14,14,14,18,18,18,26,26,26,32,32,32,42,42,42,18,18,18,0 }, + { 4,4,4,4,4,4,4,4,4,6,6,6,8,8,8,10,10,10,12,12,12,14,14,14,18,18,18,24,24,24,32,32,32,44,44,44,12,12,12,0 }, + { 4,4,4,4,4,4,4,4,4,6,6,6,8,8,8,10,10,10,12,12,12,14,14,14,18,18,18,24,24,24,30,30,30,40,40,40,18,18,18,0 }, + { 4,4,4,4,4,4,4,4,4,4,4,4,6,6,6,8,8,8,10,10,10,12,12,12,14,14,14,18,18,18,22,22,22,30,30,30,56,56,56,0 }, + { 4,4,4,4,4,4,4,4,4,4,4,4,6,6,6,6,6,6,10,10,10,12,12,12,14,14,14,16,16,16,20,20,20,26,26,26,66,66,66,0 }, + { 4,4,4,4,4,4,4,4,4,4,4,4,6,6,6,8,8,8,12,12,12,16,16,16,20,20,20,26,26,26,34,34,34,42,42,42,12,12,12,0 } + }; + static const drmp3_uint8 g_scf_mixed[8][40] = { + { 6,6,6,6,6,6,6,6,6,8,8,8,10,10,10,12,12,12,14,14,14,18,18,18,24,24,24,30,30,30,40,40,40,18,18,18,0 }, + { 12,12,12,4,4,4,8,8,8,12,12,12,16,16,16,20,20,20,24,24,24,28,28,28,36,36,36,2,2,2,2,2,2,2,2,2,26,26,26,0 }, + { 6,6,6,6,6,6,6,6,6,6,6,6,8,8,8,10,10,10,14,14,14,18,18,18,26,26,26,32,32,32,42,42,42,18,18,18,0 }, + { 6,6,6,6,6,6,6,6,6,8,8,8,10,10,10,12,12,12,14,14,14,18,18,18,24,24,24,32,32,32,44,44,44,12,12,12,0 }, + { 6,6,6,6,6,6,6,6,6,8,8,8,10,10,10,12,12,12,14,14,14,18,18,18,24,24,24,30,30,30,40,40,40,18,18,18,0 }, + { 4,4,4,4,4,4,6,6,4,4,4,6,6,6,8,8,8,10,10,10,12,12,12,14,14,14,18,18,18,22,22,22,30,30,30,56,56,56,0 }, + { 4,4,4,4,4,4,6,6,4,4,4,6,6,6,6,6,6,10,10,10,12,12,12,14,14,14,16,16,16,20,20,20,26,26,26,66,66,66,0 }, + { 4,4,4,4,4,4,6,6,4,4,4,6,6,6,8,8,8,12,12,12,16,16,16,20,20,20,26,26,26,34,34,34,42,42,42,12,12,12,0 } + }; + + unsigned tables, scfsi = 0; + int main_data_begin, part_23_sum = 0; + int gr_count = DRMP3_HDR_IS_MONO(hdr) ? 1 : 2; + int sr_idx = DRMP3_HDR_GET_MY_SAMPLE_RATE(hdr); sr_idx -= (sr_idx != 0); + + if (DRMP3_HDR_TEST_MPEG1(hdr)) + { + gr_count *= 2; + main_data_begin = drmp3_bs_get_bits(bs, 9); + scfsi = drmp3_bs_get_bits(bs, 7 + gr_count); + } else + { + main_data_begin = drmp3_bs_get_bits(bs, 8 + gr_count) >> gr_count; + } + + do + { + if (DRMP3_HDR_IS_MONO(hdr)) + { + scfsi <<= 4; + } + gr->part_23_length = (drmp3_uint16)drmp3_bs_get_bits(bs, 12); + part_23_sum += gr->part_23_length; + gr->big_values = (drmp3_uint16)drmp3_bs_get_bits(bs, 9); + if (gr->big_values > 288) + { + return -1; + } + gr->global_gain = (drmp3_uint8)drmp3_bs_get_bits(bs, 8); + gr->scalefac_compress = (drmp3_uint16)drmp3_bs_get_bits(bs, DRMP3_HDR_TEST_MPEG1(hdr) ? 4 : 9); + gr->sfbtab = g_scf_long[sr_idx]; + gr->n_long_sfb = 22; + gr->n_short_sfb = 0; + if (drmp3_bs_get_bits(bs, 1)) + { + gr->block_type = (drmp3_uint8)drmp3_bs_get_bits(bs, 2); + if (!gr->block_type) + { + return -1; + } + gr->mixed_block_flag = (drmp3_uint8)drmp3_bs_get_bits(bs, 1); + gr->region_count[0] = 7; + gr->region_count[1] = 255; + if (gr->block_type == DRMP3_SHORT_BLOCK_TYPE) + { + scfsi &= 0x0F0F; + if (!gr->mixed_block_flag) + { + gr->region_count[0] = 8; + gr->sfbtab = g_scf_short[sr_idx]; + gr->n_long_sfb = 0; + gr->n_short_sfb = 39; + } else + { + gr->sfbtab = g_scf_mixed[sr_idx]; + gr->n_long_sfb = DRMP3_HDR_TEST_MPEG1(hdr) ? 8 : 6; + gr->n_short_sfb = 30; + } + } + tables = drmp3_bs_get_bits(bs, 10); + tables <<= 5; + gr->subblock_gain[0] = (drmp3_uint8)drmp3_bs_get_bits(bs, 3); + gr->subblock_gain[1] = (drmp3_uint8)drmp3_bs_get_bits(bs, 3); + gr->subblock_gain[2] = (drmp3_uint8)drmp3_bs_get_bits(bs, 3); + } else + { + gr->block_type = 0; + gr->mixed_block_flag = 0; + tables = drmp3_bs_get_bits(bs, 15); + gr->region_count[0] = (drmp3_uint8)drmp3_bs_get_bits(bs, 4); + gr->region_count[1] = (drmp3_uint8)drmp3_bs_get_bits(bs, 3); + gr->region_count[2] = 255; + } + gr->table_select[0] = (drmp3_uint8)(tables >> 10); + gr->table_select[1] = (drmp3_uint8)((tables >> 5) & 31); + gr->table_select[2] = (drmp3_uint8)((tables) & 31); + gr->preflag = (drmp3_uint8)(DRMP3_HDR_TEST_MPEG1(hdr) ? drmp3_bs_get_bits(bs, 1) : (gr->scalefac_compress >= 500)); + gr->scalefac_scale = (drmp3_uint8)drmp3_bs_get_bits(bs, 1); + gr->count1_table = (drmp3_uint8)drmp3_bs_get_bits(bs, 1); + gr->scfsi = (drmp3_uint8)((scfsi >> 12) & 15); + scfsi <<= 4; + gr++; + } while(--gr_count); + + if (part_23_sum + bs->pos > bs->limit + main_data_begin*8) + { + return -1; + } + + return main_data_begin; +} + +static void drmp3_L3_read_scalefactors(drmp3_uint8 *scf, drmp3_uint8 *ist_pos, const drmp3_uint8 *scf_size, const drmp3_uint8 *scf_count, drmp3_bs *bitbuf, int scfsi) +{ + int i, k; + for (i = 0; i < 4 && scf_count[i]; i++, scfsi *= 2) + { + int cnt = scf_count[i]; + if (scfsi & 8) + { + DRMP3_COPY_MEMORY(scf, ist_pos, cnt); + } else + { + int bits = scf_size[i]; + if (!bits) + { + DRMP3_ZERO_MEMORY(scf, cnt); + DRMP3_ZERO_MEMORY(ist_pos, cnt); + } else + { + int max_scf = (scfsi < 0) ? (1 << bits) - 1 : -1; + for (k = 0; k < cnt; k++) + { + int s = drmp3_bs_get_bits(bitbuf, bits); + ist_pos[k] = (drmp3_uint8)(s == max_scf ? -1 : s); + scf[k] = (drmp3_uint8)s; + } + } + } + ist_pos += cnt; + scf += cnt; + } + scf[0] = scf[1] = scf[2] = 0; +} + +static float drmp3_L3_ldexp_q2(float y, int exp_q2) +{ + static const float g_expfrac[4] = { 9.31322575e-10f,7.83145814e-10f,6.58544508e-10f,5.53767716e-10f }; + int e; + do + { + e = DRMP3_MIN(30*4, exp_q2); + y *= g_expfrac[e & 3]*(1 << 30 >> (e >> 2)); + } while ((exp_q2 -= e) > 0); + return y; +} + +static void drmp3_L3_decode_scalefactors(const drmp3_uint8 *hdr, drmp3_uint8 *ist_pos, drmp3_bs *bs, const drmp3_L3_gr_info *gr, float *scf, int ch) +{ + static const drmp3_uint8 g_scf_partitions[3][28] = { + { 6,5,5, 5,6,5,5,5,6,5, 7,3,11,10,0,0, 7, 7, 7,0, 6, 6,6,3, 8, 8,5,0 }, + { 8,9,6,12,6,9,9,9,6,9,12,6,15,18,0,0, 6,15,12,0, 6,12,9,6, 6,18,9,0 }, + { 9,9,6,12,9,9,9,9,9,9,12,6,18,18,0,0,12,12,12,0,12, 9,9,6,15,12,9,0 } + }; + const drmp3_uint8 *scf_partition = g_scf_partitions[!!gr->n_short_sfb + !gr->n_long_sfb]; + drmp3_uint8 scf_size[4], iscf[40]; + int i, scf_shift = gr->scalefac_scale + 1, gain_exp, scfsi = gr->scfsi; + float gain; + + if (DRMP3_HDR_TEST_MPEG1(hdr)) + { + static const drmp3_uint8 g_scfc_decode[16] = { 0,1,2,3, 12,5,6,7, 9,10,11,13, 14,15,18,19 }; + int part = g_scfc_decode[gr->scalefac_compress]; + scf_size[1] = scf_size[0] = (drmp3_uint8)(part >> 2); + scf_size[3] = scf_size[2] = (drmp3_uint8)(part & 3); + } else + { + static const drmp3_uint8 g_mod[6*4] = { 5,5,4,4,5,5,4,1,4,3,1,1,5,6,6,1,4,4,4,1,4,3,1,1 }; + int k, modprod, sfc, ist = DRMP3_HDR_TEST_I_STEREO(hdr) && ch; + sfc = gr->scalefac_compress >> ist; + for (k = ist*3*4; sfc >= 0; sfc -= modprod, k += 4) + { + for (modprod = 1, i = 3; i >= 0; i--) + { + scf_size[i] = (drmp3_uint8)(sfc / modprod % g_mod[k + i]); + modprod *= g_mod[k + i]; + } + } + scf_partition += k; + scfsi = -16; + } + drmp3_L3_read_scalefactors(iscf, ist_pos, scf_size, scf_partition, bs, scfsi); + + if (gr->n_short_sfb) + { + int sh = 3 - scf_shift; + for (i = 0; i < gr->n_short_sfb; i += 3) + { + iscf[gr->n_long_sfb + i + 0] = (drmp3_uint8)(iscf[gr->n_long_sfb + i + 0] + (gr->subblock_gain[0] << sh)); + iscf[gr->n_long_sfb + i + 1] = (drmp3_uint8)(iscf[gr->n_long_sfb + i + 1] + (gr->subblock_gain[1] << sh)); + iscf[gr->n_long_sfb + i + 2] = (drmp3_uint8)(iscf[gr->n_long_sfb + i + 2] + (gr->subblock_gain[2] << sh)); + } + } else if (gr->preflag) + { + static const drmp3_uint8 g_preamp[10] = { 1,1,1,1,2,2,3,3,3,2 }; + for (i = 0; i < 10; i++) + { + iscf[11 + i] = (drmp3_uint8)(iscf[11 + i] + g_preamp[i]); + } + } + + gain_exp = gr->global_gain + DRMP3_BITS_DEQUANTIZER_OUT*4 - 210 - (DRMP3_HDR_IS_MS_STEREO(hdr) ? 2 : 0); + gain = drmp3_L3_ldexp_q2(1 << (DRMP3_MAX_SCFI/4), DRMP3_MAX_SCFI - gain_exp); + for (i = 0; i < (int)(gr->n_long_sfb + gr->n_short_sfb); i++) + { + scf[i] = drmp3_L3_ldexp_q2(gain, iscf[i] << scf_shift); + } +} + +static const float g_drmp3_pow43[129 + 16] = { + 0,-1,-2.519842f,-4.326749f,-6.349604f,-8.549880f,-10.902724f,-13.390518f,-16.000000f,-18.720754f,-21.544347f,-24.463781f,-27.473142f,-30.567351f,-33.741992f,-36.993181f, + 0,1,2.519842f,4.326749f,6.349604f,8.549880f,10.902724f,13.390518f,16.000000f,18.720754f,21.544347f,24.463781f,27.473142f,30.567351f,33.741992f,36.993181f,40.317474f,43.711787f,47.173345f,50.699631f,54.288352f,57.937408f,61.644865f,65.408941f,69.227979f,73.100443f,77.024898f,81.000000f,85.024491f,89.097188f,93.216975f,97.382800f,101.593667f,105.848633f,110.146801f,114.487321f,118.869381f,123.292209f,127.755065f,132.257246f,136.798076f,141.376907f,145.993119f,150.646117f,155.335327f,160.060199f,164.820202f,169.614826f,174.443577f,179.305980f,184.201575f,189.129918f,194.090580f,199.083145f,204.107210f,209.162385f,214.248292f,219.364564f,224.510845f,229.686789f,234.892058f,240.126328f,245.389280f,250.680604f,256.000000f,261.347174f,266.721841f,272.123723f,277.552547f,283.008049f,288.489971f,293.998060f,299.532071f,305.091761f,310.676898f,316.287249f,321.922592f,327.582707f,333.267377f,338.976394f,344.709550f,350.466646f,356.247482f,362.051866f,367.879608f,373.730522f,379.604427f,385.501143f,391.420496f,397.362314f,403.326427f,409.312672f,415.320884f,421.350905f,427.402579f,433.475750f,439.570269f,445.685987f,451.822757f,457.980436f,464.158883f,470.357960f,476.577530f,482.817459f,489.077615f,495.357868f,501.658090f,507.978156f,514.317941f,520.677324f,527.056184f,533.454404f,539.871867f,546.308458f,552.764065f,559.238575f,565.731879f,572.243870f,578.774440f,585.323483f,591.890898f,598.476581f,605.080431f,611.702349f,618.342238f,625.000000f,631.675540f,638.368763f,645.079578f +}; + +static float drmp3_L3_pow_43(int x) +{ + float frac; + int sign, mult = 256; + + if (x < 129) + { + return g_drmp3_pow43[16 + x]; + } + + if (x < 1024) + { + mult = 16; + x <<= 3; + } + + sign = 2*x & 64; + frac = (float)((x & 63) - sign) / ((x & ~63) + sign); + return g_drmp3_pow43[16 + ((x + sign) >> 6)]*(1.f + frac*((4.f/3) + frac*(2.f/9)))*mult; +} + +static void drmp3_L3_huffman(float *dst, drmp3_bs *bs, const drmp3_L3_gr_info *gr_info, const float *scf, int layer3gr_limit) +{ + static const drmp3_int16 tabs[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 785,785,785,785,784,784,784,784,513,513,513,513,513,513,513,513,256,256,256,256,256,256,256,256,256,256,256,256,256,256,256,256, + -255,1313,1298,1282,785,785,785,785,784,784,784,784,769,769,769,769,256,256,256,256,256,256,256,256,256,256,256,256,256,256,256,256,290,288, + -255,1313,1298,1282,769,769,769,769,529,529,529,529,529,529,529,529,528,528,528,528,528,528,528,528,512,512,512,512,512,512,512,512,290,288, + -253,-318,-351,-367,785,785,785,785,784,784,784,784,769,769,769,769,256,256,256,256,256,256,256,256,256,256,256,256,256,256,256,256,819,818,547,547,275,275,275,275,561,560,515,546,289,274,288,258, + -254,-287,1329,1299,1314,1312,1057,1057,1042,1042,1026,1026,784,784,784,784,529,529,529,529,529,529,529,529,769,769,769,769,768,768,768,768,563,560,306,306,291,259, + -252,-413,-477,-542,1298,-575,1041,1041,784,784,784,784,769,769,769,769,256,256,256,256,256,256,256,256,256,256,256,256,256,256,256,256,-383,-399,1107,1092,1106,1061,849,849,789,789,1104,1091,773,773,1076,1075,341,340,325,309,834,804,577,577,532,532,516,516,832,818,803,816,561,561,531,531,515,546,289,289,288,258, + -252,-429,-493,-559,1057,1057,1042,1042,529,529,529,529,529,529,529,529,784,784,784,784,769,769,769,769,512,512,512,512,512,512,512,512,-382,1077,-415,1106,1061,1104,849,849,789,789,1091,1076,1029,1075,834,834,597,581,340,340,339,324,804,833,532,532,832,772,818,803,817,787,816,771,290,290,290,290,288,258, + -253,-349,-414,-447,-463,1329,1299,-479,1314,1312,1057,1057,1042,1042,1026,1026,785,785,785,785,784,784,784,784,769,769,769,769,768,768,768,768,-319,851,821,-335,836,850,805,849,341,340,325,336,533,533,579,579,564,564,773,832,578,548,563,516,321,276,306,291,304,259, + -251,-572,-733,-830,-863,-879,1041,1041,784,784,784,784,769,769,769,769,256,256,256,256,256,256,256,256,256,256,256,256,256,256,256,256,-511,-527,-543,1396,1351,1381,1366,1395,1335,1380,-559,1334,1138,1138,1063,1063,1350,1392,1031,1031,1062,1062,1364,1363,1120,1120,1333,1348,881,881,881,881,375,374,359,373,343,358,341,325,791,791,1123,1122,-703,1105,1045,-719,865,865,790,790,774,774,1104,1029,338,293,323,308,-799,-815,833,788,772,818,803,816,322,292,307,320,561,531,515,546,289,274,288,258, + -251,-525,-605,-685,-765,-831,-846,1298,1057,1057,1312,1282,785,785,785,785,784,784,784,784,769,769,769,769,512,512,512,512,512,512,512,512,1399,1398,1383,1367,1382,1396,1351,-511,1381,1366,1139,1139,1079,1079,1124,1124,1364,1349,1363,1333,882,882,882,882,807,807,807,807,1094,1094,1136,1136,373,341,535,535,881,775,867,822,774,-591,324,338,-671,849,550,550,866,864,609,609,293,336,534,534,789,835,773,-751,834,804,308,307,833,788,832,772,562,562,547,547,305,275,560,515,290,290, + -252,-397,-477,-557,-622,-653,-719,-735,-750,1329,1299,1314,1057,1057,1042,1042,1312,1282,1024,1024,785,785,785,785,784,784,784,784,769,769,769,769,-383,1127,1141,1111,1126,1140,1095,1110,869,869,883,883,1079,1109,882,882,375,374,807,868,838,881,791,-463,867,822,368,263,852,837,836,-543,610,610,550,550,352,336,534,534,865,774,851,821,850,805,593,533,579,564,773,832,578,578,548,548,577,577,307,276,306,291,516,560,259,259, + -250,-2107,-2507,-2764,-2909,-2974,-3007,-3023,1041,1041,1040,1040,769,769,769,769,256,256,256,256,256,256,256,256,256,256,256,256,256,256,256,256,-767,-1052,-1213,-1277,-1358,-1405,-1469,-1535,-1550,-1582,-1614,-1647,-1662,-1694,-1726,-1759,-1774,-1807,-1822,-1854,-1886,1565,-1919,-1935,-1951,-1967,1731,1730,1580,1717,-1983,1729,1564,-1999,1548,-2015,-2031,1715,1595,-2047,1714,-2063,1610,-2079,1609,-2095,1323,1323,1457,1457,1307,1307,1712,1547,1641,1700,1699,1594,1685,1625,1442,1442,1322,1322,-780,-973,-910,1279,1278,1277,1262,1276,1261,1275,1215,1260,1229,-959,974,974,989,989,-943,735,478,478,495,463,506,414,-1039,1003,958,1017,927,942,987,957,431,476,1272,1167,1228,-1183,1256,-1199,895,895,941,941,1242,1227,1212,1135,1014,1014,490,489,503,487,910,1013,985,925,863,894,970,955,1012,847,-1343,831,755,755,984,909,428,366,754,559,-1391,752,486,457,924,997,698,698,983,893,740,740,908,877,739,739,667,667,953,938,497,287,271,271,683,606,590,712,726,574,302,302,738,736,481,286,526,725,605,711,636,724,696,651,589,681,666,710,364,467,573,695,466,466,301,465,379,379,709,604,665,679,316,316,634,633,436,436,464,269,424,394,452,332,438,363,347,408,393,448,331,422,362,407,392,421,346,406,391,376,375,359,1441,1306,-2367,1290,-2383,1337,-2399,-2415,1426,1321,-2431,1411,1336,-2447,-2463,-2479,1169,1169,1049,1049,1424,1289,1412,1352,1319,-2495,1154,1154,1064,1064,1153,1153,416,390,360,404,403,389,344,374,373,343,358,372,327,357,342,311,356,326,1395,1394,1137,1137,1047,1047,1365,1392,1287,1379,1334,1364,1349,1378,1318,1363,792,792,792,792,1152,1152,1032,1032,1121,1121,1046,1046,1120,1120,1030,1030,-2895,1106,1061,1104,849,849,789,789,1091,1076,1029,1090,1060,1075,833,833,309,324,532,532,832,772,818,803,561,561,531,560,515,546,289,274,288,258, + -250,-1179,-1579,-1836,-1996,-2124,-2253,-2333,-2413,-2477,-2542,-2574,-2607,-2622,-2655,1314,1313,1298,1312,1282,785,785,785,785,1040,1040,1025,1025,768,768,768,768,-766,-798,-830,-862,-895,-911,-927,-943,-959,-975,-991,-1007,-1023,-1039,-1055,-1070,1724,1647,-1103,-1119,1631,1767,1662,1738,1708,1723,-1135,1780,1615,1779,1599,1677,1646,1778,1583,-1151,1777,1567,1737,1692,1765,1722,1707,1630,1751,1661,1764,1614,1736,1676,1763,1750,1645,1598,1721,1691,1762,1706,1582,1761,1566,-1167,1749,1629,767,766,751,765,494,494,735,764,719,749,734,763,447,447,748,718,477,506,431,491,446,476,461,505,415,430,475,445,504,399,460,489,414,503,383,474,429,459,502,502,746,752,488,398,501,473,413,472,486,271,480,270,-1439,-1455,1357,-1471,-1487,-1503,1341,1325,-1519,1489,1463,1403,1309,-1535,1372,1448,1418,1476,1356,1462,1387,-1551,1475,1340,1447,1402,1386,-1567,1068,1068,1474,1461,455,380,468,440,395,425,410,454,364,467,466,464,453,269,409,448,268,432,1371,1473,1432,1417,1308,1460,1355,1446,1459,1431,1083,1083,1401,1416,1458,1445,1067,1067,1370,1457,1051,1051,1291,1430,1385,1444,1354,1415,1400,1443,1082,1082,1173,1113,1186,1066,1185,1050,-1967,1158,1128,1172,1097,1171,1081,-1983,1157,1112,416,266,375,400,1170,1142,1127,1065,793,793,1169,1033,1156,1096,1141,1111,1155,1080,1126,1140,898,898,808,808,897,897,792,792,1095,1152,1032,1125,1110,1139,1079,1124,882,807,838,881,853,791,-2319,867,368,263,822,852,837,866,806,865,-2399,851,352,262,534,534,821,836,594,594,549,549,593,593,533,533,848,773,579,579,564,578,548,563,276,276,577,576,306,291,516,560,305,305,275,259, + -251,-892,-2058,-2620,-2828,-2957,-3023,-3039,1041,1041,1040,1040,769,769,769,769,256,256,256,256,256,256,256,256,256,256,256,256,256,256,256,256,-511,-527,-543,-559,1530,-575,-591,1528,1527,1407,1526,1391,1023,1023,1023,1023,1525,1375,1268,1268,1103,1103,1087,1087,1039,1039,1523,-604,815,815,815,815,510,495,509,479,508,463,507,447,431,505,415,399,-734,-782,1262,-815,1259,1244,-831,1258,1228,-847,-863,1196,-879,1253,987,987,748,-767,493,493,462,477,414,414,686,669,478,446,461,445,474,429,487,458,412,471,1266,1264,1009,1009,799,799,-1019,-1276,-1452,-1581,-1677,-1757,-1821,-1886,-1933,-1997,1257,1257,1483,1468,1512,1422,1497,1406,1467,1496,1421,1510,1134,1134,1225,1225,1466,1451,1374,1405,1252,1252,1358,1480,1164,1164,1251,1251,1238,1238,1389,1465,-1407,1054,1101,-1423,1207,-1439,830,830,1248,1038,1237,1117,1223,1148,1236,1208,411,426,395,410,379,269,1193,1222,1132,1235,1221,1116,976,976,1192,1162,1177,1220,1131,1191,963,963,-1647,961,780,-1663,558,558,994,993,437,408,393,407,829,978,813,797,947,-1743,721,721,377,392,844,950,828,890,706,706,812,859,796,960,948,843,934,874,571,571,-1919,690,555,689,421,346,539,539,944,779,918,873,932,842,903,888,570,570,931,917,674,674,-2575,1562,-2591,1609,-2607,1654,1322,1322,1441,1441,1696,1546,1683,1593,1669,1624,1426,1426,1321,1321,1639,1680,1425,1425,1305,1305,1545,1668,1608,1623,1667,1592,1638,1666,1320,1320,1652,1607,1409,1409,1304,1304,1288,1288,1664,1637,1395,1395,1335,1335,1622,1636,1394,1394,1319,1319,1606,1621,1392,1392,1137,1137,1137,1137,345,390,360,375,404,373,1047,-2751,-2767,-2783,1062,1121,1046,-2799,1077,-2815,1106,1061,789,789,1105,1104,263,355,310,340,325,354,352,262,339,324,1091,1076,1029,1090,1060,1075,833,833,788,788,1088,1028,818,818,803,803,561,561,531,531,816,771,546,546,289,274,288,258, + -253,-317,-381,-446,-478,-509,1279,1279,-811,-1179,-1451,-1756,-1900,-2028,-2189,-2253,-2333,-2414,-2445,-2511,-2526,1313,1298,-2559,1041,1041,1040,1040,1025,1025,1024,1024,1022,1007,1021,991,1020,975,1019,959,687,687,1018,1017,671,671,655,655,1016,1015,639,639,758,758,623,623,757,607,756,591,755,575,754,559,543,543,1009,783,-575,-621,-685,-749,496,-590,750,749,734,748,974,989,1003,958,988,973,1002,942,987,957,972,1001,926,986,941,971,956,1000,910,985,925,999,894,970,-1071,-1087,-1102,1390,-1135,1436,1509,1451,1374,-1151,1405,1358,1480,1420,-1167,1507,1494,1389,1342,1465,1435,1450,1326,1505,1310,1493,1373,1479,1404,1492,1464,1419,428,443,472,397,736,526,464,464,486,457,442,471,484,482,1357,1449,1434,1478,1388,1491,1341,1490,1325,1489,1463,1403,1309,1477,1372,1448,1418,1433,1476,1356,1462,1387,-1439,1475,1340,1447,1402,1474,1324,1461,1371,1473,269,448,1432,1417,1308,1460,-1711,1459,-1727,1441,1099,1099,1446,1386,1431,1401,-1743,1289,1083,1083,1160,1160,1458,1445,1067,1067,1370,1457,1307,1430,1129,1129,1098,1098,268,432,267,416,266,400,-1887,1144,1187,1082,1173,1113,1186,1066,1050,1158,1128,1143,1172,1097,1171,1081,420,391,1157,1112,1170,1142,1127,1065,1169,1049,1156,1096,1141,1111,1155,1080,1126,1154,1064,1153,1140,1095,1048,-2159,1125,1110,1137,-2175,823,823,1139,1138,807,807,384,264,368,263,868,838,853,791,867,822,852,837,866,806,865,790,-2319,851,821,836,352,262,850,805,849,-2399,533,533,835,820,336,261,578,548,563,577,532,532,832,772,562,562,547,547,305,275,560,515,290,290,288,258 }; + static const drmp3_uint8 tab32[] = { 130,162,193,209,44,28,76,140,9,9,9,9,9,9,9,9,190,254,222,238,126,94,157,157,109,61,173,205}; + static const drmp3_uint8 tab33[] = { 252,236,220,204,188,172,156,140,124,108,92,76,60,44,28,12 }; + static const drmp3_int16 tabindex[2*16] = { 0,32,64,98,0,132,180,218,292,364,426,538,648,746,0,1126,1460,1460,1460,1460,1460,1460,1460,1460,1842,1842,1842,1842,1842,1842,1842,1842 }; + static const drmp3_uint8 g_linbits[] = { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,2,3,4,6,8,10,13,4,5,6,7,8,9,11,13 }; + +#define DRMP3_PEEK_BITS(n) (bs_cache >> (32 - (n))) +#define DRMP3_FLUSH_BITS(n) { bs_cache <<= (n); bs_sh += (n); } +#define DRMP3_CHECK_BITS while (bs_sh >= 0) { bs_cache |= (drmp3_uint32)*bs_next_ptr++ << bs_sh; bs_sh -= 8; } +#define DRMP3_BSPOS ((bs_next_ptr - bs->buf)*8 - 24 + bs_sh) + + float one = 0.0f; + int ireg = 0, big_val_cnt = gr_info->big_values; + const drmp3_uint8 *sfb = gr_info->sfbtab; + const drmp3_uint8 *bs_next_ptr = bs->buf + bs->pos/8; + drmp3_uint32 bs_cache = (((bs_next_ptr[0]*256u + bs_next_ptr[1])*256u + bs_next_ptr[2])*256u + bs_next_ptr[3]) << (bs->pos & 7); + int pairs_to_decode, np, bs_sh = (bs->pos & 7) - 8; + bs_next_ptr += 4; + + while (big_val_cnt > 0) + { + int tab_num = gr_info->table_select[ireg]; + int sfb_cnt = gr_info->region_count[ireg++]; + const drmp3_int16 *codebook = tabs + tabindex[tab_num]; + int linbits = g_linbits[tab_num]; + if (linbits) + { + do + { + np = *sfb++ / 2; + pairs_to_decode = DRMP3_MIN(big_val_cnt, np); + one = *scf++; + do + { + int j, w = 5; + int leaf = codebook[DRMP3_PEEK_BITS(w)]; + while (leaf < 0) + { + DRMP3_FLUSH_BITS(w); + w = leaf & 7; + leaf = codebook[DRMP3_PEEK_BITS(w) - (leaf >> 3)]; + } + DRMP3_FLUSH_BITS(leaf >> 8); + + for (j = 0; j < 2; j++, dst++, leaf >>= 4) + { + int lsb = leaf & 0x0F; + if (lsb == 15) + { + lsb += DRMP3_PEEK_BITS(linbits); + DRMP3_FLUSH_BITS(linbits); + DRMP3_CHECK_BITS; + *dst = one*drmp3_L3_pow_43(lsb)*((drmp3_int32)bs_cache < 0 ? -1: 1); + } else + { + *dst = g_drmp3_pow43[16 + lsb - 16*(bs_cache >> 31)]*one; + } + DRMP3_FLUSH_BITS(lsb ? 1 : 0); + } + DRMP3_CHECK_BITS; + } while (--pairs_to_decode); + } while ((big_val_cnt -= np) > 0 && --sfb_cnt >= 0); + } else + { + do + { + np = *sfb++ / 2; + pairs_to_decode = DRMP3_MIN(big_val_cnt, np); + one = *scf++; + do + { + int j, w = 5; + int leaf = codebook[DRMP3_PEEK_BITS(w)]; + while (leaf < 0) + { + DRMP3_FLUSH_BITS(w); + w = leaf & 7; + leaf = codebook[DRMP3_PEEK_BITS(w) - (leaf >> 3)]; + } + DRMP3_FLUSH_BITS(leaf >> 8); + + for (j = 0; j < 2; j++, dst++, leaf >>= 4) + { + int lsb = leaf & 0x0F; + *dst = g_drmp3_pow43[16 + lsb - 16*(bs_cache >> 31)]*one; + DRMP3_FLUSH_BITS(lsb ? 1 : 0); + } + DRMP3_CHECK_BITS; + } while (--pairs_to_decode); + } while ((big_val_cnt -= np) > 0 && --sfb_cnt >= 0); + } + } + + for (np = 1 - big_val_cnt;; dst += 4) + { + const drmp3_uint8 *codebook_count1 = (gr_info->count1_table) ? tab33 : tab32; + int leaf = codebook_count1[DRMP3_PEEK_BITS(4)]; + if (!(leaf & 8)) + { + leaf = codebook_count1[(leaf >> 3) + (bs_cache << 4 >> (32 - (leaf & 3)))]; + } + DRMP3_FLUSH_BITS(leaf & 7); + if (DRMP3_BSPOS > layer3gr_limit) + { + break; + } +#define DRMP3_RELOAD_SCALEFACTOR if (!--np) { np = *sfb++/2; if (!np) break; one = *scf++; } +#define DRMP3_DEQ_COUNT1(s) if (leaf & (128 >> s)) { dst[s] = ((drmp3_int32)bs_cache < 0) ? -one : one; DRMP3_FLUSH_BITS(1) } + DRMP3_RELOAD_SCALEFACTOR; + DRMP3_DEQ_COUNT1(0); + DRMP3_DEQ_COUNT1(1); + DRMP3_RELOAD_SCALEFACTOR; + DRMP3_DEQ_COUNT1(2); + DRMP3_DEQ_COUNT1(3); + DRMP3_CHECK_BITS; + } + + bs->pos = layer3gr_limit; +} + +static void drmp3_L3_midside_stereo(float *left, int n) +{ + int i = 0; + float *right = left + 576; +#if DRMP3_HAVE_SIMD + if (drmp3_have_simd()) + { + for (; i < n - 3; i += 4) + { + drmp3_f4 vl = DRMP3_VLD(left + i); + drmp3_f4 vr = DRMP3_VLD(right + i); + DRMP3_VSTORE(left + i, DRMP3_VADD(vl, vr)); + DRMP3_VSTORE(right + i, DRMP3_VSUB(vl, vr)); + } +#ifdef __GNUC__ + /* Workaround for spurious -Waggressive-loop-optimizations warning from gcc. + * For more info see: https://github.com/lieff/minimp3/issues/88 + */ + if (__builtin_constant_p(n % 4 == 0) && n % 4 == 0) + return; +#endif + } +#endif + for (; i < n; i++) + { + float a = left[i]; + float b = right[i]; + left[i] = a + b; + right[i] = a - b; + } +} + +static void drmp3_L3_intensity_stereo_band(float *left, int n, float kl, float kr) +{ + int i; + for (i = 0; i < n; i++) + { + left[i + 576] = left[i]*kr; + left[i] = left[i]*kl; + } +} + +static void drmp3_L3_stereo_top_band(const float *right, const drmp3_uint8 *sfb, int nbands, int max_band[3]) +{ + int i, k; + + max_band[0] = max_band[1] = max_band[2] = -1; + + for (i = 0; i < nbands; i++) + { + for (k = 0; k < sfb[i]; k += 2) + { + if (right[k] != 0 || right[k + 1] != 0) + { + max_band[i % 3] = i; + break; + } + } + right += sfb[i]; + } +} + +static void drmp3_L3_stereo_process(float *left, const drmp3_uint8 *ist_pos, const drmp3_uint8 *sfb, const drmp3_uint8 *hdr, int max_band[3], int mpeg2_sh) +{ + static const float g_pan[7*2] = { 0,1,0.21132487f,0.78867513f,0.36602540f,0.63397460f,0.5f,0.5f,0.63397460f,0.36602540f,0.78867513f,0.21132487f,1,0 }; + unsigned i, max_pos = DRMP3_HDR_TEST_MPEG1(hdr) ? 7 : 64; + + for (i = 0; sfb[i]; i++) + { + unsigned ipos = ist_pos[i]; + if ((int)i > max_band[i % 3] && ipos < max_pos) + { + float kl, kr, s = DRMP3_HDR_TEST_MS_STEREO(hdr) ? 1.41421356f : 1; + if (DRMP3_HDR_TEST_MPEG1(hdr)) + { + kl = g_pan[2*ipos]; + kr = g_pan[2*ipos + 1]; + } else + { + kl = 1; + kr = drmp3_L3_ldexp_q2(1, (ipos + 1) >> 1 << mpeg2_sh); + if (ipos & 1) + { + kl = kr; + kr = 1; + } + } + drmp3_L3_intensity_stereo_band(left, sfb[i], kl*s, kr*s); + } else if (DRMP3_HDR_TEST_MS_STEREO(hdr)) + { + drmp3_L3_midside_stereo(left, sfb[i]); + } + left += sfb[i]; + } +} + +static void drmp3_L3_intensity_stereo(float *left, drmp3_uint8 *ist_pos, const drmp3_L3_gr_info *gr, const drmp3_uint8 *hdr) +{ + int max_band[3], n_sfb = gr->n_long_sfb + gr->n_short_sfb; + int i, max_blocks = gr->n_short_sfb ? 3 : 1; + + drmp3_L3_stereo_top_band(left + 576, gr->sfbtab, n_sfb, max_band); + if (gr->n_long_sfb) + { + max_band[0] = max_band[1] = max_band[2] = DRMP3_MAX(DRMP3_MAX(max_band[0], max_band[1]), max_band[2]); + } + for (i = 0; i < max_blocks; i++) + { + int default_pos = DRMP3_HDR_TEST_MPEG1(hdr) ? 3 : 0; + int itop = n_sfb - max_blocks + i; + int prev = itop - max_blocks; + ist_pos[itop] = (drmp3_uint8)(max_band[i] >= prev ? default_pos : ist_pos[prev]); + } + drmp3_L3_stereo_process(left, ist_pos, gr->sfbtab, hdr, max_band, gr[1].scalefac_compress & 1); +} + +static void drmp3_L3_reorder(float *grbuf, float *scratch, const drmp3_uint8 *sfb) +{ + int i, len; + float *src = grbuf, *dst = scratch; + + for (;0 != (len = *sfb); sfb += 3, src += 2*len) + { + for (i = 0; i < len; i++, src++) + { + *dst++ = src[0*len]; + *dst++ = src[1*len]; + *dst++ = src[2*len]; + } + } + DRMP3_COPY_MEMORY(grbuf, scratch, (dst - scratch)*sizeof(float)); +} + +static void drmp3_L3_antialias(float *grbuf, int nbands) +{ + static const float g_aa[2][8] = { + {0.85749293f,0.88174200f,0.94962865f,0.98331459f,0.99551782f,0.99916056f,0.99989920f,0.99999316f}, + {0.51449576f,0.47173197f,0.31337745f,0.18191320f,0.09457419f,0.04096558f,0.01419856f,0.00369997f} + }; + + for (; nbands > 0; nbands--, grbuf += 18) + { + int i = 0; +#if DRMP3_HAVE_SIMD + if (drmp3_have_simd()) for (; i < 8; i += 4) + { + drmp3_f4 vu = DRMP3_VLD(grbuf + 18 + i); + drmp3_f4 vd = DRMP3_VLD(grbuf + 14 - i); + drmp3_f4 vc0 = DRMP3_VLD(g_aa[0] + i); + drmp3_f4 vc1 = DRMP3_VLD(g_aa[1] + i); + vd = DRMP3_VREV(vd); + DRMP3_VSTORE(grbuf + 18 + i, DRMP3_VSUB(DRMP3_VMUL(vu, vc0), DRMP3_VMUL(vd, vc1))); + vd = DRMP3_VADD(DRMP3_VMUL(vu, vc1), DRMP3_VMUL(vd, vc0)); + DRMP3_VSTORE(grbuf + 14 - i, DRMP3_VREV(vd)); + } +#endif +#ifndef DR_MP3_ONLY_SIMD + for(; i < 8; i++) + { + float u = grbuf[18 + i]; + float d = grbuf[17 - i]; + grbuf[18 + i] = u*g_aa[0][i] - d*g_aa[1][i]; + grbuf[17 - i] = u*g_aa[1][i] + d*g_aa[0][i]; + } +#endif + } +} + +static void drmp3_L3_dct3_9(float *y) +{ + float s0, s1, s2, s3, s4, s5, s6, s7, s8, t0, t2, t4; + + s0 = y[0]; s2 = y[2]; s4 = y[4]; s6 = y[6]; s8 = y[8]; + t0 = s0 + s6*0.5f; + s0 -= s6; + t4 = (s4 + s2)*0.93969262f; + t2 = (s8 + s2)*0.76604444f; + s6 = (s4 - s8)*0.17364818f; + s4 += s8 - s2; + + s2 = s0 - s4*0.5f; + y[4] = s4 + s0; + s8 = t0 - t2 + s6; + s0 = t0 - t4 + t2; + s4 = t0 + t4 - s6; + + s1 = y[1]; s3 = y[3]; s5 = y[5]; s7 = y[7]; + + s3 *= 0.86602540f; + t0 = (s5 + s1)*0.98480775f; + t4 = (s5 - s7)*0.34202014f; + t2 = (s1 + s7)*0.64278761f; + s1 = (s1 - s5 - s7)*0.86602540f; + + s5 = t0 - s3 - t2; + s7 = t4 - s3 - t0; + s3 = t4 + s3 - t2; + + y[0] = s4 - s7; + y[1] = s2 + s1; + y[2] = s0 - s3; + y[3] = s8 + s5; + y[5] = s8 - s5; + y[6] = s0 + s3; + y[7] = s2 - s1; + y[8] = s4 + s7; +} + +static void drmp3_L3_imdct36(float *grbuf, float *overlap, const float *window, int nbands) +{ + int i, j; + static const float g_twid9[18] = { + 0.73727734f,0.79335334f,0.84339145f,0.88701083f,0.92387953f,0.95371695f,0.97629601f,0.99144486f,0.99904822f,0.67559021f,0.60876143f,0.53729961f,0.46174861f,0.38268343f,0.30070580f,0.21643961f,0.13052619f,0.04361938f + }; + + for (j = 0; j < nbands; j++, grbuf += 18, overlap += 9) + { + float co[9], si[9]; + co[0] = -grbuf[0]; + si[0] = grbuf[17]; + for (i = 0; i < 4; i++) + { + si[8 - 2*i] = grbuf[4*i + 1] - grbuf[4*i + 2]; + co[1 + 2*i] = grbuf[4*i + 1] + grbuf[4*i + 2]; + si[7 - 2*i] = grbuf[4*i + 4] - grbuf[4*i + 3]; + co[2 + 2*i] = -(grbuf[4*i + 3] + grbuf[4*i + 4]); + } + drmp3_L3_dct3_9(co); + drmp3_L3_dct3_9(si); + + si[1] = -si[1]; + si[3] = -si[3]; + si[5] = -si[5]; + si[7] = -si[7]; + + i = 0; + +#if DRMP3_HAVE_SIMD + if (drmp3_have_simd()) for (; i < 8; i += 4) + { + drmp3_f4 vovl = DRMP3_VLD(overlap + i); + drmp3_f4 vc = DRMP3_VLD(co + i); + drmp3_f4 vs = DRMP3_VLD(si + i); + drmp3_f4 vr0 = DRMP3_VLD(g_twid9 + i); + drmp3_f4 vr1 = DRMP3_VLD(g_twid9 + 9 + i); + drmp3_f4 vw0 = DRMP3_VLD(window + i); + drmp3_f4 vw1 = DRMP3_VLD(window + 9 + i); + drmp3_f4 vsum = DRMP3_VADD(DRMP3_VMUL(vc, vr1), DRMP3_VMUL(vs, vr0)); + DRMP3_VSTORE(overlap + i, DRMP3_VSUB(DRMP3_VMUL(vc, vr0), DRMP3_VMUL(vs, vr1))); + DRMP3_VSTORE(grbuf + i, DRMP3_VSUB(DRMP3_VMUL(vovl, vw0), DRMP3_VMUL(vsum, vw1))); + vsum = DRMP3_VADD(DRMP3_VMUL(vovl, vw1), DRMP3_VMUL(vsum, vw0)); + DRMP3_VSTORE(grbuf + 14 - i, DRMP3_VREV(vsum)); + } +#endif + for (; i < 9; i++) + { + float ovl = overlap[i]; + float sum = co[i]*g_twid9[9 + i] + si[i]*g_twid9[0 + i]; + overlap[i] = co[i]*g_twid9[0 + i] - si[i]*g_twid9[9 + i]; + grbuf[i] = ovl*window[0 + i] - sum*window[9 + i]; + grbuf[17 - i] = ovl*window[9 + i] + sum*window[0 + i]; + } + } +} + +static void drmp3_L3_idct3(float x0, float x1, float x2, float *dst) +{ + float m1 = x1*0.86602540f; + float a1 = x0 - x2*0.5f; + dst[1] = x0 + x2; + dst[0] = a1 + m1; + dst[2] = a1 - m1; +} + +static void drmp3_L3_imdct12(float *x, float *dst, float *overlap) +{ + static const float g_twid3[6] = { 0.79335334f,0.92387953f,0.99144486f, 0.60876143f,0.38268343f,0.13052619f }; + float co[3], si[3]; + int i; + + drmp3_L3_idct3(-x[0], x[6] + x[3], x[12] + x[9], co); + drmp3_L3_idct3(x[15], x[12] - x[9], x[6] - x[3], si); + si[1] = -si[1]; + + for (i = 0; i < 3; i++) + { + float ovl = overlap[i]; + float sum = co[i]*g_twid3[3 + i] + si[i]*g_twid3[0 + i]; + overlap[i] = co[i]*g_twid3[0 + i] - si[i]*g_twid3[3 + i]; + dst[i] = ovl*g_twid3[2 - i] - sum*g_twid3[5 - i]; + dst[5 - i] = ovl*g_twid3[5 - i] + sum*g_twid3[2 - i]; + } +} + +static void drmp3_L3_imdct_short(float *grbuf, float *overlap, int nbands) +{ + for (;nbands > 0; nbands--, overlap += 9, grbuf += 18) + { + float tmp[18]; + DRMP3_COPY_MEMORY(tmp, grbuf, sizeof(tmp)); + DRMP3_COPY_MEMORY(grbuf, overlap, 6*sizeof(float)); + drmp3_L3_imdct12(tmp, grbuf + 6, overlap + 6); + drmp3_L3_imdct12(tmp + 1, grbuf + 12, overlap + 6); + drmp3_L3_imdct12(tmp + 2, overlap, overlap + 6); + } +} + +static void drmp3_L3_change_sign(float *grbuf) +{ + int b, i; + for (b = 0, grbuf += 18; b < 32; b += 2, grbuf += 36) + for (i = 1; i < 18; i += 2) + grbuf[i] = -grbuf[i]; +} + +static void drmp3_L3_imdct_gr(float *grbuf, float *overlap, unsigned block_type, unsigned n_long_bands) +{ + static const float g_mdct_window[2][18] = { + { 0.99904822f,0.99144486f,0.97629601f,0.95371695f,0.92387953f,0.88701083f,0.84339145f,0.79335334f,0.73727734f,0.04361938f,0.13052619f,0.21643961f,0.30070580f,0.38268343f,0.46174861f,0.53729961f,0.60876143f,0.67559021f }, + { 1,1,1,1,1,1,0.99144486f,0.92387953f,0.79335334f,0,0,0,0,0,0,0.13052619f,0.38268343f,0.60876143f } + }; + if (n_long_bands) + { + drmp3_L3_imdct36(grbuf, overlap, g_mdct_window[0], n_long_bands); + grbuf += 18*n_long_bands; + overlap += 9*n_long_bands; + } + if (block_type == DRMP3_SHORT_BLOCK_TYPE) + drmp3_L3_imdct_short(grbuf, overlap, 32 - n_long_bands); + else + drmp3_L3_imdct36(grbuf, overlap, g_mdct_window[block_type == DRMP3_STOP_BLOCK_TYPE], 32 - n_long_bands); +} + +static void drmp3_L3_save_reservoir(drmp3dec *h, drmp3dec_scratch *s) +{ + int pos = (s->bs.pos + 7)/8u; + int remains = s->bs.limit/8u - pos; + if (remains > DRMP3_MAX_BITRESERVOIR_BYTES) + { + pos += remains - DRMP3_MAX_BITRESERVOIR_BYTES; + remains = DRMP3_MAX_BITRESERVOIR_BYTES; + } + if (remains > 0) + { + DRMP3_MOVE_MEMORY(h->reserv_buf, s->maindata + pos, remains); + } + h->reserv = remains; +} + +static int drmp3_L3_restore_reservoir(drmp3dec *h, drmp3_bs *bs, drmp3dec_scratch *s, int main_data_begin) +{ + int frame_bytes = (bs->limit - bs->pos)/8; + int bytes_have = DRMP3_MIN(h->reserv, main_data_begin); + DRMP3_COPY_MEMORY(s->maindata, h->reserv_buf + DRMP3_MAX(0, h->reserv - main_data_begin), DRMP3_MIN(h->reserv, main_data_begin)); + DRMP3_COPY_MEMORY(s->maindata + bytes_have, bs->buf + bs->pos/8, frame_bytes); + drmp3_bs_init(&s->bs, s->maindata, bytes_have + frame_bytes); + return h->reserv >= main_data_begin; +} + +static void drmp3_L3_decode(drmp3dec *h, drmp3dec_scratch *s, drmp3_L3_gr_info *gr_info, int nch) +{ + int ch; + + for (ch = 0; ch < nch; ch++) + { + int layer3gr_limit = s->bs.pos + gr_info[ch].part_23_length; + drmp3_L3_decode_scalefactors(h->header, s->ist_pos[ch], &s->bs, gr_info + ch, s->scf, ch); + drmp3_L3_huffman(s->grbuf[ch], &s->bs, gr_info + ch, s->scf, layer3gr_limit); + } + + if (DRMP3_HDR_TEST_I_STEREO(h->header)) + { + drmp3_L3_intensity_stereo(s->grbuf[0], s->ist_pos[1], gr_info, h->header); + } else if (DRMP3_HDR_IS_MS_STEREO(h->header)) + { + drmp3_L3_midside_stereo(s->grbuf[0], 576); + } + + for (ch = 0; ch < nch; ch++, gr_info++) + { + int aa_bands = 31; + int n_long_bands = (gr_info->mixed_block_flag ? 2 : 0) << (int)(DRMP3_HDR_GET_MY_SAMPLE_RATE(h->header) == 2); + + if (gr_info->n_short_sfb) + { + aa_bands = n_long_bands - 1; + drmp3_L3_reorder(s->grbuf[ch] + n_long_bands*18, s->syn[0], gr_info->sfbtab + gr_info->n_long_sfb); + } + + drmp3_L3_antialias(s->grbuf[ch], aa_bands); + drmp3_L3_imdct_gr(s->grbuf[ch], h->mdct_overlap[ch], gr_info->block_type, n_long_bands); + drmp3_L3_change_sign(s->grbuf[ch]); + } +} + +static void drmp3d_DCT_II(float *grbuf, int n) +{ + static const float g_sec[24] = { + 10.19000816f,0.50060302f,0.50241929f,3.40760851f,0.50547093f,0.52249861f,2.05778098f,0.51544732f,0.56694406f,1.48416460f,0.53104258f,0.64682180f,1.16943991f,0.55310392f,0.78815460f,0.97256821f,0.58293498f,1.06067765f,0.83934963f,0.62250412f,1.72244716f,0.74453628f,0.67480832f,5.10114861f + }; + int i, k = 0; +#if DRMP3_HAVE_SIMD + if (drmp3_have_simd()) for (; k < n; k += 4) + { + drmp3_f4 t[4][8], *x; + float *y = grbuf + k; + + for (x = t[0], i = 0; i < 8; i++, x++) + { + drmp3_f4 x0 = DRMP3_VLD(&y[i*18]); + drmp3_f4 x1 = DRMP3_VLD(&y[(15 - i)*18]); + drmp3_f4 x2 = DRMP3_VLD(&y[(16 + i)*18]); + drmp3_f4 x3 = DRMP3_VLD(&y[(31 - i)*18]); + drmp3_f4 t0 = DRMP3_VADD(x0, x3); + drmp3_f4 t1 = DRMP3_VADD(x1, x2); + drmp3_f4 t2 = DRMP3_VMUL_S(DRMP3_VSUB(x1, x2), g_sec[3*i + 0]); + drmp3_f4 t3 = DRMP3_VMUL_S(DRMP3_VSUB(x0, x3), g_sec[3*i + 1]); + x[0] = DRMP3_VADD(t0, t1); + x[8] = DRMP3_VMUL_S(DRMP3_VSUB(t0, t1), g_sec[3*i + 2]); + x[16] = DRMP3_VADD(t3, t2); + x[24] = DRMP3_VMUL_S(DRMP3_VSUB(t3, t2), g_sec[3*i + 2]); + } + for (x = t[0], i = 0; i < 4; i++, x += 8) + { + drmp3_f4 x0 = x[0], x1 = x[1], x2 = x[2], x3 = x[3], x4 = x[4], x5 = x[5], x6 = x[6], x7 = x[7], xt; + xt = DRMP3_VSUB(x0, x7); x0 = DRMP3_VADD(x0, x7); + x7 = DRMP3_VSUB(x1, x6); x1 = DRMP3_VADD(x1, x6); + x6 = DRMP3_VSUB(x2, x5); x2 = DRMP3_VADD(x2, x5); + x5 = DRMP3_VSUB(x3, x4); x3 = DRMP3_VADD(x3, x4); + x4 = DRMP3_VSUB(x0, x3); x0 = DRMP3_VADD(x0, x3); + x3 = DRMP3_VSUB(x1, x2); x1 = DRMP3_VADD(x1, x2); + x[0] = DRMP3_VADD(x0, x1); + x[4] = DRMP3_VMUL_S(DRMP3_VSUB(x0, x1), 0.70710677f); + x5 = DRMP3_VADD(x5, x6); + x6 = DRMP3_VMUL_S(DRMP3_VADD(x6, x7), 0.70710677f); + x7 = DRMP3_VADD(x7, xt); + x3 = DRMP3_VMUL_S(DRMP3_VADD(x3, x4), 0.70710677f); + x5 = DRMP3_VSUB(x5, DRMP3_VMUL_S(x7, 0.198912367f)); /* rotate by PI/8 */ + x7 = DRMP3_VADD(x7, DRMP3_VMUL_S(x5, 0.382683432f)); + x5 = DRMP3_VSUB(x5, DRMP3_VMUL_S(x7, 0.198912367f)); + x0 = DRMP3_VSUB(xt, x6); xt = DRMP3_VADD(xt, x6); + x[1] = DRMP3_VMUL_S(DRMP3_VADD(xt, x7), 0.50979561f); + x[2] = DRMP3_VMUL_S(DRMP3_VADD(x4, x3), 0.54119611f); + x[3] = DRMP3_VMUL_S(DRMP3_VSUB(x0, x5), 0.60134488f); + x[5] = DRMP3_VMUL_S(DRMP3_VADD(x0, x5), 0.89997619f); + x[6] = DRMP3_VMUL_S(DRMP3_VSUB(x4, x3), 1.30656302f); + x[7] = DRMP3_VMUL_S(DRMP3_VSUB(xt, x7), 2.56291556f); + } + + if (k > n - 3) + { +#if DRMP3_HAVE_SSE +#define DRMP3_VSAVE2(i, v) _mm_storel_pi((__m64 *)(void*)&y[i*18], v) +#else +#define DRMP3_VSAVE2(i, v) vst1_f32((float32_t *)&y[(i)*18], vget_low_f32(v)) +#endif + for (i = 0; i < 7; i++, y += 4*18) + { + drmp3_f4 s = DRMP3_VADD(t[3][i], t[3][i + 1]); + DRMP3_VSAVE2(0, t[0][i]); + DRMP3_VSAVE2(1, DRMP3_VADD(t[2][i], s)); + DRMP3_VSAVE2(2, DRMP3_VADD(t[1][i], t[1][i + 1])); + DRMP3_VSAVE2(3, DRMP3_VADD(t[2][1 + i], s)); + } + DRMP3_VSAVE2(0, t[0][7]); + DRMP3_VSAVE2(1, DRMP3_VADD(t[2][7], t[3][7])); + DRMP3_VSAVE2(2, t[1][7]); + DRMP3_VSAVE2(3, t[3][7]); + } else + { +#define DRMP3_VSAVE4(i, v) DRMP3_VSTORE(&y[(i)*18], v) + for (i = 0; i < 7; i++, y += 4*18) + { + drmp3_f4 s = DRMP3_VADD(t[3][i], t[3][i + 1]); + DRMP3_VSAVE4(0, t[0][i]); + DRMP3_VSAVE4(1, DRMP3_VADD(t[2][i], s)); + DRMP3_VSAVE4(2, DRMP3_VADD(t[1][i], t[1][i + 1])); + DRMP3_VSAVE4(3, DRMP3_VADD(t[2][1 + i], s)); + } + DRMP3_VSAVE4(0, t[0][7]); + DRMP3_VSAVE4(1, DRMP3_VADD(t[2][7], t[3][7])); + DRMP3_VSAVE4(2, t[1][7]); + DRMP3_VSAVE4(3, t[3][7]); + } + } else +#endif +#ifdef DR_MP3_ONLY_SIMD + {} /* for HAVE_SIMD=1, MINIMP3_ONLY_SIMD=1 case we do not need non-intrinsic "else" branch */ +#else + for (; k < n; k++) + { + float t[4][8], *x, *y = grbuf + k; + + for (x = t[0], i = 0; i < 8; i++, x++) + { + float x0 = y[i*18]; + float x1 = y[(15 - i)*18]; + float x2 = y[(16 + i)*18]; + float x3 = y[(31 - i)*18]; + float t0 = x0 + x3; + float t1 = x1 + x2; + float t2 = (x1 - x2)*g_sec[3*i + 0]; + float t3 = (x0 - x3)*g_sec[3*i + 1]; + x[0] = t0 + t1; + x[8] = (t0 - t1)*g_sec[3*i + 2]; + x[16] = t3 + t2; + x[24] = (t3 - t2)*g_sec[3*i + 2]; + } + for (x = t[0], i = 0; i < 4; i++, x += 8) + { + float x0 = x[0], x1 = x[1], x2 = x[2], x3 = x[3], x4 = x[4], x5 = x[5], x6 = x[6], x7 = x[7], xt; + xt = x0 - x7; x0 += x7; + x7 = x1 - x6; x1 += x6; + x6 = x2 - x5; x2 += x5; + x5 = x3 - x4; x3 += x4; + x4 = x0 - x3; x0 += x3; + x3 = x1 - x2; x1 += x2; + x[0] = x0 + x1; + x[4] = (x0 - x1)*0.70710677f; + x5 = x5 + x6; + x6 = (x6 + x7)*0.70710677f; + x7 = x7 + xt; + x3 = (x3 + x4)*0.70710677f; + x5 -= x7*0.198912367f; /* rotate by PI/8 */ + x7 += x5*0.382683432f; + x5 -= x7*0.198912367f; + x0 = xt - x6; xt += x6; + x[1] = (xt + x7)*0.50979561f; + x[2] = (x4 + x3)*0.54119611f; + x[3] = (x0 - x5)*0.60134488f; + x[5] = (x0 + x5)*0.89997619f; + x[6] = (x4 - x3)*1.30656302f; + x[7] = (xt - x7)*2.56291556f; + + } + for (i = 0; i < 7; i++, y += 4*18) + { + y[0*18] = t[0][i]; + y[1*18] = t[2][i] + t[3][i] + t[3][i + 1]; + y[2*18] = t[1][i] + t[1][i + 1]; + y[3*18] = t[2][i + 1] + t[3][i] + t[3][i + 1]; + } + y[0*18] = t[0][7]; + y[1*18] = t[2][7] + t[3][7]; + y[2*18] = t[1][7]; + y[3*18] = t[3][7]; + } +#endif +} + +#ifndef DR_MP3_FLOAT_OUTPUT +typedef drmp3_int16 drmp3d_sample_t; + +static drmp3_int16 drmp3d_scale_pcm(float sample) +{ + drmp3_int16 s; +#if DRMP3_HAVE_ARMV6 + drmp3_int32 s32 = (drmp3_int32)(sample + .5f); + s32 -= (s32 < 0); + s = (drmp3_int16)drmp3_clip_int16_arm(s32); +#else + if (sample >= 32766.5) return (drmp3_int16) 32767; + if (sample <= -32767.5) return (drmp3_int16)-32768; + s = (drmp3_int16)(sample + .5f); + s -= (s < 0); /* away from zero, to be compliant */ +#endif + return s; +} +#else +typedef float drmp3d_sample_t; + +static float drmp3d_scale_pcm(float sample) +{ + return sample*(1.f/32768.f); +} +#endif + +static void drmp3d_synth_pair(drmp3d_sample_t *pcm, int nch, const float *z) +{ + float a; + a = (z[14*64] - z[ 0]) * 29; + a += (z[ 1*64] + z[13*64]) * 213; + a += (z[12*64] - z[ 2*64]) * 459; + a += (z[ 3*64] + z[11*64]) * 2037; + a += (z[10*64] - z[ 4*64]) * 5153; + a += (z[ 5*64] + z[ 9*64]) * 6574; + a += (z[ 8*64] - z[ 6*64]) * 37489; + a += z[ 7*64] * 75038; + pcm[0] = drmp3d_scale_pcm(a); + + z += 2; + a = z[14*64] * 104; + a += z[12*64] * 1567; + a += z[10*64] * 9727; + a += z[ 8*64] * 64019; + a += z[ 6*64] * -9975; + a += z[ 4*64] * -45; + a += z[ 2*64] * 146; + a += z[ 0*64] * -5; + pcm[16*nch] = drmp3d_scale_pcm(a); +} + +static void drmp3d_synth(float *xl, drmp3d_sample_t *dstl, int nch, float *lins) +{ + int i; + float *xr = xl + 576*(nch - 1); + drmp3d_sample_t *dstr = dstl + (nch - 1); + + static const float g_win[] = { + -1,26,-31,208,218,401,-519,2063,2000,4788,-5517,7134,5959,35640,-39336,74992, + -1,24,-35,202,222,347,-581,2080,1952,4425,-5879,7640,5288,33791,-41176,74856, + -1,21,-38,196,225,294,-645,2087,1893,4063,-6237,8092,4561,31947,-43006,74630, + -1,19,-41,190,227,244,-711,2085,1822,3705,-6589,8492,3776,30112,-44821,74313, + -1,17,-45,183,228,197,-779,2075,1739,3351,-6935,8840,2935,28289,-46617,73908, + -1,16,-49,176,228,153,-848,2057,1644,3004,-7271,9139,2037,26482,-48390,73415, + -2,14,-53,169,227,111,-919,2032,1535,2663,-7597,9389,1082,24694,-50137,72835, + -2,13,-58,161,224,72,-991,2001,1414,2330,-7910,9592,70,22929,-51853,72169, + -2,11,-63,154,221,36,-1064,1962,1280,2006,-8209,9750,-998,21189,-53534,71420, + -2,10,-68,147,215,2,-1137,1919,1131,1692,-8491,9863,-2122,19478,-55178,70590, + -3,9,-73,139,208,-29,-1210,1870,970,1388,-8755,9935,-3300,17799,-56778,69679, + -3,8,-79,132,200,-57,-1283,1817,794,1095,-8998,9966,-4533,16155,-58333,68692, + -4,7,-85,125,189,-83,-1356,1759,605,814,-9219,9959,-5818,14548,-59838,67629, + -4,7,-91,117,177,-106,-1428,1698,402,545,-9416,9916,-7154,12980,-61289,66494, + -5,6,-97,111,163,-127,-1498,1634,185,288,-9585,9838,-8540,11455,-62684,65290 + }; + float *zlin = lins + 15*64; + const float *w = g_win; + + zlin[4*15] = xl[18*16]; + zlin[4*15 + 1] = xr[18*16]; + zlin[4*15 + 2] = xl[0]; + zlin[4*15 + 3] = xr[0]; + + zlin[4*31] = xl[1 + 18*16]; + zlin[4*31 + 1] = xr[1 + 18*16]; + zlin[4*31 + 2] = xl[1]; + zlin[4*31 + 3] = xr[1]; + + drmp3d_synth_pair(dstr, nch, lins + 4*15 + 1); + drmp3d_synth_pair(dstr + 32*nch, nch, lins + 4*15 + 64 + 1); + drmp3d_synth_pair(dstl, nch, lins + 4*15); + drmp3d_synth_pair(dstl + 32*nch, nch, lins + 4*15 + 64); + +#if DRMP3_HAVE_SIMD + if (drmp3_have_simd()) for (i = 14; i >= 0; i--) + { +#define DRMP3_VLOAD(k) drmp3_f4 w0 = DRMP3_VSET(*w++); drmp3_f4 w1 = DRMP3_VSET(*w++); drmp3_f4 vz = DRMP3_VLD(&zlin[4*i - 64*k]); drmp3_f4 vy = DRMP3_VLD(&zlin[4*i - 64*(15 - k)]); +#define DRMP3_V0(k) { DRMP3_VLOAD(k) b = DRMP3_VADD(DRMP3_VMUL(vz, w1), DRMP3_VMUL(vy, w0)) ; a = DRMP3_VSUB(DRMP3_VMUL(vz, w0), DRMP3_VMUL(vy, w1)); } +#define DRMP3_V1(k) { DRMP3_VLOAD(k) b = DRMP3_VADD(b, DRMP3_VADD(DRMP3_VMUL(vz, w1), DRMP3_VMUL(vy, w0))); a = DRMP3_VADD(a, DRMP3_VSUB(DRMP3_VMUL(vz, w0), DRMP3_VMUL(vy, w1))); } +#define DRMP3_V2(k) { DRMP3_VLOAD(k) b = DRMP3_VADD(b, DRMP3_VADD(DRMP3_VMUL(vz, w1), DRMP3_VMUL(vy, w0))); a = DRMP3_VADD(a, DRMP3_VSUB(DRMP3_VMUL(vy, w1), DRMP3_VMUL(vz, w0))); } + drmp3_f4 a, b; + zlin[4*i] = xl[18*(31 - i)]; + zlin[4*i + 1] = xr[18*(31 - i)]; + zlin[4*i + 2] = xl[1 + 18*(31 - i)]; + zlin[4*i + 3] = xr[1 + 18*(31 - i)]; + zlin[4*i + 64] = xl[1 + 18*(1 + i)]; + zlin[4*i + 64 + 1] = xr[1 + 18*(1 + i)]; + zlin[4*i - 64 + 2] = xl[18*(1 + i)]; + zlin[4*i - 64 + 3] = xr[18*(1 + i)]; + + DRMP3_V0(0) DRMP3_V2(1) DRMP3_V1(2) DRMP3_V2(3) DRMP3_V1(4) DRMP3_V2(5) DRMP3_V1(6) DRMP3_V2(7) + + { +#ifndef DR_MP3_FLOAT_OUTPUT +#if DRMP3_HAVE_SSE + static const drmp3_f4 g_max = { 32767.0f, 32767.0f, 32767.0f, 32767.0f }; + static const drmp3_f4 g_min = { -32768.0f, -32768.0f, -32768.0f, -32768.0f }; + __m128i pcm8 = _mm_packs_epi32(_mm_cvtps_epi32(_mm_max_ps(_mm_min_ps(a, g_max), g_min)), + _mm_cvtps_epi32(_mm_max_ps(_mm_min_ps(b, g_max), g_min))); + dstr[(15 - i)*nch] = (drmp3_int16)_mm_extract_epi16(pcm8, 1); + dstr[(17 + i)*nch] = (drmp3_int16)_mm_extract_epi16(pcm8, 5); + dstl[(15 - i)*nch] = (drmp3_int16)_mm_extract_epi16(pcm8, 0); + dstl[(17 + i)*nch] = (drmp3_int16)_mm_extract_epi16(pcm8, 4); + dstr[(47 - i)*nch] = (drmp3_int16)_mm_extract_epi16(pcm8, 3); + dstr[(49 + i)*nch] = (drmp3_int16)_mm_extract_epi16(pcm8, 7); + dstl[(47 - i)*nch] = (drmp3_int16)_mm_extract_epi16(pcm8, 2); + dstl[(49 + i)*nch] = (drmp3_int16)_mm_extract_epi16(pcm8, 6); +#else + int16x4_t pcma, pcmb; + a = DRMP3_VADD(a, DRMP3_VSET(0.5f)); + b = DRMP3_VADD(b, DRMP3_VSET(0.5f)); + pcma = vqmovn_s32(vqaddq_s32(vcvtq_s32_f32(a), vreinterpretq_s32_u32(vcltq_f32(a, DRMP3_VSET(0))))); + pcmb = vqmovn_s32(vqaddq_s32(vcvtq_s32_f32(b), vreinterpretq_s32_u32(vcltq_f32(b, DRMP3_VSET(0))))); + vst1_lane_s16(dstr + (15 - i)*nch, pcma, 1); + vst1_lane_s16(dstr + (17 + i)*nch, pcmb, 1); + vst1_lane_s16(dstl + (15 - i)*nch, pcma, 0); + vst1_lane_s16(dstl + (17 + i)*nch, pcmb, 0); + vst1_lane_s16(dstr + (47 - i)*nch, pcma, 3); + vst1_lane_s16(dstr + (49 + i)*nch, pcmb, 3); + vst1_lane_s16(dstl + (47 - i)*nch, pcma, 2); + vst1_lane_s16(dstl + (49 + i)*nch, pcmb, 2); +#endif +#else + #if DRMP3_HAVE_SSE + static const drmp3_f4 g_scale = { 1.0f/32768.0f, 1.0f/32768.0f, 1.0f/32768.0f, 1.0f/32768.0f }; + #else + const drmp3_f4 g_scale = vdupq_n_f32(1.0f/32768.0f); + #endif + a = DRMP3_VMUL(a, g_scale); + b = DRMP3_VMUL(b, g_scale); +#if DRMP3_HAVE_SSE + _mm_store_ss(dstr + (15 - i)*nch, _mm_shuffle_ps(a, a, _MM_SHUFFLE(1, 1, 1, 1))); + _mm_store_ss(dstr + (17 + i)*nch, _mm_shuffle_ps(b, b, _MM_SHUFFLE(1, 1, 1, 1))); + _mm_store_ss(dstl + (15 - i)*nch, _mm_shuffle_ps(a, a, _MM_SHUFFLE(0, 0, 0, 0))); + _mm_store_ss(dstl + (17 + i)*nch, _mm_shuffle_ps(b, b, _MM_SHUFFLE(0, 0, 0, 0))); + _mm_store_ss(dstr + (47 - i)*nch, _mm_shuffle_ps(a, a, _MM_SHUFFLE(3, 3, 3, 3))); + _mm_store_ss(dstr + (49 + i)*nch, _mm_shuffle_ps(b, b, _MM_SHUFFLE(3, 3, 3, 3))); + _mm_store_ss(dstl + (47 - i)*nch, _mm_shuffle_ps(a, a, _MM_SHUFFLE(2, 2, 2, 2))); + _mm_store_ss(dstl + (49 + i)*nch, _mm_shuffle_ps(b, b, _MM_SHUFFLE(2, 2, 2, 2))); +#else + vst1q_lane_f32(dstr + (15 - i)*nch, a, 1); + vst1q_lane_f32(dstr + (17 + i)*nch, b, 1); + vst1q_lane_f32(dstl + (15 - i)*nch, a, 0); + vst1q_lane_f32(dstl + (17 + i)*nch, b, 0); + vst1q_lane_f32(dstr + (47 - i)*nch, a, 3); + vst1q_lane_f32(dstr + (49 + i)*nch, b, 3); + vst1q_lane_f32(dstl + (47 - i)*nch, a, 2); + vst1q_lane_f32(dstl + (49 + i)*nch, b, 2); +#endif +#endif /* DR_MP3_FLOAT_OUTPUT */ + } + } else +#endif +#ifdef DR_MP3_ONLY_SIMD + {} /* for HAVE_SIMD=1, MINIMP3_ONLY_SIMD=1 case we do not need non-intrinsic "else" branch */ +#else + for (i = 14; i >= 0; i--) + { +#define DRMP3_LOAD(k) float w0 = *w++; float w1 = *w++; float *vz = &zlin[4*i - k*64]; float *vy = &zlin[4*i - (15 - k)*64]; +#define DRMP3_S0(k) { int j; DRMP3_LOAD(k); for (j = 0; j < 4; j++) b[j] = vz[j]*w1 + vy[j]*w0, a[j] = vz[j]*w0 - vy[j]*w1; } +#define DRMP3_S1(k) { int j; DRMP3_LOAD(k); for (j = 0; j < 4; j++) b[j] += vz[j]*w1 + vy[j]*w0, a[j] += vz[j]*w0 - vy[j]*w1; } +#define DRMP3_S2(k) { int j; DRMP3_LOAD(k); for (j = 0; j < 4; j++) b[j] += vz[j]*w1 + vy[j]*w0, a[j] += vy[j]*w1 - vz[j]*w0; } + float a[4], b[4]; + + zlin[4*i] = xl[18*(31 - i)]; + zlin[4*i + 1] = xr[18*(31 - i)]; + zlin[4*i + 2] = xl[1 + 18*(31 - i)]; + zlin[4*i + 3] = xr[1 + 18*(31 - i)]; + zlin[4*(i + 16)] = xl[1 + 18*(1 + i)]; + zlin[4*(i + 16) + 1] = xr[1 + 18*(1 + i)]; + zlin[4*(i - 16) + 2] = xl[18*(1 + i)]; + zlin[4*(i - 16) + 3] = xr[18*(1 + i)]; + + DRMP3_S0(0) DRMP3_S2(1) DRMP3_S1(2) DRMP3_S2(3) DRMP3_S1(4) DRMP3_S2(5) DRMP3_S1(6) DRMP3_S2(7) + + dstr[(15 - i)*nch] = drmp3d_scale_pcm(a[1]); + dstr[(17 + i)*nch] = drmp3d_scale_pcm(b[1]); + dstl[(15 - i)*nch] = drmp3d_scale_pcm(a[0]); + dstl[(17 + i)*nch] = drmp3d_scale_pcm(b[0]); + dstr[(47 - i)*nch] = drmp3d_scale_pcm(a[3]); + dstr[(49 + i)*nch] = drmp3d_scale_pcm(b[3]); + dstl[(47 - i)*nch] = drmp3d_scale_pcm(a[2]); + dstl[(49 + i)*nch] = drmp3d_scale_pcm(b[2]); + } +#endif +} + +static void drmp3d_synth_granule(float *qmf_state, float *grbuf, int nbands, int nch, drmp3d_sample_t *pcm, float *lins) +{ + int i; + for (i = 0; i < nch; i++) + { + drmp3d_DCT_II(grbuf + 576*i, nbands); + } + + DRMP3_COPY_MEMORY(lins, qmf_state, sizeof(float)*15*64); + + for (i = 0; i < nbands; i += 2) + { + drmp3d_synth(grbuf + i, pcm + 32*nch*i, nch, lins + i*64); + } +#ifndef DR_MP3_NONSTANDARD_BUT_LOGICAL + if (nch == 1) + { + for (i = 0; i < 15*64; i += 2) + { + qmf_state[i] = lins[nbands*64 + i]; + } + } else +#endif + { + DRMP3_COPY_MEMORY(qmf_state, lins + nbands*64, sizeof(float)*15*64); + } +} + +static int drmp3d_match_frame(const drmp3_uint8 *hdr, int mp3_bytes, int frame_bytes) +{ + int i, nmatch; + for (i = 0, nmatch = 0; nmatch < DRMP3_MAX_FRAME_SYNC_MATCHES; nmatch++) + { + i += drmp3_hdr_frame_bytes(hdr + i, frame_bytes) + drmp3_hdr_padding(hdr + i); + if (i + DRMP3_HDR_SIZE > mp3_bytes) + return nmatch > 0; + if (!drmp3_hdr_compare(hdr, hdr + i)) + return 0; + } + return 1; +} + +static int drmp3d_find_frame(const drmp3_uint8 *mp3, int mp3_bytes, int *free_format_bytes, int *ptr_frame_bytes) +{ + int i, k; + for (i = 0; i < mp3_bytes - DRMP3_HDR_SIZE; i++, mp3++) + { + if (drmp3_hdr_valid(mp3)) + { + int frame_bytes = drmp3_hdr_frame_bytes(mp3, *free_format_bytes); + int frame_and_padding = frame_bytes + drmp3_hdr_padding(mp3); + + for (k = DRMP3_HDR_SIZE; !frame_bytes && k < DRMP3_MAX_FREE_FORMAT_FRAME_SIZE && i + 2*k < mp3_bytes - DRMP3_HDR_SIZE; k++) + { + if (drmp3_hdr_compare(mp3, mp3 + k)) + { + int fb = k - drmp3_hdr_padding(mp3); + int nextfb = fb + drmp3_hdr_padding(mp3 + k); + if (i + k + nextfb + DRMP3_HDR_SIZE > mp3_bytes || !drmp3_hdr_compare(mp3, mp3 + k + nextfb)) + continue; + frame_and_padding = k; + frame_bytes = fb; + *free_format_bytes = fb; + } + } + + if ((frame_bytes && i + frame_and_padding <= mp3_bytes && + drmp3d_match_frame(mp3, mp3_bytes - i, frame_bytes)) || + (!i && frame_and_padding == mp3_bytes)) + { + *ptr_frame_bytes = frame_and_padding; + return i; + } + *free_format_bytes = 0; + } + } + *ptr_frame_bytes = 0; + return mp3_bytes; +} + +DRMP3_API void drmp3dec_init(drmp3dec *dec) +{ + dec->header[0] = 0; +} + +DRMP3_API int drmp3dec_decode_frame(drmp3dec *dec, const drmp3_uint8 *mp3, int mp3_bytes, void *pcm, drmp3dec_frame_info *info) +{ + int i = 0, igr, frame_size = 0, success = 1; + const drmp3_uint8 *hdr; + drmp3_bs bs_frame[1]; + drmp3dec_scratch scratch; + + if (mp3_bytes > 4 && dec->header[0] == 0xff && drmp3_hdr_compare(dec->header, mp3)) + { + frame_size = drmp3_hdr_frame_bytes(mp3, dec->free_format_bytes) + drmp3_hdr_padding(mp3); + if (frame_size != mp3_bytes && (frame_size + DRMP3_HDR_SIZE > mp3_bytes || !drmp3_hdr_compare(mp3, mp3 + frame_size))) + { + frame_size = 0; + } + } + if (!frame_size) + { + DRMP3_ZERO_MEMORY(dec, sizeof(drmp3dec)); + i = drmp3d_find_frame(mp3, mp3_bytes, &dec->free_format_bytes, &frame_size); + if (!frame_size || i + frame_size > mp3_bytes) + { + info->frame_bytes = i; + return 0; + } + } + + hdr = mp3 + i; + DRMP3_COPY_MEMORY(dec->header, hdr, DRMP3_HDR_SIZE); + info->frame_bytes = i + frame_size; + info->channels = DRMP3_HDR_IS_MONO(hdr) ? 1 : 2; + info->hz = drmp3_hdr_sample_rate_hz(hdr); + info->layer = 4 - DRMP3_HDR_GET_LAYER(hdr); + info->bitrate_kbps = drmp3_hdr_bitrate_kbps(hdr); + + drmp3_bs_init(bs_frame, hdr + DRMP3_HDR_SIZE, frame_size - DRMP3_HDR_SIZE); + if (DRMP3_HDR_IS_CRC(hdr)) + { + drmp3_bs_get_bits(bs_frame, 16); + } + + if (info->layer == 3) + { + int main_data_begin = drmp3_L3_read_side_info(bs_frame, scratch.gr_info, hdr); + if (main_data_begin < 0 || bs_frame->pos > bs_frame->limit) + { + drmp3dec_init(dec); + return 0; + } + success = drmp3_L3_restore_reservoir(dec, bs_frame, &scratch, main_data_begin); + if (success && pcm != NULL) + { + for (igr = 0; igr < (DRMP3_HDR_TEST_MPEG1(hdr) ? 2 : 1); igr++, pcm = DRMP3_OFFSET_PTR(pcm, sizeof(drmp3d_sample_t)*576*info->channels)) + { + DRMP3_ZERO_MEMORY(scratch.grbuf[0], 576*2*sizeof(float)); + drmp3_L3_decode(dec, &scratch, scratch.gr_info + igr*info->channels, info->channels); + drmp3d_synth_granule(dec->qmf_state, scratch.grbuf[0], 18, info->channels, (drmp3d_sample_t*)pcm, scratch.syn[0]); + } + } + drmp3_L3_save_reservoir(dec, &scratch); + } else + { +#ifdef DR_MP3_ONLY_MP3 + return 0; +#else + drmp3_L12_scale_info sci[1]; + + if (pcm == NULL) { + return drmp3_hdr_frame_samples(hdr); + } + + drmp3_L12_read_scale_info(hdr, bs_frame, sci); + + DRMP3_ZERO_MEMORY(scratch.grbuf[0], 576*2*sizeof(float)); + for (i = 0, igr = 0; igr < 3; igr++) + { + if (12 == (i += drmp3_L12_dequantize_granule(scratch.grbuf[0] + i, bs_frame, sci, info->layer | 1))) + { + i = 0; + drmp3_L12_apply_scf_384(sci, sci->scf + igr, scratch.grbuf[0]); + drmp3d_synth_granule(dec->qmf_state, scratch.grbuf[0], 12, info->channels, (drmp3d_sample_t*)pcm, scratch.syn[0]); + DRMP3_ZERO_MEMORY(scratch.grbuf[0], 576*2*sizeof(float)); + pcm = DRMP3_OFFSET_PTR(pcm, sizeof(drmp3d_sample_t)*384*info->channels); + } + if (bs_frame->pos > bs_frame->limit) + { + drmp3dec_init(dec); + return 0; + } + } +#endif + } + + return success*drmp3_hdr_frame_samples(dec->header); +} + +DRMP3_API void drmp3dec_f32_to_s16(const float *in, drmp3_int16 *out, size_t num_samples) +{ + size_t i = 0; +#if DRMP3_HAVE_SIMD + size_t aligned_count = num_samples & ~7; + for(; i < aligned_count; i+=8) + { + drmp3_f4 scale = DRMP3_VSET(32768.0f); + drmp3_f4 a = DRMP3_VMUL(DRMP3_VLD(&in[i ]), scale); + drmp3_f4 b = DRMP3_VMUL(DRMP3_VLD(&in[i+4]), scale); +#if DRMP3_HAVE_SSE + drmp3_f4 s16max = DRMP3_VSET( 32767.0f); + drmp3_f4 s16min = DRMP3_VSET(-32768.0f); + __m128i pcm8 = _mm_packs_epi32(_mm_cvtps_epi32(_mm_max_ps(_mm_min_ps(a, s16max), s16min)), + _mm_cvtps_epi32(_mm_max_ps(_mm_min_ps(b, s16max), s16min))); + out[i ] = (drmp3_int16)_mm_extract_epi16(pcm8, 0); + out[i+1] = (drmp3_int16)_mm_extract_epi16(pcm8, 1); + out[i+2] = (drmp3_int16)_mm_extract_epi16(pcm8, 2); + out[i+3] = (drmp3_int16)_mm_extract_epi16(pcm8, 3); + out[i+4] = (drmp3_int16)_mm_extract_epi16(pcm8, 4); + out[i+5] = (drmp3_int16)_mm_extract_epi16(pcm8, 5); + out[i+6] = (drmp3_int16)_mm_extract_epi16(pcm8, 6); + out[i+7] = (drmp3_int16)_mm_extract_epi16(pcm8, 7); +#else + int16x4_t pcma, pcmb; + a = DRMP3_VADD(a, DRMP3_VSET(0.5f)); + b = DRMP3_VADD(b, DRMP3_VSET(0.5f)); + pcma = vqmovn_s32(vqaddq_s32(vcvtq_s32_f32(a), vreinterpretq_s32_u32(vcltq_f32(a, DRMP3_VSET(0))))); + pcmb = vqmovn_s32(vqaddq_s32(vcvtq_s32_f32(b), vreinterpretq_s32_u32(vcltq_f32(b, DRMP3_VSET(0))))); + vst1_lane_s16(out+i , pcma, 0); + vst1_lane_s16(out+i+1, pcma, 1); + vst1_lane_s16(out+i+2, pcma, 2); + vst1_lane_s16(out+i+3, pcma, 3); + vst1_lane_s16(out+i+4, pcmb, 0); + vst1_lane_s16(out+i+5, pcmb, 1); + vst1_lane_s16(out+i+6, pcmb, 2); + vst1_lane_s16(out+i+7, pcmb, 3); +#endif + } +#endif + for(; i < num_samples; i++) + { + float sample = in[i] * 32768.0f; + if (sample >= 32766.5) + out[i] = (drmp3_int16) 32767; + else if (sample <= -32767.5) + out[i] = (drmp3_int16)-32768; + else + { + short s = (drmp3_int16)(sample + .5f); + s -= (s < 0); /* away from zero, to be compliant */ + out[i] = s; + } + } +} + + + +/************************************************************************************************************************************************************ + + Main Public API + + ************************************************************************************************************************************************************/ +/* SIZE_MAX */ +#if defined(SIZE_MAX) + #define DRMP3_SIZE_MAX SIZE_MAX +#else + #if defined(_WIN64) || defined(_LP64) || defined(__LP64__) + #define DRMP3_SIZE_MAX ((drmp3_uint64)0xFFFFFFFFFFFFFFFF) + #else + #define DRMP3_SIZE_MAX 0xFFFFFFFF + #endif +#endif +/* End SIZE_MAX */ + +/* Options. */ +#ifndef DRMP3_SEEK_LEADING_MP3_FRAMES +#define DRMP3_SEEK_LEADING_MP3_FRAMES 2 +#endif + +#define DRMP3_MIN_DATA_CHUNK_SIZE 16384 + +/* The size in bytes of each chunk of data to read from the MP3 stream. minimp3 recommends at least 16K, but in an attempt to reduce data movement I'm making this slightly larger. */ +#ifndef DRMP3_DATA_CHUNK_SIZE +#define DRMP3_DATA_CHUNK_SIZE (DRMP3_MIN_DATA_CHUNK_SIZE*4) +#endif + + +#define DRMP3_COUNTOF(x) (sizeof(x) / sizeof(x[0])) +#define DRMP3_CLAMP(x, lo, hi) (DRMP3_MAX(lo, DRMP3_MIN(x, hi))) + +#ifndef DRMP3_PI_D +#define DRMP3_PI_D 3.14159265358979323846264 +#endif + +#define DRMP3_DEFAULT_RESAMPLER_LPF_ORDER 2 + +static DRMP3_INLINE float drmp3_mix_f32(float x, float y, float a) +{ + return x*(1-a) + y*a; +} +static DRMP3_INLINE float drmp3_mix_f32_fast(float x, float y, float a) +{ + float r0 = (y - x); + float r1 = r0*a; + return x + r1; + /*return x + (y - x)*a;*/ +} + + +/* +Greatest common factor using Euclid's algorithm iteratively. +*/ +static DRMP3_INLINE drmp3_uint32 drmp3_gcf_u32(drmp3_uint32 a, drmp3_uint32 b) +{ + for (;;) { + if (b == 0) { + break; + } else { + drmp3_uint32 t = a; + a = b; + b = t % a; + } + } + + return a; +} + + +static void* drmp3__malloc_default(size_t sz, void* pUserData) +{ + (void)pUserData; + return DRMP3_MALLOC(sz); +} + +static void* drmp3__realloc_default(void* p, size_t sz, void* pUserData) +{ + (void)pUserData; + return DRMP3_REALLOC(p, sz); +} + +static void drmp3__free_default(void* p, void* pUserData) +{ + (void)pUserData; + DRMP3_FREE(p); +} + + +static void* drmp3__malloc_from_callbacks(size_t sz, const drmp3_allocation_callbacks* pAllocationCallbacks) +{ + if (pAllocationCallbacks == NULL) { + return NULL; + } + + if (pAllocationCallbacks->onMalloc != NULL) { + return pAllocationCallbacks->onMalloc(sz, pAllocationCallbacks->pUserData); + } + + /* Try using realloc(). */ + if (pAllocationCallbacks->onRealloc != NULL) { + return pAllocationCallbacks->onRealloc(NULL, sz, pAllocationCallbacks->pUserData); + } + + return NULL; +} + +static void* drmp3__realloc_from_callbacks(void* p, size_t szNew, size_t szOld, const drmp3_allocation_callbacks* pAllocationCallbacks) +{ + if (pAllocationCallbacks == NULL) { + return NULL; + } + + if (pAllocationCallbacks->onRealloc != NULL) { + return pAllocationCallbacks->onRealloc(p, szNew, pAllocationCallbacks->pUserData); + } + + /* Try emulating realloc() in terms of malloc()/free(). */ + if (pAllocationCallbacks->onMalloc != NULL && pAllocationCallbacks->onFree != NULL) { + void* p2; + + p2 = pAllocationCallbacks->onMalloc(szNew, pAllocationCallbacks->pUserData); + if (p2 == NULL) { + return NULL; + } + + if (p != NULL) { + DRMP3_COPY_MEMORY(p2, p, szOld); + pAllocationCallbacks->onFree(p, pAllocationCallbacks->pUserData); + } + + return p2; + } + + return NULL; +} + +static void drmp3__free_from_callbacks(void* p, const drmp3_allocation_callbacks* pAllocationCallbacks) +{ + if (p == NULL || pAllocationCallbacks == NULL) { + return; + } + + if (pAllocationCallbacks->onFree != NULL) { + pAllocationCallbacks->onFree(p, pAllocationCallbacks->pUserData); + } +} + + +static drmp3_allocation_callbacks drmp3_copy_allocation_callbacks_or_defaults(const drmp3_allocation_callbacks* pAllocationCallbacks) +{ + if (pAllocationCallbacks != NULL) { + /* Copy. */ + return *pAllocationCallbacks; + } else { + /* Defaults. */ + drmp3_allocation_callbacks allocationCallbacks; + allocationCallbacks.pUserData = NULL; + allocationCallbacks.onMalloc = drmp3__malloc_default; + allocationCallbacks.onRealloc = drmp3__realloc_default; + allocationCallbacks.onFree = drmp3__free_default; + return allocationCallbacks; + } +} + + + +static size_t drmp3__on_read(drmp3* pMP3, void* pBufferOut, size_t bytesToRead) +{ + size_t bytesRead = pMP3->onRead(pMP3->pUserData, pBufferOut, bytesToRead); + pMP3->streamCursor += bytesRead; + return bytesRead; +} + +static drmp3_bool32 drmp3__on_seek(drmp3* pMP3, int offset, drmp3_seek_origin origin) +{ + DRMP3_ASSERT(offset >= 0); + + if (!pMP3->onSeek(pMP3->pUserData, offset, origin)) { + return DRMP3_FALSE; + } + + if (origin == drmp3_seek_origin_start) { + pMP3->streamCursor = (drmp3_uint64)offset; + } else { + pMP3->streamCursor += offset; + } + + return DRMP3_TRUE; +} + +static drmp3_bool32 drmp3__on_seek_64(drmp3* pMP3, drmp3_uint64 offset, drmp3_seek_origin origin) +{ + if (offset <= 0x7FFFFFFF) { + return drmp3__on_seek(pMP3, (int)offset, origin); + } + + + /* Getting here "offset" is too large for a 32-bit integer. We just keep seeking forward until we hit the offset. */ + if (!drmp3__on_seek(pMP3, 0x7FFFFFFF, drmp3_seek_origin_start)) { + return DRMP3_FALSE; + } + + offset -= 0x7FFFFFFF; + while (offset > 0) { + if (offset <= 0x7FFFFFFF) { + if (!drmp3__on_seek(pMP3, (int)offset, drmp3_seek_origin_current)) { + return DRMP3_FALSE; + } + offset = 0; + } else { + if (!drmp3__on_seek(pMP3, 0x7FFFFFFF, drmp3_seek_origin_current)) { + return DRMP3_FALSE; + } + offset -= 0x7FFFFFFF; + } + } + + return DRMP3_TRUE; +} + + +static drmp3_uint32 drmp3_decode_next_frame_ex__callbacks(drmp3* pMP3, drmp3d_sample_t* pPCMFrames) +{ + drmp3_uint32 pcmFramesRead = 0; + + DRMP3_ASSERT(pMP3 != NULL); + DRMP3_ASSERT(pMP3->onRead != NULL); + + if (pMP3->atEnd) { + return 0; + } + + for (;;) { + drmp3dec_frame_info info; + + /* minimp3 recommends doing data submission in chunks of at least 16K. If we don't have at least 16K bytes available, get more. */ + if (pMP3->dataSize < DRMP3_MIN_DATA_CHUNK_SIZE) { + size_t bytesRead; + + /* First we need to move the data down. */ + if (pMP3->pData != NULL) { + DRMP3_MOVE_MEMORY(pMP3->pData, pMP3->pData + pMP3->dataConsumed, pMP3->dataSize); + } + + pMP3->dataConsumed = 0; + + if (pMP3->dataCapacity < DRMP3_DATA_CHUNK_SIZE) { + drmp3_uint8* pNewData; + size_t newDataCap; + + newDataCap = DRMP3_DATA_CHUNK_SIZE; + + pNewData = (drmp3_uint8*)drmp3__realloc_from_callbacks(pMP3->pData, newDataCap, pMP3->dataCapacity, &pMP3->allocationCallbacks); + if (pNewData == NULL) { + return 0; /* Out of memory. */ + } + + pMP3->pData = pNewData; + pMP3->dataCapacity = newDataCap; + } + + bytesRead = drmp3__on_read(pMP3, pMP3->pData + pMP3->dataSize, (pMP3->dataCapacity - pMP3->dataSize)); + if (bytesRead == 0) { + if (pMP3->dataSize == 0) { + pMP3->atEnd = DRMP3_TRUE; + return 0; /* No data. */ + } + } + + pMP3->dataSize += bytesRead; + } + + if (pMP3->dataSize > INT_MAX) { + pMP3->atEnd = DRMP3_TRUE; + return 0; /* File too big. */ + } + + DRMP3_ASSERT(pMP3->pData != NULL); + DRMP3_ASSERT(pMP3->dataCapacity > 0); + + /* Do a runtime check here to try silencing a false-positive from clang-analyzer. */ + if (pMP3->pData == NULL) { + return 0; + } + + pcmFramesRead = drmp3dec_decode_frame(&pMP3->decoder, pMP3->pData + pMP3->dataConsumed, (int)pMP3->dataSize, pPCMFrames, &info); /* <-- Safe size_t -> int conversion thanks to the check above. */ + + /* Consume the data. */ + if (info.frame_bytes > 0) { + pMP3->dataConsumed += (size_t)info.frame_bytes; + pMP3->dataSize -= (size_t)info.frame_bytes; + } + + /* pcmFramesRead will be equal to 0 if decoding failed. If it is zero and info.frame_bytes > 0 then we have successfully decoded the frame. */ + if (pcmFramesRead > 0) { + pcmFramesRead = drmp3_hdr_frame_samples(pMP3->decoder.header); + pMP3->pcmFramesConsumedInMP3Frame = 0; + pMP3->pcmFramesRemainingInMP3Frame = pcmFramesRead; + pMP3->mp3FrameChannels = info.channels; + pMP3->mp3FrameSampleRate = info.hz; + break; + } else if (info.frame_bytes == 0) { + /* Need more data. minimp3 recommends doing data submission in 16K chunks. */ + size_t bytesRead; + + /* First we need to move the data down. */ + DRMP3_MOVE_MEMORY(pMP3->pData, pMP3->pData + pMP3->dataConsumed, pMP3->dataSize); + pMP3->dataConsumed = 0; + + if (pMP3->dataCapacity == pMP3->dataSize) { + /* No room. Expand. */ + drmp3_uint8* pNewData; + size_t newDataCap; + + newDataCap = pMP3->dataCapacity + DRMP3_DATA_CHUNK_SIZE; + + pNewData = (drmp3_uint8*)drmp3__realloc_from_callbacks(pMP3->pData, newDataCap, pMP3->dataCapacity, &pMP3->allocationCallbacks); + if (pNewData == NULL) { + return 0; /* Out of memory. */ + } + + pMP3->pData = pNewData; + pMP3->dataCapacity = newDataCap; + } + + /* Fill in a chunk. */ + bytesRead = drmp3__on_read(pMP3, pMP3->pData + pMP3->dataSize, (pMP3->dataCapacity - pMP3->dataSize)); + if (bytesRead == 0) { + pMP3->atEnd = DRMP3_TRUE; + return 0; /* Error reading more data. */ + } + + pMP3->dataSize += bytesRead; + } + }; + + return pcmFramesRead; +} + +static drmp3_uint32 drmp3_decode_next_frame_ex__memory(drmp3* pMP3, drmp3d_sample_t* pPCMFrames) +{ + drmp3_uint32 pcmFramesRead = 0; + drmp3dec_frame_info info; + + DRMP3_ASSERT(pMP3 != NULL); + DRMP3_ASSERT(pMP3->memory.pData != NULL); + + if (pMP3->atEnd) { + return 0; + } + + for (;;) { + pcmFramesRead = drmp3dec_decode_frame(&pMP3->decoder, pMP3->memory.pData + pMP3->memory.currentReadPos, (int)(pMP3->memory.dataSize - pMP3->memory.currentReadPos), pPCMFrames, &info); + if (pcmFramesRead > 0) { + pcmFramesRead = drmp3_hdr_frame_samples(pMP3->decoder.header); + pMP3->pcmFramesConsumedInMP3Frame = 0; + pMP3->pcmFramesRemainingInMP3Frame = pcmFramesRead; + pMP3->mp3FrameChannels = info.channels; + pMP3->mp3FrameSampleRate = info.hz; + break; + } else if (info.frame_bytes > 0) { + /* No frames were read, but it looks like we skipped past one. Read the next MP3 frame. */ + pMP3->memory.currentReadPos += (size_t)info.frame_bytes; + } else { + /* Nothing at all was read. Abort. */ + break; + } + } + + /* Consume the data. */ + pMP3->memory.currentReadPos += (size_t)info.frame_bytes; + + return pcmFramesRead; +} + +static drmp3_uint32 drmp3_decode_next_frame_ex(drmp3* pMP3, drmp3d_sample_t* pPCMFrames) +{ + if (pMP3->memory.pData != NULL && pMP3->memory.dataSize > 0) { + return drmp3_decode_next_frame_ex__memory(pMP3, pPCMFrames); + } else { + return drmp3_decode_next_frame_ex__callbacks(pMP3, pPCMFrames); + } +} + +static drmp3_uint32 drmp3_decode_next_frame(drmp3* pMP3) +{ + DRMP3_ASSERT(pMP3 != NULL); + return drmp3_decode_next_frame_ex(pMP3, (drmp3d_sample_t*)pMP3->pcmFrames); +} + +#if 0 +static drmp3_uint32 drmp3_seek_next_frame(drmp3* pMP3) +{ + drmp3_uint32 pcmFrameCount; + + DRMP3_ASSERT(pMP3 != NULL); + + pcmFrameCount = drmp3_decode_next_frame_ex(pMP3, NULL); + if (pcmFrameCount == 0) { + return 0; + } + + /* We have essentially just skipped past the frame, so just set the remaining samples to 0. */ + pMP3->currentPCMFrame += pcmFrameCount; + pMP3->pcmFramesConsumedInMP3Frame = pcmFrameCount; + pMP3->pcmFramesRemainingInMP3Frame = 0; + + return pcmFrameCount; +} +#endif + +static drmp3_bool32 drmp3_init_internal(drmp3* pMP3, drmp3_read_proc onRead, drmp3_seek_proc onSeek, void* pUserData, const drmp3_allocation_callbacks* pAllocationCallbacks) +{ + DRMP3_ASSERT(pMP3 != NULL); + DRMP3_ASSERT(onRead != NULL); + + /* This function assumes the output object has already been reset to 0. Do not do that here, otherwise things will break. */ + drmp3dec_init(&pMP3->decoder); + + pMP3->onRead = onRead; + pMP3->onSeek = onSeek; + pMP3->pUserData = pUserData; + pMP3->allocationCallbacks = drmp3_copy_allocation_callbacks_or_defaults(pAllocationCallbacks); + + if (pMP3->allocationCallbacks.onFree == NULL || (pMP3->allocationCallbacks.onMalloc == NULL && pMP3->allocationCallbacks.onRealloc == NULL)) { + return DRMP3_FALSE; /* Invalid allocation callbacks. */ + } + + /* Decode the first frame to confirm that it is indeed a valid MP3 stream. */ + if (drmp3_decode_next_frame(pMP3) == 0) { + drmp3__free_from_callbacks(pMP3->pData, &pMP3->allocationCallbacks); /* The call above may have allocated memory. Need to make sure it's freed before aborting. */ + return DRMP3_FALSE; /* Not a valid MP3 stream. */ + } + + pMP3->channels = pMP3->mp3FrameChannels; + pMP3->sampleRate = pMP3->mp3FrameSampleRate; + + return DRMP3_TRUE; +} + +DRMP3_API drmp3_bool32 drmp3_init(drmp3* pMP3, drmp3_read_proc onRead, drmp3_seek_proc onSeek, void* pUserData, const drmp3_allocation_callbacks* pAllocationCallbacks) +{ + if (pMP3 == NULL || onRead == NULL) { + return DRMP3_FALSE; + } + + DRMP3_ZERO_OBJECT(pMP3); + return drmp3_init_internal(pMP3, onRead, onSeek, pUserData, pAllocationCallbacks); +} + + +static size_t drmp3__on_read_memory(void* pUserData, void* pBufferOut, size_t bytesToRead) +{ + drmp3* pMP3 = (drmp3*)pUserData; + size_t bytesRemaining; + + DRMP3_ASSERT(pMP3 != NULL); + DRMP3_ASSERT(pMP3->memory.dataSize >= pMP3->memory.currentReadPos); + + bytesRemaining = pMP3->memory.dataSize - pMP3->memory.currentReadPos; + if (bytesToRead > bytesRemaining) { + bytesToRead = bytesRemaining; + } + + if (bytesToRead > 0) { + DRMP3_COPY_MEMORY(pBufferOut, pMP3->memory.pData + pMP3->memory.currentReadPos, bytesToRead); + pMP3->memory.currentReadPos += bytesToRead; + } + + return bytesToRead; +} + +static drmp3_bool32 drmp3__on_seek_memory(void* pUserData, int byteOffset, drmp3_seek_origin origin) +{ + drmp3* pMP3 = (drmp3*)pUserData; + + DRMP3_ASSERT(pMP3 != NULL); + + if (origin == drmp3_seek_origin_current) { + if (byteOffset > 0) { + if (pMP3->memory.currentReadPos + byteOffset > pMP3->memory.dataSize) { + byteOffset = (int)(pMP3->memory.dataSize - pMP3->memory.currentReadPos); /* Trying to seek too far forward. */ + } + } else { + if (pMP3->memory.currentReadPos < (size_t)-byteOffset) { + byteOffset = -(int)pMP3->memory.currentReadPos; /* Trying to seek too far backwards. */ + } + } + + /* This will never underflow thanks to the clamps above. */ + pMP3->memory.currentReadPos += byteOffset; + } else { + if ((drmp3_uint32)byteOffset <= pMP3->memory.dataSize) { + pMP3->memory.currentReadPos = byteOffset; + } else { + pMP3->memory.currentReadPos = pMP3->memory.dataSize; /* Trying to seek too far forward. */ + } + } + + return DRMP3_TRUE; +} + +DRMP3_API drmp3_bool32 drmp3_init_memory(drmp3* pMP3, const void* pData, size_t dataSize, const drmp3_allocation_callbacks* pAllocationCallbacks) +{ + if (pMP3 == NULL) { + return DRMP3_FALSE; + } + + DRMP3_ZERO_OBJECT(pMP3); + + if (pData == NULL || dataSize == 0) { + return DRMP3_FALSE; + } + + pMP3->memory.pData = (const drmp3_uint8*)pData; + pMP3->memory.dataSize = dataSize; + pMP3->memory.currentReadPos = 0; + + return drmp3_init_internal(pMP3, drmp3__on_read_memory, drmp3__on_seek_memory, pMP3, pAllocationCallbacks); +} + + +#ifndef DR_MP3_NO_STDIO +#include +#include /* For wcslen(), wcsrtombs() */ + +/* Errno */ +/* drmp3_result_from_errno() is only used inside DR_MP3_NO_STDIO for now. Move this out if it's ever used elsewhere. */ +#include +static drmp3_result drmp3_result_from_errno(int e) +{ + switch (e) + { + case 0: return DRMP3_SUCCESS; + #ifdef EPERM + case EPERM: return DRMP3_INVALID_OPERATION; + #endif + #ifdef ENOENT + case ENOENT: return DRMP3_DOES_NOT_EXIST; + #endif + #ifdef ESRCH + case ESRCH: return DRMP3_DOES_NOT_EXIST; + #endif + #ifdef EINTR + case EINTR: return DRMP3_INTERRUPT; + #endif + #ifdef EIO + case EIO: return DRMP3_IO_ERROR; + #endif + #ifdef ENXIO + case ENXIO: return DRMP3_DOES_NOT_EXIST; + #endif + #ifdef E2BIG + case E2BIG: return DRMP3_INVALID_ARGS; + #endif + #ifdef ENOEXEC + case ENOEXEC: return DRMP3_INVALID_FILE; + #endif + #ifdef EBADF + case EBADF: return DRMP3_INVALID_FILE; + #endif + #ifdef ECHILD + case ECHILD: return DRMP3_ERROR; + #endif + #ifdef EAGAIN + case EAGAIN: return DRMP3_UNAVAILABLE; + #endif + #ifdef ENOMEM + case ENOMEM: return DRMP3_OUT_OF_MEMORY; + #endif + #ifdef EACCES + case EACCES: return DRMP3_ACCESS_DENIED; + #endif + #ifdef EFAULT + case EFAULT: return DRMP3_BAD_ADDRESS; + #endif + #ifdef ENOTBLK + case ENOTBLK: return DRMP3_ERROR; + #endif + #ifdef EBUSY + case EBUSY: return DRMP3_BUSY; + #endif + #ifdef EEXIST + case EEXIST: return DRMP3_ALREADY_EXISTS; + #endif + #ifdef EXDEV + case EXDEV: return DRMP3_ERROR; + #endif + #ifdef ENODEV + case ENODEV: return DRMP3_DOES_NOT_EXIST; + #endif + #ifdef ENOTDIR + case ENOTDIR: return DRMP3_NOT_DIRECTORY; + #endif + #ifdef EISDIR + case EISDIR: return DRMP3_IS_DIRECTORY; + #endif + #ifdef EINVAL + case EINVAL: return DRMP3_INVALID_ARGS; + #endif + #ifdef ENFILE + case ENFILE: return DRMP3_TOO_MANY_OPEN_FILES; + #endif + #ifdef EMFILE + case EMFILE: return DRMP3_TOO_MANY_OPEN_FILES; + #endif + #ifdef ENOTTY + case ENOTTY: return DRMP3_INVALID_OPERATION; + #endif + #ifdef ETXTBSY + case ETXTBSY: return DRMP3_BUSY; + #endif + #ifdef EFBIG + case EFBIG: return DRMP3_TOO_BIG; + #endif + #ifdef ENOSPC + case ENOSPC: return DRMP3_NO_SPACE; + #endif + #ifdef ESPIPE + case ESPIPE: return DRMP3_BAD_SEEK; + #endif + #ifdef EROFS + case EROFS: return DRMP3_ACCESS_DENIED; + #endif + #ifdef EMLINK + case EMLINK: return DRMP3_TOO_MANY_LINKS; + #endif + #ifdef EPIPE + case EPIPE: return DRMP3_BAD_PIPE; + #endif + #ifdef EDOM + case EDOM: return DRMP3_OUT_OF_RANGE; + #endif + #ifdef ERANGE + case ERANGE: return DRMP3_OUT_OF_RANGE; + #endif + #ifdef EDEADLK + case EDEADLK: return DRMP3_DEADLOCK; + #endif + #ifdef ENAMETOOLONG + case ENAMETOOLONG: return DRMP3_PATH_TOO_LONG; + #endif + #ifdef ENOLCK + case ENOLCK: return DRMP3_ERROR; + #endif + #ifdef ENOSYS + case ENOSYS: return DRMP3_NOT_IMPLEMENTED; + #endif + #ifdef ENOTEMPTY + case ENOTEMPTY: return DRMP3_DIRECTORY_NOT_EMPTY; + #endif + #ifdef ELOOP + case ELOOP: return DRMP3_TOO_MANY_LINKS; + #endif + #ifdef ENOMSG + case ENOMSG: return DRMP3_NO_MESSAGE; + #endif + #ifdef EIDRM + case EIDRM: return DRMP3_ERROR; + #endif + #ifdef ECHRNG + case ECHRNG: return DRMP3_ERROR; + #endif + #ifdef EL2NSYNC + case EL2NSYNC: return DRMP3_ERROR; + #endif + #ifdef EL3HLT + case EL3HLT: return DRMP3_ERROR; + #endif + #ifdef EL3RST + case EL3RST: return DRMP3_ERROR; + #endif + #ifdef ELNRNG + case ELNRNG: return DRMP3_OUT_OF_RANGE; + #endif + #ifdef EUNATCH + case EUNATCH: return DRMP3_ERROR; + #endif + #ifdef ENOCSI + case ENOCSI: return DRMP3_ERROR; + #endif + #ifdef EL2HLT + case EL2HLT: return DRMP3_ERROR; + #endif + #ifdef EBADE + case EBADE: return DRMP3_ERROR; + #endif + #ifdef EBADR + case EBADR: return DRMP3_ERROR; + #endif + #ifdef EXFULL + case EXFULL: return DRMP3_ERROR; + #endif + #ifdef ENOANO + case ENOANO: return DRMP3_ERROR; + #endif + #ifdef EBADRQC + case EBADRQC: return DRMP3_ERROR; + #endif + #ifdef EBADSLT + case EBADSLT: return DRMP3_ERROR; + #endif + #ifdef EBFONT + case EBFONT: return DRMP3_INVALID_FILE; + #endif + #ifdef ENOSTR + case ENOSTR: return DRMP3_ERROR; + #endif + #ifdef ENODATA + case ENODATA: return DRMP3_NO_DATA_AVAILABLE; + #endif + #ifdef ETIME + case ETIME: return DRMP3_TIMEOUT; + #endif + #ifdef ENOSR + case ENOSR: return DRMP3_NO_DATA_AVAILABLE; + #endif + #ifdef ENONET + case ENONET: return DRMP3_NO_NETWORK; + #endif + #ifdef ENOPKG + case ENOPKG: return DRMP3_ERROR; + #endif + #ifdef EREMOTE + case EREMOTE: return DRMP3_ERROR; + #endif + #ifdef ENOLINK + case ENOLINK: return DRMP3_ERROR; + #endif + #ifdef EADV + case EADV: return DRMP3_ERROR; + #endif + #ifdef ESRMNT + case ESRMNT: return DRMP3_ERROR; + #endif + #ifdef ECOMM + case ECOMM: return DRMP3_ERROR; + #endif + #ifdef EPROTO + case EPROTO: return DRMP3_ERROR; + #endif + #ifdef EMULTIHOP + case EMULTIHOP: return DRMP3_ERROR; + #endif + #ifdef EDOTDOT + case EDOTDOT: return DRMP3_ERROR; + #endif + #ifdef EBADMSG + case EBADMSG: return DRMP3_BAD_MESSAGE; + #endif + #ifdef EOVERFLOW + case EOVERFLOW: return DRMP3_TOO_BIG; + #endif + #ifdef ENOTUNIQ + case ENOTUNIQ: return DRMP3_NOT_UNIQUE; + #endif + #ifdef EBADFD + case EBADFD: return DRMP3_ERROR; + #endif + #ifdef EREMCHG + case EREMCHG: return DRMP3_ERROR; + #endif + #ifdef ELIBACC + case ELIBACC: return DRMP3_ACCESS_DENIED; + #endif + #ifdef ELIBBAD + case ELIBBAD: return DRMP3_INVALID_FILE; + #endif + #ifdef ELIBSCN + case ELIBSCN: return DRMP3_INVALID_FILE; + #endif + #ifdef ELIBMAX + case ELIBMAX: return DRMP3_ERROR; + #endif + #ifdef ELIBEXEC + case ELIBEXEC: return DRMP3_ERROR; + #endif + #ifdef EILSEQ + case EILSEQ: return DRMP3_INVALID_DATA; + #endif + #ifdef ERESTART + case ERESTART: return DRMP3_ERROR; + #endif + #ifdef ESTRPIPE + case ESTRPIPE: return DRMP3_ERROR; + #endif + #ifdef EUSERS + case EUSERS: return DRMP3_ERROR; + #endif + #ifdef ENOTSOCK + case ENOTSOCK: return DRMP3_NOT_SOCKET; + #endif + #ifdef EDESTADDRREQ + case EDESTADDRREQ: return DRMP3_NO_ADDRESS; + #endif + #ifdef EMSGSIZE + case EMSGSIZE: return DRMP3_TOO_BIG; + #endif + #ifdef EPROTOTYPE + case EPROTOTYPE: return DRMP3_BAD_PROTOCOL; + #endif + #ifdef ENOPROTOOPT + case ENOPROTOOPT: return DRMP3_PROTOCOL_UNAVAILABLE; + #endif + #ifdef EPROTONOSUPPORT + case EPROTONOSUPPORT: return DRMP3_PROTOCOL_NOT_SUPPORTED; + #endif + #ifdef ESOCKTNOSUPPORT + case ESOCKTNOSUPPORT: return DRMP3_SOCKET_NOT_SUPPORTED; + #endif + #ifdef EOPNOTSUPP + case EOPNOTSUPP: return DRMP3_INVALID_OPERATION; + #endif + #ifdef EPFNOSUPPORT + case EPFNOSUPPORT: return DRMP3_PROTOCOL_FAMILY_NOT_SUPPORTED; + #endif + #ifdef EAFNOSUPPORT + case EAFNOSUPPORT: return DRMP3_ADDRESS_FAMILY_NOT_SUPPORTED; + #endif + #ifdef EADDRINUSE + case EADDRINUSE: return DRMP3_ALREADY_IN_USE; + #endif + #ifdef EADDRNOTAVAIL + case EADDRNOTAVAIL: return DRMP3_ERROR; + #endif + #ifdef ENETDOWN + case ENETDOWN: return DRMP3_NO_NETWORK; + #endif + #ifdef ENETUNREACH + case ENETUNREACH: return DRMP3_NO_NETWORK; + #endif + #ifdef ENETRESET + case ENETRESET: return DRMP3_NO_NETWORK; + #endif + #ifdef ECONNABORTED + case ECONNABORTED: return DRMP3_NO_NETWORK; + #endif + #ifdef ECONNRESET + case ECONNRESET: return DRMP3_CONNECTION_RESET; + #endif + #ifdef ENOBUFS + case ENOBUFS: return DRMP3_NO_SPACE; + #endif + #ifdef EISCONN + case EISCONN: return DRMP3_ALREADY_CONNECTED; + #endif + #ifdef ENOTCONN + case ENOTCONN: return DRMP3_NOT_CONNECTED; + #endif + #ifdef ESHUTDOWN + case ESHUTDOWN: return DRMP3_ERROR; + #endif + #ifdef ETOOMANYREFS + case ETOOMANYREFS: return DRMP3_ERROR; + #endif + #ifdef ETIMEDOUT + case ETIMEDOUT: return DRMP3_TIMEOUT; + #endif + #ifdef ECONNREFUSED + case ECONNREFUSED: return DRMP3_CONNECTION_REFUSED; + #endif + #ifdef EHOSTDOWN + case EHOSTDOWN: return DRMP3_NO_HOST; + #endif + #ifdef EHOSTUNREACH + case EHOSTUNREACH: return DRMP3_NO_HOST; + #endif + #ifdef EALREADY + case EALREADY: return DRMP3_IN_PROGRESS; + #endif + #ifdef EINPROGRESS + case EINPROGRESS: return DRMP3_IN_PROGRESS; + #endif + #ifdef ESTALE + case ESTALE: return DRMP3_INVALID_FILE; + #endif + #ifdef EUCLEAN + case EUCLEAN: return DRMP3_ERROR; + #endif + #ifdef ENOTNAM + case ENOTNAM: return DRMP3_ERROR; + #endif + #ifdef ENAVAIL + case ENAVAIL: return DRMP3_ERROR; + #endif + #ifdef EISNAM + case EISNAM: return DRMP3_ERROR; + #endif + #ifdef EREMOTEIO + case EREMOTEIO: return DRMP3_IO_ERROR; + #endif + #ifdef EDQUOT + case EDQUOT: return DRMP3_NO_SPACE; + #endif + #ifdef ENOMEDIUM + case ENOMEDIUM: return DRMP3_DOES_NOT_EXIST; + #endif + #ifdef EMEDIUMTYPE + case EMEDIUMTYPE: return DRMP3_ERROR; + #endif + #ifdef ECANCELED + case ECANCELED: return DRMP3_CANCELLED; + #endif + #ifdef ENOKEY + case ENOKEY: return DRMP3_ERROR; + #endif + #ifdef EKEYEXPIRED + case EKEYEXPIRED: return DRMP3_ERROR; + #endif + #ifdef EKEYREVOKED + case EKEYREVOKED: return DRMP3_ERROR; + #endif + #ifdef EKEYREJECTED + case EKEYREJECTED: return DRMP3_ERROR; + #endif + #ifdef EOWNERDEAD + case EOWNERDEAD: return DRMP3_ERROR; + #endif + #ifdef ENOTRECOVERABLE + case ENOTRECOVERABLE: return DRMP3_ERROR; + #endif + #ifdef ERFKILL + case ERFKILL: return DRMP3_ERROR; + #endif + #ifdef EHWPOISON + case EHWPOISON: return DRMP3_ERROR; + #endif + default: return DRMP3_ERROR; + } +} +/* End Errno */ + +/* fopen */ +static drmp3_result drmp3_fopen(FILE** ppFile, const char* pFilePath, const char* pOpenMode) +{ +#if defined(_MSC_VER) && _MSC_VER >= 1400 + errno_t err; +#endif + + if (ppFile != NULL) { + *ppFile = NULL; /* Safety. */ + } + + if (pFilePath == NULL || pOpenMode == NULL || ppFile == NULL) { + return DRMP3_INVALID_ARGS; + } + +#if defined(_MSC_VER) && _MSC_VER >= 1400 + err = fopen_s(ppFile, pFilePath, pOpenMode); + if (err != 0) { + return drmp3_result_from_errno(err); + } +#else +#if defined(_WIN32) || defined(__APPLE__) + *ppFile = fopen(pFilePath, pOpenMode); +#else + #if defined(_FILE_OFFSET_BITS) && _FILE_OFFSET_BITS == 64 && defined(_LARGEFILE64_SOURCE) + *ppFile = fopen64(pFilePath, pOpenMode); + #else + *ppFile = fopen(pFilePath, pOpenMode); + #endif +#endif + if (*ppFile == NULL) { + drmp3_result result = drmp3_result_from_errno(errno); + if (result == DRMP3_SUCCESS) { + result = DRMP3_ERROR; /* Just a safety check to make sure we never ever return success when pFile == NULL. */ + } + + return result; + } +#endif + + return DRMP3_SUCCESS; +} + +/* +_wfopen() isn't always available in all compilation environments. + + * Windows only. + * MSVC seems to support it universally as far back as VC6 from what I can tell (haven't checked further back). + * MinGW-64 (both 32- and 64-bit) seems to support it. + * MinGW wraps it in !defined(__STRICT_ANSI__). + * OpenWatcom wraps it in !defined(_NO_EXT_KEYS). + +This can be reviewed as compatibility issues arise. The preference is to use _wfopen_s() and _wfopen() as opposed to the wcsrtombs() +fallback, so if you notice your compiler not detecting this properly I'm happy to look at adding support. +*/ +#if defined(_WIN32) + #if defined(_MSC_VER) || defined(__MINGW64__) || (!defined(__STRICT_ANSI__) && !defined(_NO_EXT_KEYS)) + #define DRMP3_HAS_WFOPEN + #endif +#endif + +static drmp3_result drmp3_wfopen(FILE** ppFile, const wchar_t* pFilePath, const wchar_t* pOpenMode, const drmp3_allocation_callbacks* pAllocationCallbacks) +{ + if (ppFile != NULL) { + *ppFile = NULL; /* Safety. */ + } + + if (pFilePath == NULL || pOpenMode == NULL || ppFile == NULL) { + return DRMP3_INVALID_ARGS; + } + +#if defined(DRMP3_HAS_WFOPEN) + { + /* Use _wfopen() on Windows. */ + #if defined(_MSC_VER) && _MSC_VER >= 1400 + errno_t err = _wfopen_s(ppFile, pFilePath, pOpenMode); + if (err != 0) { + return drmp3_result_from_errno(err); + } + #else + *ppFile = _wfopen(pFilePath, pOpenMode); + if (*ppFile == NULL) { + return drmp3_result_from_errno(errno); + } + #endif + (void)pAllocationCallbacks; + } +#else + /* + Use fopen() on anything other than Windows. Requires a conversion. This is annoying because + fopen() is locale specific. The only real way I can think of to do this is with wcsrtombs(). Note + that wcstombs() is apparently not thread-safe because it uses a static global mbstate_t object for + maintaining state. I've checked this with -std=c89 and it works, but if somebody get's a compiler + error I'll look into improving compatibility. + */ + + /* + Some compilers don't support wchar_t or wcsrtombs() which we're using below. In this case we just + need to abort with an error. If you encounter a compiler lacking such support, add it to this list + and submit a bug report and it'll be added to the library upstream. + */ + #if defined(__DJGPP__) + { + /* Nothing to do here. This will fall through to the error check below. */ + } + #else + { + mbstate_t mbs; + size_t lenMB; + const wchar_t* pFilePathTemp = pFilePath; + char* pFilePathMB = NULL; + char pOpenModeMB[32] = {0}; + + /* Get the length first. */ + DRMP3_ZERO_OBJECT(&mbs); + lenMB = wcsrtombs(NULL, &pFilePathTemp, 0, &mbs); + if (lenMB == (size_t)-1) { + return drmp3_result_from_errno(errno); + } + + pFilePathMB = (char*)drmp3__malloc_from_callbacks(lenMB + 1, pAllocationCallbacks); + if (pFilePathMB == NULL) { + return DRMP3_OUT_OF_MEMORY; + } + + pFilePathTemp = pFilePath; + DRMP3_ZERO_OBJECT(&mbs); + wcsrtombs(pFilePathMB, &pFilePathTemp, lenMB + 1, &mbs); + + /* The open mode should always consist of ASCII characters so we should be able to do a trivial conversion. */ + { + size_t i = 0; + for (;;) { + if (pOpenMode[i] == 0) { + pOpenModeMB[i] = '\0'; + break; + } + + pOpenModeMB[i] = (char)pOpenMode[i]; + i += 1; + } + } + + *ppFile = fopen(pFilePathMB, pOpenModeMB); + + drmp3__free_from_callbacks(pFilePathMB, pAllocationCallbacks); + } + #endif + + if (*ppFile == NULL) { + return DRMP3_ERROR; + } +#endif + + return DRMP3_SUCCESS; +} +/* End fopen */ + + +static size_t drmp3__on_read_stdio(void* pUserData, void* pBufferOut, size_t bytesToRead) +{ + return fread(pBufferOut, 1, bytesToRead, (FILE*)pUserData); +} + +static drmp3_bool32 drmp3__on_seek_stdio(void* pUserData, int offset, drmp3_seek_origin origin) +{ + return fseek((FILE*)pUserData, offset, (origin == drmp3_seek_origin_current) ? SEEK_CUR : SEEK_SET) == 0; +} + +DRMP3_API drmp3_bool32 drmp3_init_file(drmp3* pMP3, const char* pFilePath, const drmp3_allocation_callbacks* pAllocationCallbacks) +{ + drmp3_bool32 result; + FILE* pFile; + + if (drmp3_fopen(&pFile, pFilePath, "rb") != DRMP3_SUCCESS) { + return DRMP3_FALSE; + } + + result = drmp3_init(pMP3, drmp3__on_read_stdio, drmp3__on_seek_stdio, (void*)pFile, pAllocationCallbacks); + if (result != DRMP3_TRUE) { + fclose(pFile); + return result; + } + + return DRMP3_TRUE; +} + +DRMP3_API drmp3_bool32 drmp3_init_file_w(drmp3* pMP3, const wchar_t* pFilePath, const drmp3_allocation_callbacks* pAllocationCallbacks) +{ + drmp3_bool32 result; + FILE* pFile; + + if (drmp3_wfopen(&pFile, pFilePath, L"rb", pAllocationCallbacks) != DRMP3_SUCCESS) { + return DRMP3_FALSE; + } + + result = drmp3_init(pMP3, drmp3__on_read_stdio, drmp3__on_seek_stdio, (void*)pFile, pAllocationCallbacks); + if (result != DRMP3_TRUE) { + fclose(pFile); + return result; + } + + return DRMP3_TRUE; +} +#endif + +DRMP3_API void drmp3_uninit(drmp3* pMP3) +{ + if (pMP3 == NULL) { + return; + } + +#ifndef DR_MP3_NO_STDIO + if (pMP3->onRead == drmp3__on_read_stdio) { + FILE* pFile = (FILE*)pMP3->pUserData; + if (pFile != NULL) { + fclose(pFile); + pMP3->pUserData = NULL; /* Make sure the file handle is cleared to NULL to we don't attempt to close it a second time. */ + } + } +#endif + + drmp3__free_from_callbacks(pMP3->pData, &pMP3->allocationCallbacks); +} + +#if defined(DR_MP3_FLOAT_OUTPUT) +static void drmp3_f32_to_s16(drmp3_int16* dst, const float* src, drmp3_uint64 sampleCount) +{ + drmp3_uint64 i; + drmp3_uint64 i4; + drmp3_uint64 sampleCount4; + + /* Unrolled. */ + i = 0; + sampleCount4 = sampleCount >> 2; + for (i4 = 0; i4 < sampleCount4; i4 += 1) { + float x0 = src[i+0]; + float x1 = src[i+1]; + float x2 = src[i+2]; + float x3 = src[i+3]; + + x0 = ((x0 < -1) ? -1 : ((x0 > 1) ? 1 : x0)); + x1 = ((x1 < -1) ? -1 : ((x1 > 1) ? 1 : x1)); + x2 = ((x2 < -1) ? -1 : ((x2 > 1) ? 1 : x2)); + x3 = ((x3 < -1) ? -1 : ((x3 > 1) ? 1 : x3)); + + x0 = x0 * 32767.0f; + x1 = x1 * 32767.0f; + x2 = x2 * 32767.0f; + x3 = x3 * 32767.0f; + + dst[i+0] = (drmp3_int16)x0; + dst[i+1] = (drmp3_int16)x1; + dst[i+2] = (drmp3_int16)x2; + dst[i+3] = (drmp3_int16)x3; + + i += 4; + } + + /* Leftover. */ + for (; i < sampleCount; i += 1) { + float x = src[i]; + x = ((x < -1) ? -1 : ((x > 1) ? 1 : x)); /* clip */ + x = x * 32767.0f; /* -1..1 to -32767..32767 */ + + dst[i] = (drmp3_int16)x; + } +} +#endif + +#if !defined(DR_MP3_FLOAT_OUTPUT) +static void drmp3_s16_to_f32(float* dst, const drmp3_int16* src, drmp3_uint64 sampleCount) +{ + drmp3_uint64 i; + for (i = 0; i < sampleCount; i += 1) { + float x = (float)src[i]; + x = x * 0.000030517578125f; /* -32768..32767 to -1..0.999969482421875 */ + dst[i] = x; + } +} +#endif + + +static drmp3_uint64 drmp3_read_pcm_frames_raw(drmp3* pMP3, drmp3_uint64 framesToRead, void* pBufferOut) +{ + drmp3_uint64 totalFramesRead = 0; + + DRMP3_ASSERT(pMP3 != NULL); + DRMP3_ASSERT(pMP3->onRead != NULL); + + while (framesToRead > 0) { + drmp3_uint32 framesToConsume = (drmp3_uint32)DRMP3_MIN(pMP3->pcmFramesRemainingInMP3Frame, framesToRead); + if (pBufferOut != NULL) { + #if defined(DR_MP3_FLOAT_OUTPUT) + /* f32 */ + float* pFramesOutF32 = (float*)DRMP3_OFFSET_PTR(pBufferOut, sizeof(float) * totalFramesRead * pMP3->channels); + float* pFramesInF32 = (float*)DRMP3_OFFSET_PTR(&pMP3->pcmFrames[0], sizeof(float) * pMP3->pcmFramesConsumedInMP3Frame * pMP3->mp3FrameChannels); + DRMP3_COPY_MEMORY(pFramesOutF32, pFramesInF32, sizeof(float) * framesToConsume * pMP3->channels); + #else + /* s16 */ + drmp3_int16* pFramesOutS16 = (drmp3_int16*)DRMP3_OFFSET_PTR(pBufferOut, sizeof(drmp3_int16) * totalFramesRead * pMP3->channels); + drmp3_int16* pFramesInS16 = (drmp3_int16*)DRMP3_OFFSET_PTR(&pMP3->pcmFrames[0], sizeof(drmp3_int16) * pMP3->pcmFramesConsumedInMP3Frame * pMP3->mp3FrameChannels); + DRMP3_COPY_MEMORY(pFramesOutS16, pFramesInS16, sizeof(drmp3_int16) * framesToConsume * pMP3->channels); + #endif + } + + pMP3->currentPCMFrame += framesToConsume; + pMP3->pcmFramesConsumedInMP3Frame += framesToConsume; + pMP3->pcmFramesRemainingInMP3Frame -= framesToConsume; + totalFramesRead += framesToConsume; + framesToRead -= framesToConsume; + + if (framesToRead == 0) { + break; + } + + DRMP3_ASSERT(pMP3->pcmFramesRemainingInMP3Frame == 0); + + /* + At this point we have exhausted our in-memory buffer so we need to re-fill. Note that the sample rate may have changed + at this point which means we'll also need to update our sample rate conversion pipeline. + */ + if (drmp3_decode_next_frame(pMP3) == 0) { + break; + } + } + + return totalFramesRead; +} + + +DRMP3_API drmp3_uint64 drmp3_read_pcm_frames_f32(drmp3* pMP3, drmp3_uint64 framesToRead, float* pBufferOut) +{ + if (pMP3 == NULL || pMP3->onRead == NULL) { + return 0; + } + +#if defined(DR_MP3_FLOAT_OUTPUT) + /* Fast path. No conversion required. */ + return drmp3_read_pcm_frames_raw(pMP3, framesToRead, pBufferOut); +#else + /* Slow path. Convert from s16 to f32. */ + { + drmp3_int16 pTempS16[8192]; + drmp3_uint64 totalPCMFramesRead = 0; + + while (totalPCMFramesRead < framesToRead) { + drmp3_uint64 framesJustRead; + drmp3_uint64 framesRemaining = framesToRead - totalPCMFramesRead; + drmp3_uint64 framesToReadNow = DRMP3_COUNTOF(pTempS16) / pMP3->channels; + if (framesToReadNow > framesRemaining) { + framesToReadNow = framesRemaining; + } + + framesJustRead = drmp3_read_pcm_frames_raw(pMP3, framesToReadNow, pTempS16); + if (framesJustRead == 0) { + break; + } + + drmp3_s16_to_f32((float*)DRMP3_OFFSET_PTR(pBufferOut, sizeof(float) * totalPCMFramesRead * pMP3->channels), pTempS16, framesJustRead * pMP3->channels); + totalPCMFramesRead += framesJustRead; + } + + return totalPCMFramesRead; + } +#endif +} + +DRMP3_API drmp3_uint64 drmp3_read_pcm_frames_s16(drmp3* pMP3, drmp3_uint64 framesToRead, drmp3_int16* pBufferOut) +{ + if (pMP3 == NULL || pMP3->onRead == NULL) { + return 0; + } + +#if !defined(DR_MP3_FLOAT_OUTPUT) + /* Fast path. No conversion required. */ + return drmp3_read_pcm_frames_raw(pMP3, framesToRead, pBufferOut); +#else + /* Slow path. Convert from f32 to s16. */ + { + float pTempF32[4096]; + drmp3_uint64 totalPCMFramesRead = 0; + + while (totalPCMFramesRead < framesToRead) { + drmp3_uint64 framesJustRead; + drmp3_uint64 framesRemaining = framesToRead - totalPCMFramesRead; + drmp3_uint64 framesToReadNow = DRMP3_COUNTOF(pTempF32) / pMP3->channels; + if (framesToReadNow > framesRemaining) { + framesToReadNow = framesRemaining; + } + + framesJustRead = drmp3_read_pcm_frames_raw(pMP3, framesToReadNow, pTempF32); + if (framesJustRead == 0) { + break; + } + + drmp3_f32_to_s16((drmp3_int16*)DRMP3_OFFSET_PTR(pBufferOut, sizeof(drmp3_int16) * totalPCMFramesRead * pMP3->channels), pTempF32, framesJustRead * pMP3->channels); + totalPCMFramesRead += framesJustRead; + } + + return totalPCMFramesRead; + } +#endif +} + +static void drmp3_reset(drmp3* pMP3) +{ + DRMP3_ASSERT(pMP3 != NULL); + + pMP3->pcmFramesConsumedInMP3Frame = 0; + pMP3->pcmFramesRemainingInMP3Frame = 0; + pMP3->currentPCMFrame = 0; + pMP3->dataSize = 0; + pMP3->atEnd = DRMP3_FALSE; + drmp3dec_init(&pMP3->decoder); +} + +static drmp3_bool32 drmp3_seek_to_start_of_stream(drmp3* pMP3) +{ + DRMP3_ASSERT(pMP3 != NULL); + DRMP3_ASSERT(pMP3->onSeek != NULL); + + /* Seek to the start of the stream to begin with. */ + if (!drmp3__on_seek(pMP3, 0, drmp3_seek_origin_start)) { + return DRMP3_FALSE; + } + + /* Clear any cached data. */ + drmp3_reset(pMP3); + return DRMP3_TRUE; +} + + +static drmp3_bool32 drmp3_seek_forward_by_pcm_frames__brute_force(drmp3* pMP3, drmp3_uint64 frameOffset) +{ + drmp3_uint64 framesRead; + + /* + Just using a dumb read-and-discard for now. What would be nice is to parse only the header of the MP3 frame, and then skip over leading + frames without spending the time doing a full decode. I cannot see an easy way to do this in minimp3, however, so it may involve some + kind of manual processing. + */ +#if defined(DR_MP3_FLOAT_OUTPUT) + framesRead = drmp3_read_pcm_frames_f32(pMP3, frameOffset, NULL); +#else + framesRead = drmp3_read_pcm_frames_s16(pMP3, frameOffset, NULL); +#endif + if (framesRead != frameOffset) { + return DRMP3_FALSE; + } + + return DRMP3_TRUE; +} + +static drmp3_bool32 drmp3_seek_to_pcm_frame__brute_force(drmp3* pMP3, drmp3_uint64 frameIndex) +{ + DRMP3_ASSERT(pMP3 != NULL); + + if (frameIndex == pMP3->currentPCMFrame) { + return DRMP3_TRUE; + } + + /* + If we're moving foward we just read from where we're at. Otherwise we need to move back to the start of + the stream and read from the beginning. + */ + if (frameIndex < pMP3->currentPCMFrame) { + /* Moving backward. Move to the start of the stream and then move forward. */ + if (!drmp3_seek_to_start_of_stream(pMP3)) { + return DRMP3_FALSE; + } + } + + DRMP3_ASSERT(frameIndex >= pMP3->currentPCMFrame); + return drmp3_seek_forward_by_pcm_frames__brute_force(pMP3, (frameIndex - pMP3->currentPCMFrame)); +} + +static drmp3_bool32 drmp3_find_closest_seek_point(drmp3* pMP3, drmp3_uint64 frameIndex, drmp3_uint32* pSeekPointIndex) +{ + drmp3_uint32 iSeekPoint; + + DRMP3_ASSERT(pSeekPointIndex != NULL); + + *pSeekPointIndex = 0; + + if (frameIndex < pMP3->pSeekPoints[0].pcmFrameIndex) { + return DRMP3_FALSE; + } + + /* Linear search for simplicity to begin with while I'm getting this thing working. Once it's all working change this to a binary search. */ + for (iSeekPoint = 0; iSeekPoint < pMP3->seekPointCount; ++iSeekPoint) { + if (pMP3->pSeekPoints[iSeekPoint].pcmFrameIndex > frameIndex) { + break; /* Found it. */ + } + + *pSeekPointIndex = iSeekPoint; + } + + return DRMP3_TRUE; +} + +static drmp3_bool32 drmp3_seek_to_pcm_frame__seek_table(drmp3* pMP3, drmp3_uint64 frameIndex) +{ + drmp3_seek_point seekPoint; + drmp3_uint32 priorSeekPointIndex; + drmp3_uint16 iMP3Frame; + drmp3_uint64 leftoverFrames; + + DRMP3_ASSERT(pMP3 != NULL); + DRMP3_ASSERT(pMP3->pSeekPoints != NULL); + DRMP3_ASSERT(pMP3->seekPointCount > 0); + + /* If there is no prior seekpoint it means the target PCM frame comes before the first seek point. Just assume a seekpoint at the start of the file in this case. */ + if (drmp3_find_closest_seek_point(pMP3, frameIndex, &priorSeekPointIndex)) { + seekPoint = pMP3->pSeekPoints[priorSeekPointIndex]; + } else { + seekPoint.seekPosInBytes = 0; + seekPoint.pcmFrameIndex = 0; + seekPoint.mp3FramesToDiscard = 0; + seekPoint.pcmFramesToDiscard = 0; + } + + /* First thing to do is seek to the first byte of the relevant MP3 frame. */ + if (!drmp3__on_seek_64(pMP3, seekPoint.seekPosInBytes, drmp3_seek_origin_start)) { + return DRMP3_FALSE; /* Failed to seek. */ + } + + /* Clear any cached data. */ + drmp3_reset(pMP3); + + /* Whole MP3 frames need to be discarded first. */ + for (iMP3Frame = 0; iMP3Frame < seekPoint.mp3FramesToDiscard; ++iMP3Frame) { + drmp3_uint32 pcmFramesRead; + drmp3d_sample_t* pPCMFrames; + + /* Pass in non-null for the last frame because we want to ensure the sample rate converter is preloaded correctly. */ + pPCMFrames = NULL; + if (iMP3Frame == seekPoint.mp3FramesToDiscard-1) { + pPCMFrames = (drmp3d_sample_t*)pMP3->pcmFrames; + } + + /* We first need to decode the next frame. */ + pcmFramesRead = drmp3_decode_next_frame_ex(pMP3, pPCMFrames); + if (pcmFramesRead == 0) { + return DRMP3_FALSE; + } + } + + /* We seeked to an MP3 frame in the raw stream so we need to make sure the current PCM frame is set correctly. */ + pMP3->currentPCMFrame = seekPoint.pcmFrameIndex - seekPoint.pcmFramesToDiscard; + + /* + Now at this point we can follow the same process as the brute force technique where we just skip over unnecessary MP3 frames and then + read-and-discard at least 2 whole MP3 frames. + */ + leftoverFrames = frameIndex - pMP3->currentPCMFrame; + return drmp3_seek_forward_by_pcm_frames__brute_force(pMP3, leftoverFrames); +} + +DRMP3_API drmp3_bool32 drmp3_seek_to_pcm_frame(drmp3* pMP3, drmp3_uint64 frameIndex) +{ + if (pMP3 == NULL || pMP3->onSeek == NULL) { + return DRMP3_FALSE; + } + + if (frameIndex == 0) { + return drmp3_seek_to_start_of_stream(pMP3); + } + + /* Use the seek table if we have one. */ + if (pMP3->pSeekPoints != NULL && pMP3->seekPointCount > 0) { + return drmp3_seek_to_pcm_frame__seek_table(pMP3, frameIndex); + } else { + return drmp3_seek_to_pcm_frame__brute_force(pMP3, frameIndex); + } +} + +DRMP3_API drmp3_bool32 drmp3_get_mp3_and_pcm_frame_count(drmp3* pMP3, drmp3_uint64* pMP3FrameCount, drmp3_uint64* pPCMFrameCount) +{ + drmp3_uint64 currentPCMFrame; + drmp3_uint64 totalPCMFrameCount; + drmp3_uint64 totalMP3FrameCount; + + if (pMP3 == NULL) { + return DRMP3_FALSE; + } + + /* + The way this works is we move back to the start of the stream, iterate over each MP3 frame and calculate the frame count based + on our output sample rate, the seek back to the PCM frame we were sitting on before calling this function. + */ + + /* The stream must support seeking for this to work. */ + if (pMP3->onSeek == NULL) { + return DRMP3_FALSE; + } + + /* We'll need to seek back to where we were, so grab the PCM frame we're currently sitting on so we can restore later. */ + currentPCMFrame = pMP3->currentPCMFrame; + + if (!drmp3_seek_to_start_of_stream(pMP3)) { + return DRMP3_FALSE; + } + + totalPCMFrameCount = 0; + totalMP3FrameCount = 0; + + for (;;) { + drmp3_uint32 pcmFramesInCurrentMP3Frame; + + pcmFramesInCurrentMP3Frame = drmp3_decode_next_frame_ex(pMP3, NULL); + if (pcmFramesInCurrentMP3Frame == 0) { + break; + } + + totalPCMFrameCount += pcmFramesInCurrentMP3Frame; + totalMP3FrameCount += 1; + } + + /* Finally, we need to seek back to where we were. */ + if (!drmp3_seek_to_start_of_stream(pMP3)) { + return DRMP3_FALSE; + } + + if (!drmp3_seek_to_pcm_frame(pMP3, currentPCMFrame)) { + return DRMP3_FALSE; + } + + if (pMP3FrameCount != NULL) { + *pMP3FrameCount = totalMP3FrameCount; + } + if (pPCMFrameCount != NULL) { + *pPCMFrameCount = totalPCMFrameCount; + } + + return DRMP3_TRUE; +} + +DRMP3_API drmp3_uint64 drmp3_get_pcm_frame_count(drmp3* pMP3) +{ + drmp3_uint64 totalPCMFrameCount; + if (!drmp3_get_mp3_and_pcm_frame_count(pMP3, NULL, &totalPCMFrameCount)) { + return 0; + } + + return totalPCMFrameCount; +} + +DRMP3_API drmp3_uint64 drmp3_get_mp3_frame_count(drmp3* pMP3) +{ + drmp3_uint64 totalMP3FrameCount; + if (!drmp3_get_mp3_and_pcm_frame_count(pMP3, &totalMP3FrameCount, NULL)) { + return 0; + } + + return totalMP3FrameCount; +} + +static void drmp3__accumulate_running_pcm_frame_count(drmp3* pMP3, drmp3_uint32 pcmFrameCountIn, drmp3_uint64* pRunningPCMFrameCount, float* pRunningPCMFrameCountFractionalPart) +{ + float srcRatio; + float pcmFrameCountOutF; + drmp3_uint32 pcmFrameCountOut; + + srcRatio = (float)pMP3->mp3FrameSampleRate / (float)pMP3->sampleRate; + DRMP3_ASSERT(srcRatio > 0); + + pcmFrameCountOutF = *pRunningPCMFrameCountFractionalPart + (pcmFrameCountIn / srcRatio); + pcmFrameCountOut = (drmp3_uint32)pcmFrameCountOutF; + *pRunningPCMFrameCountFractionalPart = pcmFrameCountOutF - pcmFrameCountOut; + *pRunningPCMFrameCount += pcmFrameCountOut; +} + +typedef struct +{ + drmp3_uint64 bytePos; + drmp3_uint64 pcmFrameIndex; /* <-- After sample rate conversion. */ +} drmp3__seeking_mp3_frame_info; + +DRMP3_API drmp3_bool32 drmp3_calculate_seek_points(drmp3* pMP3, drmp3_uint32* pSeekPointCount, drmp3_seek_point* pSeekPoints) +{ + drmp3_uint32 seekPointCount; + drmp3_uint64 currentPCMFrame; + drmp3_uint64 totalMP3FrameCount; + drmp3_uint64 totalPCMFrameCount; + + if (pMP3 == NULL || pSeekPointCount == NULL || pSeekPoints == NULL) { + return DRMP3_FALSE; /* Invalid args. */ + } + + seekPointCount = *pSeekPointCount; + if (seekPointCount == 0) { + return DRMP3_FALSE; /* The client has requested no seek points. Consider this to be invalid arguments since the client has probably not intended this. */ + } + + /* We'll need to seek back to the current sample after calculating the seekpoints so we need to go ahead and grab the current location at the top. */ + currentPCMFrame = pMP3->currentPCMFrame; + + /* We never do more than the total number of MP3 frames and we limit it to 32-bits. */ + if (!drmp3_get_mp3_and_pcm_frame_count(pMP3, &totalMP3FrameCount, &totalPCMFrameCount)) { + return DRMP3_FALSE; + } + + /* If there's less than DRMP3_SEEK_LEADING_MP3_FRAMES+1 frames we just report 1 seek point which will be the very start of the stream. */ + if (totalMP3FrameCount < DRMP3_SEEK_LEADING_MP3_FRAMES+1) { + seekPointCount = 1; + pSeekPoints[0].seekPosInBytes = 0; + pSeekPoints[0].pcmFrameIndex = 0; + pSeekPoints[0].mp3FramesToDiscard = 0; + pSeekPoints[0].pcmFramesToDiscard = 0; + } else { + drmp3_uint64 pcmFramesBetweenSeekPoints; + drmp3__seeking_mp3_frame_info mp3FrameInfo[DRMP3_SEEK_LEADING_MP3_FRAMES+1]; + drmp3_uint64 runningPCMFrameCount = 0; + float runningPCMFrameCountFractionalPart = 0; + drmp3_uint64 nextTargetPCMFrame; + drmp3_uint32 iMP3Frame; + drmp3_uint32 iSeekPoint; + + if (seekPointCount > totalMP3FrameCount-1) { + seekPointCount = (drmp3_uint32)totalMP3FrameCount-1; + } + + pcmFramesBetweenSeekPoints = totalPCMFrameCount / (seekPointCount+1); + + /* + Here is where we actually calculate the seek points. We need to start by moving the start of the stream. We then enumerate over each + MP3 frame. + */ + if (!drmp3_seek_to_start_of_stream(pMP3)) { + return DRMP3_FALSE; + } + + /* + We need to cache the byte positions of the previous MP3 frames. As a new MP3 frame is iterated, we cycle the byte positions in this + array. The value in the first item in this array is the byte position that will be reported in the next seek point. + */ + + /* We need to initialize the array of MP3 byte positions for the leading MP3 frames. */ + for (iMP3Frame = 0; iMP3Frame < DRMP3_SEEK_LEADING_MP3_FRAMES+1; ++iMP3Frame) { + drmp3_uint32 pcmFramesInCurrentMP3FrameIn; + + /* The byte position of the next frame will be the stream's cursor position, minus whatever is sitting in the buffer. */ + DRMP3_ASSERT(pMP3->streamCursor >= pMP3->dataSize); + mp3FrameInfo[iMP3Frame].bytePos = pMP3->streamCursor - pMP3->dataSize; + mp3FrameInfo[iMP3Frame].pcmFrameIndex = runningPCMFrameCount; + + /* We need to get information about this frame so we can know how many samples it contained. */ + pcmFramesInCurrentMP3FrameIn = drmp3_decode_next_frame_ex(pMP3, NULL); + if (pcmFramesInCurrentMP3FrameIn == 0) { + return DRMP3_FALSE; /* This should never happen. */ + } + + drmp3__accumulate_running_pcm_frame_count(pMP3, pcmFramesInCurrentMP3FrameIn, &runningPCMFrameCount, &runningPCMFrameCountFractionalPart); + } + + /* + At this point we will have extracted the byte positions of the leading MP3 frames. We can now start iterating over each seek point and + calculate them. + */ + nextTargetPCMFrame = 0; + for (iSeekPoint = 0; iSeekPoint < seekPointCount; ++iSeekPoint) { + nextTargetPCMFrame += pcmFramesBetweenSeekPoints; + + for (;;) { + if (nextTargetPCMFrame < runningPCMFrameCount) { + /* The next seek point is in the current MP3 frame. */ + pSeekPoints[iSeekPoint].seekPosInBytes = mp3FrameInfo[0].bytePos; + pSeekPoints[iSeekPoint].pcmFrameIndex = nextTargetPCMFrame; + pSeekPoints[iSeekPoint].mp3FramesToDiscard = DRMP3_SEEK_LEADING_MP3_FRAMES; + pSeekPoints[iSeekPoint].pcmFramesToDiscard = (drmp3_uint16)(nextTargetPCMFrame - mp3FrameInfo[DRMP3_SEEK_LEADING_MP3_FRAMES-1].pcmFrameIndex); + break; + } else { + size_t i; + drmp3_uint32 pcmFramesInCurrentMP3FrameIn; + + /* + The next seek point is not in the current MP3 frame, so continue on to the next one. The first thing to do is cycle the cached + MP3 frame info. + */ + for (i = 0; i < DRMP3_COUNTOF(mp3FrameInfo)-1; ++i) { + mp3FrameInfo[i] = mp3FrameInfo[i+1]; + } + + /* Cache previous MP3 frame info. */ + mp3FrameInfo[DRMP3_COUNTOF(mp3FrameInfo)-1].bytePos = pMP3->streamCursor - pMP3->dataSize; + mp3FrameInfo[DRMP3_COUNTOF(mp3FrameInfo)-1].pcmFrameIndex = runningPCMFrameCount; + + /* + Go to the next MP3 frame. This shouldn't ever fail, but just in case it does we just set the seek point and break. If it happens, it + should only ever do it for the last seek point. + */ + pcmFramesInCurrentMP3FrameIn = drmp3_decode_next_frame_ex(pMP3, NULL); + if (pcmFramesInCurrentMP3FrameIn == 0) { + pSeekPoints[iSeekPoint].seekPosInBytes = mp3FrameInfo[0].bytePos; + pSeekPoints[iSeekPoint].pcmFrameIndex = nextTargetPCMFrame; + pSeekPoints[iSeekPoint].mp3FramesToDiscard = DRMP3_SEEK_LEADING_MP3_FRAMES; + pSeekPoints[iSeekPoint].pcmFramesToDiscard = (drmp3_uint16)(nextTargetPCMFrame - mp3FrameInfo[DRMP3_SEEK_LEADING_MP3_FRAMES-1].pcmFrameIndex); + break; + } + + drmp3__accumulate_running_pcm_frame_count(pMP3, pcmFramesInCurrentMP3FrameIn, &runningPCMFrameCount, &runningPCMFrameCountFractionalPart); + } + } + } + + /* Finally, we need to seek back to where we were. */ + if (!drmp3_seek_to_start_of_stream(pMP3)) { + return DRMP3_FALSE; + } + if (!drmp3_seek_to_pcm_frame(pMP3, currentPCMFrame)) { + return DRMP3_FALSE; + } + } + + *pSeekPointCount = seekPointCount; + return DRMP3_TRUE; +} + +DRMP3_API drmp3_bool32 drmp3_bind_seek_table(drmp3* pMP3, drmp3_uint32 seekPointCount, drmp3_seek_point* pSeekPoints) +{ + if (pMP3 == NULL) { + return DRMP3_FALSE; + } + + if (seekPointCount == 0 || pSeekPoints == NULL) { + /* Unbinding. */ + pMP3->seekPointCount = 0; + pMP3->pSeekPoints = NULL; + } else { + /* Binding. */ + pMP3->seekPointCount = seekPointCount; + pMP3->pSeekPoints = pSeekPoints; + } + + return DRMP3_TRUE; +} + + +static float* drmp3__full_read_and_close_f32(drmp3* pMP3, drmp3_config* pConfig, drmp3_uint64* pTotalFrameCount) +{ + drmp3_uint64 totalFramesRead = 0; + drmp3_uint64 framesCapacity = 0; + float* pFrames = NULL; + float temp[4096]; + + DRMP3_ASSERT(pMP3 != NULL); + + for (;;) { + drmp3_uint64 framesToReadRightNow = DRMP3_COUNTOF(temp) / pMP3->channels; + drmp3_uint64 framesJustRead = drmp3_read_pcm_frames_f32(pMP3, framesToReadRightNow, temp); + if (framesJustRead == 0) { + break; + } + + /* Reallocate the output buffer if there's not enough room. */ + if (framesCapacity < totalFramesRead + framesJustRead) { + drmp3_uint64 oldFramesBufferSize; + drmp3_uint64 newFramesBufferSize; + drmp3_uint64 newFramesCap; + float* pNewFrames; + + newFramesCap = framesCapacity * 2; + if (newFramesCap < totalFramesRead + framesJustRead) { + newFramesCap = totalFramesRead + framesJustRead; + } + + oldFramesBufferSize = framesCapacity * pMP3->channels * sizeof(float); + newFramesBufferSize = newFramesCap * pMP3->channels * sizeof(float); + if (newFramesBufferSize > (drmp3_uint64)DRMP3_SIZE_MAX) { + break; + } + + pNewFrames = (float*)drmp3__realloc_from_callbacks(pFrames, (size_t)newFramesBufferSize, (size_t)oldFramesBufferSize, &pMP3->allocationCallbacks); + if (pNewFrames == NULL) { + drmp3__free_from_callbacks(pFrames, &pMP3->allocationCallbacks); + break; + } + + pFrames = pNewFrames; + framesCapacity = newFramesCap; + } + + DRMP3_COPY_MEMORY(pFrames + totalFramesRead*pMP3->channels, temp, (size_t)(framesJustRead*pMP3->channels*sizeof(float))); + totalFramesRead += framesJustRead; + + /* If the number of frames we asked for is less that what we actually read it means we've reached the end. */ + if (framesJustRead != framesToReadRightNow) { + break; + } + } + + if (pConfig != NULL) { + pConfig->channels = pMP3->channels; + pConfig->sampleRate = pMP3->sampleRate; + } + + drmp3_uninit(pMP3); + + if (pTotalFrameCount) { + *pTotalFrameCount = totalFramesRead; + } + + return pFrames; +} + +static drmp3_int16* drmp3__full_read_and_close_s16(drmp3* pMP3, drmp3_config* pConfig, drmp3_uint64* pTotalFrameCount) +{ + drmp3_uint64 totalFramesRead = 0; + drmp3_uint64 framesCapacity = 0; + drmp3_int16* pFrames = NULL; + drmp3_int16 temp[4096]; + + DRMP3_ASSERT(pMP3 != NULL); + + for (;;) { + drmp3_uint64 framesToReadRightNow = DRMP3_COUNTOF(temp) / pMP3->channels; + drmp3_uint64 framesJustRead = drmp3_read_pcm_frames_s16(pMP3, framesToReadRightNow, temp); + if (framesJustRead == 0) { + break; + } + + /* Reallocate the output buffer if there's not enough room. */ + if (framesCapacity < totalFramesRead + framesJustRead) { + drmp3_uint64 newFramesBufferSize; + drmp3_uint64 oldFramesBufferSize; + drmp3_uint64 newFramesCap; + drmp3_int16* pNewFrames; + + newFramesCap = framesCapacity * 2; + if (newFramesCap < totalFramesRead + framesJustRead) { + newFramesCap = totalFramesRead + framesJustRead; + } + + oldFramesBufferSize = framesCapacity * pMP3->channels * sizeof(drmp3_int16); + newFramesBufferSize = newFramesCap * pMP3->channels * sizeof(drmp3_int16); + if (newFramesBufferSize > (drmp3_uint64)DRMP3_SIZE_MAX) { + break; + } + + pNewFrames = (drmp3_int16*)drmp3__realloc_from_callbacks(pFrames, (size_t)newFramesBufferSize, (size_t)oldFramesBufferSize, &pMP3->allocationCallbacks); + if (pNewFrames == NULL) { + drmp3__free_from_callbacks(pFrames, &pMP3->allocationCallbacks); + break; + } + + pFrames = pNewFrames; + framesCapacity = newFramesCap; + } + + DRMP3_COPY_MEMORY(pFrames + totalFramesRead*pMP3->channels, temp, (size_t)(framesJustRead*pMP3->channels*sizeof(drmp3_int16))); + totalFramesRead += framesJustRead; + + /* If the number of frames we asked for is less that what we actually read it means we've reached the end. */ + if (framesJustRead != framesToReadRightNow) { + break; + } + } + + if (pConfig != NULL) { + pConfig->channels = pMP3->channels; + pConfig->sampleRate = pMP3->sampleRate; + } + + drmp3_uninit(pMP3); + + if (pTotalFrameCount) { + *pTotalFrameCount = totalFramesRead; + } + + return pFrames; +} + + +DRMP3_API float* drmp3_open_and_read_pcm_frames_f32(drmp3_read_proc onRead, drmp3_seek_proc onSeek, void* pUserData, drmp3_config* pConfig, drmp3_uint64* pTotalFrameCount, const drmp3_allocation_callbacks* pAllocationCallbacks) +{ + drmp3 mp3; + if (!drmp3_init(&mp3, onRead, onSeek, pUserData, pAllocationCallbacks)) { + return NULL; + } + + return drmp3__full_read_and_close_f32(&mp3, pConfig, pTotalFrameCount); +} + +DRMP3_API drmp3_int16* drmp3_open_and_read_pcm_frames_s16(drmp3_read_proc onRead, drmp3_seek_proc onSeek, void* pUserData, drmp3_config* pConfig, drmp3_uint64* pTotalFrameCount, const drmp3_allocation_callbacks* pAllocationCallbacks) +{ + drmp3 mp3; + if (!drmp3_init(&mp3, onRead, onSeek, pUserData, pAllocationCallbacks)) { + return NULL; + } + + return drmp3__full_read_and_close_s16(&mp3, pConfig, pTotalFrameCount); +} + + +DRMP3_API float* drmp3_open_memory_and_read_pcm_frames_f32(const void* pData, size_t dataSize, drmp3_config* pConfig, drmp3_uint64* pTotalFrameCount, const drmp3_allocation_callbacks* pAllocationCallbacks) +{ + drmp3 mp3; + if (!drmp3_init_memory(&mp3, pData, dataSize, pAllocationCallbacks)) { + return NULL; + } + + return drmp3__full_read_and_close_f32(&mp3, pConfig, pTotalFrameCount); +} + +DRMP3_API drmp3_int16* drmp3_open_memory_and_read_pcm_frames_s16(const void* pData, size_t dataSize, drmp3_config* pConfig, drmp3_uint64* pTotalFrameCount, const drmp3_allocation_callbacks* pAllocationCallbacks) +{ + drmp3 mp3; + if (!drmp3_init_memory(&mp3, pData, dataSize, pAllocationCallbacks)) { + return NULL; + } + + return drmp3__full_read_and_close_s16(&mp3, pConfig, pTotalFrameCount); +} + + +#ifndef DR_MP3_NO_STDIO +DRMP3_API float* drmp3_open_file_and_read_pcm_frames_f32(const char* filePath, drmp3_config* pConfig, drmp3_uint64* pTotalFrameCount, const drmp3_allocation_callbacks* pAllocationCallbacks) +{ + drmp3 mp3; + if (!drmp3_init_file(&mp3, filePath, pAllocationCallbacks)) { + return NULL; + } + + return drmp3__full_read_and_close_f32(&mp3, pConfig, pTotalFrameCount); +} + +DRMP3_API drmp3_int16* drmp3_open_file_and_read_pcm_frames_s16(const char* filePath, drmp3_config* pConfig, drmp3_uint64* pTotalFrameCount, const drmp3_allocation_callbacks* pAllocationCallbacks) +{ + drmp3 mp3; + if (!drmp3_init_file(&mp3, filePath, pAllocationCallbacks)) { + return NULL; + } + + return drmp3__full_read_and_close_s16(&mp3, pConfig, pTotalFrameCount); +} +#endif + +DRMP3_API void* drmp3_malloc(size_t sz, const drmp3_allocation_callbacks* pAllocationCallbacks) +{ + if (pAllocationCallbacks != NULL) { + return drmp3__malloc_from_callbacks(sz, pAllocationCallbacks); + } else { + return drmp3__malloc_default(sz, NULL); + } +} + +DRMP3_API void drmp3_free(void* p, const drmp3_allocation_callbacks* pAllocationCallbacks) +{ + if (pAllocationCallbacks != NULL) { + drmp3__free_from_callbacks(p, pAllocationCallbacks); + } else { + drmp3__free_default(p, NULL); + } +} + +#endif /* dr_mp3_c */ +#endif /*DR_MP3_IMPLEMENTATION*/ + +/* +DIFFERENCES BETWEEN minimp3 AND dr_mp3 +====================================== +- First, keep in mind that minimp3 (https://github.com/lieff/minimp3) is where all the real work was done. All of the + code relating to the actual decoding remains mostly unmodified, apart from some namespacing changes. +- dr_mp3 adds a pulling style API which allows you to deliver raw data via callbacks. So, rather than pushing data + to the decoder, the decoder _pulls_ data from your callbacks. +- In addition to callbacks, a decoder can be initialized from a block of memory and a file. +- The dr_mp3 pull API reads PCM frames rather than whole MP3 frames. +- dr_mp3 adds convenience APIs for opening and decoding entire files in one go. +- dr_mp3 is fully namespaced, including the implementation section, which is more suitable when compiling projects + as a single translation unit (aka unity builds). At the time of writing this, a unity build is not possible when + using minimp3 in conjunction with stb_vorbis. dr_mp3 addresses this. +*/ + +/* +RELEASE NOTES - v0.5.0 +======================= +Version 0.5.0 has breaking API changes. + +Improved Client-Defined Memory Allocation +----------------------------------------- +The main change with this release is the addition of a more flexible way of implementing custom memory allocation routines. The +existing system of DRMP3_MALLOC, DRMP3_REALLOC and DRMP3_FREE are still in place and will be used by default when no custom +allocation callbacks are specified. + +To use the new system, you pass in a pointer to a drmp3_allocation_callbacks object to drmp3_init() and family, like this: + + void* my_malloc(size_t sz, void* pUserData) + { + return malloc(sz); + } + void* my_realloc(void* p, size_t sz, void* pUserData) + { + return realloc(p, sz); + } + void my_free(void* p, void* pUserData) + { + free(p); + } + + ... + + drmp3_allocation_callbacks allocationCallbacks; + allocationCallbacks.pUserData = &myData; + allocationCallbacks.onMalloc = my_malloc; + allocationCallbacks.onRealloc = my_realloc; + allocationCallbacks.onFree = my_free; + drmp3_init_file(&mp3, "my_file.mp3", NULL, &allocationCallbacks); + +The advantage of this new system is that it allows you to specify user data which will be passed in to the allocation routines. + +Passing in null for the allocation callbacks object will cause dr_mp3 to use defaults which is the same as DRMP3_MALLOC, +DRMP3_REALLOC and DRMP3_FREE and the equivalent of how it worked in previous versions. + +Every API that opens a drmp3 object now takes this extra parameter. These include the following: + + drmp3_init() + drmp3_init_file() + drmp3_init_memory() + drmp3_open_and_read_pcm_frames_f32() + drmp3_open_and_read_pcm_frames_s16() + drmp3_open_memory_and_read_pcm_frames_f32() + drmp3_open_memory_and_read_pcm_frames_s16() + drmp3_open_file_and_read_pcm_frames_f32() + drmp3_open_file_and_read_pcm_frames_s16() + +Renamed APIs +------------ +The following APIs have been renamed for consistency with other dr_* libraries and to make it clear that they return PCM frame +counts rather than sample counts. + + drmp3_open_and_read_f32() -> drmp3_open_and_read_pcm_frames_f32() + drmp3_open_and_read_s16() -> drmp3_open_and_read_pcm_frames_s16() + drmp3_open_memory_and_read_f32() -> drmp3_open_memory_and_read_pcm_frames_f32() + drmp3_open_memory_and_read_s16() -> drmp3_open_memory_and_read_pcm_frames_s16() + drmp3_open_file_and_read_f32() -> drmp3_open_file_and_read_pcm_frames_f32() + drmp3_open_file_and_read_s16() -> drmp3_open_file_and_read_pcm_frames_s16() +*/ + +/* +REVISION HISTORY +================ +v0.6.37 - 2023-07-07 + - Silence a static analysis warning. + +v0.6.36 - 2023-06-17 + - Fix an incorrect date in revision history. No functional change. + +v0.6.35 - 2023-05-22 + - Minor code restructure. No functional change. + +v0.6.34 - 2022-09-17 + - Fix compilation with DJGPP. + - Fix compilation when compiling with x86 with no SSE2. + - Remove an unnecessary variable from the drmp3 structure. + +v0.6.33 - 2022-04-10 + - Fix compilation error with the MSVC ARM64 build. + - Fix compilation error on older versions of GCC. + - Remove some unused functions. + +v0.6.32 - 2021-12-11 + - Fix a warning with Clang. + +v0.6.31 - 2021-08-22 + - Fix a bug when loading from memory. + +v0.6.30 - 2021-08-16 + - Silence some warnings. + - Replace memory operations with DRMP3_* macros. + +v0.6.29 - 2021-08-08 + - Bring up to date with minimp3. + +v0.6.28 - 2021-07-31 + - Fix platform detection for ARM64. + - Fix a compilation error with C89. + +v0.6.27 - 2021-02-21 + - Fix a warning due to referencing _MSC_VER when it is undefined. + +v0.6.26 - 2021-01-31 + - Bring up to date with minimp3. + +v0.6.25 - 2020-12-26 + - Remove DRMP3_DEFAULT_CHANNELS and DRMP3_DEFAULT_SAMPLE_RATE which are leftovers from some removed APIs. + +v0.6.24 - 2020-12-07 + - Fix a typo in version date for 0.6.23. + +v0.6.23 - 2020-12-03 + - Fix an error where a file can be closed twice when initialization of the decoder fails. + +v0.6.22 - 2020-12-02 + - Fix an error where it's possible for a file handle to be left open when initialization of the decoder fails. + +v0.6.21 - 2020-11-28 + - Bring up to date with minimp3. + +v0.6.20 - 2020-11-21 + - Fix compilation with OpenWatcom. + +v0.6.19 - 2020-11-13 + - Minor code clean up. + +v0.6.18 - 2020-11-01 + - Improve compiler support for older versions of GCC. + +v0.6.17 - 2020-09-28 + - Bring up to date with minimp3. + +v0.6.16 - 2020-08-02 + - Simplify sized types. + +v0.6.15 - 2020-07-25 + - Fix a compilation warning. + +v0.6.14 - 2020-07-23 + - Fix undefined behaviour with memmove(). + +v0.6.13 - 2020-07-06 + - Fix a bug when converting from s16 to f32 in drmp3_read_pcm_frames_f32(). + +v0.6.12 - 2020-06-23 + - Add include guard for the implementation section. + +v0.6.11 - 2020-05-26 + - Fix use of uninitialized variable error. + +v0.6.10 - 2020-05-16 + - Add compile-time and run-time version querying. + - DRMP3_VERSION_MINOR + - DRMP3_VERSION_MAJOR + - DRMP3_VERSION_REVISION + - DRMP3_VERSION_STRING + - drmp3_version() + - drmp3_version_string() + +v0.6.9 - 2020-04-30 + - Change the `pcm` parameter of drmp3dec_decode_frame() to a `const drmp3_uint8*` for consistency with internal APIs. + +v0.6.8 - 2020-04-26 + - Optimizations to decoding when initializing from memory. + +v0.6.7 - 2020-04-25 + - Fix a compilation error with DR_MP3_NO_STDIO + - Optimization to decoding by reducing some data movement. + +v0.6.6 - 2020-04-23 + - Fix a minor bug with the running PCM frame counter. + +v0.6.5 - 2020-04-19 + - Fix compilation error on ARM builds. + +v0.6.4 - 2020-04-19 + - Bring up to date with changes to minimp3. + +v0.6.3 - 2020-04-13 + - Fix some pedantic warnings. + +v0.6.2 - 2020-04-10 + - Fix a crash in drmp3_open_*_and_read_pcm_frames_*() if the output config object is NULL. + +v0.6.1 - 2020-04-05 + - Fix warnings. + +v0.6.0 - 2020-04-04 + - API CHANGE: Remove the pConfig parameter from the following APIs: + - drmp3_init() + - drmp3_init_memory() + - drmp3_init_file() + - Add drmp3_init_file_w() for opening a file from a wchar_t encoded path. + +v0.5.6 - 2020-02-12 + - Bring up to date with minimp3. + +v0.5.5 - 2020-01-29 + - Fix a memory allocation bug in high level s16 decoding APIs. + +v0.5.4 - 2019-12-02 + - Fix a possible null pointer dereference when using custom memory allocators for realloc(). + +v0.5.3 - 2019-11-14 + - Fix typos in documentation. + +v0.5.2 - 2019-11-02 + - Bring up to date with minimp3. + +v0.5.1 - 2019-10-08 + - Fix a warning with GCC. + +v0.5.0 - 2019-10-07 + - API CHANGE: Add support for user defined memory allocation routines. This system allows the program to specify their own memory allocation + routines with a user data pointer for client-specific contextual data. This adds an extra parameter to the end of the following APIs: + - drmp3_init() + - drmp3_init_file() + - drmp3_init_memory() + - drmp3_open_and_read_pcm_frames_f32() + - drmp3_open_and_read_pcm_frames_s16() + - drmp3_open_memory_and_read_pcm_frames_f32() + - drmp3_open_memory_and_read_pcm_frames_s16() + - drmp3_open_file_and_read_pcm_frames_f32() + - drmp3_open_file_and_read_pcm_frames_s16() + - API CHANGE: Renamed the following APIs: + - drmp3_open_and_read_f32() -> drmp3_open_and_read_pcm_frames_f32() + - drmp3_open_and_read_s16() -> drmp3_open_and_read_pcm_frames_s16() + - drmp3_open_memory_and_read_f32() -> drmp3_open_memory_and_read_pcm_frames_f32() + - drmp3_open_memory_and_read_s16() -> drmp3_open_memory_and_read_pcm_frames_s16() + - drmp3_open_file_and_read_f32() -> drmp3_open_file_and_read_pcm_frames_f32() + - drmp3_open_file_and_read_s16() -> drmp3_open_file_and_read_pcm_frames_s16() + +v0.4.7 - 2019-07-28 + - Fix a compiler error. + +v0.4.6 - 2019-06-14 + - Fix a compiler error. + +v0.4.5 - 2019-06-06 + - Bring up to date with minimp3. + +v0.4.4 - 2019-05-06 + - Fixes to the VC6 build. + +v0.4.3 - 2019-05-05 + - Use the channel count and/or sample rate of the first MP3 frame instead of DRMP3_DEFAULT_CHANNELS and + DRMP3_DEFAULT_SAMPLE_RATE when they are set to 0. To use the old behaviour, just set the relevant property to + DRMP3_DEFAULT_CHANNELS or DRMP3_DEFAULT_SAMPLE_RATE. + - Add s16 reading APIs + - drmp3_read_pcm_frames_s16 + - drmp3_open_memory_and_read_pcm_frames_s16 + - drmp3_open_and_read_pcm_frames_s16 + - drmp3_open_file_and_read_pcm_frames_s16 + - Add drmp3_get_mp3_and_pcm_frame_count() to the public header section. + - Add support for C89. + - Change license to choice of public domain or MIT-0. + +v0.4.2 - 2019-02-21 + - Fix a warning. + +v0.4.1 - 2018-12-30 + - Fix a warning. + +v0.4.0 - 2018-12-16 + - API CHANGE: Rename some APIs: + - drmp3_read_f32 -> to drmp3_read_pcm_frames_f32 + - drmp3_seek_to_frame -> drmp3_seek_to_pcm_frame + - drmp3_open_and_decode_f32 -> drmp3_open_and_read_pcm_frames_f32 + - drmp3_open_and_decode_memory_f32 -> drmp3_open_memory_and_read_pcm_frames_f32 + - drmp3_open_and_decode_file_f32 -> drmp3_open_file_and_read_pcm_frames_f32 + - Add drmp3_get_pcm_frame_count(). + - Add drmp3_get_mp3_frame_count(). + - Improve seeking performance. + +v0.3.2 - 2018-09-11 + - Fix a couple of memory leaks. + - Bring up to date with minimp3. + +v0.3.1 - 2018-08-25 + - Fix C++ build. + +v0.3.0 - 2018-08-25 + - Bring up to date with minimp3. This has a minor API change: the "pcm" parameter of drmp3dec_decode_frame() has + been changed from short* to void* because it can now output both s16 and f32 samples, depending on whether or + not the DR_MP3_FLOAT_OUTPUT option is set. + +v0.2.11 - 2018-08-08 + - Fix a bug where the last part of a file is not read. + +v0.2.10 - 2018-08-07 + - Improve 64-bit detection. + +v0.2.9 - 2018-08-05 + - Fix C++ build on older versions of GCC. + - Bring up to date with minimp3. + +v0.2.8 - 2018-08-02 + - Fix compilation errors with older versions of GCC. + +v0.2.7 - 2018-07-13 + - Bring up to date with minimp3. + +v0.2.6 - 2018-07-12 + - Bring up to date with minimp3. + +v0.2.5 - 2018-06-22 + - Bring up to date with minimp3. + +v0.2.4 - 2018-05-12 + - Bring up to date with minimp3. + +v0.2.3 - 2018-04-29 + - Fix TCC build. + +v0.2.2 - 2018-04-28 + - Fix bug when opening a decoder from memory. + +v0.2.1 - 2018-04-27 + - Efficiency improvements when the decoder reaches the end of the stream. + +v0.2 - 2018-04-21 + - Bring up to date with minimp3. + - Start using major.minor.revision versioning. + +v0.1d - 2018-03-30 + - Bring up to date with minimp3. + +v0.1c - 2018-03-11 + - Fix C++ build error. + +v0.1b - 2018-03-07 + - Bring up to date with minimp3. + +v0.1a - 2018-02-28 + - Fix compilation error on GCC/Clang. + - Fix some warnings. + +v0.1 - 2018-02-xx + - Initial versioned release. +*/ + +/* +This software is available as a choice of the following licenses. Choose +whichever you prefer. + +=============================================================================== +ALTERNATIVE 1 - Public Domain (www.unlicense.org) +=============================================================================== +This is free and unencumbered software released into the public domain. + +Anyone is free to copy, modify, publish, use, compile, sell, or distribute this +software, either in source code form or as a compiled binary, for any purpose, +commercial or non-commercial, and by any means. + +In jurisdictions that recognize copyright laws, the author or authors of this +software dedicate any and all copyright interest in the software to the public +domain. We make this dedication for the benefit of the public at large and to +the detriment of our heirs and successors. We intend this dedication to be an +overt act of relinquishment in perpetuity of all present and future rights to +this software under copyright law. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN +ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +For more information, please refer to + +=============================================================================== +ALTERNATIVE 2 - MIT No Attribution +=============================================================================== +Copyright 2023 David Reid + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ + +/* + https://github.com/lieff/minimp3 + To the extent possible under law, the author(s) have dedicated all copyright and related and neighboring rights to this software to the public domain worldwide. + This software is distributed without any warranty. + See . +*/ \ No newline at end of file diff --git a/include/gfx_dimensions.h b/include/gfx_dimensions.h index 4435e14e..d19808a3 100644 --- a/include/gfx_dimensions.h +++ b/include/gfx_dimensions.h @@ -20,4 +20,5 @@ Note that RECT commands must be enhanced to support negative coordinates with th #define GFX_DIMENSIONS_RECT_FROM_LEFT_EDGE(v) ((int)floorf(GFX_DIMENSIONS_FROM_LEFT_EDGE(v))) #define GFX_DIMENSIONS_RECT_FROM_RIGHT_EDGE(v) ((int)ceilf(GFX_DIMENSIONS_FROM_RIGHT_EDGE(v))) // If screen is taller than it is wide, radius should be equal to SCREEN_HEIGHT since we scale horizontally -#define GFX_DIMENSIONS_FULL_RADIUS (SCREEN_HEIGHT * (GFX_DIMENSIONS_ASPECT_RATIO > 1 ? GFX_DIMENSIONS_ASPECT_RATIO : 1)) \ No newline at end of file +#define GFX_DIMENSIONS_FULL_RADIUS (SCREEN_HEIGHT * (GFX_DIMENSIONS_ASPECT_RATIO > 1 ? GFX_DIMENSIONS_ASPECT_RATIO : 1)) +#define GFX_FULL_WIDTH GFX_DIMENSIONS_FROM_RIGHT_EDGE(0) \ No newline at end of file diff --git a/include/samples_table.h b/include/samples_table.h new file mode 100644 index 00000000..5f1a3aeb --- /dev/null +++ b/include/samples_table.h @@ -0,0 +1,717 @@ +#pragma once +#include "align_asset_macro.h" + +#define dgSample00 "__OTR__sound/samples/sfx_1/00_twirl" +static const ALIGN_ASSET(2) char gSample00[] = dgSample00; + +#define dgSample01 "__OTR__sound/samples/sfx_1/01_brushing" +static const ALIGN_ASSET(2) char gSample01[] = dgSample01; + +#define dgSample02 "__OTR__sound/samples/sfx_1/02_hand_touch" +static const ALIGN_ASSET(2) char gSample02[] = dgSample02; + +#define dgSample03 "__OTR__sound/samples/sfx_1/03_yoshi" +static const ALIGN_ASSET(2) char gSample03[] = dgSample03; + +#define dgSample04 "__OTR__sound/samples/sfx_1/04_plop" +static const ALIGN_ASSET(2) char gSample04[] = dgSample04; + +#define dgSample05 "__OTR__sound/samples/sfx_1/05_heavy_landing" +static const ALIGN_ASSET(2) char gSample05[] = dgSample05; + +#define dgSample06 "__OTR__sound/samples/sfx_terrain/00_step_default" +static const ALIGN_ASSET(2) char gSample06[] = dgSample06; + +#define dgSample07 "__OTR__sound/samples/sfx_terrain/01_step_grass" +static const ALIGN_ASSET(2) char gSample07[] = dgSample07; + +#define dgSample08 "__OTR__sound/samples/sfx_terrain/02_step_stone" +static const ALIGN_ASSET(2) char gSample08[] = dgSample08; + +#define dgSample09 "__OTR__sound/samples/sfx_terrain/03_step_spooky" +static const ALIGN_ASSET(2) char gSample09[] = dgSample09; + +#define dgSample0A "__OTR__sound/samples/sfx_terrain/04_step_snow" +static const ALIGN_ASSET(2) char gSample0A[] = dgSample0A; + +#define dgSample0B "__OTR__sound/samples/sfx_terrain/05_step_ice" +static const ALIGN_ASSET(2) char gSample0B[] = dgSample0B; + +#define dgSample0C "__OTR__sound/samples/sfx_terrain/06_step_metal" +static const ALIGN_ASSET(2) char gSample0C[] = dgSample0C; + +#define dgSample0D "__OTR__sound/samples/sfx_terrain/07_step_sand" +static const ALIGN_ASSET(2) char gSample0D[] = dgSample0D; + +#define dgSample0E "__OTR__sound/samples/sfx_water/00_plunge" +static const ALIGN_ASSET(2) char gSample0E[] = dgSample0E; + +#define dgSample0F "__OTR__sound/samples/sfx_water/01_splash" +static const ALIGN_ASSET(2) char gSample0F[] = dgSample0F; + +#define dgSample10 "__OTR__sound/samples/sfx_water/02_swim" +static const ALIGN_ASSET(2) char gSample10[] = dgSample10; + +#define dgSample11 "__OTR__sound/samples/sfx_4/00" +static const ALIGN_ASSET(2) char gSample11[] = dgSample11; + +#define dgSample12 "__OTR__sound/samples/sfx_4/01" +static const ALIGN_ASSET(2) char gSample12[] = dgSample12; + +#define dgSample13 "__OTR__sound/samples/sfx_4/02" +static const ALIGN_ASSET(2) char gSample13[] = dgSample13; + +#define dgSample14 "__OTR__sound/samples/sfx_4/03" +static const ALIGN_ASSET(2) char gSample14[] = dgSample14; + +#define dgSample15 "__OTR__sound/samples/sfx_4/04" +static const ALIGN_ASSET(2) char gSample15[] = dgSample15; + +#define dgSample16 "__OTR__sound/samples/sfx_4/05" +static const ALIGN_ASSET(2) char gSample16[] = dgSample16; + +#define dgSample17 "__OTR__sound/samples/sfx_4/06" +static const ALIGN_ASSET(2) char gSample17[] = dgSample17; + +#define dgSample18 "__OTR__sound/samples/sfx_4/07" +static const ALIGN_ASSET(2) char gSample18[] = dgSample18; + +#define dgSample19 "__OTR__sound/samples/sfx_4/08" +static const ALIGN_ASSET(2) char gSample19[] = dgSample19; + +#define dgSample1A "__OTR__sound/samples/sfx_4/09" +static const ALIGN_ASSET(2) char gSample1A[] = dgSample1A; + +#define dgSample1B "__OTR__sound/samples/sfx_5/00" +static const ALIGN_ASSET(2) char gSample1B[] = dgSample1B; + +#define dgSample1C "__OTR__sound/samples/sfx_5/01" +static const ALIGN_ASSET(2) char gSample1C[] = dgSample1C; + +#define dgSample1D "__OTR__sound/samples/sfx_5/02" +static const ALIGN_ASSET(2) char gSample1D[] = dgSample1D; + +#define dgSample1E "__OTR__sound/samples/sfx_5/03" +static const ALIGN_ASSET(2) char gSample1E[] = dgSample1E; + +#define dgSample1F "__OTR__sound/samples/sfx_5/04" +static const ALIGN_ASSET(2) char gSample1F[] = dgSample1F; + +#define dgSample20 "__OTR__sound/samples/sfx_5/05" +static const ALIGN_ASSET(2) char gSample20[] = dgSample20; + +#define dgSample21 "__OTR__sound/samples/sfx_5/06" +static const ALIGN_ASSET(2) char gSample21[] = dgSample21; + +#define dgSample22 "__OTR__sound/samples/sfx_5/07" +static const ALIGN_ASSET(2) char gSample22[] = dgSample22; + +#define dgSample23 "__OTR__sound/samples/sfx_5/08" +static const ALIGN_ASSET(2) char gSample23[] = dgSample23; + +#define dgSample24 "__OTR__sound/samples/sfx_5/09" +static const ALIGN_ASSET(2) char gSample24[] = dgSample24; + +#define dgSample25 "__OTR__sound/samples/sfx_5/0A" +static const ALIGN_ASSET(2) char gSample25[] = dgSample25; + +#define dgSample26 "__OTR__sound/samples/sfx_5/0B" +static const ALIGN_ASSET(2) char gSample26[] = dgSample26; + +#define dgSample27 "__OTR__sound/samples/sfx_5/0C" +static const ALIGN_ASSET(2) char gSample27[] = dgSample27; + +#define dgSample28 "__OTR__sound/samples/sfx_5/0D" +static const ALIGN_ASSET(2) char gSample28[] = dgSample28; + +#define dgSample29 "__OTR__sound/samples/sfx_5/0E" +static const ALIGN_ASSET(2) char gSample29[] = dgSample29; + +#define dgSample2A "__OTR__sound/samples/sfx_5/0F" +static const ALIGN_ASSET(2) char gSample2A[] = dgSample2A; + +#define dgSample2B "__OTR__sound/samples/sfx_5/10" +static const ALIGN_ASSET(2) char gSample2B[] = dgSample2B; + +#define dgSample2C "__OTR__sound/samples/sfx_5/11" +static const ALIGN_ASSET(2) char gSample2C[] = dgSample2C; + +#define dgSample2D "__OTR__sound/samples/sfx_5/12" +static const ALIGN_ASSET(2) char gSample2D[] = dgSample2D; + +#define dgSample2E "__OTR__sound/samples/sfx_5/13" +static const ALIGN_ASSET(2) char gSample2E[] = dgSample2E; + +#define dgSample2F "__OTR__sound/samples/sfx_5/14" +static const ALIGN_ASSET(2) char gSample2F[] = dgSample2F; + +#define dgSample30 "__OTR__sound/samples/sfx_5/15" +static const ALIGN_ASSET(2) char gSample30[] = dgSample30; + +#define dgSample31 "__OTR__sound/samples/sfx_5/16" +static const ALIGN_ASSET(2) char gSample31[] = dgSample31; + +#define dgSample32 "__OTR__sound/samples/sfx_5/17" +static const ALIGN_ASSET(2) char gSample32[] = dgSample32; + +#define dgSample33 "__OTR__sound/samples/sfx_5/18" +static const ALIGN_ASSET(2) char gSample33[] = dgSample33; + +#define dgSample34 "__OTR__sound/samples/sfx_5/19" +static const ALIGN_ASSET(2) char gSample34[] = dgSample34; + +#define dgSample35 "__OTR__sound/samples/sfx_5/1A" +static const ALIGN_ASSET(2) char gSample35[] = dgSample35; + +#define dgSample36 "__OTR__sound/samples/sfx_5/1B" +static const ALIGN_ASSET(2) char gSample36[] = dgSample36; + +#define dgSample37 "__OTR__sound/samples/sfx_5/1C" +static const ALIGN_ASSET(2) char gSample37[] = dgSample37; + +#define dgSample38 "__OTR__sound/samples/sfx_6/00" +static const ALIGN_ASSET(2) char gSample38[] = dgSample38; + +#define dgSample39 "__OTR__sound/samples/sfx_6/01" +static const ALIGN_ASSET(2) char gSample39[] = dgSample39; + +#define dgSample3A "__OTR__sound/samples/sfx_6/02" +static const ALIGN_ASSET(2) char gSample3A[] = dgSample3A; + +#define dgSample3B "__OTR__sound/samples/sfx_6/03" +static const ALIGN_ASSET(2) char gSample3B[] = dgSample3B; + +#define dgSample3C "__OTR__sound/samples/sfx_6/04" +static const ALIGN_ASSET(2) char gSample3C[] = dgSample3C; + +#define dgSample3D "__OTR__sound/samples/sfx_6/05" +static const ALIGN_ASSET(2) char gSample3D[] = dgSample3D; + +#define dgSample3E "__OTR__sound/samples/sfx_6/06" +static const ALIGN_ASSET(2) char gSample3E[] = dgSample3E; + +#define dgSample3F "__OTR__sound/samples/sfx_6/07" +static const ALIGN_ASSET(2) char gSample3F[] = dgSample3F; + +#define dgSample40 "__OTR__sound/samples/sfx_6/08" +static const ALIGN_ASSET(2) char gSample40[] = dgSample40; + +#define dgSample41 "__OTR__sound/samples/sfx_6/09" +static const ALIGN_ASSET(2) char gSample41[] = dgSample41; + +#define dgSample42 "__OTR__sound/samples/sfx_6/0A" +static const ALIGN_ASSET(2) char gSample42[] = dgSample42; + +#define dgSample43 "__OTR__sound/samples/sfx_6/0B" +static const ALIGN_ASSET(2) char gSample43[] = dgSample43; + +#define dgSample44 "__OTR__sound/samples/sfx_6/0C" +static const ALIGN_ASSET(2) char gSample44[] = dgSample44; + +#define dgSample45 "__OTR__sound/samples/sfx_6/0D" +static const ALIGN_ASSET(2) char gSample45[] = dgSample45; + +#define dgSample46 "__OTR__sound/samples/sfx_7/00" +static const ALIGN_ASSET(2) char gSample46[] = dgSample46; + +#define dgSample47 "__OTR__sound/samples/sfx_7/01" +static const ALIGN_ASSET(2) char gSample47[] = dgSample47; + +#define dgSample48 "__OTR__sound/samples/sfx_7/02" +static const ALIGN_ASSET(2) char gSample48[] = dgSample48; + +#define dgSample49 "__OTR__sound/samples/sfx_7/03" +static const ALIGN_ASSET(2) char gSample49[] = dgSample49; + +#define dgSample4A "__OTR__sound/samples/sfx_7/04" +static const ALIGN_ASSET(2) char gSample4A[] = dgSample4A; + +#define dgSample4B "__OTR__sound/samples/sfx_7/05" +static const ALIGN_ASSET(2) char gSample4B[] = dgSample4B; + +#define dgSample4C "__OTR__sound/samples/sfx_7/06" +static const ALIGN_ASSET(2) char gSample4C[] = dgSample4C; + +#define dgSample4D "__OTR__sound/samples/sfx_7/07" +static const ALIGN_ASSET(2) char gSample4D[] = dgSample4D; + +#define dgSample4E "__OTR__sound/samples/sfx_7/08" +static const ALIGN_ASSET(2) char gSample4E[] = dgSample4E; + +#define dgSample4F "__OTR__sound/samples/sfx_7/09" +static const ALIGN_ASSET(2) char gSample4F[] = dgSample4F; + +#define dgSample50 "__OTR__sound/samples/sfx_7/0A" +static const ALIGN_ASSET(2) char gSample50[] = dgSample50; + +#define dgSample51 "__OTR__sound/samples/sfx_7/0B" +static const ALIGN_ASSET(2) char gSample51[] = dgSample51; + +#define dgSample52 "__OTR__sound/samples/sfx_7/0C" +static const ALIGN_ASSET(2) char gSample52[] = dgSample52; + +#define dgSample53 "__OTR__sound/samples/sfx_7/0D_chain_chomp_bark" +static const ALIGN_ASSET(2) char gSample53[] = dgSample53; + +#define dgSample54 "__OTR__sound/samples/sfx_mario/00_mario_jump_hoo" +static const ALIGN_ASSET(2) char gSample54[] = dgSample54; + +#define dgSample55 "__OTR__sound/samples/sfx_mario/01_mario_jump_wah" +static const ALIGN_ASSET(2) char gSample55[] = dgSample55; + +#define dgSample56 "__OTR__sound/samples/sfx_mario/02_mario_yah" +static const ALIGN_ASSET(2) char gSample56[] = dgSample56; + +#define dgSample57 "__OTR__sound/samples/sfx_mario/03_mario_haha" +static const ALIGN_ASSET(2) char gSample57[] = dgSample57; + +#define dgSample58 "__OTR__sound/samples/sfx_mario/04_mario_yahoo" +static const ALIGN_ASSET(2) char gSample58[] = dgSample58; + +#define dgSample59 "__OTR__sound/samples/sfx_mario/05_mario_uh" +static const ALIGN_ASSET(2) char gSample59[] = dgSample59; + +#define dgSample5A "__OTR__sound/samples/sfx_mario/06_mario_hrmm" +static const ALIGN_ASSET(2) char gSample5A[] = dgSample5A; + +#define dgSample5B "__OTR__sound/samples/sfx_mario/07_mario_wah2" +static const ALIGN_ASSET(2) char gSample5B[] = dgSample5B; + +#define dgSample5C "__OTR__sound/samples/sfx_mario/08_mario_whoa" +static const ALIGN_ASSET(2) char gSample5C[] = dgSample5C; + +#define dgSample5D "__OTR__sound/samples/sfx_mario/09_mario_eeuh" +static const ALIGN_ASSET(2) char gSample5D[] = dgSample5D; + +#define dgSample5E "__OTR__sound/samples/sfx_mario/0A_mario_attacked" +static const ALIGN_ASSET(2) char gSample5E[] = dgSample5E; + +#define dgSample5F "__OTR__sound/samples/sfx_mario/0B_mario_ooof" +static const ALIGN_ASSET(2) char gSample5F[] = dgSample5F; + +#define dgSample60 "__OTR__sound/samples/sfx_mario/0C_mario_here_we_go" +static const ALIGN_ASSET(2) char gSample60[] = dgSample60; + +#define dgSample61 "__OTR__sound/samples/sfx_mario/0D_mario_yawning" +static const ALIGN_ASSET(2) char gSample61[] = dgSample61; + +#define dgSample62 "__OTR__sound/samples/sfx_mario/0E_mario_snoring1" +static const ALIGN_ASSET(2) char gSample62[] = dgSample62; + +#define dgSample63 "__OTR__sound/samples/sfx_mario/0F_mario_snoring2" +static const ALIGN_ASSET(2) char gSample63[] = dgSample63; + +#define dgSample64 "__OTR__sound/samples/sfx_mario/10_mario_doh" +static const ALIGN_ASSET(2) char gSample64[] = dgSample64; + +#define dgSample65 "__OTR__sound/samples/sfx_mario/11_mario_game_over" +static const ALIGN_ASSET(2) char gSample65[] = dgSample65; + +#define dgSample66 "__OTR__sound/samples/sfx_mario/12_mario_hello" +static const ALIGN_ASSET(2) char gSample66[] = dgSample66; + +#define dgSample67 "__OTR__sound/samples/sfx_mario/13_mario_press_start_to_play" +static const ALIGN_ASSET(2) char gSample67[] = dgSample67; + +#define dgSample68 "__OTR__sound/samples/sfx_mario/14_mario_twirl_bounce" +static const ALIGN_ASSET(2) char gSample68[] = dgSample68; + +#define dgSample69 "__OTR__sound/samples/sfx_mario/15_mario_snoring3" +static const ALIGN_ASSET(2) char gSample69[] = dgSample69; + +#define dgSample6A "__OTR__sound/samples/sfx_mario/16_mario_so_longa_bowser" +static const ALIGN_ASSET(2) char gSample6A[] = dgSample6A; + +#define dgSample6B "__OTR__sound/samples/sfx_mario/17_mario_ima_tired" +static const ALIGN_ASSET(2) char gSample6B[] = dgSample6B; + +#define dgSample6C "__OTR__sound/samples/sfx_mario/18_mario_waha" +static const ALIGN_ASSET(2) char gSample6C[] = dgSample6C; + +#define dgSample6D "__OTR__sound/samples/sfx_mario/19_mario_yippee" +static const ALIGN_ASSET(2) char gSample6D[] = dgSample6D; + +#define dgSample6E "__OTR__sound/samples/sfx_mario/1A_mario_lets_a_go" +static const ALIGN_ASSET(2) char gSample6E[] = dgSample6E; + +#define dgSample6F "__OTR__sound/samples/sfx_9/00" +static const ALIGN_ASSET(2) char gSample6F[] = dgSample6F; + +#define dgSample70 "__OTR__sound/samples/sfx_9/01" +static const ALIGN_ASSET(2) char gSample70[] = dgSample70; + +#define dgSample71 "__OTR__sound/samples/sfx_9/02" +static const ALIGN_ASSET(2) char gSample71[] = dgSample71; + +#define dgSample72 "__OTR__sound/samples/sfx_9/03" +static const ALIGN_ASSET(2) char gSample72[] = dgSample72; + +#define dgSample73 "__OTR__sound/samples/sfx_9/04_camera_buzz" +static const ALIGN_ASSET(2) char gSample73[] = dgSample73; + +#define dgSample74 "__OTR__sound/samples/sfx_9/05_camera_shutter" +static const ALIGN_ASSET(2) char gSample74[] = dgSample74; + +#define dgSample75 "__OTR__sound/samples/sfx_9/06" +static const ALIGN_ASSET(2) char gSample75[] = dgSample75; + +#define dgSample76 "__OTR__sound/samples/sfx_mario_peach/00_mario_waaaooow" +static const ALIGN_ASSET(2) char gSample76[] = dgSample76; + +#define dgSample77 "__OTR__sound/samples/sfx_mario_peach/01_mario_hoohoo" +static const ALIGN_ASSET(2) char gSample77[] = dgSample77; + +#define dgSample78 "__OTR__sound/samples/sfx_mario_peach/02_mario_panting" +static const ALIGN_ASSET(2) char gSample78[] = dgSample78; + +#define dgSample79 "__OTR__sound/samples/sfx_mario_peach/03_mario_dying" +static const ALIGN_ASSET(2) char gSample79[] = dgSample79; + +#define dgSample7A "__OTR__sound/samples/sfx_mario_peach/04_mario_on_fire" +static const ALIGN_ASSET(2) char gSample7A[] = dgSample7A; + +#define dgSample7B "__OTR__sound/samples/sfx_mario_peach/05_mario_uh2" +static const ALIGN_ASSET(2) char gSample7B[] = dgSample7B; + +#define dgSample7C "__OTR__sound/samples/sfx_mario_peach/06_mario_coughing" +static const ALIGN_ASSET(2) char gSample7C[] = dgSample7C; + +#define dgSample7D "__OTR__sound/samples/sfx_mario_peach/07_mario_its_a_me_mario" +static const ALIGN_ASSET(2) char gSample7D[] = dgSample7D; + +#define dgSample7E "__OTR__sound/samples/sfx_mario_peach/08_mario_punch_yah" +static const ALIGN_ASSET(2) char gSample7E[] = dgSample7E; + +#define dgSample7F "__OTR__sound/samples/sfx_mario_peach/09_mario_punch_hoo" +static const ALIGN_ASSET(2) char gSample7F[] = dgSample7F; + +#define dgSample80 "__OTR__sound/samples/sfx_mario_peach/0A_mario_mama_mia" +static const ALIGN_ASSET(2) char gSample80[] = dgSample80; + +#define dgSample81 "__OTR__sound/samples/sfx_mario_peach/0B_mario_okey_dokey" +static const ALIGN_ASSET(2) char gSample81[] = dgSample81; + +#define dgSample82 "__OTR__sound/samples/sfx_mario_peach/0C_mario_drowning" +static const ALIGN_ASSET(2) char gSample82[] = dgSample82; + +#define dgSample83 "__OTR__sound/samples/sfx_mario_peach/0D_mario_thank_you_playing_my_game" +static const ALIGN_ASSET(2) char gSample83[] = dgSample83; + +#define dgSample84 "__OTR__sound/samples/sfx_mario_peach/0E_peach_dear_mario" +static const ALIGN_ASSET(2) char gSample84[] = dgSample84; + +#define dgSample85 "__OTR__sound/samples/sfx_mario_peach/0F_peach_mario" +static const ALIGN_ASSET(2) char gSample85[] = dgSample85; + +#define dgSample86 "__OTR__sound/samples/sfx_mario_peach/10_peach_power_of_the_stars" +static const ALIGN_ASSET(2) char gSample86[] = dgSample86; + +#define dgSample87 "__OTR__sound/samples/sfx_mario_peach/11_peach_thanks_to_you" +static const ALIGN_ASSET(2) char gSample87[] = dgSample87; + +#define dgSample88 "__OTR__sound/samples/sfx_mario_peach/12_peach_thank_you_mario" +static const ALIGN_ASSET(2) char gSample88[] = dgSample88; + +#define dgSample89 "__OTR__sound/samples/sfx_mario_peach/13_peach_something_special" +static const ALIGN_ASSET(2) char gSample89[] = dgSample89; + +#define dgSample8A "__OTR__sound/samples/sfx_mario_peach/14_peach_bake_a_cake" +static const ALIGN_ASSET(2) char gSample8A[] = dgSample8A; + +#define dgSample8B "__OTR__sound/samples/sfx_mario_peach/15_peach_for_mario" +static const ALIGN_ASSET(2) char gSample8B[] = dgSample8B; + +#define dgSample8C "__OTR__sound/samples/sfx_mario_peach/16_peach_mario2" +static const ALIGN_ASSET(2) char gSample8C[] = dgSample8C; + +#define dgSample8D "__OTR__sound/samples/instruments/00" +static const ALIGN_ASSET(2) char gSample8D[] = dgSample8D; + +#define dgSample8E "__OTR__sound/samples/instruments/01_banjo_1" +static const ALIGN_ASSET(2) char gSample8E[] = dgSample8E; + +#define dgSample8F "__OTR__sound/samples/instruments/02" +static const ALIGN_ASSET(2) char gSample8F[] = dgSample8F; + +#define dgSample90 "__OTR__sound/samples/instruments/03_human_whistle" +static const ALIGN_ASSET(2) char gSample90[] = dgSample90; + +#define dgSample91 "__OTR__sound/samples/instruments/04_bright_piano" +static const ALIGN_ASSET(2) char gSample91[] = dgSample91; + +#define dgSample92 "__OTR__sound/samples/instruments/05_acoustic_bass" +static const ALIGN_ASSET(2) char gSample92[] = dgSample92; + +#define dgSample93 "__OTR__sound/samples/instruments/06_kick_drum_1" +static const ALIGN_ASSET(2) char gSample93[] = dgSample93; + +#define dgSample94 "__OTR__sound/samples/instruments/07_rimshot" +static const ALIGN_ASSET(2) char gSample94[] = dgSample94; + +#define dgSample95 "__OTR__sound/samples/instruments/08" +static const ALIGN_ASSET(2) char gSample95[] = dgSample95; + +#define dgSample96 "__OTR__sound/samples/instruments/09" +static const ALIGN_ASSET(2) char gSample96[] = dgSample96; + +#define dgSample97 "__OTR__sound/samples/instruments/0A_tambourine" +static const ALIGN_ASSET(2) char gSample97[] = dgSample97; + +#define dgSample98 "__OTR__sound/samples/instruments/0B" +static const ALIGN_ASSET(2) char gSample98[] = dgSample98; + +#define dgSample99 "__OTR__sound/samples/instruments/0C_conga_stick" +static const ALIGN_ASSET(2) char gSample99[] = dgSample99; + +#define dgSample9A "__OTR__sound/samples/instruments/0D_clave" +static const ALIGN_ASSET(2) char gSample9A[] = dgSample9A; + +#define dgSample9B "__OTR__sound/samples/instruments/0E_hihat_closed" +static const ALIGN_ASSET(2) char gSample9B[] = dgSample9B; + +#define dgSample9C "__OTR__sound/samples/instruments/0F_hihat_open" +static const ALIGN_ASSET(2) char gSample9C[] = dgSample9C; + +#define dgSample9D "__OTR__sound/samples/instruments/10_cymbal_bell" +static const ALIGN_ASSET(2) char gSample9D[] = dgSample9D; + +#define dgSample9E "__OTR__sound/samples/instruments/11_splash_cymbal" +static const ALIGN_ASSET(2) char gSample9E[] = dgSample9E; + +#define dgSample9F "__OTR__sound/samples/instruments/12_snare_drum_1" +static const ALIGN_ASSET(2) char gSample9F[] = dgSample9F; + +#define dgSampleA0 "__OTR__sound/samples/instruments/13_snare_drum_2" +static const ALIGN_ASSET(2) char gSampleA0[] = dgSampleA0; + +#define dgSampleA1 "__OTR__sound/samples/instruments/14_strings_5" +static const ALIGN_ASSET(2) char gSampleA1[] = dgSampleA1; + +#define dgSampleA2 "__OTR__sound/samples/instruments/15_strings_4" +static const ALIGN_ASSET(2) char gSampleA2[] = dgSampleA2; + +#define dgSampleA3 "__OTR__sound/samples/instruments/16_french_horns" +static const ALIGN_ASSET(2) char gSampleA3[] = dgSampleA3; + +#define dgSampleA4 "__OTR__sound/samples/instruments/17_trumpet" +static const ALIGN_ASSET(2) char gSampleA4[] = dgSampleA4; + +#define dgSampleA5 "__OTR__sound/samples/instruments/18_timpani" +static const ALIGN_ASSET(2) char gSampleA5[] = dgSampleA5; + +#define dgSampleA6 "__OTR__sound/samples/instruments/19_brass" +static const ALIGN_ASSET(2) char gSampleA6[] = dgSampleA6; + +#define dgSampleA7 "__OTR__sound/samples/instruments/1A_slap_bass" +static const ALIGN_ASSET(2) char gSampleA7[] = dgSampleA7; + +#define dgSampleA8 "__OTR__sound/samples/instruments/1B_organ_2" +static const ALIGN_ASSET(2) char gSampleA8[] = dgSampleA8; + +#define dgSampleA9 "__OTR__sound/samples/instruments/1C" +static const ALIGN_ASSET(2) char gSampleA9[] = dgSampleA9; + +#define dgSampleAA "__OTR__sound/samples/instruments/1D" +static const ALIGN_ASSET(2) char gSampleAA[] = dgSampleAA; + +#define dgSampleAB "__OTR__sound/samples/instruments/1E_closed_triangle" +static const ALIGN_ASSET(2) char gSampleAB[] = dgSampleAB; + +#define dgSampleAC "__OTR__sound/samples/instruments/1F_open_triangle" +static const ALIGN_ASSET(2) char gSampleAC[] = dgSampleAC; + +#define dgSampleAD "__OTR__sound/samples/instruments/20_cabasa" +static const ALIGN_ASSET(2) char gSampleAD[] = dgSampleAD; + +#define dgSampleAE "__OTR__sound/samples/instruments/21_sine_bass" +static const ALIGN_ASSET(2) char gSampleAE[] = dgSampleAE; + +#define dgSampleAF "__OTR__sound/samples/instruments/22_boys_choir" +static const ALIGN_ASSET(2) char gSampleAF[] = dgSampleAF; + +#define dgSampleB0 "__OTR__sound/samples/instruments/23_strings_1" +static const ALIGN_ASSET(2) char gSampleB0[] = dgSampleB0; + +#define dgSampleB1 "__OTR__sound/samples/instruments/24_strings_2" +static const ALIGN_ASSET(2) char gSampleB1[] = dgSampleB1; + +#define dgSampleB2 "__OTR__sound/samples/instruments/25_strings_3" +static const ALIGN_ASSET(2) char gSampleB2[] = dgSampleB2; + +#define dgSampleB3 "__OTR__sound/samples/instruments/26_crystal_rhodes" +static const ALIGN_ASSET(2) char gSampleB3[] = dgSampleB3; + +#define dgSampleB4 "__OTR__sound/samples/instruments/27_harpsichord" +static const ALIGN_ASSET(2) char gSampleB4[] = dgSampleB4; + +#define dgSampleB5 "__OTR__sound/samples/instruments/28_sitar_1" +static const ALIGN_ASSET(2) char gSampleB5[] = dgSampleB5; + +#define dgSampleB6 "__OTR__sound/samples/instruments/29_orchestra_hit" +static const ALIGN_ASSET(2) char gSampleB6[] = dgSampleB6; + +#define dgSampleB7 "__OTR__sound/samples/instruments/2A" +static const ALIGN_ASSET(2) char gSampleB7[] = dgSampleB7; + +#define dgSampleB8 "__OTR__sound/samples/instruments/2B" +static const ALIGN_ASSET(2) char gSampleB8[] = dgSampleB8; + +#define dgSampleB9 "__OTR__sound/samples/instruments/2C" +static const ALIGN_ASSET(2) char gSampleB9[] = dgSampleB9; + +#define dgSampleBA "__OTR__sound/samples/instruments/2D_trombone" +static const ALIGN_ASSET(2) char gSampleBA[] = dgSampleBA; + +#define dgSampleBB "__OTR__sound/samples/instruments/2E_accordion" +static const ALIGN_ASSET(2) char gSampleBB[] = dgSampleBB; + +#define dgSampleBC "__OTR__sound/samples/instruments/2F_sleigh_bells" +static const ALIGN_ASSET(2) char gSampleBC[] = dgSampleBC; + +#define dgSampleBD "__OTR__sound/samples/instruments/30_rarefaction-lahna" +static const ALIGN_ASSET(2) char gSampleBD[] = dgSampleBD; + +#define dgSampleBE "__OTR__sound/samples/instruments/31_rarefaction-convolution" +static const ALIGN_ASSET(2) char gSampleBE[] = dgSampleBE; + +#define dgSampleBF "__OTR__sound/samples/instruments/32_metal_rimshot" +static const ALIGN_ASSET(2) char gSampleBF[] = dgSampleBF; + +#define dgSampleC0 "__OTR__sound/samples/instruments/33_kick_drum_2" +static const ALIGN_ASSET(2) char gSampleC0[] = dgSampleC0; + +#define dgSampleC1 "__OTR__sound/samples/instruments/34_alto_flute" +static const ALIGN_ASSET(2) char gSampleC1[] = dgSampleC1; + +#define dgSampleC2 "__OTR__sound/samples/instruments/35_gospel_organ" +static const ALIGN_ASSET(2) char gSampleC2[] = dgSampleC2; + +#define dgSampleC3 "__OTR__sound/samples/instruments/36_sawtooth_synth" +static const ALIGN_ASSET(2) char gSampleC3[] = dgSampleC3; + +#define dgSampleC4 "__OTR__sound/samples/instruments/37_square_synth" +static const ALIGN_ASSET(2) char gSampleC4[] = dgSampleC4; + +#define dgSampleC5 "__OTR__sound/samples/instruments/38_electric_kick_drum" +static const ALIGN_ASSET(2) char gSampleC5[] = dgSampleC5; + +#define dgSampleC6 "__OTR__sound/samples/instruments/39_sitar_2" +static const ALIGN_ASSET(2) char gSampleC6[] = dgSampleC6; + +#define dgSampleC7 "__OTR__sound/samples/instruments/3A_music_box" +static const ALIGN_ASSET(2) char gSampleC7[] = dgSampleC7; + +#define dgSampleC8 "__OTR__sound/samples/instruments/3B_banjo_2" +static const ALIGN_ASSET(2) char gSampleC8[] = dgSampleC8; + +#define dgSampleC9 "__OTR__sound/samples/instruments/3C_acoustic_guitar" +static const ALIGN_ASSET(2) char gSampleC9[] = dgSampleC9; + +#define dgSampleCA "__OTR__sound/samples/instruments/3D" +static const ALIGN_ASSET(2) char gSampleCA[] = dgSampleCA; + +#define dgSampleCB "__OTR__sound/samples/instruments/3E_monk_choir" +static const ALIGN_ASSET(2) char gSampleCB[] = dgSampleCB; + +#define dgSampleCC "__OTR__sound/samples/instruments/3F" +static const ALIGN_ASSET(2) char gSampleCC[] = dgSampleCC; + +#define dgSampleCD "__OTR__sound/samples/instruments/40_bell" +static const ALIGN_ASSET(2) char gSampleCD[] = dgSampleCD; + +#define dgSampleCE "__OTR__sound/samples/instruments/41_pan_flute" +static const ALIGN_ASSET(2) char gSampleCE[] = dgSampleCE; + +#define dgSampleCF "__OTR__sound/samples/instruments/42_vibraphone" +static const ALIGN_ASSET(2) char gSampleCF[] = dgSampleCF; + +#define dgSampleD0 "__OTR__sound/samples/instruments/43_harmonica" +static const ALIGN_ASSET(2) char gSampleD0[] = dgSampleD0; + +#define dgSampleD1 "__OTR__sound/samples/instruments/44_grand_piano" +static const ALIGN_ASSET(2) char gSampleD1[] = dgSampleD1; + +#define dgSampleD2 "__OTR__sound/samples/instruments/45_french_horns_lq" +static const ALIGN_ASSET(2) char gSampleD2[] = dgSampleD2; + +#define dgSampleD3 "__OTR__sound/samples/instruments/46_pizzicato_strings_1" +static const ALIGN_ASSET(2) char gSampleD3[] = dgSampleD3; + +#define dgSampleD4 "__OTR__sound/samples/instruments/47_pizzicato_strings_2" +static const ALIGN_ASSET(2) char gSampleD4[] = dgSampleD4; + +#define dgSampleD5 "__OTR__sound/samples/instruments/48_steel_drum" +static const ALIGN_ASSET(2) char gSampleD5[] = dgSampleD5; + +#define dgSampleD6 "__OTR__sound/samples/piranha_music_box/00_music_box" +static const ALIGN_ASSET(2) char gSampleD6[] = dgSampleD6; + +#define dgSampleD7 "__OTR__sound/samples/course_start/00_la" +static const ALIGN_ASSET(2) char gSampleD7[] = dgSampleD7; + +#define dgSampleD8 "__OTR__sound/samples/bowser_organ/00_organ_1" +static const ALIGN_ASSET(2) char gSampleD8[] = dgSampleD8; + +#define dgSampleD9 "__OTR__sound/samples/bowser_organ/01_organ_1_lq" +static const ALIGN_ASSET(2) char gSampleD9[] = dgSampleD9; + +#define dgSampleDA "__OTR__sound/samples/bowser_organ/02_boys_choir" +static const ALIGN_ASSET(2) char gSampleDA[] = dgSampleDA; + +static const char* gSampleTable[] = { + gSample00, gSample01, gSample02, gSample03, + gSample04, gSample05, gSample06, gSample07, + gSample08, gSample09, gSample0A, gSample0B, + gSample0C, gSample0D, gSample0E, gSample0F, + gSample10, gSample11, gSample12, gSample13, + gSample14, gSample15, gSample16, gSample17, + gSample18, gSample19, gSample1A, gSample1B, + gSample1C, gSample1D, gSample1E, gSample1F, + gSample20, gSample21, gSample22, gSample23, + gSample24, gSample25, gSample26, gSample27, + gSample28, gSample29, gSample2A, gSample2B, + gSample2C, gSample2D, gSample2E, gSample2F, + gSample30, gSample31, gSample32, gSample33, + gSample34, gSample35, gSample36, gSample37, + gSample38, gSample39, gSample3A, gSample3B, + gSample3C, gSample3D, gSample3E, gSample3F, + gSample40, gSample41, gSample42, gSample43, + gSample44, gSample45, gSample46, gSample47, + gSample48, gSample49, gSample4A, gSample4B, + gSample4C, gSample4D, gSample4E, gSample4F, + gSample50, gSample51, gSample52, gSample53, + gSample54, gSample55, gSample56, gSample57, + gSample58, gSample59, gSample5A, gSample5B, + gSample5C, gSample5D, gSample5E, gSample5F, + gSample60, gSample61, gSample62, gSample63, + gSample64, gSample65, gSample66, gSample67, + gSample68, gSample69, gSample6A, gSample6B, + gSample6C, gSample6D, gSample6E, gSample6F, + gSample70, gSample71, gSample72, gSample73, + gSample74, gSample75, gSample76, gSample77, + gSample78, gSample79, gSample7A, gSample7B, + gSample7C, gSample7D, gSample7E, gSample7F, + gSample80, gSample81, gSample82, gSample83, + gSample84, gSample85, gSample86, gSample87, + gSample88, gSample89, gSample8A, gSample8B, + gSample8C, gSample8D, gSample8E, gSample8F, + gSample90, gSample91, gSample92, gSample93, + gSample94, gSample95, gSample96, gSample97, + gSample98, gSample99, gSample9A, gSample9B, + gSample9C, gSample9D, gSample9E, gSample9F, + gSampleA0, gSampleA1, gSampleA2, gSampleA3, + gSampleA4, gSampleA5, gSampleA6, gSampleA7, + gSampleA8, gSampleA9, gSampleAA, gSampleAB, + gSampleAC, gSampleAD, gSampleAE, gSampleAF, + gSampleB0, gSampleB1, gSampleB2, gSampleB3, + gSampleB4, gSampleB5, gSampleB6, gSampleB7, + gSampleB8, gSampleB9, gSampleBA, gSampleBB, + gSampleBC, gSampleBD, gSampleBE, gSampleBF, + gSampleC0, gSampleC1, gSampleC2, gSampleC3, + gSampleC4, gSampleC5, gSampleC6, gSampleC7, + gSampleC8, gSampleC9, gSampleCA, gSampleCB, + gSampleCC, gSampleCD, gSampleCE, gSampleCF, + gSampleD0, gSampleD1, gSampleD2, gSampleD3, + gSampleD4, gSampleD5, gSampleD6, gSampleD7, + gSampleD8, gSampleD9, gSampleDA, +}; diff --git a/include/sequences_table.h b/include/sequences_table.h new file mode 100644 index 00000000..fa36557a --- /dev/null +++ b/include/sequences_table.h @@ -0,0 +1,119 @@ +#pragma once +#include "align_asset_macro.h" + +#define dgSequence00 "__OTR__sound/sequences/us/00_sound_player" +static const ALIGN_ASSET(2) char gSequence00[] = dgSequence00; + +#define dgSequence01 "__OTR__sound/sequences/us/01_cutscene_collect_star" +static const ALIGN_ASSET(2) char gSequence01[] = dgSequence01; + +#define dgSequence02 "__OTR__sound/sequences/us/02_menu_title_screen" +static const ALIGN_ASSET(2) char gSequence02[] = dgSequence02; + +#define dgSequence03 "__OTR__sound/sequences/us/03_level_grass" +static const ALIGN_ASSET(2) char gSequence03[] = dgSequence03; + +#define dgSequence04 "__OTR__sound/sequences/us/04_level_inside_castle" +static const ALIGN_ASSET(2) char gSequence04[] = dgSequence04; + +#define dgSequence05 "__OTR__sound/sequences/us/05_level_water" +static const ALIGN_ASSET(2) char gSequence05[] = dgSequence05; + +#define dgSequence06 "__OTR__sound/sequences/us/06_level_hot" +static const ALIGN_ASSET(2) char gSequence06[] = dgSequence06; + +#define dgSequence07 "__OTR__sound/sequences/us/07_level_boss_koopa" +static const ALIGN_ASSET(2) char gSequence07[] = dgSequence07; + +#define dgSequence08 "__OTR__sound/sequences/us/08_level_snow" +static const ALIGN_ASSET(2) char gSequence08[] = dgSequence08; + +#define dgSequence09 "__OTR__sound/sequences/us/09_level_slide" +static const ALIGN_ASSET(2) char gSequence09[] = dgSequence09; + +#define dgSequence0A "__OTR__sound/sequences/us/0A_level_spooky" +static const ALIGN_ASSET(2) char gSequence0A[] = dgSequence0A; + +#define dgSequence0B "__OTR__sound/sequences/us/0B_event_piranha_plant" +static const ALIGN_ASSET(2) char gSequence0B[] = dgSequence0B; + +#define dgSequence0C "__OTR__sound/sequences/us/0C_level_underground" +static const ALIGN_ASSET(2) char gSequence0C[] = dgSequence0C; + +#define dgSequence0D "__OTR__sound/sequences/us/0D_menu_star_select" +static const ALIGN_ASSET(2) char gSequence0D[] = dgSequence0D; + +#define dgSequence0E "__OTR__sound/sequences/us/0E_event_powerup" +static const ALIGN_ASSET(2) char gSequence0E[] = dgSequence0E; + +#define dgSequence0F "__OTR__sound/sequences/us/0F_event_metal_cap" +static const ALIGN_ASSET(2) char gSequence0F[] = dgSequence0F; + +#define dgSequence10 "__OTR__sound/sequences/us/10_event_koopa_message" +static const ALIGN_ASSET(2) char gSequence10[] = dgSequence10; + +#define dgSequence11 "__OTR__sound/sequences/us/11_level_koopa_road" +static const ALIGN_ASSET(2) char gSequence11[] = dgSequence11; + +#define dgSequence12 "__OTR__sound/sequences/us/12_event_high_score" +static const ALIGN_ASSET(2) char gSequence12[] = dgSequence12; + +#define dgSequence13 "__OTR__sound/sequences/us/13_event_merry_go_round" +static const ALIGN_ASSET(2) char gSequence13[] = dgSequence13; + +#define dgSequence14 "__OTR__sound/sequences/us/14_event_race" +static const ALIGN_ASSET(2) char gSequence14[] = dgSequence14; + +#define dgSequence15 "__OTR__sound/sequences/us/15_cutscene_star_spawn" +static const ALIGN_ASSET(2) char gSequence15[] = dgSequence15; + +#define dgSequence16 "__OTR__sound/sequences/us/16_event_boss" +static const ALIGN_ASSET(2) char gSequence16[] = dgSequence16; + +#define dgSequence17 "__OTR__sound/sequences/us/17_cutscene_collect_key" +static const ALIGN_ASSET(2) char gSequence17[] = dgSequence17; + +#define dgSequence18 "__OTR__sound/sequences/us/18_event_endless_stairs" +static const ALIGN_ASSET(2) char gSequence18[] = dgSequence18; + +#define dgSequence19 "__OTR__sound/sequences/us/19_level_boss_koopa_final" +static const ALIGN_ASSET(2) char gSequence19[] = dgSequence19; + +#define dgSequence1A "__OTR__sound/sequences/us/1A_cutscene_credits" +static const ALIGN_ASSET(2) char gSequence1A[] = dgSequence1A; + +#define dgSequence1B "__OTR__sound/sequences/us/1B_event_solve_puzzle" +static const ALIGN_ASSET(2) char gSequence1B[] = dgSequence1B; + +#define dgSequence1C "__OTR__sound/sequences/us/1C_event_toad_message" +static const ALIGN_ASSET(2) char gSequence1C[] = dgSequence1C; + +#define dgSequence1D "__OTR__sound/sequences/us/1D_event_peach_message" +static const ALIGN_ASSET(2) char gSequence1D[] = dgSequence1D; + +#define dgSequence1E "__OTR__sound/sequences/us/1E_cutscene_intro" +static const ALIGN_ASSET(2) char gSequence1E[] = dgSequence1E; + +#define dgSequence1F "__OTR__sound/sequences/us/1F_cutscene_victory" +static const ALIGN_ASSET(2) char gSequence1F[] = dgSequence1F; + +#define dgSequence20 "__OTR__sound/sequences/us/20_cutscene_ending" +static const ALIGN_ASSET(2) char gSequence20[] = dgSequence20; + +#define dgSequence21 "__OTR__sound/sequences/us/21_menu_file_select" +static const ALIGN_ASSET(2) char gSequence21[] = dgSequence21; + +#define dgSequence22 "__OTR__sound/sequences/us/22_cutscene_lakitu" +static const ALIGN_ASSET(2) char gSequence22[] = dgSequence22; + +static const char* gSequenceTable[] = { + gSequence00, gSequence01, gSequence02, gSequence03, + gSequence04, gSequence05, gSequence06, gSequence07, + gSequence08, gSequence09, gSequence0A, gSequence0B, + gSequence0C, gSequence0D, gSequence0E, gSequence0F, + gSequence10, gSequence11, gSequence12, gSequence13, + gSequence14, gSequence15, gSequence16, gSequence17, + gSequence18, gSequence19, gSequence1A, gSequence1B, + gSequence1C, gSequence1D, gSequence1E, gSequence1F, + gSequence20, gSequence21, gSequence22, +}; diff --git a/libultraship b/libultraship index 39e41666..59f522b5 160000 --- a/libultraship +++ b/libultraship @@ -1 +1 @@ -Subproject commit 39e416666d97b781df8938c26d3f7bf262d782a2 +Subproject commit 59f522b5ca1e6f2eaebafdb6009ae8fc1477fd90 diff --git a/sound/sound_data.c b/sound/sound_data.c index 84c6616a..5313689c 100644 --- a/sound/sound_data.c +++ b/sound/sound_data.c @@ -1,3 +1,5 @@ +#include "sound/sound_data.h" + unsigned char gSoundDataADSR[] = { #include "sound/sound_data.ctl.inc.c" }; @@ -9,9 +11,3 @@ unsigned char gSoundDataRaw[] = { unsigned char gMusicData[] = { #include "sound/sequences.bin.inc.c" }; - -#ifndef VERSION_SH -unsigned char gBankSetsData[] = { -#include "sound/bank_sets.inc.c" -}; -#endif diff --git a/sound/sound_data.h b/sound/sound_data.h new file mode 100644 index 00000000..5a7b8553 --- /dev/null +++ b/sound/sound_data.h @@ -0,0 +1,11 @@ +#pragma once + +#include "types.h" +#include "align_asset_macro.h" + +extern u8 gSoundDataADSR[]; // sound_data.ctl +extern u8 gSoundDataRaw[]; // sound_data.tbl +extern u8 gMusicData[]; // sequences.s + +#define dgBankSetsData "__OTR__sound/bank_sets" +static const ALIGN_ASSET(2) char gBankSetsData[] = dgBankSetsData; \ No newline at end of file diff --git a/src/audio/copt/seq_channel_layer_process_script_copt.inc.c b/src/audio/copt/seq_channel_layer_process_script_copt.inc.c index ce5ebbba..ff3bc5a8 100644 --- a/src/audio/copt/seq_channel_layer_process_script_copt.inc.c +++ b/src/audio/copt/seq_channel_layer_process_script_copt.inc.c @@ -19,81 +19,6 @@ #define PORTAMENTO_MODE_4 4 #define PORTAMENTO_MODE_5 5 -#define COPT 0 -#define M64_READ_U8(state, dst) \ -{ \ - u8 * _ptr_pc; \ - u8 _pc; \ - _ptr_pc = (*state).pc; \ - ((*state).pc)++; \ - _pc = *_ptr_pc; \ - dst = _pc; \ -} - - -#define M64_READ_S16(state, dst) \ -{ \ - s16 _ret; \ - _ret = *(*state).pc << 8; \ - ((*state).pc)++; \ - _ret = *(*state).pc | _ret; \ - ((*state).pc)++; \ - dst = _ret; \ -} -#define M64_READ_COMPRESSED_U16(state, dst) \ -{ \ - u16 ret = *(state->pc++); \ - if (ret & 0x80) { \ - ret = (ret << 8) & 0x7f00; \ - ret = *(state->pc++) | ret; \ - } \ - dst = ret; \ -} - -#define GET_INSTRUMENT(seqChannel, instId, _instOut, _adsr, dst, l) \ -{ \ -struct AdsrSettings *adsr = _adsr; \ -struct Instrument **instOut = _instOut;\ - u8 _instId = instId; \ - struct Instrument *inst; \ - UNUSED u32 pad; \ - /* copt inlines instId here */ \ - if (instId >= gCtlEntries[(*seqChannel).bankId].numInstruments) { \ - _instId = gCtlEntries[(*seqChannel).bankId].numInstruments; \ - if (_instId == 0) { \ - dst = 0; \ - goto ret ## l; \ - } \ - _instId--; \ - } \ - inst = gCtlEntries[(*seqChannel).bankId].instruments[_instId]; \ - if (inst == NULL) { \ - while (_instId != 0xff) { \ - inst = gCtlEntries[(*seqChannel).bankId].instruments[_instId]; \ - if (inst != NULL) { \ - goto gi ## l; \ - } \ - _instId--; \ - } \ - gi ## l:; \ - } \ - if (((uintptr_t) gBankLoadedPool.persistent.pool.start <= (uintptr_t) inst \ - && (uintptr_t) inst <= (uintptr_t)(gBankLoadedPool.persistent.pool.start \ - + gBankLoadedPool.persistent.pool.size)) \ - || ((uintptr_t) gBankLoadedPool.temporary.pool.start <= (uintptr_t) inst \ - && (uintptr_t) inst <= (uintptr_t)(gBankLoadedPool.temporary.pool.start \ - + gBankLoadedPool.temporary.pool.size))) { \ - (*adsr).envelope = (*inst).envelope; \ - (*adsr).releaseRate = (*inst).releaseRate; \ - *instOut = inst; \ - _instId++; \ - goto ret ## l; \ - } \ - gAudioErrorFlags = _instId + 0x20000; \ - *instOut = NULL; \ - ret ## l: ; \ -} - void seq_channel_layer_process_script(struct SequenceChannelLayer *layer) { struct SequencePlayer *seqPlayer; // sp5C, t4 struct SequenceChannel *seqChannel; // sp58, t5 @@ -150,7 +75,8 @@ void seq_channel_layer_process_script(struct SequenceChannelLayer *layer) { seqPlayer = (*seqChannel).seqPlayer; for (;;) { state = &layer->scriptState; - //M64_READ_U8(state, cmd); +// M64_READ_U8(state, cmd); +// cmd = m64_read_u8(state); { u8 *_ptr_pc; _ptr_pc = (*state).pc++; @@ -173,13 +99,13 @@ void seq_channel_layer_process_script(struct SequenceChannelLayer *layer) { break; case 0xfc: // layer_call - M64_READ_S16(state, sp3A); + sp3A = m64_read_s16(state); state->depth++, state->stack[state->depth - 1] = state->pc; state->pc = seqPlayer->seqData + sp3A; break; case 0xf8: // layer_loop; loop start, N iterations (or 256 if N = 0) - M64_READ_U8(state, state->remLoopIters[state->depth]); + state->remLoopIters[state->depth] = m64_read_u8(state); state->depth++, state->stack[state->depth - 1] = state->pc; break; @@ -192,7 +118,7 @@ void seq_channel_layer_process_script(struct SequenceChannelLayer *layer) { break; case 0xfb: // layer_jump - M64_READ_S16(state, sp3A); + sp3A = m64_read_s16(state); state->pc = seqPlayer->seqData + sp3A; break; @@ -233,21 +159,21 @@ void seq_channel_layer_process_script(struct SequenceChannelLayer *layer) { break; case 0xc3: // layer_setshortnotedefaultplaypercentage - M64_READ_COMPRESSED_U16(state, sp3A); + sp3A = m64_read_compressed_u16(state); layer->shortNoteDefaultPlayPercentage = sp3A; break; case 0xc6: // layer_setinstr - M64_READ_U8(state, cmdSemitone); + cmdSemitone = m64_read_u8(state); if (cmdSemitone < 127) { - GET_INSTRUMENT(seqChannel, cmdSemitone, &(*layer).instrument, &(*layer).adsr, cmdSemitone, 1); + get_instrument(seqChannel, cmdSemitone, &(*layer).instrument, &(*layer).adsr); } break; case 0xc7: // layer_portamento - M64_READ_U8(state, (*layer).portamento.mode); - M64_READ_U8(state, cmdSemitone); + (*layer).portamento.mode = m64_read_u8(state); + cmdSemitone = m64_read_u8(state); cmdSemitone = cmdSemitone + (*seqChannel).transposition; cmdSemitone += (*layer).transposition; @@ -264,7 +190,7 @@ void seq_channel_layer_process_script(struct SequenceChannelLayer *layer) { break; } - M64_READ_COMPRESSED_U16(state, sp3A); + sp3A = m64_read_compressed_u16(state); layer->portamentoTime = sp3A; break; @@ -286,7 +212,7 @@ void seq_channel_layer_process_script(struct SequenceChannelLayer *layer) { } if (cmd == 0xc0) { // layer_delay - M64_READ_COMPRESSED_U16(state, layer->delay); + layer->delay = m64_read_compressed_u16(state); layer->stopSomething = TRUE; } else { layer->stopSomething = FALSE; @@ -294,14 +220,14 @@ void seq_channel_layer_process_script(struct SequenceChannelLayer *layer) { if (seqChannel->largeNotes == TRUE) { switch (cmd & 0xc0) { case 0x00: // layer_note0 (play percentage, velocity, duration) - M64_READ_COMPRESSED_U16(state, sp3A); + sp3A = m64_read_compressed_u16(state); vel = *((*state).pc++); layer->noteDuration = *((*state).pc++); layer->playPercentage = sp3A; goto l1090; case 0x40: // layer_note1 (play percentage, velocity) - M64_READ_COMPRESSED_U16(state, sp3A); + sp3A = m64_read_compressed_u16(state); vel = *((*state).pc++); layer->noteDuration = 0; layer->playPercentage = sp3A; @@ -319,7 +245,7 @@ l1090: } else { switch (cmd & 0xc0) { case 0x00: // play note, type 0 (play percentage) - M64_READ_COMPRESSED_U16(state, sp3A); + sp3A = m64_read_compressed_u16(state); layer->playPercentage = sp3A; goto l1138; @@ -346,8 +272,9 @@ l1138: } else { if (seqChannel->instOrWave == 0) { // drum cmdSemitone += (*seqChannel).transposition + (*layer).transposition; - if (cmdSemitone >= gCtlEntries[seqChannel->bankId].numDrums) { - cmdSemitone = gCtlEntries[seqChannel->bankId].numDrums; + struct CtlEntry* bank = GameEngine_LoadBank(seqChannel->bankId); + if (cmdSemitone >= bank->numDrums) { + cmdSemitone = bank->numDrums; if (cmdSemitone == 0) { // this goto looks a bit like a function return... layer->stopSomething = TRUE; @@ -357,7 +284,7 @@ l1138: cmdSemitone--; } - drum = gCtlEntries[seqChannel->bankId].drums[cmdSemitone]; + drum = bank->drums[cmdSemitone]; if (drum == NULL) { layer->stopSomething = TRUE; } else { @@ -380,8 +307,6 @@ l1138: } if (layer->portamento.mode != 0) { - //! copt needs a ternary: - //usedSemitone = (layer->portamentoTargetNote < cmdSemitone) ? cmdSemitone : layer->portamentoTargetNote; if (layer->portamentoTargetNote < cmdSemitone) { usedSemitone = cmdSemitone; } else { diff --git a/src/audio/external.c b/src/audio/external.c index b62fe6ff..63532ee7 100644 --- a/src/audio/external.c +++ b/src/audio/external.c @@ -440,76 +440,6 @@ void process_level_music_dynamics(void); static u8 begin_background_music_fade(u16 fadeDuration); void func_80320ED8(void); -#ifndef VERSION_JP -void unused_8031E4F0(void) { - // This is a debug function which is almost entirely optimized away, - // except for loops, string literals, and a read of a volatile variable. - // The string literals have allowed it to be partially reconstructed. - s32 i; - - stubbed_printf("AUTOSEQ "); - stubbed_printf("%2x %2x <%5x : %5x / %5x>\n", gSeqLoadedPool.temporary.entries[0].id, - gSeqLoadedPool.temporary.entries[1].id, gSeqLoadedPool.temporary.entries[0].size, - gSeqLoadedPool.temporary.entries[1].size, gSeqLoadedPool.temporary.pool.size); - - stubbed_printf("AUTOBNK "); - stubbed_printf("%2x %3x <%5x : %5x / %5x>\n", gBankLoadedPool.temporary.entries[0].id, - gBankLoadedPool.temporary.entries[1].id, gBankLoadedPool.temporary.entries[0].size, - gBankLoadedPool.temporary.entries[1].size, gBankLoadedPool.temporary.pool.size); - - stubbed_printf("STAYSEQ "); - stubbed_printf("[%2x] <%5x / %5x>\n", gSeqLoadedPool.persistent.numEntries, - gSeqLoadedPool.persistent.pool.cur - gSeqLoadedPool.persistent.pool.start, - gSeqLoadedPool.persistent.pool.size); - for (i = 0; (u32) i < gSeqLoadedPool.persistent.numEntries; i++) { - stubbed_printf("%2x ", gSeqLoadedPool.persistent.entries[i].id); - } - stubbed_printf("\n"); - - stubbed_printf("STAYBNK "); - stubbed_printf("[%2x] <%5x / %5x>\n", gBankLoadedPool.persistent.numEntries, - gBankLoadedPool.persistent.pool.cur - gBankLoadedPool.persistent.pool.start, - gBankLoadedPool.persistent.pool.size); - for (i = 0; (u32) i < gBankLoadedPool.persistent.numEntries; i++) { - stubbed_printf("%2x ", gBankLoadedPool.persistent.entries[i].id); - } - stubbed_printf("\n\n"); - - stubbed_printf(" 0123456789ABCDEF0123456789ABCDEF01234567\n"); - stubbed_printf("--------------------------------------------\n"); - - // gSeqLoadStatus/gBankLoadStatus, 4 entries at a time? - stubbed_printf("SEQ "); - for (i = 0; i < 40; i++) { - stubbed_printf("%1x", 0); - } - stubbed_printf("\n"); - - stubbed_printf("BNK "); - for (i = 0; i < 40; i += 4) { - stubbed_printf("%1x", 0); - } - stubbed_printf("\n"); - - stubbed_printf("FIXHEAP "); - stubbed_printf("%4x / %4x\n", 0, 0); - stubbed_printf("DRVHEAP "); - stubbed_printf("%5x / %5x\n", 0, 0); - stubbed_printf("DMACACHE %4d Blocks\n", 0); - stubbed_printf("CHANNELS %2d / MAX %3d \n", 0, 0); - - stubbed_printf("TEMPOMAX %d\n", gTempoInternalToExternal / TEMPO_SCALE); - stubbed_printf("TEMPO G0 %d\n", gSequencePlayers[SEQ_PLAYER_LEVEL].tempo / TEMPO_SCALE); - stubbed_printf("TEMPO G1 %d\n", gSequencePlayers[SEQ_PLAYER_ENV].tempo / TEMPO_SCALE); - stubbed_printf("TEMPO G2 %d\n", gSequencePlayers[SEQ_PLAYER_SFX].tempo / TEMPO_SCALE); - stubbed_printf("DEBUGFLAG %8x\n", gAudioErrorFlags); -} - -void unused_8031E568(void) { - stubbed_printf("COUNT %8d\n", gAudioFrameCount); -} -#endif - #if defined(VERSION_EU) || defined(VERSION_SH) const char unusedErrorStr1[] = "Error : Queue is not empty ( %x ) \n"; const char unusedErrorStr2[] = "specchg error\n"; diff --git a/src/audio/heap.c b/src/audio/heap.c index 2efd90e9..712ee07f 100644 --- a/src/audio/heap.c +++ b/src/audio/heap.c @@ -36,9 +36,6 @@ u8 sAudioHeapPad[0x20]; // probably two unused pools struct SoundAllocPool gSeqAndBankPool; struct SoundAllocPool gPersistentCommonPool; struct SoundAllocPool gTemporaryCommonPool; - -struct SoundMultiPool gSeqLoadedPool; -struct SoundMultiPool gBankLoadedPool; struct SoundMultiPool gUnusedLoadedPool; #ifdef VERSION_SH @@ -55,8 +52,6 @@ struct PoolSplit sTemporaryCommonPoolSplit; #ifdef VERSION_SH u8 gUnkLoadStatus[0x40]; #endif -u8 gBankLoadStatus[0x40]; -u8 gSeqLoadStatus[0x100]; #if defined(VERSION_EU) || defined(VERSION_SH) volatile u8 gAudioResetStatus; @@ -188,14 +183,6 @@ void reset_bank_and_seq_load_status(void) { gSeqLoadStatus[i] = SOUND_LOAD_STATUS_NOT_LOADED; } } -#else - for (i = 0; i < 64; i++) { - gBankLoadStatus[i] = SOUND_LOAD_STATUS_NOT_LOADED; - } - - for (i = 0; i < 256; i++) { - gSeqLoadStatus[i] = SOUND_LOAD_STATUS_NOT_LOADED; - } #endif } @@ -361,23 +348,15 @@ void seq_and_bank_pool_init(struct PoolSplit2 *a) { void persistent_pools_init(struct PoolSplit *a) { gPersistentCommonPool.cur = gPersistentCommonPool.start; - sound_alloc_pool_init(&gSeqLoadedPool.persistent.pool, SOUND_ALLOC_FUNC(&gPersistentCommonPool, a->wantSeq), a->wantSeq); - sound_alloc_pool_init(&gBankLoadedPool.persistent.pool, SOUND_ALLOC_FUNC(&gPersistentCommonPool, a->wantBank), a->wantBank); sound_alloc_pool_init(&gUnusedLoadedPool.persistent.pool, SOUND_ALLOC_FUNC(&gPersistentCommonPool, a->wantUnused), a->wantUnused); - persistent_pool_clear(&gSeqLoadedPool.persistent); - persistent_pool_clear(&gBankLoadedPool.persistent); persistent_pool_clear(&gUnusedLoadedPool.persistent); } void temporary_pools_init(struct PoolSplit *a) { gTemporaryCommonPool.cur = gTemporaryCommonPool.start; - sound_alloc_pool_init(&gSeqLoadedPool.temporary.pool, SOUND_ALLOC_FUNC(&gTemporaryCommonPool, a->wantSeq), a->wantSeq); - sound_alloc_pool_init(&gBankLoadedPool.temporary.pool, SOUND_ALLOC_FUNC(&gTemporaryCommonPool, a->wantBank), a->wantBank); sound_alloc_pool_init(&gUnusedLoadedPool.temporary.pool, SOUND_ALLOC_FUNC(&gTemporaryCommonPool, a->wantUnused), a->wantUnused); - temporary_pool_clear(&gSeqLoadedPool.temporary); - temporary_pool_clear(&gBankLoadedPool.temporary); temporary_pool_clear(&gUnusedLoadedPool.temporary); } #undef SOUND_ALLOC_FUNC @@ -444,15 +423,6 @@ void *alloc_bank_or_seq(struct SoundMultiPool *arg0, s32 arg1, s32 size, s32 arg if (arg3 == 0) { tp = &arg0->temporary; -#ifndef VERSION_SH - if (arg0 == &gSeqLoadedPool) { - table = gSeqLoadStatus; - isSound = FALSE; - } else if (arg0 == &gBankLoadedPool) { - table = gBankLoadStatus; - isSound = TRUE; - } -#endif #ifdef VERSION_SH if (tp->entries[0].id == (s8)nullID) { diff --git a/src/audio/heap.h b/src/audio/heap.h index d7f457d3..a1da3f97 100644 --- a/src/audio/heap.h +++ b/src/audio/heap.h @@ -3,6 +3,7 @@ #include +#include "port/Engine.h" #include "internal.h" #define SOUND_LOAD_STATUS_NOT_LOADED 0 @@ -12,8 +13,8 @@ #define SOUND_LOAD_STATUS_4 4 #define SOUND_LOAD_STATUS_5 5 -#define IS_BANK_LOAD_COMPLETE(bankId) (gBankLoadStatus[bankId] >= SOUND_LOAD_STATUS_COMPLETE) -#define IS_SEQ_LOAD_COMPLETE(seqId) (gSeqLoadStatus[seqId] >= SOUND_LOAD_STATUS_COMPLETE) +#define IS_BANK_LOAD_COMPLETE(bankId) GameEngine_IsBankLoaded(bankId) +#define IS_SEQ_LOAD_COMPLETE(seqId) GameEngine_IsSequenceLoaded(seqId) struct SoundAllocPool { u8 *start; @@ -91,15 +92,11 @@ extern struct SoundAllocPool gAudioInitPool; extern struct SoundAllocPool gNotesAndBuffersPool; extern struct SoundAllocPool gPersistentCommonPool; extern struct SoundAllocPool gTemporaryCommonPool; -extern struct SoundMultiPool gSeqLoadedPool; -extern struct SoundMultiPool gBankLoadedPool; #ifdef VERSION_SH extern struct Unk1Pool gUnkPool1; extern struct UnkPool gUnkPool2; extern struct UnkPool gUnkPool3; #endif -extern u8 gBankLoadStatus[64]; -extern u8 gSeqLoadStatus[256]; extern volatile u8 gAudioResetStatus; extern u8 gAudioResetPresetIdToLoad; diff --git a/src/audio/internal.h b/src/audio/internal.h index 2dd2f54b..ad852ee8 100644 --- a/src/audio/internal.h +++ b/src/audio/internal.h @@ -163,13 +163,13 @@ struct AdpcmLoop { u32 end; u32 count; u32 pad; - s16 state[16]; // only exists if count != 0. 8-byte aligned + s16* state; // only exists if count != 0. 8-byte aligned }; struct AdpcmBook { s32 order; s32 npredictors; - s16 book[1]; // size 8 * order * npredictors. 8-byte aligned + s16* book; // size 8 * order * npredictors. 8-byte aligned }; struct AudioBankSample { @@ -217,7 +217,7 @@ struct Drum { struct AudioBank { struct Drum **drums; - struct Instrument *instruments[1]; + struct Instrument **instruments; }; // dynamic size struct CtlEntry { diff --git a/src/audio/load.c b/src/audio/load.c index b951f1f0..d71b050e 100644 --- a/src/audio/load.c +++ b/src/audio/load.c @@ -7,6 +7,8 @@ #include "heap.h" #include "load.h" #include "seqplayer.h" +#include "sound/sound_data.h" +#include "sequences_table.h" #include #include @@ -27,10 +29,6 @@ void port_eu_init(void); struct Note *gNotes; -#if defined(VERSION_EU) -UNUSED static u8 pad[4]; -#endif - struct SequencePlayer gSequencePlayers[SEQUENCE_PLAYERS]; struct SequenceChannel gSequenceChannels[SEQUENCE_CHANNELS]; struct SequenceChannelLayer gSequenceLayers[SEQUENCE_LAYERS]; @@ -62,14 +60,9 @@ u8 sSampleDmaReuseQueueHead2; // sh: 0x803505E3 // bss correct up to here -ALSeqFile *gSeqFileHeader; -ALSeqFile *gAlCtlHeader; -ALSeqFile *gAlTbl; u8 *gAlBankSets; u16 gSequenceCount; -struct CtlEntry *gCtlEntries; // sh: 0x803505F8 - #if defined(VERSION_EU) u32 padEuBss1; struct AudioBufferParametersEU gAudioBufferParameters; @@ -101,11 +94,6 @@ s8 gAudioUpdatesPerFrame; extern u64 gAudioGlobalsStartMarker; extern u64 gAudioGlobalsEndMarker; -extern u8 gSoundDataADSR[]; // sound_data.ctl -extern u8 gSoundDataRaw[]; // sound_data.tbl -extern u8 gMusicData[]; // sequences.s -extern u8 gBankSetsData[]; // bank_sets.s - ALSeqFile *get_audio_file_header(s32 arg0); #include @@ -410,399 +398,23 @@ out2: #endif } -#if defined(VERSION_JP) || defined(VERSION_US) -// This function gets optimized out on US due to being static and never called -UNUSED static -#endif -void patch_sound(UNUSED struct AudioBankSound *sound, UNUSED u8 *memBase, UNUSED u8 *offsetBase) { - struct AudioBankSample *sample; - void *patched; - UNUSED u8 *mem; // unused on US - -#define PATCH(x, base) (patched = (void *)((uintptr_t) (x) + (uintptr_t) base)) - - if (sound->sample != NULL) { - sample = sound->sample = PATCH(sound->sample, memBase); - if (sample->loaded == 0) { - sample->sampleAddr = PATCH(sample->sampleAddr, offsetBase); - sample->loop = PATCH(sample->loop, memBase); - sample->book = PATCH(sample->book, memBase); - sample->loaded = 1; - } -#if defined(VERSION_EU) - else if (sample->loaded == 0x80) { - PATCH(sample->sampleAddr, offsetBase); - mem = soundAlloc(&gNotesAndBuffersPool, sample->sampleSize); - if (mem == NULL) { - sample->sampleAddr = patched; - sample->loaded = 1; - } else { - audio_dma_copy_immediate((uintptr_t) patched, mem, sample->sampleSize); - sample->loaded = 0x81; - sample->sampleAddr = mem; - } - sample->loop = PATCH(sample->loop, memBase); - sample->book = PATCH(sample->book, memBase); - } -#endif - } - -#undef PATCH +struct AudioBankSample* load_sequence_immediate(s32 seqId, s32 arg1) { + return GameEngine_LoadSequence(seqId); } -#ifdef VERSION_EU -#define PATCH_SOUND patch_sound -#else -// copt inline of the above -#define PATCH_SOUND(_sound, mem, offset) \ -{ \ - struct AudioBankSound *sound = _sound; \ - struct AudioBankSample *sample; \ - void *patched; \ - if ((*sound).sample != (void *) 0) \ - { \ - patched = (void *)(((uintptr_t)(*sound).sample) + ((uintptr_t)((u8 *) mem))); \ - (*sound).sample = patched; \ - sample = (*sound).sample; \ - if ((*sample).loaded == 0) \ - { \ - patched = (void *)(((uintptr_t)(*sample).sampleAddr) + ((uintptr_t) offset)); \ - (*sample).sampleAddr = patched; \ - patched = (void *)(((uintptr_t)(*sample).loop) + ((uintptr_t)((u8 *) mem))); \ - (*sample).loop = patched; \ - patched = (void *)(((uintptr_t)(*sample).book) + ((uintptr_t)((u8 *) mem))); \ - (*sample).book = patched; \ - (*sample).loaded = 1; \ - } \ - } \ -} -#endif - -// on US/JP this inlines patch_sound, using some -sopt compiler flag -void patch_audio_bank(struct AudioBank *mem, u8 *offset, u32 numInstruments, u32 numDrums) { - struct Instrument *instrument; - struct Instrument **itInstrs; - struct Instrument **end; - struct AudioBank *temp; - u32 i; - void *patched; - struct Drum *drum; - struct Drum **drums; -#if defined(VERSION_EU) - u32 numDrums2; -#endif - -#define BASE_OFFSET_REAL(x, base) (void *)((uintptr_t) (x) + (uintptr_t) base) -#define PATCH(x, base) (patched = BASE_OFFSET_REAL(x, base)) -#define PATCH_MEM(x) x = PATCH(x, mem) - -#if defined(VERSION_JP) || defined(VERSION_US) -#define BASE_OFFSET(x, base) BASE_OFFSET_REAL(x, base) -#else -#define BASE_OFFSET(x, base) BASE_OFFSET_REAL(base, x) -#endif - - drums = mem->drums; -#if defined(VERSION_JP) || defined(VERSION_US) - if (drums != NULL && numDrums > 0) { - mem->drums = (void *)((uintptr_t) drums + (uintptr_t) mem); - if (numDrums > 0) //! unneeded when -sopt is enabled - for (i = 0; i < numDrums; i++) { -#else - numDrums2 = numDrums; - if (drums != NULL && numDrums2 > 0) { - mem->drums = PATCH(drums, mem); - for (i = 0; i < numDrums2; i++) { -#endif - patched = mem->drums[i]; - if (patched != NULL) { - drum = PATCH(patched, mem); - mem->drums[i] = drum; - if (drum->loaded == 0) { -#if defined(VERSION_JP) || defined(VERSION_US) - //! copt replaces drum with 'patched' for these two lines - PATCH_SOUND(&(*(struct Drum *)patched).sound, mem, offset); - patched = (*(struct Drum *)patched).envelope; -#else - patch_sound(&drum->sound, (u8 *) mem, offset); - patched = drum->envelope; -#endif - drum->envelope = BASE_OFFSET(mem, patched); - drum->loaded = 1; - } - - } - } - } - - //! Doesn't affect EU, but required for US/JP - temp = &*mem; -#if defined(VERSION_JP) || defined(VERSION_US) - if (numInstruments >= 1) -#endif - if (numInstruments > 0) { - //! Doesn't affect EU, but required for US/JP - struct Instrument **tempInst; - itInstrs = temp->instruments; - tempInst = temp->instruments; - end = numInstruments + tempInst; - -#if defined(VERSION_JP) || defined(VERSION_US) -l2: -#else - do { -#endif - if (*itInstrs != NULL) { - *itInstrs = BASE_OFFSET(*itInstrs, mem); - instrument = *itInstrs; - - if (instrument->loaded == 0) { - PATCH_SOUND(&instrument->lowNotesSound, (u8 *) mem, offset); - PATCH_SOUND(&instrument->normalNotesSound, (u8 *) mem, offset); - PATCH_SOUND(&instrument->highNotesSound, (u8 *) mem, offset); - patched = instrument->envelope; - instrument->envelope = BASE_OFFSET(mem, patched); - instrument->loaded = 1; - } - } - itInstrs++; -#if defined(VERSION_JP) || defined(VERSION_US) - //! goto generated by copt, required to match US/JP - if (end != itInstrs) { - goto l2; - } -#else - } while (end != itInstrs); -#endif - } -#undef PATCH_MEM -#undef PATCH -#undef BASE_OFFSET_REAL -#undef BASE_OFFSET -#undef PATCH_SOUND -} - -struct AudioBank *bank_load_immediate(s32 bankId, s32 arg1) { - UNUSED u32 pad1[4]; - u32 buf[4]; - u32 numInstruments, numDrums; - struct AudioBank *ret; - u8 *ctlData; - s32 alloc; - - // (This is broken if the length is 1 (mod 16), but that never happens -- - // it's always divisible by 4.) - alloc = gAlCtlHeader->seqArray[bankId].len + 0xf; - alloc = ALIGN16(alloc); - alloc -= 0x10; - ctlData = gAlCtlHeader->seqArray[bankId].offset; - ret = alloc_bank_or_seq(&gBankLoadedPool, 1, alloc, arg1, bankId); - if (ret == NULL) { - return NULL; - } - - audio_dma_copy_immediate((uintptr_t) ctlData, buf, 0x10); - numInstruments = buf[0]; - numDrums = buf[1]; - audio_dma_copy_immediate((uintptr_t)(ctlData + 0x10), ret, alloc); - patch_audio_bank(ret, gAlTbl->seqArray[bankId].offset, numInstruments, numDrums); - gCtlEntries[bankId].numInstruments = (u8) numInstruments; - gCtlEntries[bankId].numDrums = (u8) numDrums; - gCtlEntries[bankId].instruments = ret->instruments; - gCtlEntries[bankId].drums = ret->drums; - gBankLoadStatus[bankId] = SOUND_LOAD_STATUS_COMPLETE; - return ret; -} - -struct AudioBank *bank_load_async(s32 bankId, s32 arg1, struct SequencePlayer *seqPlayer) { - u32 numInstruments, numDrums; - UNUSED u32 pad1[2]; - u32 buf[4]; - UNUSED u32 pad2; - size_t alloc; - struct AudioBank *ret; - u8 *ctlData; - OSMesgQueue *mesgQueue; -#if defined(VERSION_EU) - UNUSED u32 pad3; -#endif - - alloc = gAlCtlHeader->seqArray[bankId].len + 0xf; - alloc = ALIGN16(alloc); - alloc -= 0x10; - ctlData = gAlCtlHeader->seqArray[bankId].offset; - ret = alloc_bank_or_seq(&gBankLoadedPool, 1, alloc, arg1, bankId); - if (ret == NULL) { - return NULL; - } - - audio_dma_copy_immediate((uintptr_t) ctlData, buf, 0x10); - numInstruments = buf[0]; - numDrums = buf[1]; - seqPlayer->loadingBankId = (u8) bankId; -#if defined(VERSION_EU) - gCtlEntries[bankId].numInstruments = numInstruments; - gCtlEntries[bankId].numDrums = numDrums; - gCtlEntries[bankId].instruments = ret->instruments; - gCtlEntries[bankId].drums = 0; - seqPlayer->bankDmaCurrMemAddr = (u8 *) ret; - seqPlayer->bankDmaCurrDevAddr = (uintptr_t)(ctlData + 0x10); - seqPlayer->bankDmaRemaining = alloc; - if (1) { - } -#else - seqPlayer->loadingBankNumInstruments = numInstruments; - seqPlayer->loadingBankNumDrums = numDrums; - seqPlayer->bankDmaCurrMemAddr = (u8 *) ret; - seqPlayer->loadingBank = ret; - seqPlayer->bankDmaCurrDevAddr = (uintptr_t)(ctlData + 0x10); - seqPlayer->bankDmaRemaining = alloc; -#endif - mesgQueue = &seqPlayer->bankDmaMesgQueue; - osCreateMesgQueue(mesgQueue, &seqPlayer->bankDmaMesg, 1); -#if defined(VERSION_JP) || defined(VERSION_US) - seqPlayer->bankDmaMesg = OS_MESG_PTR(NULL); -#endif - seqPlayer->bankDmaInProgress = TRUE; - audio_dma_partial_copy_async(&seqPlayer->bankDmaCurrDevAddr, &seqPlayer->bankDmaCurrMemAddr, - &seqPlayer->bankDmaRemaining, mesgQueue, &seqPlayer->bankDmaIoMesg); - gBankLoadStatus[bankId] = SOUND_LOAD_STATUS_IN_PROGRESS; - return ret; -} - -void *sequence_dma_immediate(s32 seqId, s32 arg1) { - s32 seqLength; - void *ptr; - u8 *seqData; - - seqLength = gSeqFileHeader->seqArray[seqId].len + 0xf; - seqLength = ALIGN16(seqLength); - seqData = gSeqFileHeader->seqArray[seqId].offset; - ptr = alloc_bank_or_seq(&gSeqLoadedPool, 1, seqLength, arg1, seqId); - if (ptr == NULL) { - return NULL; - } - - audio_dma_copy_immediate((uintptr_t) seqData, ptr, seqLength); - gSeqLoadStatus[seqId] = SOUND_LOAD_STATUS_COMPLETE; - return ptr; -} - -void *sequence_dma_async(s32 seqId, s32 arg1, struct SequencePlayer *seqPlayer) { - s32 seqLength; - void *ptr; - u8 *seqData; - OSMesgQueue *mesgQueue; - - eu_stubbed_printf_1("Seq %d Loading Start\n", seqId); - seqLength = gSeqFileHeader->seqArray[seqId].len + 0xf; - seqLength = ALIGN16(seqLength); - seqData = gSeqFileHeader->seqArray[seqId].offset; - ptr = alloc_bank_or_seq(&gSeqLoadedPool, 1, seqLength, arg1, seqId); - if (ptr == NULL) { - eu_stubbed_printf_0("Heap Overflow Error\n"); - return NULL; - } - - if (seqLength <= 0x40) { - // Immediately load short sequenece - audio_dma_copy_immediate((uintptr_t) seqData, ptr, seqLength); - if (1) { - } - gSeqLoadStatus[seqId] = SOUND_LOAD_STATUS_COMPLETE; - } else { - audio_dma_copy_immediate((uintptr_t) seqData, ptr, 0x40); - mesgQueue = &seqPlayer->seqDmaMesgQueue; - osCreateMesgQueue(mesgQueue, &seqPlayer->seqDmaMesg, 1); -#if defined(VERSION_JP) || defined(VERSION_US) - seqPlayer->seqDmaMesg = OS_MESG_PTR(NULL); -#endif - seqPlayer->seqDmaInProgress = TRUE; - audio_dma_copy_async((uintptr_t)(seqData + 0x40), (u8 *) ptr + 0x40, seqLength - 0x40, mesgQueue, - &seqPlayer->seqDmaIoMesg); - gSeqLoadStatus[seqId] = SOUND_LOAD_STATUS_IN_PROGRESS; - } - return ptr; -} - -u8 get_missing_bank(u32 seqId, s32 *nonNullCount, s32 *nullCount) { - void *temp; +struct CtlEntry* load_banks_immediate(s32 seqId, u8 *outDefaultBank) { u32 bankId; - u16 offset; - u8 i; - u8 ret; - - *nullCount = 0; - *nonNullCount = 0; -#if defined(VERSION_EU) - offset = ((u16 *) gAlBankSets)[seqId]; - for (i = gAlBankSets[offset++], ret = 0; i != 0; i--) { + u16 offset = ((u16 *) gAlBankSets)[seqId]; + struct CtlEntry *output; + for (u8 i = gAlBankSets[offset++]; i != 0; i--) { bankId = gAlBankSets[offset++]; -#else - offset = ((u16 *) gAlBankSets)[seqId] + 1; - for (i = gAlBankSets[offset - 1], ret = 0; i != 0; i--) { - offset++; - bankId = gAlBankSets[offset - 1]; -#endif - - if (IS_BANK_LOAD_COMPLETE(bankId) == TRUE) { -#if defined(VERSION_EU) - temp = get_bank_or_seq(&gBankLoadedPool, 2, bankId); -#else - temp = get_bank_or_seq(&gBankLoadedPool, 2, gAlBankSets[offset - 1]); -#endif - } else { - temp = NULL; - } - - if (temp == NULL) { - (*nullCount)++; - ret = bankId; - } else { - (*nonNullCount)++; - } - } - - return ret; -} - -struct AudioBank *load_banks_immediate(s32 seqId, u8 *outDefaultBank) { - void *ret; - u32 bankId; - u16 offset; - u8 i; - - offset = ((u16 *) gAlBankSets)[seqId]; -#ifdef VERSION_EU - for (i = gAlBankSets[offset++]; i != 0; i--) { - bankId = gAlBankSets[offset++]; -#else - offset++; - for (i = gAlBankSets[offset - 1]; i != 0; i--) { - offset++; - bankId = gAlBankSets[offset - 1]; -#endif - - if (IS_BANK_LOAD_COMPLETE(bankId) == TRUE) { -#ifdef VERSION_EU - ret = get_bank_or_seq(&gBankLoadedPool, 2, bankId); -#else - ret = get_bank_or_seq(&gBankLoadedPool, 2, gAlBankSets[offset - 1]); -#endif - } else { - ret = NULL; - } - - if (ret == NULL) { - ret = bank_load_immediate(bankId, 2); - } + output = GameEngine_LoadBank(bankId); } *outDefaultBank = bankId; - return ret; + return output; } void preload_sequence(u32 seqId, u8 preloadMask) { - void *sequenceData; u8 temp; if (seqId >= gSequenceCount) { @@ -811,18 +423,13 @@ void preload_sequence(u32 seqId, u8 preloadMask) { gAudioLoadLock = AUDIO_LOCK_LOADING; if (preloadMask & PRELOAD_BANKS) { + // TODO: Bank is loaded on demand load_banks_immediate(seqId, &temp); } if (preloadMask & PRELOAD_SEQUENCE) { - // @bug should be IS_SEQ_LOAD_COMPLETE - if (IS_BANK_LOAD_COMPLETE(seqId) == TRUE) { - eu_stubbed_printf_1("SEQ %d ALREADY CACHED\n", seqId); - sequenceData = get_bank_or_seq(&gSeqLoadedPool, 2, seqId); - } else { - sequenceData = NULL; - } - if (sequenceData == NULL && sequence_dma_immediate(seqId, 2) == NULL) { + struct AudioBankSample* sequenceData = load_sequence_immediate(seqId, 2); + if (sequenceData == NULL) { gAudioLoadLock = AUDIO_LOCK_NOT_LOADING; return; } @@ -844,61 +451,28 @@ void load_sequence(u32 player, u32 seqId, s32 loadAsync) { } void load_sequence_internal(u32 player, u32 seqId, s32 loadAsync) { - void *sequenceData; struct SequencePlayer *seqPlayer = &gSequencePlayers[player]; - UNUSED u32 padding[2]; if (seqId >= gSequenceCount) { return; } sequence_player_disable(seqPlayer); - if (loadAsync) { - s32 numMissingBanks = 0; - s32 dummy = 0; - s32 bankId = get_missing_bank(seqId, &dummy, &numMissingBanks); - if (numMissingBanks == 1) { - eu_stubbed_printf_0("Ok,one bank slow load Start \n"); - if (bank_load_async(bankId, 2, seqPlayer) == NULL) { - return; - } - // @bug This should set the last bank (i.e. the first in the JSON) - // as default, not the missing one. This code path never gets - // taken, though -- all sequence loading is synchronous. - seqPlayer->defaultBank[0] = bankId; - } else { - eu_stubbed_printf_1("Sorry,too many %d bank is none.fast load Start \n", numMissingBanks); - if (load_banks_immediate(seqId, &seqPlayer->defaultBank[0]) == NULL) { - return; - } - } - } else if (load_banks_immediate(seqId, &seqPlayer->defaultBank[0]) == NULL) { + struct CtlEntry* bank = load_banks_immediate(seqId, &seqPlayer->defaultBank[0]); + + if (bank == NULL) { return; } eu_stubbed_printf_2("Seq %d:Default Load Id is %d\n", seqId, seqPlayer->defaultBank[0]); eu_stubbed_printf_0("Seq Loading Start\n"); - seqPlayer->seqId = seqId; - sequenceData = get_bank_or_seq(&gSeqLoadedPool, 2, seqId); + struct AudioBankSample* sequenceData = load_sequence_immediate(seqId, 2); if (sequenceData == NULL) { - if (seqPlayer->seqDmaInProgress) { - eu_stubbed_printf_0("Error:Before Sequence-SlowDma remain.\n"); - eu_stubbed_printf_0(" Cancel Seq Start.\n"); - return; - } - if (loadAsync) { - sequenceData = sequence_dma_async(seqId, 2, seqPlayer); - } else { - sequenceData = sequence_dma_immediate(seqId, 2); - } - - if (sequenceData == NULL) { - return; - } + return; } - eu_stubbed_printf_1("SEQ %d ALREADY CACHED\n", seqId); + seqPlayer->seqId = seqId; init_sequence_player(player); seqPlayer->scriptState.depth = 0; seqPlayer->delay = 0; @@ -907,27 +481,16 @@ void load_sequence_internal(u32 player, u32 seqId, s32 loadAsync) { seqPlayer->scriptState.pc = sequenceData; } +unsigned char gBankTempData[] = { +#include "sound/bank_sets.inc.c" +}; + // (void) must be omitted from parameters to fix stack with -framepointer void audio_init() { -#if defined(VERSION_EU) - UNUSED s8 pad[16]; -#else - UNUSED s8 pad[32]; -#endif -#if defined(VERSION_JP) || defined(VERSION_US) - u8 buf[0x10]; -#endif - s32 i, j, k; + s32 i, j; UNUSED s32 lim1; // lim1 unused in EU -#if defined(VERSION_EU) - UNUSED u8 buf[0x10]; - s32 UNUSED lim2, lim3; -#else - s32 lim2, lim3; -#endif UNUSED u32 size; UNUSED u64 *ptr64; - void *data; UNUSED s32 pad2; gAudioLoadLock = AUDIO_LOCK_UNINITIALIZED; @@ -940,8 +503,6 @@ void audio_init() { } memset(gAudioHeap, 0, gAudioHeapSize); - - #else for (i = 0; i < gAudioHeapSize / 8; i++) { ((u64 *) gAudioHeap)[i] = 0; @@ -955,7 +516,6 @@ void audio_init() { } #endif - eu_stubbed_printf_1("AudioHeap is %x\n", gAudioHeapSize); for (i = 0; i < NUMAIBUFFERS; i++) { @@ -993,49 +553,10 @@ void audio_init() { audio_reset_session(&gAudioSessionPresets[0]); #endif - // Not sure about these prints - eu_stubbed_printf_1("Heap reset.Synth Change %x \n", 0); - eu_stubbed_printf_3("Heap %x %x %x\n", 0, 0, 0); - eu_stubbed_printf_0("Main Heap Initialize.\n"); - // Load headers for sounds and sequences - gSeqFileHeader = (ALSeqFile *) buf; - data = gMusicData; - audio_dma_copy_immediate((uintptr_t) data, gSeqFileHeader, 0x10); - gSequenceCount = gSeqFileHeader->seqCount; -#if defined(VERSION_EU) - size = gSequenceCount * sizeof(ALSeqData) + 4; - size = ALIGN16(size); -#else - size = ALIGN16(gSequenceCount * sizeof(ALSeqData) + 4); -#endif - gSeqFileHeader = soundAlloc(&gAudioInitPool, size); - audio_dma_copy_immediate((uintptr_t) data, gSeqFileHeader, size); - alSeqFileNew(gSeqFileHeader, data); - - // Load header for CTL (instrument metadata) - gAlCtlHeader = (ALSeqFile *) buf; - data = gSoundDataADSR; - audio_dma_copy_immediate((uintptr_t) data, gAlCtlHeader, 0x10); - size = gAlCtlHeader->seqCount * sizeof(ALSeqData) + 4; - size = ALIGN16(size); - gCtlEntries = soundAlloc(&gAudioInitPool, gAlCtlHeader->seqCount * sizeof(struct CtlEntry)); - gAlCtlHeader = soundAlloc(&gAudioInitPool, size); - audio_dma_copy_immediate((uintptr_t) data, gAlCtlHeader, size); - alSeqFileNew(gAlCtlHeader, data); - - // Load header for TBL (raw sound data) - gAlTbl = (ALSeqFile *) buf; - audio_dma_copy_immediate((uintptr_t) data, gAlTbl, 0x10); - size = gAlTbl->seqCount * sizeof(ALSeqData) + 4; - size = ALIGN16(size); - gAlTbl = soundAlloc(&gAudioInitPool, size); - audio_dma_copy_immediate((uintptr_t) gSoundDataRaw, gAlTbl, size); - alSeqFileNew(gAlTbl, gSoundDataRaw); - - // Load bank sets for each sequence + gSequenceCount = sizeof(gSequenceTable) / sizeof(gSequenceTable[0]); gAlBankSets = soundAlloc(&gAudioInitPool, 0xA0); - audio_dma_copy_immediate((uintptr_t) gBankSetsData, gAlBankSets, 0xA0); + audio_dma_copy_immediate((uintptr_t) gBankTempData, gAlBankSets, 0xA0); init_sequence_players(); gAudioLoadLock = AUDIO_LOCK_NOT_LOADING; // Should probably contain the sizes of the data banks, but those aren't diff --git a/src/audio/load.h b/src/audio/load.h index 9be0c183..74bd0c11 100644 --- a/src/audio/load.h +++ b/src/audio/load.h @@ -30,12 +30,8 @@ extern struct NotePool gNoteFreeLists; extern OSMesgQueue gCurrAudioFrameDmaQueue; extern u32 gSampleDmaNumListItems; -extern ALSeqFile *gAlCtlHeader; -extern ALSeqFile *gAlTbl; -extern ALSeqFile *gSeqFileHeader; extern u8 *gAlBankSets; -extern struct CtlEntry *gCtlEntries; #if defined(VERSION_EU) || defined(VERSION_SH) extern struct AudioBufferParametersEU gAudioBufferParameters; #endif diff --git a/src/audio/seqplayer.c b/src/audio/seqplayer.c index e1e3fa7d..fb053d11 100644 --- a/src/audio/seqplayer.c +++ b/src/audio/seqplayer.c @@ -315,7 +315,7 @@ void sequence_player_disable(struct SequencePlayer *seqPlayer) { && gSeqLoadStatus[seqPlayer->seqId] != 5 #endif ) { - gSeqLoadStatus[seqPlayer->seqId] = SOUND_LOAD_STATUS_DISCARDABLE; + GameEngine_UnloadSequence(seqPlayer->seqId); } if (IS_BANK_LOAD_COMPLETE(seqPlayer->defaultBank[0]) @@ -326,26 +326,9 @@ void sequence_player_disable(struct SequencePlayer *seqPlayer) { #ifdef VERSION_SH gBankLoadStatus[seqPlayer->defaultBank[0]] = 4; #else - gBankLoadStatus[seqPlayer->defaultBank[0]] = SOUND_LOAD_STATUS_DISCARDABLE; + GameEngine_UnloadBank(seqPlayer->defaultBank[0]); #endif } - - // (Note that if this is called from alloc_bank_or_seq, the side will get swapped - // later in that function. Thus, we signal that we want to load into the slot - // of the bank that we no longer need.) -#if defined(VERSION_EU) || defined(VERSION_SH) - if (seqPlayer->defaultBank[0] == gBankLoadedPool.temporary.entries[0].id) { - gBankLoadedPool.temporary.nextSide = 1; - } else if (seqPlayer->defaultBank[0] == gBankLoadedPool.temporary.entries[1].id) { - gBankLoadedPool.temporary.nextSide = 0; - } -#else - if (gBankLoadedPool.temporary.entries[0].id == seqPlayer->defaultBank[0]) { - gBankLoadedPool.temporary.nextSide = 1; - } else if (gBankLoadedPool.temporary.entries[1].id == seqPlayer->defaultBank[0]) { - gBankLoadedPool.temporary.nextSide = 0; - } -#endif } /** @@ -1401,10 +1384,13 @@ s32 seq_channel_layer_process_script_part3(struct SequenceChannelLayer *layer, s #endif u8 get_instrument(struct SequenceChannel *seqChannel, u8 instId, struct Instrument **instOut, struct AdsrSettings *adsr) { - struct Instrument *inst; -#if defined(VERSION_EU) || defined(VERSION_SH) - inst = get_instrument_inner(seqChannel->bankId, instId); - if (inst == NULL) { + struct CtlEntry *bank = GameEngine_LoadBank(seqChannel->bankId); + if(instId >= bank->numInstruments) { + *instOut = NULL; + return 0; + } + struct Instrument* inst = bank->instruments[instId]; + if(inst == NULL) { *instOut = NULL; return 0; } @@ -1413,47 +1399,6 @@ u8 get_instrument(struct SequenceChannel *seqChannel, u8 instId, struct Instrume *instOut = inst; instId++; return instId; -#else - UNUSED u32 pad; - - if (instId >= gCtlEntries[seqChannel->bankId].numInstruments) { - instId = gCtlEntries[seqChannel->bankId].numInstruments; - if (instId == 0) { - return 0; - } - instId--; - } - - inst = gCtlEntries[seqChannel->bankId].instruments[instId]; - if (inst == NULL) { - struct SequenceChannel seqChannelCpy = *seqChannel; - - while (instId != 0xff) { - inst = gCtlEntries[seqChannelCpy.bankId].instruments[instId]; - if (inst != NULL) { - break; - } - instId--; - } - } - - if (((uintptr_t) gBankLoadedPool.persistent.pool.start <= (uintptr_t) inst - && (uintptr_t) inst <= (uintptr_t)(gBankLoadedPool.persistent.pool.start - + gBankLoadedPool.persistent.pool.size)) - || ((uintptr_t) gBankLoadedPool.temporary.pool.start <= (uintptr_t) inst - && (uintptr_t) inst <= (uintptr_t)(gBankLoadedPool.temporary.pool.start - + gBankLoadedPool.temporary.pool.size))) { - adsr->envelope = inst->envelope; - adsr->releaseRate = inst->releaseRate; - *instOut = inst; - instId++; - return instId; - } - - gAudioErrorFlags = instId + 0x20000; - *instOut = NULL; - return 0; -#endif } void set_instrument(struct SequenceChannel *seqChannel, u8 instId) { @@ -1844,7 +1789,7 @@ void sequence_channel_process_script(struct SequenceChannel *seqChannel) { #ifdef VERSION_SH if (get_bank_or_seq(1, 2, cmd) != NULL) #else - if (get_bank_or_seq(&gBankLoadedPool, 2, cmd) != NULL) + if(IS_BANK_LOAD_COMPLETE(cmd)) #endif { seqChannel->bankId = cmd; @@ -2225,9 +2170,6 @@ void sequence_channel_process_script(struct SequenceChannel *seqChannel) { void sequence_player_process_sequence(struct SequencePlayer *seqPlayer) { u8 cmd; -#ifdef VERSION_SH - UNUSED u32 pad; -#endif u8 loBits; u8 temp; s32 value; @@ -2235,96 +2177,20 @@ void sequence_player_process_sequence(struct SequencePlayer *seqPlayer) { u16 u16v; u8 *seqData; struct M64ScriptState *state; -#if defined(VERSION_EU) || defined(VERSION_SH) - s32 temp32; -#endif if (seqPlayer->enabled == FALSE) { return; } -#ifndef VERSION_SH - if (seqPlayer->bankDmaInProgress == TRUE) { -#ifdef VERSION_EU - if (osRecvMesg(&seqPlayer->bankDmaMesgQueue, NULL, 0) == -1) { - return; - } - if (seqPlayer->bankDmaRemaining == 0) { - seqPlayer->bankDmaInProgress = FALSE; - patch_audio_bank( - (struct AudioBank *) (gCtlEntries[seqPlayer->loadingBankId].instruments - 1), - gAlTbl->seqArray[seqPlayer->loadingBankId].offset, - gCtlEntries[seqPlayer->loadingBankId].numInstruments, - gCtlEntries[seqPlayer->loadingBankId].numDrums); - gCtlEntries[seqPlayer->loadingBankId].drums = - ((struct AudioBank *) (gCtlEntries[seqPlayer->loadingBankId].instruments - 1))->drums; - gBankLoadStatus[seqPlayer->loadingBankId] = SOUND_LOAD_STATUS_COMPLETE; - } else { - audio_dma_partial_copy_async(&seqPlayer->bankDmaCurrDevAddr, &seqPlayer->bankDmaCurrMemAddr, - &seqPlayer->bankDmaRemaining, &seqPlayer->bankDmaMesgQueue, - &seqPlayer->bankDmaIoMesg); - } -#else - if (!OS_MESG_VALID(seqPlayer->bankDmaMesg)) { - return; - } - if (seqPlayer->bankDmaRemaining == 0) { - seqPlayer->bankDmaInProgress = FALSE; - patch_audio_bank(seqPlayer->loadingBank, gAlTbl->seqArray[seqPlayer->loadingBankId].offset, - seqPlayer->loadingBankNumInstruments, seqPlayer->loadingBankNumDrums); - gCtlEntries[seqPlayer->loadingBankId].numInstruments = seqPlayer->loadingBankNumInstruments; - gCtlEntries[seqPlayer->loadingBankId].numDrums = seqPlayer->loadingBankNumDrums; - gCtlEntries[seqPlayer->loadingBankId].instruments = seqPlayer->loadingBank->instruments; - gCtlEntries[seqPlayer->loadingBankId].drums = seqPlayer->loadingBank->drums; - gBankLoadStatus[seqPlayer->loadingBankId] = SOUND_LOAD_STATUS_COMPLETE; - } else { - osCreateMesgQueue(&seqPlayer->bankDmaMesgQueue, &seqPlayer->bankDmaMesg, 1); - seqPlayer->bankDmaMesg = OS_MESG_PTR(NULL); - audio_dma_partial_copy_async(&seqPlayer->bankDmaCurrDevAddr, &seqPlayer->bankDmaCurrMemAddr, - &seqPlayer->bankDmaRemaining, &seqPlayer->bankDmaMesgQueue, - &seqPlayer->bankDmaIoMesg); - } -#endif - return; - } - - if (seqPlayer->seqDmaInProgress == TRUE) { -#ifdef VERSION_EU - if (osRecvMesg(&seqPlayer->seqDmaMesgQueue, NULL, 0) == -1) { - return; - } -#else - if (!OS_MESG_VALID(seqPlayer->seqDmaMesg)) { - return; - } -#endif - seqPlayer->seqDmaInProgress = FALSE; - gSeqLoadStatus[seqPlayer->seqId] = SOUND_LOAD_STATUS_COMPLETE; - } -#endif - // If discarded, bail out. - if (IS_SEQ_LOAD_COMPLETE(seqPlayer->seqId) == FALSE - || ( -#ifdef VERSION_SH - seqPlayer->defaultBank[0] != 0xff && -#endif - IS_BANK_LOAD_COMPLETE(seqPlayer->defaultBank[0]) == FALSE)) { + if (IS_SEQ_LOAD_COMPLETE(seqPlayer->seqId) == FALSE || IS_BANK_LOAD_COMPLETE(seqPlayer->defaultBank[0]) == FALSE ){ eu_stubbed_printf_1("Disappear Sequence or Bank %d\n", seqPlayer->seqId); sequence_player_disable(seqPlayer); return; } - // Remove possible SOUND_LOAD_STATUS_DISCARDABLE marks. -#ifdef VERSION_SH - if (gSeqLoadStatus[seqPlayer->seqId] != 5) -#endif - gSeqLoadStatus[seqPlayer->seqId] = SOUND_LOAD_STATUS_COMPLETE; - -#ifdef VERSION_SH - if (gBankLoadStatus[seqPlayer->defaultBank[0]] != 5) -#endif - gBankLoadStatus[seqPlayer->defaultBank[0]] = SOUND_LOAD_STATUS_COMPLETE; + GameEngine_LoadSequence(seqPlayer->seqId); + GameEngine_LoadBank(seqPlayer->defaultBank[0]); if (seqPlayer->muted && (seqPlayer->muteBehavior & MUTE_BEHAVIOR_STOP_SCRIPT) != 0) { return; @@ -2354,11 +2220,7 @@ void sequence_player_process_sequence(struct SequencePlayer *seqPlayer) { sequence_player_disable(seqPlayer); break; } -#if defined(VERSION_EU) || defined(VERSION_SH) state->pc = state->stack[--state->depth]; -#else - state->depth--, state->pc = state->stack[state->depth]; -#endif } if (cmd == 0xfd) { // seq_delay @@ -2378,27 +2240,13 @@ void sequence_player_process_sequence(struct SequencePlayer *seqPlayer) { case 0xfc: // seq_call u16v = m64_read_s16(state); - if (0 && state->depth >= 4) { - eu_stubbed_printf_0("Macro Level Over Error!\n"); - } -#if defined(VERSION_EU) || defined(VERSION_SH) state->stack[state->depth++] = state->pc; -#else - state->depth++, state->stack[state->depth - 1] = state->pc; -#endif state->pc = seqPlayer->seqData + u16v; break; case 0xf8: // seq_loop; loop start, N iterations (or 256 if N = 0) - if (0 && state->depth >= 4) { - eu_stubbed_printf_0("Macro Level Over Error!\n"); - } state->remLoopIters[state->depth] = m64_read_u8(state); -#if defined(VERSION_EU) || defined(VERSION_SH) state->stack[state->depth++] = state->pc; -#else - state->depth++, state->stack[state->depth - 1] = state->pc; -#endif break; case 0xf7: // seq_loopend @@ -2725,18 +2573,11 @@ void process_sequences(UNUSED s32 iterationsRemaining) { s32 i; for (i = 0; i < SEQUENCE_PLAYERS; i++) { if (gSequencePlayers[i].enabled == TRUE) { -#if defined(VERSION_EU) || defined(VERSION_SH) sequence_player_process_sequence(&gSequencePlayers[i]); sequence_player_process_sound(&gSequencePlayers[i]); -#else - sequence_player_process_sequence(gSequencePlayers + i); - sequence_player_process_sound(gSequencePlayers + i); -#endif } } -#if defined(VERSION_JP) || defined(VERSION_US) reclaim_notes(); -#endif process_notes(); } @@ -2788,11 +2629,6 @@ void init_sequence_players(void) { for (i = 0; i < ARRAY_COUNT(gSequenceChannels); i++) { gSequenceChannels[i].seqPlayer = NULL; gSequenceChannels[i].enabled = FALSE; -#if defined(VERSION_JP) || defined(VERSION_US) - } - - for (i = 0; i < ARRAY_COUNT(gSequenceChannels); i++) { -#endif for (j = 0; j < LAYERS_MAX; j++) { gSequenceChannels[i].layers[j] = NULL; } diff --git a/src/game/skybox.c b/src/game/skybox.c index a623829c..d6c50cc6 100644 --- a/src/game/skybox.c +++ b/src/game/skybox.c @@ -59,6 +59,8 @@ struct Skybox { }; struct Skybox sSkyBoxInfo[2]; +static uint8_t** gSkyboxTiles; +static s8 gLastSkybox; static const char* gSkyboxTextures[] = { gSkyboxWater, gSkyboxBitfs, gSkyboxWdw, gSkyboxCloudFloor, gSkyboxCcm, @@ -246,7 +248,12 @@ void draw_skybox_tile_grid(Gfx **dlist, s8 background, s8 player, s8 colorIndex) void* data = ResourceGetDataByName(gSkyboxTextures[background]); - uint8_t* texture = malloc(2048); + uint8_t* texture = gSkyboxTiles[tileIndex]; + + if(texture == NULL){ + texture = malloc(2048); + gSkyboxTiles[tileIndex] = texture; + } // Memcpy texture with calculated offset from x and y using the fact that every tile is 2048 bytes memcpy(texture, CalculateDataOffset(data, x, y), 2048); @@ -286,7 +293,7 @@ void *create_skybox_ortho_matrix(s8 player) { * Creates the skybox's display list, then draws the 3x3 grid of tiles. */ Gfx *init_skybox_display_list(s8 player, s8 background, s8 colorIndex) { - s32 dlCommandCount = 5 + (3 * 3) * 7; // 5 for the start and end, plus 9 skybox tiles + s32 dlCommandCount = 10 + (3 * 3) * 7; // 5 for the start and end, plus 9 skybox tiles void *skybox = alloc_display_list(dlCommandCount * sizeof(Gfx)); Gfx *dlist = skybox; @@ -294,6 +301,15 @@ Gfx *init_skybox_display_list(s8 player, s8 background, s8 colorIndex) { return NULL; } else { Mtx *ortho = create_skybox_ortho_matrix(player); + if(gSkyboxTiles == NULL){ + gSkyboxTiles = malloc(80 * sizeof(uint8_t*)); + gLastSkybox = background; + } else if(gLastSkybox != background ){ + for(size_t i = 0; i < 80; i++){ + gSPInvalidateTexCache(dlist++, (uintptr_t) gSkyboxTiles[i]); + } + gLastSkybox = background; + } gSPDisplayList(dlist++, dl_skybox_begin); gSPMatrix(dlist++, VIRTUAL_TO_PHYSICAL(ortho), G_MTX_PROJECTION | G_MTX_MUL | G_MTX_NOPUSH); @@ -302,6 +318,7 @@ Gfx *init_skybox_display_list(s8 player, s8 background, s8 colorIndex) { gSPDisplayList(dlist++, dl_skybox_end); gSPEndDisplayList(dlist); } + return skybox; } diff --git a/src/port/Engine.cpp b/src/port/Engine.cpp index 98665f1e..0e3b3041 100644 --- a/src/port/Engine.cpp +++ b/src/port/Engine.cpp @@ -1,6 +1,11 @@ #include "Engine.h" #include "ImguiUI.h" #include "port/importer/AnimationFactory.h" +#include "port/importer/AudioBankFactory.h" +#include "port/importer/AudioSampleFactory.h" +#include "audio/internal.h" +#include "banks_table.h" +#include "sequences_table.h" #include #include @@ -15,6 +20,8 @@ GameEngine::GameEngine(){ this->context->GetWindow()->SetTargetFps(30); this->context->GetWindow()->SetMaximumFrameLatency(1); this->context->GetResourceManager()->GetResourceLoader()->RegisterResourceFactory(LUS::ResourceType::Anim, "Animation", std::make_shared()); + this->context->GetResourceManager()->GetResourceLoader()->RegisterResourceFactory(LUS::ResourceType::Bank, "Bank", std::make_shared()); + this->context->GetResourceManager()->GetResourceLoader()->RegisterResourceFactory(LUS::ResourceType::Audio, "Audio", std::make_shared()); } void GameEngine::Create(){ @@ -50,4 +57,56 @@ extern "C" uint32_t GameEngine_GetSampleRate() { extern "C" float GameEngine_GetAspectRatio() { return gfx_current_dimensions.aspect_ratio; +} + +extern "C" CtlEntry* GameEngine_LoadBank(uint8_t bankId) { + auto engine = GameEngine::Instance; + if(bankId > (sizeof(gBankTable) / sizeof(gBankTable[0]))){ + return nullptr; + } + if(engine->banks.contains(bankId)){ + return engine->banks[bankId]; + } + auto ctl = static_cast(ResourceGetDataByName(gBankTable[bankId])); + engine->banks[bankId] = ctl; + return ctl; +} + +extern "C" uint8_t GameEngine_IsBankLoaded(uint8_t bankId) { + auto engine = GameEngine::Instance; + GameEngine_LoadBank(bankId); + return engine->banks.contains(bankId); +} + +extern "C" void GameEngine_UnloadBank(uint8_t bankId) { + auto engine = GameEngine::Instance; + if(engine->banks.contains(bankId)){ + engine->banks.erase(bankId); + } +} + +extern "C" AudioBankSample* GameEngine_LoadSequence(uint8_t seqId) { + auto engine = GameEngine::Instance; + if(seqId > (sizeof(gSequenceTable) / sizeof(gSequenceTable[0]))){ + return nullptr; + } + if(engine->samples.contains(seqId)){ + return engine->samples[seqId]; + } + auto sample = static_cast(ResourceGetDataByName(gSequenceTable[seqId])); + engine->samples[seqId] = sample; + return sample; +} + +extern "C" uint8_t GameEngine_IsSequenceLoaded(uint8_t seqId) { + auto engine = GameEngine::Instance; + GameEngine_LoadSequence(seqId); + return engine->samples.contains(seqId); +} + +extern "C" void GameEngine_UnloadSequence(uint8_t seqId) { + auto engine = GameEngine::Instance; + if(engine->samples.contains(seqId)){ + engine->samples.erase(seqId); + } } \ No newline at end of file diff --git a/src/port/Engine.h b/src/port/Engine.h index 3192b17e..3403ac16 100644 --- a/src/port/Engine.h +++ b/src/port/Engine.h @@ -9,11 +9,16 @@ #define AUDIO_FRAMES_PER_UPDATE 2 #define NUM_AUDIO_CHANNELS 2 +struct CtlEntry; +struct AudioBankSample; + class GameEngine { public: static GameEngine* Instance; std::shared_ptr context; + std::unordered_map banks; + std::unordered_map samples; GameEngine(); static void Create(); @@ -24,4 +29,10 @@ class GameEngine { #else uint32_t GameEngine_GetSampleRate(); float GameEngine_GetAspectRatio(); +struct CtlEntry* GameEngine_LoadBank(uint8_t bankId); +uint8_t GameEngine_IsBankLoaded(uint8_t bankId); +void GameEngine_UnloadBank(uint8_t bankId); +struct AudioBankSample* GameEngine_LoadSequence(uint8_t seqId); +uint8_t GameEngine_IsSequenceLoaded(uint8_t seqId); +void GameEngine_UnloadSequence(uint8_t seqId); #endif \ No newline at end of file diff --git a/src/port/Variables.cpp b/src/port/Variables.cpp index ffce12a3..98bc69f3 100644 --- a/src/port/Variables.cpp +++ b/src/port/Variables.cpp @@ -15,7 +15,7 @@ u64 osClockRate = 62500000; s8 gShowProfiler = FALSE; s8 gShowDebugText = FALSE; -s8 gDebugLevelSelect = FALSE; +s8 gDebugLevelSelect = TRUE; OSIoMesg gDmaIoMesg; OSMesg gMainReceivedMesg; diff --git a/src/port/importer/AudioBankFactory.cpp b/src/port/importer/AudioBankFactory.cpp new file mode 100644 index 00000000..10931d4a --- /dev/null +++ b/src/port/importer/AudioBankFactory.cpp @@ -0,0 +1,112 @@ +#include "AudioBankFactory.h" +#include "port/importer/types/AudioBank.h" +#include "spdlog/spdlog.h" +#include "resourcebridge.h" + +std::shared_ptr CubeOS::AudioBankFactory::ReadResource(std::shared_ptr initData, std::shared_ptr reader) { + auto resource = std::make_shared(initData); + std::shared_ptr factory = nullptr; + + switch (resource->GetInitData()->ResourceVersion) { + case 0: + factory = std::make_shared(); + break; + } + + if (factory == nullptr) { + SPDLOG_ERROR("Failed to load AudioBank with version {}", resource->GetInitData()->ResourceVersion); + return nullptr; + } + + factory->ParseFileBinary(reader, resource); + + return resource; +} + + + +void CubeOS::AudioBankFactoryV0::ParseFileBinary(std::shared_ptr reader, std::shared_ptr resource) { + std::shared_ptr bank = std::static_pointer_cast(resource); + + ResourceVersionFactory::ParseFileBinary(reader, bank); + + uint32_t instrumentCount = reader->ReadUInt32(); + + for(size_t i = 0; i < instrumentCount; i++){ + auto* instrument = new Instrument(); +// memset(instrument, 0, sizeof(Instrument)); + bool valid = reader->ReadUByte(); + if(!valid){ + bank->instruments.push_back(nullptr); + continue; + } + instrument->loaded = 1; + instrument->releaseRate = reader->ReadUByte(); + instrument->normalRangeLo = reader->ReadUByte(); + instrument->normalRangeHi = reader->ReadUByte(); + + uint32_t envelopeSize = reader->ReadUInt32(); + if(envelopeSize != 0){ + instrument->envelope = new AdsrEnvelope[envelopeSize]; + for(size_t j = 0; j < envelopeSize; j++){ + instrument->envelope[j].delay = BSWAP16(reader->ReadInt16()); + instrument->envelope[j].arg = BSWAP16(reader->ReadInt16()); + } + } + + uint32_t soundFlags = reader->ReadUInt32(); + bool hasLo = soundFlags & (1 << 0); + bool hasMed = soundFlags & (1 << 1); + bool hasHi = soundFlags & (1 << 2); + + if(hasLo){ + std::string lowSampleName = reader->ReadString(); + instrument->lowNotesSound.sample = static_cast(ResourceGetDataByName(lowSampleName.c_str())); + instrument->lowNotesSound.tuning = reader->ReadFloat(); + } + + if(hasMed){ + std::string normalSampleName = reader->ReadString(); + instrument->normalNotesSound.sample = static_cast(ResourceGetDataByName(normalSampleName.c_str())); + instrument->normalNotesSound.tuning = reader->ReadFloat(); + } + + if(hasHi){ + std::string highSampleName = reader->ReadString(); + instrument->highNotesSound.sample = static_cast(ResourceGetDataByName(highSampleName.c_str())); + instrument->highNotesSound.tuning = reader->ReadFloat(); + } + + bank->instruments.push_back(instrument); + } + + uint32_t drumCount = reader->ReadUInt32(); + + for(size_t i = 0; i < drumCount; i++){ + auto* drum = new Drum(); + drum->releaseRate = reader->ReadUByte(); + drum->pan = reader->ReadUByte(); + drum->loaded = 1; + + uint32_t envelopeSize = reader->ReadUInt32(); + if(envelopeSize != 0){ + drum->envelope = new AdsrEnvelope[envelopeSize]; + for(size_t j = 0; j < envelopeSize; j++){ + drum->envelope[j].delay = BSWAP16(reader->ReadInt16()); + drum->envelope[j].arg = BSWAP16(reader->ReadInt16()); + } + } + + std::string sampleName = reader->ReadString(); + drum->sound.sample = static_cast(ResourceGetDataByName(sampleName.c_str())); + drum->sound.tuning = reader->ReadFloat(); + + bank->drums.push_back(drum); + } + + bank->mData.unused = 1; + bank->mData.numInstruments = instrumentCount; + bank->mData.numDrums = drumCount; + bank->mData.instruments = bank->instruments.data(); + bank->mData.drums = bank->drums.data(); +} \ No newline at end of file diff --git a/src/port/importer/AudioBankFactory.h b/src/port/importer/AudioBankFactory.h new file mode 100644 index 00000000..a8b39a2b --- /dev/null +++ b/src/port/importer/AudioBankFactory.h @@ -0,0 +1,16 @@ +#pragma once + +#include "ResourceFactory.h" + +namespace CubeOS{ +class AudioBankFactory : public LUS::ResourceFactory { + public: + std::shared_ptr + ReadResource(std::shared_ptr initData, std::shared_ptr reader) override; +}; + +class AudioBankFactoryV0 : public LUS::ResourceVersionFactory { + public: + void ParseFileBinary(std::shared_ptr reader, std::shared_ptr resource) override; +}; +} diff --git a/src/port/importer/AudioSampleFactory.cpp b/src/port/importer/AudioSampleFactory.cpp new file mode 100644 index 00000000..d913a8b9 --- /dev/null +++ b/src/port/importer/AudioSampleFactory.cpp @@ -0,0 +1,62 @@ +#include "AudioSampleFactory.h" +#include +#include "port/importer/types/AudioSample.h" +#include "spdlog/spdlog.h" + +std::shared_ptr CubeOS::AudioSampleFactory::ReadResource(std::shared_ptr initData, std::shared_ptr reader) { + auto resource = std::make_shared(initData); + std::shared_ptr factory = nullptr; + + switch (resource->GetInitData()->ResourceVersion) { + case 0: + factory = std::make_shared(); + break; + } + + if (factory == nullptr) { + SPDLOG_ERROR("Failed to load AudioBank with version {}", resource->GetInitData()->ResourceVersion); + return nullptr; + } + + factory->ParseFileBinary(reader, resource); + + return resource; +} + +void CubeOS::AudioSampleFactoryV0::ParseFileBinary(std::shared_ptr reader, std::shared_ptr resource) { + std::shared_ptr bank = std::static_pointer_cast(resource); + + ResourceVersionFactory::ParseFileBinary(reader, bank); + + bank->loop.start = reader->ReadUInt32(); + bank->loop.end = reader->ReadUInt32(); + bank->loop.count = reader->ReadInt32(); + bank->loop.pad = reader->ReadInt32(); + + uint32_t stateSize = reader->ReadUInt32(); + std::vector state; + if(stateSize > 0){ + bank->loop.state = new int16_t[stateSize]; + reader->Read((char*) bank->loop.state, stateSize * sizeof(int16_t)); + } else { + bank->loop.state = nullptr; + } + + bank->book.order = reader->ReadInt32(); + bank->book.npredictors = reader->ReadInt32(); + + uint32_t tableSize = reader->ReadUInt32(); + bank->book.book = new int16_t[tableSize]; + reader->Read((char*) bank->book.book, tableSize * sizeof(int16_t)); + + int32_t sampleSize = reader->ReadInt32(); + char* sampleData = new char[sampleSize]; + reader->Read(sampleData, sampleSize); + + bank->mData.unused = 0; + bank->mData.loaded = 1; + bank->mData.loop = &bank->loop; + bank->mData.book = &bank->book; + bank->mData.sampleAddr = (uint8_t*) sampleData; + bank->mData.sampleSize = sampleSize; +} \ No newline at end of file diff --git a/src/port/importer/AudioSampleFactory.h b/src/port/importer/AudioSampleFactory.h new file mode 100644 index 00000000..9c99f2d2 --- /dev/null +++ b/src/port/importer/AudioSampleFactory.h @@ -0,0 +1,16 @@ +#pragma once + +#include "ResourceFactory.h" + +namespace CubeOS{ +class AudioSampleFactory : public LUS::ResourceFactory { + public: + std::shared_ptr + ReadResource(std::shared_ptr initData, std::shared_ptr reader) override; +}; + +class AudioSampleFactoryV0 : public LUS::ResourceVersionFactory { + public: + void ParseFileBinary(std::shared_ptr reader, std::shared_ptr resource) override; +}; +} diff --git a/src/port/importer/types/AudioBank.cpp b/src/port/importer/types/AudioBank.cpp new file mode 100644 index 00000000..d8f5d167 --- /dev/null +++ b/src/port/importer/types/AudioBank.cpp @@ -0,0 +1,11 @@ +#include "AudioBank.h" + +namespace CubeOS { +CtlEntry* AudioBank::GetPointer() { + return &mData; +} + +size_t AudioBank::GetPointerSize() { + return sizeof(mData); +} +} \ No newline at end of file diff --git a/src/port/importer/types/AudioBank.h b/src/port/importer/types/AudioBank.h new file mode 100644 index 00000000..fbb24d11 --- /dev/null +++ b/src/port/importer/types/AudioBank.h @@ -0,0 +1,62 @@ +#pragma once + +#include +#include +#include "AudioSample.h" + +struct AudioBankSound { + AudioBankSample *sample; + float tuning; // frequency scale factor +}; // size = 0x8 + +struct AdsrEnvelope { + int16_t delay; + int16_t arg; +}; // size = 0x4 + +struct Instrument { + /*0x00*/ uint8_t loaded; + /*0x01*/ uint8_t normalRangeLo; + /*0x02*/ uint8_t normalRangeHi; + /*0x03*/ uint8_t releaseRate; + /*0x04*/ AdsrEnvelope *envelope; + /*0x08*/ AudioBankSound lowNotesSound; + /*0x10*/ AudioBankSound normalNotesSound; + /*0x18*/ AudioBankSound highNotesSound; +}; // size = 0x20 + +struct Drum { + uint8_t releaseRate; + uint8_t pan; + uint8_t loaded; + AudioBankSound sound; + AdsrEnvelope *envelope; +}; + +struct CtlEntry { + uint8_t unused; + uint8_t numInstruments; + uint8_t numDrums; + struct Instrument **instruments; + struct Drum **drums; +}; // size = 0xC + + +namespace CubeOS { + + +class AudioBank : public LUS::Resource { + public: + using Resource::Resource; + + AudioBank() : Resource(std::shared_ptr()) {} + + CtlEntry* GetPointer(); + size_t GetPointerSize(); + + CtlEntry mData; + + std::vector instruments; + std::vector drums; +}; +} \ No newline at end of file diff --git a/src/port/importer/types/AudioSample.cpp b/src/port/importer/types/AudioSample.cpp new file mode 100644 index 00000000..54324305 --- /dev/null +++ b/src/port/importer/types/AudioSample.cpp @@ -0,0 +1,11 @@ +#include "AudioSample.h" + +namespace CubeOS { +AudioBankSample* AudioSample::GetPointer() { + return &mData; +} + +size_t AudioSample::GetPointerSize() { + return sizeof(mData); +} +} \ No newline at end of file diff --git a/src/port/importer/types/AudioSample.h b/src/port/importer/types/AudioSample.h new file mode 100644 index 00000000..25dc9243 --- /dev/null +++ b/src/port/importer/types/AudioSample.h @@ -0,0 +1,48 @@ +#pragma once + +#include +#include + + +struct AdpcmLoop { + uint32_t start; + uint32_t end; + uint32_t count; + uint32_t pad; + int16_t* state; // only exists if count != 0. 8-byte aligned +}; + +struct AdpcmBook { + int32_t order; + int32_t npredictors; + int16_t* book; // size 8 * order * npredictors. 8-byte aligned +}; + +struct AudioBankSample { + uint8_t unused; + uint8_t loaded; + uint8_t *sampleAddr; + AdpcmLoop *loop; + AdpcmBook *book; + uint32_t sampleSize; // never read. either 0 or 1 mod 9, depending on padding +}; + +namespace CubeOS { + + +class AudioSample : public LUS::Resource { + public: + using Resource::Resource; + + AudioSample() : Resource(std::shared_ptr()) {} + + AudioBankSample* GetPointer(); + size_t GetPointerSize(); + + AudioBankSample mData; + + AdpcmLoop loop; + AdpcmBook book; + std::vector sampleAddr; +}; +} \ No newline at end of file diff --git a/tools/assemble_sound.py b/tools/assemble_sound.py index f4a81d26..a0b7b873 100755 --- a/tools/assemble_sound.py +++ b/tools/assemble_sound.py @@ -678,7 +678,6 @@ def serialize_tbl(sample_bank, ser, is_shindou): else: ser.align_garbage(16) - def serialize_seqfile( out_filename, out_header_filename, @@ -994,6 +993,7 @@ def main(): for f in sorted(os.listdir(dir)): fname = os.path.join(dir, f) if not f.endswith(".aifc"): + print("warning: ignoring non-AIFC file " + fname) continue try: with open(fname, "rb") as inf: diff --git a/tools/disassemble_sound.py b/tools/disassemble_sound.py index caef0efa..cfbb8867 100755 --- a/tools/disassemble_sound.py +++ b/tools/disassemble_sound.py @@ -88,7 +88,6 @@ def align(val, al): name_tbl = {} - def gen_name(prefix, name_table=[]): if prefix not in name_tbl: name_tbl[prefix] = 0 @@ -305,6 +304,7 @@ def parse_ctl(parsed_header, data, sample_bank, index, is_shindou): # Put drums somewhere in the middle of the instruments to make sample # addresses come in increasing order. (This logic isn't totally right, # but it works for our purposes.) + all_insts = [] need_drums = len(drums) > 0 for inst in insts: @@ -522,8 +522,10 @@ def write_aiff(entry, filename): write_aifc(entry, temp) temp.flush() temp.close() + open(filename + ".aifc", "wb").write(open(temp.name, "rb").read()) aifc_decode = os.path.join(os.path.dirname(__file__), 'aifc_decode.exe' if os.name == 'nt' else 'aifc_decode') subprocess.run([aifc_decode, temp.name, filename], check=True) + # Write aifc file too finally: temp.close() os.remove(temp.name) @@ -642,12 +644,12 @@ def main(): tbl_entries = parse_sh_header(tbl_header_data, TYPE_TBL) sample_banks = parse_tbl(tbl_data, tbl_entries)[1] - for index, (offset, length, sh_meta) in enumerate(ctl_entries): sample_bank = sample_banks[sh_meta[0]] entry = ctl_data[offset : offset + length] header = (sh_meta[1], sh_meta[2], "0000-00-00") - banks.append(parse_ctl(header, entry, sample_bank, index, True)) + bank = parse_ctl(header, entry, sample_bank, index, True) + banks.append(bank) else: ctl_entries = parse_seqfile(ctl_data, TYPE_CTL) tbl_entries = parse_seqfile(tbl_data, TYPE_TBL) @@ -655,14 +657,32 @@ def main(): tbls, sample_banks, sample_bank_map = parse_tbl(tbl_data, tbl_entries) + print(f"Table entries {len(tbl_entries)}") + print(f"Control entries {len(ctl_entries)}") + print(f"TBLs {len(tbls)}") + print(f"Map {len(sample_bank_map)}") + + print("================================") for index, (offset, length), sample_bank_name in zip( range(len(ctl_entries)), ctl_entries, tbls ): sample_bank = sample_bank_map[sample_bank_name] entry = ctl_data[offset : offset + length] header = parse_ctl_header(entry[:16]) - banks.append(parse_ctl(header, entry[16:], sample_bank, index, False)) + bank = parse_ctl(header, entry[16:], sample_bank, index, False) + banks.append(bank) + print(f"Bank {bank.name}") + print(f"Instruments {len(bank.insts)}") + print(f"Drums {len(bank.drums)}") + print(f"Samples {len(bank.samples)}") + print(f"Envelopes {len(bank.envelopes)}") + print(f"All Instruments {len(bank.all_insts)}") + print(f"Inst Offsets {len(bank.inst_list)}") + print(f"Sample bank {bank.sample_bank.name}") + print(f"Sample bank {bank.sample_bank.offset}") + print("================================") + print("Extracting...") # Special mode used for asset extraction: generate aifc files, with paths # given by command line arguments if only_samples: @@ -670,6 +690,7 @@ def main(): created_dirs = set() for arg in only_samples_list: filename, index = arg.rsplit(":", 1) + print(filename, index) index_to_filename[int(index)] = filename index = -1 for sample_bank in sample_banks: @@ -677,7 +698,9 @@ def main(): for offset in offsets: entry = sample_bank.entries[offset] index += 1 + print(index, entry.name) if index in index_to_filename: + print(index, index_to_filename[index]) filename = index_to_filename[index] dir = os.path.dirname(filename) if dir not in created_dirs: