map

minimalist audio player
git clone https://git.porkepik.fr/map
Log | Files | Refs | README | LICENSE

commit 9ecaf8e2d2dee80bd93f45a935f97c33f4ad8906
parent e9a898cb22eb0bdf7c330c6db4e1c3a096e66021
Author: Thomas Philippe <dev@porkepik.fr>
Date:   Mon, 18 May 2020 05:21:08 +0200

remove ogg vorbis and wav support

Diffstat:
MMakefile | 4++--
MREADME | 2+-
Mext/dr_flac.h | 3518+++++++++++++++++++++++++++++++++++++++++++++++++++----------------------------
Mext/dr_mp3.h | 2256+++++++++++++++++++++++++++++++++++++++++++++++--------------------------------
Dext/dr_wav.h | 5373-------------------------------------------------------------------------------
Dext/stb_vorbis.c | 5555-------------------------------------------------------------------------------
Mmap.c | 30++----------------------------
Mmp3.c | 2+-
Dvorbis.c | 39---------------------------------------
Dwav.c | 39---------------------------------------
10 files changed, 3637 insertions(+), 13181 deletions(-)

diff --git a/Makefile b/Makefile @@ -1,8 +1,8 @@ .POSIX: PREFIX = /usr/local CFLAGS = -O2 -std=c99 -D_DEFAULT_SOURCE -Wall -LDLIBS = -lasound -lm -OBJ = alsa.o flac.o map.o mp3.o term.o vorbis.o wav.o +LDLIBS = -lasound +OBJ = alsa.o flac.o map.o mp3.o term.o BIN = map $(BIN): $(OBJ) diff --git a/README b/README @@ -2,7 +2,7 @@ map - minimalist audio player ============================= Linux only, using the ALSA backend. -Supports FLAC/MP3/Vorbis/WAV. +Supports FLAC and MP3. Play audio files in a directory. controls diff --git a/ext/dr_flac.h b/ext/dr_flac.h @@ -1,8 +1,10 @@ /* FLAC audio decoder. Choice of public domain or MIT-0. See license statements at the end of this file. -dr_flac - v0.12.6 - 2020-03-07 +dr_flac - v0.12.13 - 2020-05-16 David Reid - mackron@gmail.com + +GitHub: https://github.com/mackron/dr_libs */ /* @@ -115,16 +117,18 @@ to the old per-sample APIs. You now need to use the "pcm_frame" versions. /* -USAGE -===== -dr_flac is a single-file library. To use it, do something like the following in one .c file. +Introduction +============ +dr_flac is a single file library. To use it, do something like the following in one .c file. + ```c #define DR_FLAC_IMPLEMENTATION #include "dr_flac.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: +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 drflac* pFlac = drflac_open_file("MySong.flac", NULL); if (pFlac == NULL) { // Failed to open FLAC file @@ -132,28 +136,26 @@ do something like the following: drflac_int32* pSamples = malloc(pFlac->totalPCMFrameCount * pFlac->channels * sizeof(drflac_int32)); drflac_uint64 numberOfInterleavedSamplesActuallyRead = drflac_read_pcm_frames_s32(pFlac, pFlac->totalPCMFrameCount, pSamples); + ``` -The drflac object represents the decoder. It is a transparent type so all the information you need, such as the number of -channels and the bits per sample, should be directly accessible - just make sure you don't change their values. Samples are -always output as interleaved signed 32-bit PCM. In the example above a native FLAC stream was opened, however dr_flac has -seamless support for Ogg encapsulated FLAC streams as well. +The drflac object represents the decoder. It is a transparent type so all the information you need, such as the number of channels and the bits per sample, +should be directly accessible - just make sure you don't change their values. Samples are always output as interleaved signed 32-bit PCM. In the example above +a native FLAC stream was opened, however dr_flac has seamless support for Ogg encapsulated FLAC streams as well. -You do not need to decode the entire stream in one go - you just specify how many samples you'd like at any given time and -the decoder will give you as many samples as it can, up to the amount requested. Later on when you need the next batch of -samples, just call it again. Example: +You do not need to decode the entire stream in one go - you just specify how many samples you'd like at any given time and the decoder will give you as many +samples as it can, up to the amount requested. Later on when you need the next batch of samples, just call it again. Example: + ```c while (drflac_read_pcm_frames_s32(pFlac, chunkSizeInPCMFrames, pChunkSamples) > 0) { do_something(); } + ``` -You can seek to a specific sample with drflac_seek_to_sample(). The given sample is based on interleaving. So for example, -if you were to seek to the sample at index 0 in a stereo stream, you'll be seeking to the first sample of the left channel. -The sample at index 1 will be the first sample of the right channel. The sample at index 2 will be the second sample of the -left channel, etc. - +You can seek to a specific PCM frame with `drflac_seek_to_pcm_frame()`. If you just want to quickly decode an entire FLAC file in one go you can do something like this: + ```c unsigned int channels; unsigned int sampleRate; drflac_uint64 totalPCMFrameCount; @@ -165,94 +167,140 @@ If you just want to quickly decode an entire FLAC file in one go you can do some ... drflac_free(pSampleData); + ``` - -You can read samples as signed 16-bit integer and 32-bit floating-point PCM with the *_s16() and *_f32() family of APIs -respectively, but note that these should be considered lossy. +You can read samples as signed 16-bit integer and 32-bit floating-point PCM with the *_s16() and *_f32() family of APIs respectively, but note that these +should be considered lossy. -If you need access to metadata (album art, etc.), use drflac_open_with_metadata(), drflac_open_file_with_metdata() or -drflac_open_memory_with_metadata(). The rationale for keeping these APIs separate is that they're slightly slower than the -normal versions and also just a little bit harder to use. +If you need access to metadata (album art, etc.), use `drflac_open_with_metadata()`, `drflac_open_file_with_metdata()` or `drflac_open_memory_with_metadata()`. +The rationale for keeping these APIs separate is that they're slightly slower than the normal versions and also just a little bit harder to use. dr_flac +reports metadata to the application through the use of a callback, and every metadata block is reported before `drflac_open_with_metdata()` returns. -dr_flac reports metadata to the application through the use of a callback, and every metadata block is reported before -drflac_open_with_metdata() returns. +The main opening APIs (`drflac_open()`, etc.) will fail if the header is not present. The presents a problem in certain scenarios such as broadcast style +streams or internet radio where the header may not be present because the user has started playback mid-stream. To handle this, use the relaxed APIs: + + `drflac_open_relaxed()` + `drflac_open_with_metadata_relaxed()` +It is not recommended to use these APIs for file based streams because a missing header would usually indicate a corrupt or perverse file. In addition, these +APIs can take a long time to initialize because they may need to spend a lot of time finding the first frame. -The main opening APIs (drflac_open(), etc.) will fail if the header is not present. The presents a problem in certain -scenarios such as broadcast style streams or internet radio where the header may not be present because the user has -started playback mid-stream. To handle this, use the relaxed APIs: drflac_open_relaxed() and drflac_open_with_metadata_relaxed(). -It is not recommended to use these APIs for file based streams because a missing header would usually indicate a -corrupt or perverse file. In addition, these APIs can take a long time to initialize because they may need to spend -a lot of time finding the first frame. - - -OPTIONS -======= +Build Options +============= #define these options before including this file. #define DR_FLAC_NO_STDIO - Disable drflac_open_file() and family. + Disable `drflac_open_file()` and family. #define DR_FLAC_NO_OGG Disables support for Ogg/FLAC streams. #define DR_FLAC_BUFFER_SIZE <number> - Defines the size of the internal buffer to store data from onRead(). This buffer is used to reduce the number of calls - back to the client for more data. Larger values means more memory, but better performance. My tests show diminishing - returns after about 4KB (which is the default). Consider reducing this if you have a very efficient implementation of - onRead(), or increase it if it's very inefficient. Must be a multiple of 8. + Defines the size of the internal buffer to store data from onRead(). This buffer is used to reduce the number of calls back to the client for more data. + Larger values means more memory, but better performance. My tests show diminishing returns after about 4KB (which is the default). Consider reducing this if + you have a very efficient implementation of onRead(), or increase it if it's very inefficient. Must be a multiple of 8. #define DR_FLAC_NO_CRC - Disables CRC checks. This will offer a performance boost when CRC is unnecessary. This will disable binary search seeking. - When seeking, the seek table will be used if available. Otherwise the seek will be performed using brute force. + Disables CRC checks. This will offer a performance boost when CRC is unnecessary. This will disable binary search seeking. When seeking, the seek table will + be used if available. Otherwise the seek will be performed using brute force. #define DR_FLAC_NO_SIMD - Disables SIMD optimizations (SSE on x86/x64 architectures, NEON on ARM architectures). Use this if you are having - compatibility issues with your compiler. + Disables SIMD optimizations (SSE on x86/x64 architectures, NEON on ARM architectures). Use this if you are having compatibility issues with your compiler. -QUICK NOTES -=========== -- dr_flac does not currently support changing the sample rate nor channel count mid stream. -- This has not been tested on big-endian architectures. +Notes +===== +- dr_flac does not support changing the sample rate nor channel count mid stream. - dr_flac is not thread-safe, but its APIs can be called from any thread so long as you do your own synchronization. -- When using Ogg encapsulation, a corrupted metadata block will result in drflac_open_with_metadata() and drflac_open() - returning inconsistent samples. +- When using Ogg encapsulation, a corrupted metadata block will result in `drflac_open_with_metadata()` and `drflac_open()` returning inconsistent samples due + to differences in corrupted stream recorvery logic between the two APIs. */ #ifndef dr_flac_h #define dr_flac_h -#include <stddef.h> - -#if defined(_MSC_VER) && _MSC_VER < 1600 -typedef signed char drflac_int8; -typedef unsigned char drflac_uint8; -typedef signed short drflac_int16; -typedef unsigned short drflac_uint16; -typedef signed int drflac_int32; -typedef unsigned int drflac_uint32; -typedef signed __int64 drflac_int64; -typedef unsigned __int64 drflac_uint64; +#ifdef __cplusplus +extern "C" { +#endif + +#define DRFLAC_STRINGIFY(x) #x +#define DRFLAC_XSTRINGIFY(x) DRFLAC_STRINGIFY(x) + +#define DRFLAC_VERSION_MAJOR 0 +#define DRFLAC_VERSION_MINOR 12 +#define DRFLAC_VERSION_REVISION 13 +#define DRFLAC_VERSION_STRING DRFLAC_XSTRINGIFY(DRFLAC_VERSION_MAJOR) "." DRFLAC_XSTRINGIFY(DRFLAC_VERSION_MINOR) "." DRFLAC_XSTRINGIFY(DRFLAC_VERSION_REVISION) + +#include <stddef.h> /* For size_t. */ + +/* Sized types. Prefer built-in types. Fall back to stdint. */ +#ifdef _MSC_VER + #if defined(__clang__) + #pragma GCC diagnostic push + #pragma GCC diagnostic ignored "-Wlanguage-extension-token" + #pragma GCC diagnostic ignored "-Wlong-long" + #pragma GCC diagnostic ignored "-Wc++11-long-long" + #endif + typedef signed __int8 drflac_int8; + typedef unsigned __int8 drflac_uint8; + typedef signed __int16 drflac_int16; + typedef unsigned __int16 drflac_uint16; + typedef signed __int32 drflac_int32; + typedef unsigned __int32 drflac_uint32; + typedef signed __int64 drflac_int64; + typedef unsigned __int64 drflac_uint64; + #if defined(__clang__) + #pragma GCC diagnostic pop + #endif #else -#include <stdint.h> -typedef int8_t drflac_int8; -typedef uint8_t drflac_uint8; -typedef int16_t drflac_int16; -typedef uint16_t drflac_uint16; -typedef int32_t drflac_int32; -typedef uint32_t drflac_uint32; -typedef int64_t drflac_int64; -typedef uint64_t drflac_uint64; -#endif -typedef drflac_uint8 drflac_bool8; -typedef drflac_uint32 drflac_bool32; -#define DRFLAC_TRUE 1 -#define DRFLAC_FALSE 0 + #include <stdint.h> + typedef int8_t drflac_int8; + typedef uint8_t drflac_uint8; + typedef int16_t drflac_int16; + typedef uint16_t drflac_uint16; + typedef int32_t drflac_int32; + typedef uint32_t drflac_uint32; + typedef int64_t drflac_int64; + typedef uint64_t drflac_uint64; +#endif +typedef drflac_uint8 drflac_bool8; +typedef drflac_uint32 drflac_bool32; +#define DRFLAC_TRUE 1 +#define DRFLAC_FALSE 0 + +#if !defined(DRFLAC_API) + #if defined(DRFLAC_DLL) + #if defined(_WIN32) + #define DRFLAC_DLL_IMPORT __declspec(dllimport) + #define DRFLAC_DLL_EXPORT __declspec(dllexport) + #define DRFLAC_DLL_PRIVATE static + #else + #if defined(__GNUC__) && __GNUC__ >= 4 + #define DRFLAC_DLL_IMPORT __attribute__((visibility("default"))) + #define DRFLAC_DLL_EXPORT __attribute__((visibility("default"))) + #define DRFLAC_DLL_PRIVATE __attribute__((visibility("hidden"))) + #else + #define DRFLAC_DLL_IMPORT + #define DRFLAC_DLL_EXPORT + #define DRFLAC_DLL_PRIVATE static + #endif + #endif + + #if defined(DR_FLAC_IMPLEMENTATION) || defined(DRFLAC_IMPLEMENTATION) + #define DRFLAC_API DRFLAC_DLL_EXPORT + #else + #define DRFLAC_API DRFLAC_DLL_IMPORT + #endif + #define DRFLAC_PRIVATE DRFLAC_DLL_PRIVATE + #else + #define DRFLAC_API extern + #define DRFLAC_PRIVATE static + #endif +#endif #if defined(_MSC_VER) && _MSC_VER >= 1700 /* Visual Studio 2012 */ #define DRFLAC_DEPRECATED __declspec(deprecated) @@ -268,19 +316,17 @@ typedef drflac_uint32 drflac_bool32; #define DRFLAC_DEPRECATED #endif +DRFLAC_API void drflac_version(drflac_uint32* pMajor, drflac_uint32* pMinor, drflac_uint32* pRevision); +DRFLAC_API const char* drflac_version_string(); + /* -As data is read from the client it is placed into an internal buffer for fast access. This controls the -size of that buffer. Larger values means more speed, but also more memory. In my testing there is diminishing -returns after about 4KB, but you can fiddle with this to suit your own needs. Must be a multiple of 8. +As data is read from the client it is placed into an internal buffer for fast access. This controls the size of that buffer. Larger values means more speed, +but also more memory. In my testing there is diminishing returns after about 4KB, but you can fiddle with this to suit your own needs. Must be a multiple of 8. */ #ifndef DR_FLAC_BUFFER_SIZE #define DR_FLAC_BUFFER_SIZE 4096 #endif -#ifdef __cplusplus -extern "C" { -#endif - /* Check if we can enable 64-bit optimizations. */ #if defined(_WIN64) || defined(_LP64) || defined(__LP64__) #define DRFLAC_64BIT @@ -436,40 +482,77 @@ typedef struct /* Callback for when data needs to be read from the client. -pUserData [in] The user data that was passed to drflac_open() and family. -pBufferOut [out] The output buffer. -bytesToRead [in] The number of bytes to read. -Returns the number of bytes actually read. +Parameters +---------- +pUserData (in) + The user data that was passed to drflac_open() and family. -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. +pBufferOut (out) + The output buffer. + +bytesToRead (in) + The number of bytes to read. + + +Return Value +------------ +The number of bytes actually read. + + +Remarks +------- +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 (* drflac_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 drflac_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. +Parameters +---------- +pUserData (in) + The user data that was passed to drflac_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. -The offset will never be negative. Whether or not it is relative to the beginning or current position is determined -by the "origin" parameter which will be either drflac_seek_origin_start or drflac_seek_origin_current. -When seeking to a PCM frame using drflac_seek_to_pcm_frame(), dr_flac may call this with an offset beyond the end of -the FLAC stream. This needs to be detected and handled by returning DRFLAC_FALSE. +Return Value +------------ +Whether or not the seek was successful. + + +Remarks +------- +The offset will never be negative. Whether or not it is relative to the beginning or current position is determined by the "origin" parameter which will be +either drflac_seek_origin_start or drflac_seek_origin_current. + +When seeking to a PCM frame using drflac_seek_to_pcm_frame(), dr_flac may call this with an offset beyond the end of the FLAC stream. This needs to be detected +and handled by returning DRFLAC_FALSE. */ typedef drflac_bool32 (* drflac_seek_proc)(void* pUserData, int offset, drflac_seek_origin origin); /* Callback for when a metadata block is read. -pUserData [in] The user data that was passed to drflac_open() and family. -pMetadata [in] A pointer to a structure containing the data of the metadata block. +Parameters +---------- +pUserData (in) + The user data that was passed to drflac_open() and family. + +pMetadata (in) + A pointer to a structure containing the data of the metadata block. + + +Remarks +------- Use pMetadata->type to determine which metadata block is being handled and how to read the data. */ typedef void (* drflac_meta_proc)(void* pUserData, drflac_metadata* pMetadata); @@ -676,135 +759,292 @@ typedef struct drflac_uint8 pExtraData[1]; } drflac; + /* Opens a FLAC 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. -pAllocationCallbacks [in, optional] A pointer to application defined callbacks for managing memory allocations. +Parameters +---------- +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. + +pAllocationCallbacks (in, optional) + A pointer to application defined callbacks for managing memory allocations. + + +Return Value +------------ Returns a pointer to an object representing the decoder. -Close the decoder with drflac_close(). -pAllocationCallbacks can be NULL in which case it will use DRFLAC_MALLOC, DRFLAC_REALLOC and DRFLAC_FREE. +Remarks +------- +Close the decoder with `drflac_close()`. + +`pAllocationCallbacks` can be NULL in which case it will use `DRFLAC_MALLOC`, `DRFLAC_REALLOC` and `DRFLAC_FREE`. -This function will automatically detect whether or not you are attempting to open a native or Ogg encapsulated -FLAC, both of which should work seamlessly without any manual intervention. Ogg encapsulation also works with -multiplexed streams which basically means it can play FLAC encoded audio tracks in videos. +This function will automatically detect whether or not you are attempting to open a native or Ogg encapsulated FLAC, both of which should work seamlessly +without any manual intervention. Ogg encapsulation also works with multiplexed streams which basically means it can play FLAC encoded audio tracks in videos. -This is the lowest level function for opening a FLAC stream. You can also use drflac_open_file() and drflac_open_memory() -to open the stream from a file or from a block of memory respectively. +This is the lowest level function for opening a FLAC stream. You can also use `drflac_open_file()` and `drflac_open_memory()` to open the stream from a file or +from a block of memory respectively. -The STREAMINFO block must be present for this to succeed. Use drflac_open_relaxed() to open a FLAC stream where -the header may not be present. +The STREAMINFO block must be present for this to succeed. Use `drflac_open_relaxed()` to open a FLAC stream where the header may not be present. -See also: drflac_open_file(), drflac_open_memory(), drflac_open_with_metadata(), drflac_close() + +Seek Also +--------- +drflac_open_file() +drflac_open_memory() +drflac_open_with_metadata() +drflac_close() */ -drflac* drflac_open(drflac_read_proc onRead, drflac_seek_proc onSeek, void* pUserData, const drflac_allocation_callbacks* pAllocationCallbacks); +DRFLAC_API drflac* drflac_open(drflac_read_proc onRead, drflac_seek_proc onSeek, void* pUserData, const drflac_allocation_callbacks* pAllocationCallbacks); /* +Opens a FLAC stream with relaxed validation of the header block. + + +Parameters +---------- +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. + +container (in) + Whether or not the FLAC stream is encapsulated using standard FLAC encapsulation or Ogg encapsulation. + +pUserData (in, optional) + A pointer to application defined data that will be passed to onRead and onSeek. + +pAllocationCallbacks (in, optional) + A pointer to application defined callbacks for managing memory allocations. + + +Return Value +------------ +A pointer to an object representing the decoder. + + +Remarks +------- The same as drflac_open(), except attempts to open the stream even when a header block is not present. -Because the header is not necessarily available, the caller must explicitly define the container (Native or Ogg). Do -not set this to drflac_container_unknown - that is for internal use only. +Because the header is not necessarily available, the caller must explicitly define the container (Native or Ogg). Do not set this to `drflac_container_unknown` +as that is for internal use only. -Opening in relaxed mode will continue reading data from onRead until it finds a valid frame. If a frame is never -found it will continue forever. To abort, force your onRead callback to return 0, which dr_flac will use as an -indicator that the end of the stream was found. +Opening in relaxed mode will continue reading data from onRead until it finds a valid frame. If a frame is never found it will continue forever. To abort, +force your `onRead` callback to return 0, which dr_flac will use as an indicator that the end of the stream was found. */ -drflac* drflac_open_relaxed(drflac_read_proc onRead, drflac_seek_proc onSeek, drflac_container container, void* pUserData, const drflac_allocation_callbacks* pAllocationCallbacks); +DRFLAC_API drflac* drflac_open_relaxed(drflac_read_proc onRead, drflac_seek_proc onSeek, drflac_container container, void* pUserData, const drflac_allocation_callbacks* pAllocationCallbacks); /* Opens a FLAC decoder and notifies the caller of the metadata chunks (album art, etc.). -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. -onMeta [in] The function to call for every metadata block. -pUserData [in, optional] A pointer to application defined data that will be passed to onRead, onSeek and onMeta. -pAllocationCallbacks [in, optional] A pointer to application defined callbacks for managing memory allocations. -Returns a pointer to an object representing the decoder. +Parameters +---------- +onRead (in) + The function to call when data needs to be read from the client. -Close the decoder with drflac_close(). +onSeek (in) + The function to call when the read position of the client data needs to move. + +onMeta (in) + The function to call for every metadata block. + +pUserData (in, optional) + A pointer to application defined data that will be passed to onRead, onSeek and onMeta. -pAllocationCallbacks can be NULL in which case it will use DRFLAC_MALLOC, DRFLAC_REALLOC and DRFLAC_FREE. +pAllocationCallbacks (in, optional) + A pointer to application defined callbacks for managing memory allocations. -This is slower than drflac_open(), so avoid this one if you don't need metadata. Internally, this will allocate and free -memory on the heap for every metadata block except for STREAMINFO and PADDING blocks. -The caller is notified of the metadata via the onMeta callback. All metadata blocks will be handled before the function -returns. +Return Value +------------ +A pointer to an object representing the decoder. + + +Remarks +------- +Close the decoder with `drflac_close()`. + +`pAllocationCallbacks` can be NULL in which case it will use `DRFLAC_MALLOC`, `DRFLAC_REALLOC` and `DRFLAC_FREE`. -The STREAMINFO block must be present for this to succeed. Use drflac_open_with_metadata_relaxed() to open a FLAC -stream where the header may not be present. +This is slower than `drflac_open()`, so avoid this one if you don't need metadata. Internally, this will allocate and free memory on the heap for every +metadata block except for STREAMINFO and PADDING blocks. -Note that this will behave inconsistently with drflac_open() if the stream is an Ogg encapsulated stream and a metadata -block is corrupted. This is due to the way the Ogg stream recovers from corrupted pages. When drflac_open_with_metadata() -is being used, the open routine will try to read the contents of the metadata block, whereas drflac_open() will simply -seek past it (for the sake of efficiency). This inconsistency can result in different samples being returned depending on -whether or not the stream is being opened with metadata. +The caller is notified of the metadata via the `onMeta` callback. All metadata blocks will be handled before the function returns. -See also: drflac_open_file_with_metadata(), drflac_open_memory_with_metadata(), drflac_open(), drflac_close() +The STREAMINFO block must be present for this to succeed. Use `drflac_open_with_metadata_relaxed()` to open a FLAC stream where the header may not be present. + +Note that this will behave inconsistently with `drflac_open()` if the stream is an Ogg encapsulated stream and a metadata block is corrupted. This is due to +the way the Ogg stream recovers from corrupted pages. When `drflac_open_with_metadata()` is being used, the open routine will try to read the contents of the +metadata block, whereas `drflac_open()` will simply seek past it (for the sake of efficiency). This inconsistency can result in different samples being +returned depending on whether or not the stream is being opened with metadata. + + +Seek Also +--------- +drflac_open_file_with_metadata() +drflac_open_memory_with_metadata() +drflac_open() +drflac_close() */ -drflac* drflac_open_with_metadata(drflac_read_proc onRead, drflac_seek_proc onSeek, drflac_meta_proc onMeta, void* pUserData, const drflac_allocation_callbacks* pAllocationCallbacks); +DRFLAC_API drflac* drflac_open_with_metadata(drflac_read_proc onRead, drflac_seek_proc onSeek, drflac_meta_proc onMeta, void* pUserData, const drflac_allocation_callbacks* pAllocationCallbacks); /* The same as drflac_open_with_metadata(), except attempts to open the stream even when a header block is not present. -See also: drflac_open_with_metadata(), drflac_open_relaxed() +See Also +-------- +drflac_open_with_metadata() +drflac_open_relaxed() */ -drflac* drflac_open_with_metadata_relaxed(drflac_read_proc onRead, drflac_seek_proc onSeek, drflac_meta_proc onMeta, drflac_container container, void* pUserData, const drflac_allocation_callbacks* pAllocationCallbacks); +DRFLAC_API drflac* drflac_open_with_metadata_relaxed(drflac_read_proc onRead, drflac_seek_proc onSeek, drflac_meta_proc onMeta, drflac_container container, void* pUserData, const drflac_allocation_callbacks* pAllocationCallbacks); /* Closes the given FLAC decoder. -pFlac [in] The decoder to close. +Parameters +---------- +pFlac (in) + The decoder to close. + + +Remarks +------- This will destroy the decoder object. + + +See Also +-------- +drflac_open() +drflac_open_with_metadata() +drflac_open_file() +drflac_open_file_w() +drflac_open_file_with_metadata() +drflac_open_file_with_metadata_w() +drflac_open_memory() +drflac_open_memory_with_metadata() */ -void drflac_close(drflac* pFlac); +DRFLAC_API void drflac_close(drflac* pFlac); /* Reads sample data from the given FLAC decoder, output as interleaved signed 32-bit PCM. -pFlac [in] The decoder. -framesToRead [in] The number of PCM frames to read. -pBufferOut [out, optional] A pointer to the buffer that will receive the decoded samples. -Returns the number of PCM frames actually read. +Parameters +---------- +pFlac (in) + The decoder. + +framesToRead (in) + The number of PCM frames to read. + +pBufferOut (out, optional) + A pointer to the buffer that will receive the decoded samples. + + +Return Value +------------ +Returns the number of PCM frames actually read. If the return value is less than `framesToRead` it has reached the end. + -pBufferOut can be null, in which case the call will act as a seek, and the return value will be the number of frames -seeked. +Remarks +------- +pBufferOut can be null, in which case the call will act as a seek, and the return value will be the number of frames seeked. */ -drflac_uint64 drflac_read_pcm_frames_s32(drflac* pFlac, drflac_uint64 framesToRead, drflac_int32* pBufferOut); +DRFLAC_API drflac_uint64 drflac_read_pcm_frames_s32(drflac* pFlac, drflac_uint64 framesToRead, drflac_int32* pBufferOut); + /* -Same as drflac_read_pcm_frames_s32(), except outputs samples as 16-bit integer PCM rather than 32-bit. +Reads sample data from the given FLAC decoder, output as interleaved signed 16-bit PCM. + + +Parameters +---------- +pFlac (in) + The decoder. + +framesToRead (in) + The number of PCM frames to read. + +pBufferOut (out, optional) + A pointer to the buffer that will receive the decoded samples. + + +Return Value +------------ +Returns the number of PCM frames actually read. If the return value is less than `framesToRead` it has reached the end. + + +Remarks +------- +pBufferOut can be null, in which case the call will act as a seek, and the return value will be the number of frames seeked. Note that this is lossy for streams where the bits per sample is larger than 16. */ -drflac_uint64 drflac_read_pcm_frames_s16(drflac* pFlac, drflac_uint64 framesToRead, drflac_int16* pBufferOut); +DRFLAC_API drflac_uint64 drflac_read_pcm_frames_s16(drflac* pFlac, drflac_uint64 framesToRead, drflac_int16* pBufferOut); /* -Same as drflac_read_pcm_frames_s32(), except outputs samples as 32-bit floating-point PCM. +Reads sample data from the given FLAC decoder, output as interleaved 32-bit floating point PCM. + + +Parameters +---------- +pFlac (in) + The decoder. + +framesToRead (in) + The number of PCM frames to read. + +pBufferOut (out, optional) + A pointer to the buffer that will receive the decoded samples. + -Note that this should be considered lossy due to the nature of floating point numbers not being able to exactly -represent every possible number. +Return Value +------------ +Returns the number of PCM frames actually read. If the return value is less than `framesToRead` it has reached the end. + + +Remarks +------- +pBufferOut can be null, in which case the call will act as a seek, and the return value will be the number of frames seeked. + +Note that this should be considered lossy due to the nature of floating point numbers not being able to exactly represent every possible number. */ -drflac_uint64 drflac_read_pcm_frames_f32(drflac* pFlac, drflac_uint64 framesToRead, float* pBufferOut); +DRFLAC_API drflac_uint64 drflac_read_pcm_frames_f32(drflac* pFlac, drflac_uint64 framesToRead, float* pBufferOut); /* Seeks to the PCM frame at the given index. -pFlac [in] The decoder. -pcmFrameIndex [in] The index of the PCM frame to seek to. See notes below. -Returns DRFLAC_TRUE if successful; DRFLAC_FALSE otherwise. +Parameters +---------- +pFlac (in) + The decoder. + +pcmFrameIndex (in) + The index of the PCM frame to seek to. See notes below. + + +Return Value +------------- +`DRFLAC_TRUE` if successful; `DRFLAC_FALSE` otherwise. */ -drflac_bool32 drflac_seek_to_pcm_frame(drflac* pFlac, drflac_uint64 pcmFrameIndex); +DRFLAC_API drflac_bool32 drflac_seek_to_pcm_frame(drflac* pFlac, drflac_uint64 pcmFrameIndex); @@ -812,43 +1052,145 @@ drflac_bool32 drflac_seek_to_pcm_frame(drflac* pFlac, drflac_uint64 pcmFrameInde /* Opens a FLAC decoder from the file at the given path. -filename [in] The path of the file to open, either absolute or relative to the current directory. -pAllocationCallbacks [in, optional] A pointer to application defined callbacks for managing memory allocations. -Returns a pointer to an object representing the decoder. +Parameters +---------- +pFileName (in) + The path of the file to open, either absolute or relative to the current directory. + +pAllocationCallbacks (in, optional) + A pointer to application defined callbacks for managing memory allocations. + + +Return Value +------------ +A pointer to an object representing the decoder. + +Remarks +------- Close the decoder with drflac_close(). -This will hold a handle to the file until the decoder is closed with drflac_close(). Some platforms will restrict the -number of files a process can have open at any given time, so keep this mind if you have many decoders open at the -same time. -See also: drflac_open(), drflac_open_file_with_metadata(), drflac_close() +Remarks +------- +This will hold a handle to the file until the decoder is closed with drflac_close(). Some platforms will restrict the number of files a process can have open +at any given time, so keep this mind if you have many decoders open at the same time. + + +See Also +-------- +drflac_open_file_with_metadata() +drflac_open() +drflac_close() */ -drflac* drflac_open_file(const char* filename, const drflac_allocation_callbacks* pAllocationCallbacks); +DRFLAC_API drflac* drflac_open_file(const char* pFileName, const drflac_allocation_callbacks* pAllocationCallbacks); +DRFLAC_API drflac* drflac_open_file_w(const wchar_t* pFileName, const drflac_allocation_callbacks* pAllocationCallbacks); /* Opens a FLAC decoder from the file at the given path and notifies the caller of the metadata chunks (album art, etc.) + +Parameters +---------- +pFileName (in) + The path of the file to open, either absolute or relative to the current directory. + +pAllocationCallbacks (in, optional) + A pointer to application defined callbacks for managing memory allocations. + +onMeta (in) + The callback to fire for each metadata block. + +pUserData (in) + A pointer to the user data to pass to the metadata callback. + +pAllocationCallbacks (in) + A pointer to application defined callbacks for managing memory allocations. + + +Remarks +------- Look at the documentation for drflac_open_with_metadata() for more information on how metadata is handled. + + +See Also +-------- +drflac_open_with_metadata() +drflac_open() +drflac_close() */ -drflac* drflac_open_file_with_metadata(const char* filename, drflac_meta_proc onMeta, void* pUserData, const drflac_allocation_callbacks* pAllocationCallbacks); +DRFLAC_API drflac* drflac_open_file_with_metadata(const char* pFileName, drflac_meta_proc onMeta, void* pUserData, const drflac_allocation_callbacks* pAllocationCallbacks); +DRFLAC_API drflac* drflac_open_file_with_metadata_w(const wchar_t* pFileName, drflac_meta_proc onMeta, void* pUserData, const drflac_allocation_callbacks* pAllocationCallbacks); #endif /* Opens a FLAC decoder from a pre-allocated 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 decoder. + +Parameters +---------- +pData (in) + A pointer to the raw encoded FLAC data. + +dataSize (in) + The size in bytes of `data`. + +pAllocationCallbacks (in) + A pointer to application defined callbacks for managing memory allocations. + + +Return Value +------------ +A pointer to an object representing the decoder. + + +Remarks +------- +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 decoder. + + +See Also +-------- +drflac_open() +drflac_close() */ -drflac* drflac_open_memory(const void* data, size_t dataSize, const drflac_allocation_callbacks* pAllocationCallbacks); +DRFLAC_API drflac* drflac_open_memory(const void* pData, size_t dataSize, const drflac_allocation_callbacks* pAllocationCallbacks); /* Opens a FLAC decoder from a pre-allocated block of memory and notifies the caller of the metadata chunks (album art, etc.) + +Parameters +---------- +pData (in) + A pointer to the raw encoded FLAC data. + +dataSize (in) + The size in bytes of `data`. + +onMeta (in) + The callback to fire for each metadata block. + +pUserData (in) + A pointer to the user data to pass to the metadata callback. + +pAllocationCallbacks (in) + A pointer to application defined callbacks for managing memory allocations. + + +Remarks +------- Look at the documentation for drflac_open_with_metadata() for more information on how metadata is handled. + + +See Also +------- +drflac_open_with_metadata() +drflac_open() +drflac_close() */ -drflac* drflac_open_memory_with_metadata(const void* data, size_t dataSize, drflac_meta_proc onMeta, void* pUserData, const drflac_allocation_callbacks* pAllocationCallbacks); +DRFLAC_API drflac* drflac_open_memory_with_metadata(const void* pData, size_t dataSize, drflac_meta_proc onMeta, void* pUserData, const drflac_allocation_callbacks* pAllocationCallbacks); @@ -866,40 +1208,40 @@ read samples into a dynamically sized buffer on the heap until no samples are le Do not call this function on a broadcast type of stream (like internet radio streams and whatnot). */ -drflac_int32* drflac_open_and_read_pcm_frames_s32(drflac_read_proc onRead, drflac_seek_proc onSeek, void* pUserData, unsigned int* channels, unsigned int* sampleRate, drflac_uint64* totalPCMFrameCount, const drflac_allocation_callbacks* pAllocationCallbacks); +DRFLAC_API drflac_int32* drflac_open_and_read_pcm_frames_s32(drflac_read_proc onRead, drflac_seek_proc onSeek, void* pUserData, unsigned int* channels, unsigned int* sampleRate, drflac_uint64* totalPCMFrameCount, const drflac_allocation_callbacks* pAllocationCallbacks); /* Same as drflac_open_and_read_pcm_frames_s32(), except returns signed 16-bit integer samples. */ -drflac_int16* drflac_open_and_read_pcm_frames_s16(drflac_read_proc onRead, drflac_seek_proc onSeek, void* pUserData, unsigned int* channels, unsigned int* sampleRate, drflac_uint64* totalPCMFrameCount, const drflac_allocation_callbacks* pAllocationCallbacks); +DRFLAC_API drflac_int16* drflac_open_and_read_pcm_frames_s16(drflac_read_proc onRead, drflac_seek_proc onSeek, void* pUserData, unsigned int* channels, unsigned int* sampleRate, drflac_uint64* totalPCMFrameCount, const drflac_allocation_callbacks* pAllocationCallbacks); /* Same as drflac_open_and_read_pcm_frames_s32(), except returns 32-bit floating-point samples. */ -float* drflac_open_and_read_pcm_frames_f32(drflac_read_proc onRead, drflac_seek_proc onSeek, void* pUserData, unsigned int* channels, unsigned int* sampleRate, drflac_uint64* totalPCMFrameCount, const drflac_allocation_callbacks* pAllocationCallbacks); +DRFLAC_API float* drflac_open_and_read_pcm_frames_f32(drflac_read_proc onRead, drflac_seek_proc onSeek, void* pUserData, unsigned int* channels, unsigned int* sampleRate, drflac_uint64* totalPCMFrameCount, const drflac_allocation_callbacks* pAllocationCallbacks); #ifndef DR_FLAC_NO_STDIO /* Same as drflac_open_and_read_pcm_frames_s32() except opens the decoder from a file. */ -drflac_int32* drflac_open_file_and_read_pcm_frames_s32(const char* filename, unsigned int* channels, unsigned int* sampleRate, drflac_uint64* totalPCMFrameCount, const drflac_allocation_callbacks* pAllocationCallbacks); +DRFLAC_API drflac_int32* drflac_open_file_and_read_pcm_frames_s32(const char* filename, unsigned int* channels, unsigned int* sampleRate, drflac_uint64* totalPCMFrameCount, const drflac_allocation_callbacks* pAllocationCallbacks); /* Same as drflac_open_file_and_read_pcm_frames_s32(), except returns signed 16-bit integer samples. */ -drflac_int16* drflac_open_file_and_read_pcm_frames_s16(const char* filename, unsigned int* channels, unsigned int* sampleRate, drflac_uint64* totalPCMFrameCount, const drflac_allocation_callbacks* pAllocationCallbacks); +DRFLAC_API drflac_int16* drflac_open_file_and_read_pcm_frames_s16(const char* filename, unsigned int* channels, unsigned int* sampleRate, drflac_uint64* totalPCMFrameCount, const drflac_allocation_callbacks* pAllocationCallbacks); /* Same as drflac_open_file_and_read_pcm_frames_s32(), except returns 32-bit floating-point samples. */ -float* drflac_open_file_and_read_pcm_frames_f32(const char* filename, unsigned int* channels, unsigned int* sampleRate, drflac_uint64* totalPCMFrameCount, const drflac_allocation_callbacks* pAllocationCallbacks); +DRFLAC_API float* drflac_open_file_and_read_pcm_frames_f32(const char* filename, unsigned int* channels, unsigned int* sampleRate, drflac_uint64* totalPCMFrameCount, const drflac_allocation_callbacks* pAllocationCallbacks); #endif /* Same as drflac_open_and_read_pcm_frames_s32() except opens the decoder from a block of memory. */ -drflac_int32* drflac_open_memory_and_read_pcm_frames_s32(const void* data, size_t dataSize, unsigned int* channels, unsigned int* sampleRate, drflac_uint64* totalPCMFrameCount, const drflac_allocation_callbacks* pAllocationCallbacks); +DRFLAC_API drflac_int32* drflac_open_memory_and_read_pcm_frames_s32(const void* data, size_t dataSize, unsigned int* channels, unsigned int* sampleRate, drflac_uint64* totalPCMFrameCount, const drflac_allocation_callbacks* pAllocationCallbacks); /* Same as drflac_open_memory_and_read_pcm_frames_s32(), except returns signed 16-bit integer samples. */ -drflac_int16* drflac_open_memory_and_read_pcm_frames_s16(const void* data, size_t dataSize, unsigned int* channels, unsigned int* sampleRate, drflac_uint64* totalPCMFrameCount, const drflac_allocation_callbacks* pAllocationCallbacks); +DRFLAC_API drflac_int16* drflac_open_memory_and_read_pcm_frames_s16(const void* data, size_t dataSize, unsigned int* channels, unsigned int* sampleRate, drflac_uint64* totalPCMFrameCount, const drflac_allocation_callbacks* pAllocationCallbacks); /* Same as drflac_open_memory_and_read_pcm_frames_s32(), except returns 32-bit floating-point samples. */ -float* drflac_open_memory_and_read_pcm_frames_f32(const void* data, size_t dataSize, unsigned int* channels, unsigned int* sampleRate, drflac_uint64* totalPCMFrameCount, const drflac_allocation_callbacks* pAllocationCallbacks); +DRFLAC_API float* drflac_open_memory_and_read_pcm_frames_f32(const void* data, size_t dataSize, unsigned int* channels, unsigned int* sampleRate, drflac_uint64* totalPCMFrameCount, const drflac_allocation_callbacks* pAllocationCallbacks); /* Frees memory that was allocated internally by dr_flac. Set pAllocationCallbacks to the same object that was passed to drflac_open_*_and_read_pcm_frames_*(). If you originally passed in NULL, pass in NULL for this. */ -void drflac_free(void* p, const drflac_allocation_callbacks* pAllocationCallbacks); +DRFLAC_API void drflac_free(void* p, const drflac_allocation_callbacks* pAllocationCallbacks); /* Structure representing an iterator for vorbis comments in a VORBIS_COMMENT metadata block. */ @@ -913,13 +1255,13 @@ typedef struct Initializes a vorbis comment iterator. This can be used for iterating over the vorbis comments in a VORBIS_COMMENT metadata block. */ -void drflac_init_vorbis_comment_iterator(drflac_vorbis_comment_iterator* pIter, drflac_uint32 commentCount, const void* pComments); +DRFLAC_API void drflac_init_vorbis_comment_iterator(drflac_vorbis_comment_iterator* pIter, drflac_uint32 commentCount, const void* pComments); /* Goes to the next vorbis comment in the given iterator. If null is returned it means there are no more comments. The returned string is NOT null terminated. */ -const char* drflac_next_vorbis_comment(drflac_vorbis_comment_iterator* pIter, drflac_uint32* pCommentLengthOut); +DRFLAC_API const char* drflac_next_vorbis_comment(drflac_vorbis_comment_iterator* pIter, drflac_uint32* pCommentLengthOut); /* Structure representing an iterator for cuesheet tracks in a CUESHEET metadata block. */ @@ -954,10 +1296,10 @@ typedef struct Initializes a cuesheet track iterator. This can be used for iterating over the cuesheet tracks in a CUESHEET metadata block. */ -void drflac_init_cuesheet_track_iterator(drflac_cuesheet_track_iterator* pIter, drflac_uint32 trackCount, const void* pTrackData); +DRFLAC_API void drflac_init_cuesheet_track_iterator(drflac_cuesheet_track_iterator* pIter, drflac_uint32 trackCount, const void* pTrackData); /* Goes to the next cuesheet track in the given iterator. If DRFLAC_FALSE is returned it means there are no more comments. */ -drflac_bool32 drflac_next_cuesheet_track(drflac_cuesheet_track_iterator* pIter, drflac_cuesheet_track* pCuesheetTrack); +DRFLAC_API drflac_bool32 drflac_next_cuesheet_track(drflac_cuesheet_track_iterator* pIter, drflac_cuesheet_track* pCuesheetTrack); #ifdef __cplusplus @@ -973,7 +1315,7 @@ drflac_bool32 drflac_next_cuesheet_track(drflac_cuesheet_track_iterator* pIter, ************************************************************************************************************************************************************ ************************************************************************************************************************************************************/ -#ifdef DR_FLAC_IMPLEMENTATION +#if defined(DR_FLAC_IMPLEMENTATION) || defined(DRFLAC_IMPLEMENTATION) /* Disable some annoying warnings. */ #if defined(__GNUC__) @@ -1124,7 +1466,7 @@ drflac_bool32 drflac_next_cuesheet_track(drflac_cuesheet_track_iterator* pIter, #define DRFLAC_NO_CPUID #endif -static DRFLAC_INLINE drflac_bool32 drflac_has_sse2() +static DRFLAC_INLINE drflac_bool32 drflac_has_sse2(void) { #if defined(DRFLAC_SUPPORT_SSE2) #if (defined(DRFLAC_X64) || defined(DRFLAC_X86)) && !defined(DRFLAC_NO_SSE2) @@ -1149,7 +1491,7 @@ static DRFLAC_INLINE drflac_bool32 drflac_has_sse2() #endif } -static DRFLAC_INLINE drflac_bool32 drflac_has_sse41() +static DRFLAC_INLINE drflac_bool32 drflac_has_sse41(void) { #if defined(DRFLAC_SUPPORT_SSE41) #if (defined(DRFLAC_X64) || defined(DRFLAC_X86)) && !defined(DRFLAC_NO_SSE41) @@ -1234,15 +1576,68 @@ static DRFLAC_INLINE drflac_bool32 drflac_has_sse41() #ifndef DRFLAC_ZERO_MEMORY #define DRFLAC_ZERO_MEMORY(p, sz) memset((p), 0, (sz)) #endif +#ifndef DRFLAC_ZERO_OBJECT +#define DRFLAC_ZERO_OBJECT(p) DRFLAC_ZERO_MEMORY((p), sizeof(*(p))) +#endif #define DRFLAC_MAX_SIMD_VECTOR_SIZE 64 /* 64 for AVX-512 in the future. */ typedef drflac_int32 drflac_result; -#define DRFLAC_SUCCESS 0 -#define DRFLAC_ERROR -1 /* A generic error. */ +#define DRFLAC_SUCCESS 0 +#define DRFLAC_ERROR -1 /* A generic error. */ #define DRFLAC_INVALID_ARGS -2 -#define DRFLAC_END_OF_STREAM -128 -#define DRFLAC_CRC_MISMATCH -129 +#define DRFLAC_INVALID_OPERATION -3 +#define DRFLAC_OUT_OF_MEMORY -4 +#define DRFLAC_OUT_OF_RANGE -5 +#define DRFLAC_ACCESS_DENIED -6 +#define DRFLAC_DOES_NOT_EXIST -7 +#define DRFLAC_ALREADY_EXISTS -8 +#define DRFLAC_TOO_MANY_OPEN_FILES -9 +#define DRFLAC_INVALID_FILE -10 +#define DRFLAC_TOO_BIG -11 +#define DRFLAC_PATH_TOO_LONG -12 +#define DRFLAC_NAME_TOO_LONG -13 +#define DRFLAC_NOT_DIRECTORY -14 +#define DRFLAC_IS_DIRECTORY -15 +#define DRFLAC_DIRECTORY_NOT_EMPTY -16 +#define DRFLAC_END_OF_FILE -17 +#define DRFLAC_NO_SPACE -18 +#define DRFLAC_BUSY -19 +#define DRFLAC_IO_ERROR -20 +#define DRFLAC_INTERRUPT -21 +#define DRFLAC_UNAVAILABLE -22 +#define DRFLAC_ALREADY_IN_USE -23 +#define DRFLAC_BAD_ADDRESS -24 +#define DRFLAC_BAD_SEEK -25 +#define DRFLAC_BAD_PIPE -26 +#define DRFLAC_DEADLOCK -27 +#define DRFLAC_TOO_MANY_LINKS -28 +#define DRFLAC_NOT_IMPLEMENTED -29 +#define DRFLAC_NO_MESSAGE -30 +#define DRFLAC_BAD_MESSAGE -31 +#define DRFLAC_NO_DATA_AVAILABLE -32 +#define DRFLAC_INVALID_DATA -33 +#define DRFLAC_TIMEOUT -34 +#define DRFLAC_NO_NETWORK -35 +#define DRFLAC_NOT_UNIQUE -36 +#define DRFLAC_NOT_SOCKET -37 +#define DRFLAC_NO_ADDRESS -38 +#define DRFLAC_BAD_PROTOCOL -39 +#define DRFLAC_PROTOCOL_UNAVAILABLE -40 +#define DRFLAC_PROTOCOL_NOT_SUPPORTED -41 +#define DRFLAC_PROTOCOL_FAMILY_NOT_SUPPORTED -42 +#define DRFLAC_ADDRESS_FAMILY_NOT_SUPPORTED -43 +#define DRFLAC_SOCKET_NOT_SUPPORTED -44 +#define DRFLAC_CONNECTION_RESET -45 +#define DRFLAC_ALREADY_CONNECTED -46 +#define DRFLAC_NOT_CONNECTED -47 +#define DRFLAC_CONNECTION_REFUSED -48 +#define DRFLAC_NO_HOST -49 +#define DRFLAC_IN_PROGRESS -50 +#define DRFLAC_CANCELLED -51 +#define DRFLAC_MEMORY_ALREADY_MAPPED -52 +#define DRFLAC_AT_END -53 +#define DRFLAC_CRC_MISMATCH -128 #define DRFLAC_SUBFRAME_CONSTANT 0 #define DRFLAC_SUBFRAME_VERBATIM 1 @@ -1261,6 +1656,27 @@ typedef drflac_int32 drflac_result; #define drflac_align(x, a) ((((x) + (a) - 1) / (a)) * (a)) +DRFLAC_API void drflac_version(drflac_uint32* pMajor, drflac_uint32* pMinor, drflac_uint32* pRevision) +{ + if (pMajor) { + *pMajor = DRFLAC_VERSION_MAJOR; + } + + if (pMinor) { + *pMinor = DRFLAC_VERSION_MINOR; + } + + if (pRevision) { + *pRevision = DRFLAC_VERSION_REVISION; + } +} + +DRFLAC_API const char* drflac_version_string() +{ + return DRFLAC_VERSION_STRING; +} + + /* CPU caps. */ #if defined(__has_feature) #if __has_feature(thread_sanitizer) @@ -1286,15 +1702,14 @@ actually make sense. However, since CPU caps should never differ for a running p complicating internal API's by passing around CPU caps versus just disabling the warnings is worthwhile. I'm therefore just going to disable these warnings. This is disabled via the DRFLAC_NO_THREAD_SANITIZE attribute. */ -DRFLAC_NO_THREAD_SANITIZE static void drflac__init_cpu_caps() +DRFLAC_NO_THREAD_SANITIZE static void drflac__init_cpu_caps(void) { static drflac_bool32 isCPUCapsInitialized = DRFLAC_FALSE; if (!isCPUCapsInitialized) { - int info[4] = {0}; - /* LZCNT */ #if defined(DRFLAC_HAS_LZCNT_INTRINSIC) + int info[4] = {0}; drflac__cpuid(info, 0x80000001); drflac__gIsLZCNTSupported = (info[2] & (1 << 5)) != 0; #endif @@ -1312,7 +1727,7 @@ DRFLAC_NO_THREAD_SANITIZE static void drflac__init_cpu_caps() #else static drflac_bool32 drflac__gIsNEONSupported = DRFLAC_FALSE; -static DRFLAC_INLINE drflac_bool32 drflac__has_neon() +static DRFLAC_INLINE drflac_bool32 drflac__has_neon(void) { #if defined(DRFLAC_SUPPORT_NEON) #if defined(DRFLAC_ARM) && !defined(DRFLAC_NO_NEON) @@ -1330,7 +1745,7 @@ static DRFLAC_INLINE drflac_bool32 drflac__has_neon() #endif } -DRFLAC_NO_THREAD_SANITIZE static void drflac__init_cpu_caps() +DRFLAC_NO_THREAD_SANITIZE static void drflac__init_cpu_caps(void) { drflac__gIsNEONSupported = drflac__has_neon(); @@ -1342,7 +1757,7 @@ DRFLAC_NO_THREAD_SANITIZE static void drflac__init_cpu_caps() /* Endian Management */ -static DRFLAC_INLINE drflac_bool32 drflac__is_little_endian() +static DRFLAC_INLINE drflac_bool32 drflac__is_little_endian(void) { #if defined(DRFLAC_X86) || defined(DRFLAC_X64) return DRFLAC_TRUE; @@ -1575,7 +1990,7 @@ static DRFLAC_INLINE drflac_uint8 drflac_crc8(drflac_uint8 crc, drflac_uint32 da case 3: crc = drflac_crc8_byte(crc, (drflac_uint8)((data & (0x00FF0000UL << leftoverBits)) >> (16 + leftoverBits))); case 2: crc = drflac_crc8_byte(crc, (drflac_uint8)((data & (0x0000FF00UL << leftoverBits)) >> ( 8 + leftoverBits))); case 1: crc = drflac_crc8_byte(crc, (drflac_uint8)((data & (0x000000FFUL << leftoverBits)) >> ( 0 + leftoverBits))); - case 0: if (leftoverBits > 0) crc = (crc << leftoverBits) ^ drflac__crc8_table[(crc >> (8 - leftoverBits)) ^ (data & leftoverDataMask)]; + case 0: if (leftoverBits > 0) crc = (drflac_uint8)((crc << leftoverBits) ^ drflac__crc8_table[(crc >> (8 - leftoverBits)) ^ (data & leftoverDataMask)]); } return crc; #endif @@ -1953,7 +2368,11 @@ static DRFLAC_INLINE drflac_bool32 drflac__read_uint32(drflac_bs* bs, unsigned i /* It straddles the cached data. It will never cover more than the next chunk. We just read the number in two parts and combine them. */ drflac_uint32 bitCountHi = DRFLAC_CACHE_L1_BITS_REMAINING(bs); drflac_uint32 bitCountLo = bitCount - bitCountHi; - drflac_uint32 resultHi = (drflac_uint32)DRFLAC_CACHE_L1_SELECT_AND_SHIFT(bs, bitCountHi); + drflac_uint32 resultHi; + + DRFLAC_ASSERT(bitCountHi > 0); + DRFLAC_ASSERT(bitCountHi < 32); + resultHi = (drflac_uint32)DRFLAC_CACHE_L1_SELECT_AND_SHIFT(bs, bitCountHi); if (!drflac__reload_cache(bs)) { return DRFLAC_FALSE; @@ -2242,7 +2661,7 @@ static DRFLAC_INLINE drflac_uint32 drflac__clz_software(drflac_cache_t x) } #ifdef DRFLAC_IMPLEMENT_CLZ_LZCNT -static DRFLAC_INLINE drflac_bool32 drflac__is_lzcnt_supported() +static DRFLAC_INLINE drflac_bool32 drflac__is_lzcnt_supported(void) { /* Fast compile time check for ARM. */ #if defined(DRFLAC_HAS_LZCNT_INTRINSIC) && defined(DRFLAC_ARM) && (defined(__ARM_ARCH) && __ARM_ARCH >= 5) @@ -2422,7 +2841,7 @@ static drflac_result drflac__read_utf8_coded_number(drflac_bs* bs, drflac_uint64 { drflac_uint8 crc; drflac_uint64 result; - unsigned char utf8[7] = {0}; + drflac_uint8 utf8[7] = {0}; int byteCount; int i; @@ -2434,7 +2853,7 @@ static drflac_result drflac__read_utf8_coded_number(drflac_bs* bs, drflac_uint64 if (!drflac__read_uint8(bs, 8, utf8)) { *pNumberOut = 0; - return DRFLAC_END_OF_STREAM; + return DRFLAC_AT_END; } crc = drflac_crc8(crc, utf8[0], 8); @@ -2469,7 +2888,7 @@ static drflac_result drflac__read_utf8_coded_number(drflac_bs* bs, drflac_uint64 for (i = 1; i < byteCount; ++i) { if (!drflac__read_uint8(bs, 8, utf8 + i)) { *pNumberOut = 0; - return DRFLAC_END_OF_STREAM; + return DRFLAC_AT_END; } crc = drflac_crc8(crc, utf8[i], 8); @@ -3351,7 +3770,7 @@ static drflac_bool32 drflac__decode_samples_with_residual__rice__sse41_32(drflac /* Pre-loading the coefficients and prior samples is annoying because we need to ensure we don't try reading more than - what's available in the input buffers. It would be conenient to use a fall-through switch to do this, but this results + what's available in the input buffers. It would be convenient to use a fall-through switch to do this, but this results in strict aliasing warnings with GCC. To work around this I'm just doing something hacky. This feels a bit convoluted so I think there's opportunity for this to be simplified. */ @@ -3734,6 +4153,12 @@ static DRFLAC_INLINE void drflac__vst2q_s32(drflac_int32* p, int32x4x2_t x) vst1q_s32(p+4, x.val[1]); } +static DRFLAC_INLINE void drflac__vst2q_u32(drflac_uint32* p, uint32x4x2_t x) +{ + vst1q_u32(p+0, x.val[0]); + vst1q_u32(p+4, x.val[1]); +} + static DRFLAC_INLINE void drflac__vst2q_f32(float* p, float32x4x2_t x) { vst1q_f32(p+0, x.val[0]); @@ -3745,6 +4170,11 @@ static DRFLAC_INLINE void drflac__vst2q_s16(drflac_int16* p, int16x4x2_t x) vst1q_s16(p, vcombine_s16(x.val[0], x.val[1])); } +static DRFLAC_INLINE void drflac__vst2q_u16(drflac_uint16* p, uint16x4x2_t x) +{ + vst1q_u16(p, vcombine_u16(x.val[0], x.val[1])); +} + static DRFLAC_INLINE int32x4_t drflac__vdupq_n_s32x4(drflac_int32 x3, drflac_int32 x2, drflac_int32 x1, drflac_int32 x0) { drflac_int32 x[4]; @@ -4351,7 +4781,7 @@ static drflac_bool32 drflac__decode_samples_with_residual(drflac_bs* bs, drflac_ return DRFLAC_FALSE; } } else { - unsigned char unencodedBitsPerSample = 0; + drflac_uint8 unencodedBitsPerSample = 0; if (!drflac__read_uint8(bs, 5, &unencodedBitsPerSample)) { return DRFLAC_FALSE; } @@ -4443,7 +4873,7 @@ static drflac_bool32 drflac__read_and_seek_residual(drflac_bs* bs, drflac_uint32 return DRFLAC_FALSE; } } else { - unsigned char unencodedBitsPerSample = 0; + drflac_uint8 unencodedBitsPerSample = 0; if (!drflac__read_uint8(bs, 5, &unencodedBitsPerSample)) { return DRFLAC_FALSE; } @@ -4656,7 +5086,7 @@ static drflac_bool32 drflac__read_next_flac_frame_header(drflac_bs* bs, drflac_u drflac_uint64 pcmFrameNumber; drflac_result result = drflac__read_utf8_coded_number(bs, &pcmFrameNumber, &crc8); if (result != DRFLAC_SUCCESS) { - if (result == DRFLAC_END_OF_STREAM) { + if (result == DRFLAC_AT_END) { return DRFLAC_FALSE; } else { continue; @@ -4668,7 +5098,7 @@ static drflac_bool32 drflac__read_next_flac_frame_header(drflac_bs* bs, drflac_u drflac_uint64 flacFrameNumber = 0; drflac_result result = drflac__read_utf8_coded_number(bs, &flacFrameNumber, &crc8); if (result != DRFLAC_SUCCESS) { - if (result == DRFLAC_END_OF_STREAM) { + if (result == DRFLAC_AT_END) { return DRFLAC_FALSE; } else { continue; @@ -4768,10 +5198,10 @@ static drflac_bool32 drflac__read_subframe_header(drflac_bs* bs, drflac_subframe } else { if ((type & 0x20) != 0) { pSubframe->subframeType = DRFLAC_SUBFRAME_LPC; - pSubframe->lpcOrder = (type & 0x1F) + 1; + pSubframe->lpcOrder = (drflac_uint8)(type & 0x1F) + 1; } else if ((type & 0x08) != 0) { pSubframe->subframeType = DRFLAC_SUBFRAME_FIXED; - pSubframe->lpcOrder = (type & 0x07); + pSubframe->lpcOrder = (drflac_uint8)(type & 0x07); if (pSubframe->lpcOrder > 4) { pSubframe->subframeType = DRFLAC_SUBFRAME_RESERVED; pSubframe->lpcOrder = 0; @@ -4792,7 +5222,7 @@ static drflac_bool32 drflac__read_subframe_header(drflac_bs* bs, drflac_subframe if (!drflac__seek_past_next_set_bit(bs, &wastedBitsPerSample)) { return DRFLAC_FALSE; } - pSubframe->wastedBitsPerSample = (unsigned char)wastedBitsPerSample + 1; + pSubframe->wastedBitsPerSample = (drflac_uint8)wastedBitsPerSample + 1; } return DRFLAC_TRUE; @@ -4915,7 +5345,7 @@ static drflac_bool32 drflac__seek_subframe(drflac_bs* bs, drflac_frame* frame, i case DRFLAC_SUBFRAME_LPC: { - unsigned char lpcPrecision; + drflac_uint8 lpcPrecision; unsigned int bitsToSeek = pSubframe->lpcOrder * subframeBitsPerSample; if (!drflac__seek_bits(bs, bitsToSeek)) { @@ -4986,11 +5416,11 @@ static drflac_result drflac__decode_flac_frame(drflac* pFlac) } } - paddingSizeInBits = DRFLAC_CACHE_L1_BITS_REMAINING(&pFlac->bs) & 7; + paddingSizeInBits = (drflac_uint8)(DRFLAC_CACHE_L1_BITS_REMAINING(&pFlac->bs) & 7); if (paddingSizeInBits > 0) { drflac_uint8 padding = 0; if (!drflac__read_uint8(&pFlac->bs, paddingSizeInBits, &padding)) { - return DRFLAC_END_OF_STREAM; + return DRFLAC_AT_END; } } @@ -4998,7 +5428,7 @@ static drflac_result drflac__decode_flac_frame(drflac* pFlac) actualCRC16 = drflac__flush_crc16(&pFlac->bs); #endif if (!drflac__read_uint16(&pFlac->bs, 16, &desiredCRC16)) { - return DRFLAC_END_OF_STREAM; + return DRFLAC_AT_END; } #ifndef DR_FLAC_NO_CRC @@ -5038,7 +5468,7 @@ static drflac_result drflac__seek_flac_frame(drflac* pFlac) actualCRC16 = drflac__flush_crc16(&pFlac->bs); #endif if (!drflac__read_uint16(&pFlac->bs, 16, &desiredCRC16)) { - return DRFLAC_END_OF_STREAM; + return DRFLAC_AT_END; } #ifndef DR_FLAC_NO_CRC @@ -5121,7 +5551,7 @@ static DRFLAC_INLINE drflac_result drflac__seek_to_next_flac_frame(drflac* pFlac } -drflac_uint64 drflac__seek_forward_by_pcm_frames(drflac* pFlac, drflac_uint64 pcmFramesToSeek) +static drflac_uint64 drflac__seek_forward_by_pcm_frames(drflac* pFlac, drflac_uint64 pcmFramesToSeek) { drflac_uint64 pcmFramesRead = 0; while (pcmFramesToSeek > 0) { @@ -5296,7 +5726,7 @@ static drflac_bool32 drflac__seek_to_approximate_flac_frame_to_byte(drflac* pFla /* Now seek to the next FLAC frame. We need to decode the entire frame (not just the header) because it's possible for the header to incorrectly pass the CRC check and return bad data. We need to decode the entire frame to be more certain. Although this seems unlikely, this has happened to me in testing - to it needs to stay this way for now. + so it needs to stay this way for now. */ #if 1 if (!drflac__read_and_decode_next_flac_frame(pFlac)) { @@ -5354,7 +5784,7 @@ static drflac_bool32 drflac__seek_to_pcm_frame__binary_search_internal(drflac* p drflac_uint64 closestSeekOffsetBeforeTargetPCMFrame = byteRangeLo; drflac_uint32 seekForwardThreshold = (pFlac->maxBlockSizeInPCMFrames != 0) ? pFlac->maxBlockSizeInPCMFrames*2 : 4096; - targetByte = byteRangeLo + (drflac_uint64)(((pcmFrameIndex - pFlac->currentPCMFrame) * pFlac->channels * pFlac->bitsPerSample/8.0f) * DRFLAC_BINARY_SEARCH_APPROX_COMPRESSION_RATIO); + targetByte = byteRangeLo + (drflac_uint64)(((drflac_int64)((pcmFrameIndex - pFlac->currentPCMFrame) * pFlac->channels * pFlac->bitsPerSample)/8.0f) * DRFLAC_BINARY_SEARCH_APPROX_COMPRESSION_RATIO); if (targetByte > byteRangeHi) { targetByte = byteRangeHi; } @@ -5390,7 +5820,7 @@ static drflac_bool32 drflac__seek_to_pcm_frame__binary_search_internal(drflac* p break; /* Failed to seek to FLAC frame. */ } } else { - const float approxCompressionRatio = (lastSuccessfulSeekOffset - pFlac->firstFLACFramePosInBytes) / (pcmRangeLo * pFlac->channels * pFlac->bitsPerSample/8.0f); + const float approxCompressionRatio = (drflac_int64)(lastSuccessfulSeekOffset - pFlac->firstFLACFramePosInBytes) / ((drflac_int64)(pcmRangeLo * pFlac->channels * pFlac->bitsPerSample)/8.0f); if (pcmRangeLo > pcmFrameIndex) { /* We seeked too far forward. We need to move our target byte backward and try again. */ @@ -5419,7 +5849,7 @@ static drflac_bool32 drflac__seek_to_pcm_frame__binary_search_internal(drflac* p byteRangeHi = byteRangeLo; } - targetByte = lastSuccessfulSeekOffset + (drflac_uint64)(((pcmFrameIndex-pcmRangeLo) * pFlac->channels * pFlac->bitsPerSample/8.0f) * approxCompressionRatio); + targetByte = lastSuccessfulSeekOffset + (drflac_uint64)(((drflac_int64)((pcmFrameIndex-pcmRangeLo) * pFlac->channels * pFlac->bitsPerSample)/8.0f) * approxCompressionRatio); if (targetByte > byteRangeHi) { targetByte = byteRangeHi; } @@ -5446,7 +5876,7 @@ static drflac_bool32 drflac__seek_to_pcm_frame__binary_search(drflac* pFlac, drf drflac_uint64 byteRangeHi; drflac_uint32 seekForwardThreshold = (pFlac->maxBlockSizeInPCMFrames != 0) ? pFlac->maxBlockSizeInPCMFrames*2 : 4096; - /* Our algorithm currently assumes the PCM frame */ + /* Our algorithm currently assumes the FLAC stream is currently sitting at the start. */ if (drflac__seek_to_first_frame(pFlac) == DRFLAC_FALSE) { return DRFLAC_FALSE; } @@ -5461,7 +5891,7 @@ static drflac_bool32 drflac__seek_to_pcm_frame__binary_search(drflac* pFlac, drf the entire file is included, even though most of the time it'll exceed the end of the actual stream. This is OK as the frame searching logic will handle it. */ byteRangeLo = pFlac->firstFLACFramePosInBytes; - byteRangeHi = pFlac->firstFLACFramePosInBytes + (drflac_uint64)(pFlac->totalPCMFrameCount * pFlac->channels * pFlac->bitsPerSample/8.0f); + byteRangeHi = pFlac->firstFLACFramePosInBytes + (drflac_uint64)((drflac_int64)(pFlac->totalPCMFrameCount * pFlac->channels * pFlac->bitsPerSample)/8.0f); return drflac__seek_to_pcm_frame__binary_search_internal(pFlac, pcmFrameIndex, byteRangeLo, byteRangeHi); } @@ -5489,18 +5919,40 @@ static drflac_bool32 drflac__seek_to_pcm_frame__seek_table(drflac* pFlac, drflac iClosestSeekpoint = iSeekpoint; } + /* There's been cases where the seek table contains only zeros. We need to do some basic validation on the closest seekpoint. */ + if (pFlac->pSeekpoints[iClosestSeekpoint].pcmFrameCount == 0 || pFlac->pSeekpoints[iClosestSeekpoint].pcmFrameCount > pFlac->maxBlockSizeInPCMFrames) { + return DRFLAC_FALSE; + } + if (pFlac->pSeekpoints[iClosestSeekpoint].firstPCMFrame > pFlac->totalPCMFrameCount && pFlac->totalPCMFrameCount > 0) { + return DRFLAC_FALSE; + } + #if !defined(DR_FLAC_NO_CRC) /* At this point we should know the closest seek point. We can use a binary search for this. We need to know the total sample count for this. */ if (pFlac->totalPCMFrameCount > 0) { drflac_uint64 byteRangeLo; drflac_uint64 byteRangeHi; - byteRangeHi = pFlac->firstFLACFramePosInBytes + (drflac_uint64)(pFlac->totalPCMFrameCount * pFlac->channels * pFlac->bitsPerSample/8.0f); + byteRangeHi = pFlac->firstFLACFramePosInBytes + (drflac_uint64)((drflac_int64)(pFlac->totalPCMFrameCount * pFlac->channels * pFlac->bitsPerSample)/8.0f); byteRangeLo = pFlac->firstFLACFramePosInBytes + pFlac->pSeekpoints[iClosestSeekpoint].flacFrameOffset; + /* + If our closest seek point is not the last one, we only need to search between it and the next one. The section below calculates an appropriate starting + value for byteRangeHi which will clamp it appropriately. + + Note that the next seekpoint must have an offset greater than the closest seekpoint because otherwise our binary search algorithm will break down. There + have been cases where a seektable consists of seek points where every byte offset is set to 0 which causes problems. If this happens we need to abort. + */ if (iClosestSeekpoint < pFlac->seekpointCount-1) { - if (pFlac->pSeekpoints[iClosestSeekpoint+1].firstPCMFrame != (((drflac_uint64)0xFFFFFFFF << 32) | 0xFFFFFFFF)) { /* Is it a placeholder seekpoint. */ - byteRangeHi = pFlac->firstFLACFramePosInBytes + pFlac->pSeekpoints[iClosestSeekpoint+1].flacFrameOffset-1; /* Must be zero based. */ + drflac_uint32 iNextSeekpoint = iClosestSeekpoint + 1; + + /* Basic validation on the seekpoints to ensure they're usable. */ + if (pFlac->pSeekpoints[iClosestSeekpoint].flacFrameOffset >= pFlac->pSeekpoints[iNextSeekpoint].flacFrameOffset || pFlac->pSeekpoints[iNextSeekpoint].pcmFrameCount == 0) { + return DRFLAC_FALSE; /* The next seekpoint doesn't look right. The seek table cannot be trusted from here. Abort. */ + } + + if (pFlac->pSeekpoints[iNextSeekpoint].firstPCMFrame != (((drflac_uint64)0xFFFFFFFF << 32) | 0xFFFFFFFF)) { /* Make sure it's not a placeholder seekpoint. */ + byteRangeHi = pFlac->firstFLACFramePosInBytes + pFlac->pSeekpoints[iNextSeekpoint].flacFrameOffset - 1; /* byteRangeHi must be zero based. */ } } @@ -5664,9 +6116,9 @@ typedef struct static DRFLAC_INLINE void drflac__decode_block_header(drflac_uint32 blockHeader, drflac_uint8* isLastBlock, drflac_uint8* blockType, drflac_uint32* blockSize) { blockHeader = drflac__be2host_32(blockHeader); - *isLastBlock = (blockHeader & 0x80000000UL) >> 31; - *blockType = (blockHeader & 0x7F000000UL) >> 24; - *blockSize = (blockHeader & 0x00FFFFFFUL); + *isLastBlock = (drflac_uint8)((blockHeader & 0x80000000UL) >> 31); + *blockType = (drflac_uint8)((blockHeader & 0x7F000000UL) >> 24); + *blockSize = (blockHeader & 0x00FFFFFFUL); } static DRFLAC_INLINE drflac_bool32 drflac__read_and_decode_block_header(drflac_read_proc onRead, void* pUserData, drflac_uint8* isLastBlock, drflac_uint8* blockType, drflac_uint32* blockSize) @@ -5682,7 +6134,7 @@ static DRFLAC_INLINE drflac_bool32 drflac__read_and_decode_block_header(drflac_r return DRFLAC_TRUE; } -drflac_bool32 drflac__read_streaminfo(drflac_read_proc onRead, void* pUserData, drflac_streaminfo* pStreamInfo) +static drflac_bool32 drflac__read_streaminfo(drflac_read_proc onRead, void* pUserData, drflac_streaminfo* pStreamInfo) { drflac_uint32 blockSizes; drflac_uint64 frameSizes = 0; @@ -5713,8 +6165,8 @@ drflac_bool32 drflac__read_streaminfo(drflac_read_proc onRead, void* pUserData, frameSizes = drflac__be2host_64(frameSizes); importantProps = drflac__be2host_64(importantProps); - pStreamInfo->minBlockSizeInPCMFrames = (blockSizes & 0xFFFF0000) >> 16; - pStreamInfo->maxBlockSizeInPCMFrames = (blockSizes & 0x0000FFFF); + pStreamInfo->minBlockSizeInPCMFrames = (drflac_uint16)((blockSizes & 0xFFFF0000) >> 16); + pStreamInfo->maxBlockSizeInPCMFrames = (drflac_uint16) (blockSizes & 0x0000FFFF); pStreamInfo->minFrameSizeInPCMFrames = (drflac_uint32)((frameSizes & (((drflac_uint64)0x00FFFFFF << 16) << 24)) >> 40); pStreamInfo->maxFrameSizeInPCMFrames = (drflac_uint32)((frameSizes & (((drflac_uint64)0x00FFFFFF << 16) << 0)) >> 16); pStreamInfo->sampleRate = (drflac_uint32)((importantProps & (((drflac_uint64)0x000FFFFF << 16) << 28)) >> 44); @@ -5806,7 +6258,7 @@ static void drflac__free_from_callbacks(void* p, const drflac_allocation_callbac } -drflac_bool32 drflac__read_and_decode_metadata(drflac_read_proc onRead, drflac_seek_proc onSeek, drflac_meta_proc onMeta, void* pUserData, void* pUserDataMD, drflac_uint64* pFirstFramePos, drflac_uint64* pSeektablePos, drflac_uint32* pSeektableSize, drflac_allocation_callbacks* pAllocationCallbacks) +static drflac_bool32 drflac__read_and_decode_metadata(drflac_read_proc onRead, drflac_seek_proc onSeek, drflac_meta_proc onMeta, void* pUserData, void* pUserDataMD, drflac_uint64* pFirstFramePos, drflac_uint64* pSeektablePos, drflac_uint32* pSeektableSize, drflac_allocation_callbacks* pAllocationCallbacks) { /* We want to keep track of the byte position in the stream of the seektable. At the time of calling this function we know that @@ -6167,7 +6619,7 @@ drflac_bool32 drflac__read_and_decode_metadata(drflac_read_proc onRead, drflac_s return DRFLAC_TRUE; } -drflac_bool32 drflac__init_private__native(drflac_init_info* pInit, drflac_read_proc onRead, drflac_seek_proc onSeek, drflac_meta_proc onMeta, void* pUserData, void* pUserDataMD, drflac_bool32 relaxed) +static drflac_bool32 drflac__init_private__native(drflac_init_info* pInit, drflac_read_proc onRead, drflac_seek_proc onSeek, drflac_meta_proc onMeta, void* pUserData, void* pUserDataMD, drflac_bool32 relaxed) { /* Pre Condition: The bit stream should be sitting just past the 4-byte id header. */ @@ -6377,7 +6829,7 @@ static DRFLAC_INLINE drflac_uint32 drflac_ogg__get_page_body_size(drflac_ogg_pag return pageBodySize; } -drflac_result drflac_ogg__read_page_header_after_capture_pattern(drflac_read_proc onRead, void* pUserData, drflac_ogg_page_header* pHeader, drflac_uint32* pBytesRead, drflac_uint32* pCRC32) +static drflac_result drflac_ogg__read_page_header_after_capture_pattern(drflac_read_proc onRead, void* pUserData, drflac_ogg_page_header* pHeader, drflac_uint32* pBytesRead, drflac_uint32* pCRC32) { drflac_uint8 data[23]; drflac_uint32 i; @@ -6385,7 +6837,7 @@ drflac_result drflac_ogg__read_page_header_after_capture_pattern(drflac_read_pro DRFLAC_ASSERT(*pCRC32 == DRFLAC_OGG_CAPTURE_PATTERN_CRC32); if (onRead(pUserData, data, 23) != 23) { - return DRFLAC_END_OF_STREAM; + return DRFLAC_AT_END; } *pBytesRead += 23; @@ -6419,7 +6871,7 @@ drflac_result drflac_ogg__read_page_header_after_capture_pattern(drflac_read_pro if (onRead(pUserData, pHeader->segmentTable, pHeader->segmentCount) != pHeader->segmentCount) { - return DRFLAC_END_OF_STREAM; + return DRFLAC_AT_END; } *pBytesRead += pHeader->segmentCount; @@ -6430,14 +6882,14 @@ drflac_result drflac_ogg__read_page_header_after_capture_pattern(drflac_read_pro return DRFLAC_SUCCESS; } -drflac_result drflac_ogg__read_page_header(drflac_read_proc onRead, void* pUserData, drflac_ogg_page_header* pHeader, drflac_uint32* pBytesRead, drflac_uint32* pCRC32) +static drflac_result drflac_ogg__read_page_header(drflac_read_proc onRead, void* pUserData, drflac_ogg_page_header* pHeader, drflac_uint32* pBytesRead, drflac_uint32* pCRC32) { drflac_uint8 id[4]; *pBytesRead = 0; if (onRead(pUserData, id, 4) != 4) { - return DRFLAC_END_OF_STREAM; + return DRFLAC_AT_END; } *pBytesRead += 4; @@ -6464,7 +6916,7 @@ drflac_result drflac_ogg__read_page_header(drflac_read_proc onRead, void* pUserD id[1] = id[2]; id[2] = id[3]; if (onRead(pUserData, &id[3], 1) != 1) { - return DRFLAC_END_OF_STREAM; + return DRFLAC_AT_END; } *pBytesRead += 1; } @@ -6767,7 +7219,7 @@ static drflac_bool32 drflac__on_seek_ogg(void* pUserData, int offset, drflac_see } -drflac_bool32 drflac_ogg__seek_to_pcm_frame(drflac* pFlac, drflac_uint64 pcmFrameIndex) +static drflac_bool32 drflac_ogg__seek_to_pcm_frame(drflac* pFlac, drflac_uint64 pcmFrameIndex) { drflac_oggbs* oggbs = (drflac_oggbs*)pFlac->_oggbs; drflac_uint64 originalBytePos; @@ -6924,7 +7376,7 @@ drflac_bool32 drflac_ogg__seek_to_pcm_frame(drflac* pFlac, drflac_uint64 pcmFram -drflac_bool32 drflac__init_private__ogg(drflac_init_info* pInit, drflac_read_proc onRead, drflac_seek_proc onSeek, drflac_meta_proc onMeta, void* pUserData, void* pUserDataMD, drflac_bool32 relaxed) +static drflac_bool32 drflac__init_private__ogg(drflac_init_info* pInit, drflac_read_proc onRead, drflac_seek_proc onSeek, drflac_meta_proc onMeta, void* pUserData, void* pUserDataMD, drflac_bool32 relaxed) { drflac_ogg_page_header header; drflac_uint32 crc32 = DRFLAC_OGG_CAPTURE_PATTERN_CRC32; @@ -7082,7 +7534,7 @@ drflac_bool32 drflac__init_private__ogg(drflac_init_info* pInit, drflac_read_pro } #endif -drflac_bool32 drflac__init_private(drflac_init_info* pInit, drflac_read_proc onRead, drflac_seek_proc onSeek, drflac_meta_proc onMeta, drflac_container container, void* pUserData, void* pUserDataMD) +static drflac_bool32 drflac__init_private(drflac_init_info* pInit, drflac_read_proc onRead, drflac_seek_proc onSeek, drflac_meta_proc onMeta, drflac_container container, void* pUserData, void* pUserDataMD) { drflac_bool32 relaxed; drflac_uint8 id[4]; @@ -7167,7 +7619,7 @@ drflac_bool32 drflac__init_private(drflac_init_info* pInit, drflac_read_proc onR return DRFLAC_FALSE; } -void drflac__init_from_info(drflac* pFlac, const drflac_init_info* pInit) +static void drflac__init_from_info(drflac* pFlac, const drflac_init_info* pInit) { DRFLAC_ASSERT(pFlac != NULL); DRFLAC_ASSERT(pInit != NULL); @@ -7185,7 +7637,7 @@ void drflac__init_from_info(drflac* pFlac, const drflac_init_info* pInit) } -drflac* drflac_open_with_metadata_private(drflac_read_proc onRead, drflac_seek_proc onSeek, drflac_meta_proc onMeta, drflac_container container, void* pUserData, void* pUserDataMD, const drflac_allocation_callbacks* pAllocationCallbacks) +static drflac* drflac_open_with_metadata_private(drflac_read_proc onRead, drflac_seek_proc onSeek, drflac_meta_proc onMeta, drflac_container container, void* pUserData, void* pUserDataMD, const drflac_allocation_callbacks* pAllocationCallbacks) { drflac_init_info init; drflac_uint32 allocationSize; @@ -7372,8 +7824,7 @@ drflac* drflac_open_with_metadata_private(drflac_read_proc onRead, drflac_seek_p */ if (!init.hasStreamInfoBlock) { pFlac->currentFLACFrame.header = init.firstFrameHeader; - do - { + for (;;) { drflac_result result = drflac__decode_flac_frame(pFlac); if (result == DRFLAC_SUCCESS) { break; @@ -7389,7 +7840,7 @@ drflac* drflac_open_with_metadata_private(drflac_read_proc onRead, drflac_seek_p return NULL; } } - } while (1); + } } return pFlac; @@ -7399,6 +7850,552 @@ drflac* drflac_open_with_metadata_private(drflac_read_proc onRead, drflac_seek_p #ifndef DR_FLAC_NO_STDIO #include <stdio.h> +#include <wchar.h> /* For wcslen(), wcsrtombs() */ + +/* drflac_result_from_errno() is only used for fopen() and wfopen() so putting it inside DR_WAV_NO_STDIO for now. If something else needs this later we can move it out. */ +#include <errno.h> +static drflac_result drflac_result_from_errno(int e) +{ + switch (e) + { + case 0: return DRFLAC_SUCCESS; + #ifdef EPERM + case EPERM: return DRFLAC_INVALID_OPERATION; + #endif + #ifdef ENOENT + case ENOENT: return DRFLAC_DOES_NOT_EXIST; + #endif + #ifdef ESRCH + case ESRCH: return DRFLAC_DOES_NOT_EXIST; + #endif + #ifdef EINTR + case EINTR: return DRFLAC_INTERRUPT; + #endif + #ifdef EIO + case EIO: return DRFLAC_IO_ERROR; + #endif + #ifdef ENXIO + case ENXIO: return DRFLAC_DOES_NOT_EXIST; + #endif + #ifdef E2BIG + case E2BIG: return DRFLAC_INVALID_ARGS; + #endif + #ifdef ENOEXEC + case ENOEXEC: return DRFLAC_INVALID_FILE; + #endif + #ifdef EBADF + case EBADF: return DRFLAC_INVALID_FILE; + #endif + #ifdef ECHILD + case ECHILD: return DRFLAC_ERROR; + #endif + #ifdef EAGAIN + case EAGAIN: return DRFLAC_UNAVAILABLE; + #endif + #ifdef ENOMEM + case ENOMEM: return DRFLAC_OUT_OF_MEMORY; + #endif + #ifdef EACCES + case EACCES: return DRFLAC_ACCESS_DENIED; + #endif + #ifdef EFAULT + case EFAULT: return DRFLAC_BAD_ADDRESS; + #endif + #ifdef ENOTBLK + case ENOTBLK: return DRFLAC_ERROR; + #endif + #ifdef EBUSY + case EBUSY: return DRFLAC_BUSY; + #endif + #ifdef EEXIST + case EEXIST: return DRFLAC_ALREADY_EXISTS; + #endif + #ifdef EXDEV + case EXDEV: return DRFLAC_ERROR; + #endif + #ifdef ENODEV + case ENODEV: return DRFLAC_DOES_NOT_EXIST; + #endif + #ifdef ENOTDIR + case ENOTDIR: return DRFLAC_NOT_DIRECTORY; + #endif + #ifdef EISDIR + case EISDIR: return DRFLAC_IS_DIRECTORY; + #endif + #ifdef EINVAL + case EINVAL: return DRFLAC_INVALID_ARGS; + #endif + #ifdef ENFILE + case ENFILE: return DRFLAC_TOO_MANY_OPEN_FILES; + #endif + #ifdef EMFILE + case EMFILE: return DRFLAC_TOO_MANY_OPEN_FILES; + #endif + #ifdef ENOTTY + case ENOTTY: return DRFLAC_INVALID_OPERATION; + #endif + #ifdef ETXTBSY + case ETXTBSY: return DRFLAC_BUSY; + #endif + #ifdef EFBIG + case EFBIG: return DRFLAC_TOO_BIG; + #endif + #ifdef ENOSPC + case ENOSPC: return DRFLAC_NO_SPACE; + #endif + #ifdef ESPIPE + case ESPIPE: return DRFLAC_BAD_SEEK; + #endif + #ifdef EROFS + case EROFS: return DRFLAC_ACCESS_DENIED; + #endif + #ifdef EMLINK + case EMLINK: return DRFLAC_TOO_MANY_LINKS; + #endif + #ifdef EPIPE + case EPIPE: return DRFLAC_BAD_PIPE; + #endif + #ifdef EDOM + case EDOM: return DRFLAC_OUT_OF_RANGE; + #endif + #ifdef ERANGE + case ERANGE: return DRFLAC_OUT_OF_RANGE; + #endif + #ifdef EDEADLK + case EDEADLK: return DRFLAC_DEADLOCK; + #endif + #ifdef ENAMETOOLONG + case ENAMETOOLONG: return DRFLAC_PATH_TOO_LONG; + #endif + #ifdef ENOLCK + case ENOLCK: return DRFLAC_ERROR; + #endif + #ifdef ENOSYS + case ENOSYS: return DRFLAC_NOT_IMPLEMENTED; + #endif + #ifdef ENOTEMPTY + case ENOTEMPTY: return DRFLAC_DIRECTORY_NOT_EMPTY; + #endif + #ifdef ELOOP + case ELOOP: return DRFLAC_TOO_MANY_LINKS; + #endif + #ifdef ENOMSG + case ENOMSG: return DRFLAC_NO_MESSAGE; + #endif + #ifdef EIDRM + case EIDRM: return DRFLAC_ERROR; + #endif + #ifdef ECHRNG + case ECHRNG: return DRFLAC_ERROR; + #endif + #ifdef EL2NSYNC + case EL2NSYNC: return DRFLAC_ERROR; + #endif + #ifdef EL3HLT + case EL3HLT: return DRFLAC_ERROR; + #endif + #ifdef EL3RST + case EL3RST: return DRFLAC_ERROR; + #endif + #ifdef ELNRNG + case ELNRNG: return DRFLAC_OUT_OF_RANGE; + #endif + #ifdef EUNATCH + case EUNATCH: return DRFLAC_ERROR; + #endif + #ifdef ENOCSI + case ENOCSI: return DRFLAC_ERROR; + #endif + #ifdef EL2HLT + case EL2HLT: return DRFLAC_ERROR; + #endif + #ifdef EBADE + case EBADE: return DRFLAC_ERROR; + #endif + #ifdef EBADR + case EBADR: return DRFLAC_ERROR; + #endif + #ifdef EXFULL + case EXFULL: return DRFLAC_ERROR; + #endif + #ifdef ENOANO + case ENOANO: return DRFLAC_ERROR; + #endif + #ifdef EBADRQC + case EBADRQC: return DRFLAC_ERROR; + #endif + #ifdef EBADSLT + case EBADSLT: return DRFLAC_ERROR; + #endif + #ifdef EBFONT + case EBFONT: return DRFLAC_INVALID_FILE; + #endif + #ifdef ENOSTR + case ENOSTR: return DRFLAC_ERROR; + #endif + #ifdef ENODATA + case ENODATA: return DRFLAC_NO_DATA_AVAILABLE; + #endif + #ifdef ETIME + case ETIME: return DRFLAC_TIMEOUT; + #endif + #ifdef ENOSR + case ENOSR: return DRFLAC_NO_DATA_AVAILABLE; + #endif + #ifdef ENONET + case ENONET: return DRFLAC_NO_NETWORK; + #endif + #ifdef ENOPKG + case ENOPKG: return DRFLAC_ERROR; + #endif + #ifdef EREMOTE + case EREMOTE: return DRFLAC_ERROR; + #endif + #ifdef ENOLINK + case ENOLINK: return DRFLAC_ERROR; + #endif + #ifdef EADV + case EADV: return DRFLAC_ERROR; + #endif + #ifdef ESRMNT + case ESRMNT: return DRFLAC_ERROR; + #endif + #ifdef ECOMM + case ECOMM: return DRFLAC_ERROR; + #endif + #ifdef EPROTO + case EPROTO: return DRFLAC_ERROR; + #endif + #ifdef EMULTIHOP + case EMULTIHOP: return DRFLAC_ERROR; + #endif + #ifdef EDOTDOT + case EDOTDOT: return DRFLAC_ERROR; + #endif + #ifdef EBADMSG + case EBADMSG: return DRFLAC_BAD_MESSAGE; + #endif + #ifdef EOVERFLOW + case EOVERFLOW: return DRFLAC_TOO_BIG; + #endif + #ifdef ENOTUNIQ + case ENOTUNIQ: return DRFLAC_NOT_UNIQUE; + #endif + #ifdef EBADFD + case EBADFD: return DRFLAC_ERROR; + #endif + #ifdef EREMCHG + case EREMCHG: return DRFLAC_ERROR; + #endif + #ifdef ELIBACC + case ELIBACC: return DRFLAC_ACCESS_DENIED; + #endif + #ifdef ELIBBAD + case ELIBBAD: return DRFLAC_INVALID_FILE; + #endif + #ifdef ELIBSCN + case ELIBSCN: return DRFLAC_INVALID_FILE; + #endif + #ifdef ELIBMAX + case ELIBMAX: return DRFLAC_ERROR; + #endif + #ifdef ELIBEXEC + case ELIBEXEC: return DRFLAC_ERROR; + #endif + #ifdef EILSEQ + case EILSEQ: return DRFLAC_INVALID_DATA; + #endif + #ifdef ERESTART + case ERESTART: return DRFLAC_ERROR; + #endif + #ifdef ESTRPIPE + case ESTRPIPE: return DRFLAC_ERROR; + #endif + #ifdef EUSERS + case EUSERS: return DRFLAC_ERROR; + #endif + #ifdef ENOTSOCK + case ENOTSOCK: return DRFLAC_NOT_SOCKET; + #endif + #ifdef EDESTADDRREQ + case EDESTADDRREQ: return DRFLAC_NO_ADDRESS; + #endif + #ifdef EMSGSIZE + case EMSGSIZE: return DRFLAC_TOO_BIG; + #endif + #ifdef EPROTOTYPE + case EPROTOTYPE: return DRFLAC_BAD_PROTOCOL; + #endif + #ifdef ENOPROTOOPT + case ENOPROTOOPT: return DRFLAC_PROTOCOL_UNAVAILABLE; + #endif + #ifdef EPROTONOSUPPORT + case EPROTONOSUPPORT: return DRFLAC_PROTOCOL_NOT_SUPPORTED; + #endif + #ifdef ESOCKTNOSUPPORT + case ESOCKTNOSUPPORT: return DRFLAC_SOCKET_NOT_SUPPORTED; + #endif + #ifdef EOPNOTSUPP + case EOPNOTSUPP: return DRFLAC_INVALID_OPERATION; + #endif + #ifdef EPFNOSUPPORT + case EPFNOSUPPORT: return DRFLAC_PROTOCOL_FAMILY_NOT_SUPPORTED; + #endif + #ifdef EAFNOSUPPORT + case EAFNOSUPPORT: return DRFLAC_ADDRESS_FAMILY_NOT_SUPPORTED; + #endif + #ifdef EADDRINUSE + case EADDRINUSE: return DRFLAC_ALREADY_IN_USE; + #endif + #ifdef EADDRNOTAVAIL + case EADDRNOTAVAIL: return DRFLAC_ERROR; + #endif + #ifdef ENETDOWN + case ENETDOWN: return DRFLAC_NO_NETWORK; + #endif + #ifdef ENETUNREACH + case ENETUNREACH: return DRFLAC_NO_NETWORK; + #endif + #ifdef ENETRESET + case ENETRESET: return DRFLAC_NO_NETWORK; + #endif + #ifdef ECONNABORTED + case ECONNABORTED: return DRFLAC_NO_NETWORK; + #endif + #ifdef ECONNRESET + case ECONNRESET: return DRFLAC_CONNECTION_RESET; + #endif + #ifdef ENOBUFS + case ENOBUFS: return DRFLAC_NO_SPACE; + #endif + #ifdef EISCONN + case EISCONN: return DRFLAC_ALREADY_CONNECTED; + #endif + #ifdef ENOTCONN + case ENOTCONN: return DRFLAC_NOT_CONNECTED; + #endif + #ifdef ESHUTDOWN + case ESHUTDOWN: return DRFLAC_ERROR; + #endif + #ifdef ETOOMANYREFS + case ETOOMANYREFS: return DRFLAC_ERROR; + #endif + #ifdef ETIMEDOUT + case ETIMEDOUT: return DRFLAC_TIMEOUT; + #endif + #ifdef ECONNREFUSED + case ECONNREFUSED: return DRFLAC_CONNECTION_REFUSED; + #endif + #ifdef EHOSTDOWN + case EHOSTDOWN: return DRFLAC_NO_HOST; + #endif + #ifdef EHOSTUNREACH + case EHOSTUNREACH: return DRFLAC_NO_HOST; + #endif + #ifdef EALREADY + case EALREADY: return DRFLAC_IN_PROGRESS; + #endif + #ifdef EINPROGRESS + case EINPROGRESS: return DRFLAC_IN_PROGRESS; + #endif + #ifdef ESTALE + case ESTALE: return DRFLAC_INVALID_FILE; + #endif + #ifdef EUCLEAN + case EUCLEAN: return DRFLAC_ERROR; + #endif + #ifdef ENOTNAM + case ENOTNAM: return DRFLAC_ERROR; + #endif + #ifdef ENAVAIL + case ENAVAIL: return DRFLAC_ERROR; + #endif + #ifdef EISNAM + case EISNAM: return DRFLAC_ERROR; + #endif + #ifdef EREMOTEIO + case EREMOTEIO: return DRFLAC_IO_ERROR; + #endif + #ifdef EDQUOT + case EDQUOT: return DRFLAC_NO_SPACE; + #endif + #ifdef ENOMEDIUM + case ENOMEDIUM: return DRFLAC_DOES_NOT_EXIST; + #endif + #ifdef EMEDIUMTYPE + case EMEDIUMTYPE: return DRFLAC_ERROR; + #endif + #ifdef ECANCELED + case ECANCELED: return DRFLAC_CANCELLED; + #endif + #ifdef ENOKEY + case ENOKEY: return DRFLAC_ERROR; + #endif + #ifdef EKEYEXPIRED + case EKEYEXPIRED: return DRFLAC_ERROR; + #endif + #ifdef EKEYREVOKED + case EKEYREVOKED: return DRFLAC_ERROR; + #endif + #ifdef EKEYREJECTED + case EKEYREJECTED: return DRFLAC_ERROR; + #endif + #ifdef EOWNERDEAD + case EOWNERDEAD: return DRFLAC_ERROR; + #endif + #ifdef ENOTRECOVERABLE + case ENOTRECOVERABLE: return DRFLAC_ERROR; + #endif + #ifdef ERFKILL + case ERFKILL: return DRFLAC_ERROR; + #endif + #ifdef EHWPOISON + case EHWPOISON: return DRFLAC_ERROR; + #endif + default: return DRFLAC_ERROR; + } +} + +static drflac_result drflac_fopen(FILE** ppFile, const char* pFilePath, const char* pOpenMode) +{ +#if _MSC_VER && _MSC_VER >= 1400 + errno_t err; +#endif + + if (ppFile != NULL) { + *ppFile = NULL; /* Safety. */ + } + + if (pFilePath == NULL || pOpenMode == NULL || ppFile == NULL) { + return DRFLAC_INVALID_ARGS; + } + +#if _MSC_VER && _MSC_VER >= 1400 + err = fopen_s(ppFile, pFilePath, pOpenMode); + if (err != 0) { + return drflac_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) { + drflac_result result = drflac_result_from_errno(errno); + if (result == DRFLAC_SUCCESS) { + result = DRFLAC_ERROR; /* Just a safety check to make sure we never ever return success when pFile == NULL. */ + } + + return result; + } +#endif + + return DRFLAC_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__). + +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__) + #define DRFLAC_HAS_WFOPEN + #endif +#endif + +static drflac_result drflac_wfopen(FILE** ppFile, const wchar_t* pFilePath, const wchar_t* pOpenMode, const drflac_allocation_callbacks* pAllocationCallbacks) +{ + if (ppFile != NULL) { + *ppFile = NULL; /* Safety. */ + } + + if (pFilePath == NULL || pOpenMode == NULL || ppFile == NULL) { + return DRFLAC_INVALID_ARGS; + } + +#if defined(DRFLAC_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 drflac_result_from_errno(err); + } + #else + *ppFile = _wfopen(pFilePath, pOpenMode); + if (*ppFile == NULL) { + return drflac_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. + */ + { + mbstate_t mbs; + size_t lenMB; + const wchar_t* pFilePathTemp = pFilePath; + char* pFilePathMB = NULL; + char pOpenModeMB[32] = {0}; + + /* Get the length first. */ + DRFLAC_ZERO_OBJECT(&mbs); + lenMB = wcsrtombs(NULL, &pFilePathTemp, 0, &mbs); + if (lenMB == (size_t)-1) { + return drflac_result_from_errno(errno); + } + + pFilePathMB = (char*)drflac__malloc_from_callbacks(lenMB + 1, pAllocationCallbacks); + if (pFilePathMB == NULL) { + return DRFLAC_OUT_OF_MEMORY; + } + + pFilePathTemp = pFilePath; + DRFLAC_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); + + drflac__free_from_callbacks(pFilePathMB, pAllocationCallbacks); + } + + if (*ppFile == NULL) { + return DRFLAC_ERROR; + } +#endif + + return DRFLAC_SUCCESS; +} static size_t drflac__on_read_stdio(void* pUserData, void* bufferOut, size_t bytesToRead) { @@ -7412,31 +8409,31 @@ static drflac_bool32 drflac__on_seek_stdio(void* pUserData, int offset, drflac_s return fseek((FILE*)pUserData, offset, (origin == drflac_seek_origin_current) ? SEEK_CUR : SEEK_SET) == 0; } -static FILE* drflac__fopen(const char* filename) + +DRFLAC_API drflac* drflac_open_file(const char* pFileName, const drflac_allocation_callbacks* pAllocationCallbacks) { + drflac* pFlac; FILE* pFile; -#if defined(_MSC_VER) && _MSC_VER >= 1400 - if (fopen_s(&pFile, filename, "rb") != 0) { + + if (drflac_fopen(&pFile, pFileName, "rb") != DRFLAC_SUCCESS) { return NULL; } -#else - pFile = fopen(filename, "rb"); - if (pFile == NULL) { + + pFlac = drflac_open(drflac__on_read_stdio, drflac__on_seek_stdio, (void*)pFile, pAllocationCallbacks); + if (pFlac == NULL) { + fclose(pFile); return NULL; } -#endif - return pFile; + return pFlac; } - -drflac* drflac_open_file(const char* filename, const drflac_allocation_callbacks* pAllocationCallbacks) +DRFLAC_API drflac* drflac_open_file_w(const wchar_t* pFileName, const drflac_allocation_callbacks* pAllocationCallbacks) { drflac* pFlac; FILE* pFile; - pFile = drflac__fopen(filename); - if (pFile == NULL) { + if (drflac_wfopen(&pFile, pFileName, L"rb", pAllocationCallbacks) != DRFLAC_SUCCESS) { return NULL; } @@ -7449,13 +8446,30 @@ drflac* drflac_open_file(const char* filename, const drflac_allocation_callbacks return pFlac; } -drflac* drflac_open_file_with_metadata(const char* filename, drflac_meta_proc onMeta, void* pUserData, const drflac_allocation_callbacks* pAllocationCallbacks) +DRFLAC_API drflac* drflac_open_file_with_metadata(const char* pFileName, drflac_meta_proc onMeta, void* pUserData, const drflac_allocation_callbacks* pAllocationCallbacks) +{ + drflac* pFlac; + FILE* pFile; + + if (drflac_fopen(&pFile, pFileName, "rb") != DRFLAC_SUCCESS) { + return NULL; + } + + pFlac = drflac_open_with_metadata_private(drflac__on_read_stdio, drflac__on_seek_stdio, onMeta, drflac_container_unknown, (void*)pFile, pUserData, pAllocationCallbacks); + if (pFlac == NULL) { + fclose(pFile); + return pFlac; + } + + return pFlac; +} + +DRFLAC_API drflac* drflac_open_file_with_metadata_w(const wchar_t* pFileName, drflac_meta_proc onMeta, void* pUserData, const drflac_allocation_callbacks* pAllocationCallbacks) { drflac* pFlac; FILE* pFile; - pFile = drflac__fopen(filename); - if (pFile == NULL) { + if (drflac_wfopen(&pFile, pFileName, L"rb", pAllocationCallbacks) != DRFLAC_SUCCESS) { return NULL; } @@ -7518,12 +8532,12 @@ static drflac_bool32 drflac__on_seek_memory(void* pUserData, int offset, drflac_ return DRFLAC_TRUE; } -drflac* drflac_open_memory(const void* data, size_t dataSize, const drflac_allocation_callbacks* pAllocationCallbacks) +DRFLAC_API drflac* drflac_open_memory(const void* pData, size_t dataSize, const drflac_allocation_callbacks* pAllocationCallbacks) { drflac__memory_stream memoryStream; drflac* pFlac; - memoryStream.data = (const unsigned char*)data; + memoryStream.data = (const drflac_uint8*)pData; memoryStream.dataSize = dataSize; memoryStream.currentReadPos = 0; pFlac = drflac_open(drflac__on_read_memory, drflac__on_seek_memory, &memoryStream, pAllocationCallbacks); @@ -7549,12 +8563,12 @@ drflac* drflac_open_memory(const void* data, size_t dataSize, const drflac_alloc return pFlac; } -drflac* drflac_open_memory_with_metadata(const void* data, size_t dataSize, drflac_meta_proc onMeta, void* pUserData, const drflac_allocation_callbacks* pAllocationCallbacks) +DRFLAC_API drflac* drflac_open_memory_with_metadata(const void* pData, size_t dataSize, drflac_meta_proc onMeta, void* pUserData, const drflac_allocation_callbacks* pAllocationCallbacks) { drflac__memory_stream memoryStream; drflac* pFlac; - memoryStream.data = (const unsigned char*)data; + memoryStream.data = (const drflac_uint8*)pData; memoryStream.dataSize = dataSize; memoryStream.currentReadPos = 0; pFlac = drflac_open_with_metadata_private(drflac__on_read_memory, drflac__on_seek_memory, onMeta, drflac_container_unknown, &memoryStream, pUserData, pAllocationCallbacks); @@ -7582,25 +8596,25 @@ drflac* drflac_open_memory_with_metadata(const void* data, size_t dataSize, drfl -drflac* drflac_open(drflac_read_proc onRead, drflac_seek_proc onSeek, void* pUserData, const drflac_allocation_callbacks* pAllocationCallbacks) +DRFLAC_API drflac* drflac_open(drflac_read_proc onRead, drflac_seek_proc onSeek, void* pUserData, const drflac_allocation_callbacks* pAllocationCallbacks) { return drflac_open_with_metadata_private(onRead, onSeek, NULL, drflac_container_unknown, pUserData, pUserData, pAllocationCallbacks); } -drflac* drflac_open_relaxed(drflac_read_proc onRead, drflac_seek_proc onSeek, drflac_container container, void* pUserData, const drflac_allocation_callbacks* pAllocationCallbacks) +DRFLAC_API drflac* drflac_open_relaxed(drflac_read_proc onRead, drflac_seek_proc onSeek, drflac_container container, void* pUserData, const drflac_allocation_callbacks* pAllocationCallbacks) { return drflac_open_with_metadata_private(onRead, onSeek, NULL, container, pUserData, pUserData, pAllocationCallbacks); } -drflac* drflac_open_with_metadata(drflac_read_proc onRead, drflac_seek_proc onSeek, drflac_meta_proc onMeta, void* pUserData, const drflac_allocation_callbacks* pAllocationCallbacks) +DRFLAC_API drflac* drflac_open_with_metadata(drflac_read_proc onRead, drflac_seek_proc onSeek, drflac_meta_proc onMeta, void* pUserData, const drflac_allocation_callbacks* pAllocationCallbacks) { return drflac_open_with_metadata_private(onRead, onSeek, onMeta, drflac_container_unknown, pUserData, pUserData, pAllocationCallbacks); } -drflac* drflac_open_with_metadata_relaxed(drflac_read_proc onRead, drflac_seek_proc onSeek, drflac_meta_proc onMeta, drflac_container container, void* pUserData, const drflac_allocation_callbacks* pAllocationCallbacks) +DRFLAC_API drflac* drflac_open_with_metadata_relaxed(drflac_read_proc onRead, drflac_seek_proc onSeek, drflac_meta_proc onMeta, drflac_container container, void* pUserData, const drflac_allocation_callbacks* pAllocationCallbacks) { return drflac_open_with_metadata_private(onRead, onSeek, onMeta, container, pUserData, pUserData, pAllocationCallbacks); } -void drflac_close(drflac* pFlac) +DRFLAC_API void drflac_close(drflac* pFlac) { if (pFlac == NULL) { return; @@ -7633,78 +8647,77 @@ void drflac_close(drflac* pFlac) #if 0 -static DRFLAC_INLINE void drflac_read_pcm_frames_s32__decode_left_side__reference(drflac* pFlac, drflac_uint64 frameCount, drflac_int32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int32* pOutputSamples) +static DRFLAC_INLINE void drflac_read_pcm_frames_s32__decode_left_side__reference(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int32* pOutputSamples) { drflac_uint64 i; for (i = 0; i < frameCount; ++i) { - drflac_int32 left = pInputSamples0[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample); - drflac_int32 side = pInputSamples1[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample); - drflac_int32 right = left - side; + drflac_uint32 left = (drflac_uint32)pInputSamples0[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample); + drflac_uint32 side = (drflac_uint32)pInputSamples1[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample); + drflac_uint32 right = left - side; - pOutputSamples[i*2+0] = left; - pOutputSamples[i*2+1] = right; + pOutputSamples[i*2+0] = (drflac_int32)left; + pOutputSamples[i*2+1] = (drflac_int32)right; } } #endif -static DRFLAC_INLINE void drflac_read_pcm_frames_s32__decode_left_side__scalar(drflac* pFlac, drflac_uint64 frameCount, drflac_int32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int32* pOutputSamples) +static DRFLAC_INLINE void drflac_read_pcm_frames_s32__decode_left_side__scalar(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int32* pOutputSamples) { drflac_uint64 i; drflac_uint64 frameCount4 = frameCount >> 2; + const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0; + const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1; + drflac_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + drflac_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; - drflac_int32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - drflac_int32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; for (i = 0; i < frameCount4; ++i) { - drflac_int32 left0 = pInputSamples0[i*4+0] << shift0; - drflac_int32 left1 = pInputSamples0[i*4+1] << shift0; - drflac_int32 left2 = pInputSamples0[i*4+2] << shift0; - drflac_int32 left3 = pInputSamples0[i*4+3] << shift0; - - drflac_int32 side0 = pInputSamples1[i*4+0] << shift1; - drflac_int32 side1 = pInputSamples1[i*4+1] << shift1; - drflac_int32 side2 = pInputSamples1[i*4+2] << shift1; - drflac_int32 side3 = pInputSamples1[i*4+3] << shift1; - - drflac_int32 right0 = left0 - side0; - drflac_int32 right1 = left1 - side1; - drflac_int32 right2 = left2 - side2; - drflac_int32 right3 = left3 - side3; - - pOutputSamples[i*8+0] = left0; - pOutputSamples[i*8+1] = right0; - pOutputSamples[i*8+2] = left1; - pOutputSamples[i*8+3] = right1; - pOutputSamples[i*8+4] = left2; - pOutputSamples[i*8+5] = right2; - pOutputSamples[i*8+6] = left3; - pOutputSamples[i*8+7] = right3; + drflac_uint32 left0 = pInputSamples0U32[i*4+0] << shift0; + drflac_uint32 left1 = pInputSamples0U32[i*4+1] << shift0; + drflac_uint32 left2 = pInputSamples0U32[i*4+2] << shift0; + drflac_uint32 left3 = pInputSamples0U32[i*4+3] << shift0; + + drflac_uint32 side0 = pInputSamples1U32[i*4+0] << shift1; + drflac_uint32 side1 = pInputSamples1U32[i*4+1] << shift1; + drflac_uint32 side2 = pInputSamples1U32[i*4+2] << shift1; + drflac_uint32 side3 = pInputSamples1U32[i*4+3] << shift1; + + drflac_uint32 right0 = left0 - side0; + drflac_uint32 right1 = left1 - side1; + drflac_uint32 right2 = left2 - side2; + drflac_uint32 right3 = left3 - side3; + + pOutputSamples[i*8+0] = (drflac_int32)left0; + pOutputSamples[i*8+1] = (drflac_int32)right0; + pOutputSamples[i*8+2] = (drflac_int32)left1; + pOutputSamples[i*8+3] = (drflac_int32)right1; + pOutputSamples[i*8+4] = (drflac_int32)left2; + pOutputSamples[i*8+5] = (drflac_int32)right2; + pOutputSamples[i*8+6] = (drflac_int32)left3; + pOutputSamples[i*8+7] = (drflac_int32)right3; } for (i = (frameCount4 << 2); i < frameCount; ++i) { - int left = pInputSamples0[i] << shift0; - int side = pInputSamples1[i] << shift1; - int right = left - side; + drflac_uint32 left = pInputSamples0U32[i] << shift0; + drflac_uint32 side = pInputSamples1U32[i] << shift1; + drflac_uint32 right = left - side; - pOutputSamples[i*2+0] = left; - pOutputSamples[i*2+1] = right; + pOutputSamples[i*2+0] = (drflac_int32)left; + pOutputSamples[i*2+1] = (drflac_int32)right; } } #if defined(DRFLAC_SUPPORT_SSE2) -static DRFLAC_INLINE void drflac_read_pcm_frames_s32__decode_left_side__sse2(drflac* pFlac, drflac_uint64 frameCount, drflac_int32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int32* pOutputSamples) +static DRFLAC_INLINE void drflac_read_pcm_frames_s32__decode_left_side__sse2(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int32* pOutputSamples) { - drflac_uint64 frameCount4; - drflac_int32 shift0; - drflac_int32 shift1; drflac_uint64 i; + drflac_uint64 frameCount4 = frameCount >> 2; + const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0; + const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1; + drflac_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + drflac_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; DRFLAC_ASSERT(pFlac->bitsPerSample <= 24); - frameCount4 = frameCount >> 2; - - shift0 = (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample); - shift1 = (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample); - for (i = 0; i < frameCount4; ++i) { __m128i left = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples0 + i), shift0); __m128i side = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples1 + i), shift1); @@ -7715,60 +8728,57 @@ static DRFLAC_INLINE void drflac_read_pcm_frames_s32__decode_left_side__sse2(drf } for (i = (frameCount4 << 2); i < frameCount; ++i) { - drflac_int32 left = pInputSamples0[i] << shift0; - drflac_int32 side = pInputSamples1[i] << shift1; - drflac_int32 right = left - side; + drflac_uint32 left = pInputSamples0U32[i] << shift0; + drflac_uint32 side = pInputSamples1U32[i] << shift1; + drflac_uint32 right = left - side; - pOutputSamples[i*2+0] = left; - pOutputSamples[i*2+1] = right; + pOutputSamples[i*2+0] = (drflac_int32)left; + pOutputSamples[i*2+1] = (drflac_int32)right; } } #endif #if defined(DRFLAC_SUPPORT_NEON) -static DRFLAC_INLINE void drflac_read_pcm_frames_s32__decode_left_side__neon(drflac* pFlac, drflac_uint64 frameCount, drflac_int32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int32* pOutputSamples) +static DRFLAC_INLINE void drflac_read_pcm_frames_s32__decode_left_side__neon(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int32* pOutputSamples) { - drflac_uint64 frameCount4; - drflac_int32 shift0; - drflac_int32 shift1; drflac_uint64 i; + drflac_uint64 frameCount4 = frameCount >> 2; + const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0; + const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1; + drflac_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + drflac_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; int32x4_t shift0_4; int32x4_t shift1_4; DRFLAC_ASSERT(pFlac->bitsPerSample <= 24); - frameCount4 = frameCount >> 2; - - shift0 = (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample); - shift1 = (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample); - shift0_4 = vdupq_n_s32(shift0); shift1_4 = vdupq_n_s32(shift1); for (i = 0; i < frameCount4; ++i) { - int32x4_t left; - int32x4_t side; - int32x4_t right; + uint32x4_t left; + uint32x4_t side; + uint32x4_t right; - left = vshlq_s32(vld1q_s32(pInputSamples0 + i*4), shift0_4); - side = vshlq_s32(vld1q_s32(pInputSamples1 + i*4), shift1_4); - right = vsubq_s32(left, side); + left = vshlq_u32(vld1q_u32(pInputSamples0U32 + i*4), shift0_4); + side = vshlq_u32(vld1q_u32(pInputSamples1U32 + i*4), shift1_4); + right = vsubq_u32(left, side); - drflac__vst2q_s32(pOutputSamples + i*8, vzipq_s32(left, right)); + drflac__vst2q_u32((drflac_uint32*)pOutputSamples + i*8, vzipq_u32(left, right)); } for (i = (frameCount4 << 2); i < frameCount; ++i) { - drflac_int32 left = pInputSamples0[i] << shift0; - drflac_int32 side = pInputSamples1[i] << shift1; - drflac_int32 right = left - side; + drflac_uint32 left = pInputSamples0U32[i] << shift0; + drflac_uint32 side = pInputSamples1U32[i] << shift1; + drflac_uint32 right = left - side; - pOutputSamples[i*2+0] = left; - pOutputSamples[i*2+1] = right; + pOutputSamples[i*2+0] = (drflac_int32)left; + pOutputSamples[i*2+1] = (drflac_int32)right; } } #endif -static DRFLAC_INLINE void drflac_read_pcm_frames_s32__decode_left_side(drflac* pFlac, drflac_uint64 frameCount, drflac_int32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int32* pOutputSamples) +static DRFLAC_INLINE void drflac_read_pcm_frames_s32__decode_left_side(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int32* pOutputSamples) { #if defined(DRFLAC_SUPPORT_SSE2) if (drflac__gIsSSE2Supported && pFlac->bitsPerSample <= 24) { @@ -7791,78 +8801,77 @@ static DRFLAC_INLINE void drflac_read_pcm_frames_s32__decode_left_side(drflac* p #if 0 -static DRFLAC_INLINE void drflac_read_pcm_frames_s32__decode_right_side__reference(drflac* pFlac, drflac_uint64 frameCount, drflac_int32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int32* pOutputSamples) +static DRFLAC_INLINE void drflac_read_pcm_frames_s32__decode_right_side__reference(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int32* pOutputSamples) { drflac_uint64 i; for (i = 0; i < frameCount; ++i) { - drflac_int32 side = pInputSamples0[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample); - drflac_int32 right = pInputSamples1[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample); - drflac_int32 left = right + side; + drflac_uint32 side = (drflac_uint32)pInputSamples0[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample); + drflac_uint32 right = (drflac_uint32)pInputSamples1[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample); + drflac_uint32 left = right + side; - pOutputSamples[i*2+0] = left; - pOutputSamples[i*2+1] = right; + pOutputSamples[i*2+0] = (drflac_int32)left; + pOutputSamples[i*2+1] = (drflac_int32)right; } } #endif -static DRFLAC_INLINE void drflac_read_pcm_frames_s32__decode_right_side__scalar(drflac* pFlac, drflac_uint64 frameCount, drflac_int32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int32* pOutputSamples) +static DRFLAC_INLINE void drflac_read_pcm_frames_s32__decode_right_side__scalar(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int32* pOutputSamples) { drflac_uint64 i; drflac_uint64 frameCount4 = frameCount >> 2; + const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0; + const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1; + drflac_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + drflac_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; - drflac_int32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - drflac_int32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; for (i = 0; i < frameCount4; ++i) { - drflac_int32 side0 = pInputSamples0[i*4+0] << shift0; - drflac_int32 side1 = pInputSamples0[i*4+1] << shift0; - drflac_int32 side2 = pInputSamples0[i*4+2] << shift0; - drflac_int32 side3 = pInputSamples0[i*4+3] << shift0; - - drflac_int32 right0 = pInputSamples1[i*4+0] << shift1; - drflac_int32 right1 = pInputSamples1[i*4+1] << shift1; - drflac_int32 right2 = pInputSamples1[i*4+2] << shift1; - drflac_int32 right3 = pInputSamples1[i*4+3] << shift1; - - drflac_int32 left0 = right0 + side0; - drflac_int32 left1 = right1 + side1; - drflac_int32 left2 = right2 + side2; - drflac_int32 left3 = right3 + side3; - - pOutputSamples[i*8+0] = left0; - pOutputSamples[i*8+1] = right0; - pOutputSamples[i*8+2] = left1; - pOutputSamples[i*8+3] = right1; - pOutputSamples[i*8+4] = left2; - pOutputSamples[i*8+5] = right2; - pOutputSamples[i*8+6] = left3; - pOutputSamples[i*8+7] = right3; + drflac_uint32 side0 = pInputSamples0U32[i*4+0] << shift0; + drflac_uint32 side1 = pInputSamples0U32[i*4+1] << shift0; + drflac_uint32 side2 = pInputSamples0U32[i*4+2] << shift0; + drflac_uint32 side3 = pInputSamples0U32[i*4+3] << shift0; + + drflac_uint32 right0 = pInputSamples1U32[i*4+0] << shift1; + drflac_uint32 right1 = pInputSamples1U32[i*4+1] << shift1; + drflac_uint32 right2 = pInputSamples1U32[i*4+2] << shift1; + drflac_uint32 right3 = pInputSamples1U32[i*4+3] << shift1; + + drflac_uint32 left0 = right0 + side0; + drflac_uint32 left1 = right1 + side1; + drflac_uint32 left2 = right2 + side2; + drflac_uint32 left3 = right3 + side3; + + pOutputSamples[i*8+0] = (drflac_int32)left0; + pOutputSamples[i*8+1] = (drflac_int32)right0; + pOutputSamples[i*8+2] = (drflac_int32)left1; + pOutputSamples[i*8+3] = (drflac_int32)right1; + pOutputSamples[i*8+4] = (drflac_int32)left2; + pOutputSamples[i*8+5] = (drflac_int32)right2; + pOutputSamples[i*8+6] = (drflac_int32)left3; + pOutputSamples[i*8+7] = (drflac_int32)right3; } for (i = (frameCount4 << 2); i < frameCount; ++i) { - drflac_int32 side = pInputSamples0[i] << shift0; - drflac_int32 right = pInputSamples1[i] << shift1; - drflac_int32 left = right + side; + drflac_uint32 side = pInputSamples0U32[i] << shift0; + drflac_uint32 right = pInputSamples1U32[i] << shift1; + drflac_uint32 left = right + side; - pOutputSamples[i*2+0] = left; - pOutputSamples[i*2+1] = right; + pOutputSamples[i*2+0] = (drflac_int32)left; + pOutputSamples[i*2+1] = (drflac_int32)right; } } #if defined(DRFLAC_SUPPORT_SSE2) -static DRFLAC_INLINE void drflac_read_pcm_frames_s32__decode_right_side__sse2(drflac* pFlac, drflac_uint64 frameCount, drflac_int32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int32* pOutputSamples) +static DRFLAC_INLINE void drflac_read_pcm_frames_s32__decode_right_side__sse2(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int32* pOutputSamples) { - drflac_uint64 frameCount4; - drflac_int32 shift0; - drflac_int32 shift1; drflac_uint64 i; + drflac_uint64 frameCount4 = frameCount >> 2; + const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0; + const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1; + drflac_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + drflac_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; DRFLAC_ASSERT(pFlac->bitsPerSample <= 24); - frameCount4 = frameCount >> 2; - - shift0 = (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample); - shift1 = (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample); - for (i = 0; i < frameCount4; ++i) { __m128i side = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples0 + i), shift0); __m128i right = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples1 + i), shift1); @@ -7873,60 +8882,57 @@ static DRFLAC_INLINE void drflac_read_pcm_frames_s32__decode_right_side__sse2(dr } for (i = (frameCount4 << 2); i < frameCount; ++i) { - drflac_int32 side = pInputSamples0[i] << shift0; - drflac_int32 right = pInputSamples1[i] << shift1; - drflac_int32 left = right + side; + drflac_uint32 side = pInputSamples0U32[i] << shift0; + drflac_uint32 right = pInputSamples1U32[i] << shift1; + drflac_uint32 left = right + side; - pOutputSamples[i*2+0] = left; - pOutputSamples[i*2+1] = right; + pOutputSamples[i*2+0] = (drflac_int32)left; + pOutputSamples[i*2+1] = (drflac_int32)right; } } #endif #if defined(DRFLAC_SUPPORT_NEON) -static DRFLAC_INLINE void drflac_read_pcm_frames_s32__decode_right_side__neon(drflac* pFlac, drflac_uint64 frameCount, drflac_int32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int32* pOutputSamples) +static DRFLAC_INLINE void drflac_read_pcm_frames_s32__decode_right_side__neon(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int32* pOutputSamples) { - drflac_uint64 frameCount4; - drflac_int32 shift0; - drflac_int32 shift1; drflac_uint64 i; + drflac_uint64 frameCount4 = frameCount >> 2; + const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0; + const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1; + drflac_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + drflac_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; int32x4_t shift0_4; int32x4_t shift1_4; DRFLAC_ASSERT(pFlac->bitsPerSample <= 24); - frameCount4 = frameCount >> 2; - - shift0 = (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample); - shift1 = (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample); - shift0_4 = vdupq_n_s32(shift0); shift1_4 = vdupq_n_s32(shift1); for (i = 0; i < frameCount4; ++i) { - int32x4_t side; - int32x4_t right; - int32x4_t left; + uint32x4_t side; + uint32x4_t right; + uint32x4_t left; - side = vshlq_s32(vld1q_s32(pInputSamples0 + i*4), shift0_4); - right = vshlq_s32(vld1q_s32(pInputSamples1 + i*4), shift1_4); - left = vaddq_s32(right, side); + side = vshlq_u32(vld1q_u32(pInputSamples0U32 + i*4), shift0_4); + right = vshlq_u32(vld1q_u32(pInputSamples1U32 + i*4), shift1_4); + left = vaddq_u32(right, side); - drflac__vst2q_s32(pOutputSamples + i*8, vzipq_s32(left, right)); + drflac__vst2q_u32((drflac_uint32*)pOutputSamples + i*8, vzipq_u32(left, right)); } for (i = (frameCount4 << 2); i < frameCount; ++i) { - drflac_int32 side = pInputSamples0[i] << shift0; - drflac_int32 right = pInputSamples1[i] << shift1; - drflac_int32 left = right + side; + drflac_uint32 side = pInputSamples0U32[i] << shift0; + drflac_uint32 right = pInputSamples1U32[i] << shift1; + drflac_uint32 left = right + side; - pOutputSamples[i*2+0] = left; - pOutputSamples[i*2+1] = right; + pOutputSamples[i*2+0] = (drflac_int32)left; + pOutputSamples[i*2+1] = (drflac_int32)right; } } #endif -static DRFLAC_INLINE void drflac_read_pcm_frames_s32__decode_right_side(drflac* pFlac, drflac_uint64 frameCount, drflac_int32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int32* pOutputSamples) +static DRFLAC_INLINE void drflac_read_pcm_frames_s32__decode_right_side(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int32* pOutputSamples) { #if defined(DRFLAC_SUPPORT_SSE2) if (drflac__gIsSSE2Supported && pFlac->bitsPerSample <= 24) { @@ -7949,142 +8955,143 @@ static DRFLAC_INLINE void drflac_read_pcm_frames_s32__decode_right_side(drflac* #if 0 -static DRFLAC_INLINE void drflac_read_pcm_frames_s32__decode_mid_side__reference(drflac* pFlac, drflac_uint64 frameCount, drflac_int32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int32* pOutputSamples) +static DRFLAC_INLINE void drflac_read_pcm_frames_s32__decode_mid_side__reference(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int32* pOutputSamples) { for (drflac_uint64 i = 0; i < frameCount; ++i) { - int mid = pInputSamples0[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - int side = pInputSamples1[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + drflac_uint32 mid = pInputSamples0U32[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + drflac_uint32 side = pInputSamples1U32[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; - mid = (((drflac_uint32)mid) << 1) | (side & 0x01); + mid = (mid << 1) | (side & 0x01); - pOutputSamples[i*2+0] = ((mid + side) >> 1) << unusedBitsPerSample; - pOutputSamples[i*2+1] = ((mid - side) >> 1) << unusedBitsPerSample; + pOutputSamples[i*2+0] = (drflac_int32)((drflac_uint32)((drflac_int32)(mid + side) >> 1) << unusedBitsPerSample); + pOutputSamples[i*2+1] = (drflac_int32)((drflac_uint32)((drflac_int32)(mid - side) >> 1) << unusedBitsPerSample); } } #endif -static DRFLAC_INLINE void drflac_read_pcm_frames_s32__decode_mid_side__scalar(drflac* pFlac, drflac_uint64 frameCount, drflac_int32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int32* pOutputSamples) +static DRFLAC_INLINE void drflac_read_pcm_frames_s32__decode_mid_side__scalar(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int32* pOutputSamples) { drflac_uint64 i; drflac_uint64 frameCount4 = frameCount >> 2; - + const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0; + const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1; drflac_int32 shift = unusedBitsPerSample; + if (shift > 0) { shift -= 1; for (i = 0; i < frameCount4; ++i) { - drflac_int32 temp0L; - drflac_int32 temp1L; - drflac_int32 temp2L; - drflac_int32 temp3L; - drflac_int32 temp0R; - drflac_int32 temp1R; - drflac_int32 temp2R; - drflac_int32 temp3R; - - drflac_int32 mid0 = pInputSamples0[i*4+0] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - drflac_int32 mid1 = pInputSamples0[i*4+1] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - drflac_int32 mid2 = pInputSamples0[i*4+2] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - drflac_int32 mid3 = pInputSamples0[i*4+3] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - - drflac_int32 side0 = pInputSamples1[i*4+0] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; - drflac_int32 side1 = pInputSamples1[i*4+1] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; - drflac_int32 side2 = pInputSamples1[i*4+2] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; - drflac_int32 side3 = pInputSamples1[i*4+3] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; - - mid0 = (((drflac_uint32)mid0) << 1) | (side0 & 0x01); - mid1 = (((drflac_uint32)mid1) << 1) | (side1 & 0x01); - mid2 = (((drflac_uint32)mid2) << 1) | (side2 & 0x01); - mid3 = (((drflac_uint32)mid3) << 1) | (side3 & 0x01); - - temp0L = ((mid0 + side0) << shift); - temp1L = ((mid1 + side1) << shift); - temp2L = ((mid2 + side2) << shift); - temp3L = ((mid3 + side3) << shift); - - temp0R = ((mid0 - side0) << shift); - temp1R = ((mid1 - side1) << shift); - temp2R = ((mid2 - side2) << shift); - temp3R = ((mid3 - side3) << shift); - - pOutputSamples[i*8+0] = temp0L; - pOutputSamples[i*8+1] = temp0R; - pOutputSamples[i*8+2] = temp1L; - pOutputSamples[i*8+3] = temp1R; - pOutputSamples[i*8+4] = temp2L; - pOutputSamples[i*8+5] = temp2R; - pOutputSamples[i*8+6] = temp3L; - pOutputSamples[i*8+7] = temp3R; + drflac_uint32 temp0L; + drflac_uint32 temp1L; + drflac_uint32 temp2L; + drflac_uint32 temp3L; + drflac_uint32 temp0R; + drflac_uint32 temp1R; + drflac_uint32 temp2R; + drflac_uint32 temp3R; + + drflac_uint32 mid0 = pInputSamples0U32[i*4+0] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + drflac_uint32 mid1 = pInputSamples0U32[i*4+1] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + drflac_uint32 mid2 = pInputSamples0U32[i*4+2] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + drflac_uint32 mid3 = pInputSamples0U32[i*4+3] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + + drflac_uint32 side0 = pInputSamples1U32[i*4+0] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + drflac_uint32 side1 = pInputSamples1U32[i*4+1] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + drflac_uint32 side2 = pInputSamples1U32[i*4+2] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + drflac_uint32 side3 = pInputSamples1U32[i*4+3] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + + mid0 = (mid0 << 1) | (side0 & 0x01); + mid1 = (mid1 << 1) | (side1 & 0x01); + mid2 = (mid2 << 1) | (side2 & 0x01); + mid3 = (mid3 << 1) | (side3 & 0x01); + + temp0L = (mid0 + side0) << shift; + temp1L = (mid1 + side1) << shift; + temp2L = (mid2 + side2) << shift; + temp3L = (mid3 + side3) << shift; + + temp0R = (mid0 - side0) << shift; + temp1R = (mid1 - side1) << shift; + temp2R = (mid2 - side2) << shift; + temp3R = (mid3 - side3) << shift; + + pOutputSamples[i*8+0] = (drflac_int32)temp0L; + pOutputSamples[i*8+1] = (drflac_int32)temp0R; + pOutputSamples[i*8+2] = (drflac_int32)temp1L; + pOutputSamples[i*8+3] = (drflac_int32)temp1R; + pOutputSamples[i*8+4] = (drflac_int32)temp2L; + pOutputSamples[i*8+5] = (drflac_int32)temp2R; + pOutputSamples[i*8+6] = (drflac_int32)temp3L; + pOutputSamples[i*8+7] = (drflac_int32)temp3R; } } else { for (i = 0; i < frameCount4; ++i) { - drflac_int32 temp0L; - drflac_int32 temp1L; - drflac_int32 temp2L; - drflac_int32 temp3L; - drflac_int32 temp0R; - drflac_int32 temp1R; - drflac_int32 temp2R; - drflac_int32 temp3R; - - drflac_int32 mid0 = pInputSamples0[i*4+0] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - drflac_int32 mid1 = pInputSamples0[i*4+1] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - drflac_int32 mid2 = pInputSamples0[i*4+2] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - drflac_int32 mid3 = pInputSamples0[i*4+3] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - - drflac_int32 side0 = pInputSamples1[i*4+0] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; - drflac_int32 side1 = pInputSamples1[i*4+1] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; - drflac_int32 side2 = pInputSamples1[i*4+2] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; - drflac_int32 side3 = pInputSamples1[i*4+3] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; - - mid0 = (((drflac_uint32)mid0) << 1) | (side0 & 0x01); - mid1 = (((drflac_uint32)mid1) << 1) | (side1 & 0x01); - mid2 = (((drflac_uint32)mid2) << 1) | (side2 & 0x01); - mid3 = (((drflac_uint32)mid3) << 1) | (side3 & 0x01); - - temp0L = ((mid0 + side0) >> 1); - temp1L = ((mid1 + side1) >> 1); - temp2L = ((mid2 + side2) >> 1); - temp3L = ((mid3 + side3) >> 1); - - temp0R = ((mid0 - side0) >> 1); - temp1R = ((mid1 - side1) >> 1); - temp2R = ((mid2 - side2) >> 1); - temp3R = ((mid3 - side3) >> 1); - - pOutputSamples[i*8+0] = temp0L; - pOutputSamples[i*8+1] = temp0R; - pOutputSamples[i*8+2] = temp1L; - pOutputSamples[i*8+3] = temp1R; - pOutputSamples[i*8+4] = temp2L; - pOutputSamples[i*8+5] = temp2R; - pOutputSamples[i*8+6] = temp3L; - pOutputSamples[i*8+7] = temp3R; + drflac_uint32 temp0L; + drflac_uint32 temp1L; + drflac_uint32 temp2L; + drflac_uint32 temp3L; + drflac_uint32 temp0R; + drflac_uint32 temp1R; + drflac_uint32 temp2R; + drflac_uint32 temp3R; + + drflac_uint32 mid0 = pInputSamples0U32[i*4+0] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + drflac_uint32 mid1 = pInputSamples0U32[i*4+1] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + drflac_uint32 mid2 = pInputSamples0U32[i*4+2] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + drflac_uint32 mid3 = pInputSamples0U32[i*4+3] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + + drflac_uint32 side0 = pInputSamples1U32[i*4+0] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + drflac_uint32 side1 = pInputSamples1U32[i*4+1] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + drflac_uint32 side2 = pInputSamples1U32[i*4+2] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + drflac_uint32 side3 = pInputSamples1U32[i*4+3] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + + mid0 = (mid0 << 1) | (side0 & 0x01); + mid1 = (mid1 << 1) | (side1 & 0x01); + mid2 = (mid2 << 1) | (side2 & 0x01); + mid3 = (mid3 << 1) | (side3 & 0x01); + + temp0L = (drflac_uint32)((drflac_int32)(mid0 + side0) >> 1); + temp1L = (drflac_uint32)((drflac_int32)(mid1 + side1) >> 1); + temp2L = (drflac_uint32)((drflac_int32)(mid2 + side2) >> 1); + temp3L = (drflac_uint32)((drflac_int32)(mid3 + side3) >> 1); + + temp0R = (drflac_uint32)((drflac_int32)(mid0 - side0) >> 1); + temp1R = (drflac_uint32)((drflac_int32)(mid1 - side1) >> 1); + temp2R = (drflac_uint32)((drflac_int32)(mid2 - side2) >> 1); + temp3R = (drflac_uint32)((drflac_int32)(mid3 - side3) >> 1); + + pOutputSamples[i*8+0] = (drflac_int32)temp0L; + pOutputSamples[i*8+1] = (drflac_int32)temp0R; + pOutputSamples[i*8+2] = (drflac_int32)temp1L; + pOutputSamples[i*8+3] = (drflac_int32)temp1R; + pOutputSamples[i*8+4] = (drflac_int32)temp2L; + pOutputSamples[i*8+5] = (drflac_int32)temp2R; + pOutputSamples[i*8+6] = (drflac_int32)temp3L; + pOutputSamples[i*8+7] = (drflac_int32)temp3R; } } for (i = (frameCount4 << 2); i < frameCount; ++i) { - drflac_int32 mid = pInputSamples0[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - drflac_int32 side = pInputSamples1[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + drflac_uint32 mid = pInputSamples0U32[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + drflac_uint32 side = pInputSamples1U32[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; - mid = (((drflac_uint32)mid) << 1) | (side & 0x01); + mid = (mid << 1) | (side & 0x01); - pOutputSamples[i*2+0] = ((mid + side) >> 1) << unusedBitsPerSample; - pOutputSamples[i*2+1] = ((mid - side) >> 1) << unusedBitsPerSample; + pOutputSamples[i*2+0] = (drflac_int32)((drflac_uint32)((drflac_int32)(mid + side) >> 1) << unusedBitsPerSample); + pOutputSamples[i*2+1] = (drflac_int32)((drflac_uint32)((drflac_int32)(mid - side) >> 1) << unusedBitsPerSample); } } #if defined(DRFLAC_SUPPORT_SSE2) -static DRFLAC_INLINE void drflac_read_pcm_frames_s32__decode_mid_side__sse2(drflac* pFlac, drflac_uint64 frameCount, drflac_int32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int32* pOutputSamples) +static DRFLAC_INLINE void drflac_read_pcm_frames_s32__decode_mid_side__sse2(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int32* pOutputSamples) { drflac_uint64 i; - drflac_uint64 frameCount4; - int shift; + drflac_uint64 frameCount4 = frameCount >> 2; + const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0; + const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1; + drflac_int32 shift = unusedBitsPerSample; DRFLAC_ASSERT(pFlac->bitsPerSample <= 24); - frameCount4 = frameCount >> 2; - - shift = unusedBitsPerSample; if (shift == 0) { for (i = 0; i < frameCount4; ++i) { __m128i mid; @@ -8105,13 +9112,13 @@ static DRFLAC_INLINE void drflac_read_pcm_frames_s32__decode_mid_side__sse2(drfl } for (i = (frameCount4 << 2); i < frameCount; ++i) { - drflac_int32 mid = pInputSamples0[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - drflac_int32 side = pInputSamples1[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + drflac_uint32 mid = pInputSamples0U32[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + drflac_uint32 side = pInputSamples1U32[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; - mid = (((drflac_uint32)mid) << 1) | (side & 0x01); + mid = (mid << 1) | (side & 0x01); - pOutputSamples[i*2+0] = ((mid + side) >> 1); - pOutputSamples[i*2+1] = ((mid - side) >> 1); + pOutputSamples[i*2+0] = (drflac_int32)(mid + side) >> 1; + pOutputSamples[i*2+1] = (drflac_int32)(mid - side) >> 1; } } else { shift -= 1; @@ -8134,63 +9141,62 @@ static DRFLAC_INLINE void drflac_read_pcm_frames_s32__decode_mid_side__sse2(drfl } for (i = (frameCount4 << 2); i < frameCount; ++i) { - drflac_int32 mid = pInputSamples0[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - drflac_int32 side = pInputSamples1[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + drflac_uint32 mid = pInputSamples0U32[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + drflac_uint32 side = pInputSamples1U32[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; - mid = (((drflac_uint32)mid) << 1) | (side & 0x01); + mid = (mid << 1) | (side & 0x01); - pOutputSamples[i*2+0] = ((mid + side) << shift); - pOutputSamples[i*2+1] = ((mid - side) << shift); + pOutputSamples[i*2+0] = (drflac_int32)((mid + side) << shift); + pOutputSamples[i*2+1] = (drflac_int32)((mid - side) << shift); } } } #endif #if defined(DRFLAC_SUPPORT_NEON) -static DRFLAC_INLINE void drflac_read_pcm_frames_s32__decode_mid_side__neon(drflac* pFlac, drflac_uint64 frameCount, drflac_int32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int32* pOutputSamples) +static DRFLAC_INLINE void drflac_read_pcm_frames_s32__decode_mid_side__neon(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int32* pOutputSamples) { drflac_uint64 i; - drflac_uint64 frameCount4; - int shift; - int32x4_t wbpsShift0_4; /* wbps = Wasted Bits Per Sample */ - int32x4_t wbpsShift1_4; /* wbps = Wasted Bits Per Sample */ - int32x4_t one4; + drflac_uint64 frameCount4 = frameCount >> 2; + const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0; + const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1; + drflac_int32 shift = unusedBitsPerSample; + int32x4_t wbpsShift0_4; /* wbps = Wasted Bits Per Sample */ + int32x4_t wbpsShift1_4; /* wbps = Wasted Bits Per Sample */ + uint32x4_t one4; DRFLAC_ASSERT(pFlac->bitsPerSample <= 24); - frameCount4 = frameCount >> 2; - wbpsShift0_4 = vdupq_n_s32(pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample); wbpsShift1_4 = vdupq_n_s32(pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample); - one4 = vdupq_n_s32(1); + one4 = vdupq_n_u32(1); - shift = unusedBitsPerSample; if (shift == 0) { for (i = 0; i < frameCount4; ++i) { - int32x4_t mid; - int32x4_t side; + uint32x4_t mid; + uint32x4_t side; int32x4_t left; int32x4_t right; - mid = vshlq_s32(vld1q_s32(pInputSamples0 + i*4), wbpsShift0_4); - side = vshlq_s32(vld1q_s32(pInputSamples1 + i*4), wbpsShift1_4); + mid = vshlq_u32(vld1q_u32(pInputSamples0U32 + i*4), wbpsShift0_4); + side = vshlq_u32(vld1q_u32(pInputSamples1U32 + i*4), wbpsShift1_4); - mid = vorrq_s32(vshlq_n_s32(mid, 1), vandq_s32(side, one4)); + mid = vorrq_u32(vshlq_n_u32(mid, 1), vandq_u32(side, one4)); - left = vshrq_n_s32(vaddq_s32(mid, side), 1); - right = vshrq_n_s32(vsubq_s32(mid, side), 1); + left = vshrq_n_s32(vreinterpretq_s32_u32(vaddq_u32(mid, side)), 1); + right = vshrq_n_s32(vreinterpretq_s32_u32(vsubq_u32(mid, side)), 1); drflac__vst2q_s32(pOutputSamples + i*8, vzipq_s32(left, right)); } for (i = (frameCount4 << 2); i < frameCount; ++i) { - drflac_int32 mid = pInputSamples0[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - drflac_int32 side = pInputSamples1[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + drflac_uint32 mid = pInputSamples0U32[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + drflac_uint32 side = pInputSamples1U32[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; - mid = (((drflac_uint32)mid) << 1) | (side & 0x01); + mid = (mid << 1) | (side & 0x01); - pOutputSamples[i*2+0] = ((mid + side) >> 1); - pOutputSamples[i*2+1] = ((mid - side) >> 1); + pOutputSamples[i*2+0] = (drflac_int32)(mid + side) >> 1; + pOutputSamples[i*2+1] = (drflac_int32)(mid - side) >> 1; } } else { int32x4_t shift4; @@ -8199,36 +9205,36 @@ static DRFLAC_INLINE void drflac_read_pcm_frames_s32__decode_mid_side__neon(drfl shift4 = vdupq_n_s32(shift); for (i = 0; i < frameCount4; ++i) { - int32x4_t mid; - int32x4_t side; + uint32x4_t mid; + uint32x4_t side; int32x4_t left; int32x4_t right; - mid = vshlq_s32(vld1q_s32(pInputSamples0 + i*4), wbpsShift0_4); - side = vshlq_s32(vld1q_s32(pInputSamples1 + i*4), wbpsShift1_4); + mid = vshlq_u32(vld1q_u32(pInputSamples0U32 + i*4), wbpsShift0_4); + side = vshlq_u32(vld1q_u32(pInputSamples1U32 + i*4), wbpsShift1_4); - mid = vorrq_s32(vshlq_n_s32(mid, 1), vandq_s32(side, one4)); + mid = vorrq_u32(vshlq_n_u32(mid, 1), vandq_u32(side, one4)); - left = vshlq_s32(vaddq_s32(mid, side), shift4); - right = vshlq_s32(vsubq_s32(mid, side), shift4); + left = vreinterpretq_s32_u32(vshlq_u32(vaddq_u32(mid, side), shift4)); + right = vreinterpretq_s32_u32(vshlq_u32(vsubq_u32(mid, side), shift4)); drflac__vst2q_s32(pOutputSamples + i*8, vzipq_s32(left, right)); } for (i = (frameCount4 << 2); i < frameCount; ++i) { - drflac_int32 mid = pInputSamples0[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - drflac_int32 side = pInputSamples1[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + drflac_uint32 mid = pInputSamples0U32[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + drflac_uint32 side = pInputSamples1U32[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; - mid = (((drflac_uint32)mid) << 1) | (side & 0x01); + mid = (mid << 1) | (side & 0x01); - pOutputSamples[i*2+0] = ((mid + side) << shift); - pOutputSamples[i*2+1] = ((mid - side) << shift); + pOutputSamples[i*2+0] = (drflac_int32)((mid + side) << shift); + pOutputSamples[i*2+1] = (drflac_int32)((mid - side) << shift); } } } #endif -static DRFLAC_INLINE void drflac_read_pcm_frames_s32__decode_mid_side(drflac* pFlac, drflac_uint64 frameCount, drflac_int32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int32* pOutputSamples) +static DRFLAC_INLINE void drflac_read_pcm_frames_s32__decode_mid_side(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int32* pOutputSamples) { #if defined(DRFLAC_SUPPORT_SSE2) if (drflac__gIsSSE2Supported && pFlac->bitsPerSample <= 24) { @@ -8251,58 +9257,60 @@ static DRFLAC_INLINE void drflac_read_pcm_frames_s32__decode_mid_side(drflac* pF #if 0 -static DRFLAC_INLINE void drflac_read_pcm_frames_s32__decode_independent_stereo__reference(drflac* pFlac, drflac_uint64 frameCount, drflac_int32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int32* pOutputSamples) +static DRFLAC_INLINE void drflac_read_pcm_frames_s32__decode_independent_stereo__reference(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int32* pOutputSamples) { for (drflac_uint64 i = 0; i < frameCount; ++i) { - pOutputSamples[i*2+0] = (pInputSamples0[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample)); - pOutputSamples[i*2+1] = (pInputSamples1[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample)); + pOutputSamples[i*2+0] = (drflac_int32)((drflac_uint32)pInputSamples0[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample)); + pOutputSamples[i*2+1] = (drflac_int32)((drflac_uint32)pInputSamples1[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample)); } } #endif -static DRFLAC_INLINE void drflac_read_pcm_frames_s32__decode_independent_stereo__scalar(drflac* pFlac, drflac_uint64 frameCount, drflac_int32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int32* pOutputSamples) +static DRFLAC_INLINE void drflac_read_pcm_frames_s32__decode_independent_stereo__scalar(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int32* pOutputSamples) { drflac_uint64 i; drflac_uint64 frameCount4 = frameCount >> 2; - - drflac_int32 shift0 = (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample); - drflac_int32 shift1 = (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample); + const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0; + const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1; + drflac_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + drflac_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; for (i = 0; i < frameCount4; ++i) { - drflac_int32 tempL0 = pInputSamples0[i*4+0] << shift0; - drflac_int32 tempL1 = pInputSamples0[i*4+1] << shift0; - drflac_int32 tempL2 = pInputSamples0[i*4+2] << shift0; - drflac_int32 tempL3 = pInputSamples0[i*4+3] << shift0; - - drflac_int32 tempR0 = pInputSamples1[i*4+0] << shift1; - drflac_int32 tempR1 = pInputSamples1[i*4+1] << shift1; - drflac_int32 tempR2 = pInputSamples1[i*4+2] << shift1; - drflac_int32 tempR3 = pInputSamples1[i*4+3] << shift1; - - pOutputSamples[i*8+0] = tempL0; - pOutputSamples[i*8+1] = tempR0; - pOutputSamples[i*8+2] = tempL1; - pOutputSamples[i*8+3] = tempR1; - pOutputSamples[i*8+4] = tempL2; - pOutputSamples[i*8+5] = tempR2; - pOutputSamples[i*8+6] = tempL3; - pOutputSamples[i*8+7] = tempR3; + drflac_uint32 tempL0 = pInputSamples0U32[i*4+0] << shift0; + drflac_uint32 tempL1 = pInputSamples0U32[i*4+1] << shift0; + drflac_uint32 tempL2 = pInputSamples0U32[i*4+2] << shift0; + drflac_uint32 tempL3 = pInputSamples0U32[i*4+3] << shift0; + + drflac_uint32 tempR0 = pInputSamples1U32[i*4+0] << shift1; + drflac_uint32 tempR1 = pInputSamples1U32[i*4+1] << shift1; + drflac_uint32 tempR2 = pInputSamples1U32[i*4+2] << shift1; + drflac_uint32 tempR3 = pInputSamples1U32[i*4+3] << shift1; + + pOutputSamples[i*8+0] = (drflac_int32)tempL0; + pOutputSamples[i*8+1] = (drflac_int32)tempR0; + pOutputSamples[i*8+2] = (drflac_int32)tempL1; + pOutputSamples[i*8+3] = (drflac_int32)tempR1; + pOutputSamples[i*8+4] = (drflac_int32)tempL2; + pOutputSamples[i*8+5] = (drflac_int32)tempR2; + pOutputSamples[i*8+6] = (drflac_int32)tempL3; + pOutputSamples[i*8+7] = (drflac_int32)tempR3; } for (i = (frameCount4 << 2); i < frameCount; ++i) { - pOutputSamples[i*2+0] = (pInputSamples0[i] << shift0); - pOutputSamples[i*2+1] = (pInputSamples1[i] << shift1); + pOutputSamples[i*2+0] = (drflac_int32)(pInputSamples0U32[i] << shift0); + pOutputSamples[i*2+1] = (drflac_int32)(pInputSamples1U32[i] << shift1); } } #if defined(DRFLAC_SUPPORT_SSE2) -static DRFLAC_INLINE void drflac_read_pcm_frames_s32__decode_independent_stereo__sse2(drflac* pFlac, drflac_uint64 frameCount, drflac_int32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int32* pOutputSamples) +static DRFLAC_INLINE void drflac_read_pcm_frames_s32__decode_independent_stereo__sse2(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int32* pOutputSamples) { drflac_uint64 i; drflac_uint64 frameCount4 = frameCount >> 2; - - int shift0 = (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample); - int shift1 = (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample); + const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0; + const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1; + drflac_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + drflac_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; for (i = 0; i < frameCount4; ++i) { __m128i left = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples0 + i), shift0); @@ -8313,20 +9321,21 @@ static DRFLAC_INLINE void drflac_read_pcm_frames_s32__decode_independent_stereo_ } for (i = (frameCount4 << 2); i < frameCount; ++i) { - pOutputSamples[i*2+0] = (pInputSamples0[i] << shift0); - pOutputSamples[i*2+1] = (pInputSamples1[i] << shift1); + pOutputSamples[i*2+0] = (drflac_int32)(pInputSamples0U32[i] << shift0); + pOutputSamples[i*2+1] = (drflac_int32)(pInputSamples1U32[i] << shift1); } } #endif #if defined(DRFLAC_SUPPORT_NEON) -static DRFLAC_INLINE void drflac_read_pcm_frames_s32__decode_independent_stereo__neon(drflac* pFlac, drflac_uint64 frameCount, drflac_int32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int32* pOutputSamples) +static DRFLAC_INLINE void drflac_read_pcm_frames_s32__decode_independent_stereo__neon(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int32* pOutputSamples) { drflac_uint64 i; drflac_uint64 frameCount4 = frameCount >> 2; - - int shift0 = (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample); - int shift1 = (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample); + const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0; + const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1; + drflac_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + drflac_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; int32x4_t shift4_0 = vdupq_n_s32(shift0); int32x4_t shift4_1 = vdupq_n_s32(shift1); @@ -8335,20 +9344,20 @@ static DRFLAC_INLINE void drflac_read_pcm_frames_s32__decode_independent_stereo_ int32x4_t left; int32x4_t right; - left = vshlq_s32(vld1q_s32(pInputSamples0 + i*4), shift4_0); - right = vshlq_s32(vld1q_s32(pInputSamples1 + i*4), shift4_1); + left = vreinterpretq_s32_u32(vshlq_u32(vld1q_u32(pInputSamples0U32 + i*4), shift4_0)); + right = vreinterpretq_s32_u32(vshlq_u32(vld1q_u32(pInputSamples1U32 + i*4), shift4_1)); drflac__vst2q_s32(pOutputSamples + i*8, vzipq_s32(left, right)); } for (i = (frameCount4 << 2); i < frameCount; ++i) { - pOutputSamples[i*2+0] = (pInputSamples0[i] << shift0); - pOutputSamples[i*2+1] = (pInputSamples1[i] << shift1); + pOutputSamples[i*2+0] = (drflac_int32)(pInputSamples0U32[i] << shift0); + pOutputSamples[i*2+1] = (drflac_int32)(pInputSamples1U32[i] << shift1); } } #endif -static DRFLAC_INLINE void drflac_read_pcm_frames_s32__decode_independent_stereo(drflac* pFlac, drflac_uint64 frameCount, drflac_int32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int32* pOutputSamples) +static DRFLAC_INLINE void drflac_read_pcm_frames_s32__decode_independent_stereo(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int32* pOutputSamples) { #if defined(DRFLAC_SUPPORT_SSE2) if (drflac__gIsSSE2Supported && pFlac->bitsPerSample <= 24) { @@ -8370,10 +9379,10 @@ static DRFLAC_INLINE void drflac_read_pcm_frames_s32__decode_independent_stereo( } -drflac_uint64 drflac_read_pcm_frames_s32(drflac* pFlac, drflac_uint64 framesToRead, drflac_int32* pBufferOut) +DRFLAC_API drflac_uint64 drflac_read_pcm_frames_s32(drflac* pFlac, drflac_uint64 framesToRead, drflac_int32* pBufferOut) { drflac_uint64 framesRead; - drflac_int32 unusedBitsPerSample; + drflac_uint32 unusedBitsPerSample; if (pFlac == NULL || framesToRead == 0) { return 0; @@ -8383,6 +9392,7 @@ drflac_uint64 drflac_read_pcm_frames_s32(drflac* pFlac, drflac_uint64 framesToRe return drflac__seek_forward_by_pcm_frames(pFlac, framesToRead); } + DRFLAC_ASSERT(pFlac->bitsPerSample <= 32); unusedBitsPerSample = 32 - pFlac->bitsPerSample; framesRead = 0; @@ -8452,13 +9462,13 @@ drflac_uint64 drflac_read_pcm_frames_s32(drflac* pFlac, drflac_uint64 framesToRe #if 0 -static DRFLAC_INLINE void drflac_read_pcm_frames_s16__decode_left_side__reference(drflac* pFlac, drflac_uint64 frameCount, drflac_int32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int16* pOutputSamples) +static DRFLAC_INLINE void drflac_read_pcm_frames_s16__decode_left_side__reference(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int16* pOutputSamples) { drflac_uint64 i; for (i = 0; i < frameCount; ++i) { - drflac_int32 left = pInputSamples0[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample); - drflac_int32 side = pInputSamples1[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample); - drflac_int32 right = left - side; + drflac_uint32 left = (drflac_uint32)pInputSamples0[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample); + drflac_uint32 side = (drflac_uint32)pInputSamples1[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample); + drflac_uint32 right = left - side; left >>= 16; right >>= 16; @@ -8469,28 +9479,30 @@ static DRFLAC_INLINE void drflac_read_pcm_frames_s16__decode_left_side__referenc } #endif -static DRFLAC_INLINE void drflac_read_pcm_frames_s16__decode_left_side__scalar(drflac* pFlac, drflac_uint64 frameCount, drflac_int32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int16* pOutputSamples) +static DRFLAC_INLINE void drflac_read_pcm_frames_s16__decode_left_side__scalar(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int16* pOutputSamples) { drflac_uint64 i; drflac_uint64 frameCount4 = frameCount >> 2; + const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0; + const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1; + drflac_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + drflac_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; - drflac_int32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - drflac_int32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; for (i = 0; i < frameCount4; ++i) { - drflac_int32 left0 = pInputSamples0[i*4+0] << shift0; - drflac_int32 left1 = pInputSamples0[i*4+1] << shift0; - drflac_int32 left2 = pInputSamples0[i*4+2] << shift0; - drflac_int32 left3 = pInputSamples0[i*4+3] << shift0; + drflac_uint32 left0 = pInputSamples0U32[i*4+0] << shift0; + drflac_uint32 left1 = pInputSamples0U32[i*4+1] << shift0; + drflac_uint32 left2 = pInputSamples0U32[i*4+2] << shift0; + drflac_uint32 left3 = pInputSamples0U32[i*4+3] << shift0; - drflac_int32 side0 = pInputSamples1[i*4+0] << shift1; - drflac_int32 side1 = pInputSamples1[i*4+1] << shift1; - drflac_int32 side2 = pInputSamples1[i*4+2] << shift1; - drflac_int32 side3 = pInputSamples1[i*4+3] << shift1; + drflac_uint32 side0 = pInputSamples1U32[i*4+0] << shift1; + drflac_uint32 side1 = pInputSamples1U32[i*4+1] << shift1; + drflac_uint32 side2 = pInputSamples1U32[i*4+2] << shift1; + drflac_uint32 side3 = pInputSamples1U32[i*4+3] << shift1; - drflac_int32 right0 = left0 - side0; - drflac_int32 right1 = left1 - side1; - drflac_int32 right2 = left2 - side2; - drflac_int32 right3 = left3 - side3; + drflac_uint32 right0 = left0 - side0; + drflac_uint32 right1 = left1 - side1; + drflac_uint32 right2 = left2 - side2; + drflac_uint32 right3 = left3 - side3; left0 >>= 16; left1 >>= 16; @@ -8513,9 +9525,9 @@ static DRFLAC_INLINE void drflac_read_pcm_frames_s16__decode_left_side__scalar(d } for (i = (frameCount4 << 2); i < frameCount; ++i) { - drflac_int32 left = pInputSamples0[i] << shift0; - drflac_int32 side = pInputSamples1[i] << shift1; - drflac_int32 right = left - side; + drflac_uint32 left = pInputSamples0U32[i] << shift0; + drflac_uint32 side = pInputSamples1U32[i] << shift1; + drflac_uint32 right = left - side; left >>= 16; right >>= 16; @@ -8526,20 +9538,17 @@ static DRFLAC_INLINE void drflac_read_pcm_frames_s16__decode_left_side__scalar(d } #if defined(DRFLAC_SUPPORT_SSE2) -static DRFLAC_INLINE void drflac_read_pcm_frames_s16__decode_left_side__sse2(drflac* pFlac, drflac_uint64 frameCount, drflac_int32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int16* pOutputSamples) +static DRFLAC_INLINE void drflac_read_pcm_frames_s16__decode_left_side__sse2(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int16* pOutputSamples) { - drflac_uint64 frameCount4; - drflac_int32 shift0; - drflac_int32 shift1; drflac_uint64 i; + drflac_uint64 frameCount4 = frameCount >> 2; + const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0; + const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1; + drflac_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + drflac_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; DRFLAC_ASSERT(pFlac->bitsPerSample <= 24); - frameCount4 = frameCount >> 2; - - shift0 = (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample); - shift1 = (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample); - for (i = 0; i < frameCount4; ++i) { __m128i left = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples0 + i), shift0); __m128i side = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples1 + i), shift1); @@ -8552,9 +9561,9 @@ static DRFLAC_INLINE void drflac_read_pcm_frames_s16__decode_left_side__sse2(drf } for (i = (frameCount4 << 2); i < frameCount; ++i) { - drflac_int32 left = pInputSamples0[i] << shift0; - drflac_int32 side = pInputSamples1[i] << shift1; - drflac_int32 right = left - side; + drflac_uint32 left = pInputSamples0U32[i] << shift0; + drflac_uint32 side = pInputSamples1U32[i] << shift1; + drflac_uint32 right = left - side; left >>= 16; right >>= 16; @@ -8566,44 +9575,41 @@ static DRFLAC_INLINE void drflac_read_pcm_frames_s16__decode_left_side__sse2(drf #endif #if defined(DRFLAC_SUPPORT_NEON) -static DRFLAC_INLINE void drflac_read_pcm_frames_s16__decode_left_side__neon(drflac* pFlac, drflac_uint64 frameCount, drflac_int32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int16* pOutputSamples) +static DRFLAC_INLINE void drflac_read_pcm_frames_s16__decode_left_side__neon(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int16* pOutputSamples) { - drflac_uint64 frameCount4; - drflac_int32 shift0; - drflac_int32 shift1; drflac_uint64 i; + drflac_uint64 frameCount4 = frameCount >> 2; + const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0; + const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1; + drflac_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + drflac_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; int32x4_t shift0_4; int32x4_t shift1_4; DRFLAC_ASSERT(pFlac->bitsPerSample <= 24); - frameCount4 = frameCount >> 2; - - shift0 = (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample); - shift1 = (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample); - shift0_4 = vdupq_n_s32(shift0); shift1_4 = vdupq_n_s32(shift1); for (i = 0; i < frameCount4; ++i) { - int32x4_t left; - int32x4_t side; - int32x4_t right; + uint32x4_t left; + uint32x4_t side; + uint32x4_t right; - left = vshlq_s32(vld1q_s32(pInputSamples0 + i*4), shift0_4); - side = vshlq_s32(vld1q_s32(pInputSamples1 + i*4), shift1_4); - right = vsubq_s32(left, side); + left = vshlq_u32(vld1q_u32(pInputSamples0U32 + i*4), shift0_4); + side = vshlq_u32(vld1q_u32(pInputSamples1U32 + i*4), shift1_4); + right = vsubq_u32(left, side); - left = vshrq_n_s32(left, 16); - right = vshrq_n_s32(right, 16); + left = vshrq_n_u32(left, 16); + right = vshrq_n_u32(right, 16); - drflac__vst2q_s16(pOutputSamples + i*8, vzip_s16(vmovn_s32(left), vmovn_s32(right))); + drflac__vst2q_u16((drflac_uint16*)pOutputSamples + i*8, vzip_u16(vmovn_u32(left), vmovn_u32(right))); } for (i = (frameCount4 << 2); i < frameCount; ++i) { - drflac_int32 left = pInputSamples0[i] << shift0; - drflac_int32 side = pInputSamples1[i] << shift1; - drflac_int32 right = left - side; + drflac_uint32 left = pInputSamples0U32[i] << shift0; + drflac_uint32 side = pInputSamples1U32[i] << shift1; + drflac_uint32 right = left - side; left >>= 16; right >>= 16; @@ -8614,7 +9620,7 @@ static DRFLAC_INLINE void drflac_read_pcm_frames_s16__decode_left_side__neon(drf } #endif -static DRFLAC_INLINE void drflac_read_pcm_frames_s16__decode_left_side(drflac* pFlac, drflac_uint64 frameCount, drflac_int32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int16* pOutputSamples) +static DRFLAC_INLINE void drflac_read_pcm_frames_s16__decode_left_side(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int16* pOutputSamples) { #if defined(DRFLAC_SUPPORT_SSE2) if (drflac__gIsSSE2Supported && pFlac->bitsPerSample <= 24) { @@ -8637,13 +9643,13 @@ static DRFLAC_INLINE void drflac_read_pcm_frames_s16__decode_left_side(drflac* p #if 0 -static DRFLAC_INLINE void drflac_read_pcm_frames_s16__decode_right_side__reference(drflac* pFlac, drflac_uint64 frameCount, drflac_int32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int16* pOutputSamples) +static DRFLAC_INLINE void drflac_read_pcm_frames_s16__decode_right_side__reference(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int16* pOutputSamples) { drflac_uint64 i; for (i = 0; i < frameCount; ++i) { - drflac_int32 side = pInputSamples0[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample); - drflac_int32 right = pInputSamples1[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample); - drflac_int32 left = right + side; + drflac_uint32 side = (drflac_uint32)pInputSamples0[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample); + drflac_uint32 right = (drflac_uint32)pInputSamples1[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample); + drflac_uint32 left = right + side; left >>= 16; right >>= 16; @@ -8654,28 +9660,30 @@ static DRFLAC_INLINE void drflac_read_pcm_frames_s16__decode_right_side__referen } #endif -static DRFLAC_INLINE void drflac_read_pcm_frames_s16__decode_right_side__scalar(drflac* pFlac, drflac_uint64 frameCount, drflac_int32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int16* pOutputSamples) +static DRFLAC_INLINE void drflac_read_pcm_frames_s16__decode_right_side__scalar(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int16* pOutputSamples) { drflac_uint64 i; drflac_uint64 frameCount4 = frameCount >> 2; + const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0; + const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1; + drflac_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + drflac_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; - drflac_int32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - drflac_int32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; for (i = 0; i < frameCount4; ++i) { - drflac_int32 side0 = pInputSamples0[i*4+0] << shift0; - drflac_int32 side1 = pInputSamples0[i*4+1] << shift0; - drflac_int32 side2 = pInputSamples0[i*4+2] << shift0; - drflac_int32 side3 = pInputSamples0[i*4+3] << shift0; + drflac_uint32 side0 = pInputSamples0U32[i*4+0] << shift0; + drflac_uint32 side1 = pInputSamples0U32[i*4+1] << shift0; + drflac_uint32 side2 = pInputSamples0U32[i*4+2] << shift0; + drflac_uint32 side3 = pInputSamples0U32[i*4+3] << shift0; - drflac_int32 right0 = pInputSamples1[i*4+0] << shift1; - drflac_int32 right1 = pInputSamples1[i*4+1] << shift1; - drflac_int32 right2 = pInputSamples1[i*4+2] << shift1; - drflac_int32 right3 = pInputSamples1[i*4+3] << shift1; + drflac_uint32 right0 = pInputSamples1U32[i*4+0] << shift1; + drflac_uint32 right1 = pInputSamples1U32[i*4+1] << shift1; + drflac_uint32 right2 = pInputSamples1U32[i*4+2] << shift1; + drflac_uint32 right3 = pInputSamples1U32[i*4+3] << shift1; - drflac_int32 left0 = right0 + side0; - drflac_int32 left1 = right1 + side1; - drflac_int32 left2 = right2 + side2; - drflac_int32 left3 = right3 + side3; + drflac_uint32 left0 = right0 + side0; + drflac_uint32 left1 = right1 + side1; + drflac_uint32 left2 = right2 + side2; + drflac_uint32 left3 = right3 + side3; left0 >>= 16; left1 >>= 16; @@ -8698,9 +9706,9 @@ static DRFLAC_INLINE void drflac_read_pcm_frames_s16__decode_right_side__scalar( } for (i = (frameCount4 << 2); i < frameCount; ++i) { - drflac_int32 side = pInputSamples0[i] << shift0; - drflac_int32 right = pInputSamples1[i] << shift1; - drflac_int32 left = right + side; + drflac_uint32 side = pInputSamples0U32[i] << shift0; + drflac_uint32 right = pInputSamples1U32[i] << shift1; + drflac_uint32 left = right + side; left >>= 16; right >>= 16; @@ -8711,20 +9719,17 @@ static DRFLAC_INLINE void drflac_read_pcm_frames_s16__decode_right_side__scalar( } #if defined(DRFLAC_SUPPORT_SSE2) -static DRFLAC_INLINE void drflac_read_pcm_frames_s16__decode_right_side__sse2(drflac* pFlac, drflac_uint64 frameCount, drflac_int32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int16* pOutputSamples) +static DRFLAC_INLINE void drflac_read_pcm_frames_s16__decode_right_side__sse2(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int16* pOutputSamples) { - drflac_uint64 frameCount4; - drflac_int32 shift0; - drflac_int32 shift1; drflac_uint64 i; + drflac_uint64 frameCount4 = frameCount >> 2; + const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0; + const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1; + drflac_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + drflac_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; DRFLAC_ASSERT(pFlac->bitsPerSample <= 24); - frameCount4 = frameCount >> 2; - - shift0 = (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample); - shift1 = (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample); - for (i = 0; i < frameCount4; ++i) { __m128i side = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples0 + i), shift0); __m128i right = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples1 + i), shift1); @@ -8737,9 +9742,9 @@ static DRFLAC_INLINE void drflac_read_pcm_frames_s16__decode_right_side__sse2(dr } for (i = (frameCount4 << 2); i < frameCount; ++i) { - drflac_int32 side = pInputSamples0[i] << shift0; - drflac_int32 right = pInputSamples1[i] << shift1; - drflac_int32 left = right + side; + drflac_uint32 side = pInputSamples0U32[i] << shift0; + drflac_uint32 right = pInputSamples1U32[i] << shift1; + drflac_uint32 left = right + side; left >>= 16; right >>= 16; @@ -8751,44 +9756,41 @@ static DRFLAC_INLINE void drflac_read_pcm_frames_s16__decode_right_side__sse2(dr #endif #if defined(DRFLAC_SUPPORT_NEON) -static DRFLAC_INLINE void drflac_read_pcm_frames_s16__decode_right_side__neon(drflac* pFlac, drflac_uint64 frameCount, drflac_int32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int16* pOutputSamples) +static DRFLAC_INLINE void drflac_read_pcm_frames_s16__decode_right_side__neon(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int16* pOutputSamples) { - drflac_uint64 frameCount4; - drflac_int32 shift0; - drflac_int32 shift1; drflac_uint64 i; + drflac_uint64 frameCount4 = frameCount >> 2; + const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0; + const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1; + drflac_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + drflac_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; int32x4_t shift0_4; int32x4_t shift1_4; DRFLAC_ASSERT(pFlac->bitsPerSample <= 24); - frameCount4 = frameCount >> 2; - - shift0 = (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample); - shift1 = (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample); - shift0_4 = vdupq_n_s32(shift0); shift1_4 = vdupq_n_s32(shift1); for (i = 0; i < frameCount4; ++i) { - int32x4_t side; - int32x4_t right; - int32x4_t left; + uint32x4_t side; + uint32x4_t right; + uint32x4_t left; - side = vshlq_s32(vld1q_s32(pInputSamples0 + i*4), shift0_4); - right = vshlq_s32(vld1q_s32(pInputSamples1 + i*4), shift1_4); - left = vaddq_s32(right, side); + side = vshlq_u32(vld1q_u32(pInputSamples0U32 + i*4), shift0_4); + right = vshlq_u32(vld1q_u32(pInputSamples1U32 + i*4), shift1_4); + left = vaddq_u32(right, side); - left = vshrq_n_s32(left, 16); - right = vshrq_n_s32(right, 16); + left = vshrq_n_u32(left, 16); + right = vshrq_n_u32(right, 16); - drflac__vst2q_s16(pOutputSamples + i*8, vzip_s16(vmovn_s32(left), vmovn_s32(right))); + drflac__vst2q_u16((drflac_uint16*)pOutputSamples + i*8, vzip_u16(vmovn_u32(left), vmovn_u32(right))); } for (i = (frameCount4 << 2); i < frameCount; ++i) { - drflac_int32 side = pInputSamples0[i] << shift0; - drflac_int32 right = pInputSamples1[i] << shift1; - drflac_int32 left = right + side; + drflac_uint32 side = pInputSamples0U32[i] << shift0; + drflac_uint32 right = pInputSamples1U32[i] << shift1; + drflac_uint32 left = right + side; left >>= 16; right >>= 16; @@ -8799,7 +9801,7 @@ static DRFLAC_INLINE void drflac_read_pcm_frames_s16__decode_right_side__neon(dr } #endif -static DRFLAC_INLINE void drflac_read_pcm_frames_s16__decode_right_side(drflac* pFlac, drflac_uint64 frameCount, drflac_int32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int16* pOutputSamples) +static DRFLAC_INLINE void drflac_read_pcm_frames_s16__decode_right_side(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int16* pOutputSamples) { #if defined(DRFLAC_SUPPORT_SSE2) if (drflac__gIsSSE2Supported && pFlac->bitsPerSample <= 24) { @@ -8822,62 +9824,64 @@ static DRFLAC_INLINE void drflac_read_pcm_frames_s16__decode_right_side(drflac* #if 0 -static DRFLAC_INLINE void drflac_read_pcm_frames_s16__decode_mid_side__reference(drflac* pFlac, drflac_uint64 frameCount, drflac_int32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int16* pOutputSamples) +static DRFLAC_INLINE void drflac_read_pcm_frames_s16__decode_mid_side__reference(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int16* pOutputSamples) { for (drflac_uint64 i = 0; i < frameCount; ++i) { - drflac_int32 mid = pInputSamples0[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - drflac_int32 side = pInputSamples1[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + drflac_uint32 mid = (drflac_uint32)pInputSamples0[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + drflac_uint32 side = (drflac_uint32)pInputSamples1[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; - mid = (((drflac_uint32)mid) << 1) | (side & 0x01); + mid = (mid << 1) | (side & 0x01); - pOutputSamples[i*2+0] = (drflac_int16)((((mid + side) >> 1) << unusedBitsPerSample) >> 16); - pOutputSamples[i*2+1] = (drflac_int16)((((mid - side) >> 1) << unusedBitsPerSample) >> 16); + pOutputSamples[i*2+0] = (drflac_int16)(((drflac_uint32)((drflac_int32)(mid + side) >> 1) << unusedBitsPerSample) >> 16); + pOutputSamples[i*2+1] = (drflac_int16)(((drflac_uint32)((drflac_int32)(mid - side) >> 1) << unusedBitsPerSample) >> 16); } } #endif -static DRFLAC_INLINE void drflac_read_pcm_frames_s16__decode_mid_side__scalar(drflac* pFlac, drflac_uint64 frameCount, drflac_int32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int16* pOutputSamples) +static DRFLAC_INLINE void drflac_read_pcm_frames_s16__decode_mid_side__scalar(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int16* pOutputSamples) { drflac_uint64 i; drflac_uint64 frameCount4 = frameCount >> 2; + const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0; + const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1; + drflac_uint32 shift = unusedBitsPerSample; - int shift = unusedBitsPerSample; if (shift > 0) { shift -= 1; for (i = 0; i < frameCount4; ++i) { - drflac_int32 temp0L; - drflac_int32 temp1L; - drflac_int32 temp2L; - drflac_int32 temp3L; - drflac_int32 temp0R; - drflac_int32 temp1R; - drflac_int32 temp2R; - drflac_int32 temp3R; - - drflac_int32 mid0 = pInputSamples0[i*4+0] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - drflac_int32 mid1 = pInputSamples0[i*4+1] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - drflac_int32 mid2 = pInputSamples0[i*4+2] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - drflac_int32 mid3 = pInputSamples0[i*4+3] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - - drflac_int32 side0 = pInputSamples1[i*4+0] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; - drflac_int32 side1 = pInputSamples1[i*4+1] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; - drflac_int32 side2 = pInputSamples1[i*4+2] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; - drflac_int32 side3 = pInputSamples1[i*4+3] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; - - mid0 = (((drflac_uint32)mid0) << 1) | (side0 & 0x01); - mid1 = (((drflac_uint32)mid1) << 1) | (side1 & 0x01); - mid2 = (((drflac_uint32)mid2) << 1) | (side2 & 0x01); - mid3 = (((drflac_uint32)mid3) << 1) | (side3 & 0x01); - - temp0L = ((mid0 + side0) << shift); - temp1L = ((mid1 + side1) << shift); - temp2L = ((mid2 + side2) << shift); - temp3L = ((mid3 + side3) << shift); - - temp0R = ((mid0 - side0) << shift); - temp1R = ((mid1 - side1) << shift); - temp2R = ((mid2 - side2) << shift); - temp3R = ((mid3 - side3) << shift); + drflac_uint32 temp0L; + drflac_uint32 temp1L; + drflac_uint32 temp2L; + drflac_uint32 temp3L; + drflac_uint32 temp0R; + drflac_uint32 temp1R; + drflac_uint32 temp2R; + drflac_uint32 temp3R; + + drflac_uint32 mid0 = pInputSamples0U32[i*4+0] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + drflac_uint32 mid1 = pInputSamples0U32[i*4+1] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + drflac_uint32 mid2 = pInputSamples0U32[i*4+2] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + drflac_uint32 mid3 = pInputSamples0U32[i*4+3] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + + drflac_uint32 side0 = pInputSamples1U32[i*4+0] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + drflac_uint32 side1 = pInputSamples1U32[i*4+1] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + drflac_uint32 side2 = pInputSamples1U32[i*4+2] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + drflac_uint32 side3 = pInputSamples1U32[i*4+3] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + + mid0 = (mid0 << 1) | (side0 & 0x01); + mid1 = (mid1 << 1) | (side1 & 0x01); + mid2 = (mid2 << 1) | (side2 & 0x01); + mid3 = (mid3 << 1) | (side3 & 0x01); + + temp0L = (mid0 + side0) << shift; + temp1L = (mid1 + side1) << shift; + temp2L = (mid2 + side2) << shift; + temp3L = (mid3 + side3) << shift; + + temp0R = (mid0 - side0) << shift; + temp1R = (mid1 - side1) << shift; + temp2R = (mid2 - side2) << shift; + temp3R = (mid3 - side3) << shift; temp0L >>= 16; temp1L >>= 16; @@ -8900,39 +9904,39 @@ static DRFLAC_INLINE void drflac_read_pcm_frames_s16__decode_mid_side__scalar(dr } } else { for (i = 0; i < frameCount4; ++i) { - drflac_int32 temp0L; - drflac_int32 temp1L; - drflac_int32 temp2L; - drflac_int32 temp3L; - drflac_int32 temp0R; - drflac_int32 temp1R; - drflac_int32 temp2R; - drflac_int32 temp3R; - - drflac_int32 mid0 = pInputSamples0[i*4+0] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - drflac_int32 mid1 = pInputSamples0[i*4+1] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - drflac_int32 mid2 = pInputSamples0[i*4+2] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - drflac_int32 mid3 = pInputSamples0[i*4+3] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - - drflac_int32 side0 = pInputSamples1[i*4+0] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; - drflac_int32 side1 = pInputSamples1[i*4+1] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; - drflac_int32 side2 = pInputSamples1[i*4+2] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; - drflac_int32 side3 = pInputSamples1[i*4+3] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; - - mid0 = (((drflac_uint32)mid0) << 1) | (side0 & 0x01); - mid1 = (((drflac_uint32)mid1) << 1) | (side1 & 0x01); - mid2 = (((drflac_uint32)mid2) << 1) | (side2 & 0x01); - mid3 = (((drflac_uint32)mid3) << 1) | (side3 & 0x01); - - temp0L = ((mid0 + side0) >> 1); - temp1L = ((mid1 + side1) >> 1); - temp2L = ((mid2 + side2) >> 1); - temp3L = ((mid3 + side3) >> 1); - - temp0R = ((mid0 - side0) >> 1); - temp1R = ((mid1 - side1) >> 1); - temp2R = ((mid2 - side2) >> 1); - temp3R = ((mid3 - side3) >> 1); + drflac_uint32 temp0L; + drflac_uint32 temp1L; + drflac_uint32 temp2L; + drflac_uint32 temp3L; + drflac_uint32 temp0R; + drflac_uint32 temp1R; + drflac_uint32 temp2R; + drflac_uint32 temp3R; + + drflac_uint32 mid0 = pInputSamples0U32[i*4+0] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + drflac_uint32 mid1 = pInputSamples0U32[i*4+1] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + drflac_uint32 mid2 = pInputSamples0U32[i*4+2] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + drflac_uint32 mid3 = pInputSamples0U32[i*4+3] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + + drflac_uint32 side0 = pInputSamples1U32[i*4+0] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + drflac_uint32 side1 = pInputSamples1U32[i*4+1] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + drflac_uint32 side2 = pInputSamples1U32[i*4+2] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + drflac_uint32 side3 = pInputSamples1U32[i*4+3] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + + mid0 = (mid0 << 1) | (side0 & 0x01); + mid1 = (mid1 << 1) | (side1 & 0x01); + mid2 = (mid2 << 1) | (side2 & 0x01); + mid3 = (mid3 << 1) | (side3 & 0x01); + + temp0L = ((drflac_int32)(mid0 + side0) >> 1); + temp1L = ((drflac_int32)(mid1 + side1) >> 1); + temp2L = ((drflac_int32)(mid2 + side2) >> 1); + temp3L = ((drflac_int32)(mid3 + side3) >> 1); + + temp0R = ((drflac_int32)(mid0 - side0) >> 1); + temp1R = ((drflac_int32)(mid1 - side1) >> 1); + temp2R = ((drflac_int32)(mid2 - side2) >> 1); + temp3R = ((drflac_int32)(mid3 - side3) >> 1); temp0L >>= 16; temp1L >>= 16; @@ -8956,28 +9960,27 @@ static DRFLAC_INLINE void drflac_read_pcm_frames_s16__decode_mid_side__scalar(dr } for (i = (frameCount4 << 2); i < frameCount; ++i) { - drflac_int32 mid = pInputSamples0[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - drflac_int32 side = pInputSamples1[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + drflac_uint32 mid = pInputSamples0U32[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + drflac_uint32 side = pInputSamples1U32[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; - mid = (((drflac_uint32)mid) << 1) | (side & 0x01); + mid = (mid << 1) | (side & 0x01); - pOutputSamples[i*2+0] = (drflac_int16)((((mid + side) >> 1) << unusedBitsPerSample) >> 16); - pOutputSamples[i*2+1] = (drflac_int16)((((mid - side) >> 1) << unusedBitsPerSample) >> 16); + pOutputSamples[i*2+0] = (drflac_int16)(((drflac_uint32)((drflac_int32)(mid + side) >> 1) << unusedBitsPerSample) >> 16); + pOutputSamples[i*2+1] = (drflac_int16)(((drflac_uint32)((drflac_int32)(mid - side) >> 1) << unusedBitsPerSample) >> 16); } } #if defined(DRFLAC_SUPPORT_SSE2) -static DRFLAC_INLINE void drflac_read_pcm_frames_s16__decode_mid_side__sse2(drflac* pFlac, drflac_uint64 frameCount, drflac_int32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int16* pOutputSamples) +static DRFLAC_INLINE void drflac_read_pcm_frames_s16__decode_mid_side__sse2(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int16* pOutputSamples) { drflac_uint64 i; - drflac_uint64 frameCount4; - drflac_int32 shift; + drflac_uint64 frameCount4 = frameCount >> 2; + const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0; + const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1; + drflac_uint32 shift = unusedBitsPerSample; DRFLAC_ASSERT(pFlac->bitsPerSample <= 24); - frameCount4 = frameCount >> 2; - - shift = unusedBitsPerSample; if (shift == 0) { for (i = 0; i < frameCount4; ++i) { __m128i mid; @@ -9000,13 +10003,13 @@ static DRFLAC_INLINE void drflac_read_pcm_frames_s16__decode_mid_side__sse2(drfl } for (i = (frameCount4 << 2); i < frameCount; ++i) { - drflac_int32 mid = pInputSamples0[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - drflac_int32 side = pInputSamples1[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + drflac_uint32 mid = pInputSamples0U32[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + drflac_uint32 side = pInputSamples1U32[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; - mid = (((drflac_uint32)mid) << 1) | (side & 0x01); + mid = (mid << 1) | (side & 0x01); - pOutputSamples[i*2+0] = (drflac_int16)(((mid + side) >> 1) >> 16); - pOutputSamples[i*2+1] = (drflac_int16)(((mid - side) >> 1) >> 16); + pOutputSamples[i*2+0] = (drflac_int16)(((drflac_int32)(mid + side) >> 1) >> 16); + pOutputSamples[i*2+1] = (drflac_int16)(((drflac_int32)(mid - side) >> 1) >> 16); } } else { shift -= 1; @@ -9031,10 +10034,10 @@ static DRFLAC_INLINE void drflac_read_pcm_frames_s16__decode_mid_side__sse2(drfl } for (i = (frameCount4 << 2); i < frameCount; ++i) { - drflac_int32 mid = pInputSamples0[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - drflac_int32 side = pInputSamples1[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + drflac_uint32 mid = pInputSamples0U32[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + drflac_uint32 side = pInputSamples1U32[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; - mid = (((drflac_uint32)mid) << 1) | (side & 0x01); + mid = (mid << 1) | (side & 0x01); pOutputSamples[i*2+0] = (drflac_int16)(((mid + side) << shift) >> 16); pOutputSamples[i*2+1] = (drflac_int16)(((mid - side) << shift) >> 16); @@ -9044,36 +10047,35 @@ static DRFLAC_INLINE void drflac_read_pcm_frames_s16__decode_mid_side__sse2(drfl #endif #if defined(DRFLAC_SUPPORT_NEON) -static DRFLAC_INLINE void drflac_read_pcm_frames_s16__decode_mid_side__neon(drflac* pFlac, drflac_uint64 frameCount, drflac_int32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int16* pOutputSamples) +static DRFLAC_INLINE void drflac_read_pcm_frames_s16__decode_mid_side__neon(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int16* pOutputSamples) { drflac_uint64 i; - drflac_uint64 frameCount4; - int shift; + drflac_uint64 frameCount4 = frameCount >> 2; + const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0; + const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1; + drflac_uint32 shift = unusedBitsPerSample; int32x4_t wbpsShift0_4; /* wbps = Wasted Bits Per Sample */ int32x4_t wbpsShift1_4; /* wbps = Wasted Bits Per Sample */ DRFLAC_ASSERT(pFlac->bitsPerSample <= 24); - frameCount4 = frameCount >> 2; - wbpsShift0_4 = vdupq_n_s32(pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample); wbpsShift1_4 = vdupq_n_s32(pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample); - shift = unusedBitsPerSample; if (shift == 0) { for (i = 0; i < frameCount4; ++i) { - int32x4_t mid; - int32x4_t side; + uint32x4_t mid; + uint32x4_t side; int32x4_t left; int32x4_t right; - mid = vshlq_s32(vld1q_s32(pInputSamples0 + i*4), wbpsShift0_4); - side = vshlq_s32(vld1q_s32(pInputSamples1 + i*4), wbpsShift1_4); + mid = vshlq_u32(vld1q_u32(pInputSamples0U32 + i*4), wbpsShift0_4); + side = vshlq_u32(vld1q_u32(pInputSamples1U32 + i*4), wbpsShift1_4); - mid = vorrq_s32(vshlq_n_s32(mid, 1), vandq_s32(side, vdupq_n_s32(1))); + mid = vorrq_u32(vshlq_n_u32(mid, 1), vandq_u32(side, vdupq_n_u32(1))); - left = vshrq_n_s32(vaddq_s32(mid, side), 1); - right = vshrq_n_s32(vsubq_s32(mid, side), 1); + left = vshrq_n_s32(vreinterpretq_s32_u32(vaddq_u32(mid, side)), 1); + right = vshrq_n_s32(vreinterpretq_s32_u32(vsubq_u32(mid, side)), 1); left = vshrq_n_s32(left, 16); right = vshrq_n_s32(right, 16); @@ -9082,13 +10084,13 @@ static DRFLAC_INLINE void drflac_read_pcm_frames_s16__decode_mid_side__neon(drfl } for (i = (frameCount4 << 2); i < frameCount; ++i) { - drflac_int32 mid = pInputSamples0[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - drflac_int32 side = pInputSamples1[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + drflac_uint32 mid = pInputSamples0U32[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + drflac_uint32 side = pInputSamples1U32[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; - mid = (((drflac_uint32)mid) << 1) | (side & 0x01); + mid = (mid << 1) | (side & 0x01); - pOutputSamples[i*2+0] = (drflac_int16)(((mid + side) >> 1) >> 16); - pOutputSamples[i*2+1] = (drflac_int16)(((mid - side) >> 1) >> 16); + pOutputSamples[i*2+0] = (drflac_int16)(((drflac_int32)(mid + side) >> 1) >> 16); + pOutputSamples[i*2+1] = (drflac_int16)(((drflac_int32)(mid - side) >> 1) >> 16); } } else { int32x4_t shift4; @@ -9097,18 +10099,18 @@ static DRFLAC_INLINE void drflac_read_pcm_frames_s16__decode_mid_side__neon(drfl shift4 = vdupq_n_s32(shift); for (i = 0; i < frameCount4; ++i) { - int32x4_t mid; - int32x4_t side; + uint32x4_t mid; + uint32x4_t side; int32x4_t left; int32x4_t right; - mid = vshlq_s32(vld1q_s32(pInputSamples0 + i*4), wbpsShift0_4); - side = vshlq_s32(vld1q_s32(pInputSamples1 + i*4), wbpsShift1_4); + mid = vshlq_u32(vld1q_u32(pInputSamples0U32 + i*4), wbpsShift0_4); + side = vshlq_u32(vld1q_u32(pInputSamples1U32 + i*4), wbpsShift1_4); - mid = vorrq_s32(vshlq_n_s32(mid, 1), vandq_s32(side, vdupq_n_s32(1))); + mid = vorrq_u32(vshlq_n_u32(mid, 1), vandq_u32(side, vdupq_n_u32(1))); - left = vshlq_s32(vaddq_s32(mid, side), shift4); - right = vshlq_s32(vsubq_s32(mid, side), shift4); + left = vreinterpretq_s32_u32(vshlq_u32(vaddq_u32(mid, side), shift4)); + right = vreinterpretq_s32_u32(vshlq_u32(vsubq_u32(mid, side), shift4)); left = vshrq_n_s32(left, 16); right = vshrq_n_s32(right, 16); @@ -9117,10 +10119,10 @@ static DRFLAC_INLINE void drflac_read_pcm_frames_s16__decode_mid_side__neon(drfl } for (i = (frameCount4 << 2); i < frameCount; ++i) { - drflac_int32 mid = pInputSamples0[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - drflac_int32 side = pInputSamples1[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + drflac_uint32 mid = pInputSamples0U32[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + drflac_uint32 side = pInputSamples1U32[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; - mid = (((drflac_uint32)mid) << 1) | (side & 0x01); + mid = (mid << 1) | (side & 0x01); pOutputSamples[i*2+0] = (drflac_int16)(((mid + side) << shift) >> 16); pOutputSamples[i*2+1] = (drflac_int16)(((mid - side) << shift) >> 16); @@ -9129,7 +10131,7 @@ static DRFLAC_INLINE void drflac_read_pcm_frames_s16__decode_mid_side__neon(drfl } #endif -static DRFLAC_INLINE void drflac_read_pcm_frames_s16__decode_mid_side(drflac* pFlac, drflac_uint64 frameCount, drflac_int32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int16* pOutputSamples) +static DRFLAC_INLINE void drflac_read_pcm_frames_s16__decode_mid_side(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int16* pOutputSamples) { #if defined(DRFLAC_SUPPORT_SSE2) if (drflac__gIsSSE2Supported && pFlac->bitsPerSample <= 24) { @@ -9152,33 +10154,34 @@ static DRFLAC_INLINE void drflac_read_pcm_frames_s16__decode_mid_side(drflac* pF #if 0 -static DRFLAC_INLINE void drflac_read_pcm_frames_s16__decode_independent_stereo__reference(drflac* pFlac, drflac_uint64 frameCount, drflac_int32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int16* pOutputSamples) +static DRFLAC_INLINE void drflac_read_pcm_frames_s16__decode_independent_stereo__reference(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int16* pOutputSamples) { for (drflac_uint64 i = 0; i < frameCount; ++i) { - pOutputSamples[i*2+0] = (drflac_int16)((pInputSamples0[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample)) >> 16); - pOutputSamples[i*2+1] = (drflac_int16)((pInputSamples1[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample)) >> 16); + pOutputSamples[i*2+0] = (drflac_int16)((drflac_int32)((drflac_uint32)pInputSamples0[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample)) >> 16); + pOutputSamples[i*2+1] = (drflac_int16)((drflac_int32)((drflac_uint32)pInputSamples1[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample)) >> 16); } } #endif -static DRFLAC_INLINE void drflac_read_pcm_frames_s16__decode_independent_stereo__scalar(drflac* pFlac, drflac_uint64 frameCount, drflac_int32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int16* pOutputSamples) +static DRFLAC_INLINE void drflac_read_pcm_frames_s16__decode_independent_stereo__scalar(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int16* pOutputSamples) { drflac_uint64 i; drflac_uint64 frameCount4 = frameCount >> 2; - - int shift0 = (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample); - int shift1 = (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample); + const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0; + const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1; + drflac_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + drflac_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; for (i = 0; i < frameCount4; ++i) { - drflac_int32 tempL0 = pInputSamples0[i*4+0] << shift0; - drflac_int32 tempL1 = pInputSamples0[i*4+1] << shift0; - drflac_int32 tempL2 = pInputSamples0[i*4+2] << shift0; - drflac_int32 tempL3 = pInputSamples0[i*4+3] << shift0; + drflac_uint32 tempL0 = pInputSamples0U32[i*4+0] << shift0; + drflac_uint32 tempL1 = pInputSamples0U32[i*4+1] << shift0; + drflac_uint32 tempL2 = pInputSamples0U32[i*4+2] << shift0; + drflac_uint32 tempL3 = pInputSamples0U32[i*4+3] << shift0; - drflac_int32 tempR0 = pInputSamples1[i*4+0] << shift1; - drflac_int32 tempR1 = pInputSamples1[i*4+1] << shift1; - drflac_int32 tempR2 = pInputSamples1[i*4+2] << shift1; - drflac_int32 tempR3 = pInputSamples1[i*4+3] << shift1; + drflac_uint32 tempR0 = pInputSamples1U32[i*4+0] << shift1; + drflac_uint32 tempR1 = pInputSamples1U32[i*4+1] << shift1; + drflac_uint32 tempR2 = pInputSamples1U32[i*4+2] << shift1; + drflac_uint32 tempR3 = pInputSamples1U32[i*4+3] << shift1; tempL0 >>= 16; tempL1 >>= 16; @@ -9201,19 +10204,20 @@ static DRFLAC_INLINE void drflac_read_pcm_frames_s16__decode_independent_stereo_ } for (i = (frameCount4 << 2); i < frameCount; ++i) { - pOutputSamples[i*2+0] = (drflac_int16)((pInputSamples0[i] << shift0) >> 16); - pOutputSamples[i*2+1] = (drflac_int16)((pInputSamples1[i] << shift1) >> 16); + pOutputSamples[i*2+0] = (drflac_int16)((pInputSamples0U32[i] << shift0) >> 16); + pOutputSamples[i*2+1] = (drflac_int16)((pInputSamples1U32[i] << shift1) >> 16); } } #if defined(DRFLAC_SUPPORT_SSE2) -static DRFLAC_INLINE void drflac_read_pcm_frames_s16__decode_independent_stereo__sse2(drflac* pFlac, drflac_uint64 frameCount, drflac_int32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int16* pOutputSamples) +static DRFLAC_INLINE void drflac_read_pcm_frames_s16__decode_independent_stereo__sse2(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int16* pOutputSamples) { drflac_uint64 i; drflac_uint64 frameCount4 = frameCount >> 2; - - drflac_int32 shift0 = (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample); - drflac_int32 shift1 = (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample); + const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0; + const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1; + drflac_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + drflac_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; for (i = 0; i < frameCount4; ++i) { __m128i left = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples0 + i), shift0); @@ -9227,20 +10231,21 @@ static DRFLAC_INLINE void drflac_read_pcm_frames_s16__decode_independent_stereo_ } for (i = (frameCount4 << 2); i < frameCount; ++i) { - pOutputSamples[i*2+0] = (drflac_int16)((pInputSamples0[i] << shift0) >> 16); - pOutputSamples[i*2+1] = (drflac_int16)((pInputSamples1[i] << shift1) >> 16); + pOutputSamples[i*2+0] = (drflac_int16)((pInputSamples0U32[i] << shift0) >> 16); + pOutputSamples[i*2+1] = (drflac_int16)((pInputSamples1U32[i] << shift1) >> 16); } } #endif #if defined(DRFLAC_SUPPORT_NEON) -static DRFLAC_INLINE void drflac_read_pcm_frames_s16__decode_independent_stereo__neon(drflac* pFlac, drflac_uint64 frameCount, drflac_int32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int16* pOutputSamples) +static DRFLAC_INLINE void drflac_read_pcm_frames_s16__decode_independent_stereo__neon(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int16* pOutputSamples) { drflac_uint64 i; drflac_uint64 frameCount4 = frameCount >> 2; - - drflac_int32 shift0 = (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample); - drflac_int32 shift1 = (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample); + const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0; + const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1; + drflac_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + drflac_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; int32x4_t shift0_4 = vdupq_n_s32(shift0); int32x4_t shift1_4 = vdupq_n_s32(shift1); @@ -9249,8 +10254,8 @@ static DRFLAC_INLINE void drflac_read_pcm_frames_s16__decode_independent_stereo_ int32x4_t left; int32x4_t right; - left = vshlq_s32(vld1q_s32(pInputSamples0 + i*4), shift0_4); - right = vshlq_s32(vld1q_s32(pInputSamples1 + i*4), shift1_4); + left = vreinterpretq_s32_u32(vshlq_u32(vld1q_u32(pInputSamples0U32 + i*4), shift0_4)); + right = vreinterpretq_s32_u32(vshlq_u32(vld1q_u32(pInputSamples1U32 + i*4), shift1_4)); left = vshrq_n_s32(left, 16); right = vshrq_n_s32(right, 16); @@ -9259,13 +10264,13 @@ static DRFLAC_INLINE void drflac_read_pcm_frames_s16__decode_independent_stereo_ } for (i = (frameCount4 << 2); i < frameCount; ++i) { - pOutputSamples[i*2+0] = (drflac_int16)((pInputSamples0[i] << shift0) >> 16); - pOutputSamples[i*2+1] = (drflac_int16)((pInputSamples1[i] << shift1) >> 16); + pOutputSamples[i*2+0] = (drflac_int16)((pInputSamples0U32[i] << shift0) >> 16); + pOutputSamples[i*2+1] = (drflac_int16)((pInputSamples1U32[i] << shift1) >> 16); } } #endif -static DRFLAC_INLINE void drflac_read_pcm_frames_s16__decode_independent_stereo(drflac* pFlac, drflac_uint64 frameCount, drflac_int32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int16* pOutputSamples) +static DRFLAC_INLINE void drflac_read_pcm_frames_s16__decode_independent_stereo(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int16* pOutputSamples) { #if defined(DRFLAC_SUPPORT_SSE2) if (drflac__gIsSSE2Supported && pFlac->bitsPerSample <= 24) { @@ -9286,10 +10291,10 @@ static DRFLAC_INLINE void drflac_read_pcm_frames_s16__decode_independent_stereo( } } -drflac_uint64 drflac_read_pcm_frames_s16(drflac* pFlac, drflac_uint64 framesToRead, drflac_int16* pBufferOut) +DRFLAC_API drflac_uint64 drflac_read_pcm_frames_s16(drflac* pFlac, drflac_uint64 framesToRead, drflac_int16* pBufferOut) { drflac_uint64 framesRead; - drflac_int32 unusedBitsPerSample; + drflac_uint32 unusedBitsPerSample; if (pFlac == NULL || framesToRead == 0) { return 0; @@ -9299,6 +10304,7 @@ drflac_uint64 drflac_read_pcm_frames_s16(drflac* pFlac, drflac_uint64 framesToRe return drflac__seek_forward_by_pcm_frames(pFlac, framesToRead); } + DRFLAC_ASSERT(pFlac->bitsPerSample <= 32); unusedBitsPerSample = 32 - pFlac->bitsPerSample; framesRead = 0; @@ -9369,81 +10375,81 @@ drflac_uint64 drflac_read_pcm_frames_s16(drflac* pFlac, drflac_uint64 framesToRe #if 0 -static DRFLAC_INLINE void drflac_read_pcm_frames_f32__decode_left_side__reference(drflac* pFlac, drflac_uint64 frameCount, drflac_int32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, float* pOutputSamples) +static DRFLAC_INLINE void drflac_read_pcm_frames_f32__decode_left_side__reference(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, float* pOutputSamples) { drflac_uint64 i; for (i = 0; i < frameCount; ++i) { - drflac_int32 left = pInputSamples0[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample); - drflac_int32 side = pInputSamples1[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample); - drflac_int32 right = left - side; + drflac_uint32 left = (drflac_uint32)pInputSamples0[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample); + drflac_uint32 side = (drflac_uint32)pInputSamples1[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample); + drflac_uint32 right = left - side; - pOutputSamples[i*2+0] = (float)(left / 2147483648.0); - pOutputSamples[i*2+1] = (float)(right / 2147483648.0); + pOutputSamples[i*2+0] = (float)((drflac_int32)left / 2147483648.0); + pOutputSamples[i*2+1] = (float)((drflac_int32)right / 2147483648.0); } } #endif -static DRFLAC_INLINE void drflac_read_pcm_frames_f32__decode_left_side__scalar(drflac* pFlac, drflac_uint64 frameCount, drflac_int32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, float* pOutputSamples) +static DRFLAC_INLINE void drflac_read_pcm_frames_f32__decode_left_side__scalar(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, float* pOutputSamples) { drflac_uint64 i; drflac_uint64 frameCount4 = frameCount >> 2; + const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0; + const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1; + drflac_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + drflac_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; float factor = 1 / 2147483648.0; - drflac_int32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - drflac_int32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; for (i = 0; i < frameCount4; ++i) { - drflac_int32 left0 = pInputSamples0[i*4+0] << shift0; - drflac_int32 left1 = pInputSamples0[i*4+1] << shift0; - drflac_int32 left2 = pInputSamples0[i*4+2] << shift0; - drflac_int32 left3 = pInputSamples0[i*4+3] << shift0; - - drflac_int32 side0 = pInputSamples1[i*4+0] << shift1; - drflac_int32 side1 = pInputSamples1[i*4+1] << shift1; - drflac_int32 side2 = pInputSamples1[i*4+2] << shift1; - drflac_int32 side3 = pInputSamples1[i*4+3] << shift1; - - drflac_int32 right0 = left0 - side0; - drflac_int32 right1 = left1 - side1; - drflac_int32 right2 = left2 - side2; - drflac_int32 right3 = left3 - side3; - - pOutputSamples[i*8+0] = left0 * factor; - pOutputSamples[i*8+1] = right0 * factor; - pOutputSamples[i*8+2] = left1 * factor; - pOutputSamples[i*8+3] = right1 * factor; - pOutputSamples[i*8+4] = left2 * factor; - pOutputSamples[i*8+5] = right2 * factor; - pOutputSamples[i*8+6] = left3 * factor; - pOutputSamples[i*8+7] = right3 * factor; + drflac_uint32 left0 = pInputSamples0U32[i*4+0] << shift0; + drflac_uint32 left1 = pInputSamples0U32[i*4+1] << shift0; + drflac_uint32 left2 = pInputSamples0U32[i*4+2] << shift0; + drflac_uint32 left3 = pInputSamples0U32[i*4+3] << shift0; + + drflac_uint32 side0 = pInputSamples1U32[i*4+0] << shift1; + drflac_uint32 side1 = pInputSamples1U32[i*4+1] << shift1; + drflac_uint32 side2 = pInputSamples1U32[i*4+2] << shift1; + drflac_uint32 side3 = pInputSamples1U32[i*4+3] << shift1; + + drflac_uint32 right0 = left0 - side0; + drflac_uint32 right1 = left1 - side1; + drflac_uint32 right2 = left2 - side2; + drflac_uint32 right3 = left3 - side3; + + pOutputSamples[i*8+0] = (drflac_int32)left0 * factor; + pOutputSamples[i*8+1] = (drflac_int32)right0 * factor; + pOutputSamples[i*8+2] = (drflac_int32)left1 * factor; + pOutputSamples[i*8+3] = (drflac_int32)right1 * factor; + pOutputSamples[i*8+4] = (drflac_int32)left2 * factor; + pOutputSamples[i*8+5] = (drflac_int32)right2 * factor; + pOutputSamples[i*8+6] = (drflac_int32)left3 * factor; + pOutputSamples[i*8+7] = (drflac_int32)right3 * factor; } for (i = (frameCount4 << 2); i < frameCount; ++i) { - drflac_int32 left = pInputSamples0[i] << shift0; - drflac_int32 side = pInputSamples1[i] << shift1; - drflac_int32 right = left - side; + drflac_uint32 left = pInputSamples0U32[i] << shift0; + drflac_uint32 side = pInputSamples1U32[i] << shift1; + drflac_uint32 right = left - side; - pOutputSamples[i*2+0] = (float)(left * factor); - pOutputSamples[i*2+1] = (float)(right * factor); + pOutputSamples[i*2+0] = (drflac_int32)left * factor; + pOutputSamples[i*2+1] = (drflac_int32)right * factor; } } #if defined(DRFLAC_SUPPORT_SSE2) -static DRFLAC_INLINE void drflac_read_pcm_frames_f32__decode_left_side__sse2(drflac* pFlac, drflac_uint64 frameCount, drflac_int32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, float* pOutputSamples) +static DRFLAC_INLINE void drflac_read_pcm_frames_f32__decode_left_side__sse2(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, float* pOutputSamples) { - drflac_uint64 frameCount4; - drflac_int32 shift0; - drflac_int32 shift1; drflac_uint64 i; + drflac_uint64 frameCount4 = frameCount >> 2; + const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0; + const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1; + drflac_uint32 shift0 = (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample) - 8; + drflac_uint32 shift1 = (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample) - 8; __m128 factor; DRFLAC_ASSERT(pFlac->bitsPerSample <= 24); - frameCount4 = frameCount >> 2; - factor = _mm_set1_ps(1.0f / 8388608.0f); - shift0 = (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample) - 8; - shift1 = (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample) - 8; for (i = 0; i < frameCount4; ++i) { __m128i left = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples0 + i), shift0); @@ -9457,67 +10463,63 @@ static DRFLAC_INLINE void drflac_read_pcm_frames_f32__decode_left_side__sse2(drf } for (i = (frameCount4 << 2); i < frameCount; ++i) { - drflac_int32 left = pInputSamples0[i] << shift0; - drflac_int32 side = pInputSamples1[i] << shift1; - drflac_int32 right = left - side; + drflac_uint32 left = pInputSamples0U32[i] << shift0; + drflac_uint32 side = pInputSamples1U32[i] << shift1; + drflac_uint32 right = left - side; - pOutputSamples[i*2+0] = (float)(left / 8388608.0f); - pOutputSamples[i*2+1] = (float)(right / 8388608.0f); + pOutputSamples[i*2+0] = (drflac_int32)left / 8388608.0f; + pOutputSamples[i*2+1] = (drflac_int32)right / 8388608.0f; } } #endif #if defined(DRFLAC_SUPPORT_NEON) -static DRFLAC_INLINE void drflac_read_pcm_frames_f32__decode_left_side__neon(drflac* pFlac, drflac_uint64 frameCount, drflac_int32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, float* pOutputSamples) +static DRFLAC_INLINE void drflac_read_pcm_frames_f32__decode_left_side__neon(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, float* pOutputSamples) { - drflac_uint64 frameCount4; - drflac_int32 shift0; - drflac_int32 shift1; drflac_uint64 i; + drflac_uint64 frameCount4 = frameCount >> 2; + const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0; + const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1; + drflac_uint32 shift0 = (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample) - 8; + drflac_uint32 shift1 = (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample) - 8; float32x4_t factor4; int32x4_t shift0_4; int32x4_t shift1_4; DRFLAC_ASSERT(pFlac->bitsPerSample <= 24); - frameCount4 = frameCount >> 2; - - factor4 = vdupq_n_f32(1.0f / 8388608.0f); - - shift0 = (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample) - 8; - shift1 = (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample) - 8; - + factor4 = vdupq_n_f32(1.0f / 8388608.0f); shift0_4 = vdupq_n_s32(shift0); shift1_4 = vdupq_n_s32(shift1); for (i = 0; i < frameCount4; ++i) { - int32x4_t left; - int32x4_t side; - int32x4_t right; + uint32x4_t left; + uint32x4_t side; + uint32x4_t right; float32x4_t leftf; float32x4_t rightf; - left = vshlq_s32(vld1q_s32(pInputSamples0 + i*4), shift0_4); - side = vshlq_s32(vld1q_s32(pInputSamples1 + i*4), shift1_4); - right = vsubq_s32(left, side); - leftf = vmulq_f32(vcvtq_f32_s32(left), factor4); - rightf = vmulq_f32(vcvtq_f32_s32(right), factor4); + left = vshlq_u32(vld1q_u32(pInputSamples0U32 + i*4), shift0_4); + side = vshlq_u32(vld1q_u32(pInputSamples1U32 + i*4), shift1_4); + right = vsubq_u32(left, side); + leftf = vmulq_f32(vcvtq_f32_s32(vreinterpretq_s32_u32(left)), factor4); + rightf = vmulq_f32(vcvtq_f32_s32(vreinterpretq_s32_u32(right)), factor4); drflac__vst2q_f32(pOutputSamples + i*8, vzipq_f32(leftf, rightf)); } for (i = (frameCount4 << 2); i < frameCount; ++i) { - drflac_int32 left = pInputSamples0[i] << shift0; - drflac_int32 side = pInputSamples1[i] << shift1; - drflac_int32 right = left - side; + drflac_uint32 left = pInputSamples0U32[i] << shift0; + drflac_uint32 side = pInputSamples1U32[i] << shift1; + drflac_uint32 right = left - side; - pOutputSamples[i*2+0] = (float)(left / 8388608.0f); - pOutputSamples[i*2+1] = (float)(right / 8388608.0f); + pOutputSamples[i*2+0] = (drflac_int32)left / 8388608.0f; + pOutputSamples[i*2+1] = (drflac_int32)right / 8388608.0f; } } #endif -static DRFLAC_INLINE void drflac_read_pcm_frames_f32__decode_left_side(drflac* pFlac, drflac_uint64 frameCount, drflac_int32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, float* pOutputSamples) +static DRFLAC_INLINE void drflac_read_pcm_frames_f32__decode_left_side(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, float* pOutputSamples) { #if defined(DRFLAC_SUPPORT_SSE2) if (drflac__gIsSSE2Supported && pFlac->bitsPerSample <= 24) { @@ -9540,81 +10542,80 @@ static DRFLAC_INLINE void drflac_read_pcm_frames_f32__decode_left_side(drflac* p #if 0 -static DRFLAC_INLINE void drflac_read_pcm_frames_f32__decode_right_side__reference(drflac* pFlac, drflac_uint64 frameCount, drflac_int32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, float* pOutputSamples) +static DRFLAC_INLINE void drflac_read_pcm_frames_f32__decode_right_side__reference(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, float* pOutputSamples) { drflac_uint64 i; for (i = 0; i < frameCount; ++i) { - drflac_int32 side = pInputSamples0[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample); - drflac_int32 right = pInputSamples1[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample); - drflac_int32 left = right + side; + drflac_uint32 side = (drflac_uint32)pInputSamples0[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample); + drflac_uint32 right = (drflac_uint32)pInputSamples1[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample); + drflac_uint32 left = right + side; - pOutputSamples[i*2+0] = (float)(left / 2147483648.0); - pOutputSamples[i*2+1] = (float)(right / 2147483648.0); + pOutputSamples[i*2+0] = (float)((drflac_int32)left / 2147483648.0); + pOutputSamples[i*2+1] = (float)((drflac_int32)right / 2147483648.0); } } #endif -static DRFLAC_INLINE void drflac_read_pcm_frames_f32__decode_right_side__scalar(drflac* pFlac, drflac_uint64 frameCount, drflac_int32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, float* pOutputSamples) +static DRFLAC_INLINE void drflac_read_pcm_frames_f32__decode_right_side__scalar(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, float* pOutputSamples) { drflac_uint64 i; drflac_uint64 frameCount4 = frameCount >> 2; - + const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0; + const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1; + drflac_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + drflac_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; float factor = 1 / 2147483648.0; - drflac_int32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - drflac_int32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; for (i = 0; i < frameCount4; ++i) { - drflac_int32 side0 = pInputSamples0[i*4+0] << shift0; - drflac_int32 side1 = pInputSamples0[i*4+1] << shift0; - drflac_int32 side2 = pInputSamples0[i*4+2] << shift0; - drflac_int32 side3 = pInputSamples0[i*4+3] << shift0; - - drflac_int32 right0 = pInputSamples1[i*4+0] << shift1; - drflac_int32 right1 = pInputSamples1[i*4+1] << shift1; - drflac_int32 right2 = pInputSamples1[i*4+2] << shift1; - drflac_int32 right3 = pInputSamples1[i*4+3] << shift1; - - drflac_int32 left0 = right0 + side0; - drflac_int32 left1 = right1 + side1; - drflac_int32 left2 = right2 + side2; - drflac_int32 left3 = right3 + side3; - - pOutputSamples[i*8+0] = left0 * factor; - pOutputSamples[i*8+1] = right0 * factor; - pOutputSamples[i*8+2] = left1 * factor; - pOutputSamples[i*8+3] = right1 * factor; - pOutputSamples[i*8+4] = left2 * factor; - pOutputSamples[i*8+5] = right2 * factor; - pOutputSamples[i*8+6] = left3 * factor; - pOutputSamples[i*8+7] = right3 * factor; + drflac_uint32 side0 = pInputSamples0U32[i*4+0] << shift0; + drflac_uint32 side1 = pInputSamples0U32[i*4+1] << shift0; + drflac_uint32 side2 = pInputSamples0U32[i*4+2] << shift0; + drflac_uint32 side3 = pInputSamples0U32[i*4+3] << shift0; + + drflac_uint32 right0 = pInputSamples1U32[i*4+0] << shift1; + drflac_uint32 right1 = pInputSamples1U32[i*4+1] << shift1; + drflac_uint32 right2 = pInputSamples1U32[i*4+2] << shift1; + drflac_uint32 right3 = pInputSamples1U32[i*4+3] << shift1; + + drflac_uint32 left0 = right0 + side0; + drflac_uint32 left1 = right1 + side1; + drflac_uint32 left2 = right2 + side2; + drflac_uint32 left3 = right3 + side3; + + pOutputSamples[i*8+0] = (drflac_int32)left0 * factor; + pOutputSamples[i*8+1] = (drflac_int32)right0 * factor; + pOutputSamples[i*8+2] = (drflac_int32)left1 * factor; + pOutputSamples[i*8+3] = (drflac_int32)right1 * factor; + pOutputSamples[i*8+4] = (drflac_int32)left2 * factor; + pOutputSamples[i*8+5] = (drflac_int32)right2 * factor; + pOutputSamples[i*8+6] = (drflac_int32)left3 * factor; + pOutputSamples[i*8+7] = (drflac_int32)right3 * factor; } for (i = (frameCount4 << 2); i < frameCount; ++i) { - drflac_int32 side = pInputSamples0[i] << shift0; - drflac_int32 right = pInputSamples1[i] << shift1; - drflac_int32 left = right + side; + drflac_uint32 side = pInputSamples0U32[i] << shift0; + drflac_uint32 right = pInputSamples1U32[i] << shift1; + drflac_uint32 left = right + side; - pOutputSamples[i*2+0] = (float)(left * factor); - pOutputSamples[i*2+1] = (float)(right * factor); + pOutputSamples[i*2+0] = (drflac_int32)left * factor; + pOutputSamples[i*2+1] = (drflac_int32)right * factor; } } #if defined(DRFLAC_SUPPORT_SSE2) -static DRFLAC_INLINE void drflac_read_pcm_frames_f32__decode_right_side__sse2(drflac* pFlac, drflac_uint64 frameCount, drflac_int32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, float* pOutputSamples) +static DRFLAC_INLINE void drflac_read_pcm_frames_f32__decode_right_side__sse2(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, float* pOutputSamples) { - drflac_uint64 frameCount4; - drflac_int32 shift0; - drflac_int32 shift1; drflac_uint64 i; + drflac_uint64 frameCount4 = frameCount >> 2; + const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0; + const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1; + drflac_uint32 shift0 = (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample) - 8; + drflac_uint32 shift1 = (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample) - 8; __m128 factor; DRFLAC_ASSERT(pFlac->bitsPerSample <= 24); - frameCount4 = frameCount >> 2; - factor = _mm_set1_ps(1.0f / 8388608.0f); - shift0 = (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample) - 8; - shift1 = (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample) - 8; for (i = 0; i < frameCount4; ++i) { __m128i side = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples0 + i), shift0); @@ -9628,67 +10629,63 @@ static DRFLAC_INLINE void drflac_read_pcm_frames_f32__decode_right_side__sse2(dr } for (i = (frameCount4 << 2); i < frameCount; ++i) { - drflac_int32 side = pInputSamples0[i] << shift0; - drflac_int32 right = pInputSamples1[i] << shift1; - drflac_int32 left = right + side; + drflac_uint32 side = pInputSamples0U32[i] << shift0; + drflac_uint32 right = pInputSamples1U32[i] << shift1; + drflac_uint32 left = right + side; - pOutputSamples[i*2+0] = (float)(left / 8388608.0f); - pOutputSamples[i*2+1] = (float)(right / 8388608.0f); + pOutputSamples[i*2+0] = (drflac_int32)left / 8388608.0f; + pOutputSamples[i*2+1] = (drflac_int32)right / 8388608.0f; } } #endif #if defined(DRFLAC_SUPPORT_NEON) -static DRFLAC_INLINE void drflac_read_pcm_frames_f32__decode_right_side__neon(drflac* pFlac, drflac_uint64 frameCount, drflac_int32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, float* pOutputSamples) +static DRFLAC_INLINE void drflac_read_pcm_frames_f32__decode_right_side__neon(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, float* pOutputSamples) { - drflac_uint64 frameCount4; - drflac_int32 shift0; - drflac_int32 shift1; drflac_uint64 i; + drflac_uint64 frameCount4 = frameCount >> 2; + const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0; + const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1; + drflac_uint32 shift0 = (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample) - 8; + drflac_uint32 shift1 = (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample) - 8; float32x4_t factor4; int32x4_t shift0_4; int32x4_t shift1_4; DRFLAC_ASSERT(pFlac->bitsPerSample <= 24); - frameCount4 = frameCount >> 2; - - factor4 = vdupq_n_f32(1.0f / 8388608.0f); - - shift0 = (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample) - 8; - shift1 = (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample) - 8; - + factor4 = vdupq_n_f32(1.0f / 8388608.0f); shift0_4 = vdupq_n_s32(shift0); shift1_4 = vdupq_n_s32(shift1); for (i = 0; i < frameCount4; ++i) { - int32x4_t side; - int32x4_t right; - int32x4_t left; + uint32x4_t side; + uint32x4_t right; + uint32x4_t left; float32x4_t leftf; float32x4_t rightf; - side = vshlq_s32(vld1q_s32(pInputSamples0 + i*4), shift0_4); - right = vshlq_s32(vld1q_s32(pInputSamples1 + i*4), shift1_4); - left = vaddq_s32(right, side); - leftf = vmulq_f32(vcvtq_f32_s32(left), factor4); - rightf = vmulq_f32(vcvtq_f32_s32(right), factor4); + side = vshlq_u32(vld1q_u32(pInputSamples0U32 + i*4), shift0_4); + right = vshlq_u32(vld1q_u32(pInputSamples1U32 + i*4), shift1_4); + left = vaddq_u32(right, side); + leftf = vmulq_f32(vcvtq_f32_s32(vreinterpretq_s32_u32(left)), factor4); + rightf = vmulq_f32(vcvtq_f32_s32(vreinterpretq_s32_u32(right)), factor4); drflac__vst2q_f32(pOutputSamples + i*8, vzipq_f32(leftf, rightf)); } for (i = (frameCount4 << 2); i < frameCount; ++i) { - drflac_int32 side = pInputSamples0[i] << shift0; - drflac_int32 right = pInputSamples1[i] << shift1; - drflac_int32 left = right + side; + drflac_uint32 side = pInputSamples0U32[i] << shift0; + drflac_uint32 right = pInputSamples1U32[i] << shift1; + drflac_uint32 left = right + side; - pOutputSamples[i*2+0] = (float)(left / 8388608.0f); - pOutputSamples[i*2+1] = (float)(right / 8388608.0f); + pOutputSamples[i*2+0] = (drflac_int32)left / 8388608.0f; + pOutputSamples[i*2+1] = (drflac_int32)right / 8388608.0f; } } #endif -static DRFLAC_INLINE void drflac_read_pcm_frames_f32__decode_right_side(drflac* pFlac, drflac_uint64 frameCount, drflac_int32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, float* pOutputSamples) +static DRFLAC_INLINE void drflac_read_pcm_frames_f32__decode_right_side(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, float* pOutputSamples) { #if defined(DRFLAC_SUPPORT_SSE2) if (drflac__gIsSSE2Supported && pFlac->bitsPerSample <= 24) { @@ -9711,149 +10708,149 @@ static DRFLAC_INLINE void drflac_read_pcm_frames_f32__decode_right_side(drflac* #if 0 -static DRFLAC_INLINE void drflac_read_pcm_frames_f32__decode_mid_side__reference(drflac* pFlac, drflac_uint64 frameCount, drflac_int32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, float* pOutputSamples) +static DRFLAC_INLINE void drflac_read_pcm_frames_f32__decode_mid_side__reference(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, float* pOutputSamples) { for (drflac_uint64 i = 0; i < frameCount; ++i) { - drflac_int32 mid = pInputSamples0[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - drflac_int32 side = pInputSamples1[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + drflac_uint32 mid = (drflac_uint32)pInputSamples0[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + drflac_uint32 side = (drflac_uint32)pInputSamples1[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; - mid = (((drflac_uint32)mid) << 1) | (side & 0x01); + mid = (mid << 1) | (side & 0x01); - pOutputSamples[i*2+0] = (float)((((mid + side) >> 1) << (unusedBitsPerSample)) / 2147483648.0); - pOutputSamples[i*2+1] = (float)((((mid - side) >> 1) << (unusedBitsPerSample)) / 2147483648.0); + pOutputSamples[i*2+0] = (float)((((drflac_int32)(mid + side) >> 1) << (unusedBitsPerSample)) / 2147483648.0); + pOutputSamples[i*2+1] = (float)((((drflac_int32)(mid - side) >> 1) << (unusedBitsPerSample)) / 2147483648.0); } } #endif -static DRFLAC_INLINE void drflac_read_pcm_frames_f32__decode_mid_side__scalar(drflac* pFlac, drflac_uint64 frameCount, drflac_int32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, float* pOutputSamples) +static DRFLAC_INLINE void drflac_read_pcm_frames_f32__decode_mid_side__scalar(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, float* pOutputSamples) { drflac_uint64 i; drflac_uint64 frameCount4 = frameCount >> 2; - + const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0; + const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1; + drflac_uint32 shift = unusedBitsPerSample; float factor = 1 / 2147483648.0; - int shift = unusedBitsPerSample; if (shift > 0) { shift -= 1; for (i = 0; i < frameCount4; ++i) { - drflac_int32 temp0L; - drflac_int32 temp1L; - drflac_int32 temp2L; - drflac_int32 temp3L; - drflac_int32 temp0R; - drflac_int32 temp1R; - drflac_int32 temp2R; - drflac_int32 temp3R; - - drflac_int32 mid0 = pInputSamples0[i*4+0] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - drflac_int32 mid1 = pInputSamples0[i*4+1] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - drflac_int32 mid2 = pInputSamples0[i*4+2] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - drflac_int32 mid3 = pInputSamples0[i*4+3] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - - drflac_int32 side0 = pInputSamples1[i*4+0] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; - drflac_int32 side1 = pInputSamples1[i*4+1] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; - drflac_int32 side2 = pInputSamples1[i*4+2] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; - drflac_int32 side3 = pInputSamples1[i*4+3] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; - - mid0 = (((drflac_uint32)mid0) << 1) | (side0 & 0x01); - mid1 = (((drflac_uint32)mid1) << 1) | (side1 & 0x01); - mid2 = (((drflac_uint32)mid2) << 1) | (side2 & 0x01); - mid3 = (((drflac_uint32)mid3) << 1) | (side3 & 0x01); - - temp0L = ((mid0 + side0) << shift); - temp1L = ((mid1 + side1) << shift); - temp2L = ((mid2 + side2) << shift); - temp3L = ((mid3 + side3) << shift); - - temp0R = ((mid0 - side0) << shift); - temp1R = ((mid1 - side1) << shift); - temp2R = ((mid2 - side2) << shift); - temp3R = ((mid3 - side3) << shift); - - pOutputSamples[i*8+0] = (float)(temp0L * factor); - pOutputSamples[i*8+1] = (float)(temp0R * factor); - pOutputSamples[i*8+2] = (float)(temp1L * factor); - pOutputSamples[i*8+3] = (float)(temp1R * factor); - pOutputSamples[i*8+4] = (float)(temp2L * factor); - pOutputSamples[i*8+5] = (float)(temp2R * factor); - pOutputSamples[i*8+6] = (float)(temp3L * factor); - pOutputSamples[i*8+7] = (float)(temp3R * factor); + drflac_uint32 temp0L; + drflac_uint32 temp1L; + drflac_uint32 temp2L; + drflac_uint32 temp3L; + drflac_uint32 temp0R; + drflac_uint32 temp1R; + drflac_uint32 temp2R; + drflac_uint32 temp3R; + + drflac_uint32 mid0 = pInputSamples0U32[i*4+0] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + drflac_uint32 mid1 = pInputSamples0U32[i*4+1] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + drflac_uint32 mid2 = pInputSamples0U32[i*4+2] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + drflac_uint32 mid3 = pInputSamples0U32[i*4+3] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + + drflac_uint32 side0 = pInputSamples1U32[i*4+0] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + drflac_uint32 side1 = pInputSamples1U32[i*4+1] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + drflac_uint32 side2 = pInputSamples1U32[i*4+2] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + drflac_uint32 side3 = pInputSamples1U32[i*4+3] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + + mid0 = (mid0 << 1) | (side0 & 0x01); + mid1 = (mid1 << 1) | (side1 & 0x01); + mid2 = (mid2 << 1) | (side2 & 0x01); + mid3 = (mid3 << 1) | (side3 & 0x01); + + temp0L = (mid0 + side0) << shift; + temp1L = (mid1 + side1) << shift; + temp2L = (mid2 + side2) << shift; + temp3L = (mid3 + side3) << shift; + + temp0R = (mid0 - side0) << shift; + temp1R = (mid1 - side1) << shift; + temp2R = (mid2 - side2) << shift; + temp3R = (mid3 - side3) << shift; + + pOutputSamples[i*8+0] = (drflac_int32)temp0L * factor; + pOutputSamples[i*8+1] = (drflac_int32)temp0R * factor; + pOutputSamples[i*8+2] = (drflac_int32)temp1L * factor; + pOutputSamples[i*8+3] = (drflac_int32)temp1R * factor; + pOutputSamples[i*8+4] = (drflac_int32)temp2L * factor; + pOutputSamples[i*8+5] = (drflac_int32)temp2R * factor; + pOutputSamples[i*8+6] = (drflac_int32)temp3L * factor; + pOutputSamples[i*8+7] = (drflac_int32)temp3R * factor; } } else { for (i = 0; i < frameCount4; ++i) { - drflac_int32 temp0L; - drflac_int32 temp1L; - drflac_int32 temp2L; - drflac_int32 temp3L; - drflac_int32 temp0R; - drflac_int32 temp1R; - drflac_int32 temp2R; - drflac_int32 temp3R; - - drflac_int32 mid0 = pInputSamples0[i*4+0] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - drflac_int32 mid1 = pInputSamples0[i*4+1] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - drflac_int32 mid2 = pInputSamples0[i*4+2] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - drflac_int32 mid3 = pInputSamples0[i*4+3] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - - drflac_int32 side0 = pInputSamples1[i*4+0] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; - drflac_int32 side1 = pInputSamples1[i*4+1] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; - drflac_int32 side2 = pInputSamples1[i*4+2] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; - drflac_int32 side3 = pInputSamples1[i*4+3] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; - - mid0 = (((drflac_uint32)mid0) << 1) | (side0 & 0x01); - mid1 = (((drflac_uint32)mid1) << 1) | (side1 & 0x01); - mid2 = (((drflac_uint32)mid2) << 1) | (side2 & 0x01); - mid3 = (((drflac_uint32)mid3) << 1) | (side3 & 0x01); - - temp0L = ((mid0 + side0) >> 1); - temp1L = ((mid1 + side1) >> 1); - temp2L = ((mid2 + side2) >> 1); - temp3L = ((mid3 + side3) >> 1); - - temp0R = ((mid0 - side0) >> 1); - temp1R = ((mid1 - side1) >> 1); - temp2R = ((mid2 - side2) >> 1); - temp3R = ((mid3 - side3) >> 1); - - pOutputSamples[i*8+0] = (float)(temp0L * factor); - pOutputSamples[i*8+1] = (float)(temp0R * factor); - pOutputSamples[i*8+2] = (float)(temp1L * factor); - pOutputSamples[i*8+3] = (float)(temp1R * factor); - pOutputSamples[i*8+4] = (float)(temp2L * factor); - pOutputSamples[i*8+5] = (float)(temp2R * factor); - pOutputSamples[i*8+6] = (float)(temp3L * factor); - pOutputSamples[i*8+7] = (float)(temp3R * factor); + drflac_uint32 temp0L; + drflac_uint32 temp1L; + drflac_uint32 temp2L; + drflac_uint32 temp3L; + drflac_uint32 temp0R; + drflac_uint32 temp1R; + drflac_uint32 temp2R; + drflac_uint32 temp3R; + + drflac_uint32 mid0 = pInputSamples0U32[i*4+0] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + drflac_uint32 mid1 = pInputSamples0U32[i*4+1] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + drflac_uint32 mid2 = pInputSamples0U32[i*4+2] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + drflac_uint32 mid3 = pInputSamples0U32[i*4+3] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + + drflac_uint32 side0 = pInputSamples1U32[i*4+0] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + drflac_uint32 side1 = pInputSamples1U32[i*4+1] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + drflac_uint32 side2 = pInputSamples1U32[i*4+2] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + drflac_uint32 side3 = pInputSamples1U32[i*4+3] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + + mid0 = (mid0 << 1) | (side0 & 0x01); + mid1 = (mid1 << 1) | (side1 & 0x01); + mid2 = (mid2 << 1) | (side2 & 0x01); + mid3 = (mid3 << 1) | (side3 & 0x01); + + temp0L = (drflac_uint32)((drflac_int32)(mid0 + side0) >> 1); + temp1L = (drflac_uint32)((drflac_int32)(mid1 + side1) >> 1); + temp2L = (drflac_uint32)((drflac_int32)(mid2 + side2) >> 1); + temp3L = (drflac_uint32)((drflac_int32)(mid3 + side3) >> 1); + + temp0R = (drflac_uint32)((drflac_int32)(mid0 - side0) >> 1); + temp1R = (drflac_uint32)((drflac_int32)(mid1 - side1) >> 1); + temp2R = (drflac_uint32)((drflac_int32)(mid2 - side2) >> 1); + temp3R = (drflac_uint32)((drflac_int32)(mid3 - side3) >> 1); + + pOutputSamples[i*8+0] = (drflac_int32)temp0L * factor; + pOutputSamples[i*8+1] = (drflac_int32)temp0R * factor; + pOutputSamples[i*8+2] = (drflac_int32)temp1L * factor; + pOutputSamples[i*8+3] = (drflac_int32)temp1R * factor; + pOutputSamples[i*8+4] = (drflac_int32)temp2L * factor; + pOutputSamples[i*8+5] = (drflac_int32)temp2R * factor; + pOutputSamples[i*8+6] = (drflac_int32)temp3L * factor; + pOutputSamples[i*8+7] = (drflac_int32)temp3R * factor; } } for (i = (frameCount4 << 2); i < frameCount; ++i) { - int mid = pInputSamples0[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - int side = pInputSamples1[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + drflac_uint32 mid = pInputSamples0U32[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + drflac_uint32 side = pInputSamples1U32[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; - mid = (((drflac_uint32)mid) << 1) | (side & 0x01); + mid = (mid << 1) | (side & 0x01); - pOutputSamples[i*2+0] = (float)((((mid + side) >> 1) << unusedBitsPerSample) * factor); - pOutputSamples[i*2+1] = (float)((((mid - side) >> 1) << unusedBitsPerSample) * factor); + pOutputSamples[i*2+0] = (drflac_int32)((drflac_uint32)((drflac_int32)(mid + side) >> 1) << unusedBitsPerSample) * factor; + pOutputSamples[i*2+1] = (drflac_int32)((drflac_uint32)((drflac_int32)(mid - side) >> 1) << unusedBitsPerSample) * factor; } } #if defined(DRFLAC_SUPPORT_SSE2) -static DRFLAC_INLINE void drflac_read_pcm_frames_f32__decode_mid_side__sse2(drflac* pFlac, drflac_uint64 frameCount, drflac_int32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, float* pOutputSamples) +static DRFLAC_INLINE void drflac_read_pcm_frames_f32__decode_mid_side__sse2(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, float* pOutputSamples) { drflac_uint64 i; - drflac_uint64 frameCount4; + drflac_uint64 frameCount4 = frameCount >> 2; + const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0; + const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1; + drflac_uint32 shift = unusedBitsPerSample - 8; float factor; - drflac_int32 shift; __m128 factor128; DRFLAC_ASSERT(pFlac->bitsPerSample <= 24); - frameCount4 = frameCount >> 2; - factor = 1.0f / 8388608.0f; - factor128 = _mm_set1_ps(1.0f / 8388608.0f); + factor128 = _mm_set1_ps(factor); - shift = unusedBitsPerSample - 8; if (shift == 0) { for (i = 0; i < frameCount4; ++i) { __m128i mid; @@ -9879,13 +10876,13 @@ static DRFLAC_INLINE void drflac_read_pcm_frames_f32__decode_mid_side__sse2(drfl } for (i = (frameCount4 << 2); i < frameCount; ++i) { - drflac_int32 mid = pInputSamples0[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - drflac_int32 side = pInputSamples1[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + drflac_uint32 mid = pInputSamples0U32[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + drflac_uint32 side = pInputSamples1U32[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; - mid = (((drflac_uint32)mid) << 1) | (side & 0x01); + mid = (mid << 1) | (side & 0x01); - pOutputSamples[i*2+0] = (float)(((mid + side) >> 1) * factor); - pOutputSamples[i*2+1] = (float)(((mid - side) >> 1) * factor); + pOutputSamples[i*2+0] = ((drflac_int32)(mid + side) >> 1) * factor; + pOutputSamples[i*2+1] = ((drflac_int32)(mid - side) >> 1) * factor; } } else { shift -= 1; @@ -9913,25 +10910,27 @@ static DRFLAC_INLINE void drflac_read_pcm_frames_f32__decode_mid_side__sse2(drfl } for (i = (frameCount4 << 2); i < frameCount; ++i) { - drflac_int32 mid = pInputSamples0[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - drflac_int32 side = pInputSamples1[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + drflac_uint32 mid = pInputSamples0U32[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + drflac_uint32 side = pInputSamples1U32[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; - mid = (((drflac_uint32)mid) << 1) | (side & 0x01); + mid = (mid << 1) | (side & 0x01); - pOutputSamples[i*2+0] = (float)(((mid + side) << shift) * factor); - pOutputSamples[i*2+1] = (float)(((mid - side) << shift) * factor); + pOutputSamples[i*2+0] = (drflac_int32)((mid + side) << shift) * factor; + pOutputSamples[i*2+1] = (drflac_int32)((mid - side) << shift) * factor; } } } #endif #if defined(DRFLAC_SUPPORT_NEON) -static DRFLAC_INLINE void drflac_read_pcm_frames_f32__decode_mid_side__neon(drflac* pFlac, drflac_uint64 frameCount, drflac_int32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, float* pOutputSamples) +static DRFLAC_INLINE void drflac_read_pcm_frames_f32__decode_mid_side__neon(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, float* pOutputSamples) { drflac_uint64 i; - drflac_uint64 frameCount4; + drflac_uint64 frameCount4 = frameCount >> 2; + const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0; + const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1; + drflac_uint32 shift = unusedBitsPerSample - 8; float factor; - drflac_int32 shift; float32x4_t factor4; int32x4_t shift4; int32x4_t wbps0_4; /* Wasted Bits Per Sample */ @@ -9939,15 +10938,11 @@ static DRFLAC_INLINE void drflac_read_pcm_frames_f32__decode_mid_side__neon(drfl DRFLAC_ASSERT(pFlac->bitsPerSample <= 24); - frameCount4 = frameCount >> 2; - - factor = 1.0f / 8388608.0f; + factor = 1.0f / 8388608.0f; factor4 = vdupq_n_f32(factor); - wbps0_4 = vdupq_n_s32(pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample); wbps1_4 = vdupq_n_s32(pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample); - shift = unusedBitsPerSample - 8; if (shift == 0) { for (i = 0; i < frameCount4; ++i) { int32x4_t lefti; @@ -9955,13 +10950,13 @@ static DRFLAC_INLINE void drflac_read_pcm_frames_f32__decode_mid_side__neon(drfl float32x4_t leftf; float32x4_t rightf; - int32x4_t mid = vshlq_s32(vld1q_s32(pInputSamples0 + i*4), wbps0_4); - int32x4_t side = vshlq_s32(vld1q_s32(pInputSamples1 + i*4), wbps1_4); + uint32x4_t mid = vshlq_u32(vld1q_u32(pInputSamples0U32 + i*4), wbps0_4); + uint32x4_t side = vshlq_u32(vld1q_u32(pInputSamples1U32 + i*4), wbps1_4); - mid = vorrq_s32(vshlq_n_s32(mid, 1), vandq_s32(side, vdupq_n_s32(1))); + mid = vorrq_u32(vshlq_n_u32(mid, 1), vandq_u32(side, vdupq_n_u32(1))); - lefti = vshrq_n_s32(vaddq_s32(mid, side), 1); - righti = vshrq_n_s32(vsubq_s32(mid, side), 1); + lefti = vshrq_n_s32(vreinterpretq_s32_u32(vaddq_u32(mid, side)), 1); + righti = vshrq_n_s32(vreinterpretq_s32_u32(vsubq_u32(mid, side)), 1); leftf = vmulq_f32(vcvtq_f32_s32(lefti), factor4); rightf = vmulq_f32(vcvtq_f32_s32(righti), factor4); @@ -9970,32 +10965,32 @@ static DRFLAC_INLINE void drflac_read_pcm_frames_f32__decode_mid_side__neon(drfl } for (i = (frameCount4 << 2); i < frameCount; ++i) { - drflac_int32 mid = pInputSamples0[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - drflac_int32 side = pInputSamples1[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + drflac_uint32 mid = pInputSamples0U32[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + drflac_uint32 side = pInputSamples1U32[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; - mid = (((drflac_uint32)mid) << 1) | (side & 0x01); + mid = (mid << 1) | (side & 0x01); - pOutputSamples[i*2+0] = (float)(((mid + side) >> 1) * factor); - pOutputSamples[i*2+1] = (float)(((mid - side) >> 1) * factor); + pOutputSamples[i*2+0] = ((drflac_int32)(mid + side) >> 1) * factor; + pOutputSamples[i*2+1] = ((drflac_int32)(mid - side) >> 1) * factor; } } else { shift -= 1; shift4 = vdupq_n_s32(shift); for (i = 0; i < frameCount4; ++i) { - int32x4_t mid; - int32x4_t side; + uint32x4_t mid; + uint32x4_t side; int32x4_t lefti; int32x4_t righti; float32x4_t leftf; float32x4_t rightf; - mid = vshlq_s32(vld1q_s32(pInputSamples0 + i*4), wbps0_4); - side = vshlq_s32(vld1q_s32(pInputSamples1 + i*4), wbps1_4); + mid = vshlq_u32(vld1q_u32(pInputSamples0U32 + i*4), wbps0_4); + side = vshlq_u32(vld1q_u32(pInputSamples1U32 + i*4), wbps1_4); - mid = vorrq_s32(vshlq_n_s32(mid, 1), vandq_s32(side, vdupq_n_s32(1))); + mid = vorrq_u32(vshlq_n_u32(mid, 1), vandq_u32(side, vdupq_n_u32(1))); - lefti = vshlq_s32(vaddq_s32(mid, side), shift4); - righti = vshlq_s32(vsubq_s32(mid, side), shift4); + lefti = vreinterpretq_s32_u32(vshlq_u32(vaddq_u32(mid, side), shift4)); + righti = vreinterpretq_s32_u32(vshlq_u32(vsubq_u32(mid, side), shift4)); leftf = vmulq_f32(vcvtq_f32_s32(lefti), factor4); rightf = vmulq_f32(vcvtq_f32_s32(righti), factor4); @@ -10004,19 +10999,19 @@ static DRFLAC_INLINE void drflac_read_pcm_frames_f32__decode_mid_side__neon(drfl } for (i = (frameCount4 << 2); i < frameCount; ++i) { - drflac_int32 mid = pInputSamples0[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - drflac_int32 side = pInputSamples1[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + drflac_uint32 mid = pInputSamples0U32[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + drflac_uint32 side = pInputSamples1U32[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; - mid = (((drflac_uint32)mid) << 1) | (side & 0x01); + mid = (mid << 1) | (side & 0x01); - pOutputSamples[i*2+0] = (float)(((mid + side) << shift) * factor); - pOutputSamples[i*2+1] = (float)(((mid - side) << shift) * factor); + pOutputSamples[i*2+0] = (drflac_int32)((mid + side) << shift) * factor; + pOutputSamples[i*2+1] = (drflac_int32)((mid - side) << shift) * factor; } } } #endif -static DRFLAC_INLINE void drflac_read_pcm_frames_f32__decode_mid_side(drflac* pFlac, drflac_uint64 frameCount, drflac_int32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, float* pOutputSamples) +static DRFLAC_INLINE void drflac_read_pcm_frames_f32__decode_mid_side(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, float* pOutputSamples) { #if defined(DRFLAC_SUPPORT_SSE2) if (drflac__gIsSSE2Supported && pFlac->bitsPerSample <= 24) { @@ -10038,63 +11033,64 @@ static DRFLAC_INLINE void drflac_read_pcm_frames_f32__decode_mid_side(drflac* pF } #if 0 -static DRFLAC_INLINE void drflac_read_pcm_frames_f32__decode_independent_stereo__reference(drflac* pFlac, drflac_uint64 frameCount, drflac_int32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, float* pOutputSamples) +static DRFLAC_INLINE void drflac_read_pcm_frames_f32__decode_independent_stereo__reference(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, float* pOutputSamples) { for (drflac_uint64 i = 0; i < frameCount; ++i) { - pOutputSamples[i*2+0] = (float)((pInputSamples0[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample)) / 2147483648.0); - pOutputSamples[i*2+1] = (float)((pInputSamples1[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample)) / 2147483648.0); + pOutputSamples[i*2+0] = (float)((drflac_int32)((drflac_uint32)pInputSamples0[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample)) / 2147483648.0); + pOutputSamples[i*2+1] = (float)((drflac_int32)((drflac_uint32)pInputSamples1[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample)) / 2147483648.0); } } #endif -static DRFLAC_INLINE void drflac_read_pcm_frames_f32__decode_independent_stereo__scalar(drflac* pFlac, drflac_uint64 frameCount, drflac_int32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, float* pOutputSamples) +static DRFLAC_INLINE void drflac_read_pcm_frames_f32__decode_independent_stereo__scalar(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, float* pOutputSamples) { drflac_uint64 i; drflac_uint64 frameCount4 = frameCount >> 2; - + const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0; + const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1; + drflac_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + drflac_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; float factor = 1 / 2147483648.0; - drflac_int32 shift0 = (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample); - drflac_int32 shift1 = (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample); - for (i = 0; i < frameCount4; ++i) { - drflac_int32 tempL0 = pInputSamples0[i*4+0] << shift0; - drflac_int32 tempL1 = pInputSamples0[i*4+1] << shift0; - drflac_int32 tempL2 = pInputSamples0[i*4+2] << shift0; - drflac_int32 tempL3 = pInputSamples0[i*4+3] << shift0; - - drflac_int32 tempR0 = pInputSamples1[i*4+0] << shift1; - drflac_int32 tempR1 = pInputSamples1[i*4+1] << shift1; - drflac_int32 tempR2 = pInputSamples1[i*4+2] << shift1; - drflac_int32 tempR3 = pInputSamples1[i*4+3] << shift1; - - pOutputSamples[i*8+0] = (float)(tempL0 * factor); - pOutputSamples[i*8+1] = (float)(tempR0 * factor); - pOutputSamples[i*8+2] = (float)(tempL1 * factor); - pOutputSamples[i*8+3] = (float)(tempR1 * factor); - pOutputSamples[i*8+4] = (float)(tempL2 * factor); - pOutputSamples[i*8+5] = (float)(tempR2 * factor); - pOutputSamples[i*8+6] = (float)(tempL3 * factor); - pOutputSamples[i*8+7] = (float)(tempR3 * factor); + drflac_uint32 tempL0 = pInputSamples0U32[i*4+0] << shift0; + drflac_uint32 tempL1 = pInputSamples0U32[i*4+1] << shift0; + drflac_uint32 tempL2 = pInputSamples0U32[i*4+2] << shift0; + drflac_uint32 tempL3 = pInputSamples0U32[i*4+3] << shift0; + + drflac_uint32 tempR0 = pInputSamples1U32[i*4+0] << shift1; + drflac_uint32 tempR1 = pInputSamples1U32[i*4+1] << shift1; + drflac_uint32 tempR2 = pInputSamples1U32[i*4+2] << shift1; + drflac_uint32 tempR3 = pInputSamples1U32[i*4+3] << shift1; + + pOutputSamples[i*8+0] = (drflac_int32)tempL0 * factor; + pOutputSamples[i*8+1] = (drflac_int32)tempR0 * factor; + pOutputSamples[i*8+2] = (drflac_int32)tempL1 * factor; + pOutputSamples[i*8+3] = (drflac_int32)tempR1 * factor; + pOutputSamples[i*8+4] = (drflac_int32)tempL2 * factor; + pOutputSamples[i*8+5] = (drflac_int32)tempR2 * factor; + pOutputSamples[i*8+6] = (drflac_int32)tempL3 * factor; + pOutputSamples[i*8+7] = (drflac_int32)tempR3 * factor; } for (i = (frameCount4 << 2); i < frameCount; ++i) { - pOutputSamples[i*2+0] = (float)((pInputSamples0[i] << shift0) * factor); - pOutputSamples[i*2+1] = (float)((pInputSamples1[i] << shift1) * factor); + pOutputSamples[i*2+0] = (drflac_int32)(pInputSamples0U32[i] << shift0) * factor; + pOutputSamples[i*2+1] = (drflac_int32)(pInputSamples1U32[i] << shift1) * factor; } } #if defined(DRFLAC_SUPPORT_SSE2) -static DRFLAC_INLINE void drflac_read_pcm_frames_f32__decode_independent_stereo__sse2(drflac* pFlac, drflac_uint64 frameCount, drflac_int32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, float* pOutputSamples) +static DRFLAC_INLINE void drflac_read_pcm_frames_f32__decode_independent_stereo__sse2(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, float* pOutputSamples) { drflac_uint64 i; drflac_uint64 frameCount4 = frameCount >> 2; + const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0; + const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1; + drflac_uint32 shift0 = (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample) - 8; + drflac_uint32 shift1 = (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample) - 8; float factor = 1.0f / 8388608.0f; - __m128 factor128 = _mm_set1_ps(1.0f / 8388608.0f); - - drflac_int32 shift0 = (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample) - 8; - drflac_int32 shift1 = (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample) - 8; + __m128 factor128 = _mm_set1_ps(factor); for (i = 0; i < frameCount4; ++i) { __m128i lefti; @@ -10113,26 +11109,26 @@ static DRFLAC_INLINE void drflac_read_pcm_frames_f32__decode_independent_stereo_ } for (i = (frameCount4 << 2); i < frameCount; ++i) { - pOutputSamples[i*2+0] = (float)((pInputSamples0[i] << shift0) * factor); - pOutputSamples[i*2+1] = (float)((pInputSamples1[i] << shift1) * factor); + pOutputSamples[i*2+0] = (drflac_int32)(pInputSamples0U32[i] << shift0) * factor; + pOutputSamples[i*2+1] = (drflac_int32)(pInputSamples1U32[i] << shift1) * factor; } } #endif #if defined(DRFLAC_SUPPORT_NEON) -static DRFLAC_INLINE void drflac_read_pcm_frames_f32__decode_independent_stereo__neon(drflac* pFlac, drflac_uint64 frameCount, drflac_int32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, float* pOutputSamples) +static DRFLAC_INLINE void drflac_read_pcm_frames_f32__decode_independent_stereo__neon(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, float* pOutputSamples) { drflac_uint64 i; drflac_uint64 frameCount4 = frameCount >> 2; + const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0; + const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1; + drflac_uint32 shift0 = (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample) - 8; + drflac_uint32 shift1 = (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample) - 8; float factor = 1.0f / 8388608.0f; float32x4_t factor4 = vdupq_n_f32(factor); - - drflac_int32 shift0 = (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample) - 8; - drflac_int32 shift1 = (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample) - 8; - - int32x4_t shift0_4 = vdupq_n_s32(shift0); - int32x4_t shift1_4 = vdupq_n_s32(shift1); + int32x4_t shift0_4 = vdupq_n_s32(shift0); + int32x4_t shift1_4 = vdupq_n_s32(shift1); for (i = 0; i < frameCount4; ++i) { int32x4_t lefti; @@ -10140,8 +11136,8 @@ static DRFLAC_INLINE void drflac_read_pcm_frames_f32__decode_independent_stereo_ float32x4_t leftf; float32x4_t rightf; - lefti = vshlq_s32(vld1q_s32(pInputSamples0 + i*4), shift0_4); - righti = vshlq_s32(vld1q_s32(pInputSamples1 + i*4), shift1_4); + lefti = vreinterpretq_s32_u32(vshlq_u32(vld1q_u32(pInputSamples0U32 + i*4), shift0_4)); + righti = vreinterpretq_s32_u32(vshlq_u32(vld1q_u32(pInputSamples1U32 + i*4), shift1_4)); leftf = vmulq_f32(vcvtq_f32_s32(lefti), factor4); rightf = vmulq_f32(vcvtq_f32_s32(righti), factor4); @@ -10150,13 +11146,13 @@ static DRFLAC_INLINE void drflac_read_pcm_frames_f32__decode_independent_stereo_ } for (i = (frameCount4 << 2); i < frameCount; ++i) { - pOutputSamples[i*2+0] = (float)((pInputSamples0[i] << shift0) * factor); - pOutputSamples[i*2+1] = (float)((pInputSamples1[i] << shift1) * factor); + pOutputSamples[i*2+0] = (drflac_int32)(pInputSamples0U32[i] << shift0) * factor; + pOutputSamples[i*2+1] = (drflac_int32)(pInputSamples1U32[i] << shift1) * factor; } } #endif -static DRFLAC_INLINE void drflac_read_pcm_frames_f32__decode_independent_stereo(drflac* pFlac, drflac_uint64 frameCount, drflac_int32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, float* pOutputSamples) +static DRFLAC_INLINE void drflac_read_pcm_frames_f32__decode_independent_stereo(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, float* pOutputSamples) { #if defined(DRFLAC_SUPPORT_SSE2) if (drflac__gIsSSE2Supported && pFlac->bitsPerSample <= 24) { @@ -10177,10 +11173,10 @@ static DRFLAC_INLINE void drflac_read_pcm_frames_f32__decode_independent_stereo( } } -drflac_uint64 drflac_read_pcm_frames_f32(drflac* pFlac, drflac_uint64 framesToRead, float* pBufferOut) +DRFLAC_API drflac_uint64 drflac_read_pcm_frames_f32(drflac* pFlac, drflac_uint64 framesToRead, float* pBufferOut) { drflac_uint64 framesRead; - drflac_int32 unusedBitsPerSample; + drflac_uint32 unusedBitsPerSample; if (pFlac == NULL || framesToRead == 0) { return 0; @@ -10190,6 +11186,7 @@ drflac_uint64 drflac_read_pcm_frames_f32(drflac* pFlac, drflac_uint64 framesToRe return drflac__seek_forward_by_pcm_frames(pFlac, framesToRead); } + DRFLAC_ASSERT(pFlac->bitsPerSample <= 32); unusedBitsPerSample = 32 - pFlac->bitsPerSample; framesRead = 0; @@ -10241,7 +11238,8 @@ drflac_uint64 drflac_read_pcm_frames_f32(drflac* pFlac, drflac_uint64 framesToRe for (i = 0; i < frameCountThisIteration; ++i) { unsigned int j; for (j = 0; j < channelCount; ++j) { - pBufferOut[(i*channelCount)+j] = (float)((drflac_uint64)((pFlac->currentFLACFrame.subframes[j].pSamplesS32[iFirstPCMFrame + i]) << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[j].wastedBitsPerSample)) / 2147483648.0); + drflac_int32 sampleS32 = (drflac_int32)((drflac_uint32)(pFlac->currentFLACFrame.subframes[j].pSamplesS32[iFirstPCMFrame + i]) << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[j].wastedBitsPerSample)); + pBufferOut[(i*channelCount)+j] = (float)(sampleS32 / 2147483648.0); } } } @@ -10258,7 +11256,7 @@ drflac_uint64 drflac_read_pcm_frames_f32(drflac* pFlac, drflac_uint64 framesToRe } -drflac_bool32 drflac_seek_to_pcm_frame(drflac* pFlac, drflac_uint64 pcmFrameIndex) +DRFLAC_API drflac_bool32 drflac_seek_to_pcm_frame(drflac* pFlac, drflac_uint64 pcmFrameIndex) { if (pFlac == NULL) { return DRFLAC_FALSE; @@ -10433,7 +11431,7 @@ DRFLAC_DEFINE_FULL_READ_AND_CLOSE(s32, drflac_int32) DRFLAC_DEFINE_FULL_READ_AND_CLOSE(s16, drflac_int16) DRFLAC_DEFINE_FULL_READ_AND_CLOSE(f32, float) -drflac_int32* drflac_open_and_read_pcm_frames_s32(drflac_read_proc onRead, drflac_seek_proc onSeek, void* pUserData, unsigned int* channelsOut, unsigned int* sampleRateOut, drflac_uint64* totalPCMFrameCountOut, const drflac_allocation_callbacks* pAllocationCallbacks) +DRFLAC_API drflac_int32* drflac_open_and_read_pcm_frames_s32(drflac_read_proc onRead, drflac_seek_proc onSeek, void* pUserData, unsigned int* channelsOut, unsigned int* sampleRateOut, drflac_uint64* totalPCMFrameCountOut, const drflac_allocation_callbacks* pAllocationCallbacks) { drflac* pFlac; @@ -10455,7 +11453,7 @@ drflac_int32* drflac_open_and_read_pcm_frames_s32(drflac_read_proc onRead, drfla return drflac__full_read_and_close_s32(pFlac, channelsOut, sampleRateOut, totalPCMFrameCountOut); } -drflac_int16* drflac_open_and_read_pcm_frames_s16(drflac_read_proc onRead, drflac_seek_proc onSeek, void* pUserData, unsigned int* channelsOut, unsigned int* sampleRateOut, drflac_uint64* totalPCMFrameCountOut, const drflac_allocation_callbacks* pAllocationCallbacks) +DRFLAC_API drflac_int16* drflac_open_and_read_pcm_frames_s16(drflac_read_proc onRead, drflac_seek_proc onSeek, void* pUserData, unsigned int* channelsOut, unsigned int* sampleRateOut, drflac_uint64* totalPCMFrameCountOut, const drflac_allocation_callbacks* pAllocationCallbacks) { drflac* pFlac; @@ -10477,7 +11475,7 @@ drflac_int16* drflac_open_and_read_pcm_frames_s16(drflac_read_proc onRead, drfla return drflac__full_read_and_close_s16(pFlac, channelsOut, sampleRateOut, totalPCMFrameCountOut); } -float* drflac_open_and_read_pcm_frames_f32(drflac_read_proc onRead, drflac_seek_proc onSeek, void* pUserData, unsigned int* channelsOut, unsigned int* sampleRateOut, drflac_uint64* totalPCMFrameCountOut, const drflac_allocation_callbacks* pAllocationCallbacks) +DRFLAC_API float* drflac_open_and_read_pcm_frames_f32(drflac_read_proc onRead, drflac_seek_proc onSeek, void* pUserData, unsigned int* channelsOut, unsigned int* sampleRateOut, drflac_uint64* totalPCMFrameCountOut, const drflac_allocation_callbacks* pAllocationCallbacks) { drflac* pFlac; @@ -10500,7 +11498,7 @@ float* drflac_open_and_read_pcm_frames_f32(drflac_read_proc onRead, drflac_seek_ } #ifndef DR_FLAC_NO_STDIO -drflac_int32* drflac_open_file_and_read_pcm_frames_s32(const char* filename, unsigned int* channels, unsigned int* sampleRate, drflac_uint64* totalPCMFrameCount, const drflac_allocation_callbacks* pAllocationCallbacks) +DRFLAC_API drflac_int32* drflac_open_file_and_read_pcm_frames_s32(const char* filename, unsigned int* channels, unsigned int* sampleRate, drflac_uint64* totalPCMFrameCount, const drflac_allocation_callbacks* pAllocationCallbacks) { drflac* pFlac; @@ -10522,7 +11520,7 @@ drflac_int32* drflac_open_file_and_read_pcm_frames_s32(const char* filename, uns return drflac__full_read_and_close_s32(pFlac, channels, sampleRate, totalPCMFrameCount); } -drflac_int16* drflac_open_file_and_read_pcm_frames_s16(const char* filename, unsigned int* channels, unsigned int* sampleRate, drflac_uint64* totalPCMFrameCount, const drflac_allocation_callbacks* pAllocationCallbacks) +DRFLAC_API drflac_int16* drflac_open_file_and_read_pcm_frames_s16(const char* filename, unsigned int* channels, unsigned int* sampleRate, drflac_uint64* totalPCMFrameCount, const drflac_allocation_callbacks* pAllocationCallbacks) { drflac* pFlac; @@ -10544,7 +11542,7 @@ drflac_int16* drflac_open_file_and_read_pcm_frames_s16(const char* filename, uns return drflac__full_read_and_close_s16(pFlac, channels, sampleRate, totalPCMFrameCount); } -float* drflac_open_file_and_read_pcm_frames_f32(const char* filename, unsigned int* channels, unsigned int* sampleRate, drflac_uint64* totalPCMFrameCount, const drflac_allocation_callbacks* pAllocationCallbacks) +DRFLAC_API float* drflac_open_file_and_read_pcm_frames_f32(const char* filename, unsigned int* channels, unsigned int* sampleRate, drflac_uint64* totalPCMFrameCount, const drflac_allocation_callbacks* pAllocationCallbacks) { drflac* pFlac; @@ -10567,7 +11565,7 @@ float* drflac_open_file_and_read_pcm_frames_f32(const char* filename, unsigned i } #endif -drflac_int32* drflac_open_memory_and_read_pcm_frames_s32(const void* data, size_t dataSize, unsigned int* channels, unsigned int* sampleRate, drflac_uint64* totalPCMFrameCount, const drflac_allocation_callbacks* pAllocationCallbacks) +DRFLAC_API drflac_int32* drflac_open_memory_and_read_pcm_frames_s32(const void* data, size_t dataSize, unsigned int* channels, unsigned int* sampleRate, drflac_uint64* totalPCMFrameCount, const drflac_allocation_callbacks* pAllocationCallbacks) { drflac* pFlac; @@ -10589,7 +11587,7 @@ drflac_int32* drflac_open_memory_and_read_pcm_frames_s32(const void* data, size_ return drflac__full_read_and_close_s32(pFlac, channels, sampleRate, totalPCMFrameCount); } -drflac_int16* drflac_open_memory_and_read_pcm_frames_s16(const void* data, size_t dataSize, unsigned int* channels, unsigned int* sampleRate, drflac_uint64* totalPCMFrameCount, const drflac_allocation_callbacks* pAllocationCallbacks) +DRFLAC_API drflac_int16* drflac_open_memory_and_read_pcm_frames_s16(const void* data, size_t dataSize, unsigned int* channels, unsigned int* sampleRate, drflac_uint64* totalPCMFrameCount, const drflac_allocation_callbacks* pAllocationCallbacks) { drflac* pFlac; @@ -10611,7 +11609,7 @@ drflac_int16* drflac_open_memory_and_read_pcm_frames_s16(const void* data, size_ return drflac__full_read_and_close_s16(pFlac, channels, sampleRate, totalPCMFrameCount); } -float* drflac_open_memory_and_read_pcm_frames_f32(const void* data, size_t dataSize, unsigned int* channels, unsigned int* sampleRate, drflac_uint64* totalPCMFrameCount, const drflac_allocation_callbacks* pAllocationCallbacks) +DRFLAC_API float* drflac_open_memory_and_read_pcm_frames_f32(const void* data, size_t dataSize, unsigned int* channels, unsigned int* sampleRate, drflac_uint64* totalPCMFrameCount, const drflac_allocation_callbacks* pAllocationCallbacks) { drflac* pFlac; @@ -10634,7 +11632,7 @@ float* drflac_open_memory_and_read_pcm_frames_f32(const void* data, size_t dataS } -void drflac_free(void* p, const drflac_allocation_callbacks* pAllocationCallbacks) +DRFLAC_API void drflac_free(void* p, const drflac_allocation_callbacks* pAllocationCallbacks) { if (pAllocationCallbacks != NULL) { drflac__free_from_callbacks(p, pAllocationCallbacks); @@ -10646,7 +11644,7 @@ void drflac_free(void* p, const drflac_allocation_callbacks* pAllocationCallback -void drflac_init_vorbis_comment_iterator(drflac_vorbis_comment_iterator* pIter, drflac_uint32 commentCount, const void* pComments) +DRFLAC_API void drflac_init_vorbis_comment_iterator(drflac_vorbis_comment_iterator* pIter, drflac_uint32 commentCount, const void* pComments) { if (pIter == NULL) { return; @@ -10656,7 +11654,7 @@ void drflac_init_vorbis_comment_iterator(drflac_vorbis_comment_iterator* pIter, pIter->pRunningData = (const char*)pComments; } -const char* drflac_next_vorbis_comment(drflac_vorbis_comment_iterator* pIter, drflac_uint32* pCommentLengthOut) +DRFLAC_API const char* drflac_next_vorbis_comment(drflac_vorbis_comment_iterator* pIter, drflac_uint32* pCommentLengthOut) { drflac_int32 length; const char* pComment; @@ -10687,7 +11685,7 @@ const char* drflac_next_vorbis_comment(drflac_vorbis_comment_iterator* pIter, dr -void drflac_init_cuesheet_track_iterator(drflac_cuesheet_track_iterator* pIter, drflac_uint32 trackCount, const void* pTrackData) +DRFLAC_API void drflac_init_cuesheet_track_iterator(drflac_cuesheet_track_iterator* pIter, drflac_uint32 trackCount, const void* pTrackData) { if (pIter == NULL) { return; @@ -10697,7 +11695,7 @@ void drflac_init_cuesheet_track_iterator(drflac_cuesheet_track_iterator* pIter, pIter->pRunningData = (const char*)pTrackData; } -drflac_bool32 drflac_next_cuesheet_track(drflac_cuesheet_track_iterator* pIter, drflac_cuesheet_track* pCuesheetTrack) +DRFLAC_API drflac_bool32 drflac_next_cuesheet_track(drflac_cuesheet_track_iterator* pIter, drflac_cuesheet_track* pCuesheetTrack) { drflac_cuesheet_track cuesheetTrack; const char* pRunningData; @@ -10739,6 +11737,36 @@ drflac_bool32 drflac_next_cuesheet_track(drflac_cuesheet_track_iterator* pIter, /* REVISION HISTORY ================ +v0.12.13 - 2020-05-16 + - Add compile-time and run-time version querying. + - DRFLAC_VERSION_MINOR + - DRFLAC_VERSION_MAJOR + - DRFLAC_VERSION_REVISION + - DRFLAC_VERSION_STRING + - drflac_version() + - drflac_version_string() + +v0.12.12 - 2020-04-30 + - Fix compilation errors with VC6. + +v0.12.11 - 2020-04-19 + - Fix some pedantic warnings. + - Fix some undefined behaviour warnings. + +v0.12.10 - 2020-04-10 + - Fix some bugs when trying to seek with an invalid seek table. + +v0.12.9 - 2020-04-05 + - Fix warnings. + +v0.12.8 - 2020-04-04 + - Add drflac_open_file_w() and drflac_open_file_with_metadata_w(). + - Fix some static analysis warnings. + - Minor documentation updates. + +v0.12.7 - 2020-03-14 + - Fix compilation errors with VC6. + v0.12.6 - 2020-03-07 - Fix compilation error with Visual Studio .NET 2003. diff --git a/ext/dr_mp3.h b/ext/dr_mp3.h @@ -1,88 +1,50 @@ /* MP3 audio decoder. Choice of public domain or MIT-0. See license statements at the end of this file. -dr_mp3 - v0.5.6 - 2020-02-12 +dr_mp3 - v0.6.10 - 2020-05-16 David Reid - mackron@gmail.com -Based off 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. +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 - 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. +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. -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: +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_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() + `drmp3_init()` + `drmp3_init_memory()` + `drmp3_init_file()` -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() +Miscellaneous Changes +--------------------- +Support for loading a file from a `wchar_t` string has been added via the `drmp3_init_file_w()` API. */ /* -USAGE -===== -dr_mp3 is a single-file library. To use it, do something like the following in one .c file. +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: +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 @@ -91,28 +53,27 @@ do something like the following: ... 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 third parameter of drmp3_init_file() in the example above allows you to control the output channel count and sample rate. It -is a pointer to a drmp3_config object. Setting any of the variables of this object to 0 will cause dr_mp3 to use defaults. - -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. +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 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(). +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()`. -OPTIONS -======= +Build Options +============= #define these options before including this file. #define DR_MP3_NO_STDIO @@ -129,32 +90,137 @@ OPTIONS extern "C" { #endif -#include <stddef.h> - -#if defined(_MSC_VER) && _MSC_VER < 1600 -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; -typedef signed __int64 drmp3_int64; -typedef unsigned __int64 drmp3_uint64; +#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 10 +#define DRMP3_VERSION_STRING DRMP3_XSTRINGIFY(DRMP3_VERSION_MAJOR) "." DRMP3_XSTRINGIFY(DRMP3_VERSION_MINOR) "." DRMP3_XSTRINGIFY(DRMP3_VERSION_REVISION) + +#include <stddef.h> /* For size_t. */ + +/* Sized types. Prefer built-in types. Fall back to stdint. */ +#ifdef _MSC_VER + #if defined(__clang__) + #pragma GCC diagnostic push + #pragma GCC diagnostic ignored "-Wlanguage-extension-token" + #pragma GCC diagnostic ignored "-Wlong-long" + #pragma GCC diagnostic ignored "-Wc++11-long-long" + #endif + typedef signed __int8 drmp3_int8; + typedef unsigned __int8 drmp3_uint8; + typedef signed __int16 drmp3_int16; + typedef unsigned __int16 drmp3_uint16; + typedef signed __int32 drmp3_int32; + typedef unsigned __int32 drmp3_uint32; + typedef signed __int64 drmp3_int64; + typedef unsigned __int64 drmp3_uint64; + #if defined(__clang__) + #pragma GCC diagnostic pop + #endif #else -#include <stdint.h> -typedef int8_t drmp3_int8; -typedef uint8_t drmp3_uint8; -typedef int16_t drmp3_int16; -typedef uint16_t drmp3_uint16; -typedef int32_t drmp3_int32; -typedef uint32_t drmp3_uint32; -typedef int64_t drmp3_int64; -typedef uint64_t drmp3_uint64; + #include <stdint.h> + typedef int8_t drmp3_int8; + typedef uint8_t drmp3_uint8; + typedef int16_t drmp3_int16; + typedef uint16_t drmp3_uint16; + typedef int32_t drmp3_int32; + typedef uint32_t drmp3_uint32; + typedef int64_t drmp3_int64; + typedef uint64_t drmp3_uint64; +#endif +typedef drmp3_uint8 drmp3_bool8; +typedef drmp3_uint32 drmp3_bool32; +#define DRMP3_TRUE 1 +#define DRMP3_FALSE 0 + +#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 -typedef drmp3_uint8 drmp3_bool8; -typedef drmp3_uint32 drmp3_bool32; -#define DRMP3_TRUE 1 -#define DRMP3_FALSE 0 + +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 + #define DRMP3_MAX_PCM_FRAMES_PER_MP3_FRAME 1152 #define DRMP3_MAX_SAMPLES_PER_FRAME (DRMP3_MAX_PCM_FRAMES_PER_MP3_FRAME*2) @@ -178,6 +244,11 @@ typedef drmp3_uint32 drmp3_bool32; #define DRMP3_INLINE #endif + +DRMP3_API void drmp3_version(drmp3_uint32* pMajor, drmp3_uint32* pMinor, drmp3_uint32* pRevision); +DRMP3_API const char* drmp3_version_string(); + + /* Low Level Push API ================== @@ -191,17 +262,17 @@ typedef struct { float mdct_overlap[2][9*32], qmf_state[15*2*32]; int reserv, free_format_bytes; - unsigned char header[4], reserv_buf[511]; + drmp3_uint8 header[4], reserv_buf[511]; } drmp3dec; /* Initializes a low level decoder. */ -void drmp3dec_init(drmp3dec *dec); +DRMP3_API void drmp3dec_init(drmp3dec *dec); /* Reads a frame from a low level decoder. */ -int drmp3dec_decode_frame(drmp3dec *dec, const unsigned char *mp3, int mp3_bytes, void *pcm, drmp3dec_frame_info *info); +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. */ -void drmp3dec_f32_to_s16(const float *in, drmp3_int16 *out, int num_samples); +DRMP3_API void drmp3dec_f32_to_s16(const float *in, drmp3_int16 *out, size_t num_samples); @@ -209,57 +280,13 @@ void drmp3dec_f32_to_s16(const float *in, drmp3_int16 *out, int num_samples); Main API (Pull API) =================== */ -#ifndef DR_MP3_DEFAULT_CHANNELS -#define DR_MP3_DEFAULT_CHANNELS 2 +#ifndef DRMP3_DEFAULT_CHANNELS +#define DRMP3_DEFAULT_CHANNELS 2 #endif -#ifndef DR_MP3_DEFAULT_SAMPLE_RATE -#define DR_MP3_DEFAULT_SAMPLE_RATE 44100 +#ifndef DRMP3_DEFAULT_SAMPLE_RATE +#define DRMP3_DEFAULT_SAMPLE_RATE 44100 #endif -typedef struct drmp3_src drmp3_src; -typedef drmp3_uint64 (* drmp3_src_read_proc)(drmp3_src* pSRC, drmp3_uint64 frameCount, void* pFramesOut, void* pUserData); /* Returns the number of frames that were read. */ - -typedef enum -{ - drmp3_src_algorithm_none, - drmp3_src_algorithm_linear -} drmp3_src_algorithm; - -#define DRMP3_SRC_CACHE_SIZE_IN_FRAMES 512 -typedef struct -{ - drmp3_src* pSRC; - float pCachedFrames[2 * DRMP3_SRC_CACHE_SIZE_IN_FRAMES]; - drmp3_uint32 cachedFrameCount; - drmp3_uint32 iNextFrame; -} drmp3_src_cache; - -typedef struct -{ - drmp3_uint32 sampleRateIn; - drmp3_uint32 sampleRateOut; - drmp3_uint32 channels; - drmp3_src_algorithm algorithm; - drmp3_uint32 cacheSizeInFrames; /* The number of frames to read from the client at a time. */ -} drmp3_src_config; - -struct drmp3_src -{ - drmp3_src_config config; - drmp3_src_read_proc onRead; - void* pUserData; - float bin[256]; - drmp3_src_cache cache; /* <-- For simplifying and optimizing client -> memory reading. */ - union - { - struct - { - double alpha; - drmp3_bool32 isPrevFramesLoaded : 1; - drmp3_bool32 isNextFramesLoaded : 1; - } linear; - } algo; -}; typedef enum { @@ -313,8 +340,8 @@ typedef struct typedef struct { - drmp3_uint32 outputChannels; - drmp3_uint32 outputSampleRate; + drmp3_uint32 channels; + drmp3_uint32 sampleRate; } drmp3_config; typedef struct @@ -334,11 +361,11 @@ typedef struct 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_src src; 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 @@ -362,7 +389,7 @@ Close the loader with drmp3_uninit(). See also: drmp3_init_file(), drmp3_init_memory(), drmp3_uninit() */ -drmp3_bool32 drmp3_init(drmp3* pMP3, drmp3_read_proc onRead, drmp3_seek_proc onSeek, void* pUserData, const drmp3_config* pConfig, const drmp3_allocation_callbacks* pAllocationCallbacks); +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. @@ -372,7 +399,7 @@ the lifetime of the drmp3 object. The buffer should contain the contents of the entire MP3 file. */ -drmp3_bool32 drmp3_init_memory(drmp3* pMP3, const void* pData, size_t dataSize, const drmp3_config* pConfig, const drmp3_allocation_callbacks* pAllocationCallbacks); +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 /* @@ -382,46 +409,47 @@ This holds the internal FILE object until drmp3_uninit() is called. Keep this in objects because the operating system may restrict the number of file handles an application can have open at any given time. */ -drmp3_bool32 drmp3_init_file(drmp3* pMP3, const char* filePath, const drmp3_config* pConfig, const drmp3_allocation_callbacks* pAllocationCallbacks); +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. */ -void drmp3_uninit(drmp3* pMP3); +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_uint64 drmp3_read_pcm_frames_f32(drmp3* pMP3, drmp3_uint64 framesToRead, float* pBufferOut); +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_uint64 drmp3_read_pcm_frames_s16(drmp3* pMP3, drmp3_uint64 framesToRead, drmp3_int16* pBufferOut); +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_bool32 drmp3_seek_to_pcm_frame(drmp3* pMP3, drmp3_uint64 frameIndex); +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_uint64 drmp3_get_pcm_frame_count(drmp3* pMP3); +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_uint64 drmp3_get_mp3_frame_count(drmp3* pMP3); +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 @@ -429,7 +457,7 @@ 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_bool32 drmp3_get_mp3_and_pcm_frame_count(drmp3* pMP3, drmp3_uint64* pMP3FrameCount, drmp3_uint64* pPCMFrameCount); +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. @@ -440,7 +468,7 @@ 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_bool32 drmp3_calculate_seek_points(drmp3* pMP3, drmp3_uint32* pSeekPointCount, drmp3_seek_point* pSeekPoints); +DRMP3_API drmp3_bool32 drmp3_calculate_seek_points(drmp3* pMP3, drmp3_uint32* pSeekPointCount, drmp3_seek_point* pSeekPoints); /* Binds a seek table to the decoder. @@ -450,31 +478,36 @@ remains valid while it is bound to the decoder. Use drmp3_calculate_seek_points() to calculate the seek points. */ -drmp3_bool32 drmp3_bind_seek_table(drmp3* pMP3, drmp3_uint32 seekPointCount, drmp3_seek_point* pSeekPoints); +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. -pConfig is both an input and output. On input it contains what you want. On output it contains what you got. +On output pConfig will receive the channel count and sample rate of the stream. Free the returned pointer with drmp3_free(). */ -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_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_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); -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_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_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 -float* drmp3_open_file_and_read_pcm_frames_f32(const char* filePath, drmp3_config* pConfig, drmp3_uint64* pTotalFrameCount, const drmp3_allocation_callbacks* pAllocationCallbacks); -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_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. */ -void drmp3_free(void* p, const drmp3_allocation_callbacks* pAllocationCallbacks); +DRMP3_API void drmp3_free(void* p, const drmp3_allocation_callbacks* pAllocationCallbacks); #ifdef __cplusplus } @@ -489,11 +522,31 @@ void drmp3_free(void* p, const drmp3_allocation_callbacks* pAllocationCallbacks) ************************************************************************************************************************************************************ ************************************************************************************************************************************************************/ -#ifdef DR_MP3_IMPLEMENTATION +#if defined(DR_MP3_IMPLEMENTATION) || defined(DRMP3_IMPLEMENTATION) #include <stdlib.h> #include <string.h> #include <limits.h> /* 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() +{ + return DRMP3_VERSION_STRING; +} + /* Disable SIMD when compiling with TCC for now. */ #if defined(__TINYC__) #define DR_MP3_NO_SIMD @@ -591,7 +644,7 @@ static __inline__ __attribute__((always_inline)) void drmp3_cpuid(int CPUInfo[], #endif } #endif -static int drmp3_have_simd() +static int drmp3_have_simd(void) { #ifdef DR_MP3_ONLY_SIMD return 1; @@ -632,7 +685,7 @@ end: #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() +static int drmp3_have_simd(void) { /* TODO: detect neon for !DR_MP3_ONLY_SIMD */ return 1; } @@ -650,6 +703,17 @@ static int drmp3_have_simd() #endif +#if defined(__ARM_ARCH) && (__ARM_ARCH >= 6) && !defined(__aarch64__) +#define DRMP3_HAVE_ARMV6 1 +static __inline__ __attribute__((always_inline)) drmp3_int32 drmp3_clip_int16_arm(int32_t a) +{ + drmp3_int32 x = 0; + __asm__ ("ssat %0, #16, %1" : "=r"(x) : "r"(a)); + return x; +} +#endif + + typedef struct { const drmp3_uint8 *buf; @@ -1139,16 +1203,16 @@ static void drmp3_L3_decode_scalefactors(const drmp3_uint8 *hdr, drmp3_uint8 *is int sh = 3 - scf_shift; for (i = 0; i < gr->n_short_sfb; i += 3) { - iscf[gr->n_long_sfb + i + 0] += gr->subblock_gain[0] << sh; - iscf[gr->n_long_sfb + i + 1] += gr->subblock_gain[1] << sh; - iscf[gr->n_long_sfb + i + 2] += gr->subblock_gain[2] << sh; + 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] += g_preamp[i]; + iscf[11 + i] = (drmp3_uint8)(iscf[11 + i] + g_preamp[i]); } } @@ -1869,11 +1933,17 @@ 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 */ - return (drmp3_int16)s; +#endif + return s; } #else typedef float drmp3d_sample_t; @@ -2140,12 +2210,12 @@ static int drmp3d_find_frame(const drmp3_uint8 *mp3, int mp3_bytes, int *free_fo return mp3_bytes; } -void drmp3dec_init(drmp3dec *dec) +DRMP3_API void drmp3dec_init(drmp3dec *dec) { dec->header[0] = 0; } -int drmp3dec_decode_frame(drmp3dec *dec, const unsigned char *mp3, int mp3_bytes, void *pcm, drmp3dec_frame_info *info) +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; @@ -2240,11 +2310,11 @@ int drmp3dec_decode_frame(drmp3dec *dec, const unsigned char *mp3, int mp3_bytes return success*drmp3_hdr_frame_samples(dec->header); } -void drmp3dec_f32_to_s16(const float *in, drmp3_int16 *out, int num_samples) +DRMP3_API void drmp3dec_f32_to_s16(const float *in, drmp3_int16 *out, size_t num_samples) { - int i = 0; + size_t i = 0; #if DRMP3_HAVE_SIMD - int aligned_count = num_samples & ~7; + size_t aligned_count = num_samples & ~7; for(; i < aligned_count; i+=8) { drmp3_f4 scale = DRMP3_VSET(32768.0f); @@ -2303,6 +2373,7 @@ void drmp3dec_f32_to_s16(const float *in, drmp3_int16 *out, int num_samples) Main Public API ************************************************************************************************************************************************************/ +#include <math.h> /* For sin() and exp(). */ #if defined(SIZE_MAX) #define DRMP3_SIZE_MAX SIZE_MAX @@ -2319,6 +2390,13 @@ void drmp3dec_f32_to_s16(const float *in, drmp3_int16 *out, int num_samples) #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 + /* Standard library stuff. */ #ifndef DRMP3_ASSERT @@ -2342,23 +2420,62 @@ void drmp3dec_f32_to_s16(const float *in, drmp3_int16 *out, int num_samples) #define DRMP3_FREE(p) free((p)) #endif -#define drmp3_countof(x) (sizeof(x) / sizeof(x[0])) -#define drmp3_max(x, y) (((x) > (y)) ? (x) : (y)) -#define drmp3_min(x, y) (((x) < (y)) ? (x) : (y)) +#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_DATA_CHUNK_SIZE 16384 /* The size in bytes of each chunk of data to read from the MP3 stream. minimp3 recommends 16K. */ +#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;*/ +} + -static void drmp3_blend_f32(float* pOut, float* pInA, float* pInB, float factor, drmp3_uint32 channels) +/* +Greatest common factor using Euclid's algorithm iteratively. +*/ +static DRMP3_INLINE drmp3_uint32 drmp3_gcf_u32(drmp3_uint32 a, drmp3_uint32 b) { - drmp3_uint32 i; - for (i = 0; i < channels; ++i) { - pOut[i] = drmp3_mix_f32(pInA[i], pInB[i], factor); + for (;;) { + if (b == 0) { + break; + } else { + drmp3_uint32 t = a; + a = b; + b = t % a; + } } + + return a; +} + + +static DRMP3_INLINE double drmp3_sin(double x) +{ + /* TODO: Implement custom sin(x). */ + return sin(x); +} + +static DRMP3_INLINE double drmp3_exp(double x) +{ + /* TODO: Implement custom exp(x). */ + return exp(x); +} + +static DRMP3_INLINE double drmp3_cos(double x) +{ + return drmp3_sin((DRMP3_PI_D*0.5) - x); } @@ -2381,7 +2498,6 @@ static void drmp3__free_default(void* p, void* pUserData) } -#if 0 /* Unused, but leaving here in case I need to add it again later. */ static void* drmp3__malloc_from_callbacks(size_t sz, const drmp3_allocation_callbacks* pAllocationCallbacks) { if (pAllocationCallbacks == NULL) { @@ -2399,7 +2515,6 @@ static void* drmp3__malloc_from_callbacks(size_t sz, const drmp3_allocation_call return NULL; } -#endif static void* drmp3__realloc_from_callbacks(void* p, size_t szNew, size_t szOld, const drmp3_allocation_callbacks* pAllocationCallbacks) { @@ -2443,7 +2558,7 @@ static void drmp3__free_from_callbacks(void* p, const drmp3_allocation_callbacks } -drmp3_allocation_callbacks drmp3_copy_allocation_callbacks_or_defaults(const drmp3_allocation_callbacks* pAllocationCallbacks) +static drmp3_allocation_callbacks drmp3_copy_allocation_callbacks_or_defaults(const drmp3_allocation_callbacks* pAllocationCallbacks) { if (pAllocationCallbacks != NULL) { /* Copy. */ @@ -2460,506 +2575,139 @@ drmp3_allocation_callbacks drmp3_copy_allocation_callbacks_or_defaults(const drm } -void drmp3_src_cache_init(drmp3_src* pSRC, drmp3_src_cache* pCache) -{ - DRMP3_ASSERT(pSRC != NULL); - DRMP3_ASSERT(pCache != NULL); - - pCache->pSRC = pSRC; - pCache->cachedFrameCount = 0; - pCache->iNextFrame = 0; -} -drmp3_uint64 drmp3_src_cache_read_frames(drmp3_src_cache* pCache, drmp3_uint64 frameCount, float* pFramesOut) +static size_t drmp3__on_read(drmp3* pMP3, void* pBufferOut, size_t bytesToRead) { - drmp3_uint32 channels; - drmp3_uint64 totalFramesRead = 0; - - DRMP3_ASSERT(pCache != NULL); - DRMP3_ASSERT(pCache->pSRC != NULL); - DRMP3_ASSERT(pCache->pSRC->onRead != NULL); - DRMP3_ASSERT(frameCount > 0); - DRMP3_ASSERT(pFramesOut != NULL); - - channels = pCache->pSRC->config.channels; - - while (frameCount > 0) { - /* If there's anything in memory go ahead and copy that over first. */ - drmp3_uint32 framesToReadFromClient; - drmp3_uint64 framesRemainingInMemory = pCache->cachedFrameCount - pCache->iNextFrame; - drmp3_uint64 framesToReadFromMemory = frameCount; - if (framesToReadFromMemory > framesRemainingInMemory) { - framesToReadFromMemory = framesRemainingInMemory; - } - - DRMP3_COPY_MEMORY(pFramesOut, pCache->pCachedFrames + pCache->iNextFrame*channels, (drmp3_uint32)(framesToReadFromMemory * channels * sizeof(float))); - pCache->iNextFrame += (drmp3_uint32)framesToReadFromMemory; - - totalFramesRead += framesToReadFromMemory; - frameCount -= framesToReadFromMemory; - if (frameCount == 0) { - break; - } - - - /* At this point there are still more frames to read from the client, so we'll need to reload the cache with fresh data. */ - DRMP3_ASSERT(frameCount > 0); - pFramesOut += framesToReadFromMemory * channels; - - pCache->iNextFrame = 0; - pCache->cachedFrameCount = 0; - - framesToReadFromClient = drmp3_countof(pCache->pCachedFrames) / pCache->pSRC->config.channels; - if (framesToReadFromClient > pCache->pSRC->config.cacheSizeInFrames) { - framesToReadFromClient = pCache->pSRC->config.cacheSizeInFrames; - } - - pCache->cachedFrameCount = (drmp3_uint32)pCache->pSRC->onRead(pCache->pSRC, framesToReadFromClient, pCache->pCachedFrames, pCache->pSRC->pUserData); - - - /* Get out of this loop if nothing was able to be retrieved. */ - if (pCache->cachedFrameCount == 0) { - break; - } - } - - return totalFramesRead; + size_t bytesRead = pMP3->onRead(pMP3->pUserData, pBufferOut, bytesToRead); + pMP3->streamCursor += bytesRead; + return bytesRead; } - -drmp3_uint64 drmp3_src_read_frames_passthrough(drmp3_src* pSRC, drmp3_uint64 frameCount, void* pFramesOut, drmp3_bool32 flush); -drmp3_uint64 drmp3_src_read_frames_linear(drmp3_src* pSRC, drmp3_uint64 frameCount, void* pFramesOut, drmp3_bool32 flush); - -drmp3_bool32 drmp3_src_init(const drmp3_src_config* pConfig, drmp3_src_read_proc onRead, void* pUserData, drmp3_src* pSRC) +static drmp3_bool32 drmp3__on_seek(drmp3* pMP3, int offset, drmp3_seek_origin origin) { - if (pSRC == NULL) { - return DRMP3_FALSE; - } - - DRMP3_ZERO_OBJECT(pSRC); - - if (pConfig == NULL || onRead == NULL) { - return DRMP3_FALSE; - } + DRMP3_ASSERT(offset >= 0); - if (pConfig->channels == 0 || pConfig->channels > 2) { + if (!pMP3->onSeek(pMP3->pUserData, offset, origin)) { return DRMP3_FALSE; } - pSRC->config = *pConfig; - pSRC->onRead = onRead; - pSRC->pUserData = pUserData; - - if (pSRC->config.cacheSizeInFrames > DRMP3_SRC_CACHE_SIZE_IN_FRAMES || pSRC->config.cacheSizeInFrames == 0) { - pSRC->config.cacheSizeInFrames = DRMP3_SRC_CACHE_SIZE_IN_FRAMES; + if (origin == drmp3_seek_origin_start) { + pMP3->streamCursor = (drmp3_uint64)offset; + } else { + pMP3->streamCursor += offset; } - drmp3_src_cache_init(pSRC, &pSRC->cache); return DRMP3_TRUE; } -drmp3_bool32 drmp3_src_set_input_sample_rate(drmp3_src* pSRC, drmp3_uint32 sampleRateIn) +static drmp3_bool32 drmp3__on_seek_64(drmp3* pMP3, drmp3_uint64 offset, drmp3_seek_origin origin) { - if (pSRC == NULL) { - return DRMP3_FALSE; - } - - /* Must have a sample rate of > 0. */ - if (sampleRateIn == 0) { - return DRMP3_FALSE; + if (offset <= 0x7FFFFFFF) { + return drmp3__on_seek(pMP3, (int)offset, origin); } - pSRC->config.sampleRateIn = sampleRateIn; - return DRMP3_TRUE; -} -drmp3_bool32 drmp3_src_set_output_sample_rate(drmp3_src* pSRC, drmp3_uint32 sampleRateOut) -{ - if (pSRC == NULL) { + /* 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; } - /* Must have a sample rate of > 0. */ - if (sampleRateOut == 0) { - 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; + } } - pSRC->config.sampleRateOut = sampleRateOut; return DRMP3_TRUE; } -drmp3_uint64 drmp3_src_read_frames_ex(drmp3_src* pSRC, drmp3_uint64 frameCount, void* pFramesOut, drmp3_bool32 flush) + +static drmp3_uint32 drmp3_decode_next_frame_ex__callbacks(drmp3* pMP3, drmp3d_sample_t* pPCMFrames) { - drmp3_src_algorithm algorithm; + drmp3_uint32 pcmFramesRead = 0; - if (pSRC == NULL || frameCount == 0 || pFramesOut == NULL) { + DRMP3_ASSERT(pMP3 != NULL); + DRMP3_ASSERT(pMP3->onRead != NULL); + + if (pMP3->atEnd) { return 0; } - algorithm = pSRC->config.algorithm; - - /* Always use passthrough if the sample rates are the same. */ - if (pSRC->config.sampleRateIn == pSRC->config.sampleRateOut) { - algorithm = drmp3_src_algorithm_none; - } + for (;;) { + drmp3dec_frame_info info; - /* Could just use a function pointer instead of a switch for this... */ - switch (algorithm) - { - case drmp3_src_algorithm_none: return drmp3_src_read_frames_passthrough(pSRC, frameCount, pFramesOut, flush); - case drmp3_src_algorithm_linear: return drmp3_src_read_frames_linear(pSRC, frameCount, pFramesOut, flush); - default: return 0; - } -} + /* 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; -drmp3_uint64 drmp3_src_read_frames(drmp3_src* pSRC, drmp3_uint64 frameCount, void* pFramesOut) -{ - return drmp3_src_read_frames_ex(pSRC, frameCount, pFramesOut, DRMP3_FALSE); -} + /* First we need to move the data down. */ + memmove(pMP3->pData, pMP3->pData + pMP3->dataConsumed, pMP3->dataSize); + pMP3->dataConsumed = 0; -drmp3_uint64 drmp3_src_read_frames_passthrough(drmp3_src* pSRC, drmp3_uint64 frameCount, void* pFramesOut, drmp3_bool32 flush) -{ - DRMP3_ASSERT(pSRC != NULL); - DRMP3_ASSERT(frameCount > 0); - DRMP3_ASSERT(pFramesOut != NULL); + if (pMP3->dataCapacity < DRMP3_DATA_CHUNK_SIZE) { + drmp3_uint8* pNewData; + size_t newDataCap; - (void)flush; /* Passthrough need not care about flushing. */ - return pSRC->onRead(pSRC, frameCount, pFramesOut, pSRC->pUserData); -} + newDataCap = DRMP3_DATA_CHUNK_SIZE; -drmp3_uint64 drmp3_src_read_frames_linear(drmp3_src* pSRC, drmp3_uint64 frameCount, void* pFramesOut, drmp3_bool32 flush) -{ - double factor; - drmp3_uint64 totalFramesRead; + pNewData = (drmp3_uint8*)drmp3__realloc_from_callbacks(pMP3->pData, newDataCap, pMP3->dataCapacity, &pMP3->allocationCallbacks); + if (pNewData == NULL) { + return 0; /* Out of memory. */ + } - DRMP3_ASSERT(pSRC != NULL); - DRMP3_ASSERT(frameCount > 0); - DRMP3_ASSERT(pFramesOut != NULL); + pMP3->pData = pNewData; + pMP3->dataCapacity = newDataCap; + } - /* For linear SRC, the bin is only 2 frames: 1 prior, 1 future. */ + 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. */ + } + } - /* Load the bin if necessary. */ - if (!pSRC->algo.linear.isPrevFramesLoaded) { - drmp3_uint64 framesRead = drmp3_src_cache_read_frames(&pSRC->cache, 1, pSRC->bin); - if (framesRead == 0) { - return 0; + pMP3->dataSize += bytesRead; } - pSRC->algo.linear.isPrevFramesLoaded = DRMP3_TRUE; - } - if (!pSRC->algo.linear.isNextFramesLoaded) { - drmp3_uint64 framesRead = drmp3_src_cache_read_frames(&pSRC->cache, 1, pSRC->bin + pSRC->config.channels); - if (framesRead == 0) { - return 0; + + if (pMP3->dataSize > INT_MAX) { + pMP3->atEnd = DRMP3_TRUE; + return 0; /* File too big. */ } - pSRC->algo.linear.isNextFramesLoaded = DRMP3_TRUE; - } - factor = (double)pSRC->config.sampleRateIn / pSRC->config.sampleRateOut; + 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. */ - totalFramesRead = 0; - while (frameCount > 0) { - drmp3_uint32 i; - drmp3_uint32 framesToReadFromClient; - - /* The bin is where the previous and next frames are located. */ - float* pPrevFrame = pSRC->bin; - float* pNextFrame = pSRC->bin + pSRC->config.channels; - - drmp3_blend_f32((float*)pFramesOut, pPrevFrame, pNextFrame, (float)pSRC->algo.linear.alpha, pSRC->config.channels); - - pSRC->algo.linear.alpha += factor; - - /* The new alpha value is how we determine whether or not we need to read fresh frames. */ - framesToReadFromClient = (drmp3_uint32)pSRC->algo.linear.alpha; - pSRC->algo.linear.alpha = pSRC->algo.linear.alpha - framesToReadFromClient; - - for (i = 0; i < framesToReadFromClient; ++i) { - drmp3_uint64 framesRead; - drmp3_uint32 j; - - for (j = 0; j < pSRC->config.channels; ++j) { - pPrevFrame[j] = pNextFrame[j]; - } - - framesRead = drmp3_src_cache_read_frames(&pSRC->cache, 1, pNextFrame); - if (framesRead == 0) { - drmp3_uint32 k; - for (k = 0; k < pSRC->config.channels; ++k) { - pNextFrame[k] = 0; - } - - if (pSRC->algo.linear.isNextFramesLoaded) { - pSRC->algo.linear.isNextFramesLoaded = DRMP3_FALSE; - } else { - if (flush) { - pSRC->algo.linear.isPrevFramesLoaded = DRMP3_FALSE; - } - } - - break; - } - } - - pFramesOut = (drmp3_uint8*)pFramesOut + (1 * pSRC->config.channels * sizeof(float)); - frameCount -= 1; - totalFramesRead += 1; - - /* If there's no frames available we need to get out of this loop. */ - if (!pSRC->algo.linear.isNextFramesLoaded && (!flush || !pSRC->algo.linear.isPrevFramesLoaded)) { - break; - } - } - - return totalFramesRead; -} - - -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(drmp3* pMP3, drmp3d_sample_t* pPCMFrames, drmp3_bool32 discard); -static drmp3_uint32 drmp3_decode_next_frame(drmp3* pMP3); - -static drmp3_uint64 drmp3_read_src(drmp3_src* pSRC, drmp3_uint64 frameCount, void* pFramesOut, void* pUserData) -{ - drmp3* pMP3 = (drmp3*)pUserData; - float* pFramesOutF = (float*)pFramesOut; - drmp3_uint64 totalFramesRead = 0; - - DRMP3_ASSERT(pMP3 != NULL); - DRMP3_ASSERT(pMP3->onRead != NULL); - - while (frameCount > 0) { - /* Read from the in-memory buffer first. */ - while (pMP3->pcmFramesRemainingInMP3Frame > 0 && frameCount > 0) { - drmp3d_sample_t* frames = (drmp3d_sample_t*)pMP3->pcmFrames; -#ifndef DR_MP3_FLOAT_OUTPUT - if (pMP3->mp3FrameChannels == 1) { - if (pMP3->channels == 1) { - /* Mono -> Mono. */ - pFramesOutF[0] = frames[pMP3->pcmFramesConsumedInMP3Frame] / 32768.0f; - } else { - /* Mono -> Stereo. */ - pFramesOutF[0] = frames[pMP3->pcmFramesConsumedInMP3Frame] / 32768.0f; - pFramesOutF[1] = frames[pMP3->pcmFramesConsumedInMP3Frame] / 32768.0f; - } - } else { - if (pMP3->channels == 1) { - /* Stereo -> Mono */ - float sample = 0; - sample += frames[(pMP3->pcmFramesConsumedInMP3Frame*pMP3->mp3FrameChannels)+0] / 32768.0f; - sample += frames[(pMP3->pcmFramesConsumedInMP3Frame*pMP3->mp3FrameChannels)+1] / 32768.0f; - pFramesOutF[0] = sample * 0.5f; - } else { - /* Stereo -> Stereo */ - pFramesOutF[0] = frames[(pMP3->pcmFramesConsumedInMP3Frame*pMP3->mp3FrameChannels)+0] / 32768.0f; - pFramesOutF[1] = frames[(pMP3->pcmFramesConsumedInMP3Frame*pMP3->mp3FrameChannels)+1] / 32768.0f; - } - } -#else - if (pMP3->mp3FrameChannels == 1) { - if (pMP3->channels == 1) { - /* Mono -> Mono. */ - pFramesOutF[0] = frames[pMP3->pcmFramesConsumedInMP3Frame]; - } else { - /* Mono -> Stereo. */ - pFramesOutF[0] = frames[pMP3->pcmFramesConsumedInMP3Frame]; - pFramesOutF[1] = frames[pMP3->pcmFramesConsumedInMP3Frame]; - } - } else { - if (pMP3->channels == 1) { - /* Stereo -> Mono */ - float sample = 0; - sample += frames[(pMP3->pcmFramesConsumedInMP3Frame*pMP3->mp3FrameChannels)+0]; - sample += frames[(pMP3->pcmFramesConsumedInMP3Frame*pMP3->mp3FrameChannels)+1]; - pFramesOutF[0] = sample * 0.5f; - } else { - /* Stereo -> Stereo */ - pFramesOutF[0] = frames[(pMP3->pcmFramesConsumedInMP3Frame*pMP3->mp3FrameChannels)+0]; - pFramesOutF[1] = frames[(pMP3->pcmFramesConsumedInMP3Frame*pMP3->mp3FrameChannels)+1]; - } - } -#endif - - pMP3->pcmFramesConsumedInMP3Frame += 1; - pMP3->pcmFramesRemainingInMP3Frame -= 1; - totalFramesRead += 1; - frameCount -= 1; - pFramesOutF += pSRC->config.channels; - } - - if (frameCount == 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; -} - -static drmp3_bool32 drmp3_init_src(drmp3* pMP3) -{ - drmp3_src_config srcConfig; - DRMP3_ZERO_OBJECT(&srcConfig); - srcConfig.sampleRateIn = DR_MP3_DEFAULT_SAMPLE_RATE; - srcConfig.sampleRateOut = pMP3->sampleRate; - srcConfig.channels = pMP3->channels; - srcConfig.algorithm = drmp3_src_algorithm_linear; - if (!drmp3_src_init(&srcConfig, drmp3_read_src, pMP3, &pMP3->src)) { - drmp3_uninit(pMP3); - return DRMP3_FALSE; - } - - return DRMP3_TRUE; -} - -static drmp3_uint32 drmp3_decode_next_frame_ex(drmp3* pMP3, drmp3d_sample_t* pPCMFrames, drmp3_bool32 discard) -{ - drmp3_uint32 pcmFramesRead = 0; - - DRMP3_ASSERT(pMP3 != NULL); - DRMP3_ASSERT(pMP3->onRead != NULL); - - if (pMP3->atEnd) { - return 0; - } - - do { - drmp3dec_frame_info info; - size_t leftoverDataSize; - - /* minimp3 recommends doing data submission in 16K chunks. If we don't have at least 16K bytes available, get more. */ - if (pMP3->dataSize < DRMP3_DATA_CHUNK_SIZE) { - size_t bytesRead; - - 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. */ - } - - pcmFramesRead = drmp3dec_decode_frame(&pMP3->decoder, pMP3->pData, (int)pMP3->dataSize, pPCMFrames, &info); /* <-- Safe size_t -> int conversion thanks to the check above. */ - /* Consume the data. */ - leftoverDataSize = (pMP3->dataSize - (size_t)info.frame_bytes); if (info.frame_bytes > 0) { - memmove(pMP3->pData, pMP3->pData + info.frame_bytes, leftoverDataSize); - pMP3->dataSize = leftoverDataSize; + 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. A special case is if we are wanting to discard the frame, in which case we return successfully. - */ - if (pcmFramesRead > 0 || (info.frame_bytes > 0 && discard)) { + /* 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; - - /* We need to initialize the resampler if we don't yet have the channel count or sample rate. */ - if (pMP3->channels == 0 || pMP3->sampleRate == 0) { - if (pMP3->channels == 0) { - pMP3->channels = info.channels; - } - if (pMP3->sampleRate == 0) { - pMP3->sampleRate = info.hz; - } - drmp3_init_src(pMP3); - } - - drmp3_src_set_input_sample_rate(&pMP3->src, pMP3->mp3FrameSampleRate); break; } else if (info.frame_bytes == 0) { + /* Need more data. minimp3 recommends doing data submission in 16K chunks. */ size_t bytesRead; - /* Need more data. minimp3 recommends doing data submission in 16K chunks. */ + /* First we need to move the data down. */ + memmove(pMP3->pData, pMP3->pData + pMP3->dataConsumed, pMP3->dataSize); + pMP3->dataConsumed = 0; + if (pMP3->dataCapacity == pMP3->dataSize) { /* No room. Expand. */ drmp3_uint8* pNewData; @@ -2985,15 +2733,49 @@ static drmp3_uint32 drmp3_decode_next_frame_ex(drmp3* pMP3, drmp3d_sample_t* pPC pMP3->dataSize += bytesRead; } - } while (DRMP3_TRUE); + }; 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; + } + + pcmFramesRead = drmp3dec_decode_frame(&pMP3->decoder, pMP3->memory.pData + pMP3->memory.currentReadPos, (int)(pMP3->memory.dataSize - pMP3->memory.currentReadPos), pPCMFrames, &info); + + pMP3->pcmFramesConsumedInMP3Frame = 0; + pMP3->pcmFramesRemainingInMP3Frame = pcmFramesRead; + pMP3->mp3FrameChannels = info.channels; + pMP3->mp3FrameSampleRate = info.hz; + + /* 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, DRMP3_FALSE); + return drmp3_decode_next_frame_ex(pMP3, (drmp3d_sample_t*)pMP3->pcmFrames); } #if 0 @@ -3017,32 +2799,14 @@ static drmp3_uint32 drmp3_seek_next_frame(drmp3* pMP3) } #endif -drmp3_bool32 drmp3_init_internal(drmp3* pMP3, drmp3_read_proc onRead, drmp3_seek_proc onSeek, void* pUserData, const drmp3_config* pConfig, const drmp3_allocation_callbacks* pAllocationCallbacks) +static drmp3_bool32 drmp3_init_internal(drmp3* pMP3, drmp3_read_proc onRead, drmp3_seek_proc onSeek, void* pUserData, const drmp3_allocation_callbacks* pAllocationCallbacks) { - drmp3_config config; - 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); - /* The config can be null in which case we use defaults. */ - if (pConfig != NULL) { - config = *pConfig; - } else { - DRMP3_ZERO_OBJECT(&config); - } - - pMP3->channels = config.outputChannels; - - /* Cannot have more than 2 channels. */ - if (pMP3->channels > 2) { - pMP3->channels = 2; - } - - pMP3->sampleRate = config.outputSampleRate; - pMP3->onRead = onRead; pMP3->onSeek = onSeek; pMP3->pUserData = pUserData; @@ -3052,31 +2816,26 @@ drmp3_bool32 drmp3_init_internal(drmp3* pMP3, drmp3_read_proc onRead, drmp3_seek return DRMP3_FALSE; /* Invalid allocation callbacks. */ } - /* - We need a sample rate converter for converting the sample rate from the MP3 frames to the requested output sample rate. Note that if - we don't yet know the channel count or sample rate we defer this until the first frame is read. - */ - if (pMP3->channels != 0 && pMP3->sampleRate != 0) { - drmp3_init_src(pMP3); - } - /* Decode the first frame to confirm that it is indeed a valid MP3 stream. */ if (!drmp3_decode_next_frame(pMP3)) { drmp3_uninit(pMP3); return DRMP3_FALSE; /* Not a valid MP3 stream. */ } + pMP3->channels = pMP3->mp3FrameChannels; + pMP3->sampleRate = pMP3->mp3FrameSampleRate; + return DRMP3_TRUE; } -drmp3_bool32 drmp3_init(drmp3* pMP3, drmp3_read_proc onRead, drmp3_seek_proc onSeek, void* pUserData, const drmp3_config* pConfig, const drmp3_allocation_callbacks* pAllocationCallbacks) +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, pConfig, pAllocationCallbacks); + return drmp3_init_internal(pMP3, onRead, onSeek, pUserData, pAllocationCallbacks); } @@ -3093,66 +2852,614 @@ static size_t drmp3__on_read_memory(void* pUserData, void* pBufferOut, size_t by bytesToRead = bytesRemaining; } - if (bytesToRead > 0) { - DRMP3_COPY_MEMORY(pBufferOut, pMP3->memory.pData + pMP3->memory.currentReadPos, bytesToRead); - pMP3->memory.currentReadPos += bytesToRead; + 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 <stdio.h> +#include <wchar.h> /* For wcslen(), wcsrtombs() */ + +/* drmp3_result_from_errno() is only used inside DR_MP3_NO_STDIO for now. Move this out if it's ever used elsewhere. */ +#include <errno.h> +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; + } +} + +static drmp3_result drmp3_fopen(FILE** ppFile, const char* pFilePath, const char* pOpenMode) +{ +#if _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 _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 bytesToRead; + return DRMP3_SUCCESS; } -static drmp3_bool32 drmp3__on_seek_memory(void* pUserData, int byteOffset, drmp3_seek_origin origin) +/* +_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__). + +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__) + #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) { - drmp3* pMP3 = (drmp3*)pUserData; + if (ppFile != NULL) { + *ppFile = NULL; /* Safety. */ + } - DRMP3_ASSERT(pMP3 != NULL); + if (pFilePath == NULL || pOpenMode == NULL || ppFile == NULL) { + return DRMP3_INVALID_ARGS; + } - 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. */ - } +#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); } - - /* 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. */ + #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. + */ + { + 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); + } - return DRMP3_TRUE; -} + pFilePathMB = (char*)drmp3__malloc_from_callbacks(lenMB + 1, pAllocationCallbacks); + if (pFilePathMB == NULL) { + return DRMP3_OUT_OF_MEMORY; + } -drmp3_bool32 drmp3_init_memory(drmp3* pMP3, const void* pData, size_t dataSize, const drmp3_config* pConfig, const drmp3_allocation_callbacks* pAllocationCallbacks) -{ - if (pMP3 == NULL) { - return DRMP3_FALSE; - } + pFilePathTemp = pFilePath; + DRMP3_ZERO_OBJECT(&mbs); + wcsrtombs(pFilePathMB, &pFilePathTemp, lenMB + 1, &mbs); - DRMP3_ZERO_OBJECT(pMP3); + /* 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; + } - if (pData == NULL || dataSize == 0) { - return DRMP3_FALSE; + pOpenModeMB[i] = (char)pOpenMode[i]; + i += 1; + } + } + + *ppFile = fopen(pFilePathMB, pOpenModeMB); + + drmp3__free_from_callbacks(pFilePathMB, pAllocationCallbacks); } - pMP3->memory.pData = (const drmp3_uint8*)pData; - pMP3->memory.dataSize = dataSize; - pMP3->memory.currentReadPos = 0; + if (*ppFile == NULL) { + return DRMP3_ERROR; + } +#endif - return drmp3_init_internal(pMP3, drmp3__on_read_memory, drmp3__on_seek_memory, pMP3, pConfig, pAllocationCallbacks); + return DRMP3_SUCCESS; } -#ifndef DR_MP3_NO_STDIO -#include <stdio.h> static size_t drmp3__on_read_stdio(void* pUserData, void* pBufferOut, size_t bytesToRead) { @@ -3164,25 +3471,28 @@ static drmp3_bool32 drmp3__on_seek_stdio(void* pUserData, int offset, drmp3_seek return fseek((FILE*)pUserData, offset, (origin == drmp3_seek_origin_current) ? SEEK_CUR : SEEK_SET) == 0; } -drmp3_bool32 drmp3_init_file(drmp3* pMP3, const char* filePath, const drmp3_config* pConfig, const drmp3_allocation_callbacks* pAllocationCallbacks) +DRMP3_API drmp3_bool32 drmp3_init_file(drmp3* pMP3, const char* pFilePath, const drmp3_allocation_callbacks* pAllocationCallbacks) { FILE* pFile; -#if defined(_MSC_VER) && _MSC_VER >= 1400 - if (fopen_s(&pFile, filePath, "rb") != 0) { + if (drmp3_fopen(&pFile, pFilePath, "rb") != DRMP3_SUCCESS) { return DRMP3_FALSE; } -#else - pFile = fopen(filePath, "rb"); - if (pFile == NULL) { + + return drmp3_init(pMP3, drmp3__on_read_stdio, drmp3__on_seek_stdio, (void*)pFile, pAllocationCallbacks); +} + +DRMP3_API drmp3_bool32 drmp3_init_file_w(drmp3* pMP3, const wchar_t* pFilePath, const drmp3_allocation_callbacks* pAllocationCallbacks) +{ + FILE* pFile; + if (drmp3_wfopen(&pFile, pFilePath, L"rb", pAllocationCallbacks) != DRMP3_SUCCESS) { return DRMP3_FALSE; } -#endif - return drmp3_init(pMP3, drmp3__on_read_stdio, drmp3__on_seek_stdio, (void*)pFile, pConfig, pAllocationCallbacks); + return drmp3_init(pMP3, drmp3__on_read_stdio, drmp3__on_seek_stdio, (void*)pFile, pAllocationCallbacks); } #endif -void drmp3_uninit(drmp3* pMP3) +DRMP3_API void drmp3_uninit(drmp3* pMP3) { if (pMP3 == NULL) { return; @@ -3197,75 +3507,187 @@ void drmp3_uninit(drmp3* pMP3) drmp3__free_from_callbacks(pMP3->pData, &pMP3->allocationCallbacks); } -drmp3_uint64 drmp3_read_pcm_frames_f32(drmp3* pMP3, drmp3_uint64 framesToRead, float* pBufferOut) +#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 (pBufferOut == NULL) { - float temp[4096]; - while (framesToRead > 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 framesToReadRightNow = sizeof(temp)/sizeof(temp[0]) / pMP3->channels; - if (framesToReadRightNow > framesToRead) { - framesToReadRightNow = framesToRead; + drmp3_uint64 framesRemaining = framesToRead - totalPCMFramesRead; + drmp3_uint64 framesToReadNow = DRMP3_COUNTOF(pTempS16) / pMP3->channels; + if (framesToReadNow > framesRemaining) { + framesToReadNow = framesRemaining; } - framesJustRead = drmp3_read_pcm_frames_f32(pMP3, framesToReadRightNow, temp); + framesJustRead = drmp3_read_pcm_frames_raw(pMP3, framesToReadNow, pTempS16); if (framesJustRead == 0) { break; } - framesToRead -= framesJustRead; - totalFramesRead += framesJustRead; + drmp3_s16_to_f32((float*)DRMP3_OFFSET_PTR(pBufferOut, sizeof(drmp3_int16) * totalPCMFramesRead * pMP3->channels), pTempS16, framesJustRead * pMP3->channels); + totalPCMFramesRead += framesJustRead; } - } else { - totalFramesRead = drmp3_src_read_frames_ex(&pMP3->src, framesToRead, pBufferOut, DRMP3_TRUE); - pMP3->currentPCMFrame += totalFramesRead; - } - return totalFramesRead; + return totalPCMFramesRead; + } +#endif } -drmp3_uint64 drmp3_read_pcm_frames_s16(drmp3* pMP3, drmp3_uint64 framesToRead, drmp3_int16* pBufferOut) +DRMP3_API drmp3_uint64 drmp3_read_pcm_frames_s16(drmp3* pMP3, drmp3_uint64 framesToRead, drmp3_int16* pBufferOut) { - float tempF32[4096]; - drmp3_uint64 pcmFramesJustRead; - drmp3_uint64 totalPCMFramesRead = 0; - if (pMP3 == NULL || pMP3->onRead == NULL) { return 0; } - /* Naive implementation: read into a temp f32 buffer, then convert. */ - for (;;) { - drmp3_uint64 pcmFramesToReadThisIteration = (framesToRead - totalPCMFramesRead); - if (pcmFramesToReadThisIteration > drmp3_countof(tempF32)/pMP3->channels) { - pcmFramesToReadThisIteration = drmp3_countof(tempF32)/pMP3->channels; - } - - pcmFramesJustRead = drmp3_read_pcm_frames_f32(pMP3, pcmFramesToReadThisIteration, tempF32); - if (pcmFramesJustRead == 0) { - break; - } +#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; - drmp3dec_f32_to_s16(tempF32, pBufferOut, (int)(pcmFramesJustRead * pMP3->channels)); /* <-- Safe cast since pcmFramesJustRead will be clamped based on the size of tempF32 which is always small. */ - pBufferOut += pcmFramesJustRead * pMP3->channels; + while (totalPCMFramesRead < framesToRead) { + drmp3_uint64 framesJustRead; + drmp3_uint64 framesRemaining = framesToRead - totalPCMFramesRead; + drmp3_uint64 framesToReadNow = DRMP3_COUNTOF(pTempF32) / pMP3->channels; + if (framesToReadNow > framesRemaining) { + framesToReadNow = framesRemaining; + } - totalPCMFramesRead += pcmFramesJustRead; + framesJustRead = drmp3_read_pcm_frames_raw(pMP3, framesToReadNow, pTempF32); + if (framesJustRead == 0) { + break; + } - if (pcmFramesJustRead < pcmFramesToReadThisIteration) { - 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; + return totalPCMFramesRead; + } +#endif } -void drmp3_reset(drmp3* pMP3) +static void drmp3_reset(drmp3* pMP3) { DRMP3_ASSERT(pMP3 != NULL); @@ -3274,19 +3696,10 @@ void drmp3_reset(drmp3* pMP3) pMP3->currentPCMFrame = 0; pMP3->dataSize = 0; pMP3->atEnd = DRMP3_FALSE; - pMP3->src.bin[0] = 0; - pMP3->src.bin[1] = 0; - pMP3->src.bin[2] = 0; - pMP3->src.bin[3] = 0; - pMP3->src.cache.cachedFrameCount = 0; - pMP3->src.cache.iNextFrame = 0; - pMP3->src.algo.linear.alpha = 0; - pMP3->src.algo.linear.isNextFramesLoaded = 0; - pMP3->src.algo.linear.isPrevFramesLoaded = 0; drmp3dec_init(&pMP3->decoder); } -drmp3_bool32 drmp3_seek_to_start_of_stream(drmp3* pMP3) +static drmp3_bool32 drmp3_seek_to_start_of_stream(drmp3* pMP3) { DRMP3_ASSERT(pMP3 != NULL); DRMP3_ASSERT(pMP3->onSeek != NULL); @@ -3301,78 +3714,29 @@ drmp3_bool32 drmp3_seek_to_start_of_stream(drmp3* pMP3) return DRMP3_TRUE; } -float drmp3_get_cached_pcm_frame_count_from_src(drmp3* pMP3) -{ - return (pMP3->src.cache.cachedFrameCount - pMP3->src.cache.iNextFrame) + (float)pMP3->src.algo.linear.alpha; -} - -float drmp3_get_pcm_frames_remaining_in_mp3_frame(drmp3* pMP3) -{ - float factor = (float)pMP3->src.config.sampleRateOut / (float)pMP3->src.config.sampleRateIn; - float frameCountPreSRC = drmp3_get_cached_pcm_frame_count_from_src(pMP3) + pMP3->pcmFramesRemainingInMP3Frame; - return frameCountPreSRC * factor; -} -/* -NOTE ON SEEKING -=============== -The seeking code below is a complete mess and is broken for cases when the sample rate changes. The problem -is with the resampling and the crappy resampler used by dr_mp3. What needs to happen is the following: - -1) The resampler needs to be replaced. -2) The resampler has state which needs to be updated whenever an MP3 frame is decoded outside of - drmp3_read_pcm_frames_f32(). The resampler needs an API to "flush" some imaginary input so that it's - state is updated accordingly. -*/ -drmp3_bool32 drmp3_seek_forward_by_pcm_frames__brute_force(drmp3* pMP3, drmp3_uint64 frameOffset) +static drmp3_bool32 drmp3_seek_forward_by_pcm_frames__brute_force(drmp3* pMP3, drmp3_uint64 frameOffset) { drmp3_uint64 framesRead; -#if 0 /* - MP3 is a bit annoying when it comes to seeking because of the bit reservoir. It basically means that an MP3 frame can possibly - depend on some of the data of prior frames. This means it's not as simple as seeking to the first byte of the MP3 frame that - contains the sample because that MP3 frame will need the data from the previous MP3 frame (which we just seeked past!). To - resolve this we seek past a number of MP3 frames up to a point, and then read-and-discard the remainder. + 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. */ - drmp3_uint64 maxFramesToReadAndDiscard = (drmp3_uint64)(DRMP3_MAX_PCM_FRAMES_PER_MP3_FRAME * 3 * ((float)pMP3->src.config.sampleRateOut / (float)pMP3->src.config.sampleRateIn)); - - /* Now get rid of leading whole frames. */ - while (frameOffset > maxFramesToReadAndDiscard) { - float pcmFramesRemainingInCurrentMP3FrameF = drmp3_get_pcm_frames_remaining_in_mp3_frame(pMP3); - drmp3_uint32 pcmFramesRemainingInCurrentMP3Frame = (drmp3_uint32)pcmFramesRemainingInCurrentMP3FrameF; - if (frameOffset > pcmFramesRemainingInCurrentMP3Frame) { - frameOffset -= pcmFramesRemainingInCurrentMP3Frame; - pMP3->currentPCMFrame += pcmFramesRemainingInCurrentMP3Frame; - pMP3->pcmFramesConsumedInMP3Frame += pMP3->pcmFramesRemainingInMP3Frame; - pMP3->pcmFramesRemainingInMP3Frame = 0; - } else { - break; - } - - drmp3_uint32 pcmFrameCount = drmp3_decode_next_frame_ex(pMP3, pMP3->pcmFrames, DRMP3_FALSE); - if (pcmFrameCount == 0) { - break; - } - } - - /* The last step is to read-and-discard any remaining PCM frames to make it sample-exact. */ +#if defined(DR_MP3_FLOAT_OUTPUT) framesRead = drmp3_read_pcm_frames_f32(pMP3, frameOffset, NULL); - if (framesRead != frameOffset) { - return DRMP3_FALSE; - } #else - /* Just using a dumb read-and-discard for now pending updates to the resampler. */ - framesRead = drmp3_read_pcm_frames_f32(pMP3, frameOffset, NULL); + framesRead = drmp3_read_pcm_frames_s16(pMP3, frameOffset, NULL); +#endif if (framesRead != frameOffset) { return DRMP3_FALSE; } -#endif return DRMP3_TRUE; } -drmp3_bool32 drmp3_seek_to_pcm_frame__brute_force(drmp3* pMP3, drmp3_uint64 frameIndex) +static drmp3_bool32 drmp3_seek_to_pcm_frame__brute_force(drmp3* pMP3, drmp3_uint64 frameIndex) { DRMP3_ASSERT(pMP3 != NULL); @@ -3395,7 +3759,7 @@ drmp3_bool32 drmp3_seek_to_pcm_frame__brute_force(drmp3* pMP3, drmp3_uint64 fram return drmp3_seek_forward_by_pcm_frames__brute_force(pMP3, (frameIndex - pMP3->currentPCMFrame)); } -drmp3_bool32 drmp3_find_closest_seek_point(drmp3* pMP3, drmp3_uint64 frameIndex, drmp3_uint32* pSeekPointIndex) +static drmp3_bool32 drmp3_find_closest_seek_point(drmp3* pMP3, drmp3_uint64 frameIndex, drmp3_uint32* pSeekPointIndex) { drmp3_uint32 iSeekPoint; @@ -3419,7 +3783,7 @@ drmp3_bool32 drmp3_find_closest_seek_point(drmp3* pMP3, drmp3_uint64 frameIndex, return DRMP3_TRUE; } -drmp3_bool32 drmp3_seek_to_pcm_frame__seek_table(drmp3* pMP3, drmp3_uint64 frameIndex) +static drmp3_bool32 drmp3_seek_to_pcm_frame__seek_table(drmp3* pMP3, drmp3_uint64 frameIndex) { drmp3_seek_point seekPoint; drmp3_uint32 priorSeekPointIndex; @@ -3450,7 +3814,7 @@ drmp3_bool32 drmp3_seek_to_pcm_frame__seek_table(drmp3* pMP3, drmp3_uint64 frame /* Whole MP3 frames need to be discarded first. */ for (iMP3Frame = 0; iMP3Frame < seekPoint.mp3FramesToDiscard; ++iMP3Frame) { - drmp3_uint32 pcmFramesReadPreSRC; + 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. */ @@ -3459,9 +3823,9 @@ drmp3_bool32 drmp3_seek_to_pcm_frame__seek_table(drmp3* pMP3, drmp3_uint64 frame pPCMFrames = (drmp3d_sample_t*)pMP3->pcmFrames; } - /* We first need to decode the next frame, and then we need to flush the resampler. */ - pcmFramesReadPreSRC = drmp3_decode_next_frame_ex(pMP3, pPCMFrames, DRMP3_TRUE); - if (pcmFramesReadPreSRC == 0) { + /* We first need to decode the next frame. */ + pcmFramesRead = drmp3_decode_next_frame_ex(pMP3, pPCMFrames); + if (pcmFramesRead == 0) { return DRMP3_FALSE; } } @@ -3470,17 +3834,6 @@ drmp3_bool32 drmp3_seek_to_pcm_frame__seek_table(drmp3* pMP3, drmp3_uint64 frame pMP3->currentPCMFrame = seekPoint.pcmFrameIndex - seekPoint.pcmFramesToDiscard; /* - Update resampler. This is wrong. Need to instead update it on a per MP3 frame basis. Also broken for cases when - the sample rate is being reduced in my testing. Should work fine when the input and output sample rate is the same - or a clean multiple. - */ - pMP3->src.algo.linear.alpha = (drmp3_int64)pMP3->currentPCMFrame * ((double)pMP3->src.config.sampleRateIn / pMP3->src.config.sampleRateOut); /* <-- Cast to int64 is required for VC6. */ - pMP3->src.algo.linear.alpha = pMP3->src.algo.linear.alpha - (drmp3_uint32)(pMP3->src.algo.linear.alpha); - if (pMP3->src.algo.linear.alpha > 0) { - pMP3->src.algo.linear.isPrevFramesLoaded = 1; - } - - /* 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. */ @@ -3488,7 +3841,7 @@ drmp3_bool32 drmp3_seek_to_pcm_frame__seek_table(drmp3* pMP3, drmp3_uint64 frame return drmp3_seek_forward_by_pcm_frames__brute_force(pMP3, leftoverFrames); } -drmp3_bool32 drmp3_seek_to_pcm_frame(drmp3* pMP3, drmp3_uint64 frameIndex) +DRMP3_API drmp3_bool32 drmp3_seek_to_pcm_frame(drmp3* pMP3, drmp3_uint64 frameIndex) { if (pMP3 == NULL || pMP3->onSeek == NULL) { return DRMP3_FALSE; @@ -3506,12 +3859,11 @@ drmp3_bool32 drmp3_seek_to_pcm_frame(drmp3* pMP3, drmp3_uint64 frameIndex) } } -drmp3_bool32 drmp3_get_mp3_and_pcm_frame_count(drmp3* pMP3, drmp3_uint64* pMP3FrameCount, drmp3_uint64* pPCMFrameCount) +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; - float totalPCMFrameCountFractionalPart; if (pMP3 == NULL) { return DRMP3_FALSE; @@ -3537,25 +3889,15 @@ drmp3_bool32 drmp3_get_mp3_and_pcm_frame_count(drmp3* pMP3, drmp3_uint64* pMP3Fr totalPCMFrameCount = 0; totalMP3FrameCount = 0; - totalPCMFrameCountFractionalPart = 0; /* <-- With resampling there will be a fractional part to each MP3 frame that we need to accumulate. */ for (;;) { - drmp3_uint32 pcmFramesInCurrentMP3FrameIn; - float srcRatio; - float pcmFramesInCurrentMP3FrameOutF; - drmp3_uint32 pcmFramesInCurrentMP3FrameOut; + drmp3_uint32 pcmFramesInCurrentMP3Frame; - pcmFramesInCurrentMP3FrameIn = drmp3_decode_next_frame_ex(pMP3, NULL, DRMP3_FALSE); - if (pcmFramesInCurrentMP3FrameIn == 0) { + pcmFramesInCurrentMP3Frame = drmp3_decode_next_frame_ex(pMP3, NULL); + if (pcmFramesInCurrentMP3Frame == 0) { break; } - srcRatio = (float)pMP3->mp3FrameSampleRate / (float)pMP3->sampleRate; - DRMP3_ASSERT(srcRatio > 0); - - pcmFramesInCurrentMP3FrameOutF = totalPCMFrameCountFractionalPart + (pcmFramesInCurrentMP3FrameIn / srcRatio); - pcmFramesInCurrentMP3FrameOut = (drmp3_uint32)pcmFramesInCurrentMP3FrameOutF; - totalPCMFrameCountFractionalPart = pcmFramesInCurrentMP3FrameOutF - pcmFramesInCurrentMP3FrameOut; - totalPCMFrameCount += pcmFramesInCurrentMP3FrameOut; + totalPCMFrameCount += pcmFramesInCurrentMP3Frame; totalMP3FrameCount += 1; } @@ -3578,7 +3920,7 @@ drmp3_bool32 drmp3_get_mp3_and_pcm_frame_count(drmp3* pMP3, drmp3_uint64* pMP3Fr return DRMP3_TRUE; } -drmp3_uint64 drmp3_get_pcm_frame_count(drmp3* pMP3) +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)) { @@ -3588,7 +3930,7 @@ drmp3_uint64 drmp3_get_pcm_frame_count(drmp3* pMP3) return totalPCMFrameCount; } -drmp3_uint64 drmp3_get_mp3_frame_count(drmp3* pMP3) +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)) { @@ -3598,7 +3940,7 @@ drmp3_uint64 drmp3_get_mp3_frame_count(drmp3* pMP3) return totalMP3FrameCount; } -void drmp3__accumulate_running_pcm_frame_count(drmp3* pMP3, drmp3_uint32 pcmFrameCountIn, drmp3_uint64* pRunningPCMFrameCount, float* pRunningPCMFrameCountFractionalPart) +static void drmp3__accumulate_running_pcm_frame_count(drmp3* pMP3, drmp3_uint32 pcmFrameCountIn, drmp3_uint64* pRunningPCMFrameCount, float* pRunningPCMFrameCountFractionalPart) { float srcRatio; float pcmFrameCountOutF; @@ -3619,7 +3961,7 @@ typedef struct drmp3_uint64 pcmFrameIndex; /* <-- After sample rate conversion. */ } drmp3__seeking_mp3_frame_info; -drmp3_bool32 drmp3_calculate_seek_points(drmp3* pMP3, drmp3_uint32* pSeekPointCount, drmp3_seek_point* pSeekPoints) +DRMP3_API drmp3_bool32 drmp3_calculate_seek_points(drmp3* pMP3, drmp3_uint32* pSeekPointCount, drmp3_seek_point* pSeekPoints) { drmp3_uint32 seekPointCount; drmp3_uint64 currentPCMFrame; @@ -3688,7 +4030,7 @@ drmp3_bool32 drmp3_calculate_seek_points(drmp3* pMP3, drmp3_uint32* pSeekPointCo 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, DRMP3_FALSE); + pcmFramesInCurrentMP3FrameIn = drmp3_decode_next_frame_ex(pMP3, NULL); if (pcmFramesInCurrentMP3FrameIn == 0) { return DRMP3_FALSE; /* This should never happen. */ } @@ -3720,19 +4062,19 @@ drmp3_bool32 drmp3_calculate_seek_points(drmp3* pMP3, drmp3_uint32* pSeekPointCo 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) { + 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; + 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, DRMP3_TRUE); + pcmFramesInCurrentMP3FrameIn = drmp3_decode_next_frame_ex(pMP3, NULL); if (pcmFramesInCurrentMP3FrameIn == 0) { pSeekPoints[iSeekPoint].seekPosInBytes = mp3FrameInfo[0].bytePos; pSeekPoints[iSeekPoint].pcmFrameIndex = nextTargetPCMFrame; @@ -3759,7 +4101,7 @@ drmp3_bool32 drmp3_calculate_seek_points(drmp3* pMP3, drmp3_uint32* pSeekPointCo return DRMP3_TRUE; } -drmp3_bool32 drmp3_bind_seek_table(drmp3* pMP3, drmp3_uint32 seekPointCount, drmp3_seek_point* pSeekPoints) +DRMP3_API drmp3_bool32 drmp3_bind_seek_table(drmp3* pMP3, drmp3_uint32 seekPointCount, drmp3_seek_point* pSeekPoints) { if (pMP3 == NULL) { return DRMP3_FALSE; @@ -3779,7 +4121,7 @@ drmp3_bool32 drmp3_bind_seek_table(drmp3* pMP3, drmp3_uint32 seekPointCount, drm } -float* drmp3__full_read_and_close_f32(drmp3* pMP3, drmp3_config* pConfig, drmp3_uint64* pTotalFrameCount) +static float* drmp3__full_read_and_close_f32(drmp3* pMP3, drmp3_config* pConfig, drmp3_uint64* pTotalFrameCount) { drmp3_uint64 totalFramesRead = 0; drmp3_uint64 framesCapacity = 0; @@ -3789,7 +4131,7 @@ float* drmp3__full_read_and_close_f32(drmp3* pMP3, drmp3_config* pConfig, drmp3_ DRMP3_ASSERT(pMP3 != NULL); for (;;) { - drmp3_uint64 framesToReadRightNow = drmp3_countof(temp) / pMP3->channels; + drmp3_uint64 framesToReadRightNow = DRMP3_COUNTOF(temp) / pMP3->channels; drmp3_uint64 framesJustRead = drmp3_read_pcm_frames_f32(pMP3, framesToReadRightNow, temp); if (framesJustRead == 0) { break; @@ -3833,8 +4175,8 @@ float* drmp3__full_read_and_close_f32(drmp3* pMP3, drmp3_config* pConfig, drmp3_ } if (pConfig != NULL) { - pConfig->outputChannels = pMP3->channels; - pConfig->outputSampleRate = pMP3->sampleRate; + pConfig->channels = pMP3->channels; + pConfig->sampleRate = pMP3->sampleRate; } drmp3_uninit(pMP3); @@ -3846,7 +4188,7 @@ float* drmp3__full_read_and_close_f32(drmp3* pMP3, drmp3_config* pConfig, drmp3_ return pFrames; } -drmp3_int16* drmp3__full_read_and_close_s16(drmp3* pMP3, drmp3_config* pConfig, drmp3_uint64* pTotalFrameCount) +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; @@ -3856,7 +4198,7 @@ drmp3_int16* drmp3__full_read_and_close_s16(drmp3* pMP3, drmp3_config* pConfig, DRMP3_ASSERT(pMP3 != NULL); for (;;) { - drmp3_uint64 framesToReadRightNow = drmp3_countof(temp) / pMP3->channels; + drmp3_uint64 framesToReadRightNow = DRMP3_COUNTOF(temp) / pMP3->channels; drmp3_uint64 framesJustRead = drmp3_read_pcm_frames_s16(pMP3, framesToReadRightNow, temp); if (framesJustRead == 0) { break; @@ -3900,8 +4242,8 @@ drmp3_int16* drmp3__full_read_and_close_s16(drmp3* pMP3, drmp3_config* pConfig, } if (pConfig != NULL) { - pConfig->outputChannels = pMP3->channels; - pConfig->outputSampleRate = pMP3->sampleRate; + pConfig->channels = pMP3->channels; + pConfig->sampleRate = pMP3->sampleRate; } drmp3_uninit(pMP3); @@ -3914,20 +4256,20 @@ drmp3_int16* drmp3__full_read_and_close_s16(drmp3* pMP3, drmp3_config* pConfig, } -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 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, pConfig, pAllocationCallbacks)) { + if (!drmp3_init(&mp3, onRead, onSeek, pUserData, pAllocationCallbacks)) { return NULL; } return drmp3__full_read_and_close_f32(&mp3, pConfig, pTotalFrameCount); } -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 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, pConfig, pAllocationCallbacks)) { + if (!drmp3_init(&mp3, onRead, onSeek, pUserData, pAllocationCallbacks)) { return NULL; } @@ -3935,20 +4277,20 @@ drmp3_int16* drmp3_open_and_read_pcm_frames_s16(drmp3_read_proc onRead, drmp3_se } -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 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, pConfig, pAllocationCallbacks)) { + if (!drmp3_init_memory(&mp3, pData, dataSize, pAllocationCallbacks)) { return NULL; } return drmp3__full_read_and_close_f32(&mp3, pConfig, pTotalFrameCount); } -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_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, pConfig, pAllocationCallbacks)) { + if (!drmp3_init_memory(&mp3, pData, dataSize, pAllocationCallbacks)) { return NULL; } @@ -3957,20 +4299,20 @@ drmp3_int16* drmp3_open_memory_and_read_pcm_frames_s16(const void* pData, size_t #ifndef DR_MP3_NO_STDIO -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 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, pConfig, pAllocationCallbacks)) { + if (!drmp3_init_file(&mp3, filePath, pAllocationCallbacks)) { return NULL; } return drmp3__full_read_and_close_f32(&mp3, pConfig, pTotalFrameCount); } -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_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, pConfig, pAllocationCallbacks)) { + if (!drmp3_init_file(&mp3, filePath, pAllocationCallbacks)) { return NULL; } @@ -3978,7 +4320,16 @@ drmp3_int16* drmp3_open_file_and_read_pcm_frames_s16(const char* filePath, drmp3 } #endif -void drmp3_free(void* p, const drmp3_allocation_callbacks* pAllocationCallbacks) +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); @@ -4005,8 +4356,117 @@ DIFFERENCES BETWEEN minimp3 AND dr_mp3 */ /* +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.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. @@ -4058,9 +4518,9 @@ 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 DR_MP3_DEFAULT_CHANNELS and - DR_MP3_DEFAULT_SAMPLE_RATE when they are set to 0. To use the old behaviour, just set the relevant property to - DR_MP3_DEFAULT_CHANNELS or DR_MP3_DEFAULT_SAMPLE_RATE. + - 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 diff --git a/ext/dr_wav.h b/ext/dr_wav.h @@ -1,5373 +0,0 @@ -/* -WAV audio loader and writer. Choice of public domain or MIT-0. See license statements at the end of this file. -dr_wav - v0.11.5 - 2020-03-07 - -David Reid - mackron@gmail.com -*/ - -/* -RELEASE NOTES - v0.11.0 -======================= -Version 0.11.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 DRWAV_MALLOC, DRWAV_REALLOC and DRWAV_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 drwav_allocation_callbacks object to drwav_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); - } - - ... - - drwav_allocation_callbacks allocationCallbacks; - allocationCallbacks.pUserData = &myData; - allocationCallbacks.onMalloc = my_malloc; - allocationCallbacks.onRealloc = my_realloc; - allocationCallbacks.onFree = my_free; - drwav_init_file(&wav, "my_file.wav", &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_wav to use defaults which is the same as DRWAV_MALLOC, -DRWAV_REALLOC and DRWAV_FREE and the equivalent of how it worked in previous versions. - -Every API that opens a drwav object now takes this extra parameter. These include the following: - - drwav_init() - drwav_init_ex() - drwav_init_file() - drwav_init_file_ex() - drwav_init_file_w() - drwav_init_file_w_ex() - drwav_init_memory() - drwav_init_memory_ex() - drwav_init_write() - drwav_init_write_sequential() - drwav_init_write_sequential_pcm_frames() - drwav_init_file_write() - drwav_init_file_write_sequential() - drwav_init_file_write_sequential_pcm_frames() - drwav_init_file_write_w() - drwav_init_file_write_sequential_w() - drwav_init_file_write_sequential_pcm_frames_w() - drwav_init_memory_write() - drwav_init_memory_write_sequential() - drwav_init_memory_write_sequential_pcm_frames() - drwav_open_and_read_pcm_frames_s16() - drwav_open_and_read_pcm_frames_f32() - drwav_open_and_read_pcm_frames_s32() - drwav_open_file_and_read_pcm_frames_s16() - drwav_open_file_and_read_pcm_frames_f32() - drwav_open_file_and_read_pcm_frames_s32() - drwav_open_file_and_read_pcm_frames_s16_w() - drwav_open_file_and_read_pcm_frames_f32_w() - drwav_open_file_and_read_pcm_frames_s32_w() - drwav_open_memory_and_read_pcm_frames_s16() - drwav_open_memory_and_read_pcm_frames_f32() - drwav_open_memory_and_read_pcm_frames_s32() - -Endian Improvements -------------------- -Previously, the following APIs returned little-endian audio data. These now return native-endian data. This improves compatibility -on big-endian architectures. - - drwav_read_pcm_frames() - drwav_read_pcm_frames_s16() - drwav_read_pcm_frames_s32() - drwav_read_pcm_frames_f32() - drwav_open_and_read_pcm_frames_s16() - drwav_open_and_read_pcm_frames_s32() - drwav_open_and_read_pcm_frames_f32() - drwav_open_file_and_read_pcm_frames_s16() - drwav_open_file_and_read_pcm_frames_s32() - drwav_open_file_and_read_pcm_frames_f32() - drwav_open_file_and_read_pcm_frames_s16_w() - drwav_open_file_and_read_pcm_frames_s32_w() - drwav_open_file_and_read_pcm_frames_f32_w() - drwav_open_memory_and_read_pcm_frames_s16() - drwav_open_memory_and_read_pcm_frames_s32() - drwav_open_memory_and_read_pcm_frames_f32() - -APIs have been added to give you explicit control over whether or not audio data is read or written in big- or little-endian byte -order: - - drwav_read_pcm_frames_le() - drwav_read_pcm_frames_be() - drwav_read_pcm_frames_s16le() - drwav_read_pcm_frames_s16be() - drwav_read_pcm_frames_f32le() - drwav_read_pcm_frames_f32be() - drwav_read_pcm_frames_s32le() - drwav_read_pcm_frames_s32be() - drwav_write_pcm_frames_le() - drwav_write_pcm_frames_be() - -Removed APIs ------------- -The following APIs were deprecated in version 0.10.0 and have now been removed: - - drwav_open() - drwav_open_ex() - drwav_open_write() - drwav_open_write_sequential() - drwav_open_file() - drwav_open_file_ex() - drwav_open_file_write() - drwav_open_file_write_sequential() - drwav_open_memory() - drwav_open_memory_ex() - drwav_open_memory_write() - drwav_open_memory_write_sequential() - drwav_close() - - - -RELEASE NOTES - v0.10.0 -======================= -Version 0.10.0 has breaking API changes. There are no significant bug fixes in this release, so if you are affected you do -not need to upgrade. - -Removed APIs ------------- -The following APIs were deprecated in version 0.9.0 and have been completely removed in version 0.10.0: - - drwav_read() - drwav_read_s16() - drwav_read_f32() - drwav_read_s32() - drwav_seek_to_sample() - drwav_write() - drwav_open_and_read_s16() - drwav_open_and_read_f32() - drwav_open_and_read_s32() - drwav_open_file_and_read_s16() - drwav_open_file_and_read_f32() - drwav_open_file_and_read_s32() - drwav_open_memory_and_read_s16() - drwav_open_memory_and_read_f32() - drwav_open_memory_and_read_s32() - drwav::totalSampleCount - -See release notes for version 0.9.0 at the bottom of this file for replacement APIs. - -Deprecated APIs ---------------- -The following APIs have been deprecated. There is a confusing and completely arbitrary difference between drwav_init*() and -drwav_open*(), where drwav_init*() initializes a pre-allocated drwav object, whereas drwav_open*() will first allocated a -drwav object on the heap and then initialize it. drwav_open*() has been deprecated which means you must now use a pre- -allocated drwav object with drwav_init*(). If you need the previous functionality, you can just do a malloc() followed by -a called to one of the drwav_init*() APIs. - - drwav_open() - drwav_open_ex() - drwav_open_write() - drwav_open_write_sequential() - drwav_open_file() - drwav_open_file_ex() - drwav_open_file_write() - drwav_open_file_write_sequential() - drwav_open_memory() - drwav_open_memory_ex() - drwav_open_memory_write() - drwav_open_memory_write_sequential() - drwav_close() - -These APIs will be removed completely in a future version. The rationale for this change is to remove confusion between the -two different ways to initialize a drwav object. -*/ - -/* -USAGE -===== -This is a single-file library. To use it, do something like the following in one .c file. - #define DR_WAV_IMPLEMENTATION - #include "dr_wav.h" - -You can then #include this file in other parts of the program as you would with any other header file. Do something -like the following to read audio data: - - drwav wav; - if (!drwav_init_file(&wav, "my_song.wav")) { - // Error opening WAV file. - } - - drwav_int32* pDecodedInterleavedPCMFrames = malloc(wav.totalPCMFrameCount * wav.channels * sizeof(drwav_int32)); - size_t numberOfSamplesActuallyDecoded = drwav_read_pcm_frames_s32(&wav, wav.totalPCMFrameCount, pDecodedInterleavedPCMFrames); - - ... - - drwav_uninit(&wav); - -If you just want to quickly open and read the audio data in a single operation you can do something like this: - - unsigned int channels; - unsigned int sampleRate; - drwav_uint64 totalPCMFrameCount; - float* pSampleData = drwav_open_file_and_read_pcm_frames_f32("my_song.wav", &channels, &sampleRate, &totalPCMFrameCount); - if (pSampleData == NULL) { - // Error opening and reading WAV file. - } - - ... - - drwav_free(pSampleData); - -The examples above use versions of the API that convert the audio data to a consistent format (32-bit signed PCM, in -this case), but you can still output the audio data in its internal format (see notes below for supported formats): - - size_t framesRead = drwav_read_pcm_frames(&wav, wav.totalPCMFrameCount, pDecodedInterleavedPCMFrames); - -You can also read the raw bytes of audio data, which could be useful if dr_wav does not have native support for -a particular data format: - - size_t bytesRead = drwav_read_raw(&wav, bytesToRead, pRawDataBuffer); - - -dr_wav can also be used to output WAV files. This does not currently support compressed formats. To use this, look at -drwav_init_write(), drwav_init_file_write(), etc. Use drwav_write_pcm_frames() to write samples, or drwav_write_raw() -to write raw data in the "data" chunk. - - drwav_data_format format; - format.container = drwav_container_riff; // <-- drwav_container_riff = normal WAV files, drwav_container_w64 = Sony Wave64. - format.format = DR_WAVE_FORMAT_PCM; // <-- Any of the DR_WAVE_FORMAT_* codes. - format.channels = 2; - format.sampleRate = 44100; - format.bitsPerSample = 16; - drwav_init_file_write(&wav, "data/recording.wav", &format, NULL); - - ... - - drwav_uint64 framesWritten = drwav_write_pcm_frames(pWav, frameCount, pSamples); - - -dr_wav has seamless support the Sony Wave64 format. The decoder will automatically detect it and it should Just Work -without any manual intervention. - - -OPTIONS -======= -#define these options before including this file. - -#define DR_WAV_NO_CONVERSION_API - Disables conversion APIs such as drwav_read_pcm_frames_f32() and drwav_s16_to_f32(). - -#define DR_WAV_NO_STDIO - Disables APIs that initialize a decoder from a file such as drwav_init_file(), drwav_init_file_write(), etc. - - - -QUICK NOTES -=========== -- Samples are always interleaved. -- The default read function does not do any data conversion. Use drwav_read_pcm_frames_f32(), drwav_read_pcm_frames_s32() - and drwav_read_pcm_frames_s16() to read and convert audio data to 32-bit floating point, signed 32-bit integer and - signed 16-bit integer samples respectively. Tested and supported internal formats include the following: - - Unsigned 8-bit PCM - - Signed 12-bit PCM - - Signed 16-bit PCM - - Signed 24-bit PCM - - Signed 32-bit PCM - - IEEE 32-bit floating point - - IEEE 64-bit floating point - - A-law and u-law - - Microsoft ADPCM - - IMA ADPCM (DVI, format code 0x11) -- dr_wav will try to read the WAV file as best it can, even if it's not strictly conformant to the WAV format. -*/ - -#ifndef dr_wav_h -#define dr_wav_h - -#include <stddef.h> - -#if defined(_MSC_VER) && _MSC_VER < 1600 -typedef signed char drwav_int8; -typedef unsigned char drwav_uint8; -typedef signed short drwav_int16; -typedef unsigned short drwav_uint16; -typedef signed int drwav_int32; -typedef unsigned int drwav_uint32; -typedef signed __int64 drwav_int64; -typedef unsigned __int64 drwav_uint64; -#else -#include <stdint.h> -typedef int8_t drwav_int8; -typedef uint8_t drwav_uint8; -typedef int16_t drwav_int16; -typedef uint16_t drwav_uint16; -typedef int32_t drwav_int32; -typedef uint32_t drwav_uint32; -typedef int64_t drwav_int64; -typedef uint64_t drwav_uint64; -#endif -typedef drwav_uint8 drwav_bool8; -typedef drwav_uint32 drwav_bool32; -#define DRWAV_TRUE 1 -#define DRWAV_FALSE 0 - -#ifdef __cplusplus -extern "C" { -#endif - -typedef drwav_int32 drwav_result; -#define DRWAV_SUCCESS 0 -#define DRWAV_ERROR -1 -#define DRWAV_INVALID_ARGS -2 -#define DRWAV_INVALID_OPERATION -3 -#define DRWAV_INVALID_FILE -100 -#define DRWAV_EOF -101 - -/* Common data formats. */ -#define DR_WAVE_FORMAT_PCM 0x1 -#define DR_WAVE_FORMAT_ADPCM 0x2 -#define DR_WAVE_FORMAT_IEEE_FLOAT 0x3 -#define DR_WAVE_FORMAT_ALAW 0x6 -#define DR_WAVE_FORMAT_MULAW 0x7 -#define DR_WAVE_FORMAT_DVI_ADPCM 0x11 -#define DR_WAVE_FORMAT_EXTENSIBLE 0xFFFE - -/* Constants. */ -#ifndef DRWAV_MAX_SMPL_LOOPS -#define DRWAV_MAX_SMPL_LOOPS 1 -#endif - -/* Flags to pass into drwav_init_ex(), etc. */ -#define DRWAV_SEQUENTIAL 0x00000001 - -typedef enum -{ - drwav_seek_origin_start, - drwav_seek_origin_current -} drwav_seek_origin; - -typedef enum -{ - drwav_container_riff, - drwav_container_w64 -} drwav_container; - -typedef struct -{ - union - { - drwav_uint8 fourcc[4]; - drwav_uint8 guid[16]; - } id; - - /* The size in bytes of the chunk. */ - drwav_uint64 sizeInBytes; - - /* - RIFF = 2 byte alignment. - W64 = 8 byte alignment. - */ - unsigned int paddingSize; -} drwav_chunk_header; - -/* -Callback for when data is read. Return value is the number of bytes actually read. - -pUserData [in] The user data that was passed to drwav_init() 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 (* drwav_read_proc)(void* pUserData, void* pBufferOut, size_t bytesToRead); - -/* -Callback for when data is written. Returns value is the number of bytes actually written. - -pUserData [in] The user data that was passed to drwav_init_write() and family. -pData [out] A pointer to the data to write. -bytesToWrite [in] The number of bytes to write. - -Returns the number of bytes actually written. - -If the return value differs from bytesToWrite, it indicates an error. -*/ -typedef size_t (* drwav_write_proc)(void* pUserData, const void* pData, size_t bytesToWrite); - -/* -Callback for when data needs to be seeked. - -pUserData [in] The user data that was passed to drwav_init() 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 drwav_seek_origin_start or drwav_seek_origin_current. -*/ -typedef drwav_bool32 (* drwav_seek_proc)(void* pUserData, int offset, drwav_seek_origin origin); - -/* -Callback for when drwav_init_ex() finds a chunk. - -pChunkUserData [in] The user data that was passed to the pChunkUserData parameter of drwav_init_ex() and family. -onRead [in] A pointer to the function to call when reading. -onSeek [in] A pointer to the function to call when seeking. -pReadSeekUserData [in] The user data that was passed to the pReadSeekUserData parameter of drwav_init_ex() and family. -pChunkHeader [in] A pointer to an object containing basic header information about the chunk. Use this to identify the chunk. - -Returns the number of bytes read + seeked. - -To read data from the chunk, call onRead(), passing in pReadSeekUserData as the first parameter. Do the same -for seeking with onSeek(). The return value must be the total number of bytes you have read _plus_ seeked. - -You must not attempt to read beyond the boundary of the chunk. -*/ -typedef drwav_uint64 (* drwav_chunk_proc)(void* pChunkUserData, drwav_read_proc onRead, drwav_seek_proc onSeek, void* pReadSeekUserData, const drwav_chunk_header* pChunkHeader); - -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); -} drwav_allocation_callbacks; - -/* Structure for internal use. Only used for loaders opened with drwav_init_memory(). */ -typedef struct -{ - const drwav_uint8* data; - size_t dataSize; - size_t currentReadPos; -} drwav__memory_stream; - -/* Structure for internal use. Only used for writers opened with drwav_init_memory_write(). */ -typedef struct -{ - void** ppData; - size_t* pDataSize; - size_t dataSize; - size_t dataCapacity; - size_t currentWritePos; -} drwav__memory_stream_write; - -typedef struct -{ - drwav_container container; /* RIFF, W64. */ - drwav_uint32 format; /* DR_WAVE_FORMAT_* */ - drwav_uint32 channels; - drwav_uint32 sampleRate; - drwav_uint32 bitsPerSample; -} drwav_data_format; - -typedef struct -{ - /* - The format tag exactly as specified in the wave file's "fmt" chunk. This can be used by applications - that require support for data formats not natively supported by dr_wav. - */ - drwav_uint16 formatTag; - - /* The number of channels making up the audio data. When this is set to 1 it is mono, 2 is stereo, etc. */ - drwav_uint16 channels; - - /* The sample rate. Usually set to something like 44100. */ - drwav_uint32 sampleRate; - - /* Average bytes per second. You probably don't need this, but it's left here for informational purposes. */ - drwav_uint32 avgBytesPerSec; - - /* Block align. This is equal to the number of channels * bytes per sample. */ - drwav_uint16 blockAlign; - - /* Bits per sample. */ - drwav_uint16 bitsPerSample; - - /* The size of the extended data. Only used internally for validation, but left here for informational purposes. */ - drwav_uint16 extendedSize; - - /* - The number of valid bits per sample. When <formatTag> is equal to WAVE_FORMAT_EXTENSIBLE, <bitsPerSample> - is always rounded up to the nearest multiple of 8. This variable contains information about exactly how - many bits a valid per sample. Mainly used for informational purposes. - */ - drwav_uint16 validBitsPerSample; - - /* The channel mask. Not used at the moment. */ - drwav_uint32 channelMask; - - /* The sub-format, exactly as specified by the wave file. */ - drwav_uint8 subFormat[16]; -} drwav_fmt; - -typedef struct -{ - drwav_uint32 cuePointId; - drwav_uint32 type; - drwav_uint32 start; - drwav_uint32 end; - drwav_uint32 fraction; - drwav_uint32 playCount; -} drwav_smpl_loop; - - typedef struct -{ - drwav_uint32 manufacturer; - drwav_uint32 product; - drwav_uint32 samplePeriod; - drwav_uint32 midiUnityNotes; - drwav_uint32 midiPitchFraction; - drwav_uint32 smpteFormat; - drwav_uint32 smpteOffset; - drwav_uint32 numSampleLoops; - drwav_uint32 samplerData; - drwav_smpl_loop loops[DRWAV_MAX_SMPL_LOOPS]; -} drwav_smpl; - -typedef struct -{ - /* A pointer to the function to call when more data is needed. */ - drwav_read_proc onRead; - - /* A pointer to the function to call when data needs to be written. Only used when the drwav object is opened in write mode. */ - drwav_write_proc onWrite; - - /* A pointer to the function to call when the wav file needs to be seeked. */ - drwav_seek_proc onSeek; - - /* The user data to pass to callbacks. */ - void* pUserData; - - /* Allocation callbacks. */ - drwav_allocation_callbacks allocationCallbacks; - - - /* Whether or not the WAV file is formatted as a standard RIFF file or W64. */ - drwav_container container; - - - /* Structure containing format information exactly as specified by the wav file. */ - drwav_fmt fmt; - - /* The sample rate. Will be set to something like 44100. */ - drwav_uint32 sampleRate; - - /* The number of channels. This will be set to 1 for monaural streams, 2 for stereo, etc. */ - drwav_uint16 channels; - - /* The bits per sample. Will be set to something like 16, 24, etc. */ - drwav_uint16 bitsPerSample; - - /* Equal to fmt.formatTag, or the value specified by fmt.subFormat if fmt.formatTag is equal to 65534 (WAVE_FORMAT_EXTENSIBLE). */ - drwav_uint16 translatedFormatTag; - - /* The total number of PCM frames making up the audio data. */ - drwav_uint64 totalPCMFrameCount; - - - /* The size in bytes of the data chunk. */ - drwav_uint64 dataChunkDataSize; - - /* The position in the stream of the first byte of the data chunk. This is used for seeking. */ - drwav_uint64 dataChunkDataPos; - - /* The number of bytes remaining in the data chunk. */ - drwav_uint64 bytesRemaining; - - - /* - Only used in sequential write mode. Keeps track of the desired size of the "data" chunk at the point of initialization time. Always - set to 0 for non-sequential writes and when the drwav object is opened in read mode. Used for validation. - */ - drwav_uint64 dataChunkDataSizeTargetWrite; - - /* Keeps track of whether or not the wav writer was initialized in sequential mode. */ - drwav_bool32 isSequentialWrite; - - - /* smpl chunk. */ - drwav_smpl smpl; - - - /* A hack to avoid a DRWAV_MALLOC() when opening a decoder with drwav_init_memory(). */ - drwav__memory_stream memoryStream; - drwav__memory_stream_write memoryStreamWrite; - - /* Generic data for compressed formats. This data is shared across all block-compressed formats. */ - struct - { - drwav_uint64 iCurrentPCMFrame; /* The index of the next PCM frame that will be read by drwav_read_*(). This is used with "totalPCMFrameCount" to ensure we don't read excess samples at the end of the last block. */ - } compressed; - - /* Microsoft ADPCM specific data. */ - struct - { - drwav_uint32 bytesRemainingInBlock; - drwav_uint16 predictor[2]; - drwav_int32 delta[2]; - drwav_int32 cachedFrames[4]; /* Samples are stored in this cache during decoding. */ - drwav_uint32 cachedFrameCount; - drwav_int32 prevFrames[2][2]; /* The previous 2 samples for each channel (2 channels at most). */ - } msadpcm; - - /* IMA ADPCM specific data. */ - struct - { - drwav_uint32 bytesRemainingInBlock; - drwav_int32 predictor[2]; - drwav_int32 stepIndex[2]; - drwav_int32 cachedFrames[16]; /* Samples are stored in this cache during decoding. */ - drwav_uint32 cachedFrameCount; - } ima; -} drwav; - - -/* -Initializes a pre-allocated drwav object for reading. - -pWav [out] A pointer to the drwav object being initialized. -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. -onChunk [in, optional] The function to call when a chunk is enumerated at initialized time. -pUserData, pReadSeekUserData [in, optional] A pointer to application defined data that will be passed to onRead and onSeek. -pChunkUserData [in, optional] A pointer to application defined data that will be passed to onChunk. -flags [in, optional] A set of flags for controlling how things are loaded. - -Returns true if successful; false otherwise. - -Close the loader with drwav_uninit(). - -This is the lowest level function for initializing a WAV file. You can also use drwav_init_file() and drwav_init_memory() -to open the stream from a file or from a block of memory respectively. - -Possible values for flags: - DRWAV_SEQUENTIAL: Never perform a backwards seek while loading. This disables the chunk callback and will cause this function - to return as soon as the data chunk is found. Any chunks after the data chunk will be ignored. - -drwav_init() is equivalent to "drwav_init_ex(pWav, onRead, onSeek, NULL, pUserData, NULL, 0);". - -The onChunk callback is not called for the WAVE or FMT chunks. The contents of the FMT chunk can be read from pWav->fmt -after the function returns. - -See also: drwav_init_file(), drwav_init_memory(), drwav_uninit() -*/ -drwav_bool32 drwav_init(drwav* pWav, drwav_read_proc onRead, drwav_seek_proc onSeek, void* pUserData, const drwav_allocation_callbacks* pAllocationCallbacks); -drwav_bool32 drwav_init_ex(drwav* pWav, drwav_read_proc onRead, drwav_seek_proc onSeek, drwav_chunk_proc onChunk, void* pReadSeekUserData, void* pChunkUserData, drwav_uint32 flags, const drwav_allocation_callbacks* pAllocationCallbacks); - -/* -Initializes a pre-allocated drwav object for writing. - -onWrite [in] The function to call when data needs to be written. -onSeek [in] The function to call when the write position needs to move. -pUserData [in, optional] A pointer to application defined data that will be passed to onWrite and onSeek. - -Returns true if successful; false otherwise. - -Close the writer with drwav_uninit(). - -This is the lowest level function for initializing a WAV file. You can also use drwav_init_file_write() and drwav_init_memory_write() -to open the stream from a file or from a block of memory respectively. - -If the total sample count is known, you can use drwav_init_write_sequential(). This avoids the need for dr_wav to perform -a post-processing step for storing the total sample count and the size of the data chunk which requires a backwards seek. - -See also: drwav_init_file_write(), drwav_init_memory_write(), drwav_uninit() -*/ -drwav_bool32 drwav_init_write(drwav* pWav, const drwav_data_format* pFormat, drwav_write_proc onWrite, drwav_seek_proc onSeek, void* pUserData, const drwav_allocation_callbacks* pAllocationCallbacks); -drwav_bool32 drwav_init_write_sequential(drwav* pWav, const drwav_data_format* pFormat, drwav_uint64 totalSampleCount, drwav_write_proc onWrite, void* pUserData, const drwav_allocation_callbacks* pAllocationCallbacks); -drwav_bool32 drwav_init_write_sequential_pcm_frames(drwav* pWav, const drwav_data_format* pFormat, drwav_uint64 totalPCMFrameCount, drwav_write_proc onWrite, void* pUserData, const drwav_allocation_callbacks* pAllocationCallbacks); - -/* -Utility function to determine the target size of the entire data to be written (including all headers and chunks). - -Returns the target size in bytes. - -Useful if the application needs to know the size to allocate. - -Only writing to the RIFF chunk and one data chunk is currently supported. - -See also: drwav_init_write(), drwav_init_file_write(), drwav_init_memory_write() -*/ -drwav_uint64 drwav_target_write_size_bytes(drwav_data_format const *format, drwav_uint64 totalSampleCount); - -/* -Uninitializes the given drwav object. - -Use this only for objects initialized with drwav_init*() functions (drwav_init(), drwav_init_ex(), drwav_init_write(), drwav_init_write_sequential()). -*/ -drwav_result drwav_uninit(drwav* pWav); - - -/* -Reads raw audio data. - -This is the lowest level function for reading audio data. It simply reads the given number of -bytes of the raw internal sample data. - -Consider using drwav_read_pcm_frames_s16(), drwav_read_pcm_frames_s32() or drwav_read_pcm_frames_f32() for -reading sample data in a consistent format. - -Returns the number of bytes actually read. -*/ -size_t drwav_read_raw(drwav* pWav, size_t bytesToRead, void* pBufferOut); - -/* -Reads up to the specified number of PCM frames from the WAV file. - -The output data will be in the file's internal format, converted to native-endian byte order. Use -drwav_read_pcm_frames_s16/f32/s32() to read data in a specific format. - -If the return value is less than <framesToRead> it means the end of the file has been reached or -you have requested more PCM frames than can possibly fit in the output buffer. - -This function will only work when sample data is of a fixed size and uncompressed. If you are -using a compressed format consider using drwav_read_raw() or drwav_read_pcm_frames_s16/s32/f32(). -*/ -drwav_uint64 drwav_read_pcm_frames(drwav* pWav, drwav_uint64 framesToRead, void* pBufferOut); -drwav_uint64 drwav_read_pcm_frames_le(drwav* pWav, drwav_uint64 framesToRead, void* pBufferOut); -drwav_uint64 drwav_read_pcm_frames_be(drwav* pWav, drwav_uint64 framesToRead, void* pBufferOut); - -/* -Seeks to the given PCM frame. - -Returns true if successful; false otherwise. -*/ -drwav_bool32 drwav_seek_to_pcm_frame(drwav* pWav, drwav_uint64 targetFrameIndex); - - -/* -Writes raw audio data. - -Returns the number of bytes actually written. If this differs from bytesToWrite, it indicates an error. -*/ -size_t drwav_write_raw(drwav* pWav, size_t bytesToWrite, const void* pData); - -/* -Writes PCM frames. - -Returns the number of PCM frames written. - -Input samples need to be in native-endian byte order. On big-endian architectures the input data will be converted to -little-endian. Use drwav_write_raw() to write raw audio data without performing any conversion. -*/ -drwav_uint64 drwav_write_pcm_frames(drwav* pWav, drwav_uint64 framesToWrite, const void* pData); -drwav_uint64 drwav_write_pcm_frames_le(drwav* pWav, drwav_uint64 framesToWrite, const void* pData); -drwav_uint64 drwav_write_pcm_frames_be(drwav* pWav, drwav_uint64 framesToWrite, const void* pData); - - -/* Conversion Utilities */ -#ifndef DR_WAV_NO_CONVERSION_API - -/* -Reads a chunk of audio data and converts it to signed 16-bit PCM samples. - -Returns the number of PCM frames actually read. - -If the return value is less than <framesToRead> it means the end of the file has been reached. -*/ -drwav_uint64 drwav_read_pcm_frames_s16(drwav* pWav, drwav_uint64 framesToRead, drwav_int16* pBufferOut); -drwav_uint64 drwav_read_pcm_frames_s16le(drwav* pWav, drwav_uint64 framesToRead, drwav_int16* pBufferOut); -drwav_uint64 drwav_read_pcm_frames_s16be(drwav* pWav, drwav_uint64 framesToRead, drwav_int16* pBufferOut); - -/* Low-level function for converting unsigned 8-bit PCM samples to signed 16-bit PCM samples. */ -void drwav_u8_to_s16(drwav_int16* pOut, const drwav_uint8* pIn, size_t sampleCount); - -/* Low-level function for converting signed 24-bit PCM samples to signed 16-bit PCM samples. */ -void drwav_s24_to_s16(drwav_int16* pOut, const drwav_uint8* pIn, size_t sampleCount); - -/* Low-level function for converting signed 32-bit PCM samples to signed 16-bit PCM samples. */ -void drwav_s32_to_s16(drwav_int16* pOut, const drwav_int32* pIn, size_t sampleCount); - -/* Low-level function for converting IEEE 32-bit floating point samples to signed 16-bit PCM samples. */ -void drwav_f32_to_s16(drwav_int16* pOut, const float* pIn, size_t sampleCount); - -/* Low-level function for converting IEEE 64-bit floating point samples to signed 16-bit PCM samples. */ -void drwav_f64_to_s16(drwav_int16* pOut, const double* pIn, size_t sampleCount); - -/* Low-level function for converting A-law samples to signed 16-bit PCM samples. */ -void drwav_alaw_to_s16(drwav_int16* pOut, const drwav_uint8* pIn, size_t sampleCount); - -/* Low-level function for converting u-law samples to signed 16-bit PCM samples. */ -void drwav_mulaw_to_s16(drwav_int16* pOut, const drwav_uint8* pIn, size_t sampleCount); - - -/* -Reads a chunk of audio data and converts it to IEEE 32-bit floating point samples. - -Returns the number of PCM frames actually read. - -If the return value is less than <framesToRead> it means the end of the file has been reached. -*/ -drwav_uint64 drwav_read_pcm_frames_f32(drwav* pWav, drwav_uint64 framesToRead, float* pBufferOut); -drwav_uint64 drwav_read_pcm_frames_f32le(drwav* pWav, drwav_uint64 framesToRead, float* pBufferOut); -drwav_uint64 drwav_read_pcm_frames_f32be(drwav* pWav, drwav_uint64 framesToRead, float* pBufferOut); - -/* Low-level function for converting unsigned 8-bit PCM samples to IEEE 32-bit floating point samples. */ -void drwav_u8_to_f32(float* pOut, const drwav_uint8* pIn, size_t sampleCount); - -/* Low-level function for converting signed 16-bit PCM samples to IEEE 32-bit floating point samples. */ -void drwav_s16_to_f32(float* pOut, const drwav_int16* pIn, size_t sampleCount); - -/* Low-level function for converting signed 24-bit PCM samples to IEEE 32-bit floating point samples. */ -void drwav_s24_to_f32(float* pOut, const drwav_uint8* pIn, size_t sampleCount); - -/* Low-level function for converting signed 32-bit PCM samples to IEEE 32-bit floating point samples. */ -void drwav_s32_to_f32(float* pOut, const drwav_int32* pIn, size_t sampleCount); - -/* Low-level function for converting IEEE 64-bit floating point samples to IEEE 32-bit floating point samples. */ -void drwav_f64_to_f32(float* pOut, const double* pIn, size_t sampleCount); - -/* Low-level function for converting A-law samples to IEEE 32-bit floating point samples. */ -void drwav_alaw_to_f32(float* pOut, const drwav_uint8* pIn, size_t sampleCount); - -/* Low-level function for converting u-law samples to IEEE 32-bit floating point samples. */ -void drwav_mulaw_to_f32(float* pOut, const drwav_uint8* pIn, size_t sampleCount); - - -/* -Reads a chunk of audio data and converts it to signed 32-bit PCM samples. - -Returns the number of PCM frames actually read. - -If the return value is less than <framesToRead> it means the end of the file has been reached. -*/ -drwav_uint64 drwav_read_pcm_frames_s32(drwav* pWav, drwav_uint64 framesToRead, drwav_int32* pBufferOut); -drwav_uint64 drwav_read_pcm_frames_s32le(drwav* pWav, drwav_uint64 framesToRead, drwav_int32* pBufferOut); -drwav_uint64 drwav_read_pcm_frames_s32be(drwav* pWav, drwav_uint64 framesToRead, drwav_int32* pBufferOut); - -/* Low-level function for converting unsigned 8-bit PCM samples to signed 32-bit PCM samples. */ -void drwav_u8_to_s32(drwav_int32* pOut, const drwav_uint8* pIn, size_t sampleCount); - -/* Low-level function for converting signed 16-bit PCM samples to signed 32-bit PCM samples. */ -void drwav_s16_to_s32(drwav_int32* pOut, const drwav_int16* pIn, size_t sampleCount); - -/* Low-level function for converting signed 24-bit PCM samples to signed 32-bit PCM samples. */ -void drwav_s24_to_s32(drwav_int32* pOut, const drwav_uint8* pIn, size_t sampleCount); - -/* Low-level function for converting IEEE 32-bit floating point samples to signed 32-bit PCM samples. */ -void drwav_f32_to_s32(drwav_int32* pOut, const float* pIn, size_t sampleCount); - -/* Low-level function for converting IEEE 64-bit floating point samples to signed 32-bit PCM samples. */ -void drwav_f64_to_s32(drwav_int32* pOut, const double* pIn, size_t sampleCount); - -/* Low-level function for converting A-law samples to signed 32-bit PCM samples. */ -void drwav_alaw_to_s32(drwav_int32* pOut, const drwav_uint8* pIn, size_t sampleCount); - -/* Low-level function for converting u-law samples to signed 32-bit PCM samples. */ -void drwav_mulaw_to_s32(drwav_int32* pOut, const drwav_uint8* pIn, size_t sampleCount); - -#endif /* DR_WAV_NO_CONVERSION_API */ - - -/* High-Level Convenience Helpers */ - -#ifndef DR_WAV_NO_STDIO -/* -Helper for initializing a wave file for reading using stdio. - -This holds the internal FILE object until drwav_uninit() is called. Keep this in mind if you're caching drwav -objects because the operating system may restrict the number of file handles an application can have open at -any given time. -*/ -drwav_bool32 drwav_init_file(drwav* pWav, const char* filename, const drwav_allocation_callbacks* pAllocationCallbacks); -drwav_bool32 drwav_init_file_ex(drwav* pWav, const char* filename, drwav_chunk_proc onChunk, void* pChunkUserData, drwav_uint32 flags, const drwav_allocation_callbacks* pAllocationCallbacks); -drwav_bool32 drwav_init_file_w(drwav* pWav, const wchar_t* filename, const drwav_allocation_callbacks* pAllocationCallbacks); -drwav_bool32 drwav_init_file_ex_w(drwav* pWav, const wchar_t* filename, drwav_chunk_proc onChunk, void* pChunkUserData, drwav_uint32 flags, const drwav_allocation_callbacks* pAllocationCallbacks); - -/* -Helper for initializing a wave file for writing using stdio. - -This holds the internal FILE object until drwav_uninit() is called. Keep this in mind if you're caching drwav -objects because the operating system may restrict the number of file handles an application can have open at -any given time. -*/ -drwav_bool32 drwav_init_file_write(drwav* pWav, const char* filename, const drwav_data_format* pFormat, const drwav_allocation_callbacks* pAllocationCallbacks); -drwav_bool32 drwav_init_file_write_sequential(drwav* pWav, const char* filename, const drwav_data_format* pFormat, drwav_uint64 totalSampleCount, const drwav_allocation_callbacks* pAllocationCallbacks); -drwav_bool32 drwav_init_file_write_sequential_pcm_frames(drwav* pWav, const char* filename, const drwav_data_format* pFormat, drwav_uint64 totalPCMFrameCount, const drwav_allocation_callbacks* pAllocationCallbacks); -drwav_bool32 drwav_init_file_write_w(drwav* pWav, const wchar_t* filename, const drwav_data_format* pFormat, const drwav_allocation_callbacks* pAllocationCallbacks); -drwav_bool32 drwav_init_file_write_sequential_w(drwav* pWav, const wchar_t* filename, const drwav_data_format* pFormat, drwav_uint64 totalSampleCount, const drwav_allocation_callbacks* pAllocationCallbacks); -drwav_bool32 drwav_init_file_write_sequential_pcm_frames_w(drwav* pWav, const wchar_t* filename, const drwav_data_format* pFormat, drwav_uint64 totalPCMFrameCount, const drwav_allocation_callbacks* pAllocationCallbacks); -#endif /* DR_WAV_NO_STDIO */ - -/* -Helper for initializing a loader from a pre-allocated memory buffer. - -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 drwav object. - -The buffer should contain the contents of the entire wave file, not just the sample data. -*/ -drwav_bool32 drwav_init_memory(drwav* pWav, const void* data, size_t dataSize, const drwav_allocation_callbacks* pAllocationCallbacks); -drwav_bool32 drwav_init_memory_ex(drwav* pWav, const void* data, size_t dataSize, drwav_chunk_proc onChunk, void* pChunkUserData, drwav_uint32 flags, const drwav_allocation_callbacks* pAllocationCallbacks); - -/* -Helper for initializing a writer which outputs data to a memory buffer. - -dr_wav will manage the memory allocations, however it is up to the caller to free the data with drwav_free(). - -The buffer will remain allocated even after drwav_uninit() is called. Indeed, the buffer should not be -considered valid until after drwav_uninit() has been called anyway. -*/ -drwav_bool32 drwav_init_memory_write(drwav* pWav, void** ppData, size_t* pDataSize, const drwav_data_format* pFormat, const drwav_allocation_callbacks* pAllocationCallbacks); -drwav_bool32 drwav_init_memory_write_sequential(drwav* pWav, void** ppData, size_t* pDataSize, const drwav_data_format* pFormat, drwav_uint64 totalSampleCount, const drwav_allocation_callbacks* pAllocationCallbacks); -drwav_bool32 drwav_init_memory_write_sequential_pcm_frames(drwav* pWav, void** ppData, size_t* pDataSize, const drwav_data_format* pFormat, drwav_uint64 totalPCMFrameCount, const drwav_allocation_callbacks* pAllocationCallbacks); - - -#ifndef DR_WAV_NO_CONVERSION_API -/* -Opens and reads an entire wav file in a single operation. - -The return value is a heap-allocated buffer containing the audio data. Use drwav_free() to free the buffer. -*/ -drwav_int16* drwav_open_and_read_pcm_frames_s16(drwav_read_proc onRead, drwav_seek_proc onSeek, void* pUserData, unsigned int* channelsOut, unsigned int* sampleRateOut, drwav_uint64* totalFrameCountOut, const drwav_allocation_callbacks* pAllocationCallbacks); -float* drwav_open_and_read_pcm_frames_f32(drwav_read_proc onRead, drwav_seek_proc onSeek, void* pUserData, unsigned int* channelsOut, unsigned int* sampleRateOut, drwav_uint64* totalFrameCountOut, const drwav_allocation_callbacks* pAllocationCallbacks); -drwav_int32* drwav_open_and_read_pcm_frames_s32(drwav_read_proc onRead, drwav_seek_proc onSeek, void* pUserData, unsigned int* channelsOut, unsigned int* sampleRateOut, drwav_uint64* totalFrameCountOut, const drwav_allocation_callbacks* pAllocationCallbacks); -#ifndef DR_WAV_NO_STDIO -/* -Opens and decodes an entire wav file in a single operation. - -The return value is a heap-allocated buffer containing the audio data. Use drwav_free() to free the buffer. -*/ -drwav_int16* drwav_open_file_and_read_pcm_frames_s16(const char* filename, unsigned int* channelsOut, unsigned int* sampleRateOut, drwav_uint64* totalFrameCountOut, const drwav_allocation_callbacks* pAllocationCallbacks); -float* drwav_open_file_and_read_pcm_frames_f32(const char* filename, unsigned int* channelsOut, unsigned int* sampleRateOut, drwav_uint64* totalFrameCountOut, const drwav_allocation_callbacks* pAllocationCallbacks); -drwav_int32* drwav_open_file_and_read_pcm_frames_s32(const char* filename, unsigned int* channelsOut, unsigned int* sampleRateOut, drwav_uint64* totalFrameCountOut, const drwav_allocation_callbacks* pAllocationCallbacks); -drwav_int16* drwav_open_file_and_read_pcm_frames_s16_w(const wchar_t* filename, unsigned int* channelsOut, unsigned int* sampleRateOut, drwav_uint64* totalFrameCountOut, const drwav_allocation_callbacks* pAllocationCallbacks); -float* drwav_open_file_and_read_pcm_frames_f32_w(const wchar_t* filename, unsigned int* channelsOut, unsigned int* sampleRateOut, drwav_uint64* totalFrameCountOut, const drwav_allocation_callbacks* pAllocationCallbacks); -drwav_int32* drwav_open_file_and_read_pcm_frames_s32_w(const wchar_t* filename, unsigned int* channelsOut, unsigned int* sampleRateOut, drwav_uint64* totalFrameCountOut, const drwav_allocation_callbacks* pAllocationCallbacks); -#endif -/* -Opens and decodes an entire wav file from a block of memory in a single operation. - -The return value is a heap-allocated buffer containing the audio data. Use drwav_free() to free the buffer. -*/ -drwav_int16* drwav_open_memory_and_read_pcm_frames_s16(const void* data, size_t dataSize, unsigned int* channelsOut, unsigned int* sampleRateOut, drwav_uint64* totalFrameCountOut, const drwav_allocation_callbacks* pAllocationCallbacks); -float* drwav_open_memory_and_read_pcm_frames_f32(const void* data, size_t dataSize, unsigned int* channelsOut, unsigned int* sampleRateOut, drwav_uint64* totalFrameCountOut, const drwav_allocation_callbacks* pAllocationCallbacks); -drwav_int32* drwav_open_memory_and_read_pcm_frames_s32(const void* data, size_t dataSize, unsigned int* channelsOut, unsigned int* sampleRateOut, drwav_uint64* totalFrameCountOut, const drwav_allocation_callbacks* pAllocationCallbacks); -#endif - -/* Frees data that was allocated internally by dr_wav. */ -void drwav_free(void* p, const drwav_allocation_callbacks* pAllocationCallbacks); - -#ifdef __cplusplus -} -#endif -#endif /* dr_wav_h */ - - -/************************************************************************************************************************************************************ - ************************************************************************************************************************************************************ - - IMPLEMENTATION - - ************************************************************************************************************************************************************ - ************************************************************************************************************************************************************/ -#ifdef DR_WAV_IMPLEMENTATION -#include <stdlib.h> -#include <string.h> /* For memcpy(), memset() */ -#include <limits.h> /* For INT_MAX */ - -#ifndef DR_WAV_NO_STDIO -#include <stdio.h> -#include <wchar.h> -#endif - -/* Standard library stuff. */ -#ifndef DRWAV_ASSERT -#include <assert.h> -#define DRWAV_ASSERT(expression) assert(expression) -#endif -#ifndef DRWAV_MALLOC -#define DRWAV_MALLOC(sz) malloc((sz)) -#endif -#ifndef DRWAV_REALLOC -#define DRWAV_REALLOC(p, sz) realloc((p), (sz)) -#endif -#ifndef DRWAV_FREE -#define DRWAV_FREE(p) free((p)) -#endif -#ifndef DRWAV_COPY_MEMORY -#define DRWAV_COPY_MEMORY(dst, src, sz) memcpy((dst), (src), (sz)) -#endif -#ifndef DRWAV_ZERO_MEMORY -#define DRWAV_ZERO_MEMORY(p, sz) memset((p), 0, (sz)) -#endif - -#define drwav_countof(x) (sizeof(x) / sizeof(x[0])) -#define drwav_align(x, a) ((((x) + (a) - 1) / (a)) * (a)) -#define drwav_min(a, b) (((a) < (b)) ? (a) : (b)) -#define drwav_max(a, b) (((a) > (b)) ? (a) : (b)) -#define drwav_clamp(x, lo, hi) (drwav_max((lo), drwav_min((hi), (x)))) - -#define DRWAV_MAX_SIMD_VECTOR_SIZE 64 /* 64 for AVX-512 in the future. */ - -/* CPU architecture. */ -#if defined(__x86_64__) || defined(_M_X64) - #define DRWAV_X64 -#elif defined(__i386) || defined(_M_IX86) - #define DRWAV_X86 -#elif defined(__arm__) || defined(_M_ARM) - #define DRWAV_ARM -#endif - -#ifdef _MSC_VER - #define DRWAV_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 DRWAV_INLINE __inline__ __attribute__((always_inline)) - #else - #define DRWAV_INLINE inline __attribute__((always_inline)) - #endif -#else - #define DRWAV_INLINE -#endif - -#if defined(SIZE_MAX) - #define DRWAV_SIZE_MAX SIZE_MAX -#else - #if defined(_WIN64) || defined(_LP64) || defined(__LP64__) - #define DRWAV_SIZE_MAX ((drwav_uint64)0xFFFFFFFFFFFFFFFF) - #else - #define DRWAV_SIZE_MAX 0xFFFFFFFF - #endif -#endif - -#if defined(_MSC_VER) && _MSC_VER >= 1400 - #define DRWAV_HAS_BYTESWAP16_INTRINSIC - #define DRWAV_HAS_BYTESWAP32_INTRINSIC - #define DRWAV_HAS_BYTESWAP64_INTRINSIC -#elif defined(__clang__) - #if defined(__has_builtin) - #if __has_builtin(__builtin_bswap16) - #define DRWAV_HAS_BYTESWAP16_INTRINSIC - #endif - #if __has_builtin(__builtin_bswap32) - #define DRWAV_HAS_BYTESWAP32_INTRINSIC - #endif - #if __has_builtin(__builtin_bswap64) - #define DRWAV_HAS_BYTESWAP64_INTRINSIC - #endif - #endif -#elif defined(__GNUC__) - #if ((__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 3)) - #define DRWAV_HAS_BYTESWAP32_INTRINSIC - #define DRWAV_HAS_BYTESWAP64_INTRINSIC - #endif - #if ((__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8)) - #define DRWAV_HAS_BYTESWAP16_INTRINSIC - #endif -#endif - -/* -These limits are used for basic validation when initializing the decoder. If you exceed these limits, first of all: what on Earth are -you doing?! (Let me know, I'd be curious!) Second, you can adjust these by #define-ing them before the dr_wav implementation. -*/ -#ifndef DRWAV_MAX_SAMPLE_RATE -#define DRWAV_MAX_SAMPLE_RATE 384000 -#endif -#ifndef DRWAV_MAX_CHANNELS -#define DRWAV_MAX_CHANNELS 256 -#endif -#ifndef DRWAV_MAX_BITS_PER_SAMPLE -#define DRWAV_MAX_BITS_PER_SAMPLE 64 -#endif - -static const drwav_uint8 drwavGUID_W64_RIFF[16] = {0x72,0x69,0x66,0x66, 0x2E,0x91, 0xCF,0x11, 0xA5,0xD6, 0x28,0xDB,0x04,0xC1,0x00,0x00}; /* 66666972-912E-11CF-A5D6-28DB04C10000 */ -static const drwav_uint8 drwavGUID_W64_WAVE[16] = {0x77,0x61,0x76,0x65, 0xF3,0xAC, 0xD3,0x11, 0x8C,0xD1, 0x00,0xC0,0x4F,0x8E,0xDB,0x8A}; /* 65766177-ACF3-11D3-8CD1-00C04F8EDB8A */ -static const drwav_uint8 drwavGUID_W64_JUNK[16] = {0x6A,0x75,0x6E,0x6B, 0xF3,0xAC, 0xD3,0x11, 0x8C,0xD1, 0x00,0xC0,0x4F,0x8E,0xDB,0x8A}; /* 6B6E756A-ACF3-11D3-8CD1-00C04F8EDB8A */ -static const drwav_uint8 drwavGUID_W64_FMT [16] = {0x66,0x6D,0x74,0x20, 0xF3,0xAC, 0xD3,0x11, 0x8C,0xD1, 0x00,0xC0,0x4F,0x8E,0xDB,0x8A}; /* 20746D66-ACF3-11D3-8CD1-00C04F8EDB8A */ -static const drwav_uint8 drwavGUID_W64_FACT[16] = {0x66,0x61,0x63,0x74, 0xF3,0xAC, 0xD3,0x11, 0x8C,0xD1, 0x00,0xC0,0x4F,0x8E,0xDB,0x8A}; /* 74636166-ACF3-11D3-8CD1-00C04F8EDB8A */ -static const drwav_uint8 drwavGUID_W64_DATA[16] = {0x64,0x61,0x74,0x61, 0xF3,0xAC, 0xD3,0x11, 0x8C,0xD1, 0x00,0xC0,0x4F,0x8E,0xDB,0x8A}; /* 61746164-ACF3-11D3-8CD1-00C04F8EDB8A */ -static const drwav_uint8 drwavGUID_W64_SMPL[16] = {0x73,0x6D,0x70,0x6C, 0xF3,0xAC, 0xD3,0x11, 0x8C,0xD1, 0x00,0xC0,0x4F,0x8E,0xDB,0x8A}; /* 6C706D73-ACF3-11D3-8CD1-00C04F8EDB8A */ - -static DRWAV_INLINE drwav_bool32 drwav__guid_equal(const drwav_uint8 a[16], const drwav_uint8 b[16]) -{ - int i; - for (i = 0; i < 16; i += 1) { - if (a[i] != b[i]) { - return DRWAV_FALSE; - } - } - - return DRWAV_TRUE; -} - -static DRWAV_INLINE drwav_bool32 drwav__fourcc_equal(const unsigned char* a, const char* b) -{ - return - a[0] == b[0] && - a[1] == b[1] && - a[2] == b[2] && - a[3] == b[3]; -} - - - -static DRWAV_INLINE int drwav__is_little_endian() -{ -#if defined(DRWAV_X86) || defined(DRWAV_X64) - return DRWAV_TRUE; -#elif defined(__BYTE_ORDER) && defined(__LITTLE_ENDIAN) && __BYTE_ORDER == __LITTLE_ENDIAN - return DRWAV_TRUE; -#else - int n = 1; - return (*(char*)&n) == 1; -#endif -} - -static DRWAV_INLINE unsigned short drwav__bytes_to_u16(const unsigned char* data) -{ - return (data[0] << 0) | (data[1] << 8); -} - -static DRWAV_INLINE short drwav__bytes_to_s16(const unsigned char* data) -{ - return (short)drwav__bytes_to_u16(data); -} - -static DRWAV_INLINE unsigned int drwav__bytes_to_u32(const unsigned char* data) -{ - return (data[0] << 0) | (data[1] << 8) | (data[2] << 16) | (data[3] << 24); -} - -static DRWAV_INLINE drwav_uint64 drwav__bytes_to_u64(const unsigned char* data) -{ - return - ((drwav_uint64)data[0] << 0) | ((drwav_uint64)data[1] << 8) | ((drwav_uint64)data[2] << 16) | ((drwav_uint64)data[3] << 24) | - ((drwav_uint64)data[4] << 32) | ((drwav_uint64)data[5] << 40) | ((drwav_uint64)data[6] << 48) | ((drwav_uint64)data[7] << 56); -} - -static DRWAV_INLINE void drwav__bytes_to_guid(const unsigned char* data, drwav_uint8* guid) -{ - int i; - for (i = 0; i < 16; ++i) { - guid[i] = data[i]; - } -} - - -static DRWAV_INLINE drwav_uint16 drwav__bswap16(drwav_uint16 n) -{ -#ifdef DRWAV_HAS_BYTESWAP16_INTRINSIC - #if defined(_MSC_VER) - return _byteswap_ushort(n); - #elif defined(__GNUC__) || defined(__clang__) - return __builtin_bswap16(n); - #else - #error "This compiler does not support the byte swap intrinsic." - #endif -#else - return ((n & 0xFF00) >> 8) | - ((n & 0x00FF) << 8); -#endif -} - -static DRWAV_INLINE drwav_uint32 drwav__bswap32(drwav_uint32 n) -{ -#ifdef DRWAV_HAS_BYTESWAP32_INTRINSIC - #if defined(_MSC_VER) - return _byteswap_ulong(n); - #elif defined(__GNUC__) || defined(__clang__) - #if defined(DRWAV_ARM) && (defined(__ARM_ARCH) && __ARM_ARCH >= 6) && !defined(DRWAV_64BIT) /* <-- 64-bit inline assembly has not been tested, so disabling for now. */ - /* Inline assembly optimized implementation for ARM. In my testing, GCC does not generate optimized code with __builtin_bswap32(). */ - drwav_uint32 r; - __asm__ __volatile__ ( - #if defined(DRWAV_64BIT) - "rev %w[out], %w[in]" : [out]"=r"(r) : [in]"r"(n) /* <-- This is untested. If someone in the community could test this, that would be appreciated! */ - #else - "rev %[out], %[in]" : [out]"=r"(r) : [in]"r"(n) - #endif - ); - return r; - #else - return __builtin_bswap32(n); - #endif - #else - #error "This compiler does not support the byte swap intrinsic." - #endif -#else - return ((n & 0xFF000000) >> 24) | - ((n & 0x00FF0000) >> 8) | - ((n & 0x0000FF00) << 8) | - ((n & 0x000000FF) << 24); -#endif -} - -static DRWAV_INLINE drwav_uint64 drwav__bswap64(drwav_uint64 n) -{ -#ifdef DRWAV_HAS_BYTESWAP64_INTRINSIC - #if defined(_MSC_VER) - return _byteswap_uint64(n); - #elif defined(__GNUC__) || defined(__clang__) - return __builtin_bswap64(n); - #else - #error "This compiler does not support the byte swap intrinsic." - #endif -#else - return ((n & (drwav_uint64)0xFF00000000000000) >> 56) | - ((n & (drwav_uint64)0x00FF000000000000) >> 40) | - ((n & (drwav_uint64)0x0000FF0000000000) >> 24) | - ((n & (drwav_uint64)0x000000FF00000000) >> 8) | - ((n & (drwav_uint64)0x00000000FF000000) << 8) | - ((n & (drwav_uint64)0x0000000000FF0000) << 24) | - ((n & (drwav_uint64)0x000000000000FF00) << 40) | - ((n & (drwav_uint64)0x00000000000000FF) << 56); -#endif -} - - -static DRWAV_INLINE drwav_int16 drwav__bswap_s16(drwav_int16 n) -{ - return (drwav_int16)drwav__bswap16((drwav_uint16)n); -} - -static DRWAV_INLINE void drwav__bswap_samples_s16(drwav_int16* pSamples, drwav_uint64 sampleCount) -{ - drwav_uint64 iSample; - for (iSample = 0; iSample < sampleCount; iSample += 1) { - pSamples[iSample] = drwav__bswap_s16(pSamples[iSample]); - } -} - - -static DRWAV_INLINE void drwav__bswap_s24(drwav_uint8* p) -{ - drwav_uint8 t; - t = p[0]; - p[0] = p[2]; - p[2] = t; -} - -static DRWAV_INLINE void drwav__bswap_samples_s24(drwav_uint8* pSamples, drwav_uint64 sampleCount) -{ - drwav_uint64 iSample; - for (iSample = 0; iSample < sampleCount; iSample += 1) { - drwav_uint8* pSample = pSamples + (iSample*3); - drwav__bswap_s24(pSample); - } -} - - -static DRWAV_INLINE drwav_int32 drwav__bswap_s32(drwav_int32 n) -{ - return (drwav_int32)drwav__bswap32((drwav_uint32)n); -} - -static DRWAV_INLINE void drwav__bswap_samples_s32(drwav_int32* pSamples, drwav_uint64 sampleCount) -{ - drwav_uint64 iSample; - for (iSample = 0; iSample < sampleCount; iSample += 1) { - pSamples[iSample] = drwav__bswap_s32(pSamples[iSample]); - } -} - - -static DRWAV_INLINE float drwav__bswap_f32(float n) -{ - union { - drwav_uint32 i; - float f; - } x; - x.f = n; - x.i = drwav__bswap32(x.i); - - return x.f; -} - -static DRWAV_INLINE void drwav__bswap_samples_f32(float* pSamples, drwav_uint64 sampleCount) -{ - drwav_uint64 iSample; - for (iSample = 0; iSample < sampleCount; iSample += 1) { - pSamples[iSample] = drwav__bswap_f32(pSamples[iSample]); - } -} - - -static DRWAV_INLINE double drwav__bswap_f64(double n) -{ - union { - drwav_uint64 i; - double f; - } x; - x.f = n; - x.i = drwav__bswap64(x.i); - - return x.f; -} - -static DRWAV_INLINE void drwav__bswap_samples_f64(double* pSamples, drwav_uint64 sampleCount) -{ - drwav_uint64 iSample; - for (iSample = 0; iSample < sampleCount; iSample += 1) { - pSamples[iSample] = drwav__bswap_f64(pSamples[iSample]); - } -} - - -static DRWAV_INLINE void drwav__bswap_samples_pcm(void* pSamples, drwav_uint64 sampleCount, drwav_uint32 bytesPerSample) -{ - /* Assumes integer PCM. Floating point PCM is done in drwav__bswap_samples_ieee(). */ - switch (bytesPerSample) - { - case 2: /* s16, s12 (loosely packed) */ - { - drwav__bswap_samples_s16((drwav_int16*)pSamples, sampleCount); - } break; - case 3: /* s24 */ - { - drwav__bswap_samples_s24((drwav_uint8*)pSamples, sampleCount); - } break; - case 4: /* s32 */ - { - drwav__bswap_samples_s32((drwav_int32*)pSamples, sampleCount); - } break; - default: - { - /* Unsupported format. */ - DRWAV_ASSERT(DRWAV_FALSE); - } break; - } -} - -static DRWAV_INLINE void drwav__bswap_samples_ieee(void* pSamples, drwav_uint64 sampleCount, drwav_uint32 bytesPerSample) -{ - switch (bytesPerSample) - { - #if 0 /* Contributions welcome for f16 support. */ - case 2: /* f16 */ - { - drwav__bswap_samples_f16((drwav_float16*)pSamples, sampleCount); - } break; - #endif - case 4: /* f32 */ - { - drwav__bswap_samples_f32((float*)pSamples, sampleCount); - } break; - case 8: /* f64 */ - { - drwav__bswap_samples_f64((double*)pSamples, sampleCount); - } break; - default: - { - /* Unsupported format. */ - DRWAV_ASSERT(DRWAV_FALSE); - } break; - } -} - -static DRWAV_INLINE void drwav__bswap_samples(void* pSamples, drwav_uint64 sampleCount, drwav_uint32 bytesPerSample, drwav_uint16 format) -{ - switch (format) - { - case DR_WAVE_FORMAT_PCM: - { - drwav__bswap_samples_pcm(pSamples, sampleCount, bytesPerSample); - } break; - - case DR_WAVE_FORMAT_IEEE_FLOAT: - { - drwav__bswap_samples_ieee(pSamples, sampleCount, bytesPerSample); - } break; - - case DR_WAVE_FORMAT_ALAW: - case DR_WAVE_FORMAT_MULAW: - { - drwav__bswap_samples_s16((drwav_int16*)pSamples, sampleCount); - } break; - - case DR_WAVE_FORMAT_ADPCM: - case DR_WAVE_FORMAT_DVI_ADPCM: - default: - { - /* Unsupported format. */ - DRWAV_ASSERT(DRWAV_FALSE); - } break; - } -} - - -static void* drwav__malloc_default(size_t sz, void* pUserData) -{ - (void)pUserData; - return DRWAV_MALLOC(sz); -} - -static void* drwav__realloc_default(void* p, size_t sz, void* pUserData) -{ - (void)pUserData; - return DRWAV_REALLOC(p, sz); -} - -static void drwav__free_default(void* p, void* pUserData) -{ - (void)pUserData; - DRWAV_FREE(p); -} - - -static void* drwav__malloc_from_callbacks(size_t sz, const drwav_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* drwav__realloc_from_callbacks(void* p, size_t szNew, size_t szOld, const drwav_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) { - DRWAV_COPY_MEMORY(p2, p, szOld); - pAllocationCallbacks->onFree(p, pAllocationCallbacks->pUserData); - } - - return p2; - } - - return NULL; -} - -static void drwav__free_from_callbacks(void* p, const drwav_allocation_callbacks* pAllocationCallbacks) -{ - if (p == NULL || pAllocationCallbacks == NULL) { - return; - } - - if (pAllocationCallbacks->onFree != NULL) { - pAllocationCallbacks->onFree(p, pAllocationCallbacks->pUserData); - } -} - - -drwav_allocation_callbacks drwav_copy_allocation_callbacks_or_defaults(const drwav_allocation_callbacks* pAllocationCallbacks) -{ - if (pAllocationCallbacks != NULL) { - /* Copy. */ - return *pAllocationCallbacks; - } else { - /* Defaults. */ - drwav_allocation_callbacks allocationCallbacks; - allocationCallbacks.pUserData = NULL; - allocationCallbacks.onMalloc = drwav__malloc_default; - allocationCallbacks.onRealloc = drwav__realloc_default; - allocationCallbacks.onFree = drwav__free_default; - return allocationCallbacks; - } -} - - -static DRWAV_INLINE drwav_bool32 drwav__is_compressed_format_tag(drwav_uint16 formatTag) -{ - return - formatTag == DR_WAVE_FORMAT_ADPCM || - formatTag == DR_WAVE_FORMAT_DVI_ADPCM; -} - -static unsigned int drwav__chunk_padding_size_riff(drwav_uint64 chunkSize) -{ - return (unsigned int)(chunkSize % 2); -} - -static unsigned int drwav__chunk_padding_size_w64(drwav_uint64 chunkSize) -{ - return (unsigned int)(chunkSize % 8); -} - -drwav_uint64 drwav_read_pcm_frames_s16__msadpcm(drwav* pWav, drwav_uint64 samplesToRead, drwav_int16* pBufferOut); -drwav_uint64 drwav_read_pcm_frames_s16__ima(drwav* pWav, drwav_uint64 samplesToRead, drwav_int16* pBufferOut); -drwav_bool32 drwav_init_write__internal(drwav* pWav, const drwav_data_format* pFormat, drwav_uint64 totalSampleCount); - -static drwav_result drwav__read_chunk_header(drwav_read_proc onRead, void* pUserData, drwav_container container, drwav_uint64* pRunningBytesReadOut, drwav_chunk_header* pHeaderOut) -{ - if (container == drwav_container_riff) { - unsigned char sizeInBytes[4]; - - if (onRead(pUserData, pHeaderOut->id.fourcc, 4) != 4) { - return DRWAV_EOF; - } - - if (onRead(pUserData, sizeInBytes, 4) != 4) { - return DRWAV_INVALID_FILE; - } - - pHeaderOut->sizeInBytes = drwav__bytes_to_u32(sizeInBytes); - pHeaderOut->paddingSize = drwav__chunk_padding_size_riff(pHeaderOut->sizeInBytes); - *pRunningBytesReadOut += 8; - } else { - unsigned char sizeInBytes[8]; - - if (onRead(pUserData, pHeaderOut->id.guid, 16) != 16) { - return DRWAV_EOF; - } - - if (onRead(pUserData, sizeInBytes, 8) != 8) { - return DRWAV_INVALID_FILE; - } - - pHeaderOut->sizeInBytes = drwav__bytes_to_u64(sizeInBytes) - 24; /* <-- Subtract 24 because w64 includes the size of the header. */ - pHeaderOut->paddingSize = drwav__chunk_padding_size_w64(pHeaderOut->sizeInBytes); - *pRunningBytesReadOut += 24; - } - - return DRWAV_SUCCESS; -} - -static drwav_bool32 drwav__seek_forward(drwav_seek_proc onSeek, drwav_uint64 offset, void* pUserData) -{ - drwav_uint64 bytesRemainingToSeek = offset; - while (bytesRemainingToSeek > 0) { - if (bytesRemainingToSeek > 0x7FFFFFFF) { - if (!onSeek(pUserData, 0x7FFFFFFF, drwav_seek_origin_current)) { - return DRWAV_FALSE; - } - bytesRemainingToSeek -= 0x7FFFFFFF; - } else { - if (!onSeek(pUserData, (int)bytesRemainingToSeek, drwav_seek_origin_current)) { - return DRWAV_FALSE; - } - bytesRemainingToSeek = 0; - } - } - - return DRWAV_TRUE; -} - -static drwav_bool32 drwav__seek_from_start(drwav_seek_proc onSeek, drwav_uint64 offset, void* pUserData) -{ - if (offset <= 0x7FFFFFFF) { - return onSeek(pUserData, (int)offset, drwav_seek_origin_start); - } - - /* Larger than 32-bit seek. */ - if (!onSeek(pUserData, 0x7FFFFFFF, drwav_seek_origin_start)) { - return DRWAV_FALSE; - } - offset -= 0x7FFFFFFF; - - for (;;) { - if (offset <= 0x7FFFFFFF) { - return onSeek(pUserData, (int)offset, drwav_seek_origin_current); - } - - if (!onSeek(pUserData, 0x7FFFFFFF, drwav_seek_origin_current)) { - return DRWAV_FALSE; - } - offset -= 0x7FFFFFFF; - } - - /* Should never get here. */ - /*return DRWAV_TRUE; */ -} - - -static drwav_bool32 drwav__read_fmt(drwav_read_proc onRead, drwav_seek_proc onSeek, void* pUserData, drwav_container container, drwav_uint64* pRunningBytesReadOut, drwav_fmt* fmtOut) -{ - drwav_chunk_header header; - unsigned char fmt[16]; - - if (drwav__read_chunk_header(onRead, pUserData, container, pRunningBytesReadOut, &header) != DRWAV_SUCCESS) { - return DRWAV_FALSE; - } - - - /* Skip non-fmt chunks. */ - while ((container == drwav_container_riff && !drwav__fourcc_equal(header.id.fourcc, "fmt ")) || (container == drwav_container_w64 && !drwav__guid_equal(header.id.guid, drwavGUID_W64_FMT))) { - if (!drwav__seek_forward(onSeek, header.sizeInBytes + header.paddingSize, pUserData)) { - return DRWAV_FALSE; - } - *pRunningBytesReadOut += header.sizeInBytes + header.paddingSize; - - /* Try the next header. */ - if (drwav__read_chunk_header(onRead, pUserData, container, pRunningBytesReadOut, &header) != DRWAV_SUCCESS) { - return DRWAV_FALSE; - } - } - - - /* Validation. */ - if (container == drwav_container_riff) { - if (!drwav__fourcc_equal(header.id.fourcc, "fmt ")) { - return DRWAV_FALSE; - } - } else { - if (!drwav__guid_equal(header.id.guid, drwavGUID_W64_FMT)) { - return DRWAV_FALSE; - } - } - - - if (onRead(pUserData, fmt, sizeof(fmt)) != sizeof(fmt)) { - return DRWAV_FALSE; - } - *pRunningBytesReadOut += sizeof(fmt); - - fmtOut->formatTag = drwav__bytes_to_u16(fmt + 0); - fmtOut->channels = drwav__bytes_to_u16(fmt + 2); - fmtOut->sampleRate = drwav__bytes_to_u32(fmt + 4); - fmtOut->avgBytesPerSec = drwav__bytes_to_u32(fmt + 8); - fmtOut->blockAlign = drwav__bytes_to_u16(fmt + 12); - fmtOut->bitsPerSample = drwav__bytes_to_u16(fmt + 14); - - fmtOut->extendedSize = 0; - fmtOut->validBitsPerSample = 0; - fmtOut->channelMask = 0; - memset(fmtOut->subFormat, 0, sizeof(fmtOut->subFormat)); - - if (header.sizeInBytes > 16) { - unsigned char fmt_cbSize[2]; - int bytesReadSoFar = 0; - - if (onRead(pUserData, fmt_cbSize, sizeof(fmt_cbSize)) != sizeof(fmt_cbSize)) { - return DRWAV_FALSE; /* Expecting more data. */ - } - *pRunningBytesReadOut += sizeof(fmt_cbSize); - - bytesReadSoFar = 18; - - fmtOut->extendedSize = drwav__bytes_to_u16(fmt_cbSize); - if (fmtOut->extendedSize > 0) { - /* Simple validation. */ - if (fmtOut->formatTag == DR_WAVE_FORMAT_EXTENSIBLE) { - if (fmtOut->extendedSize != 22) { - return DRWAV_FALSE; - } - } - - if (fmtOut->formatTag == DR_WAVE_FORMAT_EXTENSIBLE) { - unsigned char fmtext[22]; - if (onRead(pUserData, fmtext, fmtOut->extendedSize) != fmtOut->extendedSize) { - return DRWAV_FALSE; /* Expecting more data. */ - } - - fmtOut->validBitsPerSample = drwav__bytes_to_u16(fmtext + 0); - fmtOut->channelMask = drwav__bytes_to_u32(fmtext + 2); - drwav__bytes_to_guid(fmtext + 6, fmtOut->subFormat); - } else { - if (!onSeek(pUserData, fmtOut->extendedSize, drwav_seek_origin_current)) { - return DRWAV_FALSE; - } - } - *pRunningBytesReadOut += fmtOut->extendedSize; - - bytesReadSoFar += fmtOut->extendedSize; - } - - /* Seek past any leftover bytes. For w64 the leftover will be defined based on the chunk size. */ - if (!onSeek(pUserData, (int)(header.sizeInBytes - bytesReadSoFar), drwav_seek_origin_current)) { - return DRWAV_FALSE; - } - *pRunningBytesReadOut += (header.sizeInBytes - bytesReadSoFar); - } - - if (header.paddingSize > 0) { - if (!onSeek(pUserData, header.paddingSize, drwav_seek_origin_current)) { - return DRWAV_FALSE; - } - *pRunningBytesReadOut += header.paddingSize; - } - - return DRWAV_TRUE; -} - - -size_t drwav__on_read(drwav_read_proc onRead, void* pUserData, void* pBufferOut, size_t bytesToRead, drwav_uint64* pCursor) -{ - size_t bytesRead; - - DRWAV_ASSERT(onRead != NULL); - DRWAV_ASSERT(pCursor != NULL); - - bytesRead = onRead(pUserData, pBufferOut, bytesToRead); - *pCursor += bytesRead; - return bytesRead; -} - -drwav_bool32 drwav__on_seek(drwav_seek_proc onSeek, void* pUserData, int offset, drwav_seek_origin origin, drwav_uint64* pCursor) -{ - DRWAV_ASSERT(onSeek != NULL); - DRWAV_ASSERT(pCursor != NULL); - - if (!onSeek(pUserData, offset, origin)) { - return DRWAV_FALSE; - } - - if (origin == drwav_seek_origin_start) { - *pCursor = offset; - } else { - *pCursor += offset; - } - - return DRWAV_TRUE; -} - - - -static drwav_uint32 drwav_get_bytes_per_pcm_frame(drwav* pWav) -{ - /* - The bytes per frame is a bit ambiguous. It can be either be based on the bits per sample, or the block align. The way I'm doing it here - is that if the bits per sample is a multiple of 8, use floor(bitsPerSample*channels/8), otherwise fall back to the block align. - */ - if ((pWav->bitsPerSample & 0x7) == 0) { - /* Bits per sample is a multiple of 8. */ - return (pWav->bitsPerSample * pWav->fmt.channels) >> 3; - } else { - return pWav->fmt.blockAlign; - } -} - - -drwav_bool32 drwav_preinit(drwav* pWav, drwav_read_proc onRead, drwav_seek_proc onSeek, void* pReadSeekUserData, const drwav_allocation_callbacks* pAllocationCallbacks) -{ - if (pWav == NULL || onRead == NULL || onSeek == NULL) { - return DRWAV_FALSE; - } - - DRWAV_ZERO_MEMORY(pWav, sizeof(*pWav)); - pWav->onRead = onRead; - pWav->onSeek = onSeek; - pWav->pUserData = pReadSeekUserData; - pWav->allocationCallbacks = drwav_copy_allocation_callbacks_or_defaults(pAllocationCallbacks); - - if (pWav->allocationCallbacks.onFree == NULL || (pWav->allocationCallbacks.onMalloc == NULL && pWav->allocationCallbacks.onRealloc == NULL)) { - return DRWAV_FALSE; /* Invalid allocation callbacks. */ - } - - return DRWAV_TRUE; -} - -drwav_bool32 drwav_init__internal(drwav* pWav, drwav_chunk_proc onChunk, void* pChunkUserData, drwav_uint32 flags) -{ - /* This function assumes drwav_preinit() has been called beforehand. */ - - drwav_uint64 cursor; /* <-- Keeps track of the byte position so we can seek to specific locations. */ - drwav_bool32 sequential; - unsigned char riff[4]; - drwav_fmt fmt; - unsigned short translatedFormatTag; - drwav_uint64 sampleCountFromFactChunk; - drwav_bool32 foundDataChunk; - drwav_uint64 dataChunkSize; - drwav_uint64 chunkSize; - - cursor = 0; - sequential = (flags & DRWAV_SEQUENTIAL) != 0; - - /* The first 4 bytes should be the RIFF identifier. */ - if (drwav__on_read(pWav->onRead, pWav->pUserData, riff, sizeof(riff), &cursor) != sizeof(riff)) { - return DRWAV_FALSE; - } - - /* - The first 4 bytes can be used to identify the container. For RIFF files it will start with "RIFF" and for - w64 it will start with "riff". - */ - if (drwav__fourcc_equal(riff, "RIFF")) { - pWav->container = drwav_container_riff; - } else if (drwav__fourcc_equal(riff, "riff")) { - int i; - drwav_uint8 riff2[12]; - - pWav->container = drwav_container_w64; - - /* Check the rest of the GUID for validity. */ - if (drwav__on_read(pWav->onRead, pWav->pUserData, riff2, sizeof(riff2), &cursor) != sizeof(riff2)) { - return DRWAV_FALSE; - } - - for (i = 0; i < 12; ++i) { - if (riff2[i] != drwavGUID_W64_RIFF[i+4]) { - return DRWAV_FALSE; - } - } - } else { - return DRWAV_FALSE; /* Unknown or unsupported container. */ - } - - - if (pWav->container == drwav_container_riff) { - unsigned char chunkSizeBytes[4]; - unsigned char wave[4]; - - /* RIFF/WAVE */ - if (drwav__on_read(pWav->onRead, pWav->pUserData, chunkSizeBytes, sizeof(chunkSizeBytes), &cursor) != sizeof(chunkSizeBytes)) { - return DRWAV_FALSE; - } - - if (drwav__bytes_to_u32(chunkSizeBytes) < 36) { - return DRWAV_FALSE; /* Chunk size should always be at least 36 bytes. */ - } - - if (drwav__on_read(pWav->onRead, pWav->pUserData, wave, sizeof(wave), &cursor) != sizeof(wave)) { - return DRWAV_FALSE; - } - - if (!drwav__fourcc_equal(wave, "WAVE")) { - return DRWAV_FALSE; /* Expecting "WAVE". */ - } - } else { - unsigned char chunkSizeBytes[8]; - drwav_uint8 wave[16]; - - /* W64 */ - if (drwav__on_read(pWav->onRead, pWav->pUserData, chunkSizeBytes, sizeof(chunkSizeBytes), &cursor) != sizeof(chunkSizeBytes)) { - return DRWAV_FALSE; - } - - if (drwav__bytes_to_u64(chunkSizeBytes) < 80) { - return DRWAV_FALSE; - } - - if (drwav__on_read(pWav->onRead, pWav->pUserData, wave, sizeof(wave), &cursor) != sizeof(wave)) { - return DRWAV_FALSE; - } - - if (!drwav__guid_equal(wave, drwavGUID_W64_WAVE)) { - return DRWAV_FALSE; - } - } - - - /* The next bytes should be the "fmt " chunk. */ - if (!drwav__read_fmt(pWav->onRead, pWav->onSeek, pWav->pUserData, pWav->container, &cursor, &fmt)) { - return DRWAV_FALSE; /* Failed to read the "fmt " chunk. */ - } - - /* Basic validation. */ - if ((fmt.sampleRate == 0 || fmt.sampleRate > DRWAV_MAX_SAMPLE_RATE) || - (fmt.channels == 0 || fmt.channels > DRWAV_MAX_CHANNELS) || - (fmt.bitsPerSample == 0 || fmt.bitsPerSample > DRWAV_MAX_BITS_PER_SAMPLE) || - fmt.blockAlign == 0) { - return DRWAV_FALSE; /* Probably an invalid WAV file. */ - } - - - /* Translate the internal format. */ - translatedFormatTag = fmt.formatTag; - if (translatedFormatTag == DR_WAVE_FORMAT_EXTENSIBLE) { - translatedFormatTag = drwav__bytes_to_u16(fmt.subFormat + 0); - } - - - - sampleCountFromFactChunk = 0; - - /* - We need to enumerate over each chunk for two reasons: - 1) The "data" chunk may not be the next one - 2) We may want to report each chunk back to the client - - In order to correctly report each chunk back to the client we will need to keep looping until the end of the file. - */ - foundDataChunk = DRWAV_FALSE; - dataChunkSize = 0; - - /* The next chunk we care about is the "data" chunk. This is not necessarily the next chunk so we'll need to loop. */ - for (;;) - { - drwav_chunk_header header; - drwav_result result = drwav__read_chunk_header(pWav->onRead, pWav->pUserData, pWav->container, &cursor, &header); - if (result != DRWAV_SUCCESS) { - if (!foundDataChunk) { - return DRWAV_FALSE; - } else { - break; /* Probably at the end of the file. Get out of the loop. */ - } - } - - /* Tell the client about this chunk. */ - if (!sequential && onChunk != NULL) { - drwav_uint64 callbackBytesRead = onChunk(pChunkUserData, pWav->onRead, pWav->onSeek, pWav->pUserData, &header); - - /* - dr_wav may need to read the contents of the chunk, so we now need to seek back to the position before - we called the callback. - */ - if (callbackBytesRead > 0) { - if (!drwav__seek_from_start(pWav->onSeek, cursor, pWav->pUserData)) { - return DRWAV_FALSE; - } - } - } - - - if (!foundDataChunk) { - pWav->dataChunkDataPos = cursor; - } - - chunkSize = header.sizeInBytes; - if (pWav->container == drwav_container_riff) { - if (drwav__fourcc_equal(header.id.fourcc, "data")) { - foundDataChunk = DRWAV_TRUE; - dataChunkSize = chunkSize; - } - } else { - if (drwav__guid_equal(header.id.guid, drwavGUID_W64_DATA)) { - foundDataChunk = DRWAV_TRUE; - dataChunkSize = chunkSize; - } - } - - /* - If at this point we have found the data chunk and we're running in sequential mode, we need to break out of this loop. The reason for - this is that we would otherwise require a backwards seek which sequential mode forbids. - */ - if (foundDataChunk && sequential) { - break; - } - - /* Optional. Get the total sample count from the FACT chunk. This is useful for compressed formats. */ - if (pWav->container == drwav_container_riff) { - if (drwav__fourcc_equal(header.id.fourcc, "fact")) { - drwav_uint32 sampleCount; - if (drwav__on_read(pWav->onRead, pWav->pUserData, &sampleCount, 4, &cursor) != 4) { - return DRWAV_FALSE; - } - chunkSize -= 4; - - if (!foundDataChunk) { - pWav->dataChunkDataPos = cursor; - } - - /* - The sample count in the "fact" chunk is either unreliable, or I'm not understanding it properly. For now I am only enabling this - for Microsoft ADPCM formats. - */ - if (pWav->translatedFormatTag == DR_WAVE_FORMAT_ADPCM) { - sampleCountFromFactChunk = sampleCount; - } else { - sampleCountFromFactChunk = 0; - } - } - } else { - if (drwav__guid_equal(header.id.guid, drwavGUID_W64_FACT)) { - if (drwav__on_read(pWav->onRead, pWav->pUserData, &sampleCountFromFactChunk, 8, &cursor) != 8) { - return DRWAV_FALSE; - } - chunkSize -= 8; - - if (!foundDataChunk) { - pWav->dataChunkDataPos = cursor; - } - } - } - - /* "smpl" chunk. */ - if (pWav->container == drwav_container_riff) { - if (drwav__fourcc_equal(header.id.fourcc, "smpl")) { - unsigned char smplHeaderData[36]; /* 36 = size of the smpl header section, not including the loop data. */ - if (chunkSize >= sizeof(smplHeaderData)) { - drwav_uint64 bytesJustRead = drwav__on_read(pWav->onRead, pWav->pUserData, smplHeaderData, sizeof(smplHeaderData), &cursor); - chunkSize -= bytesJustRead; - - if (bytesJustRead == sizeof(smplHeaderData)) { - drwav_uint32 iLoop; - - pWav->smpl.manufacturer = drwav__bytes_to_u32(smplHeaderData+0); - pWav->smpl.product = drwav__bytes_to_u32(smplHeaderData+4); - pWav->smpl.samplePeriod = drwav__bytes_to_u32(smplHeaderData+8); - pWav->smpl.midiUnityNotes = drwav__bytes_to_u32(smplHeaderData+12); - pWav->smpl.midiPitchFraction = drwav__bytes_to_u32(smplHeaderData+16); - pWav->smpl.smpteFormat = drwav__bytes_to_u32(smplHeaderData+20); - pWav->smpl.smpteOffset = drwav__bytes_to_u32(smplHeaderData+24); - pWav->smpl.numSampleLoops = drwav__bytes_to_u32(smplHeaderData+28); - pWav->smpl.samplerData = drwav__bytes_to_u32(smplHeaderData+32); - - for (iLoop = 0; iLoop < pWav->smpl.numSampleLoops && iLoop < drwav_countof(pWav->smpl.loops); ++iLoop) { - unsigned char smplLoopData[24]; /* 24 = size of a loop section in the smpl chunk. */ - bytesJustRead = drwav__on_read(pWav->onRead, pWav->pUserData, smplLoopData, sizeof(smplLoopData), &cursor); - chunkSize -= bytesJustRead; - - if (bytesJustRead == sizeof(smplLoopData)) { - pWav->smpl.loops[iLoop].cuePointId = drwav__bytes_to_u32(smplLoopData+0); - pWav->smpl.loops[iLoop].type = drwav__bytes_to_u32(smplLoopData+4); - pWav->smpl.loops[iLoop].start = drwav__bytes_to_u32(smplLoopData+8); - pWav->smpl.loops[iLoop].end = drwav__bytes_to_u32(smplLoopData+12); - pWav->smpl.loops[iLoop].fraction = drwav__bytes_to_u32(smplLoopData+16); - pWav->smpl.loops[iLoop].playCount = drwav__bytes_to_u32(smplLoopData+20); - } else { - break; /* Break from the smpl loop for loop. */ - } - } - } - } else { - /* Looks like invalid data. Ignore the chunk. */ - } - } - } else { - if (drwav__guid_equal(header.id.guid, drwavGUID_W64_SMPL)) { - /* - This path will be hit when a W64 WAV file contains a smpl chunk. I don't have a sample file to test this path, so a contribution - is welcome to add support for this. - */ - } - } - - /* Make sure we seek past the padding. */ - chunkSize += header.paddingSize; - if (!drwav__seek_forward(pWav->onSeek, chunkSize, pWav->pUserData)) { - break; - } - cursor += chunkSize; - - if (!foundDataChunk) { - pWav->dataChunkDataPos = cursor; - } - } - - /* If we haven't found a data chunk, return an error. */ - if (!foundDataChunk) { - return DRWAV_FALSE; - } - - /* We may have moved passed the data chunk. If so we need to move back. If running in sequential mode we can assume we are already sitting on the data chunk. */ - if (!sequential) { - if (!drwav__seek_from_start(pWav->onSeek, pWav->dataChunkDataPos, pWav->pUserData)) { - return DRWAV_FALSE; - } - cursor = pWav->dataChunkDataPos; - } - - - /* At this point we should be sitting on the first byte of the raw audio data. */ - - pWav->fmt = fmt; - pWav->sampleRate = fmt.sampleRate; - pWav->channels = fmt.channels; - pWav->bitsPerSample = fmt.bitsPerSample; - pWav->bytesRemaining = dataChunkSize; - pWav->translatedFormatTag = translatedFormatTag; - pWav->dataChunkDataSize = dataChunkSize; - - if (sampleCountFromFactChunk != 0) { - pWav->totalPCMFrameCount = sampleCountFromFactChunk; - } else { - pWav->totalPCMFrameCount = dataChunkSize / drwav_get_bytes_per_pcm_frame(pWav); - - if (pWav->translatedFormatTag == DR_WAVE_FORMAT_ADPCM) { - drwav_uint64 totalBlockHeaderSizeInBytes; - drwav_uint64 blockCount = dataChunkSize / fmt.blockAlign; - - /* Make sure any trailing partial block is accounted for. */ - if ((blockCount * fmt.blockAlign) < dataChunkSize) { - blockCount += 1; - } - - /* We decode two samples per byte. There will be blockCount headers in the data chunk. This is enough to know how to calculate the total PCM frame count. */ - totalBlockHeaderSizeInBytes = blockCount * (6*fmt.channels); - pWav->totalPCMFrameCount = ((dataChunkSize - totalBlockHeaderSizeInBytes) * 2) / fmt.channels; - } - if (pWav->translatedFormatTag == DR_WAVE_FORMAT_DVI_ADPCM) { - drwav_uint64 totalBlockHeaderSizeInBytes; - drwav_uint64 blockCount = dataChunkSize / fmt.blockAlign; - - /* Make sure any trailing partial block is accounted for. */ - if ((blockCount * fmt.blockAlign) < dataChunkSize) { - blockCount += 1; - } - - /* We decode two samples per byte. There will be blockCount headers in the data chunk. This is enough to know how to calculate the total PCM frame count. */ - totalBlockHeaderSizeInBytes = blockCount * (4*fmt.channels); - pWav->totalPCMFrameCount = ((dataChunkSize - totalBlockHeaderSizeInBytes) * 2) / fmt.channels; - - /* The header includes a decoded sample for each channel which acts as the initial predictor sample. */ - pWav->totalPCMFrameCount += blockCount; - } - } - - /* Some formats only support a certain number of channels. */ - if (pWav->translatedFormatTag == DR_WAVE_FORMAT_ADPCM || pWav->translatedFormatTag == DR_WAVE_FORMAT_DVI_ADPCM) { - if (pWav->channels > 2) { - return DRWAV_FALSE; - } - } - -#ifdef DR_WAV_LIBSNDFILE_COMPAT - /* - I use libsndfile as a benchmark for testing, however in the version I'm using (from the Windows installer on the libsndfile website), - it appears the total sample count libsndfile uses for MS-ADPCM is incorrect. It would seem they are computing the total sample count - from the number of blocks, however this results in the inclusion of extra silent samples at the end of the last block. The correct - way to know the total sample count is to inspect the "fact" chunk, which should always be present for compressed formats, and should - always include the sample count. This little block of code below is only used to emulate the libsndfile logic so I can properly run my - correctness tests against libsndfile, and is disabled by default. - */ - if (pWav->translatedFormatTag == DR_WAVE_FORMAT_ADPCM) { - drwav_uint64 blockCount = dataChunkSize / fmt.blockAlign; - pWav->totalPCMFrameCount = (((blockCount * (fmt.blockAlign - (6*pWav->channels))) * 2)) / fmt.channels; /* x2 because two samples per byte. */ - } - if (pWav->translatedFormatTag == DR_WAVE_FORMAT_DVI_ADPCM) { - drwav_uint64 blockCount = dataChunkSize / fmt.blockAlign; - pWav->totalPCMFrameCount = (((blockCount * (fmt.blockAlign - (4*pWav->channels))) * 2) + (blockCount * pWav->channels)) / fmt.channels; - } -#endif - - return DRWAV_TRUE; -} - -drwav_bool32 drwav_init(drwav* pWav, drwav_read_proc onRead, drwav_seek_proc onSeek, void* pUserData, const drwav_allocation_callbacks* pAllocationCallbacks) -{ - return drwav_init_ex(pWav, onRead, onSeek, NULL, pUserData, NULL, 0, pAllocationCallbacks); -} - -drwav_bool32 drwav_init_ex(drwav* pWav, drwav_read_proc onRead, drwav_seek_proc onSeek, drwav_chunk_proc onChunk, void* pReadSeekUserData, void* pChunkUserData, drwav_uint32 flags, const drwav_allocation_callbacks* pAllocationCallbacks) -{ - if (!drwav_preinit(pWav, onRead, onSeek, pReadSeekUserData, pAllocationCallbacks)) { - return DRWAV_FALSE; - } - - return drwav_init__internal(pWav, onChunk, pChunkUserData, flags); -} - - -static drwav_uint32 drwav__riff_chunk_size_riff(drwav_uint64 dataChunkSize) -{ - drwav_uint32 dataSubchunkPaddingSize = drwav__chunk_padding_size_riff(dataChunkSize); - - if (dataChunkSize <= (0xFFFFFFFFUL - 36 - dataSubchunkPaddingSize)) { - return 36 + (drwav_uint32)(dataChunkSize + dataSubchunkPaddingSize); - } else { - return 0xFFFFFFFF; - } -} - -static drwav_uint32 drwav__data_chunk_size_riff(drwav_uint64 dataChunkSize) -{ - if (dataChunkSize <= 0xFFFFFFFFUL) { - return (drwav_uint32)dataChunkSize; - } else { - return 0xFFFFFFFFUL; - } -} - -static drwav_uint64 drwav__riff_chunk_size_w64(drwav_uint64 dataChunkSize) -{ - drwav_uint64 dataSubchunkPaddingSize = drwav__chunk_padding_size_w64(dataChunkSize); - - return 80 + 24 + dataChunkSize + dataSubchunkPaddingSize; /* +24 because W64 includes the size of the GUID and size fields. */ -} - -static drwav_uint64 drwav__data_chunk_size_w64(drwav_uint64 dataChunkSize) -{ - return 24 + dataChunkSize; /* +24 because W64 includes the size of the GUID and size fields. */ -} - - -drwav_bool32 drwav_preinit_write(drwav* pWav, const drwav_data_format* pFormat, drwav_bool32 isSequential, drwav_write_proc onWrite, drwav_seek_proc onSeek, void* pUserData, const drwav_allocation_callbacks* pAllocationCallbacks) -{ - if (pWav == NULL || onWrite == NULL) { - return DRWAV_FALSE; - } - - if (!isSequential && onSeek == NULL) { - return DRWAV_FALSE; /* <-- onSeek is required when in non-sequential mode. */ - } - - /* Not currently supporting compressed formats. Will need to add support for the "fact" chunk before we enable this. */ - if (pFormat->format == DR_WAVE_FORMAT_EXTENSIBLE) { - return DRWAV_FALSE; - } - if (pFormat->format == DR_WAVE_FORMAT_ADPCM || pFormat->format == DR_WAVE_FORMAT_DVI_ADPCM) { - return DRWAV_FALSE; - } - - DRWAV_ZERO_MEMORY(pWav, sizeof(*pWav)); - pWav->onWrite = onWrite; - pWav->onSeek = onSeek; - pWav->pUserData = pUserData; - pWav->allocationCallbacks = drwav_copy_allocation_callbacks_or_defaults(pAllocationCallbacks); - - if (pWav->allocationCallbacks.onFree == NULL || (pWav->allocationCallbacks.onMalloc == NULL && pWav->allocationCallbacks.onRealloc == NULL)) { - return DRWAV_FALSE; /* Invalid allocation callbacks. */ - } - - pWav->fmt.formatTag = (drwav_uint16)pFormat->format; - pWav->fmt.channels = (drwav_uint16)pFormat->channels; - pWav->fmt.sampleRate = pFormat->sampleRate; - pWav->fmt.avgBytesPerSec = (drwav_uint32)((pFormat->bitsPerSample * pFormat->sampleRate * pFormat->channels) / 8); - pWav->fmt.blockAlign = (drwav_uint16)((pFormat->channels * pFormat->bitsPerSample) / 8); - pWav->fmt.bitsPerSample = (drwav_uint16)pFormat->bitsPerSample; - pWav->fmt.extendedSize = 0; - pWav->isSequentialWrite = isSequential; - - return DRWAV_TRUE; -} - -drwav_bool32 drwav_init_write__internal(drwav* pWav, const drwav_data_format* pFormat, drwav_uint64 totalSampleCount) -{ - /* The function assumes drwav_preinit_write() was called beforehand. */ - - size_t runningPos = 0; - drwav_uint64 initialDataChunkSize = 0; - drwav_uint64 chunkSizeFMT; - - /* - The initial values for the "RIFF" and "data" chunks depends on whether or not we are initializing in sequential mode or not. In - sequential mode we set this to its final values straight away since they can be calculated from the total sample count. In non- - sequential mode we initialize it all to zero and fill it out in drwav_uninit() using a backwards seek. - */ - if (pWav->isSequentialWrite) { - initialDataChunkSize = (totalSampleCount * pWav->fmt.bitsPerSample) / 8; - - /* - The RIFF container has a limit on the number of samples. drwav is not allowing this. There's no practical limits for Wave64 - so for the sake of simplicity I'm not doing any validation for that. - */ - if (pFormat->container == drwav_container_riff) { - if (initialDataChunkSize > (0xFFFFFFFFUL - 36)) { - return DRWAV_FALSE; /* Not enough room to store every sample. */ - } - } - } - - pWav->dataChunkDataSizeTargetWrite = initialDataChunkSize; - - - /* "RIFF" chunk. */ - if (pFormat->container == drwav_container_riff) { - drwav_uint32 chunkSizeRIFF = 36 + (drwav_uint32)initialDataChunkSize; /* +36 = "RIFF"+[RIFF Chunk Size]+"WAVE" + [sizeof "fmt " chunk] */ - runningPos += pWav->onWrite(pWav->pUserData, "RIFF", 4); - runningPos += pWav->onWrite(pWav->pUserData, &chunkSizeRIFF, 4); - runningPos += pWav->onWrite(pWav->pUserData, "WAVE", 4); - } else { - drwav_uint64 chunkSizeRIFF = 80 + 24 + initialDataChunkSize; /* +24 because W64 includes the size of the GUID and size fields. */ - runningPos += pWav->onWrite(pWav->pUserData, drwavGUID_W64_RIFF, 16); - runningPos += pWav->onWrite(pWav->pUserData, &chunkSizeRIFF, 8); - runningPos += pWav->onWrite(pWav->pUserData, drwavGUID_W64_WAVE, 16); - } - - /* "fmt " chunk. */ - if (pFormat->container == drwav_container_riff) { - chunkSizeFMT = 16; - runningPos += pWav->onWrite(pWav->pUserData, "fmt ", 4); - runningPos += pWav->onWrite(pWav->pUserData, &chunkSizeFMT, 4); - } else { - chunkSizeFMT = 40; - runningPos += pWav->onWrite(pWav->pUserData, drwavGUID_W64_FMT, 16); - runningPos += pWav->onWrite(pWav->pUserData, &chunkSizeFMT, 8); - } - - runningPos += pWav->onWrite(pWav->pUserData, &pWav->fmt.formatTag, 2); - runningPos += pWav->onWrite(pWav->pUserData, &pWav->fmt.channels, 2); - runningPos += pWav->onWrite(pWav->pUserData, &pWav->fmt.sampleRate, 4); - runningPos += pWav->onWrite(pWav->pUserData, &pWav->fmt.avgBytesPerSec, 4); - runningPos += pWav->onWrite(pWav->pUserData, &pWav->fmt.blockAlign, 2); - runningPos += pWav->onWrite(pWav->pUserData, &pWav->fmt.bitsPerSample, 2); - - pWav->dataChunkDataPos = runningPos; - - /* "data" chunk. */ - if (pFormat->container == drwav_container_riff) { - drwav_uint32 chunkSizeDATA = (drwav_uint32)initialDataChunkSize; - runningPos += pWav->onWrite(pWav->pUserData, "data", 4); - runningPos += pWav->onWrite(pWav->pUserData, &chunkSizeDATA, 4); - } else { - drwav_uint64 chunkSizeDATA = 24 + initialDataChunkSize; /* +24 because W64 includes the size of the GUID and size fields. */ - runningPos += pWav->onWrite(pWav->pUserData, drwavGUID_W64_DATA, 16); - runningPos += pWav->onWrite(pWav->pUserData, &chunkSizeDATA, 8); - } - - - /* Simple validation. */ - if (pFormat->container == drwav_container_riff) { - if (runningPos != 20 + chunkSizeFMT + 8) { - return DRWAV_FALSE; - } - } else { - if (runningPos != 40 + chunkSizeFMT + 24) { - return DRWAV_FALSE; - } - } - - - /* Set some properties for the client's convenience. */ - pWav->container = pFormat->container; - pWav->channels = (drwav_uint16)pFormat->channels; - pWav->sampleRate = pFormat->sampleRate; - pWav->bitsPerSample = (drwav_uint16)pFormat->bitsPerSample; - pWav->translatedFormatTag = (drwav_uint16)pFormat->format; - - return DRWAV_TRUE; -} - - -drwav_bool32 drwav_init_write(drwav* pWav, const drwav_data_format* pFormat, drwav_write_proc onWrite, drwav_seek_proc onSeek, void* pUserData, const drwav_allocation_callbacks* pAllocationCallbacks) -{ - if (!drwav_preinit_write(pWav, pFormat, DRWAV_FALSE, onWrite, onSeek, pUserData, pAllocationCallbacks)) { - return DRWAV_FALSE; - } - - return drwav_init_write__internal(pWav, pFormat, 0); /* DRWAV_FALSE = Not Sequential */ -} - -drwav_bool32 drwav_init_write_sequential(drwav* pWav, const drwav_data_format* pFormat, drwav_uint64 totalSampleCount, drwav_write_proc onWrite, void* pUserData, const drwav_allocation_callbacks* pAllocationCallbacks) -{ - if (!drwav_preinit_write(pWav, pFormat, DRWAV_TRUE, onWrite, NULL, pUserData, pAllocationCallbacks)) { - return DRWAV_FALSE; - } - - return drwav_init_write__internal(pWav, pFormat, totalSampleCount); /* DRWAV_TRUE = Sequential */ -} - -drwav_bool32 drwav_init_write_sequential_pcm_frames(drwav* pWav, const drwav_data_format* pFormat, drwav_uint64 totalPCMFrameCount, drwav_write_proc onWrite, void* pUserData, const drwav_allocation_callbacks* pAllocationCallbacks) -{ - if (pFormat == NULL) { - return DRWAV_FALSE; - } - - return drwav_init_write_sequential(pWav, pFormat, totalPCMFrameCount*pFormat->channels, onWrite, pUserData, pAllocationCallbacks); -} - -drwav_uint64 drwav_target_write_size_bytes(drwav_data_format const *format, drwav_uint64 totalSampleCount) -{ - drwav_uint64 targetDataSizeBytes = (totalSampleCount * format->channels * format->bitsPerSample/8); - drwav_uint64 riffChunkSizeBytes; - drwav_uint64 fileSizeBytes; - - if (format->container == drwav_container_riff) { - riffChunkSizeBytes = drwav__riff_chunk_size_riff(targetDataSizeBytes); - fileSizeBytes = (8 + riffChunkSizeBytes); /* +8 because WAV doesn't include the size of the ChunkID and ChunkSize fields. */ - } else { - riffChunkSizeBytes = drwav__riff_chunk_size_w64(targetDataSizeBytes); - fileSizeBytes = riffChunkSizeBytes; - } - - return fileSizeBytes; -} - - -#ifndef DR_WAV_NO_STDIO -FILE* drwav_fopen(const char* filePath, const char* openMode) -{ - FILE* pFile; -#if defined(_MSC_VER) && _MSC_VER >= 1400 - if (fopen_s(&pFile, filePath, openMode) != 0) { - return NULL; - } -#else - pFile = fopen(filePath, openMode); - if (pFile == NULL) { - return NULL; - } -#endif - - return pFile; -} - -FILE* drwav_wfopen(const wchar_t* pFilePath, const wchar_t* pOpenMode, const drwav_allocation_callbacks* pAllocationCallbacks) -{ - FILE* pFile; - -#if defined(_WIN32) - (void)pAllocationCallbacks; - #if defined(_MSC_VER) && _MSC_VER >= 1400 - if (_wfopen_s(&pFile, pFilePath, pOpenMode) != 0) { - return NULL; - } - #else - pFile = _wfopen(pFilePath, pOpenMode); - if (pFile == NULL) { - return NULL; - } - #endif -#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. - */ - { - mbstate_t mbs; - size_t lenMB; - const wchar_t* pFilePathTemp = pFilePath; - char* pFilePathMB = NULL; - const wchar_t* pOpenModeMBTemp = pOpenMode; - char pOpenModeMB[16]; - drwav_allocation_callbacks allocationCallbacks; - - allocationCallbacks = drwav_copy_allocation_callbacks_or_defaults(pAllocationCallbacks); - - /* Get the length first. */ - DRWAV_ZERO_MEMORY(&mbs, sizeof(mbs)); - lenMB = wcsrtombs(NULL, &pFilePathTemp, 0, &mbs); - if (lenMB == (size_t)-1) { - return NULL; - } - - pFilePathMB = (char*)drwav__malloc_from_callbacks(lenMB + 1, &allocationCallbacks); - if (pFilePathMB == NULL) { - return NULL; - } - - pFilePathTemp = pFilePath; - DRWAV_ZERO_MEMORY(&mbs, sizeof(mbs)); - wcsrtombs(pFilePathMB, &pFilePathTemp, lenMB + 1, &mbs); - - DRWAV_ZERO_MEMORY(&mbs, sizeof(mbs)); - wcsrtombs(pOpenModeMB, &pOpenModeMBTemp, sizeof(pOpenModeMB), &mbs); - - pFile = fopen(pFilePathMB, pOpenModeMB); - - drwav__free_from_callbacks(pFilePathMB, &allocationCallbacks); - } -#endif - - return pFile; -} - - -static size_t drwav__on_read_stdio(void* pUserData, void* pBufferOut, size_t bytesToRead) -{ - return fread(pBufferOut, 1, bytesToRead, (FILE*)pUserData); -} - -static size_t drwav__on_write_stdio(void* pUserData, const void* pData, size_t bytesToWrite) -{ - return fwrite(pData, 1, bytesToWrite, (FILE*)pUserData); -} - -static drwav_bool32 drwav__on_seek_stdio(void* pUserData, int offset, drwav_seek_origin origin) -{ - return fseek((FILE*)pUserData, offset, (origin == drwav_seek_origin_current) ? SEEK_CUR : SEEK_SET) == 0; -} - -drwav_bool32 drwav_init_file(drwav* pWav, const char* filename, const drwav_allocation_callbacks* pAllocationCallbacks) -{ - return drwav_init_file_ex(pWav, filename, NULL, NULL, 0, pAllocationCallbacks); -} - - -drwav_bool32 drwav_init_file__internal_FILE(drwav* pWav, FILE* pFile, drwav_chunk_proc onChunk, void* pChunkUserData, drwav_uint32 flags, const drwav_allocation_callbacks* pAllocationCallbacks) -{ - if (!drwav_preinit(pWav, drwav__on_read_stdio, drwav__on_seek_stdio, (void*)pFile, pAllocationCallbacks)) { - fclose(pFile); - return DRWAV_FALSE; - } - - return drwav_init__internal(pWav, onChunk, pChunkUserData, flags); -} - -drwav_bool32 drwav_init_file_ex(drwav* pWav, const char* filename, drwav_chunk_proc onChunk, void* pChunkUserData, drwav_uint32 flags, const drwav_allocation_callbacks* pAllocationCallbacks) -{ - FILE* pFile = drwav_fopen(filename, "rb"); - if (pFile == NULL) { - return DRWAV_FALSE; - } - - /* This takes ownership of the FILE* object. */ - return drwav_init_file__internal_FILE(pWav, pFile, onChunk, pChunkUserData, flags, pAllocationCallbacks); -} - -drwav_bool32 drwav_init_file_w(drwav* pWav, const wchar_t* filename, const drwav_allocation_callbacks* pAllocationCallbacks) -{ - return drwav_init_file_ex_w(pWav, filename, NULL, NULL, 0, pAllocationCallbacks); -} - -drwav_bool32 drwav_init_file_ex_w(drwav* pWav, const wchar_t* filename, drwav_chunk_proc onChunk, void* pChunkUserData, drwav_uint32 flags, const drwav_allocation_callbacks* pAllocationCallbacks) -{ - FILE* pFile = drwav_wfopen(filename, L"rb", pAllocationCallbacks); - if (pFile == NULL) { - return DRWAV_FALSE; - } - - /* This takes ownership of the FILE* object. */ - return drwav_init_file__internal_FILE(pWav, pFile, onChunk, pChunkUserData, flags, pAllocationCallbacks); -} - - -drwav_bool32 drwav_init_file_write__internal_FILE(drwav* pWav, FILE* pFile, const drwav_data_format* pFormat, drwav_uint64 totalSampleCount, drwav_bool32 isSequential, const drwav_allocation_callbacks* pAllocationCallbacks) -{ - if (!drwav_preinit_write(pWav, pFormat, isSequential, drwav__on_write_stdio, drwav__on_seek_stdio, (void*)pFile, pAllocationCallbacks)) { - fclose(pFile); - return DRWAV_FALSE; - } - - return drwav_init_write__internal(pWav, pFormat, totalSampleCount); -} - -drwav_bool32 drwav_init_file_write__internal(drwav* pWav, const char* filename, const drwav_data_format* pFormat, drwav_uint64 totalSampleCount, drwav_bool32 isSequential, const drwav_allocation_callbacks* pAllocationCallbacks) -{ - FILE* pFile = drwav_fopen(filename, "wb"); - if (pFile == NULL) { - return DRWAV_FALSE; - } - - /* This takes ownership of the FILE* object. */ - return drwav_init_file_write__internal_FILE(pWav, pFile, pFormat, totalSampleCount, isSequential, pAllocationCallbacks); -} - -drwav_bool32 drwav_init_file_write_w__internal(drwav* pWav, const wchar_t* filename, const drwav_data_format* pFormat, drwav_uint64 totalSampleCount, drwav_bool32 isSequential, const drwav_allocation_callbacks* pAllocationCallbacks) -{ - FILE* pFile = drwav_wfopen(filename, L"wb", pAllocationCallbacks); - if (pFile == NULL) { - return DRWAV_FALSE; - } - - /* This takes ownership of the FILE* object. */ - return drwav_init_file_write__internal_FILE(pWav, pFile, pFormat, totalSampleCount, isSequential, pAllocationCallbacks); -} - -drwav_bool32 drwav_init_file_write(drwav* pWav, const char* filename, const drwav_data_format* pFormat, const drwav_allocation_callbacks* pAllocationCallbacks) -{ - return drwav_init_file_write__internal(pWav, filename, pFormat, 0, DRWAV_FALSE, pAllocationCallbacks); -} - -drwav_bool32 drwav_init_file_write_sequential(drwav* pWav, const char* filename, const drwav_data_format* pFormat, drwav_uint64 totalSampleCount, const drwav_allocation_callbacks* pAllocationCallbacks) -{ - return drwav_init_file_write__internal(pWav, filename, pFormat, totalSampleCount, DRWAV_TRUE, pAllocationCallbacks); -} - -drwav_bool32 drwav_init_file_write_sequential_pcm_frames(drwav* pWav, const char* filename, const drwav_data_format* pFormat, drwav_uint64 totalPCMFrameCount, const drwav_allocation_callbacks* pAllocationCallbacks) -{ - if (pFormat == NULL) { - return DRWAV_FALSE; - } - - return drwav_init_file_write_sequential(pWav, filename, pFormat, totalPCMFrameCount*pFormat->channels, pAllocationCallbacks); -} - -drwav_bool32 drwav_init_file_write_w(drwav* pWav, const wchar_t* filename, const drwav_data_format* pFormat, const drwav_allocation_callbacks* pAllocationCallbacks) -{ - return drwav_init_file_write_w__internal(pWav, filename, pFormat, 0, DRWAV_FALSE, pAllocationCallbacks); -} - -drwav_bool32 drwav_init_file_write_sequential_w(drwav* pWav, const wchar_t* filename, const drwav_data_format* pFormat, drwav_uint64 totalSampleCount, const drwav_allocation_callbacks* pAllocationCallbacks) -{ - return drwav_init_file_write_w__internal(pWav, filename, pFormat, totalSampleCount, DRWAV_TRUE, pAllocationCallbacks); -} - -drwav_bool32 drwav_init_file_write_sequential_pcm_frames_w(drwav* pWav, const wchar_t* filename, const drwav_data_format* pFormat, drwav_uint64 totalPCMFrameCount, const drwav_allocation_callbacks* pAllocationCallbacks) -{ - if (pFormat == NULL) { - return DRWAV_FALSE; - } - - return drwav_init_file_write_sequential_w(pWav, filename, pFormat, totalPCMFrameCount*pFormat->channels, pAllocationCallbacks); -} -#endif /* DR_WAV_NO_STDIO */ - - -static size_t drwav__on_read_memory(void* pUserData, void* pBufferOut, size_t bytesToRead) -{ - drwav* pWav = (drwav*)pUserData; - size_t bytesRemaining; - - DRWAV_ASSERT(pWav != NULL); - DRWAV_ASSERT(pWav->memoryStream.dataSize >= pWav->memoryStream.currentReadPos); - - bytesRemaining = pWav->memoryStream.dataSize - pWav->memoryStream.currentReadPos; - if (bytesToRead > bytesRemaining) { - bytesToRead = bytesRemaining; - } - - if (bytesToRead > 0) { - DRWAV_COPY_MEMORY(pBufferOut, pWav->memoryStream.data + pWav->memoryStream.currentReadPos, bytesToRead); - pWav->memoryStream.currentReadPos += bytesToRead; - } - - return bytesToRead; -} - -static drwav_bool32 drwav__on_seek_memory(void* pUserData, int offset, drwav_seek_origin origin) -{ - drwav* pWav = (drwav*)pUserData; - DRWAV_ASSERT(pWav != NULL); - - if (origin == drwav_seek_origin_current) { - if (offset > 0) { - if (pWav->memoryStream.currentReadPos + offset > pWav->memoryStream.dataSize) { - return DRWAV_FALSE; /* Trying to seek too far forward. */ - } - } else { - if (pWav->memoryStream.currentReadPos < (size_t)-offset) { - return DRWAV_FALSE; /* Trying to seek too far backwards. */ - } - } - - /* This will never underflow thanks to the clamps above. */ - pWav->memoryStream.currentReadPos += offset; - } else { - if ((drwav_uint32)offset <= pWav->memoryStream.dataSize) { - pWav->memoryStream.currentReadPos = offset; - } else { - return DRWAV_FALSE; /* Trying to seek too far forward. */ - } - } - - return DRWAV_TRUE; -} - -static size_t drwav__on_write_memory(void* pUserData, const void* pDataIn, size_t bytesToWrite) -{ - drwav* pWav = (drwav*)pUserData; - size_t bytesRemaining; - - DRWAV_ASSERT(pWav != NULL); - DRWAV_ASSERT(pWav->memoryStreamWrite.dataCapacity >= pWav->memoryStreamWrite.currentWritePos); - - bytesRemaining = pWav->memoryStreamWrite.dataCapacity - pWav->memoryStreamWrite.currentWritePos; - if (bytesRemaining < bytesToWrite) { - /* Need to reallocate. */ - void* pNewData; - size_t newDataCapacity = (pWav->memoryStreamWrite.dataCapacity == 0) ? 256 : pWav->memoryStreamWrite.dataCapacity * 2; - - /* If doubling wasn't enough, just make it the minimum required size to write the data. */ - if ((newDataCapacity - pWav->memoryStreamWrite.currentWritePos) < bytesToWrite) { - newDataCapacity = pWav->memoryStreamWrite.currentWritePos + bytesToWrite; - } - - pNewData = drwav__realloc_from_callbacks(*pWav->memoryStreamWrite.ppData, newDataCapacity, pWav->memoryStreamWrite.dataCapacity, &pWav->allocationCallbacks); - if (pNewData == NULL) { - return 0; - } - - *pWav->memoryStreamWrite.ppData = pNewData; - pWav->memoryStreamWrite.dataCapacity = newDataCapacity; - } - - DRWAV_COPY_MEMORY(((drwav_uint8*)(*pWav->memoryStreamWrite.ppData)) + pWav->memoryStreamWrite.currentWritePos, pDataIn, bytesToWrite); - - pWav->memoryStreamWrite.currentWritePos += bytesToWrite; - if (pWav->memoryStreamWrite.dataSize < pWav->memoryStreamWrite.currentWritePos) { - pWav->memoryStreamWrite.dataSize = pWav->memoryStreamWrite.currentWritePos; - } - - *pWav->memoryStreamWrite.pDataSize = pWav->memoryStreamWrite.dataSize; - - return bytesToWrite; -} - -static drwav_bool32 drwav__on_seek_memory_write(void* pUserData, int offset, drwav_seek_origin origin) -{ - drwav* pWav = (drwav*)pUserData; - DRWAV_ASSERT(pWav != NULL); - - if (origin == drwav_seek_origin_current) { - if (offset > 0) { - if (pWav->memoryStreamWrite.currentWritePos + offset > pWav->memoryStreamWrite.dataSize) { - offset = (int)(pWav->memoryStreamWrite.dataSize - pWav->memoryStreamWrite.currentWritePos); /* Trying to seek too far forward. */ - } - } else { - if (pWav->memoryStreamWrite.currentWritePos < (size_t)-offset) { - offset = -(int)pWav->memoryStreamWrite.currentWritePos; /* Trying to seek too far backwards. */ - } - } - - /* This will never underflow thanks to the clamps above. */ - pWav->memoryStreamWrite.currentWritePos += offset; - } else { - if ((drwav_uint32)offset <= pWav->memoryStreamWrite.dataSize) { - pWav->memoryStreamWrite.currentWritePos = offset; - } else { - pWav->memoryStreamWrite.currentWritePos = pWav->memoryStreamWrite.dataSize; /* Trying to seek too far forward. */ - } - } - - return DRWAV_TRUE; -} - -drwav_bool32 drwav_init_memory(drwav* pWav, const void* data, size_t dataSize, const drwav_allocation_callbacks* pAllocationCallbacks) -{ - return drwav_init_memory_ex(pWav, data, dataSize, NULL, NULL, 0, pAllocationCallbacks); -} - -drwav_bool32 drwav_init_memory_ex(drwav* pWav, const void* data, size_t dataSize, drwav_chunk_proc onChunk, void* pChunkUserData, drwav_uint32 flags, const drwav_allocation_callbacks* pAllocationCallbacks) -{ - if (data == NULL || dataSize == 0) { - return DRWAV_FALSE; - } - - if (!drwav_preinit(pWav, drwav__on_read_memory, drwav__on_seek_memory, pWav, pAllocationCallbacks)) { - return DRWAV_FALSE; - } - - pWav->memoryStream.data = (const unsigned char*)data; - pWav->memoryStream.dataSize = dataSize; - pWav->memoryStream.currentReadPos = 0; - - return drwav_init__internal(pWav, onChunk, pChunkUserData, flags); -} - - -drwav_bool32 drwav_init_memory_write__internal(drwav* pWav, void** ppData, size_t* pDataSize, const drwav_data_format* pFormat, drwav_uint64 totalSampleCount, drwav_bool32 isSequential, const drwav_allocation_callbacks* pAllocationCallbacks) -{ - if (ppData == NULL || pDataSize == NULL) { - return DRWAV_FALSE; - } - - *ppData = NULL; /* Important because we're using realloc()! */ - *pDataSize = 0; - - if (!drwav_preinit_write(pWav, pFormat, isSequential, drwav__on_write_memory, drwav__on_seek_memory_write, pWav, pAllocationCallbacks)) { - return DRWAV_FALSE; - } - - pWav->memoryStreamWrite.ppData = ppData; - pWav->memoryStreamWrite.pDataSize = pDataSize; - pWav->memoryStreamWrite.dataSize = 0; - pWav->memoryStreamWrite.dataCapacity = 0; - pWav->memoryStreamWrite.currentWritePos = 0; - - return drwav_init_write__internal(pWav, pFormat, totalSampleCount); -} - -drwav_bool32 drwav_init_memory_write(drwav* pWav, void** ppData, size_t* pDataSize, const drwav_data_format* pFormat, const drwav_allocation_callbacks* pAllocationCallbacks) -{ - return drwav_init_memory_write__internal(pWav, ppData, pDataSize, pFormat, 0, DRWAV_FALSE, pAllocationCallbacks); -} - -drwav_bool32 drwav_init_memory_write_sequential(drwav* pWav, void** ppData, size_t* pDataSize, const drwav_data_format* pFormat, drwav_uint64 totalSampleCount, const drwav_allocation_callbacks* pAllocationCallbacks) -{ - return drwav_init_memory_write__internal(pWav, ppData, pDataSize, pFormat, totalSampleCount, DRWAV_TRUE, pAllocationCallbacks); -} - -drwav_bool32 drwav_init_memory_write_sequential_pcm_frames(drwav* pWav, void** ppData, size_t* pDataSize, const drwav_data_format* pFormat, drwav_uint64 totalPCMFrameCount, const drwav_allocation_callbacks* pAllocationCallbacks) -{ - if (pFormat == NULL) { - return DRWAV_FALSE; - } - - return drwav_init_memory_write_sequential(pWav, ppData, pDataSize, pFormat, totalPCMFrameCount*pFormat->channels, pAllocationCallbacks); -} - - - -drwav_result drwav_uninit(drwav* pWav) -{ - drwav_result result = DRWAV_SUCCESS; - - if (pWav == NULL) { - return DRWAV_INVALID_ARGS; - } - - /* - If the drwav object was opened in write mode we'll need to finalize a few things: - - Make sure the "data" chunk is aligned to 16-bits for RIFF containers, or 64 bits for W64 containers. - - Set the size of the "data" chunk. - */ - if (pWav->onWrite != NULL) { - drwav_uint32 paddingSize = 0; - - /* Padding. Do not adjust pWav->dataChunkDataSize - this should not include the padding. */ - if (pWav->container == drwav_container_riff) { - paddingSize = drwav__chunk_padding_size_riff(pWav->dataChunkDataSize); - } else { - paddingSize = drwav__chunk_padding_size_w64(pWav->dataChunkDataSize); - } - - if (paddingSize > 0) { - drwav_uint64 paddingData = 0; - pWav->onWrite(pWav->pUserData, &paddingData, paddingSize); - } - - /* - Chunk sizes. When using sequential mode, these will have been filled in at initialization time. We only need - to do this when using non-sequential mode. - */ - if (pWav->onSeek && !pWav->isSequentialWrite) { - if (pWav->container == drwav_container_riff) { - /* The "RIFF" chunk size. */ - if (pWav->onSeek(pWav->pUserData, 4, drwav_seek_origin_start)) { - drwav_uint32 riffChunkSize = drwav__riff_chunk_size_riff(pWav->dataChunkDataSize); - pWav->onWrite(pWav->pUserData, &riffChunkSize, 4); - } - - /* the "data" chunk size. */ - if (pWav->onSeek(pWav->pUserData, (int)pWav->dataChunkDataPos + 4, drwav_seek_origin_start)) { - drwav_uint32 dataChunkSize = drwav__data_chunk_size_riff(pWav->dataChunkDataSize); - pWav->onWrite(pWav->pUserData, &dataChunkSize, 4); - } - } else { - /* The "RIFF" chunk size. */ - if (pWav->onSeek(pWav->pUserData, 16, drwav_seek_origin_start)) { - drwav_uint64 riffChunkSize = drwav__riff_chunk_size_w64(pWav->dataChunkDataSize); - pWav->onWrite(pWav->pUserData, &riffChunkSize, 8); - } - - /* The "data" chunk size. */ - if (pWav->onSeek(pWav->pUserData, (int)pWav->dataChunkDataPos + 16, drwav_seek_origin_start)) { - drwav_uint64 dataChunkSize = drwav__data_chunk_size_w64(pWav->dataChunkDataSize); - pWav->onWrite(pWav->pUserData, &dataChunkSize, 8); - } - } - } - - /* Validation for sequential mode. */ - if (pWav->isSequentialWrite) { - if (pWav->dataChunkDataSize != pWav->dataChunkDataSizeTargetWrite) { - result = DRWAV_INVALID_FILE; - } - } - } - -#ifndef DR_WAV_NO_STDIO - /* - If we opened the file with drwav_open_file() we will want to close the file handle. We can know whether or not drwav_open_file() - was used by looking at the onRead and onSeek callbacks. - */ - if (pWav->onRead == drwav__on_read_stdio || pWav->onWrite == drwav__on_write_stdio) { - fclose((FILE*)pWav->pUserData); - } -#endif - - return result; -} - - - -size_t drwav_read_raw(drwav* pWav, size_t bytesToRead, void* pBufferOut) -{ - size_t bytesRead; - - if (pWav == NULL || bytesToRead == 0 || pBufferOut == NULL) { - return 0; - } - - if (bytesToRead > pWav->bytesRemaining) { - bytesToRead = (size_t)pWav->bytesRemaining; - } - - bytesRead = pWav->onRead(pWav->pUserData, pBufferOut, bytesToRead); - - pWav->bytesRemaining -= bytesRead; - return bytesRead; -} - - - -drwav_uint64 drwav_read_pcm_frames_le(drwav* pWav, drwav_uint64 framesToRead, void* pBufferOut) -{ - drwav_uint32 bytesPerFrame; - - if (pWav == NULL || framesToRead == 0 || pBufferOut == NULL) { - return 0; - } - - /* Cannot use this function for compressed formats. */ - if (drwav__is_compressed_format_tag(pWav->translatedFormatTag)) { - return 0; - } - - bytesPerFrame = drwav_get_bytes_per_pcm_frame(pWav); - if (bytesPerFrame == 0) { - return 0; - } - - /* Don't try to read more samples than can potentially fit in the output buffer. */ - if (framesToRead * bytesPerFrame > DRWAV_SIZE_MAX) { - framesToRead = DRWAV_SIZE_MAX / bytesPerFrame; - } - - return drwav_read_raw(pWav, (size_t)(framesToRead * bytesPerFrame), pBufferOut) / bytesPerFrame; -} - -drwav_uint64 drwav_read_pcm_frames_be(drwav* pWav, drwav_uint64 framesToRead, void* pBufferOut) -{ - drwav_uint64 framesRead = drwav_read_pcm_frames_le(pWav, framesToRead, pBufferOut); - drwav__bswap_samples(pBufferOut, framesRead*pWav->channels, drwav_get_bytes_per_pcm_frame(pWav)/pWav->channels, pWav->translatedFormatTag); - - return framesRead; -} - -drwav_uint64 drwav_read_pcm_frames(drwav* pWav, drwav_uint64 framesToRead, void* pBufferOut) -{ - if (drwav__is_little_endian()) { - return drwav_read_pcm_frames_le(pWav, framesToRead, pBufferOut); - } else { - return drwav_read_pcm_frames_be(pWav, framesToRead, pBufferOut); - } -} - - - -drwav_bool32 drwav_seek_to_first_pcm_frame(drwav* pWav) -{ - if (pWav->onWrite != NULL) { - return DRWAV_FALSE; /* No seeking in write mode. */ - } - - if (!pWav->onSeek(pWav->pUserData, (int)pWav->dataChunkDataPos, drwav_seek_origin_start)) { - return DRWAV_FALSE; - } - - if (drwav__is_compressed_format_tag(pWav->translatedFormatTag)) { - pWav->compressed.iCurrentPCMFrame = 0; - } - - pWav->bytesRemaining = pWav->dataChunkDataSize; - return DRWAV_TRUE; -} - -drwav_bool32 drwav_seek_to_pcm_frame(drwav* pWav, drwav_uint64 targetFrameIndex) -{ - /* Seeking should be compatible with wave files > 2GB. */ - - if (pWav == NULL || pWav->onSeek == NULL) { - return DRWAV_FALSE; - } - - /* No seeking in write mode. */ - if (pWav->onWrite != NULL) { - return DRWAV_FALSE; - } - - /* If there are no samples, just return DRWAV_TRUE without doing anything. */ - if (pWav->totalPCMFrameCount == 0) { - return DRWAV_TRUE; - } - - /* Make sure the sample is clamped. */ - if (targetFrameIndex >= pWav->totalPCMFrameCount) { - targetFrameIndex = pWav->totalPCMFrameCount - 1; - } - - /* - For compressed formats we just use a slow generic seek. If we are seeking forward we just seek forward. If we are going backwards we need - to seek back to the start. - */ - if (drwav__is_compressed_format_tag(pWav->translatedFormatTag)) { - /* TODO: This can be optimized. */ - - /* - If we're seeking forward it's simple - just keep reading samples until we hit the sample we're requesting. If we're seeking backwards, - we first need to seek back to the start and then just do the same thing as a forward seek. - */ - if (targetFrameIndex < pWav->compressed.iCurrentPCMFrame) { - if (!drwav_seek_to_first_pcm_frame(pWav)) { - return DRWAV_FALSE; - } - } - - if (targetFrameIndex > pWav->compressed.iCurrentPCMFrame) { - drwav_uint64 offsetInFrames = targetFrameIndex - pWav->compressed.iCurrentPCMFrame; - - drwav_int16 devnull[2048]; - while (offsetInFrames > 0) { - drwav_uint64 framesRead = 0; - drwav_uint64 framesToRead = offsetInFrames; - if (framesToRead > drwav_countof(devnull)/pWav->channels) { - framesToRead = drwav_countof(devnull)/pWav->channels; - } - - if (pWav->translatedFormatTag == DR_WAVE_FORMAT_ADPCM) { - framesRead = drwav_read_pcm_frames_s16__msadpcm(pWav, framesToRead, devnull); - } else if (pWav->translatedFormatTag == DR_WAVE_FORMAT_DVI_ADPCM) { - framesRead = drwav_read_pcm_frames_s16__ima(pWav, framesToRead, devnull); - } else { - assert(DRWAV_FALSE); /* If this assertion is triggered it means I've implemented a new compressed format but forgot to add a branch for it here. */ - } - - if (framesRead != framesToRead) { - return DRWAV_FALSE; - } - - offsetInFrames -= framesRead; - } - } - } else { - drwav_uint64 totalSizeInBytes; - drwav_uint64 currentBytePos; - drwav_uint64 targetBytePos; - drwav_uint64 offset; - - totalSizeInBytes = pWav->totalPCMFrameCount * drwav_get_bytes_per_pcm_frame(pWav); - DRWAV_ASSERT(totalSizeInBytes >= pWav->bytesRemaining); - - currentBytePos = totalSizeInBytes - pWav->bytesRemaining; - targetBytePos = targetFrameIndex * drwav_get_bytes_per_pcm_frame(pWav); - - if (currentBytePos < targetBytePos) { - /* Offset forwards. */ - offset = (targetBytePos - currentBytePos); - } else { - /* Offset backwards. */ - if (!drwav_seek_to_first_pcm_frame(pWav)) { - return DRWAV_FALSE; - } - offset = targetBytePos; - } - - while (offset > 0) { - int offset32 = ((offset > INT_MAX) ? INT_MAX : (int)offset); - if (!pWav->onSeek(pWav->pUserData, offset32, drwav_seek_origin_current)) { - return DRWAV_FALSE; - } - - pWav->bytesRemaining -= offset32; - offset -= offset32; - } - } - - return DRWAV_TRUE; -} - - -size_t drwav_write_raw(drwav* pWav, size_t bytesToWrite, const void* pData) -{ - size_t bytesWritten; - - if (pWav == NULL || bytesToWrite == 0 || pData == NULL) { - return 0; - } - - bytesWritten = pWav->onWrite(pWav->pUserData, pData, bytesToWrite); - pWav->dataChunkDataSize += bytesWritten; - - return bytesWritten; -} - - -drwav_uint64 drwav_write_pcm_frames_le(drwav* pWav, drwav_uint64 framesToWrite, const void* pData) -{ - drwav_uint64 bytesToWrite; - drwav_uint64 bytesWritten; - const drwav_uint8* pRunningData; - - if (pWav == NULL || framesToWrite == 0 || pData == NULL) { - return 0; - } - - bytesToWrite = ((framesToWrite * pWav->channels * pWav->bitsPerSample) / 8); - if (bytesToWrite > DRWAV_SIZE_MAX) { - return 0; - } - - bytesWritten = 0; - pRunningData = (const drwav_uint8*)pData; - - while (bytesToWrite > 0) { - size_t bytesJustWritten; - drwav_uint64 bytesToWriteThisIteration; - - bytesToWriteThisIteration = bytesToWrite; - DRWAV_ASSERT(bytesToWriteThisIteration <= DRWAV_SIZE_MAX); /* <-- This is checked above. */ - - bytesJustWritten = drwav_write_raw(pWav, (size_t)bytesToWriteThisIteration, pRunningData); - if (bytesJustWritten == 0) { - break; - } - - bytesToWrite -= bytesJustWritten; - bytesWritten += bytesJustWritten; - pRunningData += bytesJustWritten; - } - - return (bytesWritten * 8) / pWav->bitsPerSample / pWav->channels; -} - -drwav_uint64 drwav_write_pcm_frames_be(drwav* pWav, drwav_uint64 framesToWrite, const void* pData) -{ - drwav_uint64 bytesToWrite; - drwav_uint64 bytesWritten; - drwav_uint32 bytesPerSample; - const drwav_uint8* pRunningData; - - if (pWav == NULL || framesToWrite == 0 || pData == NULL) { - return 0; - } - - bytesToWrite = ((framesToWrite * pWav->channels * pWav->bitsPerSample) / 8); - if (bytesToWrite > DRWAV_SIZE_MAX) { - return 0; - } - - bytesWritten = 0; - pRunningData = (const drwav_uint8*)pData; - - bytesPerSample = drwav_get_bytes_per_pcm_frame(pWav) / pWav->channels; - - while (bytesToWrite > 0) { - drwav_uint8 temp[4096]; - drwav_uint32 sampleCount; - size_t bytesJustWritten; - drwav_uint64 bytesToWriteThisIteration; - - bytesToWriteThisIteration = bytesToWrite; - DRWAV_ASSERT(bytesToWriteThisIteration <= DRWAV_SIZE_MAX); /* <-- This is checked above. */ - - /* - WAV files are always little-endian. We need to byte swap on big-endian architectures. Since our input buffer is read-only we need - to use an intermediary buffer for the conversion. - */ - sampleCount = sizeof(temp)/bytesPerSample; - - if (bytesToWriteThisIteration > ((drwav_uint64)sampleCount)*bytesPerSample) { - bytesToWriteThisIteration = ((drwav_uint64)sampleCount)*bytesPerSample; - } - - DRWAV_COPY_MEMORY(temp, pRunningData, (size_t)bytesToWriteThisIteration); - drwav__bswap_samples(temp, sampleCount, bytesPerSample, pWav->translatedFormatTag); - - bytesJustWritten = drwav_write_raw(pWav, (size_t)bytesToWriteThisIteration, temp); - if (bytesJustWritten == 0) { - break; - } - - bytesToWrite -= bytesJustWritten; - bytesWritten += bytesJustWritten; - pRunningData += bytesJustWritten; - } - - return (bytesWritten * 8) / pWav->bitsPerSample / pWav->channels; -} - -drwav_uint64 drwav_write_pcm_frames(drwav* pWav, drwav_uint64 framesToWrite, const void* pData) -{ - if (drwav__is_little_endian()) { - return drwav_write_pcm_frames_le(pWav, framesToWrite, pData); - } else { - return drwav_write_pcm_frames_be(pWav, framesToWrite, pData); - } -} - - -drwav_uint64 drwav_read_pcm_frames_s16__msadpcm(drwav* pWav, drwav_uint64 framesToRead, drwav_int16* pBufferOut) -{ - drwav_uint64 totalFramesRead = 0; - - DRWAV_ASSERT(pWav != NULL); - DRWAV_ASSERT(framesToRead > 0); - DRWAV_ASSERT(pBufferOut != NULL); - - /* TODO: Lots of room for optimization here. */ - - while (framesToRead > 0 && pWav->compressed.iCurrentPCMFrame < pWav->totalPCMFrameCount) { - /* If there are no cached frames we need to load a new block. */ - if (pWav->msadpcm.cachedFrameCount == 0 && pWav->msadpcm.bytesRemainingInBlock == 0) { - if (pWav->channels == 1) { - /* Mono. */ - drwav_uint8 header[7]; - if (pWav->onRead(pWav->pUserData, header, sizeof(header)) != sizeof(header)) { - return totalFramesRead; - } - pWav->msadpcm.bytesRemainingInBlock = pWav->fmt.blockAlign - sizeof(header); - - pWav->msadpcm.predictor[0] = header[0]; - pWav->msadpcm.delta[0] = drwav__bytes_to_s16(header + 1); - pWav->msadpcm.prevFrames[0][1] = (drwav_int32)drwav__bytes_to_s16(header + 3); - pWav->msadpcm.prevFrames[0][0] = (drwav_int32)drwav__bytes_to_s16(header + 5); - pWav->msadpcm.cachedFrames[2] = pWav->msadpcm.prevFrames[0][0]; - pWav->msadpcm.cachedFrames[3] = pWav->msadpcm.prevFrames[0][1]; - pWav->msadpcm.cachedFrameCount = 2; - } else { - /* Stereo. */ - drwav_uint8 header[14]; - if (pWav->onRead(pWav->pUserData, header, sizeof(header)) != sizeof(header)) { - return totalFramesRead; - } - pWav->msadpcm.bytesRemainingInBlock = pWav->fmt.blockAlign - sizeof(header); - - pWav->msadpcm.predictor[0] = header[0]; - pWav->msadpcm.predictor[1] = header[1]; - pWav->msadpcm.delta[0] = drwav__bytes_to_s16(header + 2); - pWav->msadpcm.delta[1] = drwav__bytes_to_s16(header + 4); - pWav->msadpcm.prevFrames[0][1] = (drwav_int32)drwav__bytes_to_s16(header + 6); - pWav->msadpcm.prevFrames[1][1] = (drwav_int32)drwav__bytes_to_s16(header + 8); - pWav->msadpcm.prevFrames[0][0] = (drwav_int32)drwav__bytes_to_s16(header + 10); - pWav->msadpcm.prevFrames[1][0] = (drwav_int32)drwav__bytes_to_s16(header + 12); - - pWav->msadpcm.cachedFrames[0] = pWav->msadpcm.prevFrames[0][0]; - pWav->msadpcm.cachedFrames[1] = pWav->msadpcm.prevFrames[1][0]; - pWav->msadpcm.cachedFrames[2] = pWav->msadpcm.prevFrames[0][1]; - pWav->msadpcm.cachedFrames[3] = pWav->msadpcm.prevFrames[1][1]; - pWav->msadpcm.cachedFrameCount = 2; - } - } - - /* Output anything that's cached. */ - while (framesToRead > 0 && pWav->msadpcm.cachedFrameCount > 0 && pWav->compressed.iCurrentPCMFrame < pWav->totalPCMFrameCount) { - drwav_uint32 iSample = 0; - for (iSample = 0; iSample < pWav->channels; iSample += 1) { - pBufferOut[iSample] = (drwav_int16)pWav->msadpcm.cachedFrames[(drwav_countof(pWav->msadpcm.cachedFrames) - (pWav->msadpcm.cachedFrameCount*pWav->channels)) + iSample]; - } - - pBufferOut += pWav->channels; - framesToRead -= 1; - totalFramesRead += 1; - pWav->compressed.iCurrentPCMFrame += 1; - pWav->msadpcm.cachedFrameCount -= 1; - } - - if (framesToRead == 0) { - return totalFramesRead; - } - - - /* - If there's nothing left in the cache, just go ahead and load more. If there's nothing left to load in the current block we just continue to the next - loop iteration which will trigger the loading of a new block. - */ - if (pWav->msadpcm.cachedFrameCount == 0) { - if (pWav->msadpcm.bytesRemainingInBlock == 0) { - continue; - } else { - static drwav_int32 adaptationTable[] = { - 230, 230, 230, 230, 307, 409, 512, 614, - 768, 614, 512, 409, 307, 230, 230, 230 - }; - static drwav_int32 coeff1Table[] = { 256, 512, 0, 192, 240, 460, 392 }; - static drwav_int32 coeff2Table[] = { 0, -256, 0, 64, 0, -208, -232 }; - - drwav_uint8 nibbles; - drwav_int32 nibble0; - drwav_int32 nibble1; - - if (pWav->onRead(pWav->pUserData, &nibbles, 1) != 1) { - return totalFramesRead; - } - pWav->msadpcm.bytesRemainingInBlock -= 1; - - /* TODO: Optimize away these if statements. */ - nibble0 = ((nibbles & 0xF0) >> 4); if ((nibbles & 0x80)) { nibble0 |= 0xFFFFFFF0UL; } - nibble1 = ((nibbles & 0x0F) >> 0); if ((nibbles & 0x08)) { nibble1 |= 0xFFFFFFF0UL; } - - if (pWav->channels == 1) { - /* Mono. */ - drwav_int32 newSample0; - drwav_int32 newSample1; - - newSample0 = ((pWav->msadpcm.prevFrames[0][1] * coeff1Table[pWav->msadpcm.predictor[0]]) + (pWav->msadpcm.prevFrames[0][0] * coeff2Table[pWav->msadpcm.predictor[0]])) >> 8; - newSample0 += nibble0 * pWav->msadpcm.delta[0]; - newSample0 = drwav_clamp(newSample0, -32768, 32767); - - pWav->msadpcm.delta[0] = (adaptationTable[((nibbles & 0xF0) >> 4)] * pWav->msadpcm.delta[0]) >> 8; - if (pWav->msadpcm.delta[0] < 16) { - pWav->msadpcm.delta[0] = 16; - } - - pWav->msadpcm.prevFrames[0][0] = pWav->msadpcm.prevFrames[0][1]; - pWav->msadpcm.prevFrames[0][1] = newSample0; - - - newSample1 = ((pWav->msadpcm.prevFrames[0][1] * coeff1Table[pWav->msadpcm.predictor[0]]) + (pWav->msadpcm.prevFrames[0][0] * coeff2Table[pWav->msadpcm.predictor[0]])) >> 8; - newSample1 += nibble1 * pWav->msadpcm.delta[0]; - newSample1 = drwav_clamp(newSample1, -32768, 32767); - - pWav->msadpcm.delta[0] = (adaptationTable[((nibbles & 0x0F) >> 0)] * pWav->msadpcm.delta[0]) >> 8; - if (pWav->msadpcm.delta[0] < 16) { - pWav->msadpcm.delta[0] = 16; - } - - pWav->msadpcm.prevFrames[0][0] = pWav->msadpcm.prevFrames[0][1]; - pWav->msadpcm.prevFrames[0][1] = newSample1; - - - pWav->msadpcm.cachedFrames[2] = newSample0; - pWav->msadpcm.cachedFrames[3] = newSample1; - pWav->msadpcm.cachedFrameCount = 2; - } else { - /* Stereo. */ - drwav_int32 newSample0; - drwav_int32 newSample1; - - /* Left. */ - newSample0 = ((pWav->msadpcm.prevFrames[0][1] * coeff1Table[pWav->msadpcm.predictor[0]]) + (pWav->msadpcm.prevFrames[0][0] * coeff2Table[pWav->msadpcm.predictor[0]])) >> 8; - newSample0 += nibble0 * pWav->msadpcm.delta[0]; - newSample0 = drwav_clamp(newSample0, -32768, 32767); - - pWav->msadpcm.delta[0] = (adaptationTable[((nibbles & 0xF0) >> 4)] * pWav->msadpcm.delta[0]) >> 8; - if (pWav->msadpcm.delta[0] < 16) { - pWav->msadpcm.delta[0] = 16; - } - - pWav->msadpcm.prevFrames[0][0] = pWav->msadpcm.prevFrames[0][1]; - pWav->msadpcm.prevFrames[0][1] = newSample0; - - - /* Right. */ - newSample1 = ((pWav->msadpcm.prevFrames[1][1] * coeff1Table[pWav->msadpcm.predictor[1]]) + (pWav->msadpcm.prevFrames[1][0] * coeff2Table[pWav->msadpcm.predictor[1]])) >> 8; - newSample1 += nibble1 * pWav->msadpcm.delta[1]; - newSample1 = drwav_clamp(newSample1, -32768, 32767); - - pWav->msadpcm.delta[1] = (adaptationTable[((nibbles & 0x0F) >> 0)] * pWav->msadpcm.delta[1]) >> 8; - if (pWav->msadpcm.delta[1] < 16) { - pWav->msadpcm.delta[1] = 16; - } - - pWav->msadpcm.prevFrames[1][0] = pWav->msadpcm.prevFrames[1][1]; - pWav->msadpcm.prevFrames[1][1] = newSample1; - - pWav->msadpcm.cachedFrames[2] = newSample0; - pWav->msadpcm.cachedFrames[3] = newSample1; - pWav->msadpcm.cachedFrameCount = 1; - } - } - } - } - - return totalFramesRead; -} - - -drwav_uint64 drwav_read_pcm_frames_s16__ima(drwav* pWav, drwav_uint64 framesToRead, drwav_int16* pBufferOut) -{ - drwav_uint64 totalFramesRead = 0; - - DRWAV_ASSERT(pWav != NULL); - DRWAV_ASSERT(framesToRead > 0); - DRWAV_ASSERT(pBufferOut != NULL); - - /* TODO: Lots of room for optimization here. */ - - while (framesToRead > 0 && pWav->compressed.iCurrentPCMFrame < pWav->totalPCMFrameCount) { - /* If there are no cached samples we need to load a new block. */ - if (pWav->ima.cachedFrameCount == 0 && pWav->ima.bytesRemainingInBlock == 0) { - if (pWav->channels == 1) { - /* Mono. */ - drwav_uint8 header[4]; - if (pWav->onRead(pWav->pUserData, header, sizeof(header)) != sizeof(header)) { - return totalFramesRead; - } - pWav->ima.bytesRemainingInBlock = pWav->fmt.blockAlign - sizeof(header); - - pWav->ima.predictor[0] = drwav__bytes_to_s16(header + 0); - pWav->ima.stepIndex[0] = header[2]; - pWav->ima.cachedFrames[drwav_countof(pWav->ima.cachedFrames) - 1] = pWav->ima.predictor[0]; - pWav->ima.cachedFrameCount = 1; - } else { - /* Stereo. */ - drwav_uint8 header[8]; - if (pWav->onRead(pWav->pUserData, header, sizeof(header)) != sizeof(header)) { - return totalFramesRead; - } - pWav->ima.bytesRemainingInBlock = pWav->fmt.blockAlign - sizeof(header); - - pWav->ima.predictor[0] = drwav__bytes_to_s16(header + 0); - pWav->ima.stepIndex[0] = header[2]; - pWav->ima.predictor[1] = drwav__bytes_to_s16(header + 4); - pWav->ima.stepIndex[1] = header[6]; - - pWav->ima.cachedFrames[drwav_countof(pWav->ima.cachedFrames) - 2] = pWav->ima.predictor[0]; - pWav->ima.cachedFrames[drwav_countof(pWav->ima.cachedFrames) - 1] = pWav->ima.predictor[1]; - pWav->ima.cachedFrameCount = 1; - } - } - - /* Output anything that's cached. */ - while (framesToRead > 0 && pWav->ima.cachedFrameCount > 0 && pWav->compressed.iCurrentPCMFrame < pWav->totalPCMFrameCount) { - drwav_uint32 iSample; - for (iSample = 0; iSample < pWav->channels; iSample += 1) { - pBufferOut[iSample] = (drwav_int16)pWav->ima.cachedFrames[(drwav_countof(pWav->ima.cachedFrames) - (pWav->ima.cachedFrameCount*pWav->channels)) + iSample]; - } - - pBufferOut += pWav->channels; - framesToRead -= 1; - totalFramesRead += 1; - pWav->compressed.iCurrentPCMFrame += 1; - pWav->ima.cachedFrameCount -= 1; - } - - if (framesToRead == 0) { - return totalFramesRead; - } - - /* - If there's nothing left in the cache, just go ahead and load more. If there's nothing left to load in the current block we just continue to the next - loop iteration which will trigger the loading of a new block. - */ - if (pWav->ima.cachedFrameCount == 0) { - if (pWav->ima.bytesRemainingInBlock == 0) { - continue; - } else { - static drwav_int32 indexTable[16] = { - -1, -1, -1, -1, 2, 4, 6, 8, - -1, -1, -1, -1, 2, 4, 6, 8 - }; - - static drwav_int32 stepTable[89] = { - 7, 8, 9, 10, 11, 12, 13, 14, 16, 17, - 19, 21, 23, 25, 28, 31, 34, 37, 41, 45, - 50, 55, 60, 66, 73, 80, 88, 97, 107, 118, - 130, 143, 157, 173, 190, 209, 230, 253, 279, 307, - 337, 371, 408, 449, 494, 544, 598, 658, 724, 796, - 876, 963, 1060, 1166, 1282, 1411, 1552, 1707, 1878, 2066, - 2272, 2499, 2749, 3024, 3327, 3660, 4026, 4428, 4871, 5358, - 5894, 6484, 7132, 7845, 8630, 9493, 10442, 11487, 12635, 13899, - 15289, 16818, 18500, 20350, 22385, 24623, 27086, 29794, 32767 - }; - - drwav_uint32 iChannel; - - /* - From what I can tell with stereo streams, it looks like every 4 bytes (8 samples) is for one channel. So it goes 4 bytes for the - left channel, 4 bytes for the right channel. - */ - pWav->ima.cachedFrameCount = 8; - for (iChannel = 0; iChannel < pWav->channels; ++iChannel) { - drwav_uint32 iByte; - drwav_uint8 nibbles[4]; - if (pWav->onRead(pWav->pUserData, &nibbles, 4) != 4) { - pWav->ima.cachedFrameCount = 0; - return totalFramesRead; - } - pWav->ima.bytesRemainingInBlock -= 4; - - for (iByte = 0; iByte < 4; ++iByte) { - drwav_uint8 nibble0 = ((nibbles[iByte] & 0x0F) >> 0); - drwav_uint8 nibble1 = ((nibbles[iByte] & 0xF0) >> 4); - - drwav_int32 step = stepTable[pWav->ima.stepIndex[iChannel]]; - drwav_int32 predictor = pWav->ima.predictor[iChannel]; - - drwav_int32 diff = step >> 3; - if (nibble0 & 1) diff += step >> 2; - if (nibble0 & 2) diff += step >> 1; - if (nibble0 & 4) diff += step; - if (nibble0 & 8) diff = -diff; - - predictor = drwav_clamp(predictor + diff, -32768, 32767); - pWav->ima.predictor[iChannel] = predictor; - pWav->ima.stepIndex[iChannel] = drwav_clamp(pWav->ima.stepIndex[iChannel] + indexTable[nibble0], 0, (drwav_int32)drwav_countof(stepTable)-1); - pWav->ima.cachedFrames[(drwav_countof(pWav->ima.cachedFrames) - (pWav->ima.cachedFrameCount*pWav->channels)) + (iByte*2+0)*pWav->channels + iChannel] = predictor; - - - step = stepTable[pWav->ima.stepIndex[iChannel]]; - predictor = pWav->ima.predictor[iChannel]; - - diff = step >> 3; - if (nibble1 & 1) diff += step >> 2; - if (nibble1 & 2) diff += step >> 1; - if (nibble1 & 4) diff += step; - if (nibble1 & 8) diff = -diff; - - predictor = drwav_clamp(predictor + diff, -32768, 32767); - pWav->ima.predictor[iChannel] = predictor; - pWav->ima.stepIndex[iChannel] = drwav_clamp(pWav->ima.stepIndex[iChannel] + indexTable[nibble1], 0, (drwav_int32)drwav_countof(stepTable)-1); - pWav->ima.cachedFrames[(drwav_countof(pWav->ima.cachedFrames) - (pWav->ima.cachedFrameCount*pWav->channels)) + (iByte*2+1)*pWav->channels + iChannel] = predictor; - } - } - } - } - } - - return totalFramesRead; -} - - -#ifndef DR_WAV_NO_CONVERSION_API -static unsigned short g_drwavAlawTable[256] = { - 0xEA80, 0xEB80, 0xE880, 0xE980, 0xEE80, 0xEF80, 0xEC80, 0xED80, 0xE280, 0xE380, 0xE080, 0xE180, 0xE680, 0xE780, 0xE480, 0xE580, - 0xF540, 0xF5C0, 0xF440, 0xF4C0, 0xF740, 0xF7C0, 0xF640, 0xF6C0, 0xF140, 0xF1C0, 0xF040, 0xF0C0, 0xF340, 0xF3C0, 0xF240, 0xF2C0, - 0xAA00, 0xAE00, 0xA200, 0xA600, 0xBA00, 0xBE00, 0xB200, 0xB600, 0x8A00, 0x8E00, 0x8200, 0x8600, 0x9A00, 0x9E00, 0x9200, 0x9600, - 0xD500, 0xD700, 0xD100, 0xD300, 0xDD00, 0xDF00, 0xD900, 0xDB00, 0xC500, 0xC700, 0xC100, 0xC300, 0xCD00, 0xCF00, 0xC900, 0xCB00, - 0xFEA8, 0xFEB8, 0xFE88, 0xFE98, 0xFEE8, 0xFEF8, 0xFEC8, 0xFED8, 0xFE28, 0xFE38, 0xFE08, 0xFE18, 0xFE68, 0xFE78, 0xFE48, 0xFE58, - 0xFFA8, 0xFFB8, 0xFF88, 0xFF98, 0xFFE8, 0xFFF8, 0xFFC8, 0xFFD8, 0xFF28, 0xFF38, 0xFF08, 0xFF18, 0xFF68, 0xFF78, 0xFF48, 0xFF58, - 0xFAA0, 0xFAE0, 0xFA20, 0xFA60, 0xFBA0, 0xFBE0, 0xFB20, 0xFB60, 0xF8A0, 0xF8E0, 0xF820, 0xF860, 0xF9A0, 0xF9E0, 0xF920, 0xF960, - 0xFD50, 0xFD70, 0xFD10, 0xFD30, 0xFDD0, 0xFDF0, 0xFD90, 0xFDB0, 0xFC50, 0xFC70, 0xFC10, 0xFC30, 0xFCD0, 0xFCF0, 0xFC90, 0xFCB0, - 0x1580, 0x1480, 0x1780, 0x1680, 0x1180, 0x1080, 0x1380, 0x1280, 0x1D80, 0x1C80, 0x1F80, 0x1E80, 0x1980, 0x1880, 0x1B80, 0x1A80, - 0x0AC0, 0x0A40, 0x0BC0, 0x0B40, 0x08C0, 0x0840, 0x09C0, 0x0940, 0x0EC0, 0x0E40, 0x0FC0, 0x0F40, 0x0CC0, 0x0C40, 0x0DC0, 0x0D40, - 0x5600, 0x5200, 0x5E00, 0x5A00, 0x4600, 0x4200, 0x4E00, 0x4A00, 0x7600, 0x7200, 0x7E00, 0x7A00, 0x6600, 0x6200, 0x6E00, 0x6A00, - 0x2B00, 0x2900, 0x2F00, 0x2D00, 0x2300, 0x2100, 0x2700, 0x2500, 0x3B00, 0x3900, 0x3F00, 0x3D00, 0x3300, 0x3100, 0x3700, 0x3500, - 0x0158, 0x0148, 0x0178, 0x0168, 0x0118, 0x0108, 0x0138, 0x0128, 0x01D8, 0x01C8, 0x01F8, 0x01E8, 0x0198, 0x0188, 0x01B8, 0x01A8, - 0x0058, 0x0048, 0x0078, 0x0068, 0x0018, 0x0008, 0x0038, 0x0028, 0x00D8, 0x00C8, 0x00F8, 0x00E8, 0x0098, 0x0088, 0x00B8, 0x00A8, - 0x0560, 0x0520, 0x05E0, 0x05A0, 0x0460, 0x0420, 0x04E0, 0x04A0, 0x0760, 0x0720, 0x07E0, 0x07A0, 0x0660, 0x0620, 0x06E0, 0x06A0, - 0x02B0, 0x0290, 0x02F0, 0x02D0, 0x0230, 0x0210, 0x0270, 0x0250, 0x03B0, 0x0390, 0x03F0, 0x03D0, 0x0330, 0x0310, 0x0370, 0x0350 -}; - -static unsigned short g_drwavMulawTable[256] = { - 0x8284, 0x8684, 0x8A84, 0x8E84, 0x9284, 0x9684, 0x9A84, 0x9E84, 0xA284, 0xA684, 0xAA84, 0xAE84, 0xB284, 0xB684, 0xBA84, 0xBE84, - 0xC184, 0xC384, 0xC584, 0xC784, 0xC984, 0xCB84, 0xCD84, 0xCF84, 0xD184, 0xD384, 0xD584, 0xD784, 0xD984, 0xDB84, 0xDD84, 0xDF84, - 0xE104, 0xE204, 0xE304, 0xE404, 0xE504, 0xE604, 0xE704, 0xE804, 0xE904, 0xEA04, 0xEB04, 0xEC04, 0xED04, 0xEE04, 0xEF04, 0xF004, - 0xF0C4, 0xF144, 0xF1C4, 0xF244, 0xF2C4, 0xF344, 0xF3C4, 0xF444, 0xF4C4, 0xF544, 0xF5C4, 0xF644, 0xF6C4, 0xF744, 0xF7C4, 0xF844, - 0xF8A4, 0xF8E4, 0xF924, 0xF964, 0xF9A4, 0xF9E4, 0xFA24, 0xFA64, 0xFAA4, 0xFAE4, 0xFB24, 0xFB64, 0xFBA4, 0xFBE4, 0xFC24, 0xFC64, - 0xFC94, 0xFCB4, 0xFCD4, 0xFCF4, 0xFD14, 0xFD34, 0xFD54, 0xFD74, 0xFD94, 0xFDB4, 0xFDD4, 0xFDF4, 0xFE14, 0xFE34, 0xFE54, 0xFE74, - 0xFE8C, 0xFE9C, 0xFEAC, 0xFEBC, 0xFECC, 0xFEDC, 0xFEEC, 0xFEFC, 0xFF0C, 0xFF1C, 0xFF2C, 0xFF3C, 0xFF4C, 0xFF5C, 0xFF6C, 0xFF7C, - 0xFF88, 0xFF90, 0xFF98, 0xFFA0, 0xFFA8, 0xFFB0, 0xFFB8, 0xFFC0, 0xFFC8, 0xFFD0, 0xFFD8, 0xFFE0, 0xFFE8, 0xFFF0, 0xFFF8, 0x0000, - 0x7D7C, 0x797C, 0x757C, 0x717C, 0x6D7C, 0x697C, 0x657C, 0x617C, 0x5D7C, 0x597C, 0x557C, 0x517C, 0x4D7C, 0x497C, 0x457C, 0x417C, - 0x3E7C, 0x3C7C, 0x3A7C, 0x387C, 0x367C, 0x347C, 0x327C, 0x307C, 0x2E7C, 0x2C7C, 0x2A7C, 0x287C, 0x267C, 0x247C, 0x227C, 0x207C, - 0x1EFC, 0x1DFC, 0x1CFC, 0x1BFC, 0x1AFC, 0x19FC, 0x18FC, 0x17FC, 0x16FC, 0x15FC, 0x14FC, 0x13FC, 0x12FC, 0x11FC, 0x10FC, 0x0FFC, - 0x0F3C, 0x0EBC, 0x0E3C, 0x0DBC, 0x0D3C, 0x0CBC, 0x0C3C, 0x0BBC, 0x0B3C, 0x0ABC, 0x0A3C, 0x09BC, 0x093C, 0x08BC, 0x083C, 0x07BC, - 0x075C, 0x071C, 0x06DC, 0x069C, 0x065C, 0x061C, 0x05DC, 0x059C, 0x055C, 0x051C, 0x04DC, 0x049C, 0x045C, 0x041C, 0x03DC, 0x039C, - 0x036C, 0x034C, 0x032C, 0x030C, 0x02EC, 0x02CC, 0x02AC, 0x028C, 0x026C, 0x024C, 0x022C, 0x020C, 0x01EC, 0x01CC, 0x01AC, 0x018C, - 0x0174, 0x0164, 0x0154, 0x0144, 0x0134, 0x0124, 0x0114, 0x0104, 0x00F4, 0x00E4, 0x00D4, 0x00C4, 0x00B4, 0x00A4, 0x0094, 0x0084, - 0x0078, 0x0070, 0x0068, 0x0060, 0x0058, 0x0050, 0x0048, 0x0040, 0x0038, 0x0030, 0x0028, 0x0020, 0x0018, 0x0010, 0x0008, 0x0000 -}; - -static DRWAV_INLINE drwav_int16 drwav__alaw_to_s16(drwav_uint8 sampleIn) -{ - return (short)g_drwavAlawTable[sampleIn]; -} - -static DRWAV_INLINE drwav_int16 drwav__mulaw_to_s16(drwav_uint8 sampleIn) -{ - return (short)g_drwavMulawTable[sampleIn]; -} - - - -static void drwav__pcm_to_s16(drwav_int16* pOut, const unsigned char* pIn, size_t totalSampleCount, unsigned int bytesPerSample) -{ - unsigned int i; - - /* Special case for 8-bit sample data because it's treated as unsigned. */ - if (bytesPerSample == 1) { - drwav_u8_to_s16(pOut, pIn, totalSampleCount); - return; - } - - - /* Slightly more optimal implementation for common formats. */ - if (bytesPerSample == 2) { - for (i = 0; i < totalSampleCount; ++i) { - *pOut++ = ((const drwav_int16*)pIn)[i]; - } - return; - } - if (bytesPerSample == 3) { - drwav_s24_to_s16(pOut, pIn, totalSampleCount); - return; - } - if (bytesPerSample == 4) { - drwav_s32_to_s16(pOut, (const drwav_int32*)pIn, totalSampleCount); - return; - } - - - /* Anything more than 64 bits per sample is not supported. */ - if (bytesPerSample > 8) { - DRWAV_ZERO_MEMORY(pOut, totalSampleCount * sizeof(*pOut)); - return; - } - - - /* Generic, slow converter. */ - for (i = 0; i < totalSampleCount; ++i) { - drwav_uint64 sample = 0; - unsigned int shift = (8 - bytesPerSample) * 8; - - unsigned int j; - for (j = 0; j < bytesPerSample; j += 1) { - DRWAV_ASSERT(j < 8); - sample |= (drwav_uint64)(pIn[j]) << shift; - shift += 8; - } - - pIn += j; - *pOut++ = (drwav_int16)((drwav_int64)sample >> 48); - } -} - -static void drwav__ieee_to_s16(drwav_int16* pOut, const unsigned char* pIn, size_t totalSampleCount, unsigned int bytesPerSample) -{ - if (bytesPerSample == 4) { - drwav_f32_to_s16(pOut, (const float*)pIn, totalSampleCount); - return; - } else if (bytesPerSample == 8) { - drwav_f64_to_s16(pOut, (const double*)pIn, totalSampleCount); - return; - } else { - /* Only supporting 32- and 64-bit float. Output silence in all other cases. Contributions welcome for 16-bit float. */ - DRWAV_ZERO_MEMORY(pOut, totalSampleCount * sizeof(*pOut)); - return; - } -} - -drwav_uint64 drwav_read_pcm_frames_s16__pcm(drwav* pWav, drwav_uint64 framesToRead, drwav_int16* pBufferOut) -{ - drwav_uint32 bytesPerFrame; - drwav_uint64 totalFramesRead; - unsigned char sampleData[4096]; - - /* Fast path. */ - if (pWav->translatedFormatTag == DR_WAVE_FORMAT_PCM && pWav->bitsPerSample == 16) { - return drwav_read_pcm_frames(pWav, framesToRead, pBufferOut); - } - - bytesPerFrame = drwav_get_bytes_per_pcm_frame(pWav); - if (bytesPerFrame == 0) { - return 0; - } - - totalFramesRead = 0; - - while (framesToRead > 0) { - drwav_uint64 framesRead = drwav_read_pcm_frames(pWav, drwav_min(framesToRead, sizeof(sampleData)/bytesPerFrame), sampleData); - if (framesRead == 0) { - break; - } - - drwav__pcm_to_s16(pBufferOut, sampleData, (size_t)(framesRead*pWav->channels), bytesPerFrame/pWav->channels); - - pBufferOut += framesRead*pWav->channels; - framesToRead -= framesRead; - totalFramesRead += framesRead; - } - - return totalFramesRead; -} - -drwav_uint64 drwav_read_pcm_frames_s16__ieee(drwav* pWav, drwav_uint64 framesToRead, drwav_int16* pBufferOut) -{ - drwav_uint64 totalFramesRead; - unsigned char sampleData[4096]; - - drwav_uint32 bytesPerFrame = drwav_get_bytes_per_pcm_frame(pWav); - if (bytesPerFrame == 0) { - return 0; - } - - totalFramesRead = 0; - - while (framesToRead > 0) { - drwav_uint64 framesRead = drwav_read_pcm_frames(pWav, drwav_min(framesToRead, sizeof(sampleData)/bytesPerFrame), sampleData); - if (framesRead == 0) { - break; - } - - drwav__ieee_to_s16(pBufferOut, sampleData, (size_t)(framesRead*pWav->channels), bytesPerFrame/pWav->channels); - - pBufferOut += framesRead*pWav->channels; - framesToRead -= framesRead; - totalFramesRead += framesRead; - } - - return totalFramesRead; -} - -drwav_uint64 drwav_read_pcm_frames_s16__alaw(drwav* pWav, drwav_uint64 framesToRead, drwav_int16* pBufferOut) -{ - drwav_uint64 totalFramesRead; - unsigned char sampleData[4096]; - - drwav_uint32 bytesPerFrame = drwav_get_bytes_per_pcm_frame(pWav); - if (bytesPerFrame == 0) { - return 0; - } - - totalFramesRead = 0; - - while (framesToRead > 0) { - drwav_uint64 framesRead = drwav_read_pcm_frames(pWav, drwav_min(framesToRead, sizeof(sampleData)/bytesPerFrame), sampleData); - if (framesRead == 0) { - break; - } - - drwav_alaw_to_s16(pBufferOut, sampleData, (size_t)(framesRead*pWav->channels)); - - pBufferOut += framesRead*pWav->channels; - framesToRead -= framesRead; - totalFramesRead += framesRead; - } - - return totalFramesRead; -} - -drwav_uint64 drwav_read_pcm_frames_s16__mulaw(drwav* pWav, drwav_uint64 framesToRead, drwav_int16* pBufferOut) -{ - drwav_uint64 totalFramesRead; - unsigned char sampleData[4096]; - - drwav_uint32 bytesPerFrame = drwav_get_bytes_per_pcm_frame(pWav); - if (bytesPerFrame == 0) { - return 0; - } - - totalFramesRead = 0; - - while (framesToRead > 0) { - drwav_uint64 framesRead = drwav_read_pcm_frames(pWav, drwav_min(framesToRead, sizeof(sampleData)/bytesPerFrame), sampleData); - if (framesRead == 0) { - break; - } - - drwav_mulaw_to_s16(pBufferOut, sampleData, (size_t)(framesRead*pWav->channels)); - - pBufferOut += framesRead*pWav->channels; - framesToRead -= framesRead; - totalFramesRead += framesRead; - } - - return totalFramesRead; -} - -drwav_uint64 drwav_read_pcm_frames_s16(drwav* pWav, drwav_uint64 framesToRead, drwav_int16* pBufferOut) -{ - if (pWav == NULL || framesToRead == 0 || pBufferOut == NULL) { - return 0; - } - - /* Don't try to read more samples than can potentially fit in the output buffer. */ - if (framesToRead * pWav->channels * sizeof(drwav_int16) > DRWAV_SIZE_MAX) { - framesToRead = DRWAV_SIZE_MAX / sizeof(drwav_int16) / pWav->channels; - } - - if (pWav->translatedFormatTag == DR_WAVE_FORMAT_PCM) { - return drwav_read_pcm_frames_s16__pcm(pWav, framesToRead, pBufferOut); - } - - if (pWav->translatedFormatTag == DR_WAVE_FORMAT_IEEE_FLOAT) { - return drwav_read_pcm_frames_s16__ieee(pWav, framesToRead, pBufferOut); - } - - if (pWav->translatedFormatTag == DR_WA