Commit 47b17ad5 authored by David Reid's avatar David Reid

Update extras.

parent 45724880
/* /*
FLAC audio decoder. Choice of public domain or MIT-0. See license statements at the end of this file. FLAC audio decoder. Choice of public domain or MIT-0. See license statements at the end of this file.
dr_flac - v0.12.7 - 2020-03-14 dr_flac - v0.12.8 - 2020-04-04
David Reid - mackron@gmail.com 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. ...@@ -115,16 +117,18 @@ to the old per-sample APIs. You now need to use the "pcm_frame" versions.
/* /*
USAGE Introduction
===== ============
dr_flac is a single-file library. To use it, do something like the following in one .c file. dr_flac is a single file library. To use it, do something like the following in one .c file.
```c
#define DR_FLAC_IMPLEMENTATION #define DR_FLAC_IMPLEMENTATION
#include "dr_flac.h" #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, 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:
do something like the following:
```c
drflac* pFlac = drflac_open_file("MySong.flac", NULL); drflac* pFlac = drflac_open_file("MySong.flac", NULL);
if (pFlac == NULL) { if (pFlac == NULL) {
// Failed to open FLAC file // Failed to open FLAC file
...@@ -132,28 +136,29 @@ do something like the following: ...@@ -132,28 +136,29 @@ do something like the following:
drflac_int32* pSamples = malloc(pFlac->totalPCMFrameCount * pFlac->channels * sizeof(drflac_int32)); drflac_int32* pSamples = malloc(pFlac->totalPCMFrameCount * pFlac->channels * sizeof(drflac_int32));
drflac_uint64 numberOfInterleavedSamplesActuallyRead = drflac_read_pcm_frames_s32(pFlac, pFlac->totalPCMFrameCount, pSamples); 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 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,
channels and the bits per sample, should be directly accessible - just make sure you don't change their values. Samples are 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
always output as interleaved signed 32-bit PCM. In the example above a native FLAC stream was opened, however dr_flac has a native FLAC stream was opened, however dr_flac has seamless support for Ogg encapsulated FLAC streams as well.
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 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
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 as it can, up to the amount requested. Later on when you need the next batch of samples, just call it again. Example:
samples, just call it again. Example:
```c
while (drflac_read_pcm_frames_s32(pFlac, chunkSizeInPCMFrames, pChunkSamples) > 0) { while (drflac_read_pcm_frames_s32(pFlac, chunkSizeInPCMFrames, pChunkSamples) > 0) {
do_something(); do_something();
} }
```
You can seek to a specific sample with drflac_seek_to_sample(). The given sample is based on interleaving. So for example, 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
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. 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
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 sample at index 2 will be the second sample of the left channel, etc.
left channel, etc.
If you just want to quickly decode an entire FLAC file in one go you can do something like this: 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 channels;
unsigned int sampleRate; unsigned int sampleRate;
drflac_uint64 totalPCMFrameCount; drflac_uint64 totalPCMFrameCount;
...@@ -165,94 +170,132 @@ If you just want to quickly decode an entire FLAC file in one go you can do some ...@@ -165,94 +170,132 @@ If you just want to quickly decode an entire FLAC file in one go you can do some
... ...
drflac_free(pSampleData); 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
You can read samples as signed 16-bit integer and 32-bit floating-point PCM with the *_s16() and *_f32() family of APIs should be considered lossy.
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 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()`.
drflac_open_memory_with_metadata(). The rationale for keeping these APIs separate is that they're slightly slower than the 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
normal versions and also just a little bit harder to use. 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 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
drflac_open_with_metdata() returns. 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.
Build Options
=============
OPTIONS
=======
#define these options before including this file. #define these options before including this file.
#define DR_FLAC_NO_STDIO #define DR_FLAC_NO_STDIO
Disable drflac_open_file() and family. Disable `drflac_open_file()` and family.
#define DR_FLAC_NO_OGG #define DR_FLAC_NO_OGG
Disables support for Ogg/FLAC streams. Disables support for Ogg/FLAC streams.
#define DR_FLAC_BUFFER_SIZE <number> #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 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.
back to the client for more data. Larger values means more memory, but better performance. My tests show diminishing Larger values means more memory, but better performance. My tests show diminishing returns after about 4KB (which is the default). Consider reducing this if
returns after about 4KB (which is the default). Consider reducing this if you have a very efficient implementation of you have a very efficient implementation of onRead(), or increase it if it's very inefficient. Must be a multiple of 8.
onRead(), or increase it if it's very inefficient. Must be a multiple of 8.
#define DR_FLAC_NO_CRC #define DR_FLAC_NO_CRC
Disables CRC checks. This will offer a performance boost when CRC is unnecessary. This will disable binary search seeking. 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
When seeking, the seek table will be used if available. Otherwise the seek will be performed using brute force. be used if available. Otherwise the seek will be performed using brute force.
#define DR_FLAC_NO_SIMD #define DR_FLAC_NO_SIMD
Disables SIMD optimizations (SSE on x86/x64 architectures, NEON on ARM architectures). Use this if you are having Disables SIMD optimizations (SSE on x86/x64 architectures, NEON on ARM architectures). Use this if you are having compatibility issues with your compiler.
compatibility issues with your compiler.
QUICK NOTES Notes
=========== =====
- dr_flac does not currently support changing the sample rate nor channel count mid stream. - dr_flac does not support changing the sample rate nor channel count mid stream.
- This has not been tested on big-endian architectures.
- dr_flac is not thread-safe, but its APIs can be called from any thread so long as you do your own synchronization. - 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() - When using Ogg encapsulation, a corrupted metadata block will result in `drflac_open_with_metadata()` and `drflac_open()` returning inconsistent samples due
returning inconsistent samples. to differences in corrupted stream recorvery logic between the two APIs.
*/ */
#ifndef dr_flac_h #ifndef dr_flac_h
#define dr_flac_h #define dr_flac_h
#include <stddef.h> #ifdef __cplusplus
extern "C" {
#if defined(_MSC_VER) && _MSC_VER < 1600 #endif
typedef signed char drflac_int8;
typedef unsigned char drflac_uint8; #include <stddef.h> /* For size_t. */
typedef signed short drflac_int16;
typedef unsigned short drflac_uint16; /* Sized types. Prefer built-in types. Fall back to stdint. */
typedef signed int drflac_int32; #ifdef _MSC_VER
typedef unsigned int drflac_uint32; #if defined(__clang__)
typedef signed __int64 drflac_int64; #pragma GCC diagnostic push
typedef unsigned __int64 drflac_uint64; #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 #else
#include <stdint.h> #include <stdint.h>
typedef int8_t drflac_int8; typedef int8_t drflac_int8;
typedef uint8_t drflac_uint8; typedef uint8_t drflac_uint8;
typedef int16_t drflac_int16; typedef int16_t drflac_int16;
typedef uint16_t drflac_uint16; typedef uint16_t drflac_uint16;
typedef int32_t drflac_int32; typedef int32_t drflac_int32;
typedef uint32_t drflac_uint32; typedef uint32_t drflac_uint32;
typedef int64_t drflac_int64; typedef int64_t drflac_int64;
typedef uint64_t drflac_uint64; typedef uint64_t drflac_uint64;
#endif #endif
typedef drflac_uint8 drflac_bool8; typedef drflac_uint8 drflac_bool8;
typedef drflac_uint32 drflac_bool32; typedef drflac_uint32 drflac_bool32;
#define DRFLAC_TRUE 1 #define DRFLAC_TRUE 1
#define DRFLAC_FALSE 0 #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 */ #if defined(_MSC_VER) && _MSC_VER >= 1700 /* Visual Studio 2012 */
#define DRFLAC_DEPRECATED __declspec(deprecated) #define DRFLAC_DEPRECATED __declspec(deprecated)
...@@ -269,18 +312,13 @@ typedef drflac_uint32 drflac_bool32; ...@@ -269,18 +312,13 @@ typedef drflac_uint32 drflac_bool32;
#endif #endif
/* /*
As data is read from the client it is placed into an internal buffer for fast access. This controls the 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,
size of that buffer. Larger values means more speed, but also more memory. In my testing there is diminishing 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.
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 #ifndef DR_FLAC_BUFFER_SIZE
#define DR_FLAC_BUFFER_SIZE 4096 #define DR_FLAC_BUFFER_SIZE 4096
#endif #endif
#ifdef __cplusplus
extern "C" {
#endif
/* Check if we can enable 64-bit optimizations. */ /* Check if we can enable 64-bit optimizations. */
#if defined(_WIN64) || defined(_LP64) || defined(__LP64__) #if defined(_WIN64) || defined(_LP64) || defined(__LP64__)
#define DRFLAC_64BIT #define DRFLAC_64BIT
...@@ -436,40 +474,77 @@ typedef struct ...@@ -436,40 +474,77 @@ typedef struct
/* /*
Callback for when data needs to be read from the client. 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.
pBufferOut (out)
The output buffer.
bytesToRead (in)
The number of bytes to 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. 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); typedef size_t (* drflac_read_proc)(void* pUserData, void* pBufferOut, size_t bytesToRead);
/* /*
Callback for when data needs to be seeked. 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 Return Value
the FLAC stream. This needs to be detected and handled by returning DRFLAC_FALSE. ------------
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); typedef drflac_bool32 (* drflac_seek_proc)(void* pUserData, int offset, drflac_seek_origin origin);
/* /*
Callback for when a metadata block is read. 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. 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); typedef void (* drflac_meta_proc)(void* pUserData, drflac_metadata* pMetadata);
...@@ -676,135 +751,292 @@ typedef struct ...@@ -676,135 +751,292 @@ typedef struct
drflac_uint8 pExtraData[1]; drflac_uint8 pExtraData[1];
} drflac; } drflac;
/* /*
Opens a FLAC decoder. 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. 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()`.
This function will automatically detect whether or not you are attempting to open a native or Ogg encapsulated `pAllocationCallbacks` can be NULL in which case it will use `DRFLAC_MALLOC`, `DRFLAC_REALLOC` and `DRFLAC_FREE`.
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() 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
to open the stream from a file or from a block of memory respectively. without any manual intervention. Ogg encapsulation also works with multiplexed streams which basically means it can play FLAC encoded audio tracks in videos.
The STREAMINFO block must be present for this to succeed. Use drflac_open_relaxed() to open a FLAC stream where 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
the header may not be present. from a block of memory respectively.
See also: drflac_open_file(), drflac_open_memory(), drflac_open_with_metadata(), drflac_close() 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.
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. 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 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`
not set this to drflac_container_unknown - that is for internal use only. 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 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,
found it will continue forever. To abort, force your onRead callback to return 0, which dr_flac will use as an force your `onRead` callback to return 0, which dr_flac will use as an indicator that the end of the stream was found.
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.). 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 (in, optional)
A pointer to application defined callbacks for managing memory allocations.
Return Value
------------
A pointer to an object representing the decoder.
pAllocationCallbacks can be NULL in which case it will use DRFLAC_MALLOC, DRFLAC_REALLOC and DRFLAC_FREE.
This is slower than drflac_open(), so avoid this one if you don't need metadata. Internally, this will allocate and free Remarks
memory on the heap for every metadata block except for STREAMINFO and PADDING blocks. -------
Close the decoder with `drflac_close()`.
The caller is notified of the metadata via the onMeta callback. All metadata blocks will be handled before the function `pAllocationCallbacks` can be NULL in which case it will use `DRFLAC_MALLOC`, `DRFLAC_REALLOC` and `DRFLAC_FREE`.
returns.
The STREAMINFO block must be present for this to succeed. Use drflac_open_with_metadata_relaxed() to open a FLAC 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
stream where the header may not be present. 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 The caller is notified of the metadata via the `onMeta` callback. All metadata blocks will be handled before the function returns.
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.
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. 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. 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. 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. 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. 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.
Note that this should be considered lossy due to the nature of floating point numbers not being able to exactly
represent every possible number. 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 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. 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 +1044,145 @@ drflac_bool32 drflac_seek_to_pcm_frame(drflac* pFlac, drflac_uint64 pcmFrameInde ...@@ -812,43 +1044,145 @@ drflac_bool32 drflac_seek_to_pcm_frame(drflac* pFlac, drflac_uint64 pcmFrameInde
/* /*
Opens a FLAC decoder from the file at the given path. 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(). 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.) 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. 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 #endif
/* /*
Opens a FLAC decoder from a pre-allocated block of memory 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.) 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. 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 +1200,40 @@ read samples into a dynamically sized buffer on the heap until no samples are le ...@@ -866,40 +1200,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). 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. */ /* 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. */ /* 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 #ifndef DR_FLAC_NO_STDIO
/* Same as drflac_open_and_read_pcm_frames_s32() except opens the decoder from a file. */ /* 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. */ /* 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. */ /* 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 #endif
/* Same as drflac_open_and_read_pcm_frames_s32() except opens the decoder from a block of memory. */ /* 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. */ /* 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. */ /* 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. 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. 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. */ /* Structure representing an iterator for vorbis comments in a VORBIS_COMMENT metadata block. */
...@@ -913,13 +1247,13 @@ typedef struct ...@@ -913,13 +1247,13 @@ typedef struct
Initializes a vorbis comment iterator. This can be used for iterating over the vorbis comments in a VORBIS_COMMENT Initializes a vorbis comment iterator. This can be used for iterating over the vorbis comments in a VORBIS_COMMENT
metadata block. 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 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. 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. */ /* Structure representing an iterator for cuesheet tracks in a CUESHEET metadata block. */
...@@ -954,10 +1288,10 @@ typedef struct ...@@ -954,10 +1288,10 @@ typedef struct
Initializes a cuesheet track iterator. This can be used for iterating over the cuesheet tracks in a CUESHEET metadata Initializes a cuesheet track iterator. This can be used for iterating over the cuesheet tracks in a CUESHEET metadata
block. 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. */ /* 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 #ifdef __cplusplus
...@@ -973,7 +1307,7 @@ drflac_bool32 drflac_next_cuesheet_track(drflac_cuesheet_track_iterator* pIter, ...@@ -973,7 +1307,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. */ /* Disable some annoying warnings. */
#if defined(__GNUC__) #if defined(__GNUC__)
...@@ -1234,15 +1568,68 @@ static DRFLAC_INLINE drflac_bool32 drflac_has_sse41() ...@@ -1234,15 +1568,68 @@ static DRFLAC_INLINE drflac_bool32 drflac_has_sse41()
#ifndef DRFLAC_ZERO_MEMORY #ifndef DRFLAC_ZERO_MEMORY
#define DRFLAC_ZERO_MEMORY(p, sz) memset((p), 0, (sz)) #define DRFLAC_ZERO_MEMORY(p, sz) memset((p), 0, (sz))
#endif #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. */ #define DRFLAC_MAX_SIMD_VECTOR_SIZE 64 /* 64 for AVX-512 in the future. */
typedef drflac_int32 drflac_result; typedef drflac_int32 drflac_result;
#define DRFLAC_SUCCESS 0 #define DRFLAC_SUCCESS 0
#define DRFLAC_ERROR -1 /* A generic error. */ #define DRFLAC_ERROR -1 /* A generic error. */
#define DRFLAC_INVALID_ARGS -2 #define DRFLAC_INVALID_ARGS -2
#define DRFLAC_END_OF_STREAM -128 #define DRFLAC_INVALID_OPERATION -3
#define DRFLAC_CRC_MISMATCH -129 #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_CONSTANT 0
#define DRFLAC_SUBFRAME_VERBATIM 1 #define DRFLAC_SUBFRAME_VERBATIM 1
...@@ -1953,7 +2340,11 @@ static DRFLAC_INLINE drflac_bool32 drflac__read_uint32(drflac_bs* bs, unsigned i ...@@ -1953,7 +2340,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. */ /* 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 bitCountHi = DRFLAC_CACHE_L1_BITS_REMAINING(bs);
drflac_uint32 bitCountLo = bitCount - bitCountHi; 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)) { if (!drflac__reload_cache(bs)) {
return DRFLAC_FALSE; return DRFLAC_FALSE;
...@@ -2434,7 +2825,7 @@ static drflac_result drflac__read_utf8_coded_number(drflac_bs* bs, drflac_uint64 ...@@ -2434,7 +2825,7 @@ static drflac_result drflac__read_utf8_coded_number(drflac_bs* bs, drflac_uint64
if (!drflac__read_uint8(bs, 8, utf8)) { if (!drflac__read_uint8(bs, 8, utf8)) {
*pNumberOut = 0; *pNumberOut = 0;
return DRFLAC_END_OF_STREAM; return DRFLAC_AT_END;
} }
crc = drflac_crc8(crc, utf8[0], 8); crc = drflac_crc8(crc, utf8[0], 8);
...@@ -2469,7 +2860,7 @@ static drflac_result drflac__read_utf8_coded_number(drflac_bs* bs, drflac_uint64 ...@@ -2469,7 +2860,7 @@ static drflac_result drflac__read_utf8_coded_number(drflac_bs* bs, drflac_uint64
for (i = 1; i < byteCount; ++i) { for (i = 1; i < byteCount; ++i) {
if (!drflac__read_uint8(bs, 8, utf8 + i)) { if (!drflac__read_uint8(bs, 8, utf8 + i)) {
*pNumberOut = 0; *pNumberOut = 0;
return DRFLAC_END_OF_STREAM; return DRFLAC_AT_END;
} }
crc = drflac_crc8(crc, utf8[i], 8); crc = drflac_crc8(crc, utf8[i], 8);
...@@ -3351,7 +3742,7 @@ static drflac_bool32 drflac__decode_samples_with_residual__rice__sse41_32(drflac ...@@ -3351,7 +3742,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 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 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. so I think there's opportunity for this to be simplified.
*/ */
...@@ -4656,7 +5047,7 @@ static drflac_bool32 drflac__read_next_flac_frame_header(drflac_bs* bs, drflac_u ...@@ -4656,7 +5047,7 @@ static drflac_bool32 drflac__read_next_flac_frame_header(drflac_bs* bs, drflac_u
drflac_uint64 pcmFrameNumber; drflac_uint64 pcmFrameNumber;
drflac_result result = drflac__read_utf8_coded_number(bs, &pcmFrameNumber, &crc8); drflac_result result = drflac__read_utf8_coded_number(bs, &pcmFrameNumber, &crc8);
if (result != DRFLAC_SUCCESS) { if (result != DRFLAC_SUCCESS) {
if (result == DRFLAC_END_OF_STREAM) { if (result == DRFLAC_AT_END) {
return DRFLAC_FALSE; return DRFLAC_FALSE;
} else { } else {
continue; continue;
...@@ -4668,7 +5059,7 @@ static drflac_bool32 drflac__read_next_flac_frame_header(drflac_bs* bs, drflac_u ...@@ -4668,7 +5059,7 @@ static drflac_bool32 drflac__read_next_flac_frame_header(drflac_bs* bs, drflac_u
drflac_uint64 flacFrameNumber = 0; drflac_uint64 flacFrameNumber = 0;
drflac_result result = drflac__read_utf8_coded_number(bs, &flacFrameNumber, &crc8); drflac_result result = drflac__read_utf8_coded_number(bs, &flacFrameNumber, &crc8);
if (result != DRFLAC_SUCCESS) { if (result != DRFLAC_SUCCESS) {
if (result == DRFLAC_END_OF_STREAM) { if (result == DRFLAC_AT_END) {
return DRFLAC_FALSE; return DRFLAC_FALSE;
} else { } else {
continue; continue;
...@@ -4990,7 +5381,7 @@ static drflac_result drflac__decode_flac_frame(drflac* pFlac) ...@@ -4990,7 +5381,7 @@ static drflac_result drflac__decode_flac_frame(drflac* pFlac)
if (paddingSizeInBits > 0) { if (paddingSizeInBits > 0) {
drflac_uint8 padding = 0; drflac_uint8 padding = 0;
if (!drflac__read_uint8(&pFlac->bs, paddingSizeInBits, &padding)) { if (!drflac__read_uint8(&pFlac->bs, paddingSizeInBits, &padding)) {
return DRFLAC_END_OF_STREAM; return DRFLAC_AT_END;
} }
} }
...@@ -4998,7 +5389,7 @@ static drflac_result drflac__decode_flac_frame(drflac* pFlac) ...@@ -4998,7 +5389,7 @@ static drflac_result drflac__decode_flac_frame(drflac* pFlac)
actualCRC16 = drflac__flush_crc16(&pFlac->bs); actualCRC16 = drflac__flush_crc16(&pFlac->bs);
#endif #endif
if (!drflac__read_uint16(&pFlac->bs, 16, &desiredCRC16)) { if (!drflac__read_uint16(&pFlac->bs, 16, &desiredCRC16)) {
return DRFLAC_END_OF_STREAM; return DRFLAC_AT_END;
} }
#ifndef DR_FLAC_NO_CRC #ifndef DR_FLAC_NO_CRC
...@@ -5038,7 +5429,7 @@ static drflac_result drflac__seek_flac_frame(drflac* pFlac) ...@@ -5038,7 +5429,7 @@ static drflac_result drflac__seek_flac_frame(drflac* pFlac)
actualCRC16 = drflac__flush_crc16(&pFlac->bs); actualCRC16 = drflac__flush_crc16(&pFlac->bs);
#endif #endif
if (!drflac__read_uint16(&pFlac->bs, 16, &desiredCRC16)) { if (!drflac__read_uint16(&pFlac->bs, 16, &desiredCRC16)) {
return DRFLAC_END_OF_STREAM; return DRFLAC_AT_END;
} }
#ifndef DR_FLAC_NO_CRC #ifndef DR_FLAC_NO_CRC
...@@ -5121,7 +5512,7 @@ static DRFLAC_INLINE drflac_result drflac__seek_to_next_flac_frame(drflac* pFlac ...@@ -5121,7 +5512,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; drflac_uint64 pcmFramesRead = 0;
while (pcmFramesToSeek > 0) { while (pcmFramesToSeek > 0) {
...@@ -5296,7 +5687,7 @@ static drflac_bool32 drflac__seek_to_approximate_flac_frame_to_byte(drflac* pFla ...@@ -5296,7 +5687,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 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 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 1
if (!drflac__read_and_decode_next_flac_frame(pFlac)) { if (!drflac__read_and_decode_next_flac_frame(pFlac)) {
...@@ -5446,7 +5837,7 @@ static drflac_bool32 drflac__seek_to_pcm_frame__binary_search(drflac* pFlac, drf ...@@ -5446,7 +5837,7 @@ static drflac_bool32 drflac__seek_to_pcm_frame__binary_search(drflac* pFlac, drf
drflac_uint64 byteRangeHi; drflac_uint64 byteRangeHi;
drflac_uint32 seekForwardThreshold = (pFlac->maxBlockSizeInPCMFrames != 0) ? pFlac->maxBlockSizeInPCMFrames*2 : 4096; 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) { if (drflac__seek_to_first_frame(pFlac) == DRFLAC_FALSE) {
return DRFLAC_FALSE; return DRFLAC_FALSE;
} }
...@@ -5682,7 +6073,7 @@ static DRFLAC_INLINE drflac_bool32 drflac__read_and_decode_block_header(drflac_r ...@@ -5682,7 +6073,7 @@ static DRFLAC_INLINE drflac_bool32 drflac__read_and_decode_block_header(drflac_r
return DRFLAC_TRUE; 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_uint32 blockSizes;
drflac_uint64 frameSizes = 0; drflac_uint64 frameSizes = 0;
...@@ -5806,7 +6197,7 @@ static void drflac__free_from_callbacks(void* p, const drflac_allocation_callbac ...@@ -5806,7 +6197,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 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 +6558,7 @@ drflac_bool32 drflac__read_and_decode_metadata(drflac_read_proc onRead, drflac_s ...@@ -6167,7 +6558,7 @@ drflac_bool32 drflac__read_and_decode_metadata(drflac_read_proc onRead, drflac_s
return DRFLAC_TRUE; 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. */ /* Pre Condition: The bit stream should be sitting just past the 4-byte id header. */
...@@ -6377,7 +6768,7 @@ static DRFLAC_INLINE drflac_uint32 drflac_ogg__get_page_body_size(drflac_ogg_pag ...@@ -6377,7 +6768,7 @@ static DRFLAC_INLINE drflac_uint32 drflac_ogg__get_page_body_size(drflac_ogg_pag
return pageBodySize; 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_uint8 data[23];
drflac_uint32 i; drflac_uint32 i;
...@@ -6385,7 +6776,7 @@ drflac_result drflac_ogg__read_page_header_after_capture_pattern(drflac_read_pro ...@@ -6385,7 +6776,7 @@ drflac_result drflac_ogg__read_page_header_after_capture_pattern(drflac_read_pro
DRFLAC_ASSERT(*pCRC32 == DRFLAC_OGG_CAPTURE_PATTERN_CRC32); DRFLAC_ASSERT(*pCRC32 == DRFLAC_OGG_CAPTURE_PATTERN_CRC32);
if (onRead(pUserData, data, 23) != 23) { if (onRead(pUserData, data, 23) != 23) {
return DRFLAC_END_OF_STREAM; return DRFLAC_AT_END;
} }
*pBytesRead += 23; *pBytesRead += 23;
...@@ -6419,7 +6810,7 @@ drflac_result drflac_ogg__read_page_header_after_capture_pattern(drflac_read_pro ...@@ -6419,7 +6810,7 @@ drflac_result drflac_ogg__read_page_header_after_capture_pattern(drflac_read_pro
if (onRead(pUserData, pHeader->segmentTable, pHeader->segmentCount) != pHeader->segmentCount) { if (onRead(pUserData, pHeader->segmentTable, pHeader->segmentCount) != pHeader->segmentCount) {
return DRFLAC_END_OF_STREAM; return DRFLAC_AT_END;
} }
*pBytesRead += pHeader->segmentCount; *pBytesRead += pHeader->segmentCount;
...@@ -6430,14 +6821,14 @@ drflac_result drflac_ogg__read_page_header_after_capture_pattern(drflac_read_pro ...@@ -6430,14 +6821,14 @@ drflac_result drflac_ogg__read_page_header_after_capture_pattern(drflac_read_pro
return DRFLAC_SUCCESS; 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]; drflac_uint8 id[4];
*pBytesRead = 0; *pBytesRead = 0;
if (onRead(pUserData, id, 4) != 4) { if (onRead(pUserData, id, 4) != 4) {
return DRFLAC_END_OF_STREAM; return DRFLAC_AT_END;
} }
*pBytesRead += 4; *pBytesRead += 4;
...@@ -6464,7 +6855,7 @@ drflac_result drflac_ogg__read_page_header(drflac_read_proc onRead, void* pUserD ...@@ -6464,7 +6855,7 @@ drflac_result drflac_ogg__read_page_header(drflac_read_proc onRead, void* pUserD
id[1] = id[2]; id[1] = id[2];
id[2] = id[3]; id[2] = id[3];
if (onRead(pUserData, &id[3], 1) != 1) { if (onRead(pUserData, &id[3], 1) != 1) {
return DRFLAC_END_OF_STREAM; return DRFLAC_AT_END;
} }
*pBytesRead += 1; *pBytesRead += 1;
} }
...@@ -6767,7 +7158,7 @@ static drflac_bool32 drflac__on_seek_ogg(void* pUserData, int offset, drflac_see ...@@ -6767,7 +7158,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_oggbs* oggbs = (drflac_oggbs*)pFlac->_oggbs;
drflac_uint64 originalBytePos; drflac_uint64 originalBytePos;
...@@ -6924,7 +7315,7 @@ drflac_bool32 drflac_ogg__seek_to_pcm_frame(drflac* pFlac, drflac_uint64 pcmFram ...@@ -6924,7 +7315,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_ogg_page_header header;
drflac_uint32 crc32 = DRFLAC_OGG_CAPTURE_PATTERN_CRC32; drflac_uint32 crc32 = DRFLAC_OGG_CAPTURE_PATTERN_CRC32;
...@@ -7082,7 +7473,7 @@ drflac_bool32 drflac__init_private__ogg(drflac_init_info* pInit, drflac_read_pro ...@@ -7082,7 +7473,7 @@ drflac_bool32 drflac__init_private__ogg(drflac_init_info* pInit, drflac_read_pro
} }
#endif #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_bool32 relaxed;
drflac_uint8 id[4]; drflac_uint8 id[4];
...@@ -7167,7 +7558,7 @@ drflac_bool32 drflac__init_private(drflac_init_info* pInit, drflac_read_proc onR ...@@ -7167,7 +7558,7 @@ drflac_bool32 drflac__init_private(drflac_init_info* pInit, drflac_read_proc onR
return DRFLAC_FALSE; 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(pFlac != NULL);
DRFLAC_ASSERT(pInit != NULL); DRFLAC_ASSERT(pInit != NULL);
...@@ -7185,7 +7576,7 @@ void drflac__init_from_info(drflac* pFlac, const drflac_init_info* pInit) ...@@ -7185,7 +7576,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_init_info init;
drflac_uint32 allocationSize; drflac_uint32 allocationSize;
...@@ -7399,6 +7790,552 @@ drflac* drflac_open_with_metadata_private(drflac_read_proc onRead, drflac_seek_p ...@@ -7399,6 +7790,552 @@ drflac* drflac_open_with_metadata_private(drflac_read_proc onRead, drflac_seek_p
#ifndef DR_FLAC_NO_STDIO #ifndef DR_FLAC_NO_STDIO
#include <stdio.h> #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) static size_t drflac__on_read_stdio(void* pUserData, void* bufferOut, size_t bytesToRead)
{ {
...@@ -7412,31 +8349,31 @@ static drflac_bool32 drflac__on_seek_stdio(void* pUserData, int offset, drflac_s ...@@ -7412,31 +8349,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; 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; 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; return NULL;
} }
#else
pFile = fopen(filename, "rb"); pFlac = drflac_open(drflac__on_read_stdio, drflac__on_seek_stdio, (void*)pFile, pAllocationCallbacks);
if (pFile == NULL) { if (pFlac == NULL) {
fclose(pFile);
return NULL; return NULL;
} }
#endif
return pFile; return pFlac;
} }
DRFLAC_API drflac* drflac_open_file_w(const wchar_t* pFileName, const drflac_allocation_callbacks* pAllocationCallbacks)
drflac* drflac_open_file(const char* filename, const drflac_allocation_callbacks* pAllocationCallbacks)
{ {
drflac* pFlac; drflac* pFlac;
FILE* pFile; FILE* pFile;
pFile = drflac__fopen(filename); if (drflac_wfopen(&pFile, pFileName, L"rb", pAllocationCallbacks) != DRFLAC_SUCCESS) {
if (pFile == NULL) {
return NULL; return NULL;
} }
...@@ -7449,13 +8386,30 @@ drflac* drflac_open_file(const char* filename, const drflac_allocation_callbacks ...@@ -7449,13 +8386,30 @@ drflac* drflac_open_file(const char* filename, const drflac_allocation_callbacks
return pFlac; 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; drflac* pFlac;
FILE* pFile; FILE* pFile;
pFile = drflac__fopen(filename); if (drflac_fopen(&pFile, pFileName, "rb") != DRFLAC_SUCCESS) {
if (pFile == NULL) { 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;
if (drflac_wfopen(&pFile, pFileName, L"rb", pAllocationCallbacks) != DRFLAC_SUCCESS) {
return NULL; return NULL;
} }
...@@ -7518,12 +8472,12 @@ static drflac_bool32 drflac__on_seek_memory(void* pUserData, int offset, drflac_ ...@@ -7518,12 +8472,12 @@ static drflac_bool32 drflac__on_seek_memory(void* pUserData, int offset, drflac_
return DRFLAC_TRUE; 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__memory_stream memoryStream;
drflac* pFlac; drflac* pFlac;
memoryStream.data = (const unsigned char*)data; memoryStream.data = (const unsigned char*)pData;
memoryStream.dataSize = dataSize; memoryStream.dataSize = dataSize;
memoryStream.currentReadPos = 0; memoryStream.currentReadPos = 0;
pFlac = drflac_open(drflac__on_read_memory, drflac__on_seek_memory, &memoryStream, pAllocationCallbacks); pFlac = drflac_open(drflac__on_read_memory, drflac__on_seek_memory, &memoryStream, pAllocationCallbacks);
...@@ -7549,12 +8503,12 @@ drflac* drflac_open_memory(const void* data, size_t dataSize, const drflac_alloc ...@@ -7549,12 +8503,12 @@ drflac* drflac_open_memory(const void* data, size_t dataSize, const drflac_alloc
return pFlac; 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__memory_stream memoryStream;
drflac* pFlac; drflac* pFlac;
memoryStream.data = (const unsigned char*)data; memoryStream.data = (const unsigned char*)pData;
memoryStream.dataSize = dataSize; memoryStream.dataSize = dataSize;
memoryStream.currentReadPos = 0; memoryStream.currentReadPos = 0;
pFlac = drflac_open_with_metadata_private(drflac__on_read_memory, drflac__on_seek_memory, onMeta, drflac_container_unknown, &memoryStream, pUserData, pAllocationCallbacks); pFlac = drflac_open_with_metadata_private(drflac__on_read_memory, drflac__on_seek_memory, onMeta, drflac_container_unknown, &memoryStream, pUserData, pAllocationCallbacks);
...@@ -7582,25 +8536,25 @@ drflac* drflac_open_memory_with_metadata(const void* data, size_t dataSize, drfl ...@@ -7582,25 +8536,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); 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); 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); 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); 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) { if (pFlac == NULL) {
return; return;
...@@ -8370,7 +9324,7 @@ static DRFLAC_INLINE void drflac_read_pcm_frames_s32__decode_independent_stereo( ...@@ -8370,7 +9324,7 @@ 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_uint64 framesRead;
drflac_int32 unusedBitsPerSample; drflac_int32 unusedBitsPerSample;
...@@ -9286,7 +10240,7 @@ static DRFLAC_INLINE void drflac_read_pcm_frames_s16__decode_independent_stereo( ...@@ -9286,7 +10240,7 @@ 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_uint64 framesRead;
drflac_int32 unusedBitsPerSample; drflac_int32 unusedBitsPerSample;
...@@ -10177,7 +11131,7 @@ static DRFLAC_INLINE void drflac_read_pcm_frames_f32__decode_independent_stereo( ...@@ -10177,7 +11131,7 @@ 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_uint64 framesRead;
drflac_int32 unusedBitsPerSample; drflac_int32 unusedBitsPerSample;
...@@ -10241,7 +11195,8 @@ drflac_uint64 drflac_read_pcm_frames_f32(drflac* pFlac, drflac_uint64 framesToRe ...@@ -10241,7 +11195,8 @@ drflac_uint64 drflac_read_pcm_frames_f32(drflac* pFlac, drflac_uint64 framesToRe
for (i = 0; i < frameCountThisIteration; ++i) { for (i = 0; i < frameCountThisIteration; ++i) {
unsigned int j; unsigned int j;
for (j = 0; j < channelCount; ++j) { for (j = 0; j < channelCount; ++j) {
pBufferOut[(i*channelCount)+j] = (float)((drflac_int64)((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 +11213,7 @@ drflac_uint64 drflac_read_pcm_frames_f32(drflac* pFlac, drflac_uint64 framesToRe ...@@ -10258,7 +11213,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) { if (pFlac == NULL) {
return DRFLAC_FALSE; return DRFLAC_FALSE;
...@@ -10433,7 +11388,7 @@ DRFLAC_DEFINE_FULL_READ_AND_CLOSE(s32, drflac_int32) ...@@ -10433,7 +11388,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(s16, drflac_int16)
DRFLAC_DEFINE_FULL_READ_AND_CLOSE(f32, float) 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; drflac* pFlac;
...@@ -10455,7 +11410,7 @@ drflac_int32* drflac_open_and_read_pcm_frames_s32(drflac_read_proc onRead, drfla ...@@ -10455,7 +11410,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); 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; drflac* pFlac;
...@@ -10477,7 +11432,7 @@ drflac_int16* drflac_open_and_read_pcm_frames_s16(drflac_read_proc onRead, drfla ...@@ -10477,7 +11432,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); 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; drflac* pFlac;
...@@ -10500,7 +11455,7 @@ float* drflac_open_and_read_pcm_frames_f32(drflac_read_proc onRead, drflac_seek_ ...@@ -10500,7 +11455,7 @@ float* drflac_open_and_read_pcm_frames_f32(drflac_read_proc onRead, drflac_seek_
} }
#ifndef DR_FLAC_NO_STDIO #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; drflac* pFlac;
...@@ -10522,7 +11477,7 @@ drflac_int32* drflac_open_file_and_read_pcm_frames_s32(const char* filename, uns ...@@ -10522,7 +11477,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); 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; drflac* pFlac;
...@@ -10544,7 +11499,7 @@ drflac_int16* drflac_open_file_and_read_pcm_frames_s16(const char* filename, uns ...@@ -10544,7 +11499,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); 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; drflac* pFlac;
...@@ -10567,7 +11522,7 @@ float* drflac_open_file_and_read_pcm_frames_f32(const char* filename, unsigned i ...@@ -10567,7 +11522,7 @@ float* drflac_open_file_and_read_pcm_frames_f32(const char* filename, unsigned i
} }
#endif #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; drflac* pFlac;
...@@ -10589,7 +11544,7 @@ drflac_int32* drflac_open_memory_and_read_pcm_frames_s32(const void* data, size_ ...@@ -10589,7 +11544,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); 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; drflac* pFlac;
...@@ -10611,7 +11566,7 @@ drflac_int16* drflac_open_memory_and_read_pcm_frames_s16(const void* data, size_ ...@@ -10611,7 +11566,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); 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; drflac* pFlac;
...@@ -10634,7 +11589,7 @@ float* drflac_open_memory_and_read_pcm_frames_f32(const void* data, size_t dataS ...@@ -10634,7 +11589,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) { if (pAllocationCallbacks != NULL) {
drflac__free_from_callbacks(p, pAllocationCallbacks); drflac__free_from_callbacks(p, pAllocationCallbacks);
...@@ -10646,7 +11601,7 @@ void drflac_free(void* p, const drflac_allocation_callbacks* pAllocationCallback ...@@ -10646,7 +11601,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) { if (pIter == NULL) {
return; return;
...@@ -10656,7 +11611,7 @@ void drflac_init_vorbis_comment_iterator(drflac_vorbis_comment_iterator* pIter, ...@@ -10656,7 +11611,7 @@ void drflac_init_vorbis_comment_iterator(drflac_vorbis_comment_iterator* pIter,
pIter->pRunningData = (const char*)pComments; 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; drflac_int32 length;
const char* pComment; const char* pComment;
...@@ -10687,7 +11642,7 @@ const char* drflac_next_vorbis_comment(drflac_vorbis_comment_iterator* pIter, dr ...@@ -10687,7 +11642,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) { if (pIter == NULL) {
return; return;
...@@ -10697,7 +11652,7 @@ void drflac_init_cuesheet_track_iterator(drflac_cuesheet_track_iterator* pIter, ...@@ -10697,7 +11652,7 @@ void drflac_init_cuesheet_track_iterator(drflac_cuesheet_track_iterator* pIter,
pIter->pRunningData = (const char*)pTrackData; 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; drflac_cuesheet_track cuesheetTrack;
const char* pRunningData; const char* pRunningData;
...@@ -10739,6 +11694,11 @@ drflac_bool32 drflac_next_cuesheet_track(drflac_cuesheet_track_iterator* pIter, ...@@ -10739,6 +11694,11 @@ drflac_bool32 drflac_next_cuesheet_track(drflac_cuesheet_track_iterator* pIter,
/* /*
REVISION HISTORY REVISION HISTORY
================ ================
v0.12.8 - 2020-04-04
- Add drflac_open_file_w() drflac_open_file_with_metadata_w().
- Fix some static analysis warnings.
- Minor documentation updates.
v0.12.7 - 2020-03-14 v0.12.7 - 2020-03-14
- Fix compilation errors with VC6. - Fix compilation errors with VC6.
......
/* /*
MP3 audio decoder. Choice of public domain or MIT-0. See license statements at the end of this file. 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.0 - 2020-04-04
David Reid - mackron@gmail.com 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 GitHub: https://github.com/mackron/dr_libs
differences between minimp3 and dr_mp3.
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 RELEASE NOTES - VERSION 0.6
======================= ===========================
Version 0.5.0 has breaking API changes. 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
Improved Client-Defined Memory Allocation yourself.
-----------------------------------------
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: 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()`
drmp3_init_file() `drmp3_init_memory()`
drmp3_init_memory() `drmp3_init_file()`
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() Miscellaneous Changes
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() Support for loading a file from a `wchar_t` string has been added via the `drmp3_init_file_w()` API.
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()
*/ */
/* /*
USAGE Introducation
===== =============
dr_mp3 is a single-file library. To use it, do something like the following in one .c file. dr_mp3 is a single file library. To use it, do something like the following in one .c file.
```c
#define DR_MP3_IMPLEMENTATION #define DR_MP3_IMPLEMENTATION
#include "dr_mp3.h" #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, 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:
do something like the following:
```c
drmp3 mp3; drmp3 mp3;
if (!drmp3_init_file(&mp3, "MySong.mp3", NULL)) { if (!drmp3_init_file(&mp3, "MySong.mp3", NULL)) {
// Failed to open file // Failed to open file
...@@ -91,28 +53,27 @@ do something like the following: ...@@ -91,28 +53,27 @@ do something like the following:
... ...
drmp3_uint64 framesRead = drmp3_read_pcm_frames_f32(pMP3, framesToRead, pFrames); 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: 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 channels = mp3.channels;
drmp3_uint32 sampleRate = mp3.sampleRate; 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 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
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. `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 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
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 call to `drmp3_read_pcm_frames_f32()` and it will return as many PCM frames as it can, up to the requested amount.
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 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(). `drmp3_open_file_and_read_pcm_frames_f32()`.
OPTIONS Build Options
======= =============
#define these options before including this file. #define these options before including this file.
#define DR_MP3_NO_STDIO #define DR_MP3_NO_STDIO
...@@ -129,32 +90,129 @@ OPTIONS ...@@ -129,32 +90,129 @@ OPTIONS
extern "C" { extern "C" {
#endif #endif
#include <stddef.h> #include <stddef.h> /* For size_t. */
#if defined(_MSC_VER) && _MSC_VER < 1600 /* Sized types. Prefer built-in types. Fall back to stdint. */
typedef signed char drmp3_int8; #ifdef _MSC_VER
typedef unsigned char drmp3_uint8; #if defined(__clang__)
typedef signed short drmp3_int16; #pragma GCC diagnostic push
typedef unsigned short drmp3_uint16; #pragma GCC diagnostic ignored "-Wlanguage-extension-token"
typedef signed int drmp3_int32; #pragma GCC diagnostic ignored "-Wlong-long"
typedef unsigned int drmp3_uint32; #pragma GCC diagnostic ignored "-Wc++11-long-long"
typedef signed __int64 drmp3_int64; #endif
typedef unsigned __int64 drmp3_uint64; 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 #else
#include <stdint.h> #include <stdint.h>
typedef int8_t drmp3_int8; typedef int8_t drmp3_int8;
typedef uint8_t drmp3_uint8; typedef uint8_t drmp3_uint8;
typedef int16_t drmp3_int16; typedef int16_t drmp3_int16;
typedef uint16_t drmp3_uint16; typedef uint16_t drmp3_uint16;
typedef int32_t drmp3_int32; typedef int32_t drmp3_int32;
typedef uint32_t drmp3_uint32; typedef uint32_t drmp3_uint32;
typedef int64_t drmp3_int64; typedef int64_t drmp3_int64;
typedef uint64_t drmp3_uint64; typedef uint64_t drmp3_uint64;
#endif #endif
typedef drmp3_uint8 drmp3_bool8; typedef drmp3_uint8 drmp3_bool8;
typedef drmp3_uint32 drmp3_bool32; typedef drmp3_uint32 drmp3_bool32;
#define DRMP3_TRUE 1 #define DRMP3_TRUE 1
#define DRMP3_FALSE 0 #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_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_PCM_FRAMES_PER_MP3_FRAME 1152
#define DRMP3_MAX_SAMPLES_PER_FRAME (DRMP3_MAX_PCM_FRAMES_PER_MP3_FRAME*2) #define DRMP3_MAX_SAMPLES_PER_FRAME (DRMP3_MAX_PCM_FRAMES_PER_MP3_FRAME*2)
...@@ -195,13 +253,13 @@ typedef struct ...@@ -195,13 +253,13 @@ typedef struct
} drmp3dec; } drmp3dec;
/* Initializes a low level decoder. */ /* 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. */ /* 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 unsigned char *mp3, int mp3_bytes, void *pcm, drmp3dec_frame_info *info);
/* Helper for converting between f32 and s16. */ /* 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 +267,13 @@ void drmp3dec_f32_to_s16(const float *in, drmp3_int16 *out, int num_samples); ...@@ -209,57 +267,13 @@ void drmp3dec_f32_to_s16(const float *in, drmp3_int16 *out, int num_samples);
Main API (Pull API) Main API (Pull API)
=================== ===================
*/ */
#ifndef DR_MP3_DEFAULT_CHANNELS #ifndef DRMP3_DEFAULT_CHANNELS
#define DR_MP3_DEFAULT_CHANNELS 2 #define DRMP3_DEFAULT_CHANNELS 2
#endif #endif
#ifndef DR_MP3_DEFAULT_SAMPLE_RATE #ifndef DRMP3_DEFAULT_SAMPLE_RATE
#define DR_MP3_DEFAULT_SAMPLE_RATE 44100 #define DRMP3_DEFAULT_SAMPLE_RATE 44100
#endif #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 typedef enum
{ {
...@@ -313,8 +327,8 @@ typedef struct ...@@ -313,8 +327,8 @@ typedef struct
typedef struct typedef struct
{ {
drmp3_uint32 outputChannels; drmp3_uint32 channels;
drmp3_uint32 outputSampleRate; drmp3_uint32 sampleRate;
} drmp3_config; } drmp3_config;
typedef struct typedef struct
...@@ -334,7 +348,6 @@ typedef struct ...@@ -334,7 +348,6 @@ 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_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 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_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_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. */ 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 dataSize;
...@@ -362,7 +375,7 @@ Close the loader with drmp3_uninit(). ...@@ -362,7 +375,7 @@ Close the loader with drmp3_uninit().
See also: drmp3_init_file(), drmp3_init_memory(), 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. Initializes an MP3 decoder from a block of memory.
...@@ -372,7 +385,7 @@ the lifetime of the drmp3 object. ...@@ -372,7 +385,7 @@ the lifetime of the drmp3 object.
The buffer should contain the contents of the entire MP3 file. 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 #ifndef DR_MP3_NO_STDIO
/* /*
...@@ -382,46 +395,47 @@ This holds the internal FILE object until drmp3_uninit() is called. Keep this in ...@@ -382,46 +395,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 objects because the operating system may restrict the number of file handles an application can have open at
any given time. 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 #endif
/* /*
Uninitializes an MP3 decoder. 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. 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. 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. 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. 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. Seeks to a specific frame.
Note that this is _not_ an MP3 frame, but rather a PCM 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 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. 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 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. 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 Calculates the total number of MP3 and PCM frames in the MP3 stream. Cannot be used for infinite streams such as internet
...@@ -429,7 +443,7 @@ radio. Runs in linear time. Returns 0 on error. ...@@ -429,7 +443,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. 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. Calculates the seekpoints based on PCM frames. This is slow.
...@@ -440,7 +454,7 @@ seekpoints, in which case dr_mp3 will return a corrected count. ...@@ -440,7 +454,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. 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. Binds a seek table to the decoder.
...@@ -450,31 +464,31 @@ remains valid while it is bound to the decoder. ...@@ -450,31 +464,31 @@ remains valid while it is bound to the decoder.
Use drmp3_calculate_seek_points() to calculate the seek points. 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. 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(). 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_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_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);
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_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);
#ifndef DR_MP3_NO_STDIO #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_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);
#endif #endif
/* /*
Frees any memory that was allocated by a public drmp3 API. 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 #ifdef __cplusplus
} }
...@@ -489,7 +503,7 @@ void drmp3_free(void* p, const drmp3_allocation_callbacks* pAllocationCallbacks) ...@@ -489,7 +503,7 @@ 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 <stdlib.h>
#include <string.h> #include <string.h>
#include <limits.h> /* For INT_MAX */ #include <limits.h> /* For INT_MAX */
...@@ -2140,12 +2154,12 @@ static int drmp3d_find_frame(const drmp3_uint8 *mp3, int mp3_bytes, int *free_fo ...@@ -2140,12 +2154,12 @@ static int drmp3d_find_frame(const drmp3_uint8 *mp3, int mp3_bytes, int *free_fo
return mp3_bytes; return mp3_bytes;
} }
void drmp3dec_init(drmp3dec *dec) DRMP3_API void drmp3dec_init(drmp3dec *dec)
{ {
dec->header[0] = 0; 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 unsigned char *mp3, int mp3_bytes, void *pcm, drmp3dec_frame_info *info)
{ {
int i = 0, igr, frame_size = 0, success = 1; int i = 0, igr, frame_size = 0, success = 1;
const drmp3_uint8 *hdr; const drmp3_uint8 *hdr;
...@@ -2240,11 +2254,11 @@ int drmp3dec_decode_frame(drmp3dec *dec, const unsigned char *mp3, int mp3_bytes ...@@ -2240,11 +2254,11 @@ int drmp3dec_decode_frame(drmp3dec *dec, const unsigned char *mp3, int mp3_bytes
return success*drmp3_hdr_frame_samples(dec->header); 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 #if DRMP3_HAVE_SIMD
int aligned_count = num_samples & ~7; size_t aligned_count = num_samples & ~7;
for(; i < aligned_count; i+=8) for(; i < aligned_count; i+=8)
{ {
drmp3_f4 scale = DRMP3_VSET(32768.0f); drmp3_f4 scale = DRMP3_VSET(32768.0f);
...@@ -2303,6 +2317,7 @@ void drmp3dec_f32_to_s16(const float *in, drmp3_int16 *out, int num_samples) ...@@ -2303,6 +2317,7 @@ void drmp3dec_f32_to_s16(const float *in, drmp3_int16 *out, int num_samples)
Main Public API Main Public API
************************************************************************************************************************************************************/ ************************************************************************************************************************************************************/
#include <math.h> /* For sin() and exp(). */
#if defined(SIZE_MAX) #if defined(SIZE_MAX)
#define DRMP3_SIZE_MAX SIZE_MAX #define DRMP3_SIZE_MAX SIZE_MAX
...@@ -2342,23 +2357,64 @@ void drmp3dec_f32_to_s16(const float *in, drmp3_int16 *out, int num_samples) ...@@ -2342,23 +2357,64 @@ void drmp3dec_f32_to_s16(const float *in, drmp3_int16 *out, int num_samples)
#define DRMP3_FREE(p) free((p)) #define DRMP3_FREE(p) free((p))
#endif #endif
#define drmp3_countof(x) (sizeof(x) / sizeof(x[0])) #define DRMP3_COUNTOF(x) (sizeof(x) / sizeof(x[0]))
#define drmp3_max(x, y) (((x) > (y)) ? (x) : (y)) #define DRMP3_CLAMP(x, lo, hi) (DRMP3_MAX(lo, DRMP3_MIN(x, hi)))
#define drmp3_min(x, y) (((x) < (y)) ? (x) : (y))
#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_DATA_CHUNK_SIZE 16384 /* The size in bytes of each chunk of data to read from the MP3 stream. minimp3 recommends 16K. */
#ifndef DRMP3_PI_D
#define DRMP3_PI_D 3.14159265358979323846264
#endif
#define DRMP3_DEFAULT_RESAMPLER_LPF_ORDER 2
static DRMP3_INLINE float drmp3_mix_f32(float x, float y, float a) static DRMP3_INLINE float drmp3_mix_f32(float x, float y, float a)
{ {
return x*(1-a) + y*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 (;;) {
for (i = 0; i < channels; ++i) { if (b == 0) {
pOut[i] = drmp3_mix_f32(pInA[i], pInB[i], factor); 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 +2437,8 @@ static void drmp3__free_default(void* p, void* pUserData) ...@@ -2381,7 +2437,8 @@ static void drmp3__free_default(void* p, void* pUserData)
} }
#if 0 /* Unused, but leaving here in case I need to add it again later. */ /* Only used without DR_MP3_NO_STDIO. */
#ifndef DR_MP3_NO_STDIO
static void* drmp3__malloc_from_callbacks(size_t sz, const drmp3_allocation_callbacks* pAllocationCallbacks) static void* drmp3__malloc_from_callbacks(size_t sz, const drmp3_allocation_callbacks* pAllocationCallbacks)
{ {
if (pAllocationCallbacks == NULL) { if (pAllocationCallbacks == NULL) {
...@@ -2443,7 +2500,7 @@ static void drmp3__free_from_callbacks(void* p, const drmp3_allocation_callbacks ...@@ -2443,7 +2500,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) { if (pAllocationCallbacks != NULL) {
/* Copy. */ /* Copy. */
...@@ -2460,259 +2517,6 @@ drmp3_allocation_callbacks drmp3_copy_allocation_callbacks_or_defaults(const drm ...@@ -2460,259 +2517,6 @@ 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)
{
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;
}
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)
{
if (pSRC == NULL) {
return DRMP3_FALSE;
}
DRMP3_ZERO_OBJECT(pSRC);
if (pConfig == NULL || onRead == NULL) {
return DRMP3_FALSE;
}
if (pConfig->channels == 0 || pConfig->channels > 2) {
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;
}
drmp3_src_cache_init(pSRC, &pSRC->cache);
return DRMP3_TRUE;
}
drmp3_bool32 drmp3_src_set_input_sample_rate(drmp3_src* pSRC, drmp3_uint32 sampleRateIn)
{
if (pSRC == NULL) {
return DRMP3_FALSE;
}
/* Must have a sample rate of > 0. */
if (sampleRateIn == 0) {
return DRMP3_FALSE;
}
pSRC->config.sampleRateIn = sampleRateIn;
return DRMP3_TRUE;
}
drmp3_bool32 drmp3_src_set_output_sample_rate(drmp3_src* pSRC, drmp3_uint32 sampleRateOut)
{
if (pSRC == NULL) {
return DRMP3_FALSE;
}
/* Must have a sample rate of > 0. */
if (sampleRateOut == 0) {
return DRMP3_FALSE;
}
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)
{
drmp3_src_algorithm algorithm;
if (pSRC == NULL || frameCount == 0 || pFramesOut == NULL) {
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;
}
/* 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;
}
}
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);
}
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);
(void)flush; /* Passthrough need not care about flushing. */
return pSRC->onRead(pSRC, frameCount, pFramesOut, pSRC->pUserData);
}
drmp3_uint64 drmp3_src_read_frames_linear(drmp3_src* pSRC, drmp3_uint64 frameCount, void* pFramesOut, drmp3_bool32 flush)
{
double factor;
drmp3_uint64 totalFramesRead;
DRMP3_ASSERT(pSRC != NULL);
DRMP3_ASSERT(frameCount > 0);
DRMP3_ASSERT(pFramesOut != NULL);
/* For linear SRC, the bin is only 2 frames: 1 prior, 1 future. */
/* 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;
}
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;
}
pSRC->algo.linear.isNextFramesLoaded = DRMP3_TRUE;
}
factor = (double)pSRC->config.sampleRateIn / pSRC->config.sampleRateOut;
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) static size_t drmp3__on_read(drmp3* pMP3, void* pBufferOut, size_t bytesToRead)
{ {
...@@ -2768,121 +2572,17 @@ static drmp3_bool32 drmp3__on_seek_64(drmp3* pMP3, drmp3_uint64 offset, drmp3_se ...@@ -2768,121 +2572,17 @@ static drmp3_bool32 drmp3__on_seek_64(drmp3* pMP3, drmp3_uint64 offset, drmp3_se
return DRMP3_TRUE; 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) static drmp3_uint32 drmp3_decode_next_frame_ex(drmp3* pMP3, drmp3d_sample_t* pPCMFrames, drmp3_bool32 discard)
{ {
drmp3* pMP3 = (drmp3*)pUserData; drmp3_uint32 pcmFramesRead = 0;
float* pFramesOutF = (float*)pFramesOut;
drmp3_uint64 totalFramesRead = 0;
DRMP3_ASSERT(pMP3 != NULL); DRMP3_ASSERT(pMP3 != NULL);
DRMP3_ASSERT(pMP3->onRead != NULL); DRMP3_ASSERT(pMP3->onRead != NULL);
while (frameCount > 0) { if (pMP3->atEnd) {
/* Read from the in-memory buffer first. */ return 0;
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 { do {
drmp3dec_frame_info info; drmp3dec_frame_info info;
...@@ -2924,7 +2624,7 @@ static drmp3_uint32 drmp3_decode_next_frame_ex(drmp3* pMP3, drmp3d_sample_t* pPC ...@@ -2924,7 +2624,7 @@ static drmp3_uint32 drmp3_decode_next_frame_ex(drmp3* pMP3, drmp3d_sample_t* pPC
} }
pcmFramesRead = drmp3dec_decode_frame(&pMP3->decoder, pMP3->pData, (int)pMP3->dataSize, pPCMFrames, &info); /* <-- Safe size_t -> int conversion thanks to the check above. */ 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. */ /* Consume the data. */
leftoverDataSize = (pMP3->dataSize - (size_t)info.frame_bytes); leftoverDataSize = (pMP3->dataSize - (size_t)info.frame_bytes);
if (info.frame_bytes > 0) { if (info.frame_bytes > 0) {
...@@ -2942,19 +2642,6 @@ static drmp3_uint32 drmp3_decode_next_frame_ex(drmp3* pMP3, drmp3d_sample_t* pPC ...@@ -2942,19 +2642,6 @@ static drmp3_uint32 drmp3_decode_next_frame_ex(drmp3* pMP3, drmp3d_sample_t* pPC
pMP3->pcmFramesRemainingInMP3Frame = pcmFramesRead; pMP3->pcmFramesRemainingInMP3Frame = pcmFramesRead;
pMP3->mp3FrameChannels = info.channels; pMP3->mp3FrameChannels = info.channels;
pMP3->mp3FrameSampleRate = info.hz; 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; break;
} else if (info.frame_bytes == 0) { } else if (info.frame_bytes == 0) {
size_t bytesRead; size_t bytesRead;
...@@ -3017,32 +2704,14 @@ static drmp3_uint32 drmp3_seek_next_frame(drmp3* pMP3) ...@@ -3017,32 +2704,14 @@ static drmp3_uint32 drmp3_seek_next_frame(drmp3* pMP3)
} }
#endif #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(pMP3 != NULL);
DRMP3_ASSERT(onRead != 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. */ /* 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); 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->onRead = onRead;
pMP3->onSeek = onSeek; pMP3->onSeek = onSeek;
pMP3->pUserData = pUserData; pMP3->pUserData = pUserData;
...@@ -3052,31 +2721,26 @@ drmp3_bool32 drmp3_init_internal(drmp3* pMP3, drmp3_read_proc onRead, drmp3_seek ...@@ -3052,31 +2721,26 @@ drmp3_bool32 drmp3_init_internal(drmp3* pMP3, drmp3_read_proc onRead, drmp3_seek
return DRMP3_FALSE; /* Invalid allocation callbacks. */ 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. */ /* Decode the first frame to confirm that it is indeed a valid MP3 stream. */
if (!drmp3_decode_next_frame(pMP3)) { if (!drmp3_decode_next_frame(pMP3)) {
drmp3_uninit(pMP3); drmp3_uninit(pMP3);
return DRMP3_FALSE; /* Not a valid MP3 stream. */ return DRMP3_FALSE; /* Not a valid MP3 stream. */
} }
pMP3->channels = pMP3->mp3FrameChannels;
pMP3->sampleRate = pMP3->mp3FrameSampleRate;
return DRMP3_TRUE; 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) { if (pMP3 == NULL || onRead == NULL) {
return DRMP3_FALSE; return DRMP3_FALSE;
} }
DRMP3_ZERO_OBJECT(pMP3); DRMP3_ZERO_OBJECT(pMP3);
return drmp3_init_internal(pMP3, onRead, onSeek, pUserData, pConfig, pAllocationCallbacks); return drmp3_init_internal(pMP3, onRead, onSeek, pUserData, pAllocationCallbacks);
} }
...@@ -3131,7 +2795,7 @@ static drmp3_bool32 drmp3__on_seek_memory(void* pUserData, int byteOffset, drmp3 ...@@ -3131,7 +2795,7 @@ static drmp3_bool32 drmp3__on_seek_memory(void* pUserData, int byteOffset, drmp3
return DRMP3_TRUE; return DRMP3_TRUE;
} }
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)
{ {
if (pMP3 == NULL) { if (pMP3 == NULL) {
return DRMP3_FALSE; return DRMP3_FALSE;
...@@ -3147,12 +2811,560 @@ drmp3_bool32 drmp3_init_memory(drmp3* pMP3, const void* pData, size_t dataSize, ...@@ -3147,12 +2811,560 @@ drmp3_bool32 drmp3_init_memory(drmp3* pMP3, const void* pData, size_t dataSize,
pMP3->memory.dataSize = dataSize; pMP3->memory.dataSize = dataSize;
pMP3->memory.currentReadPos = 0; pMP3->memory.currentReadPos = 0;
return drmp3_init_internal(pMP3, drmp3__on_read_memory, drmp3__on_seek_memory, pMP3, pConfig, pAllocationCallbacks); return drmp3_init_internal(pMP3, drmp3__on_read_memory, drmp3__on_seek_memory, pMP3, pAllocationCallbacks);
} }
#ifndef DR_MP3_NO_STDIO #ifndef DR_MP3_NO_STDIO
#include <stdio.h> #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 DRMP3_SUCCESS;
}
/*
_wfopen() isn't always available in all compilation environments.
* Windows only.
* MSVC seems to support it universally as far back as VC6 from what I can tell (haven't checked further back).
* MinGW-64 (both 32- and 64-bit) seems to support it.
* MinGW wraps it in !defined(__STRICT_ANSI__).
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)
{
if (ppFile != NULL) {
*ppFile = NULL; /* Safety. */
}
if (pFilePath == NULL || pOpenMode == NULL || ppFile == NULL) {
return DRMP3_INVALID_ARGS;
}
#if defined(DRMP3_HAS_WFOPEN)
{
/* Use _wfopen() on Windows. */
#if defined(_MSC_VER) && _MSC_VER >= 1400
errno_t err = _wfopen_s(ppFile, pFilePath, pOpenMode);
if (err != 0) {
return drmp3_result_from_errno(err);
}
#else
*ppFile = _wfopen(pFilePath, pOpenMode);
if (*ppFile == NULL) {
return drmp3_result_from_errno(errno);
}
#endif
(void)pAllocationCallbacks;
}
#else
/*
Use fopen() on anything other than Windows. Requires a conversion. This is annoying because fopen() is locale specific. The only real way I can
think of to do this is with wcsrtombs(). Note that wcstombs() is apparently not thread-safe because it uses a static global mbstate_t object for
maintaining state. I've checked this with -std=c89 and it works, but if somebody get's a compiler error I'll look into improving compatibility.
*/
{
mbstate_t mbs;
size_t lenMB;
const wchar_t* pFilePathTemp = pFilePath;
char* pFilePathMB = NULL;
char pOpenModeMB[32] = {0};
/* Get the length first. */
DRMP3_ZERO_OBJECT(&mbs);
lenMB = wcsrtombs(NULL, &pFilePathTemp, 0, &mbs);
if (lenMB == (size_t)-1) {
return drmp3_result_from_errno(errno);
}
pFilePathMB = (char*)drmp3__malloc_from_callbacks(lenMB + 1, pAllocationCallbacks);
if (pFilePathMB == NULL) {
return DRMP3_OUT_OF_MEMORY;
}
pFilePathTemp = pFilePath;
DRMP3_ZERO_OBJECT(&mbs);
wcsrtombs(pFilePathMB, &pFilePathTemp, lenMB + 1, &mbs);
/* The open mode should always consist of ASCII characters so we should be able to do a trivial conversion. */
{
size_t i = 0;
for (;;) {
if (pOpenMode[i] == 0) {
pOpenModeMB[i] = '\0';
break;
}
pOpenModeMB[i] = (char)pOpenMode[i];
i += 1;
}
}
*ppFile = fopen(pFilePathMB, pOpenModeMB);
drmp3__free_from_callbacks(pFilePathMB, pAllocationCallbacks);
}
if (*ppFile == NULL) {
return DRMP3_ERROR;
}
#endif
return DRMP3_SUCCESS;
}
static size_t drmp3__on_read_stdio(void* pUserData, void* pBufferOut, size_t bytesToRead) static size_t drmp3__on_read_stdio(void* pUserData, void* pBufferOut, size_t bytesToRead)
{ {
...@@ -3164,25 +3376,28 @@ static drmp3_bool32 drmp3__on_seek_stdio(void* pUserData, int offset, drmp3_seek ...@@ -3164,25 +3376,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; 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; FILE* pFile;
#if defined(_MSC_VER) && _MSC_VER >= 1400 if (drmp3_fopen(&pFile, pFilePath, "rb") != DRMP3_SUCCESS) {
if (fopen_s(&pFile, filePath, "rb") != 0) {
return DRMP3_FALSE; return DRMP3_FALSE;
} }
#else
pFile = fopen(filePath, "rb"); return drmp3_init(pMP3, drmp3__on_read_stdio, drmp3__on_seek_stdio, (void*)pFile, pAllocationCallbacks);
if (pFile == NULL) { }
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; 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 #endif
void drmp3_uninit(drmp3* pMP3) DRMP3_API void drmp3_uninit(drmp3* pMP3)
{ {
if (pMP3 == NULL) { if (pMP3 == NULL) {
return; return;
...@@ -3197,75 +3412,186 @@ void drmp3_uninit(drmp3* pMP3) ...@@ -3197,75 +3412,186 @@ void drmp3_uninit(drmp3* pMP3)
drmp3__free_from_callbacks(pMP3->pData, &pMP3->allocationCallbacks); 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_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->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) { if (pMP3 == NULL || pMP3->onRead == NULL) {
return 0; return 0;
} }
if (pBufferOut == NULL) { #if defined(DR_MP3_FLOAT_OUTPUT)
float temp[4096]; /* Fast path. No conversion required. */
while (framesToRead > 0) { 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 framesJustRead;
drmp3_uint64 framesToReadRightNow = sizeof(temp)/sizeof(temp[0]) / pMP3->channels; drmp3_uint64 framesRemaining = framesToRead - totalPCMFramesRead;
if (framesToReadRightNow > framesToRead) { drmp3_uint64 framesToReadNow = DRMP3_COUNTOF(pTempS16) / pMP3->channels;
framesToReadRightNow = framesToRead; 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) { if (framesJustRead == 0) {
break; break;
} }
framesToRead -= framesJustRead; drmp3_s16_to_f32((float*)DRMP3_OFFSET_PTR(pBufferOut, sizeof(drmp3_int16) * totalPCMFramesRead * pMP3->channels), pTempS16, framesJustRead * pMP3->channels);
totalFramesRead += framesJustRead; 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) { if (pMP3 == NULL || pMP3->onRead == NULL) {
return 0; return 0;
} }
/* Naive implementation: read into a temp f32 buffer, then convert. */ #if !defined(DR_MP3_FLOAT_OUTPUT)
for (;;) { /* Fast path. No conversion required. */
drmp3_uint64 pcmFramesToReadThisIteration = (framesToRead - totalPCMFramesRead); return drmp3_read_pcm_frames_raw(pMP3, framesToRead, pBufferOut);
if (pcmFramesToReadThisIteration > drmp3_countof(tempF32)/pMP3->channels) { #else
pcmFramesToReadThisIteration = drmp3_countof(tempF32)/pMP3->channels; /* Slow path. Convert from f32 to s16. */
} {
float pTempF32[4096];
pcmFramesJustRead = drmp3_read_pcm_frames_f32(pMP3, pcmFramesToReadThisIteration, tempF32); drmp3_uint64 totalPCMFramesRead = 0;
if (pcmFramesJustRead == 0) {
break;
}
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. */ while (totalPCMFramesRead < framesToRead) {
pBufferOut += pcmFramesJustRead * pMP3->channels; 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) { drmp3_f32_to_s16((drmp3_int16*)DRMP3_OFFSET_PTR(pBufferOut, sizeof(drmp3_int16) * totalPCMFramesRead * pMP3->channels), pTempF32, framesJustRead * pMP3->channels);
break; totalPCMFramesRead += framesJustRead;
} }
}
return totalPCMFramesRead; return totalPCMFramesRead;
}
#endif
} }
void drmp3_reset(drmp3* pMP3) static void drmp3_reset(drmp3* pMP3)
{ {
DRMP3_ASSERT(pMP3 != NULL); DRMP3_ASSERT(pMP3 != NULL);
...@@ -3274,19 +3600,10 @@ void drmp3_reset(drmp3* pMP3) ...@@ -3274,19 +3600,10 @@ void drmp3_reset(drmp3* pMP3)
pMP3->currentPCMFrame = 0; pMP3->currentPCMFrame = 0;
pMP3->dataSize = 0; pMP3->dataSize = 0;
pMP3->atEnd = DRMP3_FALSE; 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); 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 != NULL);
DRMP3_ASSERT(pMP3->onSeek != NULL); DRMP3_ASSERT(pMP3->onSeek != NULL);
...@@ -3301,78 +3618,29 @@ drmp3_bool32 drmp3_seek_to_start_of_stream(drmp3* pMP3) ...@@ -3301,78 +3618,29 @@ drmp3_bool32 drmp3_seek_to_start_of_stream(drmp3* pMP3)
return DRMP3_TRUE; 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) static drmp3_bool32 drmp3_seek_forward_by_pcm_frames__brute_force(drmp3* pMP3, drmp3_uint64 frameOffset)
{
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)
{ {
drmp3_uint64 framesRead; 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 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
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 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
contains the sample because that MP3 frame will need the data from the previous MP3 frame (which we just seeked past!). To kind of manual processing.
resolve this we seek past a number of MP3 frames up to a point, and then read-and-discard the remainder.
*/ */
drmp3_uint64 maxFramesToReadAndDiscard = (drmp3_uint64)(DRMP3_MAX_PCM_FRAMES_PER_MP3_FRAME * 3 * ((float)pMP3->src.config.sampleRateOut / (float)pMP3->src.config.sampleRateIn)); #if defined(DR_MP3_FLOAT_OUTPUT)
/* 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. */
framesRead = drmp3_read_pcm_frames_f32(pMP3, frameOffset, NULL); framesRead = drmp3_read_pcm_frames_f32(pMP3, frameOffset, NULL);
if (framesRead != frameOffset) {
return DRMP3_FALSE;
}
#else #else
/* Just using a dumb read-and-discard for now pending updates to the resampler. */ framesRead = drmp3_read_pcm_frames_s16(pMP3, frameOffset, NULL);
framesRead = drmp3_read_pcm_frames_f32(pMP3, frameOffset, NULL); #endif
if (framesRead != frameOffset) { if (framesRead != frameOffset) {
return DRMP3_FALSE; return DRMP3_FALSE;
} }
#endif
return DRMP3_TRUE; 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); DRMP3_ASSERT(pMP3 != NULL);
...@@ -3395,7 +3663,7 @@ drmp3_bool32 drmp3_seek_to_pcm_frame__brute_force(drmp3* pMP3, drmp3_uint64 fram ...@@ -3395,7 +3663,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)); 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; drmp3_uint32 iSeekPoint;
...@@ -3419,7 +3687,7 @@ drmp3_bool32 drmp3_find_closest_seek_point(drmp3* pMP3, drmp3_uint64 frameIndex, ...@@ -3419,7 +3687,7 @@ drmp3_bool32 drmp3_find_closest_seek_point(drmp3* pMP3, drmp3_uint64 frameIndex,
return DRMP3_TRUE; 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_seek_point seekPoint;
drmp3_uint32 priorSeekPointIndex; drmp3_uint32 priorSeekPointIndex;
...@@ -3450,7 +3718,7 @@ drmp3_bool32 drmp3_seek_to_pcm_frame__seek_table(drmp3* pMP3, drmp3_uint64 frame ...@@ -3450,7 +3718,7 @@ drmp3_bool32 drmp3_seek_to_pcm_frame__seek_table(drmp3* pMP3, drmp3_uint64 frame
/* Whole MP3 frames need to be discarded first. */ /* Whole MP3 frames need to be discarded first. */
for (iMP3Frame = 0; iMP3Frame < seekPoint.mp3FramesToDiscard; ++iMP3Frame) { for (iMP3Frame = 0; iMP3Frame < seekPoint.mp3FramesToDiscard; ++iMP3Frame) {
drmp3_uint32 pcmFramesReadPreSRC; drmp3_uint32 pcmFramesRead;
drmp3d_sample_t* pPCMFrames; drmp3d_sample_t* pPCMFrames;
/* Pass in non-null for the last frame because we want to ensure the sample rate converter is preloaded correctly. */ /* Pass in non-null for the last frame because we want to ensure the sample rate converter is preloaded correctly. */
...@@ -3460,8 +3728,8 @@ drmp3_bool32 drmp3_seek_to_pcm_frame__seek_table(drmp3* pMP3, drmp3_uint64 frame ...@@ -3460,8 +3728,8 @@ drmp3_bool32 drmp3_seek_to_pcm_frame__seek_table(drmp3* pMP3, drmp3_uint64 frame
} }
/* We first need to decode the next frame, and then we need to flush the resampler. */ /* 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); pcmFramesRead = drmp3_decode_next_frame_ex(pMP3, pPCMFrames, DRMP3_TRUE);
if (pcmFramesReadPreSRC == 0) { if (pcmFramesRead == 0) {
return DRMP3_FALSE; return DRMP3_FALSE;
} }
} }
...@@ -3469,17 +3737,6 @@ drmp3_bool32 drmp3_seek_to_pcm_frame__seek_table(drmp3* pMP3, drmp3_uint64 frame ...@@ -3469,17 +3737,6 @@ drmp3_bool32 drmp3_seek_to_pcm_frame__seek_table(drmp3* pMP3, drmp3_uint64 frame
/* We seeked to an MP3 frame in the raw stream so we need to make sure the current PCM frame is set correctly. */ /* We seeked to an MP3 frame in the raw stream so we need to make sure the current PCM frame is set correctly. */
pMP3->currentPCMFrame = seekPoint.pcmFrameIndex - seekPoint.pcmFramesToDiscard; 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 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. read-and-discard at least 2 whole MP3 frames.
...@@ -3488,7 +3745,7 @@ drmp3_bool32 drmp3_seek_to_pcm_frame__seek_table(drmp3* pMP3, drmp3_uint64 frame ...@@ -3488,7 +3745,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); 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) { if (pMP3 == NULL || pMP3->onSeek == NULL) {
return DRMP3_FALSE; return DRMP3_FALSE;
...@@ -3506,7 +3763,7 @@ drmp3_bool32 drmp3_seek_to_pcm_frame(drmp3* pMP3, drmp3_uint64 frameIndex) ...@@ -3506,7 +3763,7 @@ 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 currentPCMFrame;
drmp3_uint64 totalPCMFrameCount; drmp3_uint64 totalPCMFrameCount;
...@@ -3578,7 +3835,7 @@ drmp3_bool32 drmp3_get_mp3_and_pcm_frame_count(drmp3* pMP3, drmp3_uint64* pMP3Fr ...@@ -3578,7 +3835,7 @@ drmp3_bool32 drmp3_get_mp3_and_pcm_frame_count(drmp3* pMP3, drmp3_uint64* pMP3Fr
return DRMP3_TRUE; 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; drmp3_uint64 totalPCMFrameCount;
if (!drmp3_get_mp3_and_pcm_frame_count(pMP3, NULL, &totalPCMFrameCount)) { if (!drmp3_get_mp3_and_pcm_frame_count(pMP3, NULL, &totalPCMFrameCount)) {
...@@ -3588,7 +3845,7 @@ drmp3_uint64 drmp3_get_pcm_frame_count(drmp3* pMP3) ...@@ -3588,7 +3845,7 @@ drmp3_uint64 drmp3_get_pcm_frame_count(drmp3* pMP3)
return totalPCMFrameCount; 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; drmp3_uint64 totalMP3FrameCount;
if (!drmp3_get_mp3_and_pcm_frame_count(pMP3, &totalMP3FrameCount, NULL)) { if (!drmp3_get_mp3_and_pcm_frame_count(pMP3, &totalMP3FrameCount, NULL)) {
...@@ -3598,7 +3855,7 @@ drmp3_uint64 drmp3_get_mp3_frame_count(drmp3* pMP3) ...@@ -3598,7 +3855,7 @@ drmp3_uint64 drmp3_get_mp3_frame_count(drmp3* pMP3)
return totalMP3FrameCount; 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 srcRatio;
float pcmFrameCountOutF; float pcmFrameCountOutF;
...@@ -3619,7 +3876,7 @@ typedef struct ...@@ -3619,7 +3876,7 @@ typedef struct
drmp3_uint64 pcmFrameIndex; /* <-- After sample rate conversion. */ drmp3_uint64 pcmFrameIndex; /* <-- After sample rate conversion. */
} drmp3__seeking_mp3_frame_info; } 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_uint32 seekPointCount;
drmp3_uint64 currentPCMFrame; drmp3_uint64 currentPCMFrame;
...@@ -3720,13 +3977,13 @@ drmp3_bool32 drmp3_calculate_seek_points(drmp3* pMP3, drmp3_uint32* pSeekPointCo ...@@ -3720,13 +3977,13 @@ 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 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. 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]; mp3FrameInfo[i] = mp3FrameInfo[i+1];
} }
/* Cache previous MP3 frame info. */ /* Cache previous MP3 frame info. */
mp3FrameInfo[drmp3_countof(mp3FrameInfo)-1].bytePos = pMP3->streamCursor - pMP3->dataSize; mp3FrameInfo[DRMP3_COUNTOF(mp3FrameInfo)-1].bytePos = pMP3->streamCursor - pMP3->dataSize;
mp3FrameInfo[drmp3_countof(mp3FrameInfo)-1].pcmFrameIndex = runningPCMFrameCount; 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 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
...@@ -3759,7 +4016,7 @@ drmp3_bool32 drmp3_calculate_seek_points(drmp3* pMP3, drmp3_uint32* pSeekPointCo ...@@ -3759,7 +4016,7 @@ drmp3_bool32 drmp3_calculate_seek_points(drmp3* pMP3, drmp3_uint32* pSeekPointCo
return DRMP3_TRUE; 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) { if (pMP3 == NULL) {
return DRMP3_FALSE; return DRMP3_FALSE;
...@@ -3779,7 +4036,7 @@ drmp3_bool32 drmp3_bind_seek_table(drmp3* pMP3, drmp3_uint32 seekPointCount, drm ...@@ -3779,7 +4036,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 totalFramesRead = 0;
drmp3_uint64 framesCapacity = 0; drmp3_uint64 framesCapacity = 0;
...@@ -3788,8 +4045,11 @@ float* drmp3__full_read_and_close_f32(drmp3* pMP3, drmp3_config* pConfig, drmp3_ ...@@ -3788,8 +4045,11 @@ float* drmp3__full_read_and_close_f32(drmp3* pMP3, drmp3_config* pConfig, drmp3_
DRMP3_ASSERT(pMP3 != NULL); DRMP3_ASSERT(pMP3 != NULL);
pConfig->channels = pMP3->channels;
pConfig->sampleRate = pMP3->sampleRate;
for (;;) { 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); drmp3_uint64 framesJustRead = drmp3_read_pcm_frames_f32(pMP3, framesToReadRightNow, temp);
if (framesJustRead == 0) { if (framesJustRead == 0) {
break; break;
...@@ -3833,8 +4093,8 @@ float* drmp3__full_read_and_close_f32(drmp3* pMP3, drmp3_config* pConfig, drmp3_ ...@@ -3833,8 +4093,8 @@ float* drmp3__full_read_and_close_f32(drmp3* pMP3, drmp3_config* pConfig, drmp3_
} }
if (pConfig != NULL) { if (pConfig != NULL) {
pConfig->outputChannels = pMP3->channels; pConfig->channels = pMP3->channels;
pConfig->outputSampleRate = pMP3->sampleRate; pConfig->sampleRate = pMP3->sampleRate;
} }
drmp3_uninit(pMP3); drmp3_uninit(pMP3);
...@@ -3846,7 +4106,7 @@ float* drmp3__full_read_and_close_f32(drmp3* pMP3, drmp3_config* pConfig, drmp3_ ...@@ -3846,7 +4106,7 @@ float* drmp3__full_read_and_close_f32(drmp3* pMP3, drmp3_config* pConfig, drmp3_
return pFrames; 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 totalFramesRead = 0;
drmp3_uint64 framesCapacity = 0; drmp3_uint64 framesCapacity = 0;
...@@ -3855,8 +4115,11 @@ drmp3_int16* drmp3__full_read_and_close_s16(drmp3* pMP3, drmp3_config* pConfig, ...@@ -3855,8 +4115,11 @@ drmp3_int16* drmp3__full_read_and_close_s16(drmp3* pMP3, drmp3_config* pConfig,
DRMP3_ASSERT(pMP3 != NULL); DRMP3_ASSERT(pMP3 != NULL);
pConfig->channels = pMP3->channels;
pConfig->sampleRate = pMP3->sampleRate;
for (;;) { 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); drmp3_uint64 framesJustRead = drmp3_read_pcm_frames_s16(pMP3, framesToReadRightNow, temp);
if (framesJustRead == 0) { if (framesJustRead == 0) {
break; break;
...@@ -3900,8 +4163,8 @@ drmp3_int16* drmp3__full_read_and_close_s16(drmp3* pMP3, drmp3_config* pConfig, ...@@ -3900,8 +4163,8 @@ drmp3_int16* drmp3__full_read_and_close_s16(drmp3* pMP3, drmp3_config* pConfig,
} }
if (pConfig != NULL) { if (pConfig != NULL) {
pConfig->outputChannels = pMP3->channels; pConfig->channels = pMP3->channels;
pConfig->outputSampleRate = pMP3->sampleRate; pConfig->sampleRate = pMP3->sampleRate;
} }
drmp3_uninit(pMP3); drmp3_uninit(pMP3);
...@@ -3914,20 +4177,20 @@ drmp3_int16* drmp3__full_read_and_close_s16(drmp3* pMP3, drmp3_config* pConfig, ...@@ -3914,20 +4177,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; drmp3 mp3;
if (!drmp3_init(&mp3, onRead, onSeek, pUserData, pConfig, pAllocationCallbacks)) { if (!drmp3_init(&mp3, onRead, onSeek, pUserData, pAllocationCallbacks)) {
return NULL; return NULL;
} }
return drmp3__full_read_and_close_f32(&mp3, pConfig, pTotalFrameCount); 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; drmp3 mp3;
if (!drmp3_init(&mp3, onRead, onSeek, pUserData, pConfig, pAllocationCallbacks)) { if (!drmp3_init(&mp3, onRead, onSeek, pUserData, pAllocationCallbacks)) {
return NULL; return NULL;
} }
...@@ -3935,20 +4198,20 @@ drmp3_int16* drmp3_open_and_read_pcm_frames_s16(drmp3_read_proc onRead, drmp3_se ...@@ -3935,20 +4198,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; drmp3 mp3;
if (!drmp3_init_memory(&mp3, pData, dataSize, pConfig, pAllocationCallbacks)) { if (!drmp3_init_memory(&mp3, pData, dataSize, pAllocationCallbacks)) {
return NULL; return NULL;
} }
return drmp3__full_read_and_close_f32(&mp3, pConfig, pTotalFrameCount); 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; drmp3 mp3;
if (!drmp3_init_memory(&mp3, pData, dataSize, pConfig, pAllocationCallbacks)) { if (!drmp3_init_memory(&mp3, pData, dataSize, pAllocationCallbacks)) {
return NULL; return NULL;
} }
...@@ -3957,20 +4220,20 @@ drmp3_int16* drmp3_open_memory_and_read_pcm_frames_s16(const void* pData, size_t ...@@ -3957,20 +4220,20 @@ drmp3_int16* drmp3_open_memory_and_read_pcm_frames_s16(const void* pData, size_t
#ifndef DR_MP3_NO_STDIO #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; drmp3 mp3;
if (!drmp3_init_file(&mp3, filePath, pConfig, pAllocationCallbacks)) { if (!drmp3_init_file(&mp3, filePath, pAllocationCallbacks)) {
return NULL; return NULL;
} }
return drmp3__full_read_and_close_f32(&mp3, pConfig, pTotalFrameCount); 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; drmp3 mp3;
if (!drmp3_init_file(&mp3, filePath, pConfig, pAllocationCallbacks)) { if (!drmp3_init_file(&mp3, filePath, pAllocationCallbacks)) {
return NULL; return NULL;
} }
...@@ -3978,7 +4241,7 @@ drmp3_int16* drmp3_open_file_and_read_pcm_frames_s16(const char* filePath, drmp3 ...@@ -3978,7 +4241,7 @@ drmp3_int16* drmp3_open_file_and_read_pcm_frames_s16(const char* filePath, drmp3
} }
#endif #endif
void drmp3_free(void* p, const drmp3_allocation_callbacks* pAllocationCallbacks) DRMP3_API void drmp3_free(void* p, const drmp3_allocation_callbacks* pAllocationCallbacks)
{ {
if (pAllocationCallbacks != NULL) { if (pAllocationCallbacks != NULL) {
drmp3__free_from_callbacks(p, pAllocationCallbacks); drmp3__free_from_callbacks(p, pAllocationCallbacks);
...@@ -4004,9 +4267,81 @@ DIFFERENCES BETWEEN minimp3 AND dr_mp3 ...@@ -4004,9 +4267,81 @@ DIFFERENCES BETWEEN minimp3 AND dr_mp3
using minimp3 in conjunction with stb_vorbis. dr_mp3 addresses this. using minimp3 in conjunction with stb_vorbis. dr_mp3 addresses this.
*/ */
/*
RELEASE NOTES - v0.5.0
=======================
Version 0.5.0 has breaking API changes.
Improved Client-Defined Memory Allocation
-----------------------------------------
The main change with this release is the addition of a more flexible way of implementing custom memory allocation routines. The
existing system of DRMP3_MALLOC, DRMP3_REALLOC and DRMP3_FREE are still in place and will be used by default when no custom
allocation callbacks are specified.
To use the new system, you pass in a pointer to a drmp3_allocation_callbacks object to drmp3_init() and family, like this:
void* my_malloc(size_t sz, void* pUserData)
{
return malloc(sz);
}
void* my_realloc(void* p, size_t sz, void* pUserData)
{
return realloc(p, sz);
}
void my_free(void* p, void* pUserData)
{
free(p);
}
...
drmp3_allocation_callbacks allocationCallbacks;
allocationCallbacks.pUserData = &myData;
allocationCallbacks.onMalloc = my_malloc;
allocationCallbacks.onRealloc = my_realloc;
allocationCallbacks.onFree = my_free;
drmp3_init_file(&mp3, "my_file.mp3", NULL, &allocationCallbacks);
The advantage of this new system is that it allows you to specify user data which will be passed in to the allocation routines.
Passing in null for the allocation callbacks object will cause dr_mp3 to use defaults which is the same as DRMP3_MALLOC,
DRMP3_REALLOC and DRMP3_FREE and the equivalent of how it worked in previous versions.
Every API that opens a drmp3 object now takes this extra parameter. These include the following:
drmp3_init()
drmp3_init_file()
drmp3_init_memory()
drmp3_open_and_read_pcm_frames_f32()
drmp3_open_and_read_pcm_frames_s16()
drmp3_open_memory_and_read_pcm_frames_f32()
drmp3_open_memory_and_read_pcm_frames_s16()
drmp3_open_file_and_read_pcm_frames_f32()
drmp3_open_file_and_read_pcm_frames_s16()
Renamed APIs
------------
The following APIs have been renamed for consistency with other dr_* libraries and to make it clear that they return PCM frame
counts rather than sample counts.
drmp3_open_and_read_f32() -> drmp3_open_and_read_pcm_frames_f32()
drmp3_open_and_read_s16() -> drmp3_open_and_read_pcm_frames_s16()
drmp3_open_memory_and_read_f32() -> drmp3_open_memory_and_read_pcm_frames_f32()
drmp3_open_memory_and_read_s16() -> drmp3_open_memory_and_read_pcm_frames_s16()
drmp3_open_file_and_read_f32() -> drmp3_open_file_and_read_pcm_frames_f32()
drmp3_open_file_and_read_s16() -> drmp3_open_file_and_read_pcm_frames_s16()
*/
/* /*
REVISION HISTORY REVISION HISTORY
================ ================
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 v0.5.6 - 2020-02-12
- Bring up to date with minimp3. - Bring up to date with minimp3.
...@@ -4058,9 +4393,9 @@ v0.4.4 - 2019-05-06 ...@@ -4058,9 +4393,9 @@ v0.4.4 - 2019-05-06
- Fixes to the VC6 build. - Fixes to the VC6 build.
v0.4.3 - 2019-05-05 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 - Use the channel count and/or sample rate of the first MP3 frame instead of DRMP3_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 DRMP3_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. DRMP3_DEFAULT_CHANNELS or DRMP3_DEFAULT_SAMPLE_RATE.
- Add s16 reading APIs - Add s16 reading APIs
- drmp3_read_pcm_frames_s16 - drmp3_read_pcm_frames_s16
- drmp3_open_memory_and_read_pcm_frames_s16 - drmp3_open_memory_and_read_pcm_frames_s16
......
This source diff could not be displayed because it is too large. You can view the blob instead.
...@@ -39870,12 +39870,17 @@ static ma_uint64 ma_decoder_internal_on_read_pcm_frames__mp3(ma_decoder* pDecode ...@@ -39870,12 +39870,17 @@ static ma_uint64 ma_decoder_internal_on_read_pcm_frames__mp3(ma_decoder* pDecode
MA_ASSERT(pDecoder != NULL); MA_ASSERT(pDecoder != NULL);
MA_ASSERT(pFramesOut != NULL); MA_ASSERT(pFramesOut != NULL);
MA_ASSERT(pDecoder->internalFormat == ma_format_f32);
pMP3 = (drmp3*)pDecoder->pInternalDecoder; pMP3 = (drmp3*)pDecoder->pInternalDecoder;
MA_ASSERT(pMP3 != NULL); MA_ASSERT(pMP3 != NULL);
#if defined(DR_MP3_FLOAT_OUTPUT)
MA_ASSERT(pDecoder->internalFormat == ma_format_f32);
return drmp3_read_pcm_frames_f32(pMP3, frameCount, (float*)pFramesOut); return drmp3_read_pcm_frames_f32(pMP3, frameCount, (float*)pFramesOut);
#else
MA_ASSERT(pDecoder->internalFormat == ma_format_s16);
return drmp3_read_pcm_frames_s16(pMP3, frameCount, (drmp3_int16*)pFramesOut);
#endif
} }
static ma_result ma_decoder_internal_on_seek_to_pcm_frame__mp3(ma_decoder* pDecoder, ma_uint64 frameIndex) static ma_result ma_decoder_internal_on_seek_to_pcm_frame__mp3(ma_decoder* pDecoder, ma_uint64 frameIndex)
...@@ -39909,7 +39914,6 @@ static ma_uint64 ma_decoder_internal_on_get_length_in_pcm_frames__mp3(ma_decoder ...@@ -39909,7 +39914,6 @@ static ma_uint64 ma_decoder_internal_on_get_length_in_pcm_frames__mp3(ma_decoder
static ma_result ma_decoder_init_mp3__internal(const ma_decoder_config* pConfig, ma_decoder* pDecoder) static ma_result ma_decoder_init_mp3__internal(const ma_decoder_config* pConfig, ma_decoder* pDecoder)
{ {
drmp3* pMP3; drmp3* pMP3;
drmp3_config mp3Config;
drmp3_allocation_callbacks allocationCallbacks; drmp3_allocation_callbacks allocationCallbacks;
MA_ASSERT(pConfig != NULL); MA_ASSERT(pConfig != NULL);
...@@ -39926,20 +39930,10 @@ static ma_result ma_decoder_init_mp3__internal(const ma_decoder_config* pConfig, ...@@ -39926,20 +39930,10 @@ static ma_result ma_decoder_init_mp3__internal(const ma_decoder_config* pConfig,
allocationCallbacks.onFree = pDecoder->allocationCallbacks.onFree; allocationCallbacks.onFree = pDecoder->allocationCallbacks.onFree;
/* /*
Try opening the decoder first. MP3 can have variable sample rates (it's per frame/packet). We therefore need Try opening the decoder first. We always use whatever dr_mp3 reports for channel count and sample rate. The format is determined by
to use some smarts to determine the most appropriate internal sample rate. These are the rules we're going the presence of DR_MP3_FLOAT_OUTPUT.
to use:
Sample Rates
1) If an output sample rate is specified in pConfig we just use that. Otherwise;
2) Fall back to 44100.
The internal channel count is always stereo, and the internal format is always f32.
*/ */
MA_ZERO_OBJECT(&mp3Config); if (!drmp3_init(pMP3, ma_decoder_internal_on_read__mp3, ma_decoder_internal_on_seek__mp3, pDecoder, &allocationCallbacks)) {
mp3Config.outputChannels = 2;
mp3Config.outputSampleRate = (pConfig->sampleRate != 0) ? pConfig->sampleRate : 44100;
if (!drmp3_init(pMP3, ma_decoder_internal_on_read__mp3, ma_decoder_internal_on_seek__mp3, pDecoder, &mp3Config, &allocationCallbacks)) {
ma__free_from_callbacks(pMP3, &pDecoder->allocationCallbacks); ma__free_from_callbacks(pMP3, &pDecoder->allocationCallbacks);
return MA_ERROR; return MA_ERROR;
} }
...@@ -39952,7 +39946,11 @@ static ma_result ma_decoder_init_mp3__internal(const ma_decoder_config* pConfig, ...@@ -39952,7 +39946,11 @@ static ma_result ma_decoder_init_mp3__internal(const ma_decoder_config* pConfig,
pDecoder->pInternalDecoder = pMP3; pDecoder->pInternalDecoder = pMP3;
/* Internal format. */ /* Internal format. */
#if defined(DR_MP3_FLOAT_OUTPUT)
pDecoder->internalFormat = ma_format_f32; pDecoder->internalFormat = ma_format_f32;
#else
pDecoder->internalFormat = ma_format_s16;
#endif
pDecoder->internalChannels = pMP3->channels; pDecoder->internalChannels = pMP3->channels;
pDecoder->internalSampleRate = pMP3->sampleRate; pDecoder->internalSampleRate = pMP3->sampleRate;
ma_get_standard_channel_map(ma_standard_channel_map_default, pDecoder->internalChannels, pDecoder->internalChannelMap); ma_get_standard_channel_map(ma_standard_channel_map_default, pDecoder->internalChannels, pDecoder->internalChannelMap);
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment