aboutsummaryrefslogtreecommitdiff
path: root/src/external
diff options
context:
space:
mode:
authorRay <raysan5@gmail.com>2019-05-16 15:31:33 +0200
committerRay <raysan5@gmail.com>2019-05-16 15:31:33 +0200
commit25ac9bfb280e13cc5f9f5824407eb21ca7fcbdd2 (patch)
tree4d3876c638b0fe2b04ea0cebab92d426a7fb45cf /src/external
parent579d9325510d80cd038c7b4e1965aaf88efea527 (diff)
downloadraylib-25ac9bfb280e13cc5f9f5824407eb21ca7fcbdd2.tar.gz
raylib-25ac9bfb280e13cc5f9f5824407eb21ca7fcbdd2.zip
Update dr_flac, dr_mp3, dr_wav to latest version
Diffstat (limited to 'src/external')
-rw-r--r--src/external/dr_flac.h6156
-rw-r--r--src/external/dr_mp3.h1803
-rw-r--r--src/external/dr_wav.h3251
3 files changed, 8051 insertions, 3159 deletions
diff --git a/src/external/dr_flac.h b/src/external/dr_flac.h
index c836847e..13f42b2a 100644
--- a/src/external/dr_flac.h
+++ b/src/external/dr_flac.h
@@ -1,119 +1,118 @@
-// FLAC audio decoder. Public domain. See "unlicense" statement at the end of this file.
-// dr_flac - v0.9.7 - 2018-07-05
-//
-// David Reid - mackron@gmail.com
-
-// USAGE
-//
-// dr_flac is a single-file library. To use it, do something like the following in one .c file.
-// #define DR_FLAC_IMPLEMENTATION
-// #include "dr_flac.h"
-//
-// You can then #include this file in other parts of the program as you would with any other header file. To decode audio data,
-// do something like the following:
-//
-// drflac* pFlac = drflac_open_file("MySong.flac");
-// if (pFlac == NULL) {
-// // Failed to open FLAC file
-// }
-//
-// drflac_int32* pSamples = malloc(pFlac->totalSampleCount * sizeof(drflac_int32));
-// drflac_uint64 numberOfInterleavedSamplesActuallyRead = drflac_read_s32(pFlac, pFlac->totalSampleCount, pSamples);
-//
-// The drflac object represents the decoder. It is a transparent type so all the information you need, such as the number of
-// channels and the bits per sample, should be directly accessible - just make sure you don't change their values. Samples are
-// always output as interleaved signed 32-bit PCM. In the example above a native FLAC stream was opened, however dr_flac has
-// seamless support for Ogg encapsulated FLAC streams as well.
-//
-// You do not need to decode the entire stream in one go - you just specify how many samples you'd like at any given time and
-// the decoder will give you as many samples as it can, up to the amount requested. Later on when you need the next batch of
-// samples, just call it again. Example:
-//
-// while (drflac_read_s32(pFlac, chunkSize, pChunkSamples) > 0) {
-// do_something();
-// }
-//
-// You can seek to a specific sample with drflac_seek_to_sample(). The given sample is based on interleaving. So for example,
-// if you were to seek to the sample at index 0 in a stereo stream, you'll be seeking to the first sample of the left channel.
-// The sample at index 1 will be the first sample of the right channel. The sample at index 2 will be the second sample of the
-// left channel, etc.
-//
-//
-// If you just want to quickly decode an entire FLAC file in one go you can do something like this:
-//
-// unsigned int channels;
-// unsigned int sampleRate;
-// drflac_uint64 totalSampleCount;
-// drflac_int32* pSampleData = drflac_open_and_decode_file_s32("MySong.flac", &channels, &sampleRate, &totalSampleCount);
-// if (pSampleData == NULL) {
-// // Failed to open and decode FLAC file.
-// }
-//
-// ...
-//
-// drflac_free(pSampleData);
-//
-//
-// You can read samples as signed 16-bit integer and 32-bit floating-point PCM with the *_s16() and *_f32() family of APIs
-// respectively, but note that these should be considered lossy.
-//
-//
-// If you need access to metadata (album art, etc.), use drflac_open_with_metadata(), drflac_open_file_with_metdata() or
-// drflac_open_memory_with_metadata(). The rationale for keeping these APIs separate is that they're slightly slower than the
-// normal versions and also just a little bit harder to use.
-//
-// dr_flac reports metadata to the application through the use of a callback, and every metadata block is reported before
-// drflac_open_with_metdata() returns.
-//
-//
-// 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 like 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
-// corrupted or perverse file. In addition, these APIs can take a long time to initialize because they may need to spend
-// a lot of time finding the first frame.
-//
-//
-//
-// OPTIONS
-// #define these options before including this file.
-//
-// #define DR_FLAC_NO_STDIO
-// Disable drflac_open_file().
-//
-// #define DR_FLAC_NO_OGG
-// Disables support for Ogg/FLAC streams.
-//
-// #define DR_FLAC_NO_WIN32_IO
-// In the Win32 build, dr_flac uses the Win32 IO APIs for drflac_open_file() by default. This setting will make it use the
-// standard FILE APIs instead. Ignored when DR_FLAC_NO_STDIO is #defined. (The rationale for this configuration is that
-// there's a bug in one compiler's Win32 implementation of the FILE APIs which is not present in the Win32 IO APIs.)
-//
-// #define DR_FLAC_BUFFER_SIZE <number>
-// Defines the size of the internal buffer to store data from onRead(). This buffer is used to reduce the number of calls
-// back to the client for more data. Larger values means more memory, but better performance. My tests show diminishing
-// returns after about 4KB (which is the default). Consider reducing this if you have a very efficient implementation of
-// onRead(), or increase it if it's very inefficient. Must be a multiple of 8.
-//
-// #define DR_FLAC_NO_CRC
-// Disables CRC checks. This will offer a performance boost when CRC is unnecessary.
-//
-// #define DR_FLAC_NO_SIMD
-// Disables SIMD optimizations (SSE on x86/x64 architectures). Use this if you are having compatibility issues with your
-// compiler.
-//
-//
-//
-// QUICK NOTES
-// - dr_flac does not currently support changing the sample rate nor channel count mid stream.
-// - Audio data is output as signed 32-bit PCM, regardless of the bits per sample the FLAC stream is encoded as.
-// - This has not been tested on big-endian architectures.
-// - Rice codes in unencoded binary form (see https://xiph.org/flac/format.html#rice_partition) has not been tested. If anybody
-// knows where I can find some test files for this, let me know.
-// - dr_flac is not thread-safe, but its APIs can be called from any thread so long as you do your own synchronization.
-// - When using Ogg encapsulation, a corrupted metadata block will result in drflac_open_with_metadata() and drflac_open()
-// returning inconsistent samples.
+/*
+FLAC audio decoder. Choice of public domain or MIT-0. See license statements at the end of this file.
+dr_flac - v0.11.7 - 2019-05-06
+
+David Reid - mackron@gmail.com
+*/
+
+/*
+USAGE
+=====
+dr_flac is a single-file library. To use it, do something like the following in one .c file.
+ #define DR_FLAC_IMPLEMENTATION
+ #include "dr_flac.h"
+
+You can then #include this file in other parts of the program as you would with any other header file. To decode audio data,
+do something like the following:
+
+ drflac* pFlac = drflac_open_file("MySong.flac");
+ if (pFlac == NULL) {
+ // Failed to open FLAC file
+ }
+
+ drflac_int32* pSamples = malloc(pFlac->totalPCMFrameCount * pFlac->channels * sizeof(drflac_int32));
+ drflac_uint64 numberOfInterleavedSamplesActuallyRead = drflac_read_pcm_frames_s32(pFlac, pFlac->totalPCMFrameCount, pSamples);
+
+The drflac object represents the decoder. It is a transparent type so all the information you need, such as the number of
+channels and the bits per sample, should be directly accessible - just make sure you don't change their values. Samples are
+always output as interleaved signed 32-bit PCM. In the example above a native FLAC stream was opened, however dr_flac has
+seamless support for Ogg encapsulated FLAC streams as well.
+
+You do not need to decode the entire stream in one go - you just specify how many samples you'd like at any given time and
+the decoder will give you as many samples as it can, up to the amount requested. Later on when you need the next batch of
+samples, just call it again. Example:
+
+ while (drflac_read_pcm_frames_s32(pFlac, chunkSizeInPCMFrames, pChunkSamples) > 0) {
+ do_something();
+ }
+
+You can seek to a specific sample with drflac_seek_to_sample(). The given sample is based on interleaving. So for example,
+if you were to seek to the sample at index 0 in a stereo stream, you'll be seeking to the first sample of the left channel.
+The sample at index 1 will be the first sample of the right channel. The sample at index 2 will be the second sample of the
+left channel, etc.
+
+
+If you just want to quickly decode an entire FLAC file in one go you can do something like this:
+
+ unsigned int channels;
+ unsigned int sampleRate;
+ drflac_uint64 totalPCMFrameCount;
+ drflac_int32* pSampleData = drflac_open_file_and_read_pcm_frames_s32("MySong.flac", &channels, &sampleRate, &totalPCMFrameCount);
+ if (pSampleData == NULL) {
+ // Failed to open and decode FLAC file.
+ }
+
+ ...
+
+ drflac_free(pSampleData);
+
+
+You can read samples as signed 16-bit integer and 32-bit floating-point PCM with the *_s16() and *_f32() family of APIs
+respectively, but note that these should be considered lossy.
+
+
+If you need access to metadata (album art, etc.), use drflac_open_with_metadata(), drflac_open_file_with_metdata() or
+drflac_open_memory_with_metadata(). The rationale for keeping these APIs separate is that they're slightly slower than the
+normal versions and also just a little bit harder to use.
+
+dr_flac reports metadata to the application through the use of a callback, and every metadata block is reported before
+drflac_open_with_metdata() returns.
+
+
+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 like 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
+corrupted or perverse file. In addition, these APIs can take a long time to initialize because they may need to spend
+a lot of time finding the first frame.
+
+
+
+OPTIONS
+=======
+#define these options before including this file.
+
+#define DR_FLAC_NO_STDIO
+ Disable drflac_open_file() and family.
+
+#define DR_FLAC_NO_OGG
+ Disables support for Ogg/FLAC streams.
+
+#define DR_FLAC_BUFFER_SIZE <number>
+ Defines the size of the internal buffer to store data from onRead(). This buffer is used to reduce the number of calls
+ back to the client for more data. Larger values means more memory, but better performance. My tests show diminishing
+ returns after about 4KB (which is the default). Consider reducing this if you have a very efficient implementation of
+ onRead(), or increase it if it's very inefficient. Must be a multiple of 8.
+
+#define DR_FLAC_NO_CRC
+ Disables CRC checks. This will offer a performance boost when CRC is unnecessary.
+
+#define DR_FLAC_NO_SIMD
+ Disables SIMD optimizations (SSE on x86/x64 architectures). Use this if you are having compatibility issues with your
+ compiler.
+
+
+
+QUICK NOTES
+===========
+- dr_flac does not currently support changing the sample rate nor channel count mid stream.
+- Audio data is output as signed 32-bit PCM, regardless of the bits per sample the FLAC stream is encoded as.
+- 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.
+- When using Ogg encapsulation, a corrupted metadata block will result in drflac_open_with_metadata() and drflac_open()
+ returning inconsistent samples.
+*/
#ifndef dr_flac_h
#define dr_flac_h
@@ -145,9 +144,25 @@ typedef drflac_uint32 drflac_bool32;
#define DRFLAC_TRUE 1
#define DRFLAC_FALSE 0
-// As data is read from the client it is placed into an internal buffer for fast access. This controls the
-// size of that buffer. Larger values means more speed, but also more memory. In my testing there is diminishing
-// returns after about 4KB, but you can fiddle with this to suit your own needs. Must be a multiple of 8.
+#if defined(_MSC_VER) && _MSC_VER >= 1700 /* Visual Studio 2012 */
+ #define DRFLAC_DEPRECATED __declspec(deprecated)
+#elif (defined(__GNUC__) && __GNUC__ >= 4) /* GCC 4 */
+ #define DRFLAC_DEPRECATED __attribute__((deprecated))
+#elif defined(__has_feature) /* Clang */
+ #if defined(__has_feature(attribute_deprecated))
+ #define DRFLAC_DEPRECATED __attribute__((deprecated))
+ #else
+ #define DRFLAC_DEPRECATED
+ #endif
+#else
+ #define DRFLAC_DEPRECATED
+#endif
+
+/*
+As data is read from the client it is placed into an internal buffer for fast access. This controls the
+size of that buffer. Larger values means more speed, but also more memory. In my testing there is diminishing
+returns after about 4KB, but you can fiddle with this to suit your own needs. Must be a multiple of 8.
+*/
#ifndef DR_FLAC_BUFFER_SIZE
#define DR_FLAC_BUFFER_SIZE 4096
#endif
@@ -156,16 +171,10 @@ typedef drflac_uint32 drflac_bool32;
extern "C" {
#endif
-// Check if we can enable 64-bit optimizations.
-#if defined(_WIN64)
-#define DRFLAC_64BIT
-#endif
-
-#if defined(__GNUC__)
-#if defined(__x86_64__) || defined(__ppc64__)
+/* Check if we can enable 64-bit optimizations. */
+#if defined(_WIN64) || defined(_LP64) || defined(__LP64__)
#define DRFLAC_64BIT
#endif
-#endif
#ifdef DRFLAC_64BIT
typedef drflac_uint64 drflac_cache_t;
@@ -173,7 +182,7 @@ typedef drflac_uint64 drflac_cache_t;
typedef drflac_uint32 drflac_cache_t;
#endif
-// The various metadata block types.
+/* The various metadata block types. */
#define DRFLAC_METADATA_BLOCK_TYPE_STREAMINFO 0
#define DRFLAC_METADATA_BLOCK_TYPE_PADDING 1
#define DRFLAC_METADATA_BLOCK_TYPE_APPLICATION 2
@@ -183,7 +192,7 @@ typedef drflac_uint32 drflac_cache_t;
#define DRFLAC_METADATA_BLOCK_TYPE_PICTURE 6
#define DRFLAC_METADATA_BLOCK_TYPE_INVALID 127
-// The various picture types specified in the PICTURE block.
+/* The various picture types specified in the PICTURE block. */
#define DRFLAC_PICTURE_TYPE_OTHER 0
#define DRFLAC_PICTURE_TYPE_FILE_ICON 1
#define DRFLAC_PICTURE_TYPE_OTHER_FILE_ICON 2
@@ -219,12 +228,12 @@ typedef enum
drflac_seek_origin_current
} drflac_seek_origin;
-// Packing is important on this structure because we map this directly to the raw data within the SEEKTABLE metadata block.
+/* Packing is important on this structure because we map this directly to the raw data within the SEEKTABLE metadata block. */
#pragma pack(2)
typedef struct
{
drflac_uint64 firstSample;
- drflac_uint64 frameOffset; // The offset from the first byte of the header of the first frame.
+ drflac_uint64 frameOffset; /* The offset from the first byte of the header of the first frame. */
drflac_uint16 sampleCount;
} drflac_seekpoint;
#pragma pack()
@@ -244,15 +253,17 @@ typedef struct
typedef struct
{
- // The metadata type. Use this to know how to interpret the data below.
+ /* The metadata type. Use this to know how to interpret the data below. */
drflac_uint32 type;
- // A pointer to the raw data. This points to a temporary buffer so don't hold on to it. It's best to
- // not modify the contents of this buffer. Use the structures below for more meaningful and structured
- // information about the metadata. It's possible for this to be null.
+ /*
+ A pointer to the raw data. This points to a temporary buffer so don't hold on to it. It's best to
+ not modify the contents of this buffer. Use the structures below for more meaningful and structured
+ information about the metadata. It's possible for this to be null.
+ */
const void* pRawData;
- // The size in bytes of the block and the buffer pointed to by pRawData if it's non-NULL.
+ /* The size in bytes of the block and the buffer pointed to by pRawData if it's non-NULL. */
drflac_uint32 rawDataSize;
union
@@ -282,7 +293,7 @@ typedef struct
drflac_uint32 vendorLength;
const char* vendor;
drflac_uint32 commentCount;
- const char* comments;
+ const void* pComments;
} vorbis_comment;
struct
@@ -291,7 +302,7 @@ typedef struct
drflac_uint64 leadInSampleCount;
drflac_bool32 isCD;
drflac_uint8 trackCount;
- const drflac_uint8* pTrackData;
+ const void* pTrackData;
} cuesheet;
struct
@@ -312,40 +323,46 @@ typedef struct
} drflac_metadata;
-// 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.
-//
-// 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.
+/*
+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.
+
+A return value of less than bytesToRead indicates the end of the stream. Do _not_ return from this callback until
+either the entire bytesToRead is filled or you have reached the end of the stream.
+*/
typedef size_t (* drflac_read_proc)(void* pUserData, void* pBufferOut, size_t bytesToRead);
-// Callback for when data needs to be seeked.
-//
-// pUserData [in] The user data that was passed to drflac_open() and family.
-// offset [in] The number of bytes to move, relative to the origin. Will never be negative.
-// origin [in] The origin of the seek - the current position or the start of the stream.
-//
-// Returns whether or not the seek was successful.
-//
-// 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.
+/*
+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.
+
+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.
+*/
typedef drflac_bool32 (* drflac_seek_proc)(void* pUserData, int offset, drflac_seek_origin origin);
-// Callback for when a metadata block is read.
-//
-// pUserData [in] The user data that was passed to drflac_open() and family.
-// pMetadata [in] A pointer to a structure containing the data of the metadata block.
-//
-// Use pMetadata->type to determine which metadata block is being handled and how to read the data.
+/*
+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.
+
+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);
-// Structure for internal use. Only used for decoders opened with drflac_open_memory.
+/* Structure for internal use. Only used for decoders opened with drflac_open_memory. */
typedef struct
{
const drflac_uint8* data;
@@ -353,420 +370,588 @@ typedef struct
size_t currentReadPos;
} drflac__memory_stream;
-// Structure for internal use. Used for bit streaming.
+/* Structure for internal use. Used for bit streaming. */
typedef struct
{
- // The function to call when more data needs to be read.
+ /* The function to call when more data needs to be read. */
drflac_read_proc onRead;
- // The function to call when the current read position needs to be moved.
+ /* The function to call when the current read position needs to be moved. */
drflac_seek_proc onSeek;
- // The user data to pass around to onRead and onSeek.
+ /* The user data to pass around to onRead and onSeek. */
void* pUserData;
- // The number of unaligned bytes in the L2 cache. This will always be 0 until the end of the stream is hit. At the end of the
- // stream there will be a number of bytes that don't cleanly fit in an L1 cache line, so we use this variable to know whether
- // or not the bistreamer needs to run on a slower path to read those last bytes. This will never be more than sizeof(drflac_cache_t).
+ /*
+ The number of unaligned bytes in the L2 cache. This will always be 0 until the end of the stream is hit. At the end of the
+ stream there will be a number of bytes that don't cleanly fit in an L1 cache line, so we use this variable to know whether
+ or not the bistreamer needs to run on a slower path to read those last bytes. This will never be more than sizeof(drflac_cache_t).
+ */
size_t unalignedByteCount;
- // The content of the unaligned bytes.
+ /* The content of the unaligned bytes. */
drflac_cache_t unalignedCache;
- // The index of the next valid cache line in the "L2" cache.
+ /* The index of the next valid cache line in the "L2" cache. */
drflac_uint32 nextL2Line;
- // The number of bits that have been consumed by the cache. This is used to determine how many valid bits are remaining.
+ /* The number of bits that have been consumed by the cache. This is used to determine how many valid bits are remaining. */
drflac_uint32 consumedBits;
- // The cached data which was most recently read from the client. There are two levels of cache. Data flows as such:
- // Client -> L2 -> L1. The L2 -> L1 movement is aligned and runs on a fast path in just a few instructions.
+ /*
+ The cached data which was most recently read from the client. There are two levels of cache. Data flows as such:
+ Client -> L2 -> L1. The L2 -> L1 movement is aligned and runs on a fast path in just a few instructions.
+ */
drflac_cache_t cacheL2[DR_FLAC_BUFFER_SIZE/sizeof(drflac_cache_t)];
drflac_cache_t cache;
- // CRC-16. This is updated whenever bits are read from the bit stream. Manually set this to 0 to reset the CRC. For FLAC, this
- // is reset to 0 at the beginning of each frame.
+ /*
+ CRC-16. This is updated whenever bits are read from the bit stream. Manually set this to 0 to reset the CRC. For FLAC, this
+ is reset to 0 at the beginning of each frame.
+ */
drflac_uint16 crc16;
- drflac_cache_t crc16Cache; // A cache for optimizing CRC calculations. This is filled when when the L1 cache is reloaded.
- drflac_uint32 crc16CacheIgnoredBytes; // The number of bytes to ignore when updating the CRC-16 from the CRC-16 cache.
+ drflac_cache_t crc16Cache; /* A cache for optimizing CRC calculations. This is filled when when the L1 cache is reloaded. */
+ drflac_uint32 crc16CacheIgnoredBytes; /* The number of bytes to ignore when updating the CRC-16 from the CRC-16 cache. */
} drflac_bs;
typedef struct
{
- // The type of the subframe: SUBFRAME_CONSTANT, SUBFRAME_VERBATIM, SUBFRAME_FIXED or SUBFRAME_LPC.
+ /* The type of the subframe: SUBFRAME_CONSTANT, SUBFRAME_VERBATIM, SUBFRAME_FIXED or SUBFRAME_LPC. */
drflac_uint8 subframeType;
- // The number of wasted bits per sample as specified by the sub-frame header.
+ /* The number of wasted bits per sample as specified by the sub-frame header. */
drflac_uint8 wastedBitsPerSample;
- // The order to use for the prediction stage for SUBFRAME_FIXED and SUBFRAME_LPC.
+ /* The order to use for the prediction stage for SUBFRAME_FIXED and SUBFRAME_LPC. */
drflac_uint8 lpcOrder;
- // The number of bits per sample for this subframe. This is not always equal to the current frame's bit per sample because
- // an extra bit is required for side channels when interchannel decorrelation is being used.
+ /*
+ The number of bits per sample for this subframe. This is not always equal to the current frame's bit per sample because
+ an extra bit is required for side channels when interchannel decorrelation is being used.
+ */
drflac_uint32 bitsPerSample;
- // A pointer to the buffer containing the decoded samples in the subframe. This pointer is an offset from drflac::pExtraData. Note that
- // it's a signed 32-bit integer for each value.
+ /*
+ A pointer to the buffer containing the decoded samples in the subframe. This pointer is an offset from drflac::pExtraData. Note that
+ it's a signed 32-bit integer for each value.
+ */
drflac_int32* pDecodedSamples;
} drflac_subframe;
typedef struct
{
- // If the stream uses variable block sizes, this will be set to the index of the first sample. If fixed block sizes are used, this will
- // always be set to 0.
+ /*
+ If the stream uses variable block sizes, this will be set to the index of the first sample. If fixed block sizes are used, this will
+ always be set to 0.
+ */
drflac_uint64 sampleNumber;
- // If the stream uses fixed block sizes, this will be set to the frame number. If variable block sizes are used, this will always be 0.
+ /* If the stream uses fixed block sizes, this will be set to the frame number. If variable block sizes are used, this will always be 0. */
drflac_uint32 frameNumber;
- // The sample rate of this frame.
+ /* The sample rate of this frame. */
drflac_uint32 sampleRate;
- // The number of samples in each sub-frame within this frame.
+ /* The number of samples in each sub-frame within this frame. */
drflac_uint16 blockSize;
- // The channel assignment of this frame. This is not always set to the channel count. If interchannel decorrelation is being used this
- // will be set to DRFLAC_CHANNEL_ASSIGNMENT_LEFT_SIDE, DRFLAC_CHANNEL_ASSIGNMENT_RIGHT_SIDE or DRFLAC_CHANNEL_ASSIGNMENT_MID_SIDE.
+ /*
+ The channel assignment of this frame. This is not always set to the channel count. If interchannel decorrelation is being used this
+ will be set to DRFLAC_CHANNEL_ASSIGNMENT_LEFT_SIDE, DRFLAC_CHANNEL_ASSIGNMENT_RIGHT_SIDE or DRFLAC_CHANNEL_ASSIGNMENT_MID_SIDE.
+ */
drflac_uint8 channelAssignment;
- // The number of bits per sample within this frame.
+ /* The number of bits per sample within this frame. */
drflac_uint8 bitsPerSample;
- // The frame's CRC.
+ /* The frame's CRC. */
drflac_uint8 crc8;
} drflac_frame_header;
typedef struct
{
- // The header.
+ /* The header. */
drflac_frame_header header;
- // The number of samples left to be read in this frame. This is initially set to the block size multiplied by the channel count. As samples
- // are read, this will be decremented. When it reaches 0, the decoder will see this frame as fully consumed and load the next frame.
+ /*
+ The number of samples left to be read in this frame. This is initially set to the block size multiplied by the channel count. As samples
+ are read, this will be decremented. When it reaches 0, the decoder will see this frame as fully consumed and load the next frame.
+ */
drflac_uint32 samplesRemaining;
- // The list of sub-frames within the frame. There is one sub-frame for each channel, and there's a maximum of 8 channels.
+ /* The list of sub-frames within the frame. There is one sub-frame for each channel, and there's a maximum of 8 channels. */
drflac_subframe subframes[8];
} drflac_frame;
typedef struct
{
- // The function to call when a metadata block is read.
+ /* The function to call when a metadata block is read. */
drflac_meta_proc onMeta;
- // The user data posted to the metadata callback function.
+ /* The user data posted to the metadata callback function. */
void* pUserDataMD;
- // The sample rate. Will be set to something like 44100.
+ /* The sample rate. Will be set to something like 44100. */
drflac_uint32 sampleRate;
- // The number of channels. This will be set to 1 for monaural streams, 2 for stereo, etc. Maximum 8. This is set based on the
- // value specified in the STREAMINFO block.
+ /*
+ The number of channels. This will be set to 1 for monaural streams, 2 for stereo, etc. Maximum 8. This is set based on the
+ value specified in the STREAMINFO block.
+ */
drflac_uint8 channels;
- // The bits per sample. Will be set to something like 16, 24, etc.
+ /* The bits per sample. Will be set to something like 16, 24, etc. */
drflac_uint8 bitsPerSample;
- // The maximum block size, in samples. This number represents the number of samples in each channel (not combined).
+ /* The maximum block size, in samples. This number represents the number of samples in each channel (not combined). */
drflac_uint16 maxBlockSize;
- // The total number of samples making up the stream. This includes every channel. For example, if the stream has 2 channels,
- // with each channel having a total of 4096, this value will be set to 2*4096 = 8192. Can be 0 in which case it's still a
- // valid stream, but just means the total sample count is unknown. Likely the case with streams like internet radio.
+ /*
+ The total number of samples making up the stream. This includes every channel. For example, if the stream has 2 channels,
+ with each channel having a total of 4096, this value will be set to 2*4096 = 8192. Can be 0 in which case it's still a
+ valid stream, but just means the total sample count is unknown. Likely the case with streams like internet radio.
+ */
drflac_uint64 totalSampleCount;
+ drflac_uint64 totalPCMFrameCount; /* <-- Equal to totalSampleCount / channels. */
- // The container type. This is set based on whether or not the decoder was opened from a native or Ogg stream.
+ /* The container type. This is set based on whether or not the decoder was opened from a native or Ogg stream. */
drflac_container container;
- // The number of seekpoints in the seektable.
+ /* The number of seekpoints in the seektable. */
drflac_uint32 seekpointCount;
- // Information about the frame the decoder is currently sitting on.
+ /* Information about the frame the decoder is currently sitting on. */
drflac_frame currentFrame;
- // The index of the sample the decoder is currently sitting on. This is only used for seeking.
+ /* The index of the sample the decoder is currently sitting on. This is only used for seeking. */
drflac_uint64 currentSample;
- // The position of the first frame in the stream. This is only ever used for seeking.
+ /* The position of the first frame in the stream. This is only ever used for seeking. */
drflac_uint64 firstFramePos;
- // A hack to avoid a malloc() when opening a decoder with drflac_open_memory().
+ /* A hack to avoid a malloc() when opening a decoder with drflac_open_memory(). */
drflac__memory_stream memoryStream;
- // A pointer to the decoded sample data. This is an offset of pExtraData.
+ /* A pointer to the decoded sample data. This is an offset of pExtraData. */
drflac_int32* pDecodedSamples;
- // A pointer to the seek table. This is an offset of pExtraData, or NULL if there is no seek table.
+ /* A pointer to the seek table. This is an offset of pExtraData, or NULL if there is no seek table. */
drflac_seekpoint* pSeekpoints;
- // Internal use only. Only used with Ogg containers. Points to a drflac_oggbs object. This is an offset of pExtraData.
+ /* Internal use only. Only used with Ogg containers. Points to a drflac_oggbs object. This is an offset of pExtraData. */
void* _oggbs;
- // The bit streamer. The raw FLAC data is fed through this object.
+ /* The bit streamer. The raw FLAC data is fed through this object. */
drflac_bs bs;
- // Variable length extra data. We attach this to the end of the object so we can avoid unnecessary mallocs.
+ /* Variable length extra data. We attach this to the end of the object so we can avoid unnecessary mallocs. */
drflac_uint8 pExtraData[1];
} drflac;
-// Opens a FLAC decoder.
-//
-// onRead [in] The function to call when data needs to be read from the client.
-// onSeek [in] The function to call when the read position of the client data needs to move.
-// pUserData [in, optional] A pointer to application defined data that will be passed to onRead and onSeek.
-//
-// Returns a pointer to an object representing the decoder.
-//
-// Close the decoder with drflac_close().
-//
-// This function will automatically detect whether or not you are attempting to open a native or Ogg encapsulated
-// FLAC, both of which should work seamlessly without any manual intervention. Ogg encapsulation also works with
-// multiplexed streams which basically means it can play FLAC encoded audio tracks in videos.
-//
-// This is the lowest level function for opening a FLAC stream. You can also use drflac_open_file() and drflac_open_memory()
-// to open the stream from a file or from a block of memory respectively.
-//
-// The STREAMINFO block must be present for this to succeed. Use drflac_open_relaxed() to open a FLAC stream where
-// the header may not be present.
-//
-// See also: drflac_open_file(), drflac_open_memory(), drflac_open_with_metadata(), drflac_close()
+/*
+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.
+
+Returns a pointer to an object representing the decoder.
+
+Close the decoder with drflac_close().
+
+This function will automatically detect whether or not you are attempting to open a native or Ogg encapsulated
+FLAC, both of which should work seamlessly without any manual intervention. Ogg encapsulation also works with
+multiplexed streams which basically means it can play FLAC encoded audio tracks in videos.
+
+This is the lowest level function for opening a FLAC stream. You can also use drflac_open_file() and drflac_open_memory()
+to open the stream from a file or from a block of memory respectively.
+
+The STREAMINFO block must be present for this to succeed. Use drflac_open_relaxed() to open a FLAC stream where
+the header may not be present.
+
+See also: drflac_open_file(), drflac_open_memory(), drflac_open_with_metadata(), drflac_close()
+*/
drflac* drflac_open(drflac_read_proc onRead, drflac_seek_proc onSeek, void* pUserData);
-// The same as drflac_open(), except attempts to open the stream even when a header block is not present.
-//
-// Because the header is not necessarily available, the caller must explicitly define the container (Native or Ogg). Do
-// not set this to drflac_container_unknown - that is for internal use only.
-//
-// Opening in relaxed mode will continue reading data from onRead until it finds a valid frame. If a frame is never
-// found it will continue forever. To abort, force your onRead callback to return 0, which dr_flac will use as an
-// indicator that the end of the stream was found.
+/*
+The same as drflac_open(), except attempts to open the stream even when a header block is not present.
+
+Because the header is not necessarily available, the caller must explicitly define the container (Native or Ogg). Do
+not set this to drflac_container_unknown - that is for internal use only.
+
+Opening in relaxed mode will continue reading data from onRead until it finds a valid frame. If a frame is never
+found it will continue forever. To abort, force your onRead callback to return 0, which dr_flac will use as an
+indicator that the end of the stream was found.
+*/
drflac* drflac_open_relaxed(drflac_read_proc onRead, drflac_seek_proc onSeek, drflac_container container, void* pUserData);
-// 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.
-//
-// Returns a pointer to an object representing the decoder.
-//
-// Close the decoder with drflac_close().
-//
-// This is slower than drflac_open(), so avoid this one if you don't need metadata. Internally, this will do a DRFLAC_MALLOC()
-// and DRFLAC_FREE() for every metadata block except for STREAMINFO and PADDING blocks.
-//
-// The caller is notified of the metadata via the onMeta callback. All metadata blocks will be handled before the function
-// returns.
-//
-// 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.
-//
-// See also: drflac_open_file_with_metadata(), drflac_open_memory_with_metadata(), drflac_open(), drflac_close()
+/*
+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.
+
+Returns a pointer to an object representing the decoder.
+
+Close the decoder with drflac_close().
+
+This is slower than drflac_open(), so avoid this one if you don't need metadata. Internally, this will do a DRFLAC_MALLOC()
+and DRFLAC_FREE() for every metadata block except for STREAMINFO and PADDING blocks.
+
+The caller is notified of the metadata via the onMeta callback. All metadata blocks will be handled before the function
+returns.
+
+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.
+
+See 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);
-// 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()
+/*
+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()
+*/
drflac* drflac_open_with_metadata_relaxed(drflac_read_proc onRead, drflac_seek_proc onSeek, drflac_meta_proc onMeta, drflac_container container, void* pUserData);
-// Closes the given FLAC decoder.
-//
-// pFlac [in] The decoder to close.
-//
-// This will destroy the decoder object.
+/*
+Closes the given FLAC decoder.
+
+pFlac [in] The decoder to close.
+
+This will destroy the decoder object.
+*/
void drflac_close(drflac* pFlac);
-// Reads sample data from the given FLAC decoder, output as interleaved signed 32-bit PCM.
-//
-// pFlac [in] The decoder.
-// samplesToRead [in] The number of samples to read.
-// pBufferOut [out, optional] A pointer to the buffer that will receive the decoded samples.
-//
-// Returns the number of samples actually read.
-//
-// pBufferOut can be null, in which case the call will act as a seek, and the return value will be the number of samples
-// seeked.
-drflac_uint64 drflac_read_s32(drflac* pFlac, drflac_uint64 samplesToRead, drflac_int32* pBufferOut);
-
-// Same as drflac_read_s32(), except outputs samples as 16-bit integer PCM rather than 32-bit.
-//
-// pFlac [in] The decoder.
-// samplesToRead [in] The number of samples to read.
-// pBufferOut [out, optional] A pointer to the buffer that will receive the decoded samples.
-//
-// Returns the number of samples actually read.
-//
-// pBufferOut can be null, in which case the call will act as a seek, and the return value will be the number of samples
-// seeked.
-//
-// Note that this is lossy for streams where the bits per sample is larger than 16.
-drflac_uint64 drflac_read_s16(drflac* pFlac, drflac_uint64 samplesToRead, drflac_int16* pBufferOut);
-
-// Same as drflac_read_s32(), except outputs samples as 32-bit floating-point PCM.
-//
-// pFlac [in] The decoder.
-// samplesToRead [in] The number of samples to read.
-// pBufferOut [out, optional] A pointer to the buffer that will receive the decoded samples.
-//
-// Returns the number of samples actually read.
-//
-// pBufferOut can be null, in which case the call will act as a seek, and the return value will be the number of samples
-// 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_f32(drflac* pFlac, drflac_uint64 samplesToRead, float* pBufferOut);
-
-// Seeks to the sample at the given index.
-//
-// pFlac [in] The decoder.
-// sampleIndex [in] The index of the sample to seek to. See notes below.
-//
-// Returns DRFLAC_TRUE if successful; DRFLAC_FALSE otherwise.
-//
-// The sample index is based on interleaving. In a stereo stream, for example, the sample at index 0 is the first sample
-// in the left channel; the sample at index 1 is the first sample on the right channel, and so on.
-//
-// When seeking, you will likely want to ensure it's rounded to a multiple of the channel count. You can do this with
-// something like drflac_seek_to_sample(pFlac, (mySampleIndex + (mySampleIndex % pFlac->channels)))
-drflac_bool32 drflac_seek_to_sample(drflac* pFlac, drflac_uint64 sampleIndex);
+/*
+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.
+
+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);
+
+/*
+Same as drflac_read_pcm_frames_s32(), except outputs samples as 16-bit integer PCM rather than 32-bit.
+
+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);
+
+/*
+Same as drflac_read_pcm_frames_s32(), except outputs samples as 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.
+*/
+drflac_uint64 drflac_read_pcm_frames_f32(drflac* pFlac, drflac_uint64 framesToRead, float* pBufferOut);
+
+/*
+Seeks to the PCM frame at the given index.
+
+pFlac [in] The decoder.
+pcmFrameIndex [in] The index of the PCM frame to seek to. See notes below.
+
+Returns DRFLAC_TRUE if successful; DRFLAC_FALSE otherwise.
+*/
+drflac_bool32 drflac_seek_to_pcm_frame(drflac* pFlac, drflac_uint64 pcmFrameIndex);
#ifndef DR_FLAC_NO_STDIO
-// 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.
-//
-// Returns a pointer to an object representing the decoder.
-//
-// 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()
+/*
+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.
+
+Returns a pointer to an object representing the decoder.
+
+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()
+*/
drflac* drflac_open_file(const char* filename);
-// Opens a FLAC decoder from the file at the given path and notifies the caller of the metadata chunks (album art, etc.)
-//
-// Look at the documentation for drflac_open_with_metadata() for more information on how metadata is handled.
+/*
+Opens a FLAC decoder from the file at the given path and notifies the caller of the metadata chunks (album art, etc.)
+
+Look at the documentation for drflac_open_with_metadata() for more information on how metadata is handled.
+*/
drflac* drflac_open_file_with_metadata(const char* filename, drflac_meta_proc onMeta, void* pUserData);
#endif
-// Opens a FLAC decoder from a pre-allocated block of memory
-//
-// This does not create a copy of the data. It is up to the application to ensure the buffer remains valid for
-// the lifetime of the decoder.
+/*
+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.
+*/
drflac* drflac_open_memory(const void* data, size_t dataSize);
-// Opens a FLAC decoder from a pre-allocated block of memory and notifies the caller of the metadata chunks (album art, etc.)
-//
-// Look at the documentation for drflac_open_with_metadata() for more information on how metadata is handled.
+/*
+Opens a FLAC decoder from a pre-allocated block of memory and notifies the caller of the metadata chunks (album art, etc.)
+
+Look at the documentation for drflac_open_with_metadata() for more information on how metadata is handled.
+*/
drflac* drflac_open_memory_with_metadata(const void* data, size_t dataSize, drflac_meta_proc onMeta, void* pUserData);
-//// High Level APIs ////
+/* High Level APIs */
-// Opens a FLAC stream from the given callbacks and fully decodes it in a single operation. The return value is a
-// pointer to the sample data as interleaved signed 32-bit PCM. The returned data must be freed with DRFLAC_FREE().
-//
-// Sometimes a FLAC file won't keep track of the total sample count. In this situation the function will continuously
-// read samples into a dynamically sized buffer on the heap until no samples are left.
-//
-// Do not call this function on a broadcast type of stream (like internet radio streams and whatnot).
-drflac_int32* drflac_open_and_decode_s32(drflac_read_proc onRead, drflac_seek_proc onSeek, void* pUserData, unsigned int* channels, unsigned int* sampleRate, drflac_uint64* totalSampleCount);
+/*
+Opens a FLAC stream from the given callbacks and fully decodes it in a single operation. The return value is a
+pointer to the sample data as interleaved signed 32-bit PCM. The returned data must be freed with DRFLAC_FREE().
-// Same as drflac_open_and_decode_s32(), except returns signed 16-bit integer samples.
-drflac_int16* drflac_open_and_decode_s16(drflac_read_proc onRead, drflac_seek_proc onSeek, void* pUserData, unsigned int* channels, unsigned int* sampleRate, drflac_uint64* totalSampleCount);
+Sometimes a FLAC file won't keep track of the total sample count. In this situation the function will continuously
+read samples into a dynamically sized buffer on the heap until no samples are left.
-// Same as drflac_open_and_decode_s32(), except returns 32-bit floating-point samples.
-float* drflac_open_and_decode_f32(drflac_read_proc onRead, drflac_seek_proc onSeek, void* pUserData, unsigned int* channels, unsigned int* sampleRate, drflac_uint64* totalSampleCount);
+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);
+
+/* 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);
+
+/* 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);
#ifndef DR_FLAC_NO_STDIO
-// Same as drflac_open_and_decode_s32() except opens the decoder from a file.
-drflac_int32* drflac_open_and_decode_file_s32(const char* filename, unsigned int* channels, unsigned int* sampleRate, drflac_uint64* totalSampleCount);
+/* 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);
-// Same as drflac_open_and_decode_file_s32(), except returns signed 16-bit integer samples.
-drflac_int16* drflac_open_and_decode_file_s16(const char* filename, unsigned int* channels, unsigned int* sampleRate, drflac_uint64* totalSampleCount);
+/* 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);
-// Same as drflac_open_and_decode_file_f32(), except returns 32-bit floating-point samples.
-float* drflac_open_and_decode_file_f32(const char* filename, unsigned int* channels, unsigned int* sampleRate, drflac_uint64* totalSampleCount);
+/* 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);
#endif
-// Same as drflac_open_and_decode_s32() except opens the decoder from a block of memory.
-drflac_int32* drflac_open_and_decode_memory_s32(const void* data, size_t dataSize, unsigned int* channels, unsigned int* sampleRate, drflac_uint64* totalSampleCount);
+/* 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);
-// Same as drflac_open_and_decode_memory_s32(), except returns signed 16-bit integer samples.
-drflac_int16* drflac_open_and_decode_memory_s16(const void* data, size_t dataSize, unsigned int* channels, unsigned int* sampleRate, drflac_uint64* totalSampleCount);
+/* 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);
-// Same as drflac_open_and_decode_memory_s32(), except returns 32-bit floating-point samples.
-float* drflac_open_and_decode_memory_f32(const void* data, size_t dataSize, unsigned int* channels, unsigned int* sampleRate, drflac_uint64* totalSampleCount);
+/* 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);
-// Frees memory that was allocated internally by dr_flac.
+/* Frees memory that was allocated internally by dr_flac. */
void drflac_free(void* p);
-// 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. */
typedef struct
{
drflac_uint32 countRemaining;
const char* pRunningData;
} drflac_vorbis_comment_iterator;
-// Initializes a vorbis comment iterator. This can be used for iterating over the vorbis comments in a VORBIS_COMMENT
-// metadata block.
-void drflac_init_vorbis_comment_iterator(drflac_vorbis_comment_iterator* pIter, drflac_uint32 commentCount, const char* pComments);
+/*
+Initializes a vorbis comment iterator. This can be used for iterating over the vorbis comments in a VORBIS_COMMENT
+metadata block.
+*/
+void drflac_init_vorbis_comment_iterator(drflac_vorbis_comment_iterator* pIter, drflac_uint32 commentCount, const void* pComments);
-// 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.
+/*
+Goes to the next vorbis comment in the given iterator. If null is returned it means there are no more comments. The
+returned string is NOT null terminated.
+*/
const char* drflac_next_vorbis_comment(drflac_vorbis_comment_iterator* pIter, drflac_uint32* pCommentLengthOut);
+/* Structure representing an iterator for cuesheet tracks in a CUESHEET metadata block. */
+typedef struct
+{
+ drflac_uint32 countRemaining;
+ const char* pRunningData;
+} drflac_cuesheet_track_iterator;
+
+/* Packing is important on this structure because we map this directly to the raw data within the CUESHEET metadata block. */
+#pragma pack(4)
+typedef struct
+{
+ drflac_uint64 offset;
+ drflac_uint8 index;
+ drflac_uint8 reserved[3];
+} drflac_cuesheet_track_index;
+#pragma pack()
+
+typedef struct
+{
+ drflac_uint64 offset;
+ drflac_uint8 trackNumber;
+ char ISRC[12];
+ drflac_bool8 isAudio;
+ drflac_bool8 preEmphasis;
+ drflac_uint8 indexCount;
+ const drflac_cuesheet_track_index* pIndexPoints;
+} drflac_cuesheet_track;
+
+/*
+Initializes a cuesheet track iterator. This can be used for iterating over the cuesheet tracks in a CUESHEET metadata
+block.
+*/
+void drflac_init_cuesheet_track_iterator(drflac_cuesheet_track_iterator* pIter, drflac_uint32 trackCount, const void* pTrackData);
+
+/* 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);
+
+
+/* Deprecated APIs */
+DRFLAC_DEPRECATED drflac_uint64 drflac_read_s32(drflac* pFlac, drflac_uint64 samplesToRead, drflac_int32* pBufferOut); /* Use drflac_read_pcm_frames_s32() instead. */
+DRFLAC_DEPRECATED drflac_uint64 drflac_read_s16(drflac* pFlac, drflac_uint64 samplesToRead, drflac_int16* pBufferOut); /* Use drflac_read_pcm_frames_s16() instead. */
+DRFLAC_DEPRECATED drflac_uint64 drflac_read_f32(drflac* pFlac, drflac_uint64 samplesToRead, float* pBufferOut); /* Use drflac_read_pcm_frames_f32() instead. */
+DRFLAC_DEPRECATED drflac_bool32 drflac_seek_to_sample(drflac* pFlac, drflac_uint64 sampleIndex); /* Use drflac_seek_to_pcm_frame() instead. */
+DRFLAC_DEPRECATED drflac_int32* drflac_open_and_decode_s32(drflac_read_proc onRead, drflac_seek_proc onSeek, void* pUserData, unsigned int* channels, unsigned int* sampleRate, drflac_uint64* totalSampleCount); /* Use drflac_open_and_read_pcm_frames_s32(). */
+DRFLAC_DEPRECATED drflac_int16* drflac_open_and_decode_s16(drflac_read_proc onRead, drflac_seek_proc onSeek, void* pUserData, unsigned int* channels, unsigned int* sampleRate, drflac_uint64* totalSampleCount); /* Use drflac_open_and_read_pcm_frames_s16(). */
+DRFLAC_DEPRECATED float* drflac_open_and_decode_f32(drflac_read_proc onRead, drflac_seek_proc onSeek, void* pUserData, unsigned int* channels, unsigned int* sampleRate, drflac_uint64* totalSampleCount); /* Use drflac_open_and_read_pcm_frames_f32(). */
+DRFLAC_DEPRECATED drflac_int32* drflac_open_and_decode_file_s32(const char* filename, unsigned int* channels, unsigned int* sampleRate, drflac_uint64* totalSampleCount); /* Use drflac_open_file_and_read_pcm_frames_s32(). */
+DRFLAC_DEPRECATED drflac_int16* drflac_open_and_decode_file_s16(const char* filename, unsigned int* channels, unsigned int* sampleRate, drflac_uint64* totalSampleCount); /* Use drflac_open_file_and_read_pcm_frames_s16(). */
+DRFLAC_DEPRECATED float* drflac_open_and_decode_file_f32(const char* filename, unsigned int* channels, unsigned int* sampleRate, drflac_uint64* totalSampleCount); /* Use drflac_open_file_and_read_pcm_frames_f32(). */
+DRFLAC_DEPRECATED drflac_int32* drflac_open_and_decode_memory_s32(const void* data, size_t dataSize, unsigned int* channels, unsigned int* sampleRate, drflac_uint64* totalSampleCount); /* Use drflac_open_memory_and_read_pcm_frames_s32(). */
+DRFLAC_DEPRECATED drflac_int16* drflac_open_and_decode_memory_s16(const void* data, size_t dataSize, unsigned int* channels, unsigned int* sampleRate, drflac_uint64* totalSampleCount); /* Use drflac_open_memory_and_read_pcm_frames_s16(). */
+DRFLAC_DEPRECATED float* drflac_open_and_decode_memory_f32(const void* data, size_t dataSize, unsigned int* channels, unsigned int* sampleRate, drflac_uint64* totalSampleCount); /* Use drflac_open_memory_and_read_pcm_frames_f32(). */
#ifdef __cplusplus
}
#endif
-#endif //dr_flac_h
+#endif /* dr_flac_h */
+
+/************************************************************************************************************************************************************
+ ************************************************************************************************************************************************************
-///////////////////////////////////////////////////////////////////////////////
-//
-// IMPLEMENTATION
-//
-///////////////////////////////////////////////////////////////////////////////
+ IMPLEMENTATION
+
+ ************************************************************************************************************************************************************
+ ************************************************************************************************************************************************************/
#ifdef DR_FLAC_IMPLEMENTATION
+
+/* Disable some annoying warnings. */
+#if defined(__GNUC__)
+ #pragma GCC diagnostic push
+ #if __GNUC__ >= 7
+ #pragma GCC diagnostic ignored "-Wimplicit-fallthrough"
+ #endif
+#endif
+
+#ifdef __linux__
+ #ifndef _BSD_SOURCE
+ #define _BSD_SOURCE
+ #endif
+ #ifndef __USE_BSD
+ #define __USE_BSD
+ #endif
+ #include <endian.h>
+#endif
+
#include <stdlib.h>
#include <string.h>
-// CPU architecture.
+#ifdef _MSC_VER
+#define DRFLAC_INLINE __forceinline
+#else
+#ifdef __GNUC__
+#define DRFLAC_INLINE __inline__ __attribute__((always_inline))
+#else
+#define DRFLAC_INLINE
+#endif
+#endif
+
+/* CPU architecture. */
#if defined(__x86_64__) || defined(_M_X64)
-#define DRFLAC_X64
+ #define DRFLAC_X64
#elif defined(__i386) || defined(_M_IX86)
-#define DRFLAC_X86
+ #define DRFLAC_X86
#elif defined(__arm__) || defined(_M_ARM)
-#define DRFLAC_ARM
+ #define DRFLAC_ARM
+#endif
+
+/* Intrinsics Support */
+#if !defined(DR_FLAC_NO_SIMD)
+ #if defined(DRFLAC_X64) || defined(DRFLAC_X86)
+ #if defined(_MSC_VER) && !defined(__clang__)
+ /* MSVC. */
+ #if _MSC_VER >= 1400 && !defined(DRFLAC_NO_SSE2) /* 2005 */
+ #define DRFLAC_SUPPORT_SSE2
+ #endif
+ #if _MSC_VER >= 1600 && !defined(DRFLAC_NO_SSE41) /* 2010 */
+ #define DRFLAC_SUPPORT_SSE41
+ #endif
+ #else
+ /* Assume GNUC-style. */
+ #if defined(__SSE2__) && !defined(DRFLAC_NO_SSE2)
+ #define DRFLAC_SUPPORT_SSE2
+ #endif
+ #if defined(__SSE4_1__) && !defined(DRFLAC_NO_SSE41)
+ #define DRFLAC_SUPPORT_SSE41
+ #endif
+ #endif
+
+ /* If at this point we still haven't determined compiler support for the intrinsics just fall back to __has_include. */
+ #if !defined(__GNUC__) && !defined(__clang__) && defined(__has_include)
+ #if !defined(DRFLAC_SUPPORT_SSE2) && !defined(DRFLAC_NO_SSE2) && __has_include(<emmintrin.h>)
+ #define DRFLAC_SUPPORT_SSE2
+ #endif
+ #if !defined(DRFLAC_SUPPORT_SSE41) && !defined(DRFLAC_NO_SSE41) && __has_include(<smmintrin.h>)
+ #define DRFLAC_SUPPORT_SSE41
+ #endif
+ #endif
+
+ #if defined(DRFLAC_SUPPORT_SSE41)
+ #include <smmintrin.h>
+ #elif defined(DRFLAC_SUPPORT_SSE2)
+ #include <emmintrin.h>
+ #endif
+ #endif
+
+ #if defined(DRFLAC_ARM)
+ #if !defined(DRFLAC_NO_NEON) && (defined(__ARM_NEON) || defined(__aarch64__) || defined(_M_ARM64))
+ #define DRFLAC_SUPPORT_NEON
+ #endif
+
+ /* Fall back to looking for the #include file. */
+ #if !defined(__GNUC__) && !defined(__clang__) && defined(__has_include)
+ #if !defined(DRFLAC_SUPPORT_NEON) && !defined(DRFLAC_NO_NEON) && __has_include(<arm_neon.h>)
+ #define DRFLAC_SUPPORT_NEON
+ #endif
+ #endif
+
+ #if defined(DRFLAC_SUPPORT_NEON)
+ #include <arm_neon.h>
+ #endif
+ #endif
#endif
-// Compile-time CPU feature support.
+/* Compile-time CPU feature support. */
#if !defined(DR_FLAC_NO_SIMD) && (defined(DRFLAC_X86) || defined(DRFLAC_X64))
#if defined(_MSC_VER) && !defined(__clang__)
#if _MSC_VER >= 1400
@@ -776,52 +961,127 @@ const char* drflac_next_vorbis_comment(drflac_vorbis_comment_iterator* pIter, dr
__cpuid(info, fid);
}
#else
- #define DRFLAC_NO_CPUID
+ #define DRFLAC_NO_CPUID
#endif
#else
#if defined(__GNUC__) || defined(__clang__)
static void drflac__cpuid(int info[4], int fid)
{
- __asm__ __volatile__ (
- "cpuid" : "=a"(info[0]), "=b"(info[1]), "=c"(info[2]), "=d"(info[3]) : "a"(fid), "c"(0)
- );
+ /*
+ It looks like the -fPIC option uses the ebx register which GCC complains about. We can work around this by just using a different register, the
+ specific register of which I'm letting the compiler decide on. The "k" prefix is used to specify a 32-bit register. The {...} syntax is for
+ supporting different assembly dialects.
+
+ What's basically happening is that we're saving and restoring the ebx register manually.
+ */
+ #if defined(DRFLAC_X86) && defined(__PIC__)
+ __asm__ __volatile__ (
+ "xchg{l} {%%}ebx, %k1;"
+ "cpuid;"
+ "xchg{l} {%%}ebx, %k1;"
+ : "=a"(info[0]), "=&r"(info[1]), "=c"(info[2]), "=d"(info[3]) : "a"(fid), "c"(0)
+ );
+ #else
+ __asm__ __volatile__ (
+ "cpuid" : "=a"(info[0]), "=b"(info[1]), "=c"(info[2]), "=d"(info[3]) : "a"(fid), "c"(0)
+ );
+ #endif
}
#else
- #define DRFLAC_NO_CPUID
+ #define DRFLAC_NO_CPUID
#endif
#endif
#else
-#define DRFLAC_NO_CPUID
+ #define DRFLAC_NO_CPUID
#endif
+static DRFLAC_INLINE drflac_bool32 drflac_has_sse2()
+{
+#if defined(DRFLAC_SUPPORT_SSE2)
+ #if (defined(DRFLAC_X64) || defined(DRFLAC_X86)) && !defined(DRFLAC_NO_SSE2)
+ #if defined(DRFLAC_X64)
+ return DRFLAC_TRUE; /* 64-bit targets always support SSE2. */
+ #elif (defined(_M_IX86_FP) && _M_IX86_FP == 2) || defined(__SSE2__)
+ return DRFLAC_TRUE; /* If the compiler is allowed to freely generate SSE2 code we can assume support. */
+ #else
+ #if defined(DRFLAC_NO_CPUID)
+ return DRFLAC_FALSE;
+ #else
+ int info[4];
+ drflac_cpuid(info, 1);
+ return (info[3] & (1 << 26)) != 0;
+ #endif
+ #endif
+ #else
+ return DRFLAC_FALSE; /* SSE2 is only supported on x86 and x64 architectures. */
+ #endif
+#else
+ return DRFLAC_FALSE; /* No compiler support. */
+#endif
+}
-#ifdef __linux__
-#define _BSD_SOURCE
-#include <endian.h>
+static DRFLAC_INLINE drflac_bool32 drflac_has_sse41()
+{
+#if defined(DRFLAC_SUPPORT_SSE41)
+ #if (defined(DRFLAC_X64) || defined(DRFLAC_X86)) && !defined(DRFLAC_NO_SSE41)
+ #if defined(DRFLAC_X64)
+ return DRFLAC_TRUE; /* 64-bit targets always support SSE4.1. */
+ #elif (defined(_M_IX86_FP) && _M_IX86_FP == 2) || defined(__SSE4_1__)
+ return DRFLAC_TRUE; /* If the compiler is allowed to freely generate SSE41 code we can assume support. */
+ #else
+ #if defined(DRFLAC_NO_CPUID)
+ return DRFLAC_FALSE;
+ #else
+ int info[4];
+ drflac_cpuid(info, 1);
+ return (info[2] & (1 << 19)) != 0;
+ #endif
+ #endif
+ #else
+ return DRFLAC_FALSE; /* SSE41 is only supported on x86 and x64 architectures. */
+ #endif
+#else
+ return DRFLAC_FALSE; /* No compiler support. */
#endif
+}
+
#if defined(_MSC_VER) && _MSC_VER >= 1500 && (defined(DRFLAC_X86) || defined(DRFLAC_X64))
-#define DRFLAC_HAS_LZCNT_INTRINSIC
+ #define DRFLAC_HAS_LZCNT_INTRINSIC
#elif (defined(__GNUC__) && ((__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 7)))
-#define DRFLAC_HAS_LZCNT_INTRINSIC
+ #define DRFLAC_HAS_LZCNT_INTRINSIC
#elif defined(__clang__)
#if __has_builtin(__builtin_clzll) || __has_builtin(__builtin_clzl)
- #define DRFLAC_HAS_LZCNT_INTRINSIC
+ #define DRFLAC_HAS_LZCNT_INTRINSIC
#endif
#endif
#if defined(_MSC_VER) && _MSC_VER >= 1300
-#define DRFLAC_HAS_BYTESWAP_INTRINSIC
-#elif defined(__GNUC__) && ((__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 3))
-#define DRFLAC_HAS_BYTESWAP_INTRINSIC
+ #define DRFLAC_HAS_BYTESWAP16_INTRINSIC
+ #define DRFLAC_HAS_BYTESWAP32_INTRINSIC
+ #define DRFLAC_HAS_BYTESWAP64_INTRINSIC
#elif defined(__clang__)
- #if __has_builtin(__builtin_bswap16) && __has_builtin(__builtin_bswap32) && __has_builtin(__builtin_bswap64)
- #define DRFLAC_HAS_BYTESWAP_INTRINSIC
+ #if __has_builtin(__builtin_bswap16)
+ #define DRFLAC_HAS_BYTESWAP16_INTRINSIC
+ #endif
+ #if __has_builtin(__builtin_bswap32)
+ #define DRFLAC_HAS_BYTESWAP32_INTRINSIC
+ #endif
+ #if __has_builtin(__builtin_bswap64)
+ #define DRFLAC_HAS_BYTESWAP64_INTRINSIC
+ #endif
+#elif defined(__GNUC__)
+ #if ((__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 3))
+ #define DRFLAC_HAS_BYTESWAP32_INTRINSIC
+ #define DRFLAC_HAS_BYTESWAP64_INTRINSIC
+ #endif
+ #if ((__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8))
+ #define DRFLAC_HAS_BYTESWAP16_INTRINSIC
#endif
#endif
-// Standard library stuff.
+/* Standard library stuff. */
#ifndef DRFLAC_ASSERT
#include <assert.h>
#define DRFLAC_ASSERT(expression) assert(expression)
@@ -842,21 +1102,11 @@ const char* drflac_next_vorbis_comment(drflac_vorbis_comment_iterator* pIter, dr
#define DRFLAC_ZERO_MEMORY(p, sz) memset((p), 0, (sz))
#endif
-#define DRFLAC_MAX_SIMD_VECTOR_SIZE 64 // 64 for AVX-512 in the future.
-
-#ifdef _MSC_VER
-#define DRFLAC_INLINE __forceinline
-#else
-#ifdef __GNUC__
-#define DRFLAC_INLINE inline __attribute__((always_inline))
-#else
-#define DRFLAC_INLINE inline
-#endif
-#endif
+#define DRFLAC_MAX_SIMD_VECTOR_SIZE 64 /* 64 for AVX-512 in the future. */
typedef drflac_int32 drflac_result;
#define DRFLAC_SUCCESS 0
-#define DRFLAC_ERROR -1 // A generic error.
+#define DRFLAC_ERROR -1 /* A generic error. */
#define DRFLAC_INVALID_ARGS -2
#define DRFLAC_END_OF_STREAM -128
#define DRFLAC_CRC_MISMATCH -129
@@ -875,6 +1125,12 @@ typedef drflac_int32 drflac_result;
#define DRFLAC_CHANNEL_ASSIGNMENT_RIGHT_SIDE 9
#define DRFLAC_CHANNEL_ASSIGNMENT_MID_SIDE 10
+/*
+Keeps track of the number of leading samples for each sub-frame. This is required because the SSE pipeline will occasionally
+reference excess prior samples.
+*/
+#define DRFLAC_LEADING_SAMPLES 32
+
#define drflac_align(x, a) ((((x) + (a) - 1) / (a)) * (a))
#define drflac_assert DRFLAC_ASSERT
@@ -882,30 +1138,35 @@ typedef drflac_int32 drflac_result;
#define drflac_zero_memory DRFLAC_ZERO_MEMORY
-// CPU caps.
+/* CPU caps. */
static drflac_bool32 drflac__gIsLZCNTSupported = DRFLAC_FALSE;
#ifndef DRFLAC_NO_CPUID
-static drflac_bool32 drflac__gIsSSE42Supported = DRFLAC_FALSE;
+static drflac_bool32 drflac__gIsSSE2Supported = DRFLAC_FALSE;
+static drflac_bool32 drflac__gIsSSE41Supported = DRFLAC_FALSE;
static void drflac__init_cpu_caps()
{
int info[4] = {0};
- // LZCNT
+ /* LZCNT */
drflac__cpuid(info, 0x80000001);
- drflac__gIsLZCNTSupported = (info[2] & (1 << 5)) != 0;
+ drflac__gIsLZCNTSupported = (info[2] & (1 << 5)) != 0;
+
+ /* SSE2 */
+ drflac__gIsSSE2Supported = drflac_has_sse2();
- // SSE4.2
- drflac__cpuid(info, 1);
- drflac__gIsSSE42Supported = (info[2] & (1 << 19)) != 0;
+ /* SSE4.1 */
+ drflac__gIsSSE41Supported = drflac_has_sse41();
}
#endif
-//// Endian Management ////
+/* Endian Management */
static DRFLAC_INLINE drflac_bool32 drflac__is_little_endian()
{
#if defined(DRFLAC_X86) || defined(DRFLAC_X64)
return DRFLAC_TRUE;
+#elif defined(__BYTE_ORDER) && defined(__LITTLE_ENDIAN) && __BYTE_ORDER == __LITTLE_ENDIAN
+ return DRFLAC_TRUE;
#else
int n = 1;
return (*(char*)&n) == 1;
@@ -914,7 +1175,7 @@ static DRFLAC_INLINE drflac_bool32 drflac__is_little_endian()
static DRFLAC_INLINE drflac_uint16 drflac__swap_endian_uint16(drflac_uint16 n)
{
-#ifdef DRFLAC_HAS_BYTESWAP_INTRINSIC
+#ifdef DRFLAC_HAS_BYTESWAP16_INTRINSIC
#if defined(_MSC_VER)
return _byteswap_ushort(n);
#elif defined(__GNUC__) || defined(__clang__)
@@ -930,7 +1191,7 @@ static DRFLAC_INLINE drflac_uint16 drflac__swap_endian_uint16(drflac_uint16 n)
static DRFLAC_INLINE drflac_uint32 drflac__swap_endian_uint32(drflac_uint32 n)
{
-#ifdef DRFLAC_HAS_BYTESWAP_INTRINSIC
+#ifdef DRFLAC_HAS_BYTESWAP32_INTRINSIC
#if defined(_MSC_VER)
return _byteswap_ulong(n);
#elif defined(__GNUC__) || defined(__clang__)
@@ -948,7 +1209,7 @@ static DRFLAC_INLINE drflac_uint32 drflac__swap_endian_uint32(drflac_uint32 n)
static DRFLAC_INLINE drflac_uint64 drflac__swap_endian_uint64(drflac_uint64 n)
{
-#ifdef DRFLAC_HAS_BYTESWAP_INTRINSIC
+#ifdef DRFLAC_HAS_BYTESWAP64_INTRINSIC
#if defined(_MSC_VER)
return _byteswap_uint64(n);
#elif defined(__GNUC__) || defined(__clang__)
@@ -971,55 +1232,39 @@ static DRFLAC_INLINE drflac_uint64 drflac__swap_endian_uint64(drflac_uint64 n)
static DRFLAC_INLINE drflac_uint16 drflac__be2host_16(drflac_uint16 n)
{
-#ifdef __linux__
- return be16toh(n);
-#else
if (drflac__is_little_endian()) {
return drflac__swap_endian_uint16(n);
}
return n;
-#endif
}
static DRFLAC_INLINE drflac_uint32 drflac__be2host_32(drflac_uint32 n)
{
-#ifdef __linux__
- return be32toh(n);
-#else
if (drflac__is_little_endian()) {
return drflac__swap_endian_uint32(n);
}
return n;
-#endif
}
static DRFLAC_INLINE drflac_uint64 drflac__be2host_64(drflac_uint64 n)
{
-#ifdef __linux__
- return be64toh(n);
-#else
if (drflac__is_little_endian()) {
return drflac__swap_endian_uint64(n);
}
return n;
-#endif
}
static DRFLAC_INLINE drflac_uint32 drflac__le2host_32(drflac_uint32 n)
{
-#ifdef __linux__
- return le32toh(n);
-#else
if (!drflac__is_little_endian()) {
return drflac__swap_endian_uint32(n);
}
return n;
-#endif
}
@@ -1036,7 +1281,7 @@ static DRFLAC_INLINE drflac_uint32 drflac__unsynchsafe_32(drflac_uint32 n)
-// The CRC code below is based on this document: http://zlib.net/crc_v3.txt
+/* The CRC code below is based on this document: http://zlib.net/crc_v3.txt */
static drflac_uint8 drflac__crc8_table[] = {
0x00, 0x07, 0x0E, 0x09, 0x1C, 0x1B, 0x12, 0x15, 0x38, 0x3F, 0x36, 0x31, 0x24, 0x23, 0x2A, 0x2D,
0x70, 0x77, 0x7E, 0x79, 0x6C, 0x6B, 0x62, 0x65, 0x48, 0x4F, 0x46, 0x41, 0x54, 0x53, 0x5A, 0x5D,
@@ -1098,8 +1343,6 @@ static DRFLAC_INLINE drflac_uint8 drflac_crc8_byte(drflac_uint8 crc, drflac_uint
static DRFLAC_INLINE drflac_uint8 drflac_crc8(drflac_uint8 crc, drflac_uint32 data, drflac_uint32 count)
{
- drflac_assert(count <= 32);
-
#ifdef DR_FLAC_NO_CRC
(void)crc;
(void)data;
@@ -1107,7 +1350,7 @@ static DRFLAC_INLINE drflac_uint8 drflac_crc8(drflac_uint8 crc, drflac_uint32 da
return 0;
#else
#if 0
- // REFERENCE (use of this implementation requires an explicit flush by doing "drflac_crc8(crc, 0, 8);")
+ /* REFERENCE (use of this implementation requires an explicit flush by doing "drflac_crc8(crc, 0, 8);") */
drflac_uint8 p = 0x07;
for (int i = count-1; i >= 0; --i) {
drflac_uint8 bit = (data & (1 << i)) >> i;
@@ -1119,13 +1362,19 @@ static DRFLAC_INLINE drflac_uint8 drflac_crc8(drflac_uint8 crc, drflac_uint32 da
}
return crc;
#else
- drflac_uint32 wholeBytes = count >> 3;
- drflac_uint32 leftoverBits = count - (wholeBytes*8);
+ drflac_uint32 wholeBytes;
+ drflac_uint32 leftoverBits;
+ drflac_uint64 leftoverDataMask;
static drflac_uint64 leftoverDataMaskTable[8] = {
0x00, 0x01, 0x03, 0x07, 0x0F, 0x1F, 0x3F, 0x7F
};
- drflac_uint64 leftoverDataMask = leftoverDataMaskTable[leftoverBits];
+
+ drflac_assert(count <= 32);
+
+ wholeBytes = count >> 3;
+ leftoverBits = count - (wholeBytes*8);
+ leftoverDataMask = leftoverDataMaskTable[leftoverBits];
switch (wholeBytes) {
case 4: crc = drflac_crc8_byte(crc, (drflac_uint8)((data & (0xFF000000UL << leftoverBits)) >> (24 + leftoverBits)));
@@ -1165,8 +1414,6 @@ static DRFLAC_INLINE drflac_uint16 drflac_crc16_bytes(drflac_uint16 crc, drflac_
static DRFLAC_INLINE drflac_uint16 drflac_crc16__32bit(drflac_uint16 crc, drflac_uint32 data, drflac_uint32 count)
{
- drflac_assert(count <= 64);
-
#ifdef DR_FLAC_NO_CRC
(void)crc;
(void)data;
@@ -1174,7 +1421,7 @@ static DRFLAC_INLINE drflac_uint16 drflac_crc16__32bit(drflac_uint16 crc, drflac
return 0;
#else
#if 0
- // REFERENCE (use of this implementation requires an explicit flush by doing "drflac_crc16(crc, 0, 16);")
+ /* REFERENCE (use of this implementation requires an explicit flush by doing "drflac_crc16(crc, 0, 16);") */
drflac_uint16 p = 0x8005;
for (int i = count-1; i >= 0; --i) {
drflac_uint16 bit = (data & (1ULL << i)) >> i;
@@ -1187,13 +1434,19 @@ static DRFLAC_INLINE drflac_uint16 drflac_crc16__32bit(drflac_uint16 crc, drflac
return crc;
#else
- drflac_uint32 wholeBytes = count >> 3;
- drflac_uint32 leftoverBits = count - (wholeBytes*8);
+ drflac_uint32 wholeBytes;
+ drflac_uint32 leftoverBits;
+ drflac_uint64 leftoverDataMask;
static drflac_uint64 leftoverDataMaskTable[8] = {
0x00, 0x01, 0x03, 0x07, 0x0F, 0x1F, 0x3F, 0x7F
};
- drflac_uint64 leftoverDataMask = leftoverDataMaskTable[leftoverBits];
+
+ drflac_assert(count <= 64);
+
+ wholeBytes = count >> 3;
+ leftoverBits = count - (wholeBytes*8);
+ leftoverDataMask = leftoverDataMaskTable[leftoverBits];
switch (wholeBytes) {
default:
@@ -1210,32 +1463,36 @@ static DRFLAC_INLINE drflac_uint16 drflac_crc16__32bit(drflac_uint16 crc, drflac
static DRFLAC_INLINE drflac_uint16 drflac_crc16__64bit(drflac_uint16 crc, drflac_uint64 data, drflac_uint32 count)
{
- drflac_assert(count <= 64);
-
#ifdef DR_FLAC_NO_CRC
(void)crc;
(void)data;
(void)count;
return 0;
#else
- drflac_uint32 wholeBytes = count >> 3;
- drflac_uint32 leftoverBits = count - (wholeBytes*8);
+ drflac_uint32 wholeBytes;
+ drflac_uint32 leftoverBits;
+ drflac_uint64 leftoverDataMask;
static drflac_uint64 leftoverDataMaskTable[8] = {
0x00, 0x01, 0x03, 0x07, 0x0F, 0x1F, 0x3F, 0x7F
};
- drflac_uint64 leftoverDataMask = leftoverDataMaskTable[leftoverBits];
+
+ drflac_assert(count <= 64);
+
+ wholeBytes = count >> 3;
+ leftoverBits = count - (wholeBytes*8);
+ leftoverDataMask = leftoverDataMaskTable[leftoverBits];
switch (wholeBytes) {
default:
- case 8: crc = drflac_crc16_byte(crc, (drflac_uint8)((data & ((drflac_uint64)0xFF00000000000000 << leftoverBits)) >> (56 + leftoverBits)));
- case 7: crc = drflac_crc16_byte(crc, (drflac_uint8)((data & ((drflac_uint64)0x00FF000000000000 << leftoverBits)) >> (48 + leftoverBits)));
- case 6: crc = drflac_crc16_byte(crc, (drflac_uint8)((data & ((drflac_uint64)0x0000FF0000000000 << leftoverBits)) >> (40 + leftoverBits)));
- case 5: crc = drflac_crc16_byte(crc, (drflac_uint8)((data & ((drflac_uint64)0x000000FF00000000 << leftoverBits)) >> (32 + leftoverBits)));
- case 4: crc = drflac_crc16_byte(crc, (drflac_uint8)((data & ((drflac_uint64)0x00000000FF000000 << leftoverBits)) >> (24 + leftoverBits)));
- case 3: crc = drflac_crc16_byte(crc, (drflac_uint8)((data & ((drflac_uint64)0x0000000000FF0000 << leftoverBits)) >> (16 + leftoverBits)));
- case 2: crc = drflac_crc16_byte(crc, (drflac_uint8)((data & ((drflac_uint64)0x000000000000FF00 << leftoverBits)) >> ( 8 + leftoverBits)));
- case 1: crc = drflac_crc16_byte(crc, (drflac_uint8)((data & ((drflac_uint64)0x00000000000000FF << leftoverBits)) >> ( 0 + leftoverBits)));
+ case 8: crc = drflac_crc16_byte(crc, (drflac_uint8)((data & (((drflac_uint64)0xFF000000 << 32) << leftoverBits)) >> (56 + leftoverBits))); /* Weird "<< 32" bitshift is required for C89 because it doesn't support 64-bit constants. Should be optimized out by a good compiler. */
+ case 7: crc = drflac_crc16_byte(crc, (drflac_uint8)((data & (((drflac_uint64)0x00FF0000 << 32) << leftoverBits)) >> (48 + leftoverBits)));
+ case 6: crc = drflac_crc16_byte(crc, (drflac_uint8)((data & (((drflac_uint64)0x0000FF00 << 32) << leftoverBits)) >> (40 + leftoverBits)));
+ case 5: crc = drflac_crc16_byte(crc, (drflac_uint8)((data & (((drflac_uint64)0x000000FF << 32) << leftoverBits)) >> (32 + leftoverBits)));
+ case 4: crc = drflac_crc16_byte(crc, (drflac_uint8)((data & (((drflac_uint64)0xFF000000 ) << leftoverBits)) >> (24 + leftoverBits)));
+ case 3: crc = drflac_crc16_byte(crc, (drflac_uint8)((data & (((drflac_uint64)0x00FF0000 ) << leftoverBits)) >> (16 + leftoverBits)));
+ case 2: crc = drflac_crc16_byte(crc, (drflac_uint8)((data & (((drflac_uint64)0x0000FF00 ) << leftoverBits)) >> ( 8 + leftoverBits)));
+ case 1: crc = drflac_crc16_byte(crc, (drflac_uint8)((data & (((drflac_uint64)0x000000FF ) << leftoverBits)) >> ( 0 + leftoverBits)));
case 0: if (leftoverBits > 0) crc = (crc << leftoverBits) ^ drflac__crc16_table[(crc >> (16 - leftoverBits)) ^ (data & leftoverDataMask)];
}
return crc;
@@ -1259,27 +1516,26 @@ static DRFLAC_INLINE drflac_uint16 drflac_crc16(drflac_uint16 crc, drflac_cache_
#define drflac__be2host__cache_line drflac__be2host_32
#endif
-// BIT READING ATTEMPT #2
-//
-// This uses a 32- or 64-bit bit-shifted cache - as bits are read, the cache is shifted such that the first valid bit is sitting
-// on the most significant bit. It uses the notion of an L1 and L2 cache (borrowed from CPU architecture), where the L1 cache
-// is a 32- or 64-bit unsigned integer (depending on whether or not a 32- or 64-bit build is being compiled) and the L2 is an
-// array of "cache lines", with each cache line being the same size as the L1. The L2 is a buffer of about 4KB and is where data
-// from onRead() is read into.
-#define DRFLAC_CACHE_L1_SIZE_BYTES(bs) (sizeof((bs)->cache))
-#define DRFLAC_CACHE_L1_SIZE_BITS(bs) (sizeof((bs)->cache)*8)
-#define DRFLAC_CACHE_L1_BITS_REMAINING(bs) (DRFLAC_CACHE_L1_SIZE_BITS(bs) - ((bs)->consumedBits))
-#ifdef DRFLAC_64BIT
-#define DRFLAC_CACHE_L1_SELECTION_MASK(_bitCount) (~(((drflac_uint64)-1LL) >> (_bitCount)))
-#else
-#define DRFLAC_CACHE_L1_SELECTION_MASK(_bitCount) (~(((drflac_uint32)-1) >> (_bitCount)))
-#endif
-#define DRFLAC_CACHE_L1_SELECTION_SHIFT(bs, _bitCount) (DRFLAC_CACHE_L1_SIZE_BITS(bs) - (_bitCount))
-#define DRFLAC_CACHE_L1_SELECT(bs, _bitCount) (((bs)->cache) & DRFLAC_CACHE_L1_SELECTION_MASK(_bitCount))
-#define DRFLAC_CACHE_L1_SELECT_AND_SHIFT(bs, _bitCount) (DRFLAC_CACHE_L1_SELECT((bs), _bitCount) >> DRFLAC_CACHE_L1_SELECTION_SHIFT((bs), _bitCount))
-#define DRFLAC_CACHE_L2_SIZE_BYTES(bs) (sizeof((bs)->cacheL2))
-#define DRFLAC_CACHE_L2_LINE_COUNT(bs) (DRFLAC_CACHE_L2_SIZE_BYTES(bs) / sizeof((bs)->cacheL2[0]))
-#define DRFLAC_CACHE_L2_LINES_REMAINING(bs) (DRFLAC_CACHE_L2_LINE_COUNT(bs) - (bs)->nextL2Line)
+/*
+BIT READING ATTEMPT #2
+
+This uses a 32- or 64-bit bit-shifted cache - as bits are read, the cache is shifted such that the first valid bit is sitting
+on the most significant bit. It uses the notion of an L1 and L2 cache (borrowed from CPU architecture), where the L1 cache
+is a 32- or 64-bit unsigned integer (depending on whether or not a 32- or 64-bit build is being compiled) and the L2 is an
+array of "cache lines", with each cache line being the same size as the L1. The L2 is a buffer of about 4KB and is where data
+from onRead() is read into.
+*/
+#define DRFLAC_CACHE_L1_SIZE_BYTES(bs) (sizeof((bs)->cache))
+#define DRFLAC_CACHE_L1_SIZE_BITS(bs) (sizeof((bs)->cache)*8)
+#define DRFLAC_CACHE_L1_BITS_REMAINING(bs) (DRFLAC_CACHE_L1_SIZE_BITS(bs) - (bs)->consumedBits)
+#define DRFLAC_CACHE_L1_SELECTION_MASK(_bitCount) (~((~(drflac_cache_t)0) >> (_bitCount)))
+#define DRFLAC_CACHE_L1_SELECTION_SHIFT(bs, _bitCount) (DRFLAC_CACHE_L1_SIZE_BITS(bs) - (_bitCount))
+#define DRFLAC_CACHE_L1_SELECT(bs, _bitCount) (((bs)->cache) & DRFLAC_CACHE_L1_SELECTION_MASK(_bitCount))
+#define DRFLAC_CACHE_L1_SELECT_AND_SHIFT(bs, _bitCount) (DRFLAC_CACHE_L1_SELECT((bs), (_bitCount)) >> DRFLAC_CACHE_L1_SELECTION_SHIFT((bs), (_bitCount)))
+#define DRFLAC_CACHE_L1_SELECT_AND_SHIFT_SAFE(bs, _bitCount)(DRFLAC_CACHE_L1_SELECT((bs), (_bitCount)) >> (DRFLAC_CACHE_L1_SELECTION_SHIFT((bs), (_bitCount)) & (DRFLAC_CACHE_L1_SIZE_BITS(bs)-1)))
+#define DRFLAC_CACHE_L2_SIZE_BYTES(bs) (sizeof((bs)->cacheL2))
+#define DRFLAC_CACHE_L2_LINE_COUNT(bs) (DRFLAC_CACHE_L2_SIZE_BYTES(bs) / sizeof((bs)->cacheL2[0]))
+#define DRFLAC_CACHE_L2_LINES_REMAINING(bs) (DRFLAC_CACHE_L2_LINE_COUNT(bs) - (bs)->nextL2Line)
#ifndef DR_FLAC_NO_CRC
@@ -1297,19 +1553,23 @@ static DRFLAC_INLINE void drflac__update_crc16(drflac_bs* bs)
static DRFLAC_INLINE drflac_uint16 drflac__flush_crc16(drflac_bs* bs)
{
- // We should never be flushing in a situation where we are not aligned on a byte boundary.
+ /* We should never be flushing in a situation where we are not aligned on a byte boundary. */
drflac_assert((DRFLAC_CACHE_L1_BITS_REMAINING(bs) & 7) == 0);
- // The bits that were read from the L1 cache need to be accumulated. The number of bytes needing to be accumulated is determined
- // by the number of bits that have been consumed.
+ /*
+ The bits that were read from the L1 cache need to be accumulated. The number of bytes needing to be accumulated is determined
+ by the number of bits that have been consumed.
+ */
if (DRFLAC_CACHE_L1_BITS_REMAINING(bs) == 0) {
drflac__update_crc16(bs);
} else {
- // We only accumulate the consumed bits.
+ /* We only accumulate the consumed bits. */
bs->crc16 = drflac_crc16_bytes(bs->crc16, bs->crc16Cache >> DRFLAC_CACHE_L1_BITS_REMAINING(bs), (bs->consumedBits >> 3) - bs->crc16CacheIgnoredBytes);
- // The bits that we just accumulated should never be accumulated again. We need to keep track of how many bytes were accumulated
- // so we can handle that later.
+ /*
+ The bits that we just accumulated should never be accumulated again. We need to keep track of how many bytes were accumulated
+ so we can handle that later.
+ */
bs->crc16CacheIgnoredBytes = bs->consumedBits >> 3;
}
@@ -1319,19 +1579,24 @@ static DRFLAC_INLINE drflac_uint16 drflac__flush_crc16(drflac_bs* bs)
static DRFLAC_INLINE drflac_bool32 drflac__reload_l1_cache_from_l2(drflac_bs* bs)
{
- // Fast path. Try loading straight from L2.
+ size_t bytesRead;
+ size_t alignedL1LineCount;
+
+ /* Fast path. Try loading straight from L2. */
if (bs->nextL2Line < DRFLAC_CACHE_L2_LINE_COUNT(bs)) {
bs->cache = bs->cacheL2[bs->nextL2Line++];
return DRFLAC_TRUE;
}
- // If we get here it means we've run out of data in the L2 cache. We'll need to fetch more from the client, if there's
- // any left.
+ /*
+ If we get here it means we've run out of data in the L2 cache. We'll need to fetch more from the client, if there's
+ any left.
+ */
if (bs->unalignedByteCount > 0) {
- return DRFLAC_FALSE; // If we have any unaligned bytes it means there's no more aligned bytes left in the client.
+ return DRFLAC_FALSE; /* If we have any unaligned bytes it means there's no more aligned bytes left in the client. */
}
- size_t bytesRead = bs->onRead(bs->pUserData, bs->cacheL2, DRFLAC_CACHE_L2_SIZE_BYTES(bs));
+ bytesRead = bs->onRead(bs->pUserData, bs->cacheL2, DRFLAC_CACHE_L2_SIZE_BYTES(bs));
bs->nextL2Line = 0;
if (bytesRead == DRFLAC_CACHE_L2_SIZE_BYTES(bs)) {
@@ -1340,13 +1605,15 @@ static DRFLAC_INLINE drflac_bool32 drflac__reload_l1_cache_from_l2(drflac_bs* bs
}
- // If we get here it means we were unable to retrieve enough data to fill the entire L2 cache. It probably
- // means we've just reached the end of the file. We need to move the valid data down to the end of the buffer
- // and adjust the index of the next line accordingly. Also keep in mind that the L2 cache must be aligned to
- // the size of the L1 so we'll need to seek backwards by any misaligned bytes.
- size_t alignedL1LineCount = bytesRead / DRFLAC_CACHE_L1_SIZE_BYTES(bs);
+ /*
+ If we get here it means we were unable to retrieve enough data to fill the entire L2 cache. It probably
+ means we've just reached the end of the file. We need to move the valid data down to the end of the buffer
+ and adjust the index of the next line accordingly. Also keep in mind that the L2 cache must be aligned to
+ the size of the L1 so we'll need to seek backwards by any misaligned bytes.
+ */
+ alignedL1LineCount = bytesRead / DRFLAC_CACHE_L1_SIZE_BYTES(bs);
- // We need to keep track of any unaligned bytes for later use.
+ /* We need to keep track of any unaligned bytes for later use. */
bs->unalignedByteCount = bytesRead - (alignedL1LineCount * DRFLAC_CACHE_L1_SIZE_BYTES(bs));
if (bs->unalignedByteCount > 0) {
bs->unalignedCache = bs->cacheL2[alignedL1LineCount];
@@ -1354,7 +1621,8 @@ static DRFLAC_INLINE drflac_bool32 drflac__reload_l1_cache_from_l2(drflac_bs* bs
if (alignedL1LineCount > 0) {
size_t offset = DRFLAC_CACHE_L2_LINE_COUNT(bs) - alignedL1LineCount;
- for (size_t i = alignedL1LineCount; i > 0; --i) {
+ size_t i;
+ for (i = alignedL1LineCount; i > 0; --i) {
bs->cacheL2[i-1 + offset] = bs->cacheL2[i-1];
}
@@ -1362,7 +1630,7 @@ static DRFLAC_INLINE drflac_bool32 drflac__reload_l1_cache_from_l2(drflac_bs* bs
bs->cache = bs->cacheL2[bs->nextL2Line++];
return DRFLAC_TRUE;
} else {
- // If we get into this branch it means we weren't able to load any L1-aligned data.
+ /* If we get into this branch it means we weren't able to load any L1-aligned data. */
bs->nextL2Line = DRFLAC_CACHE_L2_LINE_COUNT(bs);
return DRFLAC_FALSE;
}
@@ -1370,11 +1638,13 @@ static DRFLAC_INLINE drflac_bool32 drflac__reload_l1_cache_from_l2(drflac_bs* bs
static drflac_bool32 drflac__reload_cache(drflac_bs* bs)
{
+ size_t bytesRead;
+
#ifndef DR_FLAC_NO_CRC
drflac__update_crc16(bs);
#endif
- // Fast path. Try just moving the next value in the L2 cache to the L1 cache.
+ /* Fast path. Try just moving the next value in the L2 cache to the L1 cache. */
if (drflac__reload_l1_cache_from_l2(bs)) {
bs->cache = drflac__be2host__cache_line(bs->cache);
bs->consumedBits = 0;
@@ -1384,13 +1654,16 @@ static drflac_bool32 drflac__reload_cache(drflac_bs* bs)
return DRFLAC_TRUE;
}
- // Slow path.
+ /* Slow path. */
- // If we get here it means we have failed to load the L1 cache from the L2. Likely we've just reached the end of the stream and the last
- // few bytes did not meet the alignment requirements for the L2 cache. In this case we need to fall back to a slower path and read the
- // data from the unaligned cache.
- size_t bytesRead = bs->unalignedByteCount;
+ /*
+ If we get here it means we have failed to load the L1 cache from the L2. Likely we've just reached the end of the stream and the last
+ few bytes did not meet the alignment requirements for the L2 cache. In this case we need to fall back to a slower path and read the
+ data from the unaligned cache.
+ */
+ bytesRead = bs->unalignedByteCount;
if (bytesRead == 0) {
+ bs->consumedBits = DRFLAC_CACHE_L1_SIZE_BITS(bs); /* <-- The stream has been exhausted, so marked the bits as consumed. */
return DRFLAC_FALSE;
}
@@ -1398,8 +1671,8 @@ static drflac_bool32 drflac__reload_cache(drflac_bs* bs)
bs->consumedBits = (drflac_uint32)(DRFLAC_CACHE_L1_SIZE_BYTES(bs) - bytesRead) * 8;
bs->cache = drflac__be2host__cache_line(bs->unalignedCache);
- bs->cache &= DRFLAC_CACHE_L1_SELECTION_MASK(DRFLAC_CACHE_L1_SIZE_BITS(bs) - bs->consumedBits); // <-- Make sure the consumed bits are always set to zero. Other parts of the library depend on this property.
- bs->unalignedByteCount = 0; // <-- At this point the unaligned bytes have been moved into the cache and we thus have no more unaligned bytes.
+ bs->cache &= DRFLAC_CACHE_L1_SELECTION_MASK(DRFLAC_CACHE_L1_BITS_REMAINING(bs)); /* <-- Make sure the consumed bits are always set to zero. Other parts of the library depend on this property. */
+ bs->unalignedByteCount = 0; /* <-- At this point the unaligned bytes have been moved into the cache and we thus have no more unaligned bytes. */
#ifndef DR_FLAC_NO_CRC
bs->crc16Cache = bs->cache >> bs->consumedBits;
@@ -1410,10 +1683,10 @@ static drflac_bool32 drflac__reload_cache(drflac_bs* bs)
static void drflac__reset_cache(drflac_bs* bs)
{
- bs->nextL2Line = DRFLAC_CACHE_L2_LINE_COUNT(bs); // <-- This clears the L2 cache.
- bs->consumedBits = DRFLAC_CACHE_L1_SIZE_BITS(bs); // <-- This clears the L1 cache.
+ bs->nextL2Line = DRFLAC_CACHE_L2_LINE_COUNT(bs); /* <-- This clears the L2 cache. */
+ bs->consumedBits = DRFLAC_CACHE_L1_SIZE_BITS(bs); /* <-- This clears the L1 cache. */
bs->cache = 0;
- bs->unalignedByteCount = 0; // <-- This clears the trailing unaligned bytes.
+ bs->unalignedByteCount = 0; /* <-- This clears the trailing unaligned bytes. */
bs->unalignedCache = 0;
#ifndef DR_FLAC_NO_CRC
@@ -1437,18 +1710,31 @@ static DRFLAC_INLINE drflac_bool32 drflac__read_uint32(drflac_bs* bs, unsigned i
}
if (bitCount <= DRFLAC_CACHE_L1_BITS_REMAINING(bs)) {
+ /*
+ If we want to load all 32-bits from a 32-bit cache we need to do it slightly differently because we can't do
+ a 32-bit shift on a 32-bit integer. This will never be the case on 64-bit caches, so we can have a slightly
+ more optimal solution for this.
+ */
+#ifdef DRFLAC_64BIT
+ *pResultOut = (drflac_uint32)DRFLAC_CACHE_L1_SELECT_AND_SHIFT(bs, bitCount);
+ bs->consumedBits += bitCount;
+ bs->cache <<= bitCount;
+#else
if (bitCount < DRFLAC_CACHE_L1_SIZE_BITS(bs)) {
*pResultOut = (drflac_uint32)DRFLAC_CACHE_L1_SELECT_AND_SHIFT(bs, bitCount);
bs->consumedBits += bitCount;
bs->cache <<= bitCount;
} else {
+ /* Cannot shift by 32-bits, so need to do it differently. */
*pResultOut = (drflac_uint32)bs->cache;
bs->consumedBits = DRFLAC_CACHE_L1_SIZE_BITS(bs);
bs->cache = 0;
}
+#endif
+
return DRFLAC_TRUE;
} else {
- // 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 bitCountLo = bitCount - bitCountHi;
drflac_uint32 resultHi = (drflac_uint32)DRFLAC_CACHE_L1_SELECT_AND_SHIFT(bs, bitCountHi);
@@ -1466,17 +1752,19 @@ static DRFLAC_INLINE drflac_bool32 drflac__read_uint32(drflac_bs* bs, unsigned i
static drflac_bool32 drflac__read_int32(drflac_bs* bs, unsigned int bitCount, drflac_int32* pResult)
{
+ drflac_uint32 result;
+ drflac_uint32 signbit;
+
drflac_assert(bs != NULL);
drflac_assert(pResult != NULL);
drflac_assert(bitCount > 0);
drflac_assert(bitCount <= 32);
- drflac_uint32 result;
if (!drflac__read_uint32(bs, bitCount, &result)) {
return DRFLAC_FALSE;
}
- drflac_uint32 signbit = ((result >> (bitCount-1)) & 0x01);
+ signbit = ((result >> (bitCount-1)) & 0x01);
result |= (~signbit + 1) << bitCount;
*pResult = (drflac_int32)result;
@@ -1486,15 +1774,16 @@ static drflac_bool32 drflac__read_int32(drflac_bs* bs, unsigned int bitCount, dr
#ifdef DRFLAC_64BIT
static drflac_bool32 drflac__read_uint64(drflac_bs* bs, unsigned int bitCount, drflac_uint64* pResultOut)
{
+ drflac_uint32 resultHi;
+ drflac_uint32 resultLo;
+
drflac_assert(bitCount <= 64);
drflac_assert(bitCount > 32);
- drflac_uint32 resultHi;
if (!drflac__read_uint32(bs, bitCount - 32, &resultHi)) {
return DRFLAC_FALSE;
}
- drflac_uint32 resultLo;
if (!drflac__read_uint32(bs, 32, &resultLo)) {
return DRFLAC_FALSE;
}
@@ -1504,18 +1793,20 @@ static drflac_bool32 drflac__read_uint64(drflac_bs* bs, unsigned int bitCount, d
}
#endif
-// Function below is unused, but leaving it here in case I need to quickly add it again.
+/* Function below is unused, but leaving it here in case I need to quickly add it again. */
#if 0
static drflac_bool32 drflac__read_int64(drflac_bs* bs, unsigned int bitCount, drflac_int64* pResultOut)
{
+ drflac_uint64 result;
+ drflac_uint64 signbit;
+
drflac_assert(bitCount <= 64);
- drflac_uint64 result;
if (!drflac__read_uint64(bs, bitCount, &result)) {
return DRFLAC_FALSE;
}
- drflac_uint64 signbit = ((result >> (bitCount-1)) & 0x01);
+ signbit = ((result >> (bitCount-1)) & 0x01);
result |= (~signbit + 1) << bitCount;
*pResultOut = (drflac_int64)result;
@@ -1525,12 +1816,13 @@ static drflac_bool32 drflac__read_int64(drflac_bs* bs, unsigned int bitCount, dr
static drflac_bool32 drflac__read_uint16(drflac_bs* bs, unsigned int bitCount, drflac_uint16* pResult)
{
+ drflac_uint32 result;
+
drflac_assert(bs != NULL);
drflac_assert(pResult != NULL);
drflac_assert(bitCount > 0);
drflac_assert(bitCount <= 16);
- drflac_uint32 result;
if (!drflac__read_uint32(bs, bitCount, &result)) {
return DRFLAC_FALSE;
}
@@ -1542,12 +1834,13 @@ static drflac_bool32 drflac__read_uint16(drflac_bs* bs, unsigned int bitCount, d
#if 0
static drflac_bool32 drflac__read_int16(drflac_bs* bs, unsigned int bitCount, drflac_int16* pResult)
{
+ drflac_int32 result;
+
drflac_assert(bs != NULL);
drflac_assert(pResult != NULL);
drflac_assert(bitCount > 0);
drflac_assert(bitCount <= 16);
- drflac_int32 result;
if (!drflac__read_int32(bs, bitCount, &result)) {
return DRFLAC_FALSE;
}
@@ -1559,12 +1852,13 @@ static drflac_bool32 drflac__read_int16(drflac_bs* bs, unsigned int bitCount, dr
static drflac_bool32 drflac__read_uint8(drflac_bs* bs, unsigned int bitCount, drflac_uint8* pResult)
{
+ drflac_uint32 result;
+
drflac_assert(bs != NULL);
drflac_assert(pResult != NULL);
drflac_assert(bitCount > 0);
drflac_assert(bitCount <= 8);
- drflac_uint32 result;
if (!drflac__read_uint32(bs, bitCount, &result)) {
return DRFLAC_FALSE;
}
@@ -1575,12 +1869,13 @@ static drflac_bool32 drflac__read_uint8(drflac_bs* bs, unsigned int bitCount, dr
static drflac_bool32 drflac__read_int8(drflac_bs* bs, unsigned int bitCount, drflac_int8* pResult)
{
+ drflac_int32 result;
+
drflac_assert(bs != NULL);
drflac_assert(pResult != NULL);
drflac_assert(bitCount > 0);
drflac_assert(bitCount <= 8);
- drflac_int32 result;
if (!drflac__read_int32(bs, bitCount, &result)) {
return DRFLAC_FALSE;
}
@@ -1597,12 +1892,12 @@ static drflac_bool32 drflac__seek_bits(drflac_bs* bs, size_t bitsToSeek)
bs->cache <<= bitsToSeek;
return DRFLAC_TRUE;
} else {
- // It straddles the cached data. This function isn't called too frequently so I'm favouring simplicity here.
+ /* It straddles the cached data. This function isn't called too frequently so I'm favouring simplicity here. */
bitsToSeek -= DRFLAC_CACHE_L1_BITS_REMAINING(bs);
bs->consumedBits += DRFLAC_CACHE_L1_BITS_REMAINING(bs);
bs->cache = 0;
- // Simple case. Seek in groups of the same number as bits that fit within a cache line.
+ /* Simple case. Seek in groups of the same number as bits that fit within a cache line. */
#ifdef DRFLAC_64BIT
while (bitsToSeek >= DRFLAC_CACHE_L1_SIZE_BITS(bs)) {
drflac_uint64 bin;
@@ -1621,7 +1916,7 @@ static drflac_bool32 drflac__seek_bits(drflac_bs* bs, size_t bitsToSeek)
}
#endif
- // Whole leftover bytes.
+ /* Whole leftover bytes. */
while (bitsToSeek >= 8) {
drflac_uint8 bin;
if (!drflac__read_uint8(bs, 8, &bin)) {
@@ -1630,13 +1925,13 @@ static drflac_bool32 drflac__seek_bits(drflac_bs* bs, size_t bitsToSeek)
bitsToSeek -= 8;
}
- // Leftover bits.
+ /* Leftover bits. */
if (bitsToSeek > 0) {
drflac_uint8 bin;
if (!drflac__read_uint8(bs, (drflac_uint32)bitsToSeek, &bin)) {
return DRFLAC_FALSE;
}
- bitsToSeek = 0; // <-- Necessary for the assert below.
+ bitsToSeek = 0; /* <-- Necessary for the assert below. */
}
drflac_assert(bitsToSeek == 0);
@@ -1645,23 +1940,26 @@ static drflac_bool32 drflac__seek_bits(drflac_bs* bs, size_t bitsToSeek)
}
-// This function moves the bit streamer to the first bit after the sync code (bit 15 of the of the frame header). It will also update the CRC-16.
+/* This function moves the bit streamer to the first bit after the sync code (bit 15 of the of the frame header). It will also update the CRC-16. */
static drflac_bool32 drflac__find_and_seek_to_next_sync_code(drflac_bs* bs)
{
drflac_assert(bs != NULL);
- // The sync code is always aligned to 8 bits. This is convenient for us because it means we can do byte-aligned movements. The first
- // thing to do is align to the next byte.
+ /*
+ The sync code is always aligned to 8 bits. This is convenient for us because it means we can do byte-aligned movements. The first
+ thing to do is align to the next byte.
+ */
if (!drflac__seek_bits(bs, DRFLAC_CACHE_L1_BITS_REMAINING(bs) & 7)) {
return DRFLAC_FALSE;
}
for (;;) {
+ drflac_uint8 hi;
+
#ifndef DR_FLAC_NO_CRC
drflac__reset_crc16(bs);
#endif
- drflac_uint8 hi;
if (!drflac__read_uint8(bs, 8, &hi)) {
return DRFLAC_FALSE;
}
@@ -1682,8 +1980,8 @@ static drflac_bool32 drflac__find_and_seek_to_next_sync_code(drflac_bs* bs)
}
}
- // Should never get here.
- //return DRFLAC_FALSE;
+ /* Should never get here. */
+ /*return DRFLAC_FALSE;*/
}
@@ -1696,6 +1994,7 @@ static drflac_bool32 drflac__find_and_seek_to_next_sync_code(drflac_bs* bs)
static DRFLAC_INLINE drflac_uint32 drflac__clz_software(drflac_cache_t x)
{
+ drflac_uint32 n;
static drflac_uint32 clz_table_4[] = {
0,
4,
@@ -1704,13 +2003,17 @@ static DRFLAC_INLINE drflac_uint32 drflac__clz_software(drflac_cache_t x)
1, 1, 1, 1, 1, 1, 1, 1
};
- drflac_uint32 n = clz_table_4[x >> (sizeof(x)*8 - 4)];
+ if (x == 0) {
+ return sizeof(x)*8;
+ }
+
+ n = clz_table_4[x >> (sizeof(x)*8 - 4)];
if (n == 0) {
#ifdef DRFLAC_64BIT
- if ((x & 0xFFFFFFFF00000000ULL) == 0) { n = 32; x <<= 32; }
- if ((x & 0xFFFF000000000000ULL) == 0) { n += 16; x <<= 16; }
- if ((x & 0xFF00000000000000ULL) == 0) { n += 8; x <<= 8; }
- if ((x & 0xF000000000000000ULL) == 0) { n += 4; x <<= 4; }
+ if ((x & ((drflac_uint64)0xFFFFFFFF << 32)) == 0) { n = 32; x <<= 32; }
+ if ((x & ((drflac_uint64)0xFFFF0000 << 32)) == 0) { n += 16; x <<= 16; }
+ if ((x & ((drflac_uint64)0xFF000000 << 32)) == 0) { n += 8; x <<= 8; }
+ if ((x & ((drflac_uint64)0xF0000000 << 32)) == 0) { n += 4; x <<= 4; }
#else
if ((x & 0xFFFF0000) == 0) { n = 16; x <<= 16; }
if ((x & 0xFF000000) == 0) { n += 8; x <<= 8; }
@@ -1725,7 +2028,7 @@ static DRFLAC_INLINE drflac_uint32 drflac__clz_software(drflac_cache_t x)
#ifdef DRFLAC_IMPLEMENT_CLZ_LZCNT
static DRFLAC_INLINE drflac_bool32 drflac__is_lzcnt_supported()
{
- // If the compiler itself does not support the intrinsic then we'll need to return false.
+ /* If the compiler itself does not support the intrinsic then we'll need to return false. */
#ifdef DRFLAC_HAS_LZCNT_INTRINSIC
return drflac__gIsLZCNTSupported;
#else
@@ -1743,13 +2046,16 @@ static DRFLAC_INLINE drflac_uint32 drflac__clz_lzcnt(drflac_cache_t x)
#endif
#else
#if defined(__GNUC__) || defined(__clang__)
+ if (x == 0) {
+ return sizeof(x)*8;
+ }
#ifdef DRFLAC_64BIT
- return (drflac_uint32)__builtin_clzll((unsigned long long)x);
+ return (drflac_uint32)__builtin_clzll((drflac_uint64)x);
#else
- return (drflac_uint32)__builtin_clzl((unsigned long)x);
+ return (drflac_uint32)__builtin_clzl((drflac_uint32)x);
#endif
#else
- // Unsupported compiler.
+ /* Unsupported compiler. */
#error "This compiler does not support the lzcnt intrinsic."
#endif
#endif
@@ -1757,9 +2063,16 @@ static DRFLAC_INLINE drflac_uint32 drflac__clz_lzcnt(drflac_cache_t x)
#endif
#ifdef DRFLAC_IMPLEMENT_CLZ_MSVC
+#include <intrin.h> /* For BitScanReverse(). */
+
static DRFLAC_INLINE drflac_uint32 drflac__clz_msvc(drflac_cache_t x)
{
drflac_uint32 n;
+
+ if (x == 0) {
+ return sizeof(x)*8;
+ }
+
#ifdef DRFLAC_64BIT
_BitScanReverse64((unsigned long*)&n, x);
#else
@@ -1771,25 +2084,26 @@ static DRFLAC_INLINE drflac_uint32 drflac__clz_msvc(drflac_cache_t x)
static DRFLAC_INLINE drflac_uint32 drflac__clz(drflac_cache_t x)
{
- // This function assumes at least one bit is set. Checking for 0 needs to be done at a higher level, outside this function.
#ifdef DRFLAC_IMPLEMENT_CLZ_LZCNT
if (drflac__is_lzcnt_supported()) {
return drflac__clz_lzcnt(x);
} else
#endif
{
- #ifdef DRFLAC_IMPLEMENT_CLZ_MSVC
+#ifdef DRFLAC_IMPLEMENT_CLZ_MSVC
return drflac__clz_msvc(x);
- #else
+#else
return drflac__clz_software(x);
- #endif
+#endif
}
}
-static inline drflac_bool32 drflac__seek_past_next_set_bit(drflac_bs* bs, unsigned int* pOffsetOut)
+static DRFLAC_INLINE drflac_bool32 drflac__seek_past_next_set_bit(drflac_bs* bs, unsigned int* pOffsetOut)
{
drflac_uint32 zeroCounter = 0;
+ drflac_uint32 setBitOffsetPlus1;
+
while (bs->cache == 0) {
zeroCounter += (drflac_uint32)DRFLAC_CACHE_L1_BITS_REMAINING(bs);
if (!drflac__reload_cache(bs)) {
@@ -1797,7 +2111,7 @@ static inline drflac_bool32 drflac__seek_past_next_set_bit(drflac_bs* bs, unsign
}
}
- drflac_uint32 setBitOffsetPlus1 = drflac__clz(bs->cache);
+ setBitOffsetPlus1 = drflac__clz(bs->cache);
setBitOffsetPlus1 += 1;
bs->consumedBits += setBitOffsetPlus1;
@@ -1814,9 +2128,11 @@ static drflac_bool32 drflac__seek_to_byte(drflac_bs* bs, drflac_uint64 offsetFro
drflac_assert(bs != NULL);
drflac_assert(offsetFromStart > 0);
- // Seeking from the start is not quite as trivial as it sounds because the onSeek callback takes a signed 32-bit integer (which
- // is intentional because it simplifies the implementation of the onSeek callbacks), however offsetFromStart is unsigned 64-bit.
- // To resolve we just need to do an initial seek from the start, and then a series of offset seeks to make up the remainder.
+ /*
+ Seeking from the start is not quite as trivial as it sounds because the onSeek callback takes a signed 32-bit integer (which
+ is intentional because it simplifies the implementation of the onSeek callbacks), however offsetFromStart is unsigned 64-bit.
+ To resolve we just need to do an initial seek from the start, and then a series of offset seeks to make up the remainder.
+ */
if (offsetFromStart > 0x7FFFFFFF) {
drflac_uint64 bytesRemaining = offsetFromStart;
if (!bs->onSeek(bs->pUserData, 0x7FFFFFFF, drflac_seek_origin_start)) {
@@ -1842,7 +2158,7 @@ static drflac_bool32 drflac__seek_to_byte(drflac_bs* bs, drflac_uint64 offsetFro
}
}
- // The cache should be reset to force a reload of fresh data from the client.
+ /* The cache should be reset to force a reload of fresh data from the client. */
drflac__reset_cache(bs);
return DRFLAC_TRUE;
}
@@ -1850,12 +2166,18 @@ static drflac_bool32 drflac__seek_to_byte(drflac_bs* bs, drflac_uint64 offsetFro
static drflac_result drflac__read_utf8_coded_number(drflac_bs* bs, drflac_uint64* pNumberOut, drflac_uint8* pCRCOut)
{
+ drflac_uint8 crc;
+ drflac_uint64 result;
+ unsigned char utf8[7] = {0};
+ int byteCount;
+ int i;
+
drflac_assert(bs != NULL);
drflac_assert(pNumberOut != NULL);
+ drflac_assert(pCRCOut != NULL);
- drflac_uint8 crc = *pCRCOut;
+ crc = *pCRCOut;
- unsigned char utf8[7] = {0};
if (!drflac__read_uint8(bs, 8, utf8)) {
*pNumberOut = 0;
return DRFLAC_END_OF_STREAM;
@@ -1868,7 +2190,7 @@ static drflac_result drflac__read_utf8_coded_number(drflac_bs* bs, drflac_uint64
return DRFLAC_SUCCESS;
}
- int byteCount = 1;
+ byteCount = 1;
if ((utf8[0] & 0xE0) == 0xC0) {
byteCount = 2;
} else if ((utf8[0] & 0xF0) == 0xE0) {
@@ -1883,14 +2205,14 @@ static drflac_result drflac__read_utf8_coded_number(drflac_bs* bs, drflac_uint64
byteCount = 7;
} else {
*pNumberOut = 0;
- return DRFLAC_CRC_MISMATCH; // Bad UTF-8 encoding.
+ return DRFLAC_CRC_MISMATCH; /* Bad UTF-8 encoding. */
}
- // Read extra bytes.
+ /* Read extra bytes. */
drflac_assert(byteCount > 1);
- drflac_uint64 result = (drflac_uint64)(utf8[0] & (0xFF >> (byteCount + 1)));
- for (int i = 1; i < byteCount; ++i) {
+ result = (drflac_uint64)(utf8[0] & (0xFF >> (byteCount + 1)));
+ for (i = 1; i < byteCount; ++i) {
if (!drflac__read_uint8(bs, 8, utf8 + i)) {
*pNumberOut = 0;
return DRFLAC_END_OF_STREAM;
@@ -1907,20 +2229,21 @@ static drflac_result drflac__read_utf8_coded_number(drflac_bs* bs, drflac_uint64
+/*
+The next two functions are responsible for calculating the prediction.
-// The next two functions are responsible for calculating the prediction.
-//
-// When the bits per sample is >16 we need to use 64-bit integer arithmetic because otherwise we'll run out of precision. It's
-// safe to assume this will be slower on 32-bit platforms so we use a more optimal solution when the bits per sample is <=16.
+When the bits per sample is >16 we need to use 64-bit integer arithmetic because otherwise we'll run out of precision. It's
+safe to assume this will be slower on 32-bit platforms so we use a more optimal solution when the bits per sample is <=16.
+*/
static DRFLAC_INLINE drflac_int32 drflac__calculate_prediction_32(drflac_uint32 order, drflac_int32 shift, const drflac_int32* coefficients, drflac_int32* pDecodedSamples)
{
- drflac_assert(order <= 32);
+ drflac_int32 prediction = 0;
- // 32-bit version.
+ drflac_assert(order <= 32);
- // VC++ optimizes this to a single jmp. I've not yet verified this for other compilers.
- drflac_int32 prediction = 0;
+ /* 32-bit version. */
+ /* VC++ optimizes this to a single jmp. I've not yet verified this for other compilers. */
switch (order)
{
case 32: prediction += coefficients[31] * pDecodedSamples[-32];
@@ -1962,13 +2285,14 @@ static DRFLAC_INLINE drflac_int32 drflac__calculate_prediction_32(drflac_uint32
static DRFLAC_INLINE drflac_int32 drflac__calculate_prediction_64(drflac_uint32 order, drflac_int32 shift, const drflac_int32* coefficients, drflac_int32* pDecodedSamples)
{
+ drflac_int64 prediction;
+
drflac_assert(order <= 32);
- // 64-bit version.
+ /* 64-bit version. */
- // This method is faster on the 32-bit build when compiling with VC++. See note below.
+ /* This method is faster on the 32-bit build when compiling with VC++. See note below. */
#ifndef DRFLAC_64BIT
- drflac_int64 prediction;
if (order == 8)
{
prediction = coefficients[0] * (drflac_int64)pDecodedSamples[-1];
@@ -2085,18 +2409,21 @@ static DRFLAC_INLINE drflac_int32 drflac__calculate_prediction_64(drflac_uint32
}
else
{
+ int j;
+
prediction = 0;
- for (int j = 0; j < (int)order; ++j) {
+ for (j = 0; j < (int)order; ++j) {
prediction += coefficients[j] * (drflac_int64)pDecodedSamples[-j-1];
}
}
#endif
- // VC++ optimizes this to a single jmp instruction, but only the 64-bit build. The 32-bit build generates less efficient code for some
- // reason. The ugly version above is faster so we'll just switch between the two depending on the target platform.
+ /*
+ VC++ optimizes this to a single jmp instruction, but only the 64-bit build. The 32-bit build generates less efficient code for some
+ reason. The ugly version above is faster so we'll just switch between the two depending on the target platform.
+ */
#ifdef DRFLAC_64BIT
- drflac_int64 prediction = 0;
-
+ prediction = 0;
switch (order)
{
case 32: prediction += coefficients[31] * (drflac_int64)pDecodedSamples[-32];
@@ -2137,16 +2464,451 @@ static DRFLAC_INLINE drflac_int32 drflac__calculate_prediction_64(drflac_uint32
return (drflac_int32)(prediction >> shift);
}
+static DRFLAC_INLINE void drflac__calculate_prediction_64_x4(drflac_uint32 order, drflac_int32 shift, const drflac_int32* coefficients, const drflac_uint32 riceParamParts[4], drflac_int32* pDecodedSamples)
+{
+ drflac_int64 prediction0 = 0;
+ drflac_int64 prediction1 = 0;
+ drflac_int64 prediction2 = 0;
+ drflac_int64 prediction3 = 0;
+
+ drflac_assert(order <= 32);
+
+ switch (order)
+ {
+ case 32:
+ prediction0 += coefficients[31] * (drflac_int64)pDecodedSamples[-32];
+ prediction1 += coefficients[31] * (drflac_int64)pDecodedSamples[-31];
+ prediction2 += coefficients[31] * (drflac_int64)pDecodedSamples[-30];
+ prediction3 += coefficients[31] * (drflac_int64)pDecodedSamples[-29];
+ case 31:
+ prediction0 += coefficients[30] * (drflac_int64)pDecodedSamples[-31];
+ prediction1 += coefficients[30] * (drflac_int64)pDecodedSamples[-30];
+ prediction2 += coefficients[30] * (drflac_int64)pDecodedSamples[-29];
+ prediction3 += coefficients[30] * (drflac_int64)pDecodedSamples[-28];
+ case 30:
+ prediction0 += coefficients[29] * (drflac_int64)pDecodedSamples[-30];
+ prediction1 += coefficients[29] * (drflac_int64)pDecodedSamples[-29];
+ prediction2 += coefficients[29] * (drflac_int64)pDecodedSamples[-28];
+ prediction3 += coefficients[29] * (drflac_int64)pDecodedSamples[-27];
+ case 29:
+ prediction0 += coefficients[28] * (drflac_int64)pDecodedSamples[-29];
+ prediction1 += coefficients[28] * (drflac_int64)pDecodedSamples[-28];
+ prediction2 += coefficients[28] * (drflac_int64)pDecodedSamples[-27];
+ prediction3 += coefficients[28] * (drflac_int64)pDecodedSamples[-26];
+ case 28:
+ prediction0 += coefficients[27] * (drflac_int64)pDecodedSamples[-28];
+ prediction1 += coefficients[27] * (drflac_int64)pDecodedSamples[-27];
+ prediction2 += coefficients[27] * (drflac_int64)pDecodedSamples[-26];
+ prediction3 += coefficients[27] * (drflac_int64)pDecodedSamples[-25];
+ case 27:
+ prediction0 += coefficients[26] * (drflac_int64)pDecodedSamples[-27];
+ prediction1 += coefficients[26] * (drflac_int64)pDecodedSamples[-26];
+ prediction2 += coefficients[26] * (drflac_int64)pDecodedSamples[-25];
+ prediction3 += coefficients[26] * (drflac_int64)pDecodedSamples[-24];
+ case 26:
+ prediction0 += coefficients[25] * (drflac_int64)pDecodedSamples[-26];
+ prediction1 += coefficients[25] * (drflac_int64)pDecodedSamples[-25];
+ prediction2 += coefficients[25] * (drflac_int64)pDecodedSamples[-24];
+ prediction3 += coefficients[25] * (drflac_int64)pDecodedSamples[-23];
+ case 25:
+ prediction0 += coefficients[24] * (drflac_int64)pDecodedSamples[-25];
+ prediction1 += coefficients[24] * (drflac_int64)pDecodedSamples[-24];
+ prediction2 += coefficients[24] * (drflac_int64)pDecodedSamples[-23];
+ prediction3 += coefficients[24] * (drflac_int64)pDecodedSamples[-22];
+ case 24:
+ prediction0 += coefficients[23] * (drflac_int64)pDecodedSamples[-24];
+ prediction1 += coefficients[23] * (drflac_int64)pDecodedSamples[-23];
+ prediction2 += coefficients[23] * (drflac_int64)pDecodedSamples[-22];
+ prediction3 += coefficients[23] * (drflac_int64)pDecodedSamples[-21];
+ case 23:
+ prediction0 += coefficients[22] * (drflac_int64)pDecodedSamples[-23];
+ prediction1 += coefficients[22] * (drflac_int64)pDecodedSamples[-22];
+ prediction2 += coefficients[22] * (drflac_int64)pDecodedSamples[-21];
+ prediction3 += coefficients[22] * (drflac_int64)pDecodedSamples[-20];
+ case 22:
+ prediction0 += coefficients[21] * (drflac_int64)pDecodedSamples[-22];
+ prediction1 += coefficients[21] * (drflac_int64)pDecodedSamples[-21];
+ prediction2 += coefficients[21] * (drflac_int64)pDecodedSamples[-20];
+ prediction3 += coefficients[21] * (drflac_int64)pDecodedSamples[-19];
+ case 21:
+ prediction0 += coefficients[20] * (drflac_int64)pDecodedSamples[-21];
+ prediction1 += coefficients[20] * (drflac_int64)pDecodedSamples[-20];
+ prediction2 += coefficients[20] * (drflac_int64)pDecodedSamples[-19];
+ prediction3 += coefficients[20] * (drflac_int64)pDecodedSamples[-18];
+ case 20:
+ prediction0 += coefficients[19] * (drflac_int64)pDecodedSamples[-20];
+ prediction1 += coefficients[19] * (drflac_int64)pDecodedSamples[-19];
+ prediction2 += coefficients[19] * (drflac_int64)pDecodedSamples[-18];
+ prediction3 += coefficients[19] * (drflac_int64)pDecodedSamples[-17];
+ case 19:
+ prediction0 += coefficients[18] * (drflac_int64)pDecodedSamples[-19];
+ prediction1 += coefficients[18] * (drflac_int64)pDecodedSamples[-18];
+ prediction2 += coefficients[18] * (drflac_int64)pDecodedSamples[-17];
+ prediction3 += coefficients[18] * (drflac_int64)pDecodedSamples[-16];
+ case 18:
+ prediction0 += coefficients[17] * (drflac_int64)pDecodedSamples[-18];
+ prediction1 += coefficients[17] * (drflac_int64)pDecodedSamples[-17];
+ prediction2 += coefficients[17] * (drflac_int64)pDecodedSamples[-16];
+ prediction3 += coefficients[17] * (drflac_int64)pDecodedSamples[-15];
+ case 17:
+ prediction0 += coefficients[16] * (drflac_int64)pDecodedSamples[-17];
+ prediction1 += coefficients[16] * (drflac_int64)pDecodedSamples[-16];
+ prediction2 += coefficients[16] * (drflac_int64)pDecodedSamples[-15];
+ prediction3 += coefficients[16] * (drflac_int64)pDecodedSamples[-14];
+
+ case 16:
+ prediction0 += coefficients[15] * (drflac_int64)pDecodedSamples[-16];
+ prediction1 += coefficients[15] * (drflac_int64)pDecodedSamples[-15];
+ prediction2 += coefficients[15] * (drflac_int64)pDecodedSamples[-14];
+ prediction3 += coefficients[15] * (drflac_int64)pDecodedSamples[-13];
+ case 15:
+ prediction0 += coefficients[14] * (drflac_int64)pDecodedSamples[-15];
+ prediction1 += coefficients[14] * (drflac_int64)pDecodedSamples[-14];
+ prediction2 += coefficients[14] * (drflac_int64)pDecodedSamples[-13];
+ prediction3 += coefficients[14] * (drflac_int64)pDecodedSamples[-12];
+ case 14:
+ prediction0 += coefficients[13] * (drflac_int64)pDecodedSamples[-14];
+ prediction1 += coefficients[13] * (drflac_int64)pDecodedSamples[-13];
+ prediction2 += coefficients[13] * (drflac_int64)pDecodedSamples[-12];
+ prediction3 += coefficients[13] * (drflac_int64)pDecodedSamples[-11];
+ case 13:
+ prediction0 += coefficients[12] * (drflac_int64)pDecodedSamples[-13];
+ prediction1 += coefficients[12] * (drflac_int64)pDecodedSamples[-12];
+ prediction2 += coefficients[12] * (drflac_int64)pDecodedSamples[-11];
+ prediction3 += coefficients[12] * (drflac_int64)pDecodedSamples[-10];
+ case 12:
+ prediction0 += coefficients[11] * (drflac_int64)pDecodedSamples[-12];
+ prediction1 += coefficients[11] * (drflac_int64)pDecodedSamples[-11];
+ prediction2 += coefficients[11] * (drflac_int64)pDecodedSamples[-10];
+ prediction3 += coefficients[11] * (drflac_int64)pDecodedSamples[- 9];
+ case 11:
+ prediction0 += coefficients[10] * (drflac_int64)pDecodedSamples[-11];
+ prediction1 += coefficients[10] * (drflac_int64)pDecodedSamples[-10];
+ prediction2 += coefficients[10] * (drflac_int64)pDecodedSamples[- 9];
+ prediction3 += coefficients[10] * (drflac_int64)pDecodedSamples[- 8];
+ case 10:
+ prediction0 += coefficients[9] * (drflac_int64)pDecodedSamples[-10];
+ prediction1 += coefficients[9] * (drflac_int64)pDecodedSamples[- 9];
+ prediction2 += coefficients[9] * (drflac_int64)pDecodedSamples[- 8];
+ prediction3 += coefficients[9] * (drflac_int64)pDecodedSamples[- 7];
+ case 9:
+ prediction0 += coefficients[8] * (drflac_int64)pDecodedSamples[- 9];
+ prediction1 += coefficients[8] * (drflac_int64)pDecodedSamples[- 8];
+ prediction2 += coefficients[8] * (drflac_int64)pDecodedSamples[- 7];
+ prediction3 += coefficients[8] * (drflac_int64)pDecodedSamples[- 6];
+ case 8:
+ prediction0 += coefficients[7] * (drflac_int64)pDecodedSamples[- 8];
+ prediction1 += coefficients[7] * (drflac_int64)pDecodedSamples[- 7];
+ prediction2 += coefficients[7] * (drflac_int64)pDecodedSamples[- 6];
+ prediction3 += coefficients[7] * (drflac_int64)pDecodedSamples[- 5];
+ case 7:
+ prediction0 += coefficients[6] * (drflac_int64)pDecodedSamples[- 7];
+ prediction1 += coefficients[6] * (drflac_int64)pDecodedSamples[- 6];
+ prediction2 += coefficients[6] * (drflac_int64)pDecodedSamples[- 5];
+ prediction3 += coefficients[6] * (drflac_int64)pDecodedSamples[- 4];
+ case 6:
+ prediction0 += coefficients[5] * (drflac_int64)pDecodedSamples[- 6];
+ prediction1 += coefficients[5] * (drflac_int64)pDecodedSamples[- 5];
+ prediction2 += coefficients[5] * (drflac_int64)pDecodedSamples[- 4];
+ prediction3 += coefficients[5] * (drflac_int64)pDecodedSamples[- 3];
+ case 5:
+ prediction0 += coefficients[4] * (drflac_int64)pDecodedSamples[- 5];
+ prediction1 += coefficients[4] * (drflac_int64)pDecodedSamples[- 4];
+ prediction2 += coefficients[4] * (drflac_int64)pDecodedSamples[- 3];
+ prediction3 += coefficients[4] * (drflac_int64)pDecodedSamples[- 2];
+ case 4:
+ prediction0 += coefficients[3] * (drflac_int64)pDecodedSamples[- 4];
+ prediction1 += coefficients[3] * (drflac_int64)pDecodedSamples[- 3];
+ prediction2 += coefficients[3] * (drflac_int64)pDecodedSamples[- 2];
+ prediction3 += coefficients[3] * (drflac_int64)pDecodedSamples[- 1];
+ order = 3;
+ }
+
+ switch (order)
+ {
+ case 3: prediction0 += coefficients[ 2] * (drflac_int64)pDecodedSamples[- 3];
+ case 2: prediction0 += coefficients[ 1] * (drflac_int64)pDecodedSamples[- 2];
+ case 1: prediction0 += coefficients[ 0] * (drflac_int64)pDecodedSamples[- 1];
+ }
+ pDecodedSamples[0] = riceParamParts[0] + (drflac_int32)(prediction0 >> shift);
+
+ switch (order)
+ {
+ case 3: prediction1 += coefficients[ 2] * (drflac_int64)pDecodedSamples[- 2];
+ case 2: prediction1 += coefficients[ 1] * (drflac_int64)pDecodedSamples[- 1];
+ case 1: prediction1 += coefficients[ 0] * (drflac_int64)pDecodedSamples[ 0];
+ }
+ pDecodedSamples[1] = riceParamParts[1] + (drflac_int32)(prediction1 >> shift);
+
+ switch (order)
+ {
+ case 3: prediction2 += coefficients[ 2] * (drflac_int64)pDecodedSamples[- 1];
+ case 2: prediction2 += coefficients[ 1] * (drflac_int64)pDecodedSamples[ 0];
+ case 1: prediction2 += coefficients[ 0] * (drflac_int64)pDecodedSamples[ 1];
+ }
+ pDecodedSamples[2] = riceParamParts[2] + (drflac_int32)(prediction2 >> shift);
+
+ switch (order)
+ {
+ case 3: prediction3 += coefficients[ 2] * (drflac_int64)pDecodedSamples[ 0];
+ case 2: prediction3 += coefficients[ 1] * (drflac_int64)pDecodedSamples[ 1];
+ case 1: prediction3 += coefficients[ 0] * (drflac_int64)pDecodedSamples[ 2];
+ }
+ pDecodedSamples[3] = riceParamParts[3] + (drflac_int32)(prediction3 >> shift);
+}
+
+#if defined(DRFLAC_SUPPORT_SSE41)
+static DRFLAC_INLINE drflac_int32 drflac__calculate_prediction_64__sse41(drflac_uint32 order, drflac_int32 shift, const drflac_int32* coefficients, drflac_int32* pDecodedSamples)
+{
+ __m128i prediction = _mm_setzero_si128();
+
+ drflac_assert(order <= 32);
+
+ switch (order)
+ {
+ case 32:
+ case 31: prediction = _mm_add_epi64(prediction, _mm_mul_epi32(_mm_set_epi32(0, coefficients[31], 0, coefficients[30]), _mm_set_epi32(0, pDecodedSamples[-32], 0, pDecodedSamples[-31])));
+ case 30:
+ case 29: prediction = _mm_add_epi64(prediction, _mm_mul_epi32(_mm_set_epi32(0, coefficients[29], 0, coefficients[28]), _mm_set_epi32(0, pDecodedSamples[-30], 0, pDecodedSamples[-29])));
+ case 28:
+ case 27: prediction = _mm_add_epi64(prediction, _mm_mul_epi32(_mm_set_epi32(0, coefficients[27], 0, coefficients[26]), _mm_set_epi32(0, pDecodedSamples[-28], 0, pDecodedSamples[-27])));
+ case 26:
+ case 25: prediction = _mm_add_epi64(prediction, _mm_mul_epi32(_mm_set_epi32(0, coefficients[25], 0, coefficients[24]), _mm_set_epi32(0, pDecodedSamples[-26], 0, pDecodedSamples[-25])));
+ case 24:
+ case 23: prediction = _mm_add_epi64(prediction, _mm_mul_epi32(_mm_set_epi32(0, coefficients[23], 0, coefficients[22]), _mm_set_epi32(0, pDecodedSamples[-24], 0, pDecodedSamples[-23])));
+ case 22:
+ case 21: prediction = _mm_add_epi64(prediction, _mm_mul_epi32(_mm_set_epi32(0, coefficients[21], 0, coefficients[20]), _mm_set_epi32(0, pDecodedSamples[-22], 0, pDecodedSamples[-21])));
+ case 20:
+ case 19: prediction = _mm_add_epi64(prediction, _mm_mul_epi32(_mm_set_epi32(0, coefficients[19], 0, coefficients[18]), _mm_set_epi32(0, pDecodedSamples[-20], 0, pDecodedSamples[-19])));
+ case 18:
+ case 17: prediction = _mm_add_epi64(prediction, _mm_mul_epi32(_mm_set_epi32(0, coefficients[17], 0, coefficients[16]), _mm_set_epi32(0, pDecodedSamples[-18], 0, pDecodedSamples[-17])));
+ case 16:
+ case 15: prediction = _mm_add_epi64(prediction, _mm_mul_epi32(_mm_set_epi32(0, coefficients[15], 0, coefficients[14]), _mm_set_epi32(0, pDecodedSamples[-16], 0, pDecodedSamples[-15])));
+ case 14:
+ case 13: prediction = _mm_add_epi64(prediction, _mm_mul_epi32(_mm_set_epi32(0, coefficients[13], 0, coefficients[12]), _mm_set_epi32(0, pDecodedSamples[-14], 0, pDecodedSamples[-13])));
+ case 12:
+ case 11: prediction = _mm_add_epi64(prediction, _mm_mul_epi32(_mm_set_epi32(0, coefficients[11], 0, coefficients[10]), _mm_set_epi32(0, pDecodedSamples[-12], 0, pDecodedSamples[-11])));
+ case 10:
+ case 9: prediction = _mm_add_epi64(prediction, _mm_mul_epi32(_mm_set_epi32(0, coefficients[ 9], 0, coefficients[ 8]), _mm_set_epi32(0, pDecodedSamples[-10], 0, pDecodedSamples[- 9])));
+ case 8:
+ case 7: prediction = _mm_add_epi64(prediction, _mm_mul_epi32(_mm_set_epi32(0, coefficients[ 7], 0, coefficients[ 6]), _mm_set_epi32(0, pDecodedSamples[- 8], 0, pDecodedSamples[- 7])));
+ case 6:
+ case 5: prediction = _mm_add_epi64(prediction, _mm_mul_epi32(_mm_set_epi32(0, coefficients[ 5], 0, coefficients[ 4]), _mm_set_epi32(0, pDecodedSamples[- 6], 0, pDecodedSamples[- 5])));
+ case 4:
+ case 3: prediction = _mm_add_epi64(prediction, _mm_mul_epi32(_mm_set_epi32(0, coefficients[ 3], 0, coefficients[ 2]), _mm_set_epi32(0, pDecodedSamples[- 4], 0, pDecodedSamples[- 3])));
+ case 2:
+ case 1: prediction = _mm_add_epi64(prediction, _mm_mul_epi32(_mm_set_epi32(0, coefficients[ 1], 0, coefficients[ 0]), _mm_set_epi32(0, pDecodedSamples[- 2], 0, pDecodedSamples[- 1])));
+ }
+
+ return (drflac_int32)((
+ ((drflac_uint64*)&prediction)[0] +
+ ((drflac_uint64*)&prediction)[1]) >> shift);
+}
+
+static DRFLAC_INLINE void drflac__calculate_prediction_64_x2__sse41(drflac_uint32 order, drflac_int32 shift, const drflac_int32* coefficients, const drflac_uint32 riceParamParts[4], drflac_int32* pDecodedSamples)
+{
+ __m128i prediction = _mm_setzero_si128();
+ drflac_int64 predictions[2] = {0, 0};
+
+ drflac_assert(order <= 32);
+
+ switch (order)
+ {
+ case 32: prediction = _mm_add_epi64(prediction, _mm_mul_epi32(_mm_set_epi32(0, coefficients[31], 0, coefficients[31]), _mm_set_epi32(0, pDecodedSamples[-31], 0, pDecodedSamples[-32])));
+ case 31: prediction = _mm_add_epi64(prediction, _mm_mul_epi32(_mm_set_epi32(0, coefficients[30], 0, coefficients[30]), _mm_set_epi32(0, pDecodedSamples[-30], 0, pDecodedSamples[-31])));
+ case 30: prediction = _mm_add_epi64(prediction, _mm_mul_epi32(_mm_set_epi32(0, coefficients[29], 0, coefficients[29]), _mm_set_epi32(0, pDecodedSamples[-29], 0, pDecodedSamples[-30])));
+ case 29: prediction = _mm_add_epi64(prediction, _mm_mul_epi32(_mm_set_epi32(0, coefficients[28], 0, coefficients[28]), _mm_set_epi32(0, pDecodedSamples[-28], 0, pDecodedSamples[-29])));
+ case 28: prediction = _mm_add_epi64(prediction, _mm_mul_epi32(_mm_set_epi32(0, coefficients[27], 0, coefficients[27]), _mm_set_epi32(0, pDecodedSamples[-27], 0, pDecodedSamples[-28])));
+ case 27: prediction = _mm_add_epi64(prediction, _mm_mul_epi32(_mm_set_epi32(0, coefficients[26], 0, coefficients[26]), _mm_set_epi32(0, pDecodedSamples[-26], 0, pDecodedSamples[-27])));
+ case 26: prediction = _mm_add_epi64(prediction, _mm_mul_epi32(_mm_set_epi32(0, coefficients[25], 0, coefficients[25]), _mm_set_epi32(0, pDecodedSamples[-25], 0, pDecodedSamples[-26])));
+ case 25: prediction = _mm_add_epi64(prediction, _mm_mul_epi32(_mm_set_epi32(0, coefficients[24], 0, coefficients[24]), _mm_set_epi32(0, pDecodedSamples[-24], 0, pDecodedSamples[-25])));
+ case 24: prediction = _mm_add_epi64(prediction, _mm_mul_epi32(_mm_set_epi32(0, coefficients[23], 0, coefficients[23]), _mm_set_epi32(0, pDecodedSamples[-23], 0, pDecodedSamples[-24])));
+ case 23: prediction = _mm_add_epi64(prediction, _mm_mul_epi32(_mm_set_epi32(0, coefficients[22], 0, coefficients[22]), _mm_set_epi32(0, pDecodedSamples[-22], 0, pDecodedSamples[-23])));
+ case 22: prediction = _mm_add_epi64(prediction, _mm_mul_epi32(_mm_set_epi32(0, coefficients[21], 0, coefficients[21]), _mm_set_epi32(0, pDecodedSamples[-21], 0, pDecodedSamples[-22])));
+ case 21: prediction = _mm_add_epi64(prediction, _mm_mul_epi32(_mm_set_epi32(0, coefficients[20], 0, coefficients[20]), _mm_set_epi32(0, pDecodedSamples[-20], 0, pDecodedSamples[-21])));
+ case 20: prediction = _mm_add_epi64(prediction, _mm_mul_epi32(_mm_set_epi32(0, coefficients[19], 0, coefficients[19]), _mm_set_epi32(0, pDecodedSamples[-19], 0, pDecodedSamples[-20])));
+ case 19: prediction = _mm_add_epi64(prediction, _mm_mul_epi32(_mm_set_epi32(0, coefficients[18], 0, coefficients[18]), _mm_set_epi32(0, pDecodedSamples[-18], 0, pDecodedSamples[-19])));
+ case 18: prediction = _mm_add_epi64(prediction, _mm_mul_epi32(_mm_set_epi32(0, coefficients[17], 0, coefficients[17]), _mm_set_epi32(0, pDecodedSamples[-17], 0, pDecodedSamples[-18])));
+ case 17: prediction = _mm_add_epi64(prediction, _mm_mul_epi32(_mm_set_epi32(0, coefficients[16], 0, coefficients[16]), _mm_set_epi32(0, pDecodedSamples[-16], 0, pDecodedSamples[-17])));
+ case 16: prediction = _mm_add_epi64(prediction, _mm_mul_epi32(_mm_set_epi32(0, coefficients[15], 0, coefficients[15]), _mm_set_epi32(0, pDecodedSamples[-15], 0, pDecodedSamples[-16])));
+ case 15: prediction = _mm_add_epi64(prediction, _mm_mul_epi32(_mm_set_epi32(0, coefficients[14], 0, coefficients[14]), _mm_set_epi32(0, pDecodedSamples[-14], 0, pDecodedSamples[-15])));
+ case 14: prediction = _mm_add_epi64(prediction, _mm_mul_epi32(_mm_set_epi32(0, coefficients[13], 0, coefficients[13]), _mm_set_epi32(0, pDecodedSamples[-13], 0, pDecodedSamples[-14])));
+ case 13: prediction = _mm_add_epi64(prediction, _mm_mul_epi32(_mm_set_epi32(0, coefficients[12], 0, coefficients[12]), _mm_set_epi32(0, pDecodedSamples[-12], 0, pDecodedSamples[-13])));
+ case 12: prediction = _mm_add_epi64(prediction, _mm_mul_epi32(_mm_set_epi32(0, coefficients[11], 0, coefficients[11]), _mm_set_epi32(0, pDecodedSamples[-11], 0, pDecodedSamples[-12])));
+ case 11: prediction = _mm_add_epi64(prediction, _mm_mul_epi32(_mm_set_epi32(0, coefficients[10], 0, coefficients[10]), _mm_set_epi32(0, pDecodedSamples[-10], 0, pDecodedSamples[-11])));
+ case 10: prediction = _mm_add_epi64(prediction, _mm_mul_epi32(_mm_set_epi32(0, coefficients[ 9], 0, coefficients[ 9]), _mm_set_epi32(0, pDecodedSamples[- 9], 0, pDecodedSamples[-10])));
+ case 9: prediction = _mm_add_epi64(prediction, _mm_mul_epi32(_mm_set_epi32(0, coefficients[ 8], 0, coefficients[ 8]), _mm_set_epi32(0, pDecodedSamples[- 8], 0, pDecodedSamples[- 9])));
+ case 8: prediction = _mm_add_epi64(prediction, _mm_mul_epi32(_mm_set_epi32(0, coefficients[ 7], 0, coefficients[ 7]), _mm_set_epi32(0, pDecodedSamples[- 7], 0, pDecodedSamples[- 8])));
+ case 7: prediction = _mm_add_epi64(prediction, _mm_mul_epi32(_mm_set_epi32(0, coefficients[ 6], 0, coefficients[ 6]), _mm_set_epi32(0, pDecodedSamples[- 6], 0, pDecodedSamples[- 7])));
+ case 6: prediction = _mm_add_epi64(prediction, _mm_mul_epi32(_mm_set_epi32(0, coefficients[ 5], 0, coefficients[ 5]), _mm_set_epi32(0, pDecodedSamples[- 5], 0, pDecodedSamples[- 6])));
+ case 5: prediction = _mm_add_epi64(prediction, _mm_mul_epi32(_mm_set_epi32(0, coefficients[ 4], 0, coefficients[ 4]), _mm_set_epi32(0, pDecodedSamples[- 4], 0, pDecodedSamples[- 5])));
+ case 4: prediction = _mm_add_epi64(prediction, _mm_mul_epi32(_mm_set_epi32(0, coefficients[ 3], 0, coefficients[ 3]), _mm_set_epi32(0, pDecodedSamples[- 3], 0, pDecodedSamples[- 4])));
+ case 3: prediction = _mm_add_epi64(prediction, _mm_mul_epi32(_mm_set_epi32(0, coefficients[ 2], 0, coefficients[ 2]), _mm_set_epi32(0, pDecodedSamples[- 2], 0, pDecodedSamples[- 3])));
+ case 2: prediction = _mm_add_epi64(prediction, _mm_mul_epi32(_mm_set_epi32(0, coefficients[ 1], 0, coefficients[ 1]), _mm_set_epi32(0, pDecodedSamples[- 1], 0, pDecodedSamples[- 2])));
+ order = 1;
+ }
+
+ _mm_storeu_si128((__m128i*)predictions, prediction);
+
+ switch (order)
+ {
+ case 1: predictions[0] += coefficients[ 0] * (drflac_int64)pDecodedSamples[- 1];
+ }
+ pDecodedSamples[0] = riceParamParts[0] + (drflac_int32)(predictions[0] >> shift);
+
+ switch (order)
+ {
+ case 1: predictions[1] += coefficients[ 0] * (drflac_int64)pDecodedSamples[ 0];
+ }
+ pDecodedSamples[1] = riceParamParts[1] + (drflac_int32)(predictions[1] >> shift);
+}
+
+
+static DRFLAC_INLINE __m128i drflac__mm_not_si128(__m128i a)
+{
+ return _mm_xor_si128(a, _mm_cmpeq_epi32(_mm_setzero_si128(), _mm_setzero_si128()));
+}
+
+static DRFLAC_INLINE __m128i drflac__mm_slide1_epi32(__m128i a, __m128i b)
+{
+ /* a3a2a1a0/b3b2b1b0 -> a2a1a0b3 */
+
+ /* Result = a2a1a0b3 */
+ __m128i b3a3b2a2 = _mm_unpackhi_epi32(a, b);
+ __m128i a2b3a2b3 = _mm_shuffle_epi32(b3a3b2a2, _MM_SHUFFLE(0, 3, 0, 3));
+ __m128i a1a2a0b3 = _mm_unpacklo_epi32(a2b3a2b3, a);
+ __m128i a2a1a0b3 = _mm_shuffle_epi32(a1a2a0b3, _MM_SHUFFLE(2, 3, 1, 0));
+ return a2a1a0b3;
+}
+
+static DRFLAC_INLINE __m128i drflac__mm_slide2_epi32(__m128i a, __m128i b)
+{
+ /* Result = a1a0b3b2 */
+ __m128i b1b0b3b2 = _mm_shuffle_epi32(b, _MM_SHUFFLE(1, 0, 3, 2));
+ __m128i a1b3a0b2 = _mm_unpacklo_epi32(b1b0b3b2, a);
+ __m128i a1a0b3b2 = _mm_shuffle_epi32(a1b3a0b2, _MM_SHUFFLE(3, 1, 2, 0));
+ return a1a0b3b2;
+}
+
+static DRFLAC_INLINE __m128i drflac__mm_slide3_epi32(__m128i a, __m128i b)
+{
+ /* Result = a0b3b2b1 */
+ __m128i b1a1b0a0 = _mm_unpacklo_epi32(a, b);
+ __m128i a0b1a0b1 = _mm_shuffle_epi32(b1a1b0a0, _MM_SHUFFLE(0, 3, 0, 3));
+ __m128i b3a0b2b1 = _mm_unpackhi_epi32(a0b1a0b1, b);
+ __m128i a0b3b2b1 = _mm_shuffle_epi32(b3a0b2b1, _MM_SHUFFLE(2, 3, 1, 0));
+ return a0b3b2b1;
+}
+
+static DRFLAC_INLINE void drflac__calculate_prediction_32_x4__sse41(drflac_uint32 order, drflac_int32 shift, const __m128i* coefficients128, const __m128i riceParamParts128, drflac_int32* pDecodedSamples)
+{
+ drflac_assert(order <= 32);
+
+ /* I don't think this is as efficient as it could be. More work needs to be done on this. */
+ if (order > 0) {
+ drflac_int32 predictions[4];
+ drflac_uint32 riceParamParts[4];
+
+ __m128i s_09_10_11_12 = _mm_loadu_si128((const __m128i*)(pDecodedSamples - 12));
+ __m128i s_05_06_07_08 = _mm_loadu_si128((const __m128i*)(pDecodedSamples - 8));
+ __m128i s_01_02_03_04 = _mm_loadu_si128((const __m128i*)(pDecodedSamples - 4));
+
+ __m128i prediction = _mm_setzero_si128();
+
+ /*
+ The idea with this switch is to do do a single jump based on the value of "order". In my test library, "order" is never larger than 12, so
+ I have decided to do a less optimal, but simpler solution in the order > 12 case.
+ */
+ switch (order)
+ {
+ case 32: prediction = _mm_add_epi32(prediction, _mm_mullo_epi32(coefficients128[31], _mm_loadu_si128((const __m128i*)(pDecodedSamples - 32))));
+ case 31: prediction = _mm_add_epi32(prediction, _mm_mullo_epi32(coefficients128[30], _mm_loadu_si128((const __m128i*)(pDecodedSamples - 31))));
+ case 30: prediction = _mm_add_epi32(prediction, _mm_mullo_epi32(coefficients128[29], _mm_loadu_si128((const __m128i*)(pDecodedSamples - 30))));
+ case 29: prediction = _mm_add_epi32(prediction, _mm_mullo_epi32(coefficients128[28], _mm_loadu_si128((const __m128i*)(pDecodedSamples - 29))));
+ case 28: prediction = _mm_add_epi32(prediction, _mm_mullo_epi32(coefficients128[27], _mm_loadu_si128((const __m128i*)(pDecodedSamples - 28))));
+ case 27: prediction = _mm_add_epi32(prediction, _mm_mullo_epi32(coefficients128[26], _mm_loadu_si128((const __m128i*)(pDecodedSamples - 27))));
+ case 26: prediction = _mm_add_epi32(prediction, _mm_mullo_epi32(coefficients128[25], _mm_loadu_si128((const __m128i*)(pDecodedSamples - 26))));
+ case 25: prediction = _mm_add_epi32(prediction, _mm_mullo_epi32(coefficients128[24], _mm_loadu_si128((const __m128i*)(pDecodedSamples - 25))));
+ case 24: prediction = _mm_add_epi32(prediction, _mm_mullo_epi32(coefficients128[23], _mm_loadu_si128((const __m128i*)(pDecodedSamples - 24))));
+ case 23: prediction = _mm_add_epi32(prediction, _mm_mullo_epi32(coefficients128[22], _mm_loadu_si128((const __m128i*)(pDecodedSamples - 23))));
+ case 22: prediction = _mm_add_epi32(prediction, _mm_mullo_epi32(coefficients128[21], _mm_loadu_si128((const __m128i*)(pDecodedSamples - 22))));
+ case 21: prediction = _mm_add_epi32(prediction, _mm_mullo_epi32(coefficients128[20], _mm_loadu_si128((const __m128i*)(pDecodedSamples - 21))));
+ case 20: prediction = _mm_add_epi32(prediction, _mm_mullo_epi32(coefficients128[19], _mm_loadu_si128((const __m128i*)(pDecodedSamples - 20))));
+ case 19: prediction = _mm_add_epi32(prediction, _mm_mullo_epi32(coefficients128[18], _mm_loadu_si128((const __m128i*)(pDecodedSamples - 19))));
+ case 18: prediction = _mm_add_epi32(prediction, _mm_mullo_epi32(coefficients128[17], _mm_loadu_si128((const __m128i*)(pDecodedSamples - 18))));
+ case 17: prediction = _mm_add_epi32(prediction, _mm_mullo_epi32(coefficients128[16], _mm_loadu_si128((const __m128i*)(pDecodedSamples - 17))));
+ case 16: prediction = _mm_add_epi32(prediction, _mm_mullo_epi32(coefficients128[15], _mm_loadu_si128((const __m128i*)(pDecodedSamples - 16))));
+ case 15: prediction = _mm_add_epi32(prediction, _mm_mullo_epi32(coefficients128[14], _mm_loadu_si128((const __m128i*)(pDecodedSamples - 15))));
+ case 14: prediction = _mm_add_epi32(prediction, _mm_mullo_epi32(coefficients128[13], _mm_loadu_si128((const __m128i*)(pDecodedSamples - 14))));
+ case 13: prediction = _mm_add_epi32(prediction, _mm_mullo_epi32(coefficients128[12], _mm_loadu_si128((const __m128i*)(pDecodedSamples - 13))));
+
+ case 12: prediction = _mm_add_epi32(prediction, _mm_mullo_epi32(coefficients128[11], s_09_10_11_12));
+ case 11: prediction = _mm_add_epi32(prediction, _mm_mullo_epi32(coefficients128[10], drflac__mm_slide3_epi32(s_05_06_07_08, s_09_10_11_12)));
+ case 10: prediction = _mm_add_epi32(prediction, _mm_mullo_epi32(coefficients128[ 9], drflac__mm_slide2_epi32(s_05_06_07_08, s_09_10_11_12)));
+ case 9: prediction = _mm_add_epi32(prediction, _mm_mullo_epi32(coefficients128[ 8], drflac__mm_slide1_epi32(s_05_06_07_08, s_09_10_11_12)));
+ case 8: prediction = _mm_add_epi32(prediction, _mm_mullo_epi32(coefficients128[ 7], s_05_06_07_08));
+ case 7: prediction = _mm_add_epi32(prediction, _mm_mullo_epi32(coefficients128[ 6], drflac__mm_slide3_epi32(s_01_02_03_04, s_05_06_07_08)));
+ case 6: prediction = _mm_add_epi32(prediction, _mm_mullo_epi32(coefficients128[ 5], drflac__mm_slide2_epi32(s_01_02_03_04, s_05_06_07_08)));
+ case 5: prediction = _mm_add_epi32(prediction, _mm_mullo_epi32(coefficients128[ 4], drflac__mm_slide1_epi32(s_01_02_03_04, s_05_06_07_08)));
+ case 4: prediction = _mm_add_epi32(prediction, _mm_mullo_epi32(coefficients128[ 3], s_01_02_03_04)); order = 3; /* <-- Don't forget to set order to 3 here! */
+ case 3: prediction = _mm_add_epi32(prediction, _mm_mullo_epi32(coefficients128[ 2], drflac__mm_slide3_epi32(_mm_setzero_si128(), s_01_02_03_04)));
+ case 2: prediction = _mm_add_epi32(prediction, _mm_mullo_epi32(coefficients128[ 1], drflac__mm_slide2_epi32(_mm_setzero_si128(), s_01_02_03_04)));
+ case 1: prediction = _mm_add_epi32(prediction, _mm_mullo_epi32(coefficients128[ 0], drflac__mm_slide1_epi32(_mm_setzero_si128(), s_01_02_03_04)));
+ }
+
+ _mm_storeu_si128((__m128i*)predictions, prediction);
+ _mm_storeu_si128((__m128i*)riceParamParts, riceParamParts128);
+
+ predictions[0] = riceParamParts[0] + (predictions[0] >> shift);
+
+ switch (order)
+ {
+ case 3: predictions[3] += ((const drflac_int32*)&coefficients128[ 2])[0] * predictions[ 0];
+ case 2: predictions[2] += ((const drflac_int32*)&coefficients128[ 1])[0] * predictions[ 0];
+ case 1: predictions[1] += ((const drflac_int32*)&coefficients128[ 0])[0] * predictions[ 0];
+ }
+ predictions[1] = riceParamParts[1] + (predictions[1] >> shift);
+
+ switch (order)
+ {
+ case 3:
+ case 2: predictions[3] += ((const drflac_int32*)&coefficients128[ 1])[0] * predictions[ 1];
+ case 1: predictions[2] += ((const drflac_int32*)&coefficients128[ 0])[0] * predictions[ 1];
+ }
+ predictions[2] = riceParamParts[2] + (predictions[2] >> shift);
+
+ switch (order)
+ {
+ case 3:
+ case 2:
+ case 1: predictions[3] += ((const drflac_int32*)&coefficients128[ 0])[0] * predictions[ 2];
+ }
+ predictions[3] = riceParamParts[3] + (predictions[3] >> shift);
+
+ pDecodedSamples[0] = predictions[0];
+ pDecodedSamples[1] = predictions[1];
+ pDecodedSamples[2] = predictions[2];
+ pDecodedSamples[3] = predictions[3];
+ } else {
+ _mm_storeu_si128((__m128i*)pDecodedSamples, riceParamParts128);
+ }
+}
+#endif
+
#if 0
-// Reference implementation for reading and decoding samples with residual. This is intentionally left unoptimized for the
-// sake of readability and should only be used as a reference.
+/*
+Reference implementation for reading and decoding samples with residual. This is intentionally left unoptimized for the
+sake of readability and should only be used as a reference.
+*/
static drflac_bool32 drflac__decode_samples_with_residual__rice__reference(drflac_bs* bs, drflac_uint32 bitsPerSample, drflac_uint32 count, drflac_uint8 riceParam, drflac_uint32 order, drflac_int32 shift, const drflac_int32* coefficients, drflac_int32* pSamplesOut)
{
+ drflac_uint32 i;
+
drflac_assert(bs != NULL);
drflac_assert(count > 0);
drflac_assert(pSamplesOut != NULL);
- for (drflac_uint32 i = 0; i < count; ++i) {
+ for (i = 0; i < count; ++i) {
drflac_uint32 zeroCounter = 0;
for (;;) {
drflac_uint8 bit;
@@ -2193,6 +2955,8 @@ static drflac_bool32 drflac__decode_samples_with_residual__rice__reference(drfla
static drflac_bool32 drflac__read_rice_parts__reference(drflac_bs* bs, drflac_uint8 riceParam, drflac_uint32* pZeroCounterOut, drflac_uint32* pRiceParamPartOut)
{
drflac_uint32 zeroCounter = 0;
+ drflac_uint32 decodedRice;
+
for (;;) {
drflac_uint8 bit;
if (!drflac__read_uint8(bs, 1, &bit)) {
@@ -2206,7 +2970,6 @@ static drflac_bool32 drflac__read_rice_parts__reference(drflac_bs* bs, drflac_ui
}
}
- drflac_uint32 decodedRice;
if (riceParam > 0) {
if (!drflac__read_uint32(bs, riceParam, &decodedRice)) {
return DRFLAC_FALSE;
@@ -2221,13 +2984,20 @@ static drflac_bool32 drflac__read_rice_parts__reference(drflac_bs* bs, drflac_ui
}
#endif
+#if 0
static DRFLAC_INLINE drflac_bool32 drflac__read_rice_parts(drflac_bs* bs, drflac_uint8 riceParam, drflac_uint32* pZeroCounterOut, drflac_uint32* pRiceParamPartOut)
{
- drflac_cache_t riceParamMask = DRFLAC_CACHE_L1_SELECTION_MASK(riceParam);
- drflac_cache_t resultHiShift = DRFLAC_CACHE_L1_SIZE_BITS(bs) - riceParam;
+ drflac_cache_t riceParamMask;
+ drflac_uint32 zeroCounter;
+ drflac_uint32 setBitOffsetPlus1;
+ drflac_uint32 riceParamPart;
+ drflac_uint32 riceLength;
+ drflac_assert(riceParam > 0); /* <-- riceParam should never be 0. drflac__read_rice_parts__param_equals_zero() should be used instead for this case. */
- drflac_uint32 zeroCounter = 0;
+ riceParamMask = DRFLAC_CACHE_L1_SELECTION_MASK(riceParam);
+
+ zeroCounter = 0;
while (bs->cache == 0) {
zeroCounter += (drflac_uint32)DRFLAC_CACHE_L1_BITS_REMAINING(bs);
if (!drflac__reload_cache(bs)) {
@@ -2235,64 +3005,409 @@ static DRFLAC_INLINE drflac_bool32 drflac__read_rice_parts(drflac_bs* bs, drflac
}
}
- drflac_uint32 setBitOffsetPlus1 = drflac__clz(bs->cache);
+ setBitOffsetPlus1 = drflac__clz(bs->cache);
zeroCounter += setBitOffsetPlus1;
setBitOffsetPlus1 += 1;
-
- drflac_uint32 riceParamPart;
- drflac_uint32 riceLength = setBitOffsetPlus1 + riceParam;
+ riceLength = setBitOffsetPlus1 + riceParam;
if (riceLength < DRFLAC_CACHE_L1_BITS_REMAINING(bs)) {
- riceParamPart = (drflac_uint32)((bs->cache & (riceParamMask >> setBitOffsetPlus1)) >> (DRFLAC_CACHE_L1_SIZE_BITS(bs) - riceLength));
+ riceParamPart = (drflac_uint32)((bs->cache & (riceParamMask >> setBitOffsetPlus1)) >> DRFLAC_CACHE_L1_SELECTION_SHIFT(bs, riceLength));
bs->consumedBits += riceLength;
bs->cache <<= riceLength;
} else {
+ drflac_uint32 bitCountLo;
+ drflac_cache_t resultHi;
+
bs->consumedBits += riceLength;
- if (setBitOffsetPlus1 < DRFLAC_CACHE_L1_SIZE_BITS(bs)) {
- bs->cache <<= setBitOffsetPlus1;
- }
+ bs->cache <<= setBitOffsetPlus1 & (DRFLAC_CACHE_L1_SIZE_BITS(bs)-1); /* <-- Equivalent to "if (setBitOffsetPlus1 < DRFLAC_CACHE_L1_SIZE_BITS(bs)) { bs->cache <<= setBitOffsetPlus1; }" */
- // 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 bitCountLo = bs->consumedBits - DRFLAC_CACHE_L1_SIZE_BITS(bs);
- drflac_cache_t resultHi = bs->cache & riceParamMask; // <-- This mask is OK because all bits after the first bits are always zero.
+ /* 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. */
+ bitCountLo = bs->consumedBits - DRFLAC_CACHE_L1_SIZE_BITS(bs);
+ resultHi = DRFLAC_CACHE_L1_SELECT_AND_SHIFT(bs, riceParam); /* <-- Use DRFLAC_CACHE_L1_SELECT_AND_SHIFT_SAFE() if ever this function allows riceParam=0. */
if (bs->nextL2Line < DRFLAC_CACHE_L2_LINE_COUNT(bs)) {
- #ifndef DR_FLAC_NO_CRC
+#ifndef DR_FLAC_NO_CRC
drflac__update_crc16(bs);
- #endif
+#endif
bs->cache = drflac__be2host__cache_line(bs->cacheL2[bs->nextL2Line++]);
bs->consumedBits = 0;
- #ifndef DR_FLAC_NO_CRC
+#ifndef DR_FLAC_NO_CRC
bs->crc16Cache = bs->cache;
- #endif
+#endif
} else {
- // Slow path. We need to fetch more data from the client.
+ /* Slow path. We need to fetch more data from the client. */
if (!drflac__reload_cache(bs)) {
return DRFLAC_FALSE;
}
}
- riceParamPart = (drflac_uint32)((resultHi >> resultHiShift) | DRFLAC_CACHE_L1_SELECT_AND_SHIFT(bs, bitCountLo));
+ riceParamPart = (drflac_uint32)(resultHi | DRFLAC_CACHE_L1_SELECT_AND_SHIFT_SAFE(bs, bitCountLo));
bs->consumedBits += bitCountLo;
bs->cache <<= bitCountLo;
}
- *pZeroCounterOut = zeroCounter;
- *pRiceParamPartOut = riceParamPart;
+ pZeroCounterOut[0] = zeroCounter;
+ pRiceParamPartOut[0] = riceParamPart;
+
return DRFLAC_TRUE;
}
+#endif
+static DRFLAC_INLINE drflac_bool32 drflac__read_rice_parts_x1(drflac_bs* bs, drflac_uint8 riceParam, drflac_uint32* pZeroCounterOut, drflac_uint32* pRiceParamPartOut)
+{
+ drflac_uint32 riceParamPlus1 = riceParam + 1;
+ /*drflac_cache_t riceParamPlus1Mask = DRFLAC_CACHE_L1_SELECTION_MASK(riceParamPlus1);*/
+ drflac_uint32 riceParamPlus1Shift = DRFLAC_CACHE_L1_SELECTION_SHIFT(bs, riceParamPlus1);
+ drflac_uint32 riceParamPlus1MaxConsumedBits = DRFLAC_CACHE_L1_SIZE_BITS(bs) - riceParamPlus1;
+
+ /*
+ The idea here is to use local variables for the cache in an attempt to encourage the compiler to store them in registers. I have
+ no idea how this will work in practice...
+ */
+ drflac_cache_t bs_cache = bs->cache;
+ drflac_uint32 bs_consumedBits = bs->consumedBits;
+
+ /* The first thing to do is find the first unset bit. Most likely a bit will be set in the current cache line. */
+ drflac_uint32 lzcount = drflac__clz(bs_cache);
+ if (lzcount < sizeof(bs_cache)*8) {
+ pZeroCounterOut[0] = lzcount;
+
+ /*
+ It is most likely that the riceParam part (which comes after the zero counter) is also on this cache line. When extracting
+ this, we include the set bit from the unary coded part because it simplifies cache management. This bit will be handled
+ outside of this function at a higher level.
+ */
+ extract_rice_param_part:
+ bs_cache <<= lzcount;
+ bs_consumedBits += lzcount;
+
+ if (bs_consumedBits <= riceParamPlus1MaxConsumedBits) {
+ /* Getting here means the rice parameter part is wholly contained within the current cache line. */
+ pRiceParamPartOut[0] = (drflac_uint32)(bs_cache >> riceParamPlus1Shift);
+ bs_cache <<= riceParamPlus1;
+ bs_consumedBits += riceParamPlus1;
+ } else {
+ drflac_uint32 riceParamPartHi;
+ drflac_uint32 riceParamPartLo;
+ drflac_uint32 riceParamPartLoBitCount;
+
+ /*
+ Getting here means the rice parameter part straddles the cache line. We need to read from the tail of the current cache
+ line, reload the cache, and then combine it with the head of the next cache line.
+ */
+
+ /* Grab the high part of the rice parameter part. */
+ riceParamPartHi = (drflac_uint32)(bs_cache >> riceParamPlus1Shift);
+
+ /* Before reloading the cache we need to grab the size in bits of the low part. */
+ riceParamPartLoBitCount = bs_consumedBits - riceParamPlus1MaxConsumedBits;
+ drflac_assert(riceParamPartLoBitCount > 0 && riceParamPartLoBitCount < 32);
+
+ /* Now reload the cache. */
+ if (bs->nextL2Line < DRFLAC_CACHE_L2_LINE_COUNT(bs)) {
+ #ifndef DR_FLAC_NO_CRC
+ drflac__update_crc16(bs);
+ #endif
+ bs_cache = drflac__be2host__cache_line(bs->cacheL2[bs->nextL2Line++]);
+ bs_consumedBits = riceParamPartLoBitCount;
+ #ifndef DR_FLAC_NO_CRC
+ bs->crc16Cache = bs_cache;
+ #endif
+ } else {
+ /* Slow path. We need to fetch more data from the client. */
+ if (!drflac__reload_cache(bs)) {
+ return DRFLAC_FALSE;
+ }
-static drflac_bool32 drflac__decode_samples_with_residual__rice__simple(drflac_bs* bs, drflac_uint32 bitsPerSample, drflac_uint32 count, drflac_uint8 riceParam, drflac_uint32 order, drflac_int32 shift, const drflac_int32* coefficients, drflac_int32* pSamplesOut)
-{
- drflac_assert(bs != NULL);
- drflac_assert(count > 0);
- drflac_assert(pSamplesOut != NULL);
+ bs_cache = bs->cache;
+ bs_consumedBits = bs->consumedBits + riceParamPartLoBitCount;
+ }
- static drflac_uint32 t[2] = {0x00000000, 0xFFFFFFFF};
+ /* We should now have enough information to construct the rice parameter part. */
+ riceParamPartLo = (drflac_uint32)(bs_cache >> (DRFLAC_CACHE_L1_SELECTION_SHIFT(bs, riceParamPartLoBitCount)));
+ pRiceParamPartOut[0] = riceParamPartHi | riceParamPartLo;
+
+ bs_cache <<= riceParamPartLoBitCount;
+ }
+ } else {
+ /*
+ Getting here means there are no bits set on the cache line. This is a less optimal case because we just wasted a call
+ to drflac__clz() and we need to reload the cache.
+ */
+ drflac_uint32 zeroCounter = (drflac_uint32)(DRFLAC_CACHE_L1_SIZE_BITS(bs) - bs_consumedBits);
+ for (;;) {
+ if (bs->nextL2Line < DRFLAC_CACHE_L2_LINE_COUNT(bs)) {
+ #ifndef DR_FLAC_NO_CRC
+ drflac__update_crc16(bs);
+ #endif
+ bs_cache = drflac__be2host__cache_line(bs->cacheL2[bs->nextL2Line++]);
+ bs_consumedBits = 0;
+ #ifndef DR_FLAC_NO_CRC
+ bs->crc16Cache = bs_cache;
+ #endif
+ } else {
+ /* Slow path. We need to fetch more data from the client. */
+ if (!drflac__reload_cache(bs)) {
+ return DRFLAC_FALSE;
+ }
+
+ bs_cache = bs->cache;
+ bs_consumedBits = bs->consumedBits;
+ }
+
+ lzcount = drflac__clz(bs_cache);
+ zeroCounter += lzcount;
+
+ if (lzcount < sizeof(bs_cache)*8) {
+ break;
+ }
+ }
+
+ pZeroCounterOut[0] = zeroCounter;
+ goto extract_rice_param_part;
+ }
+
+ /* Make sure the cache is restored at the end of it all. */
+ bs->cache = bs_cache;
+ bs->consumedBits = bs_consumedBits;
+
+ return DRFLAC_TRUE;
+}
+
+static DRFLAC_INLINE drflac_bool32 drflac__read_rice_parts_x4(drflac_bs* bs, drflac_uint8 riceParam, drflac_uint32* pZeroCounterOut, drflac_uint32* pRiceParamPartOut)
+{
+ drflac_uint32 riceParamPlus1 = riceParam + 1;
+ /*drflac_cache_t riceParamPlus1Mask = DRFLAC_CACHE_L1_SELECTION_MASK(riceParamPlus1);*/
+ drflac_uint32 riceParamPlus1Shift = DRFLAC_CACHE_L1_SELECTION_SHIFT(bs, riceParamPlus1);
+ drflac_uint32 riceParamPlus1MaxConsumedBits = DRFLAC_CACHE_L1_SIZE_BITS(bs) - riceParamPlus1;
+
+ /*
+ The idea here is to use local variables for the cache in an attempt to encourage the compiler to store them in registers. I have
+ no idea how this will work in practice...
+ */
+ drflac_cache_t bs_cache = bs->cache;
+ drflac_uint32 bs_consumedBits = bs->consumedBits;
+
+ /*
+ What this is doing is trying to efficiently extract 4 rice parts at a time, the idea being that we can exploit certain properties
+ to our advantage to make things more efficient.
+ */
+ int i;
+ for (i = 0; i < 4; ++i) {
+ /* The first thing to do is find the first unset bit. Most likely a bit will be set in the current cache line. */
+ drflac_uint32 lzcount = drflac__clz(bs_cache);
+ if (lzcount < sizeof(bs_cache)*8) {
+ pZeroCounterOut[i] = lzcount;
+
+ /*
+ It is most likely that the riceParam part (which comes after the zero counter) is also on this cache line. When extracting
+ this, we include the set bit from the unary coded part because it simplifies cache management. This bit will be handled
+ outside of this function at a higher level.
+ */
+ extract_rice_param_part:
+ bs_cache <<= lzcount;
+ bs_consumedBits += lzcount;
+
+ if (bs_consumedBits <= riceParamPlus1MaxConsumedBits) {
+ /* Getting here means the rice parameter part is wholly contained within the current cache line. */
+ pRiceParamPartOut[i] = (drflac_uint32)(bs_cache >> riceParamPlus1Shift);
+ bs_cache <<= riceParamPlus1;
+ bs_consumedBits += riceParamPlus1;
+ } else {
+ drflac_uint32 riceParamPartHi;
+ drflac_uint32 riceParamPartLo;
+ drflac_uint32 riceParamPartLoBitCount;
+
+ /*
+ Getting here means the rice parameter part straddles the cache line. We need to read from the tail of the current cache
+ line, reload the cache, and then combine it with the head of the next cache line.
+ */
+
+ /* Grab the high part of the rice parameter part. */
+ riceParamPartHi = (drflac_uint32)(bs_cache >> riceParamPlus1Shift);
+
+ /* Before reloading the cache we need to grab the size in bits of the low part. */
+ riceParamPartLoBitCount = bs_consumedBits - riceParamPlus1MaxConsumedBits;
+
+ /* Now reload the cache. */
+ if (bs->nextL2Line < DRFLAC_CACHE_L2_LINE_COUNT(bs)) {
+ #ifndef DR_FLAC_NO_CRC
+ drflac__update_crc16(bs);
+ #endif
+ bs_cache = drflac__be2host__cache_line(bs->cacheL2[bs->nextL2Line++]);
+ bs_consumedBits = riceParamPartLoBitCount;
+ #ifndef DR_FLAC_NO_CRC
+ bs->crc16Cache = bs_cache;
+ #endif
+ } else {
+ /* Slow path. We need to fetch more data from the client. */
+ if (!drflac__reload_cache(bs)) {
+ return DRFLAC_FALSE;
+ }
+
+ bs_cache = bs->cache;
+ bs_consumedBits = bs->consumedBits + riceParamPartLoBitCount;
+ }
+
+ /* We should now have enough information to construct the rice parameter part. */
+ riceParamPartLo = (drflac_uint32)(bs_cache >> (DRFLAC_CACHE_L1_SELECTION_SHIFT(bs, riceParamPartLoBitCount)));
+ pRiceParamPartOut[i] = riceParamPartHi | riceParamPartLo;
+
+ bs_cache <<= riceParamPartLoBitCount;
+ }
+ } else {
+ /*
+ Getting here means there are no bits set on the cache line. This is a less optimal case because we just wasted a call
+ to drflac__clz() and we need to reload the cache.
+ */
+ drflac_uint32 zeroCounter = (drflac_uint32)(DRFLAC_CACHE_L1_SIZE_BITS(bs) - bs_consumedBits);
+ for (;;) {
+ if (bs->nextL2Line < DRFLAC_CACHE_L2_LINE_COUNT(bs)) {
+ #ifndef DR_FLAC_NO_CRC
+ drflac__update_crc16(bs);
+ #endif
+ bs_cache = drflac__be2host__cache_line(bs->cacheL2[bs->nextL2Line++]);
+ bs_consumedBits = 0;
+ #ifndef DR_FLAC_NO_CRC
+ bs->crc16Cache = bs_cache;
+ #endif
+ } else {
+ /* Slow path. We need to fetch more data from the client. */
+ if (!drflac__reload_cache(bs)) {
+ return DRFLAC_FALSE;
+ }
+
+ bs_cache = bs->cache;
+ bs_consumedBits = bs->consumedBits;
+ }
+
+ lzcount = drflac__clz(bs_cache);
+ zeroCounter += lzcount;
+
+ if (lzcount < sizeof(bs_cache)*8) {
+ break;
+ }
+ }
+
+ pZeroCounterOut[i] = zeroCounter;
+ goto extract_rice_param_part;
+ }
+ }
+
+ /* Make sure the cache is restored at the end of it all. */
+ bs->cache = bs_cache;
+ bs->consumedBits = bs_consumedBits;
+
+ return DRFLAC_TRUE;
+}
+
+static DRFLAC_INLINE drflac_bool32 drflac__seek_rice_parts(drflac_bs* bs, drflac_uint8 riceParam)
+{
+ drflac_uint32 riceParamPlus1 = riceParam + 1;
+ drflac_uint32 riceParamPlus1MaxConsumedBits = DRFLAC_CACHE_L1_SIZE_BITS(bs) - riceParamPlus1;
+
+ /*
+ The idea here is to use local variables for the cache in an attempt to encourage the compiler to store them in registers. I have
+ no idea how this will work in practice...
+ */
+ drflac_cache_t bs_cache = bs->cache;
+ drflac_uint32 bs_consumedBits = bs->consumedBits;
+
+ /* The first thing to do is find the first unset bit. Most likely a bit will be set in the current cache line. */
+ drflac_uint32 lzcount = drflac__clz(bs_cache);
+ if (lzcount < sizeof(bs_cache)*8) {
+ /*
+ It is most likely that the riceParam part (which comes after the zero counter) is also on this cache line. When extracting
+ this, we include the set bit from the unary coded part because it simplifies cache management. This bit will be handled
+ outside of this function at a higher level.
+ */
+ extract_rice_param_part:
+ bs_cache <<= lzcount;
+ bs_consumedBits += lzcount;
+
+ if (bs_consumedBits <= riceParamPlus1MaxConsumedBits) {
+ /* Getting here means the rice parameter part is wholly contained within the current cache line. */
+ bs_cache <<= riceParamPlus1;
+ bs_consumedBits += riceParamPlus1;
+ } else {
+ /*
+ Getting here means the rice parameter part straddles the cache line. We need to read from the tail of the current cache
+ line, reload the cache, and then combine it with the head of the next cache line.
+ */
+
+ /* Before reloading the cache we need to grab the size in bits of the low part. */
+ drflac_uint32 riceParamPartLoBitCount = bs_consumedBits - riceParamPlus1MaxConsumedBits;
+ drflac_assert(riceParamPartLoBitCount > 0 && riceParamPartLoBitCount < 32);
+
+ /* Now reload the cache. */
+ if (bs->nextL2Line < DRFLAC_CACHE_L2_LINE_COUNT(bs)) {
+ #ifndef DR_FLAC_NO_CRC
+ drflac__update_crc16(bs);
+ #endif
+ bs_cache = drflac__be2host__cache_line(bs->cacheL2[bs->nextL2Line++]);
+ bs_consumedBits = riceParamPartLoBitCount;
+ #ifndef DR_FLAC_NO_CRC
+ bs->crc16Cache = bs_cache;
+ #endif
+ } else {
+ /* Slow path. We need to fetch more data from the client. */
+ if (!drflac__reload_cache(bs)) {
+ return DRFLAC_FALSE;
+ }
+
+ bs_cache = bs->cache;
+ bs_consumedBits = bs->consumedBits + riceParamPartLoBitCount;
+ }
+
+ bs_cache <<= riceParamPartLoBitCount;
+ }
+ } else {
+ /*
+ Getting here means there are no bits set on the cache line. This is a less optimal case because we just wasted a call
+ to drflac__clz() and we need to reload the cache.
+ */
+ for (;;) {
+ if (bs->nextL2Line < DRFLAC_CACHE_L2_LINE_COUNT(bs)) {
+ #ifndef DR_FLAC_NO_CRC
+ drflac__update_crc16(bs);
+ #endif
+ bs_cache = drflac__be2host__cache_line(bs->cacheL2[bs->nextL2Line++]);
+ bs_consumedBits = 0;
+ #ifndef DR_FLAC_NO_CRC
+ bs->crc16Cache = bs_cache;
+ #endif
+ } else {
+ /* Slow path. We need to fetch more data from the client. */
+ if (!drflac__reload_cache(bs)) {
+ return DRFLAC_FALSE;
+ }
+
+ bs_cache = bs->cache;
+ bs_consumedBits = bs->consumedBits;
+ }
+ lzcount = drflac__clz(bs_cache);
+ if (lzcount < sizeof(bs_cache)*8) {
+ break;
+ }
+ }
+
+ goto extract_rice_param_part;
+ }
+
+ /* Make sure the cache is restored at the end of it all. */
+ bs->cache = bs_cache;
+ bs->consumedBits = bs_consumedBits;
+
+ return DRFLAC_TRUE;
+}
+
+
+static drflac_bool32 drflac__decode_samples_with_residual__rice__scalar(drflac_bs* bs, drflac_uint32 bitsPerSample, drflac_uint32 count, drflac_uint8 riceParam, drflac_uint32 order, drflac_int32 shift, const drflac_int32* coefficients, drflac_int32* pSamplesOut)
+{
+ drflac_uint32 t[2] = {0x00000000, 0xFFFFFFFF};
drflac_uint32 zeroCountPart0;
drflac_uint32 zeroCountPart1;
drflac_uint32 zeroCountPart2;
@@ -2301,57 +3416,100 @@ static drflac_bool32 drflac__decode_samples_with_residual__rice__simple(drflac_b
drflac_uint32 riceParamPart1;
drflac_uint32 riceParamPart2;
drflac_uint32 riceParamPart3;
- drflac_uint32 i4 = 0;
- drflac_uint32 count4 = count >> 2;
- while (i4 < count4) {
- // Rice extraction.
- if (!drflac__read_rice_parts(bs, riceParam, &zeroCountPart0, &riceParamPart0) ||
- !drflac__read_rice_parts(bs, riceParam, &zeroCountPart1, &riceParamPart1) ||
- !drflac__read_rice_parts(bs, riceParam, &zeroCountPart2, &riceParamPart2) ||
- !drflac__read_rice_parts(bs, riceParam, &zeroCountPart3, &riceParamPart3)) {
- return DRFLAC_FALSE;
- }
+ drflac_uint32 riceParamMask;
+ const drflac_int32* pSamplesOutEnd;
+ drflac_uint32 i;
- riceParamPart0 |= (zeroCountPart0 << riceParam);
- riceParamPart1 |= (zeroCountPart1 << riceParam);
- riceParamPart2 |= (zeroCountPart2 << riceParam);
- riceParamPart3 |= (zeroCountPart3 << riceParam);
+ drflac_assert(bs != NULL);
+ drflac_assert(count > 0);
+ drflac_assert(pSamplesOut != NULL);
- riceParamPart0 = (riceParamPart0 >> 1) ^ t[riceParamPart0 & 0x01];
- riceParamPart1 = (riceParamPart1 >> 1) ^ t[riceParamPart1 & 0x01];
- riceParamPart2 = (riceParamPart2 >> 1) ^ t[riceParamPart2 & 0x01];
- riceParamPart3 = (riceParamPart3 >> 1) ^ t[riceParamPart3 & 0x01];
+ riceParamMask = ~((~0UL) << riceParam);
+ pSamplesOutEnd = pSamplesOut + ((count >> 2) << 2);
+
+ if (bitsPerSample >= 24) {
+ while (pSamplesOut < pSamplesOutEnd) {
+ /*
+ Rice extraction. It's faster to do this one at a time against local variables than it is to use the x4 version
+ against an array. Not sure why, but perhaps it's making more efficient use of registers?
+ */
+ if (!drflac__read_rice_parts_x1(bs, riceParam, &zeroCountPart0, &riceParamPart0) ||
+ !drflac__read_rice_parts_x1(bs, riceParam, &zeroCountPart1, &riceParamPart1) ||
+ !drflac__read_rice_parts_x1(bs, riceParam, &zeroCountPart2, &riceParamPart2) ||
+ !drflac__read_rice_parts_x1(bs, riceParam, &zeroCountPart3, &riceParamPart3)) {
+ return DRFLAC_FALSE;
+ }
+
+ riceParamPart0 &= riceParamMask;
+ riceParamPart1 &= riceParamMask;
+ riceParamPart2 &= riceParamMask;
+ riceParamPart3 &= riceParamMask;
+
+ riceParamPart0 |= (zeroCountPart0 << riceParam);
+ riceParamPart1 |= (zeroCountPart1 << riceParam);
+ riceParamPart2 |= (zeroCountPart2 << riceParam);
+ riceParamPart3 |= (zeroCountPart3 << riceParam);
+
+ riceParamPart0 = (riceParamPart0 >> 1) ^ t[riceParamPart0 & 0x01];
+ riceParamPart1 = (riceParamPart1 >> 1) ^ t[riceParamPart1 & 0x01];
+ riceParamPart2 = (riceParamPart2 >> 1) ^ t[riceParamPart2 & 0x01];
+ riceParamPart3 = (riceParamPart3 >> 1) ^ t[riceParamPart3 & 0x01];
- if (bitsPerSample > 16) {
pSamplesOut[0] = riceParamPart0 + drflac__calculate_prediction_64(order, shift, coefficients, pSamplesOut + 0);
pSamplesOut[1] = riceParamPart1 + drflac__calculate_prediction_64(order, shift, coefficients, pSamplesOut + 1);
pSamplesOut[2] = riceParamPart2 + drflac__calculate_prediction_64(order, shift, coefficients, pSamplesOut + 2);
pSamplesOut[3] = riceParamPart3 + drflac__calculate_prediction_64(order, shift, coefficients, pSamplesOut + 3);
- } else {
+
+ pSamplesOut += 4;
+ }
+ } else {
+ while (pSamplesOut < pSamplesOutEnd) {
+ if (!drflac__read_rice_parts_x1(bs, riceParam, &zeroCountPart0, &riceParamPart0) ||
+ !drflac__read_rice_parts_x1(bs, riceParam, &zeroCountPart1, &riceParamPart1) ||
+ !drflac__read_rice_parts_x1(bs, riceParam, &zeroCountPart2, &riceParamPart2) ||
+ !drflac__read_rice_parts_x1(bs, riceParam, &zeroCountPart3, &riceParamPart3)) {
+ return DRFLAC_FALSE;
+ }
+
+ riceParamPart0 &= riceParamMask;
+ riceParamPart1 &= riceParamMask;
+ riceParamPart2 &= riceParamMask;
+ riceParamPart3 &= riceParamMask;
+
+ riceParamPart0 |= (zeroCountPart0 << riceParam);
+ riceParamPart1 |= (zeroCountPart1 << riceParam);
+ riceParamPart2 |= (zeroCountPart2 << riceParam);
+ riceParamPart3 |= (zeroCountPart3 << riceParam);
+
+ riceParamPart0 = (riceParamPart0 >> 1) ^ t[riceParamPart0 & 0x01];
+ riceParamPart1 = (riceParamPart1 >> 1) ^ t[riceParamPart1 & 0x01];
+ riceParamPart2 = (riceParamPart2 >> 1) ^ t[riceParamPart2 & 0x01];
+ riceParamPart3 = (riceParamPart3 >> 1) ^ t[riceParamPart3 & 0x01];
+
pSamplesOut[0] = riceParamPart0 + drflac__calculate_prediction_32(order, shift, coefficients, pSamplesOut + 0);
pSamplesOut[1] = riceParamPart1 + drflac__calculate_prediction_32(order, shift, coefficients, pSamplesOut + 1);
pSamplesOut[2] = riceParamPart2 + drflac__calculate_prediction_32(order, shift, coefficients, pSamplesOut + 2);
pSamplesOut[3] = riceParamPart3 + drflac__calculate_prediction_32(order, shift, coefficients, pSamplesOut + 3);
- }
- i4 += 1;
- pSamplesOut += 4;
+ pSamplesOut += 4;
+ }
}
- drflac_uint32 i = i4 << 2;
+ i = ((count >> 2) << 2);
while (i < count) {
- // Rice extraction.
- if (!drflac__read_rice_parts(bs, riceParam, &zeroCountPart0, &riceParamPart0)) {
+ /* Rice extraction. */
+ if (!drflac__read_rice_parts_x1(bs, riceParam, &zeroCountPart0, &riceParamPart0)) {
return DRFLAC_FALSE;
}
- // Rice reconstruction.
+ /* Rice reconstruction. */
+ riceParamPart0 &= riceParamMask;
riceParamPart0 |= (zeroCountPart0 << riceParam);
riceParamPart0 = (riceParamPart0 >> 1) ^ t[riceParamPart0 & 0x01];
- //riceParamPart0 = (riceParamPart0 >> 1) ^ (~(riceParamPart0 & 0x01) + 1);
+ /*riceParamPart0 = (riceParamPart0 >> 1) ^ (~(riceParamPart0 & 0x01) + 1);*/
- // Sample reconstruction.
- if (bitsPerSample > 16) {
+ /* Sample reconstruction. */
+ if (bitsPerSample >= 24) {
pSamplesOut[0] = riceParamPart0 + drflac__calculate_prediction_64(order, shift, coefficients, pSamplesOut + 0);
} else {
pSamplesOut[0] = riceParamPart0 + drflac__calculate_prediction_32(order, shift, coefficients, pSamplesOut + 0);
@@ -2360,29 +3518,188 @@ static drflac_bool32 drflac__decode_samples_with_residual__rice__simple(drflac_b
i += 1;
pSamplesOut += 1;
}
+
+ return DRFLAC_TRUE;
+}
+
+#if defined(DRFLAC_SUPPORT_SSE41)
+static drflac_bool32 drflac__decode_samples_with_residual__rice__sse41(drflac_bs* bs, drflac_uint32 bitsPerSample, drflac_uint32 count, drflac_uint8 riceParam, drflac_uint32 order, drflac_int32 shift, const drflac_int32* coefficients, drflac_int32* pSamplesOut)
+{
+ static drflac_uint32 t[2] = {0x00000000, 0xFFFFFFFF};
+
+ /*drflac_uint32 zeroCountParts[4];*/
+ /*drflac_uint32 riceParamParts[4];*/
+
+ drflac_uint32 zeroCountParts0;
+ drflac_uint32 zeroCountParts1;
+ drflac_uint32 zeroCountParts2;
+ drflac_uint32 zeroCountParts3;
+ drflac_uint32 riceParamParts0;
+ drflac_uint32 riceParamParts1;
+ drflac_uint32 riceParamParts2;
+ drflac_uint32 riceParamParts3;
+ drflac_uint32 riceParamMask;
+ const drflac_int32* pSamplesOutEnd;
+ __m128i riceParamMask128;
+ __m128i one;
+ drflac_uint32 i;
+
+ drflac_assert(bs != NULL);
+ drflac_assert(count > 0);
+ drflac_assert(pSamplesOut != NULL);
+
+ riceParamMask = ~((~0UL) << riceParam);
+ riceParamMask128 = _mm_set1_epi32(riceParamMask);
+ one = _mm_set1_epi32(0x01);
+
+ pSamplesOutEnd = pSamplesOut + ((count >> 2) << 2);
+
+ if (bitsPerSample >= 24) {
+ while (pSamplesOut < pSamplesOutEnd) {
+ __m128i zeroCountPart128;
+ __m128i riceParamPart128;
+ drflac_uint32 riceParamParts[4];
+
+ /* Rice extraction. */
+ if (!drflac__read_rice_parts_x1(bs, riceParam, &zeroCountParts0, &riceParamParts0) ||
+ !drflac__read_rice_parts_x1(bs, riceParam, &zeroCountParts1, &riceParamParts1) ||
+ !drflac__read_rice_parts_x1(bs, riceParam, &zeroCountParts2, &riceParamParts2) ||
+ !drflac__read_rice_parts_x1(bs, riceParam, &zeroCountParts3, &riceParamParts3)) {
+ return DRFLAC_FALSE;
+ }
+
+ zeroCountPart128 = _mm_set_epi32(zeroCountParts3, zeroCountParts2, zeroCountParts1, zeroCountParts0);
+ riceParamPart128 = _mm_set_epi32(riceParamParts3, riceParamParts2, riceParamParts1, riceParamParts0);
+
+ riceParamPart128 = _mm_and_si128(riceParamPart128, riceParamMask128);
+ riceParamPart128 = _mm_or_si128(riceParamPart128, _mm_slli_epi32(zeroCountPart128, riceParam));
+ riceParamPart128 = _mm_xor_si128(_mm_srli_epi32(riceParamPart128, 1), _mm_mullo_epi32(_mm_and_si128(riceParamPart128, one), _mm_set1_epi32(0xFFFFFFFF))); /* <-- Only supported from SSE4.1 */
+ /*riceParamPart128 = _mm_xor_si128(_mm_srli_epi32(riceParamPart128, 1), _mm_add_epi32(drflac__mm_not_si128(_mm_and_si128(riceParamPart128, one)), one));*/ /* <-- SSE2 compatible */
+
+ _mm_storeu_si128((__m128i*)riceParamParts, riceParamPart128);
+
+ #if defined(DRFLAC_64BIT)
+ /* The scalar implementation seems to be faster on 64-bit in my testing. */
+ drflac__calculate_prediction_64_x4(order, shift, coefficients, riceParamParts, pSamplesOut);
+ #else
+ pSamplesOut[0] = riceParamParts[0] + drflac__calculate_prediction_64__sse41(order, shift, coefficients, pSamplesOut + 0);
+ pSamplesOut[1] = riceParamParts[1] + drflac__calculate_prediction_64__sse41(order, shift, coefficients, pSamplesOut + 1);
+ pSamplesOut[2] = riceParamParts[2] + drflac__calculate_prediction_64__sse41(order, shift, coefficients, pSamplesOut + 2);
+ pSamplesOut[3] = riceParamParts[3] + drflac__calculate_prediction_64__sse41(order, shift, coefficients, pSamplesOut + 3);
+ #endif
+
+ pSamplesOut += 4;
+ }
+ } else {
+ drflac_int32 coefficientsUnaligned[32*4 + 4] = {0};
+ drflac_int32* coefficients128 = (drflac_int32*)(((size_t)coefficientsUnaligned + 15) & ~15);
+
+ for (i = 0; i < order; ++i) {
+ coefficients128[i*4+0] = coefficients[i];
+ coefficients128[i*4+1] = coefficients[i];
+ coefficients128[i*4+2] = coefficients[i];
+ coefficients128[i*4+3] = coefficients[i];
+ }
+
+ while (pSamplesOut < pSamplesOutEnd) {
+ __m128i zeroCountPart128;
+ __m128i riceParamPart128;
+ /*drflac_int32 riceParamParts[4];*/
+
+ /* Rice extraction. */
+#if 1
+ if (!drflac__read_rice_parts_x1(bs, riceParam, &zeroCountParts0, &riceParamParts0) ||
+ !drflac__read_rice_parts_x1(bs, riceParam, &zeroCountParts1, &riceParamParts1) ||
+ !drflac__read_rice_parts_x1(bs, riceParam, &zeroCountParts2, &riceParamParts2) ||
+ !drflac__read_rice_parts_x1(bs, riceParam, &zeroCountParts3, &riceParamParts3)) {
+ return DRFLAC_FALSE;
+ }
+
+ zeroCountPart128 = _mm_set_epi32(zeroCountParts3, zeroCountParts2, zeroCountParts1, zeroCountParts0);
+ riceParamPart128 = _mm_set_epi32(riceParamParts3, riceParamParts2, riceParamParts1, riceParamParts0);
+#else
+ if (!drflac__read_rice_parts_x4(bs, riceParam, zeroCountParts, riceParamParts)) {
+ return DRFLAC_FALSE;
+ }
+
+ zeroCountPart128 = _mm_set_epi32(zeroCountParts[3], zeroCountParts[2], zeroCountParts[1], zeroCountParts[0]);
+ riceParamPart128 = _mm_set_epi32(riceParamParts[3], riceParamParts[2], riceParamParts[1], riceParamParts[0]);
+#endif
+
+ riceParamPart128 = _mm_and_si128(riceParamPart128, riceParamMask128);
+ riceParamPart128 = _mm_or_si128(riceParamPart128, _mm_slli_epi32(zeroCountPart128, riceParam));
+ riceParamPart128 = _mm_xor_si128(_mm_srli_epi32(riceParamPart128, 1), _mm_mullo_epi32(_mm_and_si128(riceParamPart128, one), _mm_set1_epi32(0xFFFFFFFF)));
+
+#if 1
+ drflac__calculate_prediction_32_x4__sse41(order, shift, (const __m128i*)coefficients128, riceParamPart128, pSamplesOut);
+#else
+ _mm_storeu_si128((__m128i*)riceParamParts, riceParamPart128);
+
+ pSamplesOut[0] = riceParamParts[0] + drflac__calculate_prediction_32(order, shift, coefficients, pSamplesOut + 0);
+ pSamplesOut[1] = riceParamParts[1] + drflac__calculate_prediction_32(order, shift, coefficients, pSamplesOut + 1);
+ pSamplesOut[2] = riceParamParts[2] + drflac__calculate_prediction_32(order, shift, coefficients, pSamplesOut + 2);
+ pSamplesOut[3] = riceParamParts[3] + drflac__calculate_prediction_32(order, shift, coefficients, pSamplesOut + 3);
+#endif
+
+ pSamplesOut += 4;
+ }
+ }
+
+
+ i = ((count >> 2) << 2);
+ while (i < count) {
+ /* Rice extraction. */
+ if (!drflac__read_rice_parts_x1(bs, riceParam, &zeroCountParts0, &riceParamParts0)) {
+ return DRFLAC_FALSE;
+ }
+
+ /* Rice reconstruction. */
+ riceParamParts0 &= riceParamMask;
+ riceParamParts0 |= (zeroCountParts0 << riceParam);
+ riceParamParts0 = (riceParamParts0 >> 1) ^ t[riceParamParts0 & 0x01];
+
+ /* Sample reconstruction. */
+ if (bitsPerSample >= 24) {
+ pSamplesOut[0] = riceParamParts0 + drflac__calculate_prediction_64(order, shift, coefficients, pSamplesOut + 0);
+ } else {
+ pSamplesOut[0] = riceParamParts0 + drflac__calculate_prediction_32(order, shift, coefficients, pSamplesOut + 0);
+ }
+
+ i += 1;
+ pSamplesOut += 1;
+ }
return DRFLAC_TRUE;
}
+#endif
static drflac_bool32 drflac__decode_samples_with_residual__rice(drflac_bs* bs, drflac_uint32 bitsPerSample, drflac_uint32 count, drflac_uint8 riceParam, drflac_uint32 order, drflac_int32 shift, const drflac_int32* coefficients, drflac_int32* pSamplesOut)
{
-#if 0
- return drflac__decode_samples_with_residual__rice__reference(bs, bitsPerSample, count, riceParam, order, shift, coefficients, pSamplesOut);
-#else
- return drflac__decode_samples_with_residual__rice__simple(bs, bitsPerSample, count, riceParam, order, shift, coefficients, pSamplesOut);
+#if defined(DRFLAC_SUPPORT_SSE41)
+ if (drflac__gIsSSE41Supported) {
+ return drflac__decode_samples_with_residual__rice__sse41(bs, bitsPerSample, count, riceParam, order, shift, coefficients, pSamplesOut);
+ } else
#endif
+ {
+ /* Scalar fallback. */
+ #if 0
+ return drflac__decode_samples_with_residual__rice__reference(bs, bitsPerSample, count, riceParam, order, shift, coefficients, pSamplesOut);
+ #else
+ return drflac__decode_samples_with_residual__rice__scalar(bs, bitsPerSample, count, riceParam, order, shift, coefficients, pSamplesOut);
+ #endif
+ }
}
-// Reads and seeks past a string of residual values as Rice codes. The decoder should be sitting on the first bit of the Rice codes.
+/* Reads and seeks past a string of residual values as Rice codes. The decoder should be sitting on the first bit of the Rice codes. */
static drflac_bool32 drflac__read_and_seek_residual__rice(drflac_bs* bs, drflac_uint32 count, drflac_uint8 riceParam)
{
+ drflac_uint32 i;
+
drflac_assert(bs != NULL);
drflac_assert(count > 0);
- for (drflac_uint32 i = 0; i < count; ++i) {
- drflac_uint32 zeroCountPart;
- drflac_uint32 riceParamPart;
- if (!drflac__read_rice_parts(bs, riceParam, &zeroCountPart, &riceParamPart)) {
+ for (i = 0; i < count; ++i) {
+ if (!drflac__seek_rice_parts(bs, riceParam)) {
return DRFLAC_FALSE;
}
}
@@ -2392,14 +3709,20 @@ static drflac_bool32 drflac__read_and_seek_residual__rice(drflac_bs* bs, drflac_
static drflac_bool32 drflac__decode_samples_with_residual__unencoded(drflac_bs* bs, drflac_uint32 bitsPerSample, drflac_uint32 count, drflac_uint8 unencodedBitsPerSample, drflac_uint32 order, drflac_int32 shift, const drflac_int32* coefficients, drflac_int32* pSamplesOut)
{
+ drflac_uint32 i;
+
drflac_assert(bs != NULL);
drflac_assert(count > 0);
- drflac_assert(unencodedBitsPerSample > 0 && unencodedBitsPerSample <= 32);
+ drflac_assert(unencodedBitsPerSample <= 31); /* <-- unencodedBitsPerSample is a 5 bit number, so cannot exceed 31. */
drflac_assert(pSamplesOut != NULL);
- for (unsigned int i = 0; i < count; ++i) {
- if (!drflac__read_int32(bs, unencodedBitsPerSample, pSamplesOut + i)) {
- return DRFLAC_FALSE;
+ for (i = 0; i < count; ++i) {
+ if (unencodedBitsPerSample > 0) {
+ if (!drflac__read_int32(bs, unencodedBitsPerSample, pSamplesOut + i)) {
+ return DRFLAC_FALSE;
+ }
+ } else {
+ pSamplesOut[i] = 0;
}
if (bitsPerSample > 16) {
@@ -2413,60 +3736,66 @@ static drflac_bool32 drflac__decode_samples_with_residual__unencoded(drflac_bs*
}
-// Reads and decodes the residual for the sub-frame the decoder is currently sitting on. This function should be called
-// when the decoder is sitting at the very start of the RESIDUAL block. The first <order> residuals will be ignored. The
-// <blockSize> and <order> parameters are used to determine how many residual values need to be decoded.
+/*
+Reads and decodes the residual for the sub-frame the decoder is currently sitting on. This function should be called
+when the decoder is sitting at the very start of the RESIDUAL block. The first <order> residuals will be ignored. The
+<blockSize> and <order> parameters are used to determine how many residual values need to be decoded.
+*/
static drflac_bool32 drflac__decode_samples_with_residual(drflac_bs* bs, drflac_uint32 bitsPerSample, drflac_uint32 blockSize, drflac_uint32 order, drflac_int32 shift, const drflac_int32* coefficients, drflac_int32* pDecodedSamples)
{
+ drflac_uint8 residualMethod;
+ drflac_uint8 partitionOrder;
+ drflac_uint32 samplesInPartition;
+ drflac_uint32 partitionsRemaining;
+
drflac_assert(bs != NULL);
drflac_assert(blockSize != 0);
- drflac_assert(pDecodedSamples != NULL); // <-- Should we allow NULL, in which case we just seek past the residual rather than do a full decode?
+ drflac_assert(pDecodedSamples != NULL); /* <-- Should we allow NULL, in which case we just seek past the residual rather than do a full decode? */
- drflac_uint8 residualMethod;
if (!drflac__read_uint8(bs, 2, &residualMethod)) {
return DRFLAC_FALSE;
}
if (residualMethod != DRFLAC_RESIDUAL_CODING_METHOD_PARTITIONED_RICE && residualMethod != DRFLAC_RESIDUAL_CODING_METHOD_PARTITIONED_RICE2) {
- return DRFLAC_FALSE; // Unknown or unsupported residual coding method.
+ return DRFLAC_FALSE; /* Unknown or unsupported residual coding method. */
}
- // Ignore the first <order> values.
+ /* Ignore the first <order> values. */
pDecodedSamples += order;
-
- drflac_uint8 partitionOrder;
if (!drflac__read_uint8(bs, 4, &partitionOrder)) {
return DRFLAC_FALSE;
}
- // From the FLAC spec:
- // The Rice partition order in a Rice-coded residual section must be less than or equal to 8.
+ /*
+ From the FLAC spec:
+ The Rice partition order in a Rice-coded residual section must be less than or equal to 8.
+ */
if (partitionOrder > 8) {
return DRFLAC_FALSE;
}
- // Validation check.
+ /* Validation check. */
if ((blockSize / (1 << partitionOrder)) <= order) {
return DRFLAC_FALSE;
}
- drflac_uint32 samplesInPartition = (blockSize / (1 << partitionOrder)) - order;
- drflac_uint32 partitionsRemaining = (1 << partitionOrder);
+ samplesInPartition = (blockSize / (1 << partitionOrder)) - order;
+ partitionsRemaining = (1 << partitionOrder);
for (;;) {
drflac_uint8 riceParam = 0;
if (residualMethod == DRFLAC_RESIDUAL_CODING_METHOD_PARTITIONED_RICE) {
if (!drflac__read_uint8(bs, 4, &riceParam)) {
return DRFLAC_FALSE;
}
- if (riceParam == 16) {
+ if (riceParam == 15) {
riceParam = 0xFF;
}
} else if (residualMethod == DRFLAC_RESIDUAL_CODING_METHOD_PARTITIONED_RICE2) {
if (!drflac__read_uint8(bs, 5, &riceParam)) {
return DRFLAC_FALSE;
}
- if (riceParam == 32) {
+ if (riceParam == 31) {
riceParam = 0xFF;
}
}
@@ -2488,7 +3817,6 @@ static drflac_bool32 drflac__decode_samples_with_residual(drflac_bs* bs, drflac_
pDecodedSamples += samplesInPartition;
-
if (partitionsRemaining == 1) {
break;
}
@@ -2503,30 +3831,48 @@ static drflac_bool32 drflac__decode_samples_with_residual(drflac_bs* bs, drflac_
return DRFLAC_TRUE;
}
-// Reads and seeks past the residual for the sub-frame the decoder is currently sitting on. This function should be called
-// when the decoder is sitting at the very start of the RESIDUAL block. The first <order> residuals will be set to 0. The
-// <blockSize> and <order> parameters are used to determine how many residual values need to be decoded.
+/*
+Reads and seeks past the residual for the sub-frame the decoder is currently sitting on. This function should be called
+when the decoder is sitting at the very start of the RESIDUAL block. The first <order> residuals will be set to 0. The
+<blockSize> and <order> parameters are used to determine how many residual values need to be decoded.
+*/
static drflac_bool32 drflac__read_and_seek_residual(drflac_bs* bs, drflac_uint32 blockSize, drflac_uint32 order)
{
+ drflac_uint8 residualMethod;
+ drflac_uint8 partitionOrder;
+ drflac_uint32 samplesInPartition;
+ drflac_uint32 partitionsRemaining;
+
drflac_assert(bs != NULL);
drflac_assert(blockSize != 0);
- drflac_uint8 residualMethod;
if (!drflac__read_uint8(bs, 2, &residualMethod)) {
return DRFLAC_FALSE;
}
if (residualMethod != DRFLAC_RESIDUAL_CODING_METHOD_PARTITIONED_RICE && residualMethod != DRFLAC_RESIDUAL_CODING_METHOD_PARTITIONED_RICE2) {
- return DRFLAC_FALSE; // Unknown or unsupported residual coding method.
+ return DRFLAC_FALSE; /* Unknown or unsupported residual coding method. */
}
- drflac_uint8 partitionOrder;
if (!drflac__read_uint8(bs, 4, &partitionOrder)) {
return DRFLAC_FALSE;
}
- drflac_uint32 samplesInPartition = (blockSize / (1 << partitionOrder)) - order;
- drflac_uint32 partitionsRemaining = (1 << partitionOrder);
+ /*
+ From the FLAC spec:
+ The Rice partition order in a Rice-coded residual section must be less than or equal to 8.
+ */
+ if (partitionOrder > 8) {
+ return DRFLAC_FALSE;
+ }
+
+ /* Validation check. */
+ if ((blockSize / (1 << partitionOrder)) <= order) {
+ return DRFLAC_FALSE;
+ }
+
+ samplesInPartition = (blockSize / (1 << partitionOrder)) - order;
+ partitionsRemaining = (1 << partitionOrder);
for (;;)
{
drflac_uint8 riceParam = 0;
@@ -2534,14 +3880,14 @@ static drflac_bool32 drflac__read_and_seek_residual(drflac_bs* bs, drflac_uint32
if (!drflac__read_uint8(bs, 4, &riceParam)) {
return DRFLAC_FALSE;
}
- if (riceParam == 16) {
+ if (riceParam == 15) {
riceParam = 0xFF;
}
} else if (residualMethod == DRFLAC_RESIDUAL_CODING_METHOD_PARTITIONED_RICE2) {
if (!drflac__read_uint8(bs, 5, &riceParam)) {
return DRFLAC_FALSE;
}
- if (riceParam == 32) {
+ if (riceParam == 31) {
riceParam = 0xFF;
}
}
@@ -2576,15 +3922,19 @@ static drflac_bool32 drflac__read_and_seek_residual(drflac_bs* bs, drflac_uint32
static drflac_bool32 drflac__decode_samples__constant(drflac_bs* bs, drflac_uint32 blockSize, drflac_uint32 bitsPerSample, drflac_int32* pDecodedSamples)
{
- // Only a single sample needs to be decoded here.
+ drflac_uint32 i;
+
+ /* Only a single sample needs to be decoded here. */
drflac_int32 sample;
if (!drflac__read_int32(bs, bitsPerSample, &sample)) {
return DRFLAC_FALSE;
}
- // We don't really need to expand this, but it does simplify the process of reading samples. If this becomes a performance issue (unlikely)
- // we'll want to look at a more efficient way.
- for (drflac_uint32 i = 0; i < blockSize; ++i) {
+ /*
+ We don't really need to expand this, but it does simplify the process of reading samples. If this becomes a performance issue (unlikely)
+ we'll want to look at a more efficient way.
+ */
+ for (i = 0; i < blockSize; ++i) {
pDecodedSamples[i] = sample;
}
@@ -2593,7 +3943,9 @@ static drflac_bool32 drflac__decode_samples__constant(drflac_bs* bs, drflac_uint
static drflac_bool32 drflac__decode_samples__verbatim(drflac_bs* bs, drflac_uint32 blockSize, drflac_uint32 bitsPerSample, drflac_int32* pDecodedSamples)
{
- for (drflac_uint32 i = 0; i < blockSize; ++i) {
+ drflac_uint32 i;
+
+ for (i = 0; i < blockSize; ++i) {
drflac_int32 sample;
if (!drflac__read_int32(bs, bitsPerSample, &sample)) {
return DRFLAC_FALSE;
@@ -2607,7 +3959,9 @@ static drflac_bool32 drflac__decode_samples__verbatim(drflac_bs* bs, drflac_uint
static drflac_bool32 drflac__decode_samples__fixed(drflac_bs* bs, drflac_uint32 blockSize, drflac_uint32 bitsPerSample, drflac_uint8 lpcOrder, drflac_int32* pDecodedSamples)
{
- drflac_int32 lpcCoefficientsTable[5][4] = {
+ drflac_uint32 i;
+
+ static drflac_int32 lpcCoefficientsTable[5][4] = {
{0, 0, 0, 0},
{1, 0, 0, 0},
{2, -1, 0, 0},
@@ -2615,8 +3969,8 @@ static drflac_bool32 drflac__decode_samples__fixed(drflac_bs* bs, drflac_uint32
{4, -6, 4, -1}
};
- // Warm up samples and coefficients.
- for (drflac_uint32 i = 0; i < lpcOrder; ++i) {
+ /* Warm up samples and coefficients. */
+ for (i = 0; i < lpcOrder; ++i) {
drflac_int32 sample;
if (!drflac__read_int32(bs, bitsPerSample, &sample)) {
return DRFLAC_FALSE;
@@ -2625,7 +3979,6 @@ static drflac_bool32 drflac__decode_samples__fixed(drflac_bs* bs, drflac_uint32
pDecodedSamples[i] = sample;
}
-
if (!drflac__decode_samples_with_residual(bs, bitsPerSample, blockSize, lpcOrder, 0, lpcCoefficientsTable[lpcOrder], pDecodedSamples)) {
return DRFLAC_FALSE;
}
@@ -2636,8 +3989,11 @@ static drflac_bool32 drflac__decode_samples__fixed(drflac_bs* bs, drflac_uint32
static drflac_bool32 drflac__decode_samples__lpc(drflac_bs* bs, drflac_uint32 blockSize, drflac_uint32 bitsPerSample, drflac_uint8 lpcOrder, drflac_int32* pDecodedSamples)
{
drflac_uint8 i;
+ drflac_uint8 lpcPrecision;
+ drflac_int8 lpcShift;
+ drflac_int32 coefficients[32];
- // Warm up samples.
+ /* Warm up samples. */
for (i = 0; i < lpcOrder; ++i) {
drflac_int32 sample;
if (!drflac__read_int32(bs, bitsPerSample, &sample)) {
@@ -2647,23 +4003,19 @@ static drflac_bool32 drflac__decode_samples__lpc(drflac_bs* bs, drflac_uint32 bl
pDecodedSamples[i] = sample;
}
- drflac_uint8 lpcPrecision;
if (!drflac__read_uint8(bs, 4, &lpcPrecision)) {
return DRFLAC_FALSE;
}
if (lpcPrecision == 15) {
- return DRFLAC_FALSE; // Invalid.
+ return DRFLAC_FALSE; /* Invalid. */
}
lpcPrecision += 1;
-
- drflac_int8 lpcShift;
if (!drflac__read_int8(bs, 5, &lpcShift)) {
return DRFLAC_FALSE;
}
-
- drflac_int32 coefficients[32];
+ drflac_zero_memory(coefficients, sizeof(coefficients));
for (i = 0; i < lpcOrder; ++i) {
if (!drflac__read_int32(bs, lpcPrecision, coefficients + i)) {
return DRFLAC_FALSE;
@@ -2678,71 +4030,82 @@ static drflac_bool32 drflac__decode_samples__lpc(drflac_bs* bs, drflac_uint32 bl
}
-static drflac_bool32 drflac__read_next_frame_header(drflac_bs* bs, drflac_uint8 streaminfoBitsPerSample, drflac_frame_header* header)
+static drflac_bool32 drflac__read_next_flac_frame_header(drflac_bs* bs, drflac_uint8 streaminfoBitsPerSample, drflac_frame_header* header)
{
+ const drflac_uint32 sampleRateTable[12] = {0, 88200, 176400, 192000, 8000, 16000, 22050, 24000, 32000, 44100, 48000, 96000};
+ const drflac_uint8 bitsPerSampleTable[8] = {0, 8, 12, (drflac_uint8)-1, 16, 20, 24, (drflac_uint8)-1}; /* -1 = reserved. */
+
drflac_assert(bs != NULL);
drflac_assert(header != NULL);
- const drflac_uint32 sampleRateTable[12] = {0, 88200, 176400, 192000, 8000, 16000, 22050, 24000, 32000, 44100, 48000, 96000};
- const drflac_uint8 bitsPerSampleTable[8] = {0, 8, 12, (drflac_uint8)-1, 16, 20, 24, (drflac_uint8)-1}; // -1 = reserved.
-
- // Keep looping until we find a valid sync code.
+ /* Keep looping until we find a valid sync code. */
for (;;) {
+ drflac_uint8 crc8 = 0xCE; /* 0xCE = drflac_crc8(0, 0x3FFE, 14); */
+ drflac_uint8 reserved = 0;
+ drflac_uint8 blockingStrategy = 0;
+ drflac_uint8 blockSize = 0;
+ drflac_uint8 sampleRate = 0;
+ drflac_uint8 channelAssignment = 0;
+ drflac_uint8 bitsPerSample = 0;
+ drflac_bool32 isVariableBlockSize;
+
if (!drflac__find_and_seek_to_next_sync_code(bs)) {
return DRFLAC_FALSE;
}
- drflac_uint8 crc8 = 0xCE; // 0xCE = drflac_crc8(0, 0x3FFE, 14);
-
- drflac_uint8 reserved = 0;
if (!drflac__read_uint8(bs, 1, &reserved)) {
return DRFLAC_FALSE;
}
+ if (reserved == 1) {
+ continue;
+ }
crc8 = drflac_crc8(crc8, reserved, 1);
-
- drflac_uint8 blockingStrategy = 0;
if (!drflac__read_uint8(bs, 1, &blockingStrategy)) {
return DRFLAC_FALSE;
}
crc8 = drflac_crc8(crc8, blockingStrategy, 1);
-
- drflac_uint8 blockSize = 0;
if (!drflac__read_uint8(bs, 4, &blockSize)) {
return DRFLAC_FALSE;
}
+ if (blockSize == 0) {
+ continue;
+ }
crc8 = drflac_crc8(crc8, blockSize, 4);
-
- drflac_uint8 sampleRate = 0;
if (!drflac__read_uint8(bs, 4, &sampleRate)) {
return DRFLAC_FALSE;
}
crc8 = drflac_crc8(crc8, sampleRate, 4);
-
- drflac_uint8 channelAssignment = 0;
if (!drflac__read_uint8(bs, 4, &channelAssignment)) {
return DRFLAC_FALSE;
}
+ if (channelAssignment > 10) {
+ continue;
+ }
crc8 = drflac_crc8(crc8, channelAssignment, 4);
-
- drflac_uint8 bitsPerSample = 0;
if (!drflac__read_uint8(bs, 3, &bitsPerSample)) {
return DRFLAC_FALSE;
}
+ if (bitsPerSample == 3 || bitsPerSample == 7) {
+ continue;
+ }
crc8 = drflac_crc8(crc8, bitsPerSample, 3);
if (!drflac__read_uint8(bs, 1, &reserved)) {
return DRFLAC_FALSE;
}
+ if (reserved == 1) {
+ continue;
+ }
crc8 = drflac_crc8(crc8, reserved, 1);
- drflac_bool32 isVariableBlockSize = blockingStrategy == 1;
+ isVariableBlockSize = blockingStrategy == 1;
if (isVariableBlockSize) {
drflac_uint64 sampleNumber;
drflac_result result = drflac__read_utf8_coded_number(bs, &sampleNumber, &crc8);
@@ -2765,7 +4128,7 @@ static drflac_bool32 drflac__read_next_frame_header(drflac_bs* bs, drflac_uint8
continue;
}
}
- header->frameNumber = (drflac_uint32)frameNumber; // <-- Safe cast.
+ header->frameNumber = (drflac_uint32)frameNumber; /* <-- Safe cast. */
header->sampleNumber = 0;
}
@@ -2811,7 +4174,7 @@ static drflac_bool32 drflac__read_next_frame_header(drflac_bs* bs, drflac_uint8
crc8 = drflac_crc8(crc8, header->sampleRate, 16);
header->sampleRate *= 10;
} else {
- continue; // Invalid. Assume an invalid block.
+ continue; /* Invalid. Assume an invalid block. */
}
@@ -2826,11 +4189,11 @@ static drflac_bool32 drflac__read_next_frame_header(drflac_bs* bs, drflac_uint8
return DRFLAC_FALSE;
}
- #ifndef DR_FLAC_NO_CRC
+#ifndef DR_FLAC_NO_CRC
if (header->crc8 != crc8) {
- continue; // CRC mismatch. Loop back to the top and find the next sync code.
+ continue; /* CRC mismatch. Loop back to the top and find the next sync code. */
}
- #endif
+#endif
return DRFLAC_TRUE;
}
}
@@ -2838,16 +4201,18 @@ static drflac_bool32 drflac__read_next_frame_header(drflac_bs* bs, drflac_uint8
static drflac_bool32 drflac__read_subframe_header(drflac_bs* bs, drflac_subframe* pSubframe)
{
drflac_uint8 header;
+ int type;
+
if (!drflac__read_uint8(bs, 8, &header)) {
return DRFLAC_FALSE;
}
- // First bit should always be 0.
+ /* First bit should always be 0. */
if ((header & 0x80) != 0) {
return DRFLAC_FALSE;
}
- int type = (header & 0x7E) >> 1;
+ type = (header & 0x7E) >> 1;
if (type == 0) {
pSubframe->subframeType = DRFLAC_SUBFRAME_CONSTANT;
} else if (type == 1) {
@@ -2872,7 +4237,7 @@ static drflac_bool32 drflac__read_subframe_header(drflac_bs* bs, drflac_subframe
return DRFLAC_FALSE;
}
- // Wasted bits per sample.
+ /* Wasted bits per sample. */
pSubframe->wastedBitsPerSample = 0;
if ((header & 0x01) == 1) {
unsigned int wastedBitsPerSample;
@@ -2887,15 +4252,17 @@ static drflac_bool32 drflac__read_subframe_header(drflac_bs* bs, drflac_subframe
static drflac_bool32 drflac__decode_subframe(drflac_bs* bs, drflac_frame* frame, int subframeIndex, drflac_int32* pDecodedSamplesOut)
{
+ drflac_subframe* pSubframe;
+
drflac_assert(bs != NULL);
drflac_assert(frame != NULL);
- drflac_subframe* pSubframe = frame->subframes + subframeIndex;
+ pSubframe = frame->subframes + subframeIndex;
if (!drflac__read_subframe_header(bs, pSubframe)) {
return DRFLAC_FALSE;
}
- // Side channels require an extra bit per sample. Took a while to figure that one out...
+ /* Side channels require an extra bit per sample. Took a while to figure that one out... */
pSubframe->bitsPerSample = frame->header.bitsPerSample;
if ((frame->header.channelAssignment == DRFLAC_CHANNEL_ASSIGNMENT_LEFT_SIDE || frame->header.channelAssignment == DRFLAC_CHANNEL_ASSIGNMENT_MID_SIDE) && subframeIndex == 1) {
pSubframe->bitsPerSample += 1;
@@ -2903,7 +4270,10 @@ static drflac_bool32 drflac__decode_subframe(drflac_bs* bs, drflac_frame* frame,
pSubframe->bitsPerSample += 1;
}
- // Need to handle wasted bits per sample.
+ /* Need to handle wasted bits per sample. */
+ if (pSubframe->wastedBitsPerSample >= pSubframe->bitsPerSample) {
+ return DRFLAC_FALSE;
+ }
pSubframe->bitsPerSample -= pSubframe->wastedBitsPerSample;
pSubframe->pDecodedSamples = pDecodedSamplesOut;
@@ -2937,15 +4307,17 @@ static drflac_bool32 drflac__decode_subframe(drflac_bs* bs, drflac_frame* frame,
static drflac_bool32 drflac__seek_subframe(drflac_bs* bs, drflac_frame* frame, int subframeIndex)
{
+ drflac_subframe* pSubframe;
+
drflac_assert(bs != NULL);
drflac_assert(frame != NULL);
- drflac_subframe* pSubframe = frame->subframes + subframeIndex;
+ pSubframe = frame->subframes + subframeIndex;
if (!drflac__read_subframe_header(bs, pSubframe)) {
return DRFLAC_FALSE;
}
- // Side channels require an extra bit per sample. Took a while to figure that one out...
+ /* Side channels require an extra bit per sample. Took a while to figure that one out... */
pSubframe->bitsPerSample = frame->header.bitsPerSample;
if ((frame->header.channelAssignment == DRFLAC_CHANNEL_ASSIGNMENT_LEFT_SIDE || frame->header.channelAssignment == DRFLAC_CHANNEL_ASSIGNMENT_MID_SIDE) && subframeIndex == 1) {
pSubframe->bitsPerSample += 1;
@@ -2953,7 +4325,10 @@ static drflac_bool32 drflac__seek_subframe(drflac_bs* bs, drflac_frame* frame, i
pSubframe->bitsPerSample += 1;
}
- // Need to handle wasted bits per sample.
+ /* Need to handle wasted bits per sample. */
+ if (pSubframe->wastedBitsPerSample >= pSubframe->bitsPerSample) {
+ return DRFLAC_FALSE;
+ }
pSubframe->bitsPerSample -= pSubframe->wastedBitsPerSample;
pSubframe->pDecodedSamples = NULL;
@@ -2988,22 +4363,23 @@ static drflac_bool32 drflac__seek_subframe(drflac_bs* bs, drflac_frame* frame, i
case DRFLAC_SUBFRAME_LPC:
{
+ unsigned char lpcPrecision;
+
unsigned int bitsToSeek = pSubframe->lpcOrder * pSubframe->bitsPerSample;
if (!drflac__seek_bits(bs, bitsToSeek)) {
return DRFLAC_FALSE;
}
- unsigned char lpcPrecision;
if (!drflac__read_uint8(bs, 4, &lpcPrecision)) {
return DRFLAC_FALSE;
}
if (lpcPrecision == 15) {
- return DRFLAC_FALSE; // Invalid.
+ return DRFLAC_FALSE; /* Invalid. */
}
lpcPrecision += 1;
- bitsToSeek = (pSubframe->lpcOrder * lpcPrecision) + 5; // +5 for shift.
+ bitsToSeek = (pSubframe->lpcOrder * lpcPrecision) + 5; /* +5 for shift. */
if (!drflac__seek_bits(bs, bitsToSeek)) {
return DRFLAC_FALSE;
}
@@ -3022,35 +4398,43 @@ static drflac_bool32 drflac__seek_subframe(drflac_bs* bs, drflac_frame* frame, i
static DRFLAC_INLINE drflac_uint8 drflac__get_channel_count_from_channel_assignment(drflac_int8 channelAssignment)
{
- drflac_assert(channelAssignment <= 10);
-
drflac_uint8 lookup[] = {1, 2, 3, 4, 5, 6, 7, 8, 2, 2, 2};
+
+ drflac_assert(channelAssignment <= 10);
return lookup[channelAssignment];
}
-static drflac_result drflac__decode_frame(drflac* pFlac)
+static drflac_result drflac__decode_flac_frame(drflac* pFlac)
{
- // This function should be called while the stream is sitting on the first byte after the frame header.
+ int channelCount;
+ int i;
+ drflac_uint8 paddingSizeInBits;
+ drflac_uint16 desiredCRC16;
+#ifndef DR_FLAC_NO_CRC
+ drflac_uint16 actualCRC16;
+#endif
+
+ /* This function should be called while the stream is sitting on the first byte after the frame header. */
drflac_zero_memory(pFlac->currentFrame.subframes, sizeof(pFlac->currentFrame.subframes));
- // The frame block size must never be larger than the maximum block size defined by the FLAC stream.
+ /* The frame block size must never be larger than the maximum block size defined by the FLAC stream. */
if (pFlac->currentFrame.header.blockSize > pFlac->maxBlockSize) {
return DRFLAC_ERROR;
}
- // The number of channels in the frame must match the channel count from the STREAMINFO block.
- int channelCount = drflac__get_channel_count_from_channel_assignment(pFlac->currentFrame.header.channelAssignment);
+ /* The number of channels in the frame must match the channel count from the STREAMINFO block. */
+ channelCount = drflac__get_channel_count_from_channel_assignment(pFlac->currentFrame.header.channelAssignment);
if (channelCount != (int)pFlac->channels) {
return DRFLAC_ERROR;
}
- for (int i = 0; i < channelCount; ++i) {
- if (!drflac__decode_subframe(&pFlac->bs, &pFlac->currentFrame, i, pFlac->pDecodedSamples + (pFlac->currentFrame.header.blockSize * i))) {
+ for (i = 0; i < channelCount; ++i) {
+ if (!drflac__decode_subframe(&pFlac->bs, &pFlac->currentFrame, i, pFlac->pDecodedSamples + ((pFlac->currentFrame.header.blockSize+DRFLAC_LEADING_SAMPLES) * i) + DRFLAC_LEADING_SAMPLES)) {
return DRFLAC_ERROR;
}
}
- drflac_uint8 paddingSizeInBits = DRFLAC_CACHE_L1_BITS_REMAINING(&pFlac->bs) & 7;
+ paddingSizeInBits = DRFLAC_CACHE_L1_BITS_REMAINING(&pFlac->bs) & 7;
if (paddingSizeInBits > 0) {
drflac_uint8 padding = 0;
if (!drflac__read_uint8(&pFlac->bs, paddingSizeInBits, &padding)) {
@@ -3059,16 +4443,15 @@ static drflac_result drflac__decode_frame(drflac* pFlac)
}
#ifndef DR_FLAC_NO_CRC
- drflac_uint16 actualCRC16 = drflac__flush_crc16(&pFlac->bs);
+ actualCRC16 = drflac__flush_crc16(&pFlac->bs);
#endif
- drflac_uint16 desiredCRC16;
if (!drflac__read_uint16(&pFlac->bs, 16, &desiredCRC16)) {
return DRFLAC_END_OF_STREAM;
}
#ifndef DR_FLAC_NO_CRC
if (actualCRC16 != desiredCRC16) {
- return DRFLAC_CRC_MISMATCH; // CRC mismatch.
+ return DRFLAC_CRC_MISMATCH; /* CRC mismatch. */
}
#endif
@@ -3077,51 +4460,59 @@ static drflac_result drflac__decode_frame(drflac* pFlac)
return DRFLAC_SUCCESS;
}
-static drflac_result drflac__seek_frame(drflac* pFlac)
+static drflac_result drflac__seek_flac_frame(drflac* pFlac)
{
- int channelCount = drflac__get_channel_count_from_channel_assignment(pFlac->currentFrame.header.channelAssignment);
- for (int i = 0; i < channelCount; ++i) {
+ int channelCount;
+ int i;
+ drflac_uint16 desiredCRC16;
+#ifndef DR_FLAC_NO_CRC
+ drflac_uint16 actualCRC16;
+#endif
+
+ channelCount = drflac__get_channel_count_from_channel_assignment(pFlac->currentFrame.header.channelAssignment);
+ for (i = 0; i < channelCount; ++i) {
if (!drflac__seek_subframe(&pFlac->bs, &pFlac->currentFrame, i)) {
return DRFLAC_ERROR;
}
}
- // Padding.
+ /* Padding. */
if (!drflac__seek_bits(&pFlac->bs, DRFLAC_CACHE_L1_BITS_REMAINING(&pFlac->bs) & 7)) {
return DRFLAC_ERROR;
}
- // CRC.
+ /* CRC. */
#ifndef DR_FLAC_NO_CRC
- drflac_uint16 actualCRC16 = drflac__flush_crc16(&pFlac->bs);
+ actualCRC16 = drflac__flush_crc16(&pFlac->bs);
#endif
- drflac_uint16 desiredCRC16;
if (!drflac__read_uint16(&pFlac->bs, 16, &desiredCRC16)) {
return DRFLAC_END_OF_STREAM;
}
#ifndef DR_FLAC_NO_CRC
if (actualCRC16 != desiredCRC16) {
- return DRFLAC_CRC_MISMATCH; // CRC mismatch.
+ return DRFLAC_CRC_MISMATCH; /* CRC mismatch. */
}
#endif
return DRFLAC_SUCCESS;
}
-static drflac_bool32 drflac__read_and_decode_next_frame(drflac* pFlac)
+static drflac_bool32 drflac__read_and_decode_next_flac_frame(drflac* pFlac)
{
drflac_assert(pFlac != NULL);
for (;;) {
- if (!drflac__read_next_frame_header(&pFlac->bs, pFlac->bitsPerSample, &pFlac->currentFrame.header)) {
+ drflac_result result;
+
+ if (!drflac__read_next_flac_frame_header(&pFlac->bs, pFlac->bitsPerSample, &pFlac->currentFrame.header)) {
return DRFLAC_FALSE;
}
- drflac_result result = drflac__decode_frame(pFlac);
+ result = drflac__decode_flac_frame(pFlac);
if (result != DRFLAC_SUCCESS) {
if (result == DRFLAC_CRC_MISMATCH) {
- continue; // CRC mismatch. Skip to the next frame.
+ continue; /* CRC mismatch. Skip to the next frame. */
} else {
return DRFLAC_FALSE;
}
@@ -3134,29 +4525,67 @@ static drflac_bool32 drflac__read_and_decode_next_frame(drflac* pFlac)
static void drflac__get_current_frame_sample_range(drflac* pFlac, drflac_uint64* pFirstSampleInFrameOut, drflac_uint64* pLastSampleInFrameOut)
{
+ unsigned int channelCount;
+ drflac_uint64 firstSampleInFrame;
+ drflac_uint64 lastSampleInFrame;
+
drflac_assert(pFlac != NULL);
- unsigned int channelCount = drflac__get_channel_count_from_channel_assignment(pFlac->currentFrame.header.channelAssignment);
+ channelCount = drflac__get_channel_count_from_channel_assignment(pFlac->currentFrame.header.channelAssignment);
- drflac_uint64 firstSampleInFrame = pFlac->currentFrame.header.sampleNumber;
+ firstSampleInFrame = pFlac->currentFrame.header.sampleNumber*channelCount;
if (firstSampleInFrame == 0) {
firstSampleInFrame = pFlac->currentFrame.header.frameNumber * pFlac->maxBlockSize*channelCount;
}
- drflac_uint64 lastSampleInFrame = firstSampleInFrame + (pFlac->currentFrame.header.blockSize*channelCount);
+ lastSampleInFrame = firstSampleInFrame + (pFlac->currentFrame.header.blockSize*channelCount);
if (lastSampleInFrame > 0) {
- lastSampleInFrame -= 1; // Needs to be zero based.
+ lastSampleInFrame -= 1; /* Needs to be zero based. */
+ }
+
+ if (pFirstSampleInFrameOut) {
+ *pFirstSampleInFrameOut = firstSampleInFrame;
+ }
+ if (pLastSampleInFrameOut) {
+ *pLastSampleInFrameOut = lastSampleInFrame;
+ }
+}
+
+/* This function will be replacing drflac__get_current_frame_sample_range(), but it's not currently used so I have commented it out to silence a compiler warning. */
+#if 0
+static void drflac__get_pcm_frame_range_of_current_flac_frame(drflac* pFlac, drflac_uint64* pFirstPCMFrame, drflac_uint64* pLastPCMFrame)
+{
+ drflac_uint64 firstPCMFrame;
+ drflac_uint64 lastPCMFrame;
+
+ drflac_assert(pFlac != NULL);
+
+ firstPCMFrame = pFlac->currentFrame.header.sampleNumber;
+ if (firstPCMFrame == 0) {
+ firstPCMFrame = pFlac->currentFrame.header.frameNumber * pFlac->maxBlockSize;
}
- if (pFirstSampleInFrameOut) *pFirstSampleInFrameOut = firstSampleInFrame;
- if (pLastSampleInFrameOut) *pLastSampleInFrameOut = lastSampleInFrame;
+ lastPCMFrame = firstPCMFrame + (pFlac->currentFrame.header.blockSize);
+ if (lastPCMFrame > 0) {
+ lastPCMFrame -= 1; /* Needs to be zero based. */
+ }
+
+ if (pFirstPCMFrame) {
+ *pFirstPCMFrame = firstPCMFrame;
+ }
+ if (pLastPCMFrame) {
+ *pLastPCMFrame = lastPCMFrame;
+ }
}
+#endif
static drflac_bool32 drflac__seek_to_first_frame(drflac* pFlac)
{
+ drflac_bool32 result;
+
drflac_assert(pFlac != NULL);
- drflac_bool32 result = drflac__seek_to_byte(&pFlac->bs, pFlac->firstFramePos);
+ result = drflac__seek_to_byte(&pFlac->bs, pFlac->firstFramePos);
drflac_zero_memory(&pFlac->currentFrame, sizeof(pFlac->currentFrame));
pFlac->currentSample = 0;
@@ -3164,94 +4593,134 @@ static drflac_bool32 drflac__seek_to_first_frame(drflac* pFlac)
return result;
}
-static DRFLAC_INLINE drflac_result drflac__seek_to_next_frame(drflac* pFlac)
+static DRFLAC_INLINE drflac_result drflac__seek_to_next_flac_frame(drflac* pFlac)
{
- // This function should only ever be called while the decoder is sitting on the first byte past the FRAME_HEADER section.
+ /* This function should only ever be called while the decoder is sitting on the first byte past the FRAME_HEADER section. */
drflac_assert(pFlac != NULL);
- return drflac__seek_frame(pFlac);
+ return drflac__seek_flac_frame(pFlac);
}
-static drflac_bool32 drflac__seek_to_sample__brute_force(drflac* pFlac, drflac_uint64 sampleIndex)
+drflac_uint64 drflac__seek_forward_by_samples(drflac* pFlac, drflac_uint64 samplesToRead)
{
- drflac_assert(pFlac != NULL);
+ drflac_uint64 samplesRead = 0;
+ while (samplesToRead > 0) {
+ if (pFlac->currentFrame.samplesRemaining == 0) {
+ if (!drflac__read_and_decode_next_flac_frame(pFlac)) {
+ break; /* Couldn't read the next frame, so just break from the loop and return. */
+ }
+ } else {
+ if (pFlac->currentFrame.samplesRemaining > samplesToRead) {
+ samplesRead += samplesToRead;
+ pFlac->currentFrame.samplesRemaining -= (drflac_uint32)samplesToRead; /* <-- Safe cast. Will always be < currentFrame.samplesRemaining < 65536. */
+ samplesToRead = 0;
+ } else {
+ samplesRead += pFlac->currentFrame.samplesRemaining;
+ samplesToRead -= pFlac->currentFrame.samplesRemaining;
+ pFlac->currentFrame.samplesRemaining = 0;
+ }
+ }
+ }
- drflac_bool32 isMidFrame = DRFLAC_FALSE;
+ pFlac->currentSample += samplesRead;
+ return samplesRead;
+}
- // If we are seeking forward we start from the current position. Otherwise we need to start all the way from the start of the file.
+drflac_uint64 drflac__seek_forward_by_pcm_frames(drflac* pFlac, drflac_uint64 pcmFramesToSeek)
+{
+ return drflac__seek_forward_by_samples(pFlac, pcmFramesToSeek*pFlac->channels);
+}
+
+static drflac_bool32 drflac__seek_to_sample__brute_force(drflac* pFlac, drflac_uint64 sampleIndex)
+{
+ drflac_bool32 isMidFrame = DRFLAC_FALSE;
drflac_uint64 runningSampleCount;
+
+ drflac_assert(pFlac != NULL);
+
+ /* If we are seeking forward we start from the current position. Otherwise we need to start all the way from the start of the file. */
if (sampleIndex >= pFlac->currentSample) {
- // Seeking forward. Need to seek from the current position.
+ /* Seeking forward. Need to seek from the current position. */
runningSampleCount = pFlac->currentSample;
- // The frame header for the first frame may not yet have been read. We need to do that if necessary.
+ /* The frame header for the first frame may not yet have been read. We need to do that if necessary. */
if (pFlac->currentSample == 0 && pFlac->currentFrame.samplesRemaining == 0) {
- if (!drflac__read_next_frame_header(&pFlac->bs, pFlac->bitsPerSample, &pFlac->currentFrame.header)) {
+ if (!drflac__read_next_flac_frame_header(&pFlac->bs, pFlac->bitsPerSample, &pFlac->currentFrame.header)) {
return DRFLAC_FALSE;
}
} else {
isMidFrame = DRFLAC_TRUE;
}
} else {
- // Seeking backwards. Need to seek from the start of the file.
+ /* Seeking backwards. Need to seek from the start of the file. */
runningSampleCount = 0;
- // Move back to the start.
+ /* Move back to the start. */
if (!drflac__seek_to_first_frame(pFlac)) {
return DRFLAC_FALSE;
}
- // Decode the first frame in preparation for sample-exact seeking below.
- if (!drflac__read_next_frame_header(&pFlac->bs, pFlac->bitsPerSample, &pFlac->currentFrame.header)) {
+ /* Decode the first frame in preparation for sample-exact seeking below. */
+ if (!drflac__read_next_flac_frame_header(&pFlac->bs, pFlac->bitsPerSample, &pFlac->currentFrame.header)) {
return DRFLAC_FALSE;
}
}
- // We need to as quickly as possible find the frame that contains the target sample. To do this, we iterate over each frame and inspect its
- // header. If based on the header we can determine that the frame contains the sample, we do a full decode of that frame.
+ /*
+ We need to as quickly as possible find the frame that contains the target sample. To do this, we iterate over each frame and inspect its
+ header. If based on the header we can determine that the frame contains the sample, we do a full decode of that frame.
+ */
for (;;) {
+ drflac_uint64 sampleCountInThisFrame;
drflac_uint64 firstSampleInFrame = 0;
drflac_uint64 lastSampleInFrame = 0;
+
drflac__get_current_frame_sample_range(pFlac, &firstSampleInFrame, &lastSampleInFrame);
- drflac_uint64 sampleCountInThisFrame = (lastSampleInFrame - firstSampleInFrame) + 1;
+ sampleCountInThisFrame = (lastSampleInFrame - firstSampleInFrame) + 1;
if (sampleIndex < (runningSampleCount + sampleCountInThisFrame)) {
- // The sample should be in this frame. We need to fully decode it, however if it's an invalid frame (a CRC mismatch), we need to pretend
- // it never existed and keep iterating.
+ /*
+ The sample should be in this frame. We need to fully decode it, however if it's an invalid frame (a CRC mismatch), we need to pretend
+ it never existed and keep iterating.
+ */
drflac_uint64 samplesToDecode = sampleIndex - runningSampleCount;
if (!isMidFrame) {
- drflac_result result = drflac__decode_frame(pFlac);
+ drflac_result result = drflac__decode_flac_frame(pFlac);
if (result == DRFLAC_SUCCESS) {
- // The frame is valid. We just need to skip over some samples to ensure it's sample-exact.
- return drflac_read_s32(pFlac, samplesToDecode, NULL) == samplesToDecode; // <-- If this fails, something bad has happened (it should never fail).
+ /* The frame is valid. We just need to skip over some samples to ensure it's sample-exact. */
+ return drflac__seek_forward_by_samples(pFlac, samplesToDecode) == samplesToDecode; /* <-- If this fails, something bad has happened (it should never fail). */
} else {
if (result == DRFLAC_CRC_MISMATCH) {
- goto next_iteration; // CRC mismatch. Pretend this frame never existed.
+ goto next_iteration; /* CRC mismatch. Pretend this frame never existed. */
} else {
return DRFLAC_FALSE;
}
}
} else {
- // We started seeking mid-frame which means we need to skip the frame decoding part.
- return drflac_read_s32(pFlac, samplesToDecode, NULL) == samplesToDecode;
+ /* We started seeking mid-frame which means we need to skip the frame decoding part. */
+ return drflac__seek_forward_by_samples(pFlac, samplesToDecode) == samplesToDecode;
}
} else {
- // It's not in this frame. We need to seek past the frame, but check if there was a CRC mismatch. If so, we pretend this
- // frame never existed and leave the running sample count untouched.
+ /*
+ It's not in this frame. We need to seek past the frame, but check if there was a CRC mismatch. If so, we pretend this
+ frame never existed and leave the running sample count untouched.
+ */
if (!isMidFrame) {
- drflac_result result = drflac__seek_to_next_frame(pFlac);
+ drflac_result result = drflac__seek_to_next_flac_frame(pFlac);
if (result == DRFLAC_SUCCESS) {
runningSampleCount += sampleCountInThisFrame;
} else {
if (result == DRFLAC_CRC_MISMATCH) {
- goto next_iteration; // CRC mismatch. Pretend this frame never existed.
+ goto next_iteration; /* CRC mismatch. Pretend this frame never existed. */
} else {
return DRFLAC_FALSE;
}
}
} else {
- // We started seeking mid-frame which means we need to seek by reading to the end of the frame instead of with
- // drflac__seek_to_next_frame() which only works if the decoder is sitting on the byte just after the frame header.
+ /*
+ We started seeking mid-frame which means we need to seek by reading to the end of the frame instead of with
+ drflac__seek_to_next_flac_frame() which only works if the decoder is sitting on the byte just after the frame header.
+ */
runningSampleCount += pFlac->currentFrame.samplesRemaining;
pFlac->currentFrame.samplesRemaining = 0;
isMidFrame = DRFLAC_FALSE;
@@ -3259,8 +4728,8 @@ static drflac_bool32 drflac__seek_to_sample__brute_force(drflac* pFlac, drflac_u
}
next_iteration:
- // Grab the next frame in preparation for the next iteration.
- if (!drflac__read_next_frame_header(&pFlac->bs, pFlac->bitsPerSample, &pFlac->currentFrame.header)) {
+ /* Grab the next frame in preparation for the next iteration. */
+ if (!drflac__read_next_flac_frame_header(&pFlac->bs, pFlac->bitsPerSample, &pFlac->currentFrame.header)) {
return DRFLAC_FALSE;
}
}
@@ -3269,15 +4738,18 @@ static drflac_bool32 drflac__seek_to_sample__brute_force(drflac* pFlac, drflac_u
static drflac_bool32 drflac__seek_to_sample__seek_table(drflac* pFlac, drflac_uint64 sampleIndex)
{
+ drflac_uint32 iClosestSeekpoint = 0;
+ drflac_bool32 isMidFrame = DRFLAC_FALSE;
+ drflac_uint64 runningSampleCount;
+ drflac_uint32 iSeekpoint;
+
drflac_assert(pFlac != NULL);
if (pFlac->pSeekpoints == NULL || pFlac->seekpointCount == 0) {
return DRFLAC_FALSE;
}
-
- drflac_uint32 iClosestSeekpoint = 0;
- for (drflac_uint32 iSeekpoint = 0; iSeekpoint < pFlac->seekpointCount; ++iSeekpoint) {
+ for (iSeekpoint = 0; iSeekpoint < pFlac->seekpointCount; ++iSeekpoint) {
if (pFlac->pSeekpoints[iSeekpoint].firstSample*pFlac->channels >= sampleIndex) {
break;
}
@@ -3285,82 +4757,87 @@ static drflac_bool32 drflac__seek_to_sample__seek_table(drflac* pFlac, drflac_ui
iClosestSeekpoint = iSeekpoint;
}
-
- drflac_bool32 isMidFrame = DRFLAC_FALSE;
-
- // At this point we should have found the seekpoint closest to our sample. If we are seeking forward and the closest seekpoint is _before_ the current sample, we
- // just seek forward from where we are. Otherwise we start seeking from the seekpoint's first sample.
- drflac_uint64 runningSampleCount;
+ /*
+ At this point we should have found the seekpoint closest to our sample. If we are seeking forward and the closest seekpoint is _before_ the current sample, we
+ just seek forward from where we are. Otherwise we start seeking from the seekpoint's first sample.
+ */
if ((sampleIndex >= pFlac->currentSample) && (pFlac->pSeekpoints[iClosestSeekpoint].firstSample*pFlac->channels <= pFlac->currentSample)) {
- // Optimized case. Just seek forward from where we are.
+ /* Optimized case. Just seek forward from where we are. */
runningSampleCount = pFlac->currentSample;
- // The frame header for the first frame may not yet have been read. We need to do that if necessary.
+ /* The frame header for the first frame may not yet have been read. We need to do that if necessary. */
if (pFlac->currentSample == 0 && pFlac->currentFrame.samplesRemaining == 0) {
- if (!drflac__read_next_frame_header(&pFlac->bs, pFlac->bitsPerSample, &pFlac->currentFrame.header)) {
+ if (!drflac__read_next_flac_frame_header(&pFlac->bs, pFlac->bitsPerSample, &pFlac->currentFrame.header)) {
return DRFLAC_FALSE;
}
} else {
isMidFrame = DRFLAC_TRUE;
}
} else {
- // Slower case. Seek to the start of the seekpoint and then seek forward from there.
+ /* Slower case. Seek to the start of the seekpoint and then seek forward from there. */
runningSampleCount = pFlac->pSeekpoints[iClosestSeekpoint].firstSample*pFlac->channels;
if (!drflac__seek_to_byte(&pFlac->bs, pFlac->firstFramePos + pFlac->pSeekpoints[iClosestSeekpoint].frameOffset)) {
return DRFLAC_FALSE;
}
- // Grab the frame the seekpoint is sitting on in preparation for the sample-exact seeking below.
- if (!drflac__read_next_frame_header(&pFlac->bs, pFlac->bitsPerSample, &pFlac->currentFrame.header)) {
+ /* Grab the frame the seekpoint is sitting on in preparation for the sample-exact seeking below. */
+ if (!drflac__read_next_flac_frame_header(&pFlac->bs, pFlac->bitsPerSample, &pFlac->currentFrame.header)) {
return DRFLAC_FALSE;
}
}
for (;;) {
+ drflac_uint64 sampleCountInThisFrame;
drflac_uint64 firstSampleInFrame = 0;
drflac_uint64 lastSampleInFrame = 0;
drflac__get_current_frame_sample_range(pFlac, &firstSampleInFrame, &lastSampleInFrame);
- drflac_uint64 sampleCountInThisFrame = (lastSampleInFrame - firstSampleInFrame) + 1;
+ sampleCountInThisFrame = (lastSampleInFrame - firstSampleInFrame) + 1;
if (sampleIndex < (runningSampleCount + sampleCountInThisFrame)) {
- // The sample should be in this frame. We need to fully decode it, but if it's an invalid frame (a CRC mismatch) we need to pretend
- // it never existed and keep iterating.
+ /*
+ The sample should be in this frame. We need to fully decode it, but if it's an invalid frame (a CRC mismatch) we need to pretend
+ it never existed and keep iterating.
+ */
drflac_uint64 samplesToDecode = sampleIndex - runningSampleCount;
if (!isMidFrame) {
- drflac_result result = drflac__decode_frame(pFlac);
+ drflac_result result = drflac__decode_flac_frame(pFlac);
if (result == DRFLAC_SUCCESS) {
- // The frame is valid. We just need to skip over some samples to ensure it's sample-exact.
- return drflac_read_s32(pFlac, samplesToDecode, NULL) == samplesToDecode; // <-- If this fails, something bad has happened (it should never fail).
+ /* The frame is valid. We just need to skip over some samples to ensure it's sample-exact. */
+ return drflac__seek_forward_by_samples(pFlac, samplesToDecode) == samplesToDecode; /* <-- If this fails, something bad has happened (it should never fail). */
} else {
if (result == DRFLAC_CRC_MISMATCH) {
- goto next_iteration; // CRC mismatch. Pretend this frame never existed.
+ goto next_iteration; /* CRC mismatch. Pretend this frame never existed. */
} else {
return DRFLAC_FALSE;
}
}
} else {
- // We started seeking mid-frame which means we need to skip the frame decoding part.
- return drflac_read_s32(pFlac, samplesToDecode, NULL) == samplesToDecode;
+ /* We started seeking mid-frame which means we need to skip the frame decoding part. */
+ return drflac__seek_forward_by_samples(pFlac, samplesToDecode) == samplesToDecode;
}
} else {
- // It's not in this frame. We need to seek past the frame, but check if there was a CRC mismatch. If so, we pretend this
- // frame never existed and leave the running sample count untouched.
+ /*
+ It's not in this frame. We need to seek past the frame, but check if there was a CRC mismatch. If so, we pretend this
+ frame never existed and leave the running sample count untouched.
+ */
if (!isMidFrame) {
- drflac_result result = drflac__seek_to_next_frame(pFlac);
+ drflac_result result = drflac__seek_to_next_flac_frame(pFlac);
if (result == DRFLAC_SUCCESS) {
runningSampleCount += sampleCountInThisFrame;
} else {
if (result == DRFLAC_CRC_MISMATCH) {
- goto next_iteration; // CRC mismatch. Pretend this frame never existed.
+ goto next_iteration; /* CRC mismatch. Pretend this frame never existed. */
} else {
return DRFLAC_FALSE;
}
}
} else {
- // We started seeking mid-frame which means we need to seek by reading to the end of the frame instead of with
- // drflac__seek_to_next_frame() which only works if the decoder is sitting on the byte just after the frame header.
+ /*
+ We started seeking mid-frame which means we need to seek by reading to the end of the frame instead of with
+ drflac__seek_to_next_flac_frame() which only works if the decoder is sitting on the byte just after the frame header.
+ */
runningSampleCount += pFlac->currentFrame.samplesRemaining;
pFlac->currentFrame.samplesRemaining = 0;
isMidFrame = DRFLAC_FALSE;
@@ -3368,8 +4845,8 @@ static drflac_bool32 drflac__seek_to_sample__seek_table(drflac* pFlac, drflac_ui
}
next_iteration:
- // Grab the next frame in preparation for the next iteration.
- if (!drflac__read_next_frame_header(&pFlac->bs, pFlac->bitsPerSample, &pFlac->currentFrame.header)) {
+ /* Grab the next frame in preparation for the next iteration. */
+ if (!drflac__read_next_flac_frame_header(&pFlac->bs, pFlac->bitsPerSample, &pFlac->currentFrame.header)) {
return DRFLAC_FALSE;
}
}
@@ -3379,8 +4856,8 @@ static drflac_bool32 drflac__seek_to_sample__seek_table(drflac* pFlac, drflac_ui
#ifndef DR_FLAC_NO_OGG
typedef struct
{
- drflac_uint8 capturePattern[4]; // Should be "OggS"
- drflac_uint8 structureVersion; // Always 0.
+ drflac_uint8 capturePattern[4]; /* Should be "OggS" */
+ drflac_uint8 structureVersion; /* Always 0. */
drflac_uint8 headerType;
drflac_uint64 granulePosition;
drflac_uint32 serialNumber;
@@ -3407,8 +4884,8 @@ typedef struct
drflac_uint64 runningFilePos;
drflac_bool32 hasStreamInfoBlock;
drflac_bool32 hasMetadataBlocks;
- drflac_bs bs; // <-- A bit streamer is required for loading data during initialization.
- drflac_frame_header firstFrameHeader; // <-- The header of the first frame that was read during relaxed initalization. Only set if there is no STREAMINFO block.
+ drflac_bs bs; /* <-- A bit streamer is required for loading data during initialization. */
+ drflac_frame_header firstFrameHeader; /* <-- The header of the first frame that was read during relaxed initalization. Only set if there is no STREAMINFO block. */
#ifndef DR_FLAC_NO_OGG
drflac_uint32 oggSerial;
@@ -3438,26 +4915,27 @@ static DRFLAC_INLINE drflac_bool32 drflac__read_and_decode_block_header(drflac_r
drflac_bool32 drflac__read_streaminfo(drflac_read_proc onRead, void* pUserData, drflac_streaminfo* pStreamInfo)
{
- // min/max block size.
drflac_uint32 blockSizes;
+ drflac_uint64 frameSizes = 0;
+ drflac_uint64 importantProps;
+ drflac_uint8 md5[16];
+
+ /* min/max block size. */
if (onRead(pUserData, &blockSizes, 4) != 4) {
return DRFLAC_FALSE;
}
- // min/max frame size.
- drflac_uint64 frameSizes = 0;
+ /* min/max frame size. */
if (onRead(pUserData, &frameSizes, 6) != 6) {
return DRFLAC_FALSE;
}
- // Sample rate, channels, bits per sample and total sample count.
- drflac_uint64 importantProps;
+ /* Sample rate, channels, bits per sample and total sample count. */
if (onRead(pUserData, &importantProps, 8) != 8) {
return DRFLAC_FALSE;
}
- // MD5
- drflac_uint8 md5[16];
+ /* MD5 */
if (onRead(pUserData, md5, sizeof(md5)) != sizeof(md5)) {
return DRFLAC_FALSE;
}
@@ -3467,13 +4945,13 @@ drflac_bool32 drflac__read_streaminfo(drflac_read_proc onRead, void* pUserData,
importantProps = drflac__be2host_64(importantProps);
pStreamInfo->minBlockSize = (blockSizes & 0xFFFF0000) >> 16;
- pStreamInfo->maxBlockSize = blockSizes & 0x0000FFFF;
- pStreamInfo->minFrameSize = (drflac_uint32)((frameSizes & (drflac_uint64)0xFFFFFF0000000000) >> 40);
- pStreamInfo->maxFrameSize = (drflac_uint32)((frameSizes & (drflac_uint64)0x000000FFFFFF0000) >> 16);
- pStreamInfo->sampleRate = (drflac_uint32)((importantProps & (drflac_uint64)0xFFFFF00000000000) >> 44);
- pStreamInfo->channels = (drflac_uint8 )((importantProps & (drflac_uint64)0x00000E0000000000) >> 41) + 1;
- pStreamInfo->bitsPerSample = (drflac_uint8 )((importantProps & (drflac_uint64)0x000001F000000000) >> 36) + 1;
- pStreamInfo->totalSampleCount = (importantProps & (drflac_uint64)0x0000000FFFFFFFFF) * pStreamInfo->channels;
+ pStreamInfo->maxBlockSize = (blockSizes & 0x0000FFFF);
+ pStreamInfo->minFrameSize = (drflac_uint32)((frameSizes & (((drflac_uint64)0x00FFFFFF << 16) << 24)) >> 40);
+ pStreamInfo->maxFrameSize = (drflac_uint32)((frameSizes & (((drflac_uint64)0x00FFFFFF << 16) << 0)) >> 16);
+ pStreamInfo->sampleRate = (drflac_uint32)((importantProps & (((drflac_uint64)0x000FFFFF << 16) << 28)) >> 44);
+ pStreamInfo->channels = (drflac_uint8 )((importantProps & (((drflac_uint64)0x0000000E << 16) << 24)) >> 41) + 1;
+ pStreamInfo->bitsPerSample = (drflac_uint8 )((importantProps & (((drflac_uint64)0x0000001F << 16) << 20)) >> 36) + 1;
+ pStreamInfo->totalSampleCount = ((importantProps & ((((drflac_uint64)0x0000000F << 16) << 16) | 0xFFFFFFFF))) * pStreamInfo->channels;
drflac_copy_memory(pStreamInfo->md5, md5, sizeof(md5));
return DRFLAC_TRUE;
@@ -3481,13 +4959,16 @@ drflac_bool32 drflac__read_streaminfo(drflac_read_proc onRead, void* pUserData,
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)
{
- // 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'll be sitting on byte 42.
+ /*
+ 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'll be sitting on byte 42.
+ */
drflac_uint64 runningFilePos = 42;
drflac_uint64 seektablePos = 0;
drflac_uint32 seektableSize = 0;
for (;;) {
+ drflac_metadata metadata;
drflac_uint8 isLastBlock = 0;
drflac_uint8 blockType;
drflac_uint32 blockSize;
@@ -3496,8 +4977,6 @@ drflac_bool32 drflac__read_and_decode_metadata(drflac_read_proc onRead, drflac_s
}
runningFilePos += 4;
-
- drflac_metadata metadata;
metadata.type = blockType;
metadata.pRawData = NULL;
metadata.rawDataSize = 0;
@@ -3506,6 +4985,10 @@ drflac_bool32 drflac__read_and_decode_metadata(drflac_read_proc onRead, drflac_s
{
case DRFLAC_METADATA_BLOCK_TYPE_APPLICATION:
{
+ if (blockSize < 4) {
+ return DRFLAC_FALSE;
+ }
+
if (onMeta) {
void* pRawData = DRFLAC_MALLOC(blockSize);
if (pRawData == NULL) {
@@ -3534,7 +5017,10 @@ drflac_bool32 drflac__read_and_decode_metadata(drflac_read_proc onRead, drflac_s
seektableSize = blockSize;
if (onMeta) {
- void* pRawData = DRFLAC_MALLOC(blockSize);
+ drflac_uint32 iSeekpoint;
+ void* pRawData;
+
+ pRawData = DRFLAC_MALLOC(blockSize);
if (pRawData == NULL) {
return DRFLAC_FALSE;
}
@@ -3549,8 +5035,8 @@ drflac_bool32 drflac__read_and_decode_metadata(drflac_read_proc onRead, drflac_s
metadata.data.seektable.seekpointCount = blockSize/sizeof(drflac_seekpoint);
metadata.data.seektable.pSeekpoints = (const drflac_seekpoint*)pRawData;
- // Endian swap.
- for (drflac_uint32 iSeekpoint = 0; iSeekpoint < metadata.data.seektable.seekpointCount; ++iSeekpoint) {
+ /* Endian swap. */
+ for (iSeekpoint = 0; iSeekpoint < metadata.data.seektable.seekpointCount; ++iSeekpoint) {
drflac_seekpoint* pSeekpoint = (drflac_seekpoint*)pRawData + iSeekpoint;
pSeekpoint->firstSample = drflac__be2host_64(pSeekpoint->firstSample);
pSeekpoint->frameOffset = drflac__be2host_64(pSeekpoint->frameOffset);
@@ -3565,8 +5051,17 @@ drflac_bool32 drflac__read_and_decode_metadata(drflac_read_proc onRead, drflac_s
case DRFLAC_METADATA_BLOCK_TYPE_VORBIS_COMMENT:
{
+ if (blockSize < 8) {
+ return DRFLAC_FALSE;
+ }
+
if (onMeta) {
- void* pRawData = DRFLAC_MALLOC(blockSize);
+ void* pRawData;
+ const char* pRunningData;
+ const char* pRunningDataEnd;
+ drflac_uint32 i;
+
+ pRawData = DRFLAC_MALLOC(blockSize);
if (pRawData == NULL) {
return DRFLAC_FALSE;
}
@@ -3579,11 +5074,43 @@ drflac_bool32 drflac__read_and_decode_metadata(drflac_read_proc onRead, drflac_s
metadata.pRawData = pRawData;
metadata.rawDataSize = blockSize;
- const char* pRunningData = (const char*)pRawData;
- metadata.data.vorbis_comment.vendorLength = drflac__le2host_32(*(drflac_uint32*)pRunningData); pRunningData += 4;
- metadata.data.vorbis_comment.vendor = pRunningData; pRunningData += metadata.data.vorbis_comment.vendorLength;
- metadata.data.vorbis_comment.commentCount = drflac__le2host_32(*(drflac_uint32*)pRunningData); pRunningData += 4;
- metadata.data.vorbis_comment.comments = pRunningData;
+ pRunningData = (const char*)pRawData;
+ pRunningDataEnd = (const char*)pRawData + blockSize;
+
+ metadata.data.vorbis_comment.vendorLength = drflac__le2host_32(*(const drflac_uint32*)pRunningData); pRunningData += 4;
+
+ /* Need space for the rest of the block */
+ if ((pRunningDataEnd - pRunningData) - 4 < (drflac_int64)metadata.data.vorbis_comment.vendorLength) { /* <-- Note the order of operations to avoid overflow to a valid value */
+ DRFLAC_FREE(pRawData);
+ return DRFLAC_FALSE;
+ }
+ metadata.data.vorbis_comment.vendor = pRunningData; pRunningData += metadata.data.vorbis_comment.vendorLength;
+ metadata.data.vorbis_comment.commentCount = drflac__le2host_32(*(const drflac_uint32*)pRunningData); pRunningData += 4;
+
+ /* Need space for 'commentCount' comments after the block, which at minimum is a drflac_uint32 per comment */
+ if ((pRunningDataEnd - pRunningData) / sizeof(drflac_uint32) < metadata.data.vorbis_comment.commentCount) { /* <-- Note the order of operations to avoid overflow to a valid value */
+ DRFLAC_FREE(pRawData);
+ return DRFLAC_FALSE;
+ }
+ metadata.data.vorbis_comment.pComments = pRunningData;
+
+ /* Check that the comments section is valid before passing it to the callback */
+ for (i = 0; i < metadata.data.vorbis_comment.commentCount; ++i) {
+ drflac_uint32 commentLength;
+
+ if (pRunningDataEnd - pRunningData < 4) {
+ DRFLAC_FREE(pRawData);
+ return DRFLAC_FALSE;
+ }
+
+ commentLength = drflac__le2host_32(*(const drflac_uint32*)pRunningData); pRunningData += 4;
+ if (pRunningDataEnd - pRunningData < (drflac_int64)commentLength) { /* <-- Note the order of operations to avoid overflow to a valid value */
+ DRFLAC_FREE(pRawData);
+ return DRFLAC_FALSE;
+ }
+ pRunningData += commentLength;
+ }
+
onMeta(pUserDataMD, &metadata);
DRFLAC_FREE(pRawData);
@@ -3592,8 +5119,18 @@ drflac_bool32 drflac__read_and_decode_metadata(drflac_read_proc onRead, drflac_s
case DRFLAC_METADATA_BLOCK_TYPE_CUESHEET:
{
+ if (blockSize < 396) {
+ return DRFLAC_FALSE;
+ }
+
if (onMeta) {
- void* pRawData = DRFLAC_MALLOC(blockSize);
+ void* pRawData;
+ const char* pRunningData;
+ const char* pRunningDataEnd;
+ drflac_uint8 iTrack;
+ drflac_uint8 iIndex;
+
+ pRawData = DRFLAC_MALLOC(blockSize);
if (pRawData == NULL) {
return DRFLAC_FALSE;
}
@@ -3606,12 +5143,42 @@ drflac_bool32 drflac__read_and_decode_metadata(drflac_read_proc onRead, drflac_s
metadata.pRawData = pRawData;
metadata.rawDataSize = blockSize;
- const char* pRunningData = (const char*)pRawData;
- drflac_copy_memory(metadata.data.cuesheet.catalog, pRunningData, 128); pRunningData += 128;
- metadata.data.cuesheet.leadInSampleCount = drflac__be2host_64(*(drflac_uint64*)pRunningData); pRunningData += 4;
- metadata.data.cuesheet.isCD = ((pRunningData[0] & 0x80) >> 7) != 0; pRunningData += 259;
- metadata.data.cuesheet.trackCount = pRunningData[0]; pRunningData += 1;
- metadata.data.cuesheet.pTrackData = (const drflac_uint8*)pRunningData;
+ pRunningData = (const char*)pRawData;
+ pRunningDataEnd = (const char*)pRawData + blockSize;
+
+ drflac_copy_memory(metadata.data.cuesheet.catalog, pRunningData, 128); pRunningData += 128;
+ metadata.data.cuesheet.leadInSampleCount = drflac__be2host_64(*(const drflac_uint64*)pRunningData); pRunningData += 8;
+ metadata.data.cuesheet.isCD = (pRunningData[0] & 0x80) != 0; pRunningData += 259;
+ metadata.data.cuesheet.trackCount = pRunningData[0]; pRunningData += 1;
+ metadata.data.cuesheet.pTrackData = pRunningData;
+
+ /* Check that the cuesheet tracks are valid before passing it to the callback */
+ for (iTrack = 0; iTrack < metadata.data.cuesheet.trackCount; ++iTrack) {
+ drflac_uint8 indexCount;
+ drflac_uint32 indexPointSize;
+
+ if (pRunningDataEnd - pRunningData < 36) {
+ DRFLAC_FREE(pRawData);
+ return DRFLAC_FALSE;
+ }
+
+ /* Skip to the index point count */
+ pRunningData += 35;
+ indexCount = pRunningData[0]; pRunningData += 1;
+ indexPointSize = indexCount * sizeof(drflac_cuesheet_track_index);
+ if (pRunningDataEnd - pRunningData < (drflac_int64)indexPointSize) {
+ DRFLAC_FREE(pRawData);
+ return DRFLAC_FALSE;
+ }
+
+ /* Endian swap. */
+ for (iIndex = 0; iIndex < indexCount; ++iIndex) {
+ drflac_cuesheet_track_index* pTrack = (drflac_cuesheet_track_index*)pRunningData;
+ pRunningData += sizeof(drflac_cuesheet_track_index);
+ pTrack->offset = drflac__be2host_64(pTrack->offset);
+ }
+ }
+
onMeta(pUserDataMD, &metadata);
DRFLAC_FREE(pRawData);
@@ -3620,8 +5187,16 @@ drflac_bool32 drflac__read_and_decode_metadata(drflac_read_proc onRead, drflac_s
case DRFLAC_METADATA_BLOCK_TYPE_PICTURE:
{
+ if (blockSize < 32) {
+ return DRFLAC_FALSE;
+ }
+
if (onMeta) {
- void* pRawData = DRFLAC_MALLOC(blockSize);
+ void* pRawData;
+ const char* pRunningData;
+ const char* pRunningDataEnd;
+
+ pRawData = DRFLAC_MALLOC(blockSize);
if (pRawData == NULL) {
return DRFLAC_FALSE;
}
@@ -3634,18 +5209,39 @@ drflac_bool32 drflac__read_and_decode_metadata(drflac_read_proc onRead, drflac_s
metadata.pRawData = pRawData;
metadata.rawDataSize = blockSize;
- const char* pRunningData = (const char*)pRawData;
- metadata.data.picture.type = drflac__be2host_32(*(drflac_uint32*)pRunningData); pRunningData += 4;
- metadata.data.picture.mimeLength = drflac__be2host_32(*(drflac_uint32*)pRunningData); pRunningData += 4;
- metadata.data.picture.mime = pRunningData; pRunningData += metadata.data.picture.mimeLength;
- metadata.data.picture.descriptionLength = drflac__be2host_32(*(drflac_uint32*)pRunningData); pRunningData += 4;
- metadata.data.picture.description = pRunningData;
- metadata.data.picture.width = drflac__be2host_32(*(drflac_uint32*)pRunningData); pRunningData += 4;
- metadata.data.picture.height = drflac__be2host_32(*(drflac_uint32*)pRunningData); pRunningData += 4;
- metadata.data.picture.colorDepth = drflac__be2host_32(*(drflac_uint32*)pRunningData); pRunningData += 4;
- metadata.data.picture.indexColorCount = drflac__be2host_32(*(drflac_uint32*)pRunningData); pRunningData += 4;
- metadata.data.picture.pictureDataSize = drflac__be2host_32(*(drflac_uint32*)pRunningData); pRunningData += 4;
- metadata.data.picture.pPictureData = (const drflac_uint8*)pRunningData;
+ pRunningData = (const char*)pRawData;
+ pRunningDataEnd = (const char*)pRawData + blockSize;
+
+ metadata.data.picture.type = drflac__be2host_32(*(const drflac_uint32*)pRunningData); pRunningData += 4;
+ metadata.data.picture.mimeLength = drflac__be2host_32(*(const drflac_uint32*)pRunningData); pRunningData += 4;
+
+ /* Need space for the rest of the block */
+ if ((pRunningDataEnd - pRunningData) - 24 < (drflac_int64)metadata.data.picture.mimeLength) { /* <-- Note the order of operations to avoid overflow to a valid value */
+ DRFLAC_FREE(pRawData);
+ return DRFLAC_FALSE;
+ }
+ metadata.data.picture.mime = pRunningData; pRunningData += metadata.data.picture.mimeLength;
+ metadata.data.picture.descriptionLength = drflac__be2host_32(*(const drflac_uint32*)pRunningData); pRunningData += 4;
+
+ /* Need space for the rest of the block */
+ if ((pRunningDataEnd - pRunningData) - 20 < (drflac_int64)metadata.data.picture.descriptionLength) { /* <-- Note the order of operations to avoid overflow to a valid value */
+ DRFLAC_FREE(pRawData);
+ return DRFLAC_FALSE;
+ }
+ metadata.data.picture.description = pRunningData; pRunningData += metadata.data.picture.descriptionLength;
+ metadata.data.picture.width = drflac__be2host_32(*(const drflac_uint32*)pRunningData); pRunningData += 4;
+ metadata.data.picture.height = drflac__be2host_32(*(const drflac_uint32*)pRunningData); pRunningData += 4;
+ metadata.data.picture.colorDepth = drflac__be2host_32(*(const drflac_uint32*)pRunningData); pRunningData += 4;
+ metadata.data.picture.indexColorCount = drflac__be2host_32(*(const drflac_uint32*)pRunningData); pRunningData += 4;
+ metadata.data.picture.pictureDataSize = drflac__be2host_32(*(const drflac_uint32*)pRunningData); pRunningData += 4;
+ metadata.data.picture.pPictureData = (const drflac_uint8*)pRunningData;
+
+ /* Need space for the picture after the block */
+ if (pRunningDataEnd - pRunningData < (drflac_int64)metadata.data.picture.pictureDataSize) { /* <-- Note the order of operations to avoid overflow to a valid value */
+ DRFLAC_FREE(pRawData);
+ return DRFLAC_FALSE;
+ }
+
onMeta(pUserDataMD, &metadata);
DRFLAC_FREE(pRawData);
@@ -3657,9 +5253,9 @@ drflac_bool32 drflac__read_and_decode_metadata(drflac_read_proc onRead, drflac_s
if (onMeta) {
metadata.data.padding.unused = 0;
- // Padding doesn't have anything meaningful in it, so just skip over it, but make sure the caller is aware of it by firing the callback.
+ /* Padding doesn't have anything meaningful in it, so just skip over it, but make sure the caller is aware of it by firing the callback. */
if (!onSeek(pUserData, blockSize, drflac_seek_origin_current)) {
- isLastBlock = DRFLAC_TRUE; // An error occurred while seeking. Attempt to recover by treating this as the last block which will in turn terminate the loop.
+ isLastBlock = DRFLAC_TRUE; /* An error occurred while seeking. Attempt to recover by treating this as the last block which will in turn terminate the loop. */
} else {
onMeta(pUserDataMD, &metadata);
}
@@ -3668,18 +5264,20 @@ drflac_bool32 drflac__read_and_decode_metadata(drflac_read_proc onRead, drflac_s
case DRFLAC_METADATA_BLOCK_TYPE_INVALID:
{
- // Invalid chunk. Just skip over this one.
+ /* Invalid chunk. Just skip over this one. */
if (onMeta) {
if (!onSeek(pUserData, blockSize, drflac_seek_origin_current)) {
- isLastBlock = DRFLAC_TRUE; // An error occurred while seeking. Attempt to recover by treating this as the last block which will in turn terminate the loop.
+ isLastBlock = DRFLAC_TRUE; /* An error occurred while seeking. Attempt to recover by treating this as the last block which will in turn terminate the loop. */
}
}
} break;
default:
{
- // It's an unknown chunk, but not necessarily invalid. There's a chance more metadata blocks might be defined later on, so we
- // can at the very least report the chunk to the application and let it look at the raw data.
+ /*
+ It's an unknown chunk, but not necessarily invalid. There's a chance more metadata blocks might be defined later on, so we
+ can at the very least report the chunk to the application and let it look at the raw data.
+ */
if (onMeta) {
void* pRawData = DRFLAC_MALLOC(blockSize);
if (pRawData == NULL) {
@@ -3700,7 +5298,7 @@ drflac_bool32 drflac__read_and_decode_metadata(drflac_read_proc onRead, drflac_s
} break;
}
- // If we're not handling metadata, just skip over the block. If we are, it will have been handled earlier in the switch statement above.
+ /* If we're not handling metadata, just skip over the block. If we are, it will have been handled earlier in the switch statement above. */
if (onMeta == NULL && blockSize > 0) {
if (!onSeek(pUserData, blockSize, drflac_seek_origin_current)) {
isLastBlock = DRFLAC_TRUE;
@@ -3722,42 +5320,45 @@ drflac_bool32 drflac__read_and_decode_metadata(drflac_read_proc onRead, drflac_s
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)
{
- (void)onSeek;
-
- // Pre: The bit stream should be sitting just past the 4-byte id header.
-
- pInit->container = drflac_container_native;
+ /* Pre Condition: The bit stream should be sitting just past the 4-byte id header. */
- // The first metadata block should be the STREAMINFO block.
drflac_uint8 isLastBlock;
drflac_uint8 blockType;
drflac_uint32 blockSize;
+
+ (void)onSeek;
+
+ pInit->container = drflac_container_native;
+
+ /* The first metadata block should be the STREAMINFO block. */
if (!drflac__read_and_decode_block_header(onRead, pUserData, &isLastBlock, &blockType, &blockSize)) {
return DRFLAC_FALSE;
}
if (blockType != DRFLAC_METADATA_BLOCK_TYPE_STREAMINFO || blockSize != 34) {
if (!relaxed) {
- // We're opening in strict mode and the first block is not the STREAMINFO block. Error.
+ /* We're opening in strict mode and the first block is not the STREAMINFO block. Error. */
return DRFLAC_FALSE;
} else {
- // Relaxed mode. To open from here we need to just find the first frame and set the sample rate, etc. to whatever is defined
- // for that frame.
+ /*
+ Relaxed mode. To open from here we need to just find the first frame and set the sample rate, etc. to whatever is defined
+ for that frame.
+ */
pInit->hasStreamInfoBlock = DRFLAC_FALSE;
pInit->hasMetadataBlocks = DRFLAC_FALSE;
- if (!drflac__read_next_frame_header(&pInit->bs, 0, &pInit->firstFrameHeader)) {
- return DRFLAC_FALSE; // Couldn't find a frame.
+ if (!drflac__read_next_flac_frame_header(&pInit->bs, 0, &pInit->firstFrameHeader)) {
+ return DRFLAC_FALSE; /* Couldn't find a frame. */
}
if (pInit->firstFrameHeader.bitsPerSample == 0) {
- return DRFLAC_FALSE; // Failed to initialize because the first frame depends on the STREAMINFO block, which does not exist.
+ return DRFLAC_FALSE; /* Failed to initialize because the first frame depends on the STREAMINFO block, which does not exist. */
}
pInit->sampleRate = pInit->firstFrameHeader.sampleRate;
pInit->channels = drflac__get_channel_count_from_channel_assignment(pInit->firstFrameHeader.channelAssignment);
pInit->bitsPerSample = pInit->firstFrameHeader.bitsPerSample;
- pInit->maxBlockSize = 65535; // <-- See notes here: https://xiph.org/flac/format.html#metadata_block_streaminfo
+ pInit->maxBlockSize = 65535; /* <-- See notes here: https://xiph.org/flac/format.html#metadata_block_streaminfo */
return DRFLAC_TRUE;
}
} else {
@@ -3771,8 +5372,8 @@ drflac_bool32 drflac__init_private__native(drflac_init_info* pInit, drflac_read_
pInit->channels = streaminfo.channels;
pInit->bitsPerSample = streaminfo.bitsPerSample;
pInit->totalSampleCount = streaminfo.totalSampleCount;
- pInit->maxBlockSize = streaminfo.maxBlockSize; // Don't care about the min block size - only the max (used for determining the size of the memory allocation).
- pInit->hasMetadataBlocks = !isLastBlock;
+ pInit->maxBlockSize = streaminfo.maxBlockSize; /* Don't care about the min block size - only the max (used for determining the size of the memory allocation). */
+ pInit->hasMetadataBlocks = !isLastBlock;
if (onMeta) {
drflac_metadata metadata;
@@ -3789,7 +5390,7 @@ drflac_bool32 drflac__init_private__native(drflac_init_info* pInit, drflac_read_
#ifndef DR_FLAC_NO_OGG
#define DRFLAC_OGG_MAX_PAGE_SIZE 65307
-#define DRFLAC_OGG_CAPTURE_PATTERN_CRC32 1605413199 // CRC-32 of "OggS".
+#define DRFLAC_OGG_CAPTURE_PATTERN_CRC32 1605413199 /* CRC-32 of "OggS". */
typedef enum
{
@@ -3797,7 +5398,7 @@ typedef enum
drflac_ogg_fail_on_crc_mismatch
} drflac_ogg_crc_mismatch_recovery;
-
+#ifndef DR_FLAC_NO_CRC
static drflac_uint32 drflac__crc32_table[] = {
0x00000000L, 0x04C11DB7L, 0x09823B6EL, 0x0D4326D9L,
0x130476DCL, 0x17C56B6BL, 0x1A864DB2L, 0x1E475005L,
@@ -3864,6 +5465,7 @@ static drflac_uint32 drflac__crc32_table[] = {
0xAFB010B1L, 0xAB710D06L, 0xA6322BDFL, 0xA2F33668L,
0xBCB4666DL, 0xB8757BDAL, 0xB5365D03L, 0xB1F740B4L
};
+#endif
static DRFLAC_INLINE drflac_uint32 drflac_crc32_byte(drflac_uint32 crc32, drflac_uint8 data)
{
@@ -3895,8 +5497,9 @@ static DRFLAC_INLINE drflac_uint32 drflac_crc32_uint64(drflac_uint32 crc32, drfl
static DRFLAC_INLINE drflac_uint32 drflac_crc32_buffer(drflac_uint32 crc32, drflac_uint8* pData, drflac_uint32 dataSize)
{
- // This can be optimized.
- for (drflac_uint32 i = 0; i < dataSize; ++i) {
+ /* This can be optimized. */
+ drflac_uint32 i;
+ for (i = 0; i < dataSize; ++i) {
crc32 = drflac_crc32_byte(crc32, pData[i]);
}
return crc32;
@@ -3916,7 +5519,9 @@ static DRFLAC_INLINE drflac_uint32 drflac_ogg__get_page_header_size(drflac_ogg_p
static DRFLAC_INLINE drflac_uint32 drflac_ogg__get_page_body_size(drflac_ogg_page_header* pHeader)
{
drflac_uint32 pageBodySize = 0;
- for (int i = 0; i < pHeader->segmentCount; ++i) {
+ int i;
+
+ for (i = 0; i < pHeader->segmentCount; ++i) {
pageBodySize += pHeader->segmentTable[i];
}
@@ -3925,9 +5530,11 @@ static DRFLAC_INLINE drflac_uint32 drflac_ogg__get_page_body_size(drflac_ogg_pag
drflac_result drflac_ogg__read_page_header_after_capture_pattern(drflac_read_proc onRead, void* pUserData, drflac_ogg_page_header* pHeader, drflac_uint32* pBytesRead, drflac_uint32* pCRC32)
{
+ drflac_uint8 data[23];
+ drflac_uint32 i;
+
drflac_assert(*pCRC32 == DRFLAC_OGG_CAPTURE_PATTERN_CRC32);
- drflac_uint8 data[23];
if (onRead(pUserData, data, 23) != 23) {
return DRFLAC_END_OF_STREAM;
}
@@ -3941,13 +5548,12 @@ drflac_result drflac_ogg__read_page_header_after_capture_pattern(drflac_read_pro
drflac_copy_memory(&pHeader->checksum, &data[18], 4);
pHeader->segmentCount = data[22];
- // Calculate the CRC. Note that for the calculation the checksum part of the page needs to be set to 0.
+ /* Calculate the CRC. Note that for the calculation the checksum part of the page needs to be set to 0. */
data[18] = 0;
data[19] = 0;
data[20] = 0;
data[21] = 0;
- drflac_uint32 i;
for (i = 0; i < 23; ++i) {
*pCRC32 = drflac_crc32_byte(*pCRC32, data[i]);
}
@@ -3967,20 +5573,23 @@ drflac_result drflac_ogg__read_page_header_after_capture_pattern(drflac_read_pro
drflac_result drflac_ogg__read_page_header(drflac_read_proc onRead, void* pUserData, drflac_ogg_page_header* pHeader, drflac_uint32* pBytesRead, drflac_uint32* pCRC32)
{
+ drflac_uint8 id[4];
+
*pBytesRead = 0;
- drflac_uint8 id[4];
if (onRead(pUserData, id, 4) != 4) {
return DRFLAC_END_OF_STREAM;
}
*pBytesRead += 4;
- // We need to read byte-by-byte until we find the OggS capture pattern.
+ /* We need to read byte-by-byte until we find the OggS capture pattern. */
for (;;) {
if (drflac_ogg__is_capture_pattern(id)) {
+ drflac_result result;
+
*pCRC32 = DRFLAC_OGG_CAPTURE_PATTERN_CRC32;
- drflac_result result = drflac_ogg__read_page_header_after_capture_pattern(onRead, pUserData, pHeader, pBytesRead, pCRC32);
+ result = drflac_ogg__read_page_header_after_capture_pattern(onRead, pUserData, pHeader, pBytesRead, pCRC32);
if (result == DRFLAC_SUCCESS) {
return DRFLAC_SUCCESS;
} else {
@@ -3991,7 +5600,7 @@ drflac_result drflac_ogg__read_page_header(drflac_read_proc onRead, void* pUserD
}
}
} else {
- // The first 4 bytes did not equal the capture pattern. Read the next byte and try again.
+ /* The first 4 bytes did not equal the capture pattern. Read the next byte and try again. */
id[0] = id[1];
id[1] = id[2];
id[2] = id[3];
@@ -4004,25 +5613,27 @@ drflac_result drflac_ogg__read_page_header(drflac_read_proc onRead, void* pUserD
}
-// The main part of the Ogg encapsulation is the conversion from the physical Ogg bitstream to the native FLAC bitstream. It works
-// in three general stages: Ogg Physical Bitstream -> Ogg/FLAC Logical Bitstream -> FLAC Native Bitstream. dr_flac is designed
-// in such a way that the core sections assume everything is delivered in native format. Therefore, for each encapsulation type
-// dr_flac is supporting there needs to be a layer sitting on top of the onRead and onSeek callbacks that ensures the bits read from
-// the physical Ogg bitstream are converted and delivered in native FLAC format.
+/*
+The main part of the Ogg encapsulation is the conversion from the physical Ogg bitstream to the native FLAC bitstream. It works
+in three general stages: Ogg Physical Bitstream -> Ogg/FLAC Logical Bitstream -> FLAC Native Bitstream. dr_flac is designed
+in such a way that the core sections assume everything is delivered in native format. Therefore, for each encapsulation type
+dr_flac is supporting there needs to be a layer sitting on top of the onRead and onSeek callbacks that ensures the bits read from
+the physical Ogg bitstream are converted and delivered in native FLAC format.
+*/
typedef struct
{
- drflac_read_proc onRead; // The original onRead callback from drflac_open() and family.
- drflac_seek_proc onSeek; // The original onSeek callback from drflac_open() and family.
- void* pUserData; // The user data passed on onRead and onSeek. This is the user data that was passed on drflac_open() and family.
- drflac_uint64 currentBytePos; // The position of the byte we are sitting on in the physical byte stream. Used for efficient seeking.
- drflac_uint64 firstBytePos; // The position of the first byte in the physical bitstream. Points to the start of the "OggS" identifier of the FLAC bos page.
- drflac_uint32 serialNumber; // The serial number of the FLAC audio pages. This is determined by the initial header page that was read during initialization.
- drflac_ogg_page_header bosPageHeader; // Used for seeking.
+ drflac_read_proc onRead; /* The original onRead callback from drflac_open() and family. */
+ drflac_seek_proc onSeek; /* The original onSeek callback from drflac_open() and family. */
+ void* pUserData; /* The user data passed on onRead and onSeek. This is the user data that was passed on drflac_open() and family. */
+ drflac_uint64 currentBytePos; /* The position of the byte we are sitting on in the physical byte stream. Used for efficient seeking. */
+ drflac_uint64 firstBytePos; /* The position of the first byte in the physical bitstream. Points to the start of the "OggS" identifier of the FLAC bos page. */
+ drflac_uint32 serialNumber; /* The serial number of the FLAC audio pages. This is determined by the initial header page that was read during initialization. */
+ drflac_ogg_page_header bosPageHeader; /* Used for seeking. */
drflac_ogg_page_header currentPageHeader;
drflac_uint32 bytesRemainingInPage;
drflac_uint32 pageDataSize;
drflac_uint8 pageData[DRFLAC_OGG_MAX_PAGE_SIZE];
-} drflac_oggbs; // oggbs = Ogg Bitstream
+} drflac_oggbs; /* oggbs = Ogg Bitstream */
static size_t drflac_oggbs__read_physical(drflac_oggbs* oggbs, void* bufferOut, size_t bytesToRead)
{
@@ -4059,7 +5670,7 @@ static drflac_bool32 drflac_oggbs__seek_physical(drflac_oggbs* oggbs, drflac_uin
offset -= 0x7FFFFFFF;
}
- if (!oggbs->onSeek(oggbs->pUserData, (int)offset, drflac_seek_origin_current)) { // <-- Safe cast thanks to the loop above.
+ if (!oggbs->onSeek(oggbs->pUserData, (int)offset, drflac_seek_origin_current)) { /* <-- Safe cast thanks to the loop above. */
return DRFLAC_FALSE;
}
oggbs->currentBytePos += offset;
@@ -4074,18 +5685,23 @@ static drflac_bool32 drflac_oggbs__goto_next_page(drflac_oggbs* oggbs, drflac_og
for (;;) {
drflac_uint32 crc32 = 0;
drflac_uint32 bytesRead;
+ drflac_uint32 pageBodySize;
+#ifndef DR_FLAC_NO_CRC
+ drflac_uint32 actualCRC32;
+#endif
+
if (drflac_ogg__read_page_header(oggbs->onRead, oggbs->pUserData, &header, &bytesRead, &crc32) != DRFLAC_SUCCESS) {
return DRFLAC_FALSE;
}
oggbs->currentBytePos += bytesRead;
- drflac_uint32 pageBodySize = drflac_ogg__get_page_body_size(&header);
+ pageBodySize = drflac_ogg__get_page_body_size(&header);
if (pageBodySize > DRFLAC_OGG_MAX_PAGE_SIZE) {
- continue; // Invalid page size. Assume it's corrupted and just move to the next page.
+ continue; /* Invalid page size. Assume it's corrupted and just move to the next page. */
}
if (header.serialNumber != oggbs->serialNumber) {
- // It's not a FLAC page. Skip it.
+ /* It's not a FLAC page. Skip it. */
if (pageBodySize > 0 && !drflac_oggbs__seek_physical(oggbs, pageBodySize, drflac_seek_origin_current)) {
return DRFLAC_FALSE;
}
@@ -4093,27 +5709,29 @@ static drflac_bool32 drflac_oggbs__goto_next_page(drflac_oggbs* oggbs, drflac_og
}
- // We need to read the entire page and then do a CRC check on it. If there's a CRC mismatch we need to skip this page.
+ /* We need to read the entire page and then do a CRC check on it. If there's a CRC mismatch we need to skip this page. */
if (drflac_oggbs__read_physical(oggbs, oggbs->pageData, pageBodySize) != pageBodySize) {
return DRFLAC_FALSE;
}
oggbs->pageDataSize = pageBodySize;
#ifndef DR_FLAC_NO_CRC
- drflac_uint32 actualCRC32 = drflac_crc32_buffer(crc32, oggbs->pageData, oggbs->pageDataSize);
+ actualCRC32 = drflac_crc32_buffer(crc32, oggbs->pageData, oggbs->pageDataSize);
if (actualCRC32 != header.checksum) {
if (recoveryMethod == drflac_ogg_recover_on_crc_mismatch) {
- continue; // CRC mismatch. Skip this page.
+ continue; /* CRC mismatch. Skip this page. */
} else {
- // Even though we are failing on a CRC mismatch, we still want our stream to be in a good state. Therefore we
- // go to the next valid page to ensure we're in a good state, but return false to let the caller know that the
- // seek did not fully complete.
+ /*
+ Even though we are failing on a CRC mismatch, we still want our stream to be in a good state. Therefore we
+ go to the next valid page to ensure we're in a good state, but return false to let the caller know that the
+ seek did not fully complete.
+ */
drflac_oggbs__goto_next_page(oggbs, drflac_ogg_recover_on_crc_mismatch);
return DRFLAC_FALSE;
}
}
#else
- (void)recoveryMethod; // <-- Silence a warning.
+ (void)recoveryMethod; /* <-- Silence a warning. */
#endif
oggbs->currentPageHeader = header;
@@ -4122,7 +5740,7 @@ static drflac_bool32 drflac_oggbs__goto_next_page(drflac_oggbs* oggbs, drflac_og
}
}
-// Function below is unused at the moment, but I might be re-adding it later.
+/* Function below is unused at the moment, but I might be re-adding it later. */
#if 0
static drflac_uint8 drflac_oggbs__get_current_segment_index(drflac_oggbs* oggbs, drflac_uint8* pBytesRemainingInSeg)
{
@@ -4145,7 +5763,7 @@ static drflac_uint8 drflac_oggbs__get_current_segment_index(drflac_oggbs* oggbs,
static drflac_bool32 drflac_oggbs__seek_to_next_packet(drflac_oggbs* oggbs)
{
- // The current packet ends when we get to the segment with a lacing value of < 255 which is not at the end of a page.
+ /* The current packet ends when we get to the segment with a lacing value of < 255 which is not at the end of a page. */
for (;;) {
drflac_bool32 atEndOfPage = DRFLAC_FALSE;
@@ -4166,24 +5784,28 @@ static drflac_bool32 drflac_oggbs__seek_to_next_packet(drflac_oggbs* oggbs)
bytesToEndOfPacketOrPage += segmentSize;
}
- // At this point we will have found either the packet or the end of the page. If were at the end of the page we'll
- // want to load the next page and keep searching for the end of the packet.
+ /*
+ At this point we will have found either the packet or the end of the page. If were at the end of the page we'll
+ want to load the next page and keep searching for the end of the packet.
+ */
drflac_oggbs__seek_physical(oggbs, bytesToEndOfPacketOrPage, drflac_seek_origin_current);
oggbs->bytesRemainingInPage -= bytesToEndOfPacketOrPage;
if (atEndOfPage) {
- // We're potentially at the next packet, but we need to check the next page first to be sure because the packet may
- // straddle pages.
+ /*
+ We're potentially at the next packet, but we need to check the next page first to be sure because the packet may
+ straddle pages.
+ */
if (!drflac_oggbs__goto_next_page(oggbs)) {
return DRFLAC_FALSE;
}
- // If it's a fresh packet it most likely means we're at the next packet.
+ /* If it's a fresh packet it most likely means we're at the next packet. */
if ((oggbs->currentPageHeader.headerType & 0x01) == 0) {
return DRFLAC_TRUE;
}
} else {
- // We're at the next packet.
+ /* We're at the next packet. */
return DRFLAC_TRUE;
}
}
@@ -4191,9 +5813,9 @@ static drflac_bool32 drflac_oggbs__seek_to_next_packet(drflac_oggbs* oggbs)
static drflac_bool32 drflac_oggbs__seek_to_next_frame(drflac_oggbs* oggbs)
{
- // The bitstream should be sitting on the first byte just after the header of the frame.
+ /* The bitstream should be sitting on the first byte just after the header of the frame. */
- // What we're actually doing here is seeking to the start of the next packet.
+ /* What we're actually doing here is seeking to the start of the next packet. */
return drflac_oggbs__seek_to_next_packet(oggbs);
}
#endif
@@ -4201,12 +5823,13 @@ static drflac_bool32 drflac_oggbs__seek_to_next_frame(drflac_oggbs* oggbs)
static size_t drflac__on_read_ogg(void* pUserData, void* bufferOut, size_t bytesToRead)
{
drflac_oggbs* oggbs = (drflac_oggbs*)pUserData;
- drflac_assert(oggbs != NULL);
-
drflac_uint8* pRunningBufferOut = (drflac_uint8*)bufferOut;
-
- // Reading is done page-by-page. If we've run out of bytes in the page we need to move to the next one.
size_t bytesRead = 0;
+
+ drflac_assert(oggbs != NULL);
+ drflac_assert(pRunningBufferOut != NULL);
+
+ /* Reading is done page-by-page. If we've run out of bytes in the page we need to move to the next one. */
while (bytesRead < bytesToRead) {
size_t bytesRemainingToRead = bytesToRead - bytesRead;
@@ -4217,7 +5840,7 @@ static size_t drflac__on_read_ogg(void* pUserData, void* bufferOut, size_t bytes
break;
}
- // If we get here it means some of the requested data is contained in the next pages.
+ /* If we get here it means some of the requested data is contained in the next pages. */
if (oggbs->bytesRemainingInPage > 0) {
drflac_copy_memory(pRunningBufferOut, oggbs->pageData + (oggbs->pageDataSize - oggbs->bytesRemainingInPage), oggbs->bytesRemainingInPage);
bytesRead += oggbs->bytesRemainingInPage;
@@ -4227,7 +5850,7 @@ static size_t drflac__on_read_ogg(void* pUserData, void* bufferOut, size_t bytes
drflac_assert(bytesRemainingToRead > 0);
if (!drflac_oggbs__goto_next_page(oggbs, drflac_ogg_recover_on_crc_mismatch)) {
- break; // Failed to go to the next page. Might have simply hit the end of the stream.
+ break; /* Failed to go to the next page. Might have simply hit the end of the stream. */
}
}
@@ -4237,10 +5860,12 @@ static size_t drflac__on_read_ogg(void* pUserData, void* bufferOut, size_t bytes
static drflac_bool32 drflac__on_seek_ogg(void* pUserData, int offset, drflac_seek_origin origin)
{
drflac_oggbs* oggbs = (drflac_oggbs*)pUserData;
+ int bytesSeeked = 0;
+
drflac_assert(oggbs != NULL);
- drflac_assert(offset > 0 || (offset == 0 && origin == drflac_seek_origin_start));
+ drflac_assert(offset >= 0); /* <-- Never seek backwards. */
- // Seeking is always forward which makes things a lot simpler.
+ /* Seeking is always forward which makes things a lot simpler. */
if (origin == drflac_seek_origin_start) {
if (!drflac_oggbs__seek_physical(oggbs, (int)oggbs->firstBytePos, drflac_seek_origin_start)) {
return DRFLAC_FALSE;
@@ -4253,10 +5878,8 @@ static drflac_bool32 drflac__on_seek_ogg(void* pUserData, int offset, drflac_see
return drflac__on_seek_ogg(pUserData, offset, drflac_seek_origin_current);
}
-
drflac_assert(origin == drflac_seek_origin_current);
- int bytesSeeked = 0;
while (bytesSeeked < offset) {
int bytesRemainingToSeek = offset - bytesSeeked;
drflac_assert(bytesRemainingToSeek >= 0);
@@ -4267,7 +5890,7 @@ static drflac_bool32 drflac__on_seek_ogg(void* pUserData, int offset, drflac_see
break;
}
- // If we get here it means some of the requested data is contained in the next pages.
+ /* If we get here it means some of the requested data is contained in the next pages. */
if (oggbs->bytesRemainingInPage > 0) {
bytesSeeked += (int)oggbs->bytesRemainingInPage;
oggbs->bytesRemainingInPage = 0;
@@ -4275,7 +5898,7 @@ static drflac_bool32 drflac__on_seek_ogg(void* pUserData, int offset, drflac_see
drflac_assert(bytesRemainingToSeek > 0);
if (!drflac_oggbs__goto_next_page(oggbs, drflac_ogg_fail_on_crc_mismatch)) {
- // Failed to go to the next page. We either hit the end of the stream or had a CRC mismatch.
+ /* Failed to go to the next page. We either hit the end of the stream or had a CRC mismatch. */
return DRFLAC_FALSE;
}
}
@@ -4286,38 +5909,45 @@ static drflac_bool32 drflac__on_seek_ogg(void* pUserData, int offset, drflac_see
drflac_bool32 drflac_ogg__seek_to_sample(drflac* pFlac, drflac_uint64 sampleIndex)
{
drflac_oggbs* oggbs = (drflac_oggbs*)pFlac->_oggbs;
+ drflac_uint64 originalBytePos;
+ drflac_uint64 runningGranulePosition;
+ drflac_uint64 runningFrameBytePos;
+ drflac_uint64 runningSampleCount;
- drflac_uint64 originalBytePos = oggbs->currentBytePos; // For recovery.
+ drflac_assert(oggbs != NULL);
- // First seek to the first frame.
+ originalBytePos = oggbs->currentBytePos; /* For recovery. */
+
+ /* First seek to the first frame. */
if (!drflac__seek_to_byte(&pFlac->bs, pFlac->firstFramePos)) {
return DRFLAC_FALSE;
}
oggbs->bytesRemainingInPage = 0;
- drflac_uint64 runningGranulePosition = 0;
- drflac_uint64 runningFrameBytePos = oggbs->currentBytePos; // <-- Points to the OggS identifier.
+ runningGranulePosition = 0;
+ runningFrameBytePos = oggbs->currentBytePos; /* <-- Points to the OggS identifier. */
for (;;) {
if (!drflac_oggbs__goto_next_page(oggbs, drflac_ogg_recover_on_crc_mismatch)) {
drflac_oggbs__seek_physical(oggbs, originalBytePos, drflac_seek_origin_start);
- return DRFLAC_FALSE; // Never did find that sample...
+ return DRFLAC_FALSE; /* Never did find that sample... */
}
runningFrameBytePos = oggbs->currentBytePos - drflac_ogg__get_page_header_size(&oggbs->currentPageHeader) - oggbs->pageDataSize;
if (oggbs->currentPageHeader.granulePosition*pFlac->channels >= sampleIndex) {
- break; // The sample is somewhere in the previous page.
+ break; /* The sample is somewhere in the previous page. */
}
-
- // At this point we know the sample is not in the previous page. It could possibly be in this page. For simplicity we
- // disregard any pages that do not begin a fresh packet.
- if ((oggbs->currentPageHeader.headerType & 0x01) == 0) { // <-- Is it a fresh page?
+ /*
+ At this point we know the sample is not in the previous page. It could possibly be in this page. For simplicity we
+ disregard any pages that do not begin a fresh packet.
+ */
+ if ((oggbs->currentPageHeader.headerType & 0x01) == 0) { /* <-- Is it a fresh page? */
if (oggbs->currentPageHeader.segmentTable[0] >= 2) {
drflac_uint8 firstBytesInPage[2];
firstBytesInPage[0] = oggbs->pageData[0];
firstBytesInPage[1] = oggbs->pageData[1];
- if ((firstBytesInPage[0] == 0xFF) && (firstBytesInPage[1] & 0xFC) == 0xF8) { // <-- Does the page begin with a frame's sync code?
+ if ((firstBytesInPage[0] == 0xFF) && (firstBytesInPage[1] & 0xFC) == 0xF8) { /* <-- Does the page begin with a frame's sync code? */
runningGranulePosition = oggbs->currentPageHeader.granulePosition*pFlac->channels;
}
@@ -4326,11 +5956,12 @@ drflac_bool32 drflac_ogg__seek_to_sample(drflac* pFlac, drflac_uint64 sampleInde
}
}
-
- // We found the page that that is closest to the sample, so now we need to find it. The first thing to do is seek to the
- // start of that page. In the loop above we checked that it was a fresh page which means this page is also the start of
- // a new frame. This property means that after we've seeked to the page we can immediately start looping over frames until
- // we find the one containing the target sample.
+ /*
+ We found the page that that is closest to the sample, so now we need to find it. The first thing to do is seek to the
+ start of that page. In the loop above we checked that it was a fresh page which means this page is also the start of
+ a new frame. This property means that after we've seeked to the page we can immediately start looping over frames until
+ we find the one containing the target sample.
+ */
if (!drflac_oggbs__seek_physical(oggbs, runningFrameBytePos, drflac_seek_origin_start)) {
return DRFLAC_FALSE;
}
@@ -4338,66 +5969,75 @@ drflac_bool32 drflac_ogg__seek_to_sample(drflac* pFlac, drflac_uint64 sampleInde
return DRFLAC_FALSE;
}
-
- // At this point we'll be sitting on the first byte of the frame header of the first frame in the page. We just keep
- // looping over these frames until we find the one containing the sample we're after.
- drflac_uint64 runningSampleCount = runningGranulePosition;
+ /*
+ At this point we'll be sitting on the first byte of the frame header of the first frame in the page. We just keep
+ looping over these frames until we find the one containing the sample we're after.
+ */
+ runningSampleCount = runningGranulePosition;
for (;;) {
- // There are two ways to find the sample and seek past irrelevant frames:
- // 1) Use the native FLAC decoder.
- // 2) Use Ogg's framing system.
- //
- // Both of these options have their own pros and cons. Using the native FLAC decoder is slower because it needs to
- // do a full decode of the frame. Using Ogg's framing system is faster, but more complicated and involves some code
- // duplication for the decoding of frame headers.
- //
- // Another thing to consider is that using the Ogg framing system will perform direct seeking of the physical Ogg
- // bitstream. This is important to consider because it means we cannot read data from the drflac_bs object using the
- // standard drflac__*() APIs because that will read in extra data for its own internal caching which in turn breaks
- // the positioning of the read pointer of the physical Ogg bitstream. Therefore, anything that would normally be read
- // using the native FLAC decoding APIs, such as drflac__read_next_frame_header(), need to be re-implemented so as to
- // avoid the use of the drflac_bs object.
- //
- // Considering these issues, I have decided to use the slower native FLAC decoding method for the following reasons:
- // 1) Seeking is already partially accelerated using Ogg's paging system in the code block above.
- // 2) Seeking in an Ogg encapsulated FLAC stream is probably quite uncommon.
- // 3) Simplicity.
- if (!drflac__read_next_frame_header(&pFlac->bs, pFlac->bitsPerSample, &pFlac->currentFrame.header)) {
+ /*
+ There are two ways to find the sample and seek past irrelevant frames:
+ 1) Use the native FLAC decoder.
+ 2) Use Ogg's framing system.
+
+ Both of these options have their own pros and cons. Using the native FLAC decoder is slower because it needs to
+ do a full decode of the frame. Using Ogg's framing system is faster, but more complicated and involves some code
+ duplication for the decoding of frame headers.
+
+ Another thing to consider is that using the Ogg framing system will perform direct seeking of the physical Ogg
+ bitstream. This is important to consider because it means we cannot read data from the drflac_bs object using the
+ standard drflac__*() APIs because that will read in extra data for its own internal caching which in turn breaks
+ the positioning of the read pointer of the physical Ogg bitstream. Therefore, anything that would normally be read
+ using the native FLAC decoding APIs, such as drflac__read_next_flac_frame_header(), need to be re-implemented so as to
+ avoid the use of the drflac_bs object.
+
+ Considering these issues, I have decided to use the slower native FLAC decoding method for the following reasons:
+ 1) Seeking is already partially accelerated using Ogg's paging system in the code block above.
+ 2) Seeking in an Ogg encapsulated FLAC stream is probably quite uncommon.
+ 3) Simplicity.
+ */
+ drflac_uint64 firstSampleInFrame = 0;
+ drflac_uint64 lastSampleInFrame = 0;
+ drflac_uint64 sampleCountInThisFrame;
+
+ if (!drflac__read_next_flac_frame_header(&pFlac->bs, pFlac->bitsPerSample, &pFlac->currentFrame.header)) {
return DRFLAC_FALSE;
}
- drflac_uint64 firstSampleInFrame = 0;
- drflac_uint64 lastSampleInFrame = 0;
drflac__get_current_frame_sample_range(pFlac, &firstSampleInFrame, &lastSampleInFrame);
- drflac_uint64 sampleCountInThisFrame = (lastSampleInFrame - firstSampleInFrame) + 1;
+ sampleCountInThisFrame = (lastSampleInFrame - firstSampleInFrame) + 1;
if (sampleIndex < (runningSampleCount + sampleCountInThisFrame)) {
- // The sample should be in this frame. We need to fully decode it, however if it's an invalid frame (a CRC mismatch), we need to pretend
- // it never existed and keep iterating.
- drflac_result result = drflac__decode_frame(pFlac);
+ /*
+ The sample should be in this frame. We need to fully decode it, however if it's an invalid frame (a CRC mismatch), we need to pretend
+ it never existed and keep iterating.
+ */
+ drflac_result result = drflac__decode_flac_frame(pFlac);
if (result == DRFLAC_SUCCESS) {
- // The frame is valid. We just need to skip over some samples to ensure it's sample-exact.
- drflac_uint64 samplesToDecode = (size_t)(sampleIndex - runningSampleCount); // <-- Safe cast because the maximum number of samples in a frame is 65535.
+ /* The frame is valid. We just need to skip over some samples to ensure it's sample-exact. */
+ drflac_uint64 samplesToDecode = (size_t)(sampleIndex - runningSampleCount); /* <-- Safe cast because the maximum number of samples in a frame is 65535. */
if (samplesToDecode == 0) {
return DRFLAC_TRUE;
}
- return drflac_read_s32(pFlac, samplesToDecode, NULL) != 0; // <-- If this fails, something bad has happened (it should never fail).
+ return drflac__seek_forward_by_samples(pFlac, samplesToDecode) == samplesToDecode; /* <-- If this fails, something bad has happened (it should never fail). */
} else {
if (result == DRFLAC_CRC_MISMATCH) {
- continue; // CRC mismatch. Pretend this frame never existed.
+ continue; /* CRC mismatch. Pretend this frame never existed. */
} else {
return DRFLAC_FALSE;
}
}
} else {
- // It's not in this frame. We need to seek past the frame, but check if there was a CRC mismatch. If so, we pretend this
- // frame never existed and leave the running sample count untouched.
- drflac_result result = drflac__seek_to_next_frame(pFlac);
+ /*
+ It's not in this frame. We need to seek past the frame, but check if there was a CRC mismatch. If so, we pretend this
+ frame never existed and leave the running sample count untouched.
+ */
+ drflac_result result = drflac__seek_to_next_flac_frame(pFlac);
if (result == DRFLAC_SUCCESS) {
runningSampleCount += sampleCountInThisFrame;
} else {
if (result == DRFLAC_CRC_MISMATCH) {
- continue; // CRC mismatch. Pretend this frame never existed.
+ continue; /* CRC mismatch. Pretend this frame never existed. */
} else {
return DRFLAC_FALSE;
}
@@ -4409,45 +6049,48 @@ drflac_bool32 drflac_ogg__seek_to_sample(drflac* pFlac, drflac_uint64 sampleInde
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)
{
- // Pre: The bit stream should be sitting just past the 4-byte OggS capture pattern.
+ drflac_ogg_page_header header;
+ drflac_uint32 crc32 = DRFLAC_OGG_CAPTURE_PATTERN_CRC32;
+ drflac_uint32 bytesRead = 0;
+
+ /* Pre Condition: The bit stream should be sitting just past the 4-byte OggS capture pattern. */
(void)relaxed;
pInit->container = drflac_container_ogg;
pInit->oggFirstBytePos = 0;
- // We'll get here if the first 4 bytes of the stream were the OggS capture pattern, however it doesn't necessarily mean the
- // stream includes FLAC encoded audio. To check for this we need to scan the beginning-of-stream page markers and check if
- // any match the FLAC specification. Important to keep in mind that the stream may be multiplexed.
- drflac_ogg_page_header header;
-
- drflac_uint32 crc32 = DRFLAC_OGG_CAPTURE_PATTERN_CRC32;
- drflac_uint32 bytesRead = 0;
+ /*
+ We'll get here if the first 4 bytes of the stream were the OggS capture pattern, however it doesn't necessarily mean the
+ stream includes FLAC encoded audio. To check for this we need to scan the beginning-of-stream page markers and check if
+ any match the FLAC specification. Important to keep in mind that the stream may be multiplexed.
+ */
if (drflac_ogg__read_page_header_after_capture_pattern(onRead, pUserData, &header, &bytesRead, &crc32) != DRFLAC_SUCCESS) {
return DRFLAC_FALSE;
}
pInit->runningFilePos += bytesRead;
for (;;) {
- // Break if we're past the beginning of stream page.
+ int pageBodySize;
+
+ /* Break if we're past the beginning of stream page. */
if ((header.headerType & 0x02) == 0) {
return DRFLAC_FALSE;
}
-
- // Check if it's a FLAC header.
- int pageBodySize = drflac_ogg__get_page_body_size(&header);
- if (pageBodySize == 51) { // 51 = the lacing value of the FLAC header packet.
- // It could be a FLAC page...
+ /* Check if it's a FLAC header. */
+ pageBodySize = drflac_ogg__get_page_body_size(&header);
+ if (pageBodySize == 51) { /* 51 = the lacing value of the FLAC header packet. */
+ /* It could be a FLAC page... */
drflac_uint32 bytesRemainingInPage = pageBodySize;
-
drflac_uint8 packetType;
+
if (onRead(pUserData, &packetType, 1) != 1) {
return DRFLAC_FALSE;
}
bytesRemainingInPage -= 1;
if (packetType == 0x7F) {
- // Increasingly more likely to be a FLAC page...
+ /* Increasingly more likely to be a FLAC page... */
drflac_uint8 sig[4];
if (onRead(pUserData, sig, 4) != 4) {
return DRFLAC_FALSE;
@@ -4455,29 +6098,32 @@ drflac_bool32 drflac__init_private__ogg(drflac_init_info* pInit, drflac_read_pro
bytesRemainingInPage -= 4;
if (sig[0] == 'F' && sig[1] == 'L' && sig[2] == 'A' && sig[3] == 'C') {
- // Almost certainly a FLAC page...
+ /* Almost certainly a FLAC page... */
drflac_uint8 mappingVersion[2];
if (onRead(pUserData, mappingVersion, 2) != 2) {
return DRFLAC_FALSE;
}
if (mappingVersion[0] != 1) {
- return DRFLAC_FALSE; // Only supporting version 1.x of the Ogg mapping.
+ return DRFLAC_FALSE; /* Only supporting version 1.x of the Ogg mapping. */
}
- // The next 2 bytes are the non-audio packets, not including this one. We don't care about this because we're going to
- // be handling it in a generic way based on the serial number and packet types.
+ /*
+ The next 2 bytes are the non-audio packets, not including this one. We don't care about this because we're going to
+ be handling it in a generic way based on the serial number and packet types.
+ */
if (!onSeek(pUserData, 2, drflac_seek_origin_current)) {
return DRFLAC_FALSE;
}
- // Expecting the native FLAC signature "fLaC".
+ /* Expecting the native FLAC signature "fLaC". */
if (onRead(pUserData, sig, 4) != 4) {
return DRFLAC_FALSE;
}
if (sig[0] == 'f' && sig[1] == 'L' && sig[2] == 'a' && sig[3] == 'C') {
- // The remaining data in the page should be the STREAMINFO block.
+ /* The remaining data in the page should be the STREAMINFO block. */
+ drflac_streaminfo streaminfo;
drflac_uint8 isLastBlock;
drflac_uint8 blockType;
drflac_uint32 blockSize;
@@ -4486,12 +6132,11 @@ drflac_bool32 drflac__init_private__ogg(drflac_init_info* pInit, drflac_read_pro
}
if (blockType != DRFLAC_METADATA_BLOCK_TYPE_STREAMINFO || blockSize != 34) {
- return DRFLAC_FALSE; // Invalid block type. First block must be the STREAMINFO block.
+ return DRFLAC_FALSE; /* Invalid block type. First block must be the STREAMINFO block. */
}
- drflac_streaminfo streaminfo;
if (drflac__read_streaminfo(onRead, pUserData, &streaminfo)) {
- // Success!
+ /* Success! */
pInit->hasStreamInfoBlock = DRFLAC_TRUE;
pInit->sampleRate = streaminfo.sampleRate;
pInit->channels = streaminfo.channels;
@@ -4510,26 +6155,26 @@ drflac_bool32 drflac__init_private__ogg(drflac_init_info* pInit, drflac_read_pro
}
pInit->runningFilePos += pageBodySize;
- pInit->oggFirstBytePos = pInit->runningFilePos - 79; // Subtracting 79 will place us right on top of the "OggS" identifier of the FLAC bos page.
+ pInit->oggFirstBytePos = pInit->runningFilePos - 79; /* Subtracting 79 will place us right on top of the "OggS" identifier of the FLAC bos page. */
pInit->oggSerial = header.serialNumber;
pInit->oggBosHeader = header;
break;
} else {
- // Failed to read STREAMINFO block. Aww, so close...
+ /* Failed to read STREAMINFO block. Aww, so close... */
return DRFLAC_FALSE;
}
} else {
- // Invalid file.
+ /* Invalid file. */
return DRFLAC_FALSE;
}
} else {
- // Not a FLAC header. Skip it.
+ /* Not a FLAC header. Skip it. */
if (!onSeek(pUserData, bytesRemainingInPage, drflac_seek_origin_current)) {
return DRFLAC_FALSE;
}
}
} else {
- // Not a FLAC header. Seek past the entire page and move on to the next.
+ /* Not a FLAC header. Seek past the entire page and move on to the next. */
if (!onSeek(pUserData, bytesRemainingInPage, drflac_seek_origin_current)) {
return DRFLAC_FALSE;
}
@@ -4543,24 +6188,28 @@ drflac_bool32 drflac__init_private__ogg(drflac_init_info* pInit, drflac_read_pro
pInit->runningFilePos += pageBodySize;
- // Read the header of the next page.
+ /* Read the header of the next page. */
if (drflac_ogg__read_page_header(onRead, pUserData, &header, &bytesRead, &crc32) != DRFLAC_SUCCESS) {
return DRFLAC_FALSE;
}
pInit->runningFilePos += bytesRead;
}
-
- // If we get here it means we found a FLAC audio stream. We should be sitting on the first byte of the header of the next page. The next
- // packets in the FLAC logical stream contain the metadata. The only thing left to do in the initialization phase for Ogg is to create the
- // Ogg bistream object.
- pInit->hasMetadataBlocks = DRFLAC_TRUE; // <-- Always have at least VORBIS_COMMENT metadata block.
+ /*
+ If we get here it means we found a FLAC audio stream. We should be sitting on the first byte of the header of the next page. The next
+ packets in the FLAC logical stream contain the metadata. The only thing left to do in the initialization phase for Ogg is to create the
+ Ogg bistream object.
+ */
+ pInit->hasMetadataBlocks = DRFLAC_TRUE; /* <-- Always have at least VORBIS_COMMENT metadata block. */
return DRFLAC_TRUE;
}
#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)
{
+ drflac_bool32 relaxed;
+ drflac_uint8 id[4];
+
if (pInit == NULL || onRead == NULL || onSeek == NULL) {
return DRFLAC_FALSE;
}
@@ -4579,27 +6228,28 @@ drflac_bool32 drflac__init_private(drflac_init_info* pInit, drflac_read_proc onR
drflac__reset_cache(&pInit->bs);
- // If the container is explicitly defined then we can try opening in relaxed mode.
- drflac_bool32 relaxed = container != drflac_container_unknown;
-
- drflac_uint8 id[4];
+ /* If the container is explicitly defined then we can try opening in relaxed mode. */
+ relaxed = container != drflac_container_unknown;
- // Skip over any ID3 tags.
+ /* Skip over any ID3 tags. */
for (;;) {
if (onRead(pUserData, id, 4) != 4) {
- return DRFLAC_FALSE; // Ran out of data.
+ return DRFLAC_FALSE; /* Ran out of data. */
}
pInit->runningFilePos += 4;
if (id[0] == 'I' && id[1] == 'D' && id[2] == '3') {
drflac_uint8 header[6];
+ drflac_uint8 flags;
+ drflac_uint32 headerSize;
+
if (onRead(pUserData, header, 6) != 6) {
- return DRFLAC_FALSE; // Ran out of data.
+ return DRFLAC_FALSE; /* Ran out of data. */
}
pInit->runningFilePos += 6;
- drflac_uint8 flags = header[1];
- drflac_uint32 headerSize;
+ flags = header[1];
+
drflac_copy_memory(&headerSize, header+2, 4);
headerSize = drflac__unsynchsafe_32(drflac__be2host_32(headerSize));
if (flags & 0x10) {
@@ -4607,7 +6257,7 @@ drflac_bool32 drflac__init_private(drflac_init_info* pInit, drflac_read_proc onR
}
if (!onSeek(pUserData, headerSize, drflac_seek_origin_current)) {
- return DRFLAC_FALSE; // Failed to seek past the tag.
+ return DRFLAC_FALSE; /* Failed to seek past the tag. */
}
pInit->runningFilePos += headerSize;
} else {
@@ -4624,7 +6274,7 @@ drflac_bool32 drflac__init_private(drflac_init_info* pInit, drflac_read_proc onR
}
#endif
- // If we get here it means we likely don't have a header. Try opening in relaxed mode, if applicable.
+ /* If we get here it means we likely don't have a header. Try opening in relaxed mode, if applicable. */
if (relaxed) {
if (container == drflac_container_native) {
return drflac__init_private__native(pInit, onRead, onSeek, onMeta, pUserData, pUserDataMD, relaxed);
@@ -4636,7 +6286,7 @@ drflac_bool32 drflac__init_private(drflac_init_info* pInit, drflac_read_proc onR
#endif
}
- // Unsupported container.
+ /* Unsupported container. */
return DRFLAC_FALSE;
}
@@ -4646,61 +6296,76 @@ void drflac__init_from_info(drflac* pFlac, drflac_init_info* pInit)
drflac_assert(pInit != NULL);
drflac_zero_memory(pFlac, sizeof(*pFlac));
- pFlac->bs = pInit->bs;
- pFlac->onMeta = pInit->onMeta;
- pFlac->pUserDataMD = pInit->pUserDataMD;
- pFlac->maxBlockSize = pInit->maxBlockSize;
- pFlac->sampleRate = pInit->sampleRate;
- pFlac->channels = (drflac_uint8)pInit->channels;
- pFlac->bitsPerSample = (drflac_uint8)pInit->bitsPerSample;
- pFlac->totalSampleCount = pInit->totalSampleCount;
- pFlac->container = pInit->container;
+ pFlac->bs = pInit->bs;
+ pFlac->onMeta = pInit->onMeta;
+ pFlac->pUserDataMD = pInit->pUserDataMD;
+ pFlac->maxBlockSize = pInit->maxBlockSize;
+ pFlac->sampleRate = pInit->sampleRate;
+ pFlac->channels = (drflac_uint8)pInit->channels;
+ pFlac->bitsPerSample = (drflac_uint8)pInit->bitsPerSample;
+ pFlac->totalSampleCount = pInit->totalSampleCount;
+ pFlac->totalPCMFrameCount = pInit->totalSampleCount / pFlac->channels;
+ pFlac->container = pInit->container;
}
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)
{
+ drflac_init_info init;
+ drflac_uint32 allocationSize;
+ drflac_uint32 wholeSIMDVectorCountPerChannel;
+ drflac_uint32 decodedSamplesAllocationSize;
+#ifndef DR_FLAC_NO_OGG
+ drflac_uint32 oggbsAllocationSize;
+ drflac_oggbs oggbs;
+#endif
+ drflac_uint64 firstFramePos;
+ drflac_uint64 seektablePos;
+ drflac_uint32 seektableSize;
+ drflac* pFlac;
+
#ifndef DRFLAC_NO_CPUID
- // CPU support first.
+ /* CPU support first. */
drflac__init_cpu_caps();
#endif
- drflac_init_info init;
if (!drflac__init_private(&init, onRead, onSeek, onMeta, container, pUserData, pUserDataMD)) {
return NULL;
}
- // The size of the allocation for the drflac object needs to be large enough to fit the following:
- // 1) The main members of the drflac structure
- // 2) A block of memory large enough to store the decoded samples of the largest frame in the stream
- // 3) If the container is Ogg, a drflac_oggbs object
- //
- // The complicated part of the allocation is making sure there's enough room the decoded samples, taking into consideration
- // the different SIMD instruction sets.
- drflac_uint32 allocationSize = sizeof(drflac);
-
- // The allocation size for decoded frames depends on the number of 32-bit integers that fit inside the largest SIMD vector
- // we are supporting.
- drflac_uint32 wholeSIMDVectorCountPerChannel;
- if ((init.maxBlockSize % (DRFLAC_MAX_SIMD_VECTOR_SIZE / sizeof(drflac_int32))) == 0) {
- wholeSIMDVectorCountPerChannel = (init.maxBlockSize / (DRFLAC_MAX_SIMD_VECTOR_SIZE / sizeof(drflac_int32)));
+ /*
+ The size of the allocation for the drflac object needs to be large enough to fit the following:
+ 1) The main members of the drflac structure
+ 2) A block of memory large enough to store the decoded samples of the largest frame in the stream
+ 3) If the container is Ogg, a drflac_oggbs object
+
+ The complicated part of the allocation is making sure there's enough room the decoded samples, taking into consideration
+ the different SIMD instruction sets.
+ */
+ allocationSize = sizeof(drflac);
+
+ /*
+ The allocation size for decoded frames depends on the number of 32-bit integers that fit inside the largest SIMD vector
+ we are supporting.
+ */
+ if (((init.maxBlockSize+DRFLAC_LEADING_SAMPLES) % (DRFLAC_MAX_SIMD_VECTOR_SIZE / sizeof(drflac_int32))) == 0) {
+ wholeSIMDVectorCountPerChannel = ((init.maxBlockSize+DRFLAC_LEADING_SAMPLES) / (DRFLAC_MAX_SIMD_VECTOR_SIZE / sizeof(drflac_int32)));
} else {
- wholeSIMDVectorCountPerChannel = (init.maxBlockSize / (DRFLAC_MAX_SIMD_VECTOR_SIZE / sizeof(drflac_int32))) + 1;
+ wholeSIMDVectorCountPerChannel = ((init.maxBlockSize+DRFLAC_LEADING_SAMPLES) / (DRFLAC_MAX_SIMD_VECTOR_SIZE / sizeof(drflac_int32))) + 1;
}
- drflac_uint32 decodedSamplesAllocationSize = wholeSIMDVectorCountPerChannel * DRFLAC_MAX_SIMD_VECTOR_SIZE * init.channels;
+ decodedSamplesAllocationSize = wholeSIMDVectorCountPerChannel * DRFLAC_MAX_SIMD_VECTOR_SIZE * init.channels;
allocationSize += decodedSamplesAllocationSize;
- allocationSize += DRFLAC_MAX_SIMD_VECTOR_SIZE; // Allocate extra bytes to ensure we have enough for alignment.
+ allocationSize += DRFLAC_MAX_SIMD_VECTOR_SIZE; /* Allocate extra bytes to ensure we have enough for alignment. */
#ifndef DR_FLAC_NO_OGG
- // There's additional data required for Ogg streams.
- drflac_uint32 oggbsAllocationSize = 0;
+ /* There's additional data required for Ogg streams. */
+ oggbsAllocationSize = 0;
if (init.container == drflac_container_ogg) {
oggbsAllocationSize = sizeof(drflac_oggbs);
allocationSize += oggbsAllocationSize;
}
- drflac_oggbs oggbs;
drflac_zero_memory(&oggbs, sizeof(oggbs));
if (init.container == drflac_container_ogg) {
oggbs.onRead = onRead;
@@ -4714,12 +6379,14 @@ drflac* drflac_open_with_metadata_private(drflac_read_proc onRead, drflac_seek_p
}
#endif
- // This part is a bit awkward. We need to load the seektable so that it can be referenced in-memory, but I want the drflac object to
- // consist of only a single heap allocation. To this, the size of the seek table needs to be known, which we determine when reading
- // and decoding the metadata.
- drflac_uint64 firstFramePos = 42; // <-- We know we are at byte 42 at this point.
- drflac_uint64 seektablePos = 0;
- drflac_uint32 seektableSize = 0;
+ /*
+ This part is a bit awkward. We need to load the seektable so that it can be referenced in-memory, but I want the drflac object to
+ consist of only a single heap allocation. To this, the size of the seek table needs to be known, which we determine when reading
+ and decoding the metadata.
+ */
+ firstFramePos = 42; /* <-- We know we are at byte 42 at this point. */
+ seektablePos = 0;
+ seektableSize = 0;
if (init.hasMetadataBlocks) {
drflac_read_proc onReadOverride = onRead;
drflac_seek_proc onSeekOverride = onSeek;
@@ -4741,7 +6408,7 @@ drflac* drflac_open_with_metadata_private(drflac_read_proc onRead, drflac_seek_p
}
- drflac* pFlac = (drflac*)DRFLAC_MALLOC(allocationSize);
+ pFlac = (drflac*)DRFLAC_MALLOC(allocationSize);
drflac__init_from_info(pFlac, &init);
pFlac->pDecodedSamples = (drflac_int32*)drflac_align((size_t)pFlac->pExtraData, DRFLAC_MAX_SIMD_VECTOR_SIZE);
@@ -4750,7 +6417,7 @@ drflac* drflac_open_with_metadata_private(drflac_read_proc onRead, drflac_seek_p
drflac_oggbs* pInternalOggbs = (drflac_oggbs*)((drflac_uint8*)pFlac->pDecodedSamples + decodedSamplesAllocationSize + seektableSize);
*pInternalOggbs = oggbs;
- // The Ogg bistream needs to be layered on top of the original bitstream.
+ /* The Ogg bistream needs to be layered on top of the original bitstream. */
pFlac->bs.onRead = drflac__on_read_ogg;
pFlac->bs.onSeek = drflac__on_seek_ogg;
pFlac->bs.pUserData = (void*)pInternalOggbs;
@@ -4760,7 +6427,7 @@ drflac* drflac_open_with_metadata_private(drflac_read_proc onRead, drflac_seek_p
pFlac->firstFramePos = firstFramePos;
- // NOTE: Seektables are not currently compatible with Ogg encapsulation (Ogg has its own accelerated seeking system). I may change this later, so I'm leaving this here for now.
+ /* NOTE: Seektables are not currently compatible with Ogg encapsulation (Ogg has its own accelerated seeking system). I may change this later, so I'm leaving this here for now. */
#ifndef DR_FLAC_NO_OGG
if (init.container == drflac_container_ogg)
{
@@ -4770,32 +6437,34 @@ drflac* drflac_open_with_metadata_private(drflac_read_proc onRead, drflac_seek_p
else
#endif
{
- // If we have a seektable we need to load it now, making sure we move back to where we were previously.
+ /* If we have a seektable we need to load it now, making sure we move back to where we were previously. */
if (seektablePos != 0) {
pFlac->seekpointCount = seektableSize / sizeof(*pFlac->pSeekpoints);
pFlac->pSeekpoints = (drflac_seekpoint*)((drflac_uint8*)pFlac->pDecodedSamples + decodedSamplesAllocationSize);
- // Seek to the seektable, then just read directly into our seektable buffer.
+ /* Seek to the seektable, then just read directly into our seektable buffer. */
if (pFlac->bs.onSeek(pFlac->bs.pUserData, (int)seektablePos, drflac_seek_origin_start)) {
if (pFlac->bs.onRead(pFlac->bs.pUserData, pFlac->pSeekpoints, seektableSize) == seektableSize) {
- // Endian swap.
- for (drflac_uint32 iSeekpoint = 0; iSeekpoint < pFlac->seekpointCount; ++iSeekpoint) {
+ /* Endian swap. */
+ drflac_uint32 iSeekpoint;
+ for (iSeekpoint = 0; iSeekpoint < pFlac->seekpointCount; ++iSeekpoint) {
pFlac->pSeekpoints[iSeekpoint].firstSample = drflac__be2host_64(pFlac->pSeekpoints[iSeekpoint].firstSample);
pFlac->pSeekpoints[iSeekpoint].frameOffset = drflac__be2host_64(pFlac->pSeekpoints[iSeekpoint].frameOffset);
pFlac->pSeekpoints[iSeekpoint].sampleCount = drflac__be2host_16(pFlac->pSeekpoints[iSeekpoint].sampleCount);
}
} else {
- // Failed to read the seektable. Pretend we don't have one.
+ /* Failed to read the seektable. Pretend we don't have one. */
pFlac->pSeekpoints = NULL;
pFlac->seekpointCount = 0;
}
- // We need to seek back to where we were. If this fails it's a critical error.
+ /* We need to seek back to where we were. If this fails it's a critical error. */
if (!pFlac->bs.onSeek(pFlac->bs.pUserData, (int)pFlac->firstFramePos, drflac_seek_origin_start)) {
+ DRFLAC_FREE(pFlac);
return NULL;
}
} else {
- // Failed to seek to the seektable. Ominous sign, but for now we can just pretend we don't have one.
+ /* Failed to seek to the seektable. Ominous sign, but for now we can just pretend we don't have one. */
pFlac->pSeekpoints = NULL;
pFlac->seekpointCount = 0;
}
@@ -4803,19 +6472,20 @@ drflac* drflac_open_with_metadata_private(drflac_read_proc onRead, drflac_seek_p
}
-
- // If we get here, but don't have a STREAMINFO block, it means we've opened the stream in relaxed mode and need to decode
- // the first frame.
+ /*
+ If we get here, but don't have a STREAMINFO block, it means we've opened the stream in relaxed mode and need to decode
+ the first frame.
+ */
if (!init.hasStreamInfoBlock) {
pFlac->currentFrame.header = init.firstFrameHeader;
do
{
- drflac_result result = drflac__decode_frame(pFlac);
+ drflac_result result = drflac__decode_flac_frame(pFlac);
if (result == DRFLAC_SUCCESS) {
break;
} else {
if (result == DRFLAC_CRC_MISMATCH) {
- if (!drflac__read_next_frame_header(&pFlac->bs, pFlac->bitsPerSample, &pFlac->currentFrame.header)) {
+ if (!drflac__read_next_flac_frame_header(&pFlac->bs, pFlac->bitsPerSample, &pFlac->currentFrame.header)) {
DRFLAC_FREE(pFlac);
return NULL;
}
@@ -4834,9 +6504,6 @@ drflac* drflac_open_with_metadata_private(drflac_read_proc onRead, drflac_seek_p
#ifndef DR_FLAC_NO_STDIO
-typedef void* drflac_file;
-
-#if defined(DR_FLAC_NO_WIN32_IO) || !defined(_WIN32)
#include <stdio.h>
static size_t drflac__on_read_stdio(void* pUserData, void* bufferOut, size_t bytesToRead)
@@ -4846,15 +6513,15 @@ static size_t drflac__on_read_stdio(void* pUserData, void* bufferOut, size_t byt
static drflac_bool32 drflac__on_seek_stdio(void* pUserData, int offset, drflac_seek_origin origin)
{
- drflac_assert(offset > 0 || (offset == 0 && origin == drflac_seek_origin_start));
+ drflac_assert(offset >= 0); /* <-- Never seek backwards. */
return fseek((FILE*)pUserData, offset, (origin == drflac_seek_origin_current) ? SEEK_CUR : SEEK_SET) == 0;
}
-static drflac_file drflac__open_file_handle(const char* filename)
+static FILE* drflac__fopen(const char* filename)
{
FILE* pFile;
-#ifdef _MSC_VER
+#if defined(_MSC_VER) && _MSC_VER >= 1400
if (fopen_s(&pFile, filename, "rb") != 0) {
return NULL;
}
@@ -4865,65 +6532,23 @@ static drflac_file drflac__open_file_handle(const char* filename)
}
#endif
- return (drflac_file)pFile;
-}
-
-static void drflac__close_file_handle(drflac_file file)
-{
- fclose((FILE*)file);
-}
-#else
-#include <windows.h>
-
-// This doesn't seem to be defined for VC6.
-#ifndef INVALID_SET_FILE_POINTER
-#define INVALID_SET_FILE_POINTER ((DWORD)-1)
-#endif
-
-static size_t drflac__on_read_stdio(void* pUserData, void* bufferOut, size_t bytesToRead)
-{
- drflac_assert(bytesToRead < 0xFFFFFFFF); // dr_flac will never request huge amounts of data at a time. This is a safe assertion.
-
- DWORD bytesRead;
- ReadFile((HANDLE)pUserData, bufferOut, (DWORD)bytesToRead, &bytesRead, NULL);
-
- return (size_t)bytesRead;
-}
-
-static drflac_bool32 drflac__on_seek_stdio(void* pUserData, int offset, drflac_seek_origin origin)
-{
- drflac_assert(offset > 0 || (offset == 0 && origin == drflac_seek_origin_start));
-
- return SetFilePointer((HANDLE)pUserData, offset, NULL, (origin == drflac_seek_origin_current) ? FILE_CURRENT : FILE_BEGIN) != INVALID_SET_FILE_POINTER;
+ return pFile;
}
-static drflac_file drflac__open_file_handle(const char* filename)
-{
- HANDLE hFile = CreateFileA(filename, FILE_GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
- if (hFile == INVALID_HANDLE_VALUE) {
- return NULL;
- }
-
- return (drflac_file)hFile;
-}
-
-static void drflac__close_file_handle(drflac_file file)
-{
- CloseHandle((HANDLE)file);
-}
-#endif
-
drflac* drflac_open_file(const char* filename)
{
- drflac_file file = drflac__open_file_handle(filename);
- if (file == NULL) {
+ drflac* pFlac;
+ FILE* pFile;
+
+ pFile = drflac__fopen(filename);
+ if (pFile == NULL) {
return NULL;
}
- drflac* pFlac = drflac_open(drflac__on_read_stdio, drflac__on_seek_stdio, (void*)file);
+ pFlac = drflac_open(drflac__on_read_stdio, drflac__on_seek_stdio, (void*)pFile);
if (pFlac == NULL) {
- drflac__close_file_handle(file);
+ fclose(pFile);
return NULL;
}
@@ -4932,28 +6557,33 @@ drflac* drflac_open_file(const char* filename)
drflac* drflac_open_file_with_metadata(const char* filename, drflac_meta_proc onMeta, void* pUserData)
{
- drflac_file file = drflac__open_file_handle(filename);
- if (file == NULL) {
+ drflac* pFlac;
+ FILE* pFile;
+
+ pFile = drflac__fopen(filename);
+ if (pFile == NULL) {
return NULL;
}
- drflac* pFlac = drflac_open_with_metadata_private(drflac__on_read_stdio, drflac__on_seek_stdio, onMeta, drflac_container_unknown, (void*)file, pUserData);
+ pFlac = drflac_open_with_metadata_private(drflac__on_read_stdio, drflac__on_seek_stdio, onMeta, drflac_container_unknown, (void*)pFile, pUserData);
if (pFlac == NULL) {
- drflac__close_file_handle(file);
+ fclose(pFile);
return pFlac;
}
return pFlac;
}
-#endif //DR_FLAC_NO_STDIO
+#endif /* DR_FLAC_NO_STDIO */
static size_t drflac__on_read_memory(void* pUserData, void* bufferOut, size_t bytesToRead)
{
drflac__memory_stream* memoryStream = (drflac__memory_stream*)pUserData;
+ size_t bytesRemaining;
+
drflac_assert(memoryStream != NULL);
drflac_assert(memoryStream->dataSize >= memoryStream->currentReadPos);
- size_t bytesRemaining = memoryStream->dataSize - memoryStream->currentReadPos;
+ bytesRemaining = memoryStream->dataSize - memoryStream->currentReadPos;
if (bytesToRead > bytesRemaining) {
bytesToRead = bytesRemaining;
}
@@ -4969,21 +6599,25 @@ static size_t drflac__on_read_memory(void* pUserData, void* bufferOut, size_t by
static drflac_bool32 drflac__on_seek_memory(void* pUserData, int offset, drflac_seek_origin origin)
{
drflac__memory_stream* memoryStream = (drflac__memory_stream*)pUserData;
+
drflac_assert(memoryStream != NULL);
- drflac_assert(offset > 0 || (offset == 0 && origin == drflac_seek_origin_start));
- drflac_assert(offset <= (drflac_int64)memoryStream->dataSize);
+ drflac_assert(offset >= 0); /* <-- Never seek backwards. */
+
+ if (offset > (drflac_int64)memoryStream->dataSize) {
+ return DRFLAC_FALSE;
+ }
if (origin == drflac_seek_origin_current) {
if (memoryStream->currentReadPos + offset <= memoryStream->dataSize) {
memoryStream->currentReadPos += offset;
} else {
- memoryStream->currentReadPos = memoryStream->dataSize; // Trying to seek too far forward.
+ return DRFLAC_FALSE; /* Trying to seek too far forward. */
}
} else {
if ((drflac_uint32)offset <= memoryStream->dataSize) {
memoryStream->currentReadPos = offset;
} else {
- memoryStream->currentReadPos = memoryStream->dataSize; // Trying to seek too far forward.
+ return DRFLAC_FALSE; /* Trying to seek too far forward. */
}
}
@@ -4993,17 +6627,19 @@ static drflac_bool32 drflac__on_seek_memory(void* pUserData, int offset, drflac_
drflac* drflac_open_memory(const void* data, size_t dataSize)
{
drflac__memory_stream memoryStream;
+ drflac* pFlac;
+
memoryStream.data = (const unsigned char*)data;
memoryStream.dataSize = dataSize;
memoryStream.currentReadPos = 0;
- drflac* pFlac = drflac_open(drflac__on_read_memory, drflac__on_seek_memory, &memoryStream);
+ pFlac = drflac_open(drflac__on_read_memory, drflac__on_seek_memory, &memoryStream);
if (pFlac == NULL) {
return NULL;
}
pFlac->memoryStream = memoryStream;
- // This is an awful hack...
+ /* This is an awful hack... */
#ifndef DR_FLAC_NO_OGG
if (pFlac->container == drflac_container_ogg)
{
@@ -5022,17 +6658,19 @@ drflac* drflac_open_memory(const void* data, size_t dataSize)
drflac* drflac_open_memory_with_metadata(const void* data, size_t dataSize, drflac_meta_proc onMeta, void* pUserData)
{
drflac__memory_stream memoryStream;
+ drflac* pFlac;
+
memoryStream.data = (const unsigned char*)data;
memoryStream.dataSize = dataSize;
memoryStream.currentReadPos = 0;
- drflac* pFlac = drflac_open_with_metadata_private(drflac__on_read_memory, drflac__on_seek_memory, onMeta, drflac_container_unknown, &memoryStream, pUserData);
+ pFlac = drflac_open_with_metadata_private(drflac__on_read_memory, drflac__on_seek_memory, onMeta, drflac_container_unknown, &memoryStream, pUserData);
if (pFlac == NULL) {
return NULL;
}
pFlac->memoryStream = memoryStream;
- // This is an awful hack...
+ /* This is an awful hack... */
#ifndef DR_FLAC_NO_OGG
if (pFlac->container == drflac_container_ogg)
{
@@ -5075,19 +6713,22 @@ void drflac_close(drflac* pFlac)
}
#ifndef DR_FLAC_NO_STDIO
- // If we opened the file with drflac_open_file() we will want to close the file handle. We can know whether or not drflac_open_file()
- // was used by looking at the callbacks.
+ /*
+ If we opened the file with drflac_open_file() we will want to close the file handle. We can know whether or not drflac_open_file()
+ was used by looking at the callbacks.
+ */
if (pFlac->bs.onRead == drflac__on_read_stdio) {
- drflac__close_file_handle((drflac_file)pFlac->bs.pUserData);
+ fclose((FILE*)pFlac->bs.pUserData);
}
#ifndef DR_FLAC_NO_OGG
- // Need to clean up Ogg streams a bit differently due to the way the bit streaming is chained.
+ /* Need to clean up Ogg streams a bit differently due to the way the bit streaming is chained. */
if (pFlac->container == drflac_container_ogg) {
- drflac_assert(pFlac->bs.onRead == drflac__on_read_ogg);
drflac_oggbs* oggbs = (drflac_oggbs*)pFlac->_oggbs;
+ drflac_assert(pFlac->bs.onRead == drflac__on_read_ogg);
+
if (oggbs->onRead == drflac__on_read_stdio) {
- drflac__close_file_handle((drflac_file)oggbs->pUserData);
+ fclose((FILE*)oggbs->pUserData);
}
}
#endif
@@ -5099,30 +6740,29 @@ void drflac_close(drflac* pFlac)
drflac_uint64 drflac__read_s32__misaligned(drflac* pFlac, drflac_uint64 samplesToRead, drflac_int32* bufferOut)
{
unsigned int channelCount = drflac__get_channel_count_from_channel_assignment(pFlac->currentFrame.header.channelAssignment);
+ drflac_uint64 samplesRead;
- // We should never be calling this when the number of samples to read is >= the sample count.
+ /* We should never be calling this when the number of samples to read is >= the sample count. */
drflac_assert(samplesToRead < channelCount);
drflac_assert(pFlac->currentFrame.samplesRemaining > 0 && samplesToRead <= pFlac->currentFrame.samplesRemaining);
-
- drflac_uint64 samplesRead = 0;
+ samplesRead = 0;
while (samplesToRead > 0) {
drflac_uint64 totalSamplesInFrame = pFlac->currentFrame.header.blockSize * channelCount;
drflac_uint64 samplesReadFromFrameSoFar = totalSamplesInFrame - pFlac->currentFrame.samplesRemaining;
drflac_uint64 channelIndex = samplesReadFromFrameSoFar % channelCount;
-
drflac_uint64 nextSampleInFrame = samplesReadFromFrameSoFar / channelCount;
-
int decodedSample = 0;
+
switch (pFlac->currentFrame.header.channelAssignment)
{
case DRFLAC_CHANNEL_ASSIGNMENT_LEFT_SIDE:
{
if (channelIndex == 0) {
- decodedSample = pFlac->currentFrame.subframes[channelIndex].pDecodedSamples[nextSampleInFrame];
+ decodedSample = pFlac->currentFrame.subframes[channelIndex + 0].pDecodedSamples[nextSampleInFrame] << pFlac->currentFrame.subframes[channelIndex + 0].wastedBitsPerSample;
} else {
- int side = pFlac->currentFrame.subframes[channelIndex + 0].pDecodedSamples[nextSampleInFrame];
- int left = pFlac->currentFrame.subframes[channelIndex - 1].pDecodedSamples[nextSampleInFrame];
+ int side = pFlac->currentFrame.subframes[channelIndex + 0].pDecodedSamples[nextSampleInFrame] << pFlac->currentFrame.subframes[channelIndex + 0].wastedBitsPerSample;
+ int left = pFlac->currentFrame.subframes[channelIndex - 1].pDecodedSamples[nextSampleInFrame] << pFlac->currentFrame.subframes[channelIndex - 1].wastedBitsPerSample;
decodedSample = left - side;
}
} break;
@@ -5130,11 +6770,11 @@ drflac_uint64 drflac__read_s32__misaligned(drflac* pFlac, drflac_uint64 samplesT
case DRFLAC_CHANNEL_ASSIGNMENT_RIGHT_SIDE:
{
if (channelIndex == 0) {
- int side = pFlac->currentFrame.subframes[channelIndex + 0].pDecodedSamples[nextSampleInFrame];
- int right = pFlac->currentFrame.subframes[channelIndex + 1].pDecodedSamples[nextSampleInFrame];
+ int side = pFlac->currentFrame.subframes[channelIndex + 0].pDecodedSamples[nextSampleInFrame] << pFlac->currentFrame.subframes[channelIndex + 0].wastedBitsPerSample;
+ int right = pFlac->currentFrame.subframes[channelIndex + 1].pDecodedSamples[nextSampleInFrame] << pFlac->currentFrame.subframes[channelIndex + 1].wastedBitsPerSample;
decodedSample = side + right;
} else {
- decodedSample = pFlac->currentFrame.subframes[channelIndex].pDecodedSamples[nextSampleInFrame];
+ decodedSample = pFlac->currentFrame.subframes[channelIndex + 0].pDecodedSamples[nextSampleInFrame] << pFlac->currentFrame.subframes[channelIndex + 0].wastedBitsPerSample;
}
} break;
@@ -5143,14 +6783,14 @@ drflac_uint64 drflac__read_s32__misaligned(drflac* pFlac, drflac_uint64 samplesT
int mid;
int side;
if (channelIndex == 0) {
- mid = pFlac->currentFrame.subframes[channelIndex + 0].pDecodedSamples[nextSampleInFrame];
- side = pFlac->currentFrame.subframes[channelIndex + 1].pDecodedSamples[nextSampleInFrame];
+ mid = pFlac->currentFrame.subframes[channelIndex + 0].pDecodedSamples[nextSampleInFrame] << pFlac->currentFrame.subframes[channelIndex + 0].wastedBitsPerSample;
+ side = pFlac->currentFrame.subframes[channelIndex + 1].pDecodedSamples[nextSampleInFrame] << pFlac->currentFrame.subframes[channelIndex + 1].wastedBitsPerSample;
mid = (((unsigned int)mid) << 1) | (side & 0x01);
decodedSample = (mid + side) >> 1;
} else {
- mid = pFlac->currentFrame.subframes[channelIndex - 1].pDecodedSamples[nextSampleInFrame];
- side = pFlac->currentFrame.subframes[channelIndex + 0].pDecodedSamples[nextSampleInFrame];
+ mid = pFlac->currentFrame.subframes[channelIndex - 1].pDecodedSamples[nextSampleInFrame] << pFlac->currentFrame.subframes[channelIndex - 1].wastedBitsPerSample;
+ side = pFlac->currentFrame.subframes[channelIndex + 0].pDecodedSamples[nextSampleInFrame] << pFlac->currentFrame.subframes[channelIndex + 0].wastedBitsPerSample;
mid = (((unsigned int)mid) << 1) | (side & 0x01);
decodedSample = (mid - side) >> 1;
@@ -5160,12 +6800,11 @@ drflac_uint64 drflac__read_s32__misaligned(drflac* pFlac, drflac_uint64 samplesT
case DRFLAC_CHANNEL_ASSIGNMENT_INDEPENDENT:
default:
{
- decodedSample = pFlac->currentFrame.subframes[channelIndex].pDecodedSamples[nextSampleInFrame];
+ decodedSample = pFlac->currentFrame.subframes[channelIndex + 0].pDecodedSamples[nextSampleInFrame] << pFlac->currentFrame.subframes[channelIndex + 0].wastedBitsPerSample;
} break;
}
-
- decodedSample <<= ((32 - pFlac->bitsPerSample) + pFlac->currentFrame.subframes[channelIndex].wastedBitsPerSample);
+ decodedSample <<= (32 - pFlac->bitsPerSample);
if (bufferOut) {
*bufferOut++ = decodedSample;
@@ -5179,34 +6818,11 @@ drflac_uint64 drflac__read_s32__misaligned(drflac* pFlac, drflac_uint64 samplesT
return samplesRead;
}
-drflac_uint64 drflac__seek_forward_by_samples(drflac* pFlac, drflac_uint64 samplesToRead)
-{
- drflac_uint64 samplesRead = 0;
- while (samplesToRead > 0) {
- if (pFlac->currentFrame.samplesRemaining == 0) {
- if (!drflac__read_and_decode_next_frame(pFlac)) {
- break; // Couldn't read the next frame, so just break from the loop and return.
- }
- } else {
- if (pFlac->currentFrame.samplesRemaining > samplesToRead) {
- samplesRead += samplesToRead;
- pFlac->currentFrame.samplesRemaining -= (drflac_uint32)samplesToRead; // <-- Safe cast. Will always be < currentFrame.samplesRemaining < 65536.
- samplesToRead = 0;
- } else {
- samplesRead += pFlac->currentFrame.samplesRemaining;
- samplesToRead -= pFlac->currentFrame.samplesRemaining;
- pFlac->currentFrame.samplesRemaining = 0;
- }
- }
- }
-
- pFlac->currentSample += samplesRead;
- return samplesRead;
-}
-
drflac_uint64 drflac_read_s32(drflac* pFlac, drflac_uint64 samplesToRead, drflac_int32* bufferOut)
{
- // Note that <bufferOut> is allowed to be null, in which case this will act like a seek.
+ drflac_uint64 samplesRead;
+
+ /* Note that <bufferOut> is allowed to be null, in which case this will act like a seek. */
if (pFlac == NULL || samplesToRead == 0) {
return 0;
}
@@ -5215,22 +6831,24 @@ drflac_uint64 drflac_read_s32(drflac* pFlac, drflac_uint64 samplesToRead, drflac
return drflac__seek_forward_by_samples(pFlac, samplesToRead);
}
-
- drflac_uint64 samplesRead = 0;
+ samplesRead = 0;
while (samplesToRead > 0) {
- // If we've run out of samples in this frame, go to the next.
+ /* If we've run out of samples in this frame, go to the next. */
if (pFlac->currentFrame.samplesRemaining == 0) {
- if (!drflac__read_and_decode_next_frame(pFlac)) {
- break; // Couldn't read the next frame, so just break from the loop and return.
+ if (!drflac__read_and_decode_next_flac_frame(pFlac)) {
+ break; /* Couldn't read the next frame, so just break from the loop and return. */
}
} else {
- // Here is where we grab the samples and interleave them.
-
+ /* Here is where we grab the samples and interleave them. */
unsigned int channelCount = drflac__get_channel_count_from_channel_assignment(pFlac->currentFrame.header.channelAssignment);
drflac_uint64 totalSamplesInFrame = pFlac->currentFrame.header.blockSize * channelCount;
drflac_uint64 samplesReadFromFrameSoFar = totalSamplesInFrame - pFlac->currentFrame.samplesRemaining;
-
drflac_uint64 misalignedSampleCount = samplesReadFromFrameSoFar % channelCount;
+ drflac_uint64 alignedSampleCountPerChannel;
+ drflac_uint64 firstAlignedSampleInFrame;
+ unsigned int unusedBitsPerSample;
+ drflac_uint64 alignedSamplesRead;
+
if (misalignedSampleCount > 0) {
drflac_uint64 misalignedSamplesRead = drflac__read_s32__misaligned(pFlac, misalignedSampleCount, bufferOut);
samplesRead += misalignedSamplesRead;
@@ -5241,79 +6859,87 @@ drflac_uint64 drflac_read_s32(drflac* pFlac, drflac_uint64 samplesToRead, drflac
}
- drflac_uint64 alignedSampleCountPerChannel = samplesToRead / channelCount;
+ alignedSampleCountPerChannel = samplesToRead / channelCount;
if (alignedSampleCountPerChannel > pFlac->currentFrame.samplesRemaining / channelCount) {
alignedSampleCountPerChannel = pFlac->currentFrame.samplesRemaining / channelCount;
}
- drflac_uint64 firstAlignedSampleInFrame = samplesReadFromFrameSoFar / channelCount;
- unsigned int unusedBitsPerSample = 32 - pFlac->bitsPerSample;
+ firstAlignedSampleInFrame = samplesReadFromFrameSoFar / channelCount;
+ unusedBitsPerSample = 32 - pFlac->bitsPerSample;
switch (pFlac->currentFrame.header.channelAssignment)
{
case DRFLAC_CHANNEL_ASSIGNMENT_LEFT_SIDE:
{
+ drflac_uint64 i;
const drflac_int32* pDecodedSamples0 = pFlac->currentFrame.subframes[0].pDecodedSamples + firstAlignedSampleInFrame;
const drflac_int32* pDecodedSamples1 = pFlac->currentFrame.subframes[1].pDecodedSamples + firstAlignedSampleInFrame;
- for (drflac_uint64 i = 0; i < alignedSampleCountPerChannel; ++i) {
- int left = pDecodedSamples0[i];
- int side = pDecodedSamples1[i];
+ for (i = 0; i < alignedSampleCountPerChannel; ++i) {
+ int left = pDecodedSamples0[i] << (unusedBitsPerSample + pFlac->currentFrame.subframes[0].wastedBitsPerSample);
+ int side = pDecodedSamples1[i] << (unusedBitsPerSample + pFlac->currentFrame.subframes[1].wastedBitsPerSample);
int right = left - side;
- bufferOut[i*2+0] = left << (unusedBitsPerSample + pFlac->currentFrame.subframes[0].wastedBitsPerSample);
- bufferOut[i*2+1] = right << (unusedBitsPerSample + pFlac->currentFrame.subframes[1].wastedBitsPerSample);
+ bufferOut[i*2+0] = left;
+ bufferOut[i*2+1] = right;
}
} break;
case DRFLAC_CHANNEL_ASSIGNMENT_RIGHT_SIDE:
{
+ drflac_uint64 i;
const drflac_int32* pDecodedSamples0 = pFlac->currentFrame.subframes[0].pDecodedSamples + firstAlignedSampleInFrame;
const drflac_int32* pDecodedSamples1 = pFlac->currentFrame.subframes[1].pDecodedSamples + firstAlignedSampleInFrame;
- for (drflac_uint64 i = 0; i < alignedSampleCountPerChannel; ++i) {
- int side = pDecodedSamples0[i];
- int right = pDecodedSamples1[i];
+ for (i = 0; i < alignedSampleCountPerChannel; ++i) {
+ int side = pDecodedSamples0[i] << (unusedBitsPerSample + pFlac->currentFrame.subframes[0].wastedBitsPerSample);
+ int right = pDecodedSamples1[i] << (unusedBitsPerSample + pFlac->currentFrame.subframes[1].wastedBitsPerSample);
int left = right + side;
- bufferOut[i*2+0] = left << (unusedBitsPerSample + pFlac->currentFrame.subframes[0].wastedBitsPerSample);
- bufferOut[i*2+1] = right << (unusedBitsPerSample + pFlac->currentFrame.subframes[1].wastedBitsPerSample);
+ bufferOut[i*2+0] = left;
+ bufferOut[i*2+1] = right;
}
} break;
case DRFLAC_CHANNEL_ASSIGNMENT_MID_SIDE:
{
+ drflac_uint64 i;
const drflac_int32* pDecodedSamples0 = pFlac->currentFrame.subframes[0].pDecodedSamples + firstAlignedSampleInFrame;
const drflac_int32* pDecodedSamples1 = pFlac->currentFrame.subframes[1].pDecodedSamples + firstAlignedSampleInFrame;
- for (drflac_uint64 i = 0; i < alignedSampleCountPerChannel; ++i) {
- int side = pDecodedSamples1[i];
- int mid = (((drflac_uint32)pDecodedSamples0[i]) << 1) | (side & 0x01);
+ for (i = 0; i < alignedSampleCountPerChannel; ++i) {
+ int mid = pDecodedSamples0[i] << pFlac->currentFrame.subframes[0].wastedBitsPerSample;
+ int side = pDecodedSamples1[i] << pFlac->currentFrame.subframes[1].wastedBitsPerSample;
+
+ mid = (((drflac_uint32)mid) << 1) | (side & 0x01);
- bufferOut[i*2+0] = ((mid + side) >> 1) << (unusedBitsPerSample + pFlac->currentFrame.subframes[0].wastedBitsPerSample);
- bufferOut[i*2+1] = ((mid - side) >> 1) << (unusedBitsPerSample + pFlac->currentFrame.subframes[1].wastedBitsPerSample);
+ bufferOut[i*2+0] = ((mid + side) >> 1) << (unusedBitsPerSample);
+ bufferOut[i*2+1] = ((mid - side) >> 1) << (unusedBitsPerSample);
}
} break;
case DRFLAC_CHANNEL_ASSIGNMENT_INDEPENDENT:
default:
{
- if (pFlac->currentFrame.header.channelAssignment == 1) // 1 = Stereo
+ if (pFlac->currentFrame.header.channelAssignment == 1) /* 1 = Stereo */
{
- // Stereo optimized inner loop unroll.
+ /* Stereo optimized inner loop unroll. */
+ drflac_uint64 i;
const drflac_int32* pDecodedSamples0 = pFlac->currentFrame.subframes[0].pDecodedSamples + firstAlignedSampleInFrame;
const drflac_int32* pDecodedSamples1 = pFlac->currentFrame.subframes[1].pDecodedSamples + firstAlignedSampleInFrame;
- for (drflac_uint64 i = 0; i < alignedSampleCountPerChannel; ++i) {
+ for (i = 0; i < alignedSampleCountPerChannel; ++i) {
bufferOut[i*2+0] = pDecodedSamples0[i] << (unusedBitsPerSample + pFlac->currentFrame.subframes[0].wastedBitsPerSample);
bufferOut[i*2+1] = pDecodedSamples1[i] << (unusedBitsPerSample + pFlac->currentFrame.subframes[1].wastedBitsPerSample);
}
}
else
{
- // Generic interleaving.
- for (drflac_uint64 i = 0; i < alignedSampleCountPerChannel; ++i) {
- for (unsigned int j = 0; j < channelCount; ++j) {
+ /* Generic interleaving. */
+ drflac_uint64 i;
+ for (i = 0; i < alignedSampleCountPerChannel; ++i) {
+ unsigned int j;
+ for (j = 0; j < channelCount; ++j) {
bufferOut[(i*channelCount)+j] = (pFlac->currentFrame.subframes[j].pDecodedSamples[firstAlignedSampleInFrame + i]) << (unusedBitsPerSample + pFlac->currentFrame.subframes[j].wastedBitsPerSample);
}
}
@@ -5321,7 +6947,7 @@ drflac_uint64 drflac_read_s32(drflac* pFlac, drflac_uint64 samplesToRead, drflac
} break;
}
- drflac_uint64 alignedSamplesRead = alignedSampleCountPerChannel * channelCount;
+ alignedSamplesRead = alignedSampleCountPerChannel * channelCount;
samplesRead += alignedSamplesRead;
samplesReadFromFrameSoFar += alignedSamplesRead;
bufferOut += alignedSamplesRead;
@@ -5330,7 +6956,7 @@ drflac_uint64 drflac_read_s32(drflac* pFlac, drflac_uint64 samplesToRead, drflac
pFlac->currentFrame.samplesRemaining -= (unsigned int)alignedSamplesRead;
- // At this point we may still have some excess samples left to read.
+ /* At this point we may still have some excess samples left to read. */
if (samplesToRead > 0 && pFlac->currentFrame.samplesRemaining > 0) {
drflac_uint64 excessSamplesRead = 0;
if (samplesToRead < pFlac->currentFrame.samplesRemaining) {
@@ -5351,20 +6977,47 @@ drflac_uint64 drflac_read_s32(drflac* pFlac, drflac_uint64 samplesToRead, drflac
return samplesRead;
}
+drflac_uint64 drflac_read_pcm_frames_s32(drflac* pFlac, drflac_uint64 framesToRead, drflac_int32* pBufferOut)
+{
+#if defined(_MSC_VER) && !defined(__clang__)
+ #pragma warning(push)
+ #pragma warning(disable:4996) /* was declared deprecated */
+#elif defined(__GNUC__) || defined(__clang__)
+ #pragma GCC diagnostic push
+ #pragma GCC diagnostic ignored "-Wdeprecated-declarations"
+#endif
+ return drflac_read_s32(pFlac, framesToRead*pFlac->channels, pBufferOut) / pFlac->channels;
+#if defined(_MSC_VER) && !defined(__clang__)
+ #pragma warning(pop)
+#elif defined(__GNUC__) || defined(__clang__)
+ #pragma GCC diagnostic pop
+#endif
+}
+
+
drflac_uint64 drflac_read_s16(drflac* pFlac, drflac_uint64 samplesToRead, drflac_int16* pBufferOut)
{
- // This reads samples in 2 passes and can probably be optimized.
+ /* This reads samples in 2 passes and can probably be optimized. */
drflac_uint64 totalSamplesRead = 0;
+#if defined(_MSC_VER) && !defined(__clang__)
+ #pragma warning(push)
+ #pragma warning(disable:4996) /* was declared deprecated */
+#elif defined(__GNUC__) || defined(__clang__)
+ #pragma GCC diagnostic push
+ #pragma GCC diagnostic ignored "-Wdeprecated-declarations"
+#endif
+
while (samplesToRead > 0) {
+ drflac_uint64 i;
drflac_int32 samples32[4096];
drflac_uint64 samplesJustRead = drflac_read_s32(pFlac, (samplesToRead > 4096) ? 4096 : samplesToRead, samples32);
if (samplesJustRead == 0) {
- break; // Reached the end.
+ break; /* Reached the end. */
}
- // s32 -> s16
- for (drflac_uint64 i = 0; i < samplesJustRead; ++i) {
+ /* s32 -> s16 */
+ for (i = 0; i < samplesJustRead; ++i) {
pBufferOut[i] = (drflac_int16)(samples32[i] >> 16);
}
@@ -5373,23 +7026,69 @@ drflac_uint64 drflac_read_s16(drflac* pFlac, drflac_uint64 samplesToRead, drflac
pBufferOut += samplesJustRead;
}
+#if defined(_MSC_VER) && !defined(__clang__)
+ #pragma warning(pop)
+#elif defined(__GNUC__) || defined(__clang__)
+ #pragma GCC diagnostic pop
+#endif
+
return totalSamplesRead;
}
+drflac_uint64 drflac_read_pcm_frames_s16(drflac* pFlac, drflac_uint64 framesToRead, drflac_int16* pBufferOut)
+{
+ /* This reads samples in 2 passes and can probably be optimized. */
+ drflac_uint64 totalPCMFramesRead = 0;
+
+ while (framesToRead > 0) {
+ drflac_uint64 iFrame;
+ drflac_int32 samples32[4096];
+ drflac_uint64 framesJustRead = drflac_read_pcm_frames_s32(pFlac, (framesToRead > 4096/pFlac->channels) ? 4096/pFlac->channels : framesToRead, samples32);
+ if (framesJustRead == 0) {
+ break; /* Reached the end. */
+ }
+
+ /* s32 -> s16 */
+ for (iFrame = 0; iFrame < framesJustRead; ++iFrame) {
+ drflac_uint32 iChannel;
+ for (iChannel = 0; iChannel < pFlac->channels; ++iChannel) {
+ drflac_uint64 iSample = iFrame*pFlac->channels + iChannel;
+ pBufferOut[iSample] = (drflac_int16)(samples32[iSample] >> 16);
+ }
+ }
+
+ totalPCMFramesRead += framesJustRead;
+ framesToRead -= framesJustRead;
+ pBufferOut += framesJustRead * pFlac->channels;
+ }
+
+ return totalPCMFramesRead;
+}
+
+
drflac_uint64 drflac_read_f32(drflac* pFlac, drflac_uint64 samplesToRead, float* pBufferOut)
{
- // This reads samples in 2 passes and can probably be optimized.
+ /* This reads samples in 2 passes and can probably be optimized. */
drflac_uint64 totalSamplesRead = 0;
+#if defined(_MSC_VER) && !defined(__clang__)
+ #pragma warning(push)
+ #pragma warning(disable:4996) /* was declared deprecated */
+#elif defined(__GNUC__) || defined(__clang__)
+ #pragma GCC diagnostic push
+ #pragma GCC diagnostic ignored "-Wdeprecated-declarations"
+#endif
+
while (samplesToRead > 0) {
+ drflac_uint64 i;
drflac_int32 samples32[4096];
drflac_uint64 samplesJustRead = drflac_read_s32(pFlac, (samplesToRead > 4096) ? 4096 : samplesToRead, samples32);
if (samplesJustRead == 0) {
- break; // Reached the end.
+ break; /* Reached the end. */
}
- // s32 -> f32
- for (drflac_uint64 i = 0; i < samplesJustRead; ++i) {
+ /* s32 -> f32 */
+ for (i = 0; i < samplesJustRead; ++i) {
pBufferOut[i] = (float)(samples32[i] / 2147483648.0);
}
@@ -5398,17 +7097,717 @@ drflac_uint64 drflac_read_f32(drflac* pFlac, drflac_uint64 samplesToRead, float*
pBufferOut += samplesJustRead;
}
+#if defined(_MSC_VER) && !defined(__clang__)
+ #pragma warning(pop)
+#elif defined(__GNUC__) || defined(__clang__)
+ #pragma GCC diagnostic pop
+#endif
+
return totalSamplesRead;
}
+#if 0
+static DRFLAC_INLINE void drflac_read_pcm_frames_f32__decode_left_side__reference(drflac* pFlac, drflac_uint64 frameCount, drflac_int32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, float* pOutputSamples)
+{
+ drflac_uint64 i;
+ for (i = 0; i < frameCount; ++i) {
+ int left = pInputSamples0[i] << (unusedBitsPerSample + pFlac->currentFrame.subframes[0].wastedBitsPerSample);
+ int side = pInputSamples1[i] << (unusedBitsPerSample + pFlac->currentFrame.subframes[1].wastedBitsPerSample);
+ int right = left - side;
+
+ pOutputSamples[i*2+0] = (float)(left / 2147483648.0);
+ pOutputSamples[i*2+1] = (float)(right / 2147483648.0);
+ }
+}
+#endif
+
+static DRFLAC_INLINE void drflac_read_pcm_frames_f32__decode_left_side__scalar(drflac* pFlac, drflac_uint64 frameCount, drflac_int32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, float* pOutputSamples)
+{
+ drflac_uint64 i;
+ drflac_uint64 frameCount4 = frameCount >> 2;
+
+ float factor = 1 / 2147483648.0;
+
+ drflac_int32 shift0 = unusedBitsPerSample + pFlac->currentFrame.subframes[0].wastedBitsPerSample;
+ drflac_int32 shift1 = unusedBitsPerSample + pFlac->currentFrame.subframes[1].wastedBitsPerSample;
+ for (i = 0; i < frameCount4; ++i) {
+ drflac_int32 left0 = pInputSamples0[i*4+0] << shift0;
+ drflac_int32 left1 = pInputSamples0[i*4+1] << shift0;
+ drflac_int32 left2 = pInputSamples0[i*4+2] << shift0;
+ drflac_int32 left3 = pInputSamples0[i*4+3] << shift0;
+
+ drflac_int32 side0 = pInputSamples1[i*4+0] << shift1;
+ drflac_int32 side1 = pInputSamples1[i*4+1] << shift1;
+ drflac_int32 side2 = pInputSamples1[i*4+2] << shift1;
+ drflac_int32 side3 = pInputSamples1[i*4+3] << shift1;
+
+ drflac_int32 right0 = left0 - side0;
+ drflac_int32 right1 = left1 - side1;
+ drflac_int32 right2 = left2 - side2;
+ drflac_int32 right3 = left3 - side3;
+
+ pOutputSamples[i*8+0] = left0 * factor;
+ pOutputSamples[i*8+1] = right0 * factor;
+ pOutputSamples[i*8+2] = left1 * factor;
+ pOutputSamples[i*8+3] = right1 * factor;
+ pOutputSamples[i*8+4] = left2 * factor;
+ pOutputSamples[i*8+5] = right2 * factor;
+ pOutputSamples[i*8+6] = left3 * factor;
+ pOutputSamples[i*8+7] = right3 * factor;
+ }
+
+ for (i = (frameCount4 << 2); i < frameCount; ++i) {
+ int left = pInputSamples0[i] << shift0;
+ int side = pInputSamples1[i] << shift1;
+ int right = left - side;
+
+ pOutputSamples[i*2+0] = (float)(left * factor);
+ pOutputSamples[i*2+1] = (float)(right * factor);
+ }
+}
+
+#if defined(DRFLAC_SUPPORT_SSE2)
+static DRFLAC_INLINE void drflac_read_pcm_frames_f32__decode_left_side__sse2(drflac* pFlac, drflac_uint64 frameCount, drflac_int32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, float* pOutputSamples)
+{
+ drflac_uint64 frameCount4;
+ __m128 factor;
+ int shift0;
+ int shift1;
+ drflac_uint64 i;
+
+ drflac_assert(pFlac->bitsPerSample <= 24);
+
+ frameCount4 = frameCount >> 2;
+
+ factor = _mm_set1_ps(1.0f / 8388608.0f);
+ shift0 = (unusedBitsPerSample + pFlac->currentFrame.subframes[0].wastedBitsPerSample) - 8;
+ shift1 = (unusedBitsPerSample + pFlac->currentFrame.subframes[1].wastedBitsPerSample) - 8;
+
+ for (i = 0; i < frameCount4; ++i) {
+ __m128i inputSample0 = _mm_loadu_si128((const __m128i*)pInputSamples0 + i);
+ __m128i inputSample1 = _mm_loadu_si128((const __m128i*)pInputSamples1 + i);
+
+ __m128i left = _mm_slli_epi32(inputSample0, shift0);
+ __m128i side = _mm_slli_epi32(inputSample1, shift1);
+ __m128i right = _mm_sub_epi32(left, side);
+ __m128 leftf = _mm_mul_ps(_mm_cvtepi32_ps(left), factor);
+ __m128 rightf = _mm_mul_ps(_mm_cvtepi32_ps(right), factor);
+
+ pOutputSamples[i*8+0] = ((float*)&leftf)[0];
+ pOutputSamples[i*8+1] = ((float*)&rightf)[0];
+ pOutputSamples[i*8+2] = ((float*)&leftf)[1];
+ pOutputSamples[i*8+3] = ((float*)&rightf)[1];
+ pOutputSamples[i*8+4] = ((float*)&leftf)[2];
+ pOutputSamples[i*8+5] = ((float*)&rightf)[2];
+ pOutputSamples[i*8+6] = ((float*)&leftf)[3];
+ pOutputSamples[i*8+7] = ((float*)&rightf)[3];
+ }
+
+ for (i = (frameCount4 << 2); i < frameCount; ++i) {
+ int left = pInputSamples0[i] << shift0;
+ int side = pInputSamples1[i] << shift1;
+ int right = left - side;
+
+ pOutputSamples[i*2+0] = (float)(left / 8388608.0f);
+ pOutputSamples[i*2+1] = (float)(right / 8388608.0f);
+ }
+}
+#endif
+
+static DRFLAC_INLINE void drflac_read_pcm_frames_f32__decode_left_side(drflac* pFlac, drflac_uint64 frameCount, drflac_int32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, float* pOutputSamples)
+{
+#if defined(DRFLAC_SUPPORT_SSE2)
+ if (drflac__gIsSSE2Supported && pFlac->bitsPerSample <= 24) {
+ drflac_read_pcm_frames_f32__decode_left_side__sse2(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);
+ } else
+#endif
+ {
+ /* Scalar fallback. */
+#if 0
+ drflac_read_pcm_frames_f32__decode_left_side__reference(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);
+#else
+ drflac_read_pcm_frames_f32__decode_left_side__scalar(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);
+#endif
+ }
+}
+
+
+#if 0
+static DRFLAC_INLINE void drflac_read_pcm_frames_f32__decode_right_side__reference(drflac* pFlac, drflac_uint64 frameCount, drflac_int32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, float* pOutputSamples)
+{
+ drflac_uint64 i;
+ for (i = 0; i < frameCount; ++i) {
+ int side = pInputSamples0[i] << (unusedBitsPerSample + pFlac->currentFrame.subframes[0].wastedBitsPerSample);
+ int right = pInputSamples1[i] << (unusedBitsPerSample + pFlac->currentFrame.subframes[1].wastedBitsPerSample);
+ int left = right + side;
+
+ pOutputSamples[i*2+0] = (float)(left / 2147483648.0);
+ pOutputSamples[i*2+1] = (float)(right / 2147483648.0);
+ }
+}
+#endif
+
+static DRFLAC_INLINE void drflac_read_pcm_frames_f32__decode_right_side__scalar(drflac* pFlac, drflac_uint64 frameCount, drflac_int32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, float* pOutputSamples)
+{
+ drflac_uint64 i;
+ drflac_uint64 frameCount4 = frameCount >> 2;
+
+ float factor = 1 / 2147483648.0;
+
+ drflac_int32 shift0 = unusedBitsPerSample + pFlac->currentFrame.subframes[0].wastedBitsPerSample;
+ drflac_int32 shift1 = unusedBitsPerSample + pFlac->currentFrame.subframes[1].wastedBitsPerSample;
+ for (i = 0; i < frameCount4; ++i) {
+ drflac_int32 side0 = pInputSamples0[i*4+0] << shift0;
+ drflac_int32 side1 = pInputSamples0[i*4+1] << shift0;
+ drflac_int32 side2 = pInputSamples0[i*4+2] << shift0;
+ drflac_int32 side3 = pInputSamples0[i*4+3] << shift0;
+
+ drflac_int32 right0 = pInputSamples1[i*4+0] << shift1;
+ drflac_int32 right1 = pInputSamples1[i*4+1] << shift1;
+ drflac_int32 right2 = pInputSamples1[i*4+2] << shift1;
+ drflac_int32 right3 = pInputSamples1[i*4+3] << shift1;
+
+ drflac_int32 left0 = right0 + side0;
+ drflac_int32 left1 = right1 + side1;
+ drflac_int32 left2 = right2 + side2;
+ drflac_int32 left3 = right3 + side3;
+
+ pOutputSamples[i*8+0] = left0 * factor;
+ pOutputSamples[i*8+1] = right0 * factor;
+ pOutputSamples[i*8+2] = left1 * factor;
+ pOutputSamples[i*8+3] = right1 * factor;
+ pOutputSamples[i*8+4] = left2 * factor;
+ pOutputSamples[i*8+5] = right2 * factor;
+ pOutputSamples[i*8+6] = left3 * factor;
+ pOutputSamples[i*8+7] = right3 * factor;
+ }
+
+ for (i = (frameCount4 << 2); i < frameCount; ++i) {
+ int side = pInputSamples0[i] << shift0;
+ int right = pInputSamples1[i] << shift1;
+ int left = right + side;
+
+ pOutputSamples[i*2+0] = (float)(left * factor);
+ pOutputSamples[i*2+1] = (float)(right * factor);
+ }
+}
+
+#if defined(DRFLAC_SUPPORT_SSE2)
+static DRFLAC_INLINE void drflac_read_pcm_frames_f32__decode_right_side__sse2(drflac* pFlac, drflac_uint64 frameCount, drflac_int32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, float* pOutputSamples)
+{
+ drflac_uint64 frameCount4;
+ __m128 factor;
+ int shift0;
+ int shift1;
+ drflac_uint64 i;
+
+ drflac_assert(pFlac->bitsPerSample <= 24);
+
+ frameCount4 = frameCount >> 2;
+
+ factor = _mm_set1_ps(1.0f / 8388608.0f);
+ shift0 = (unusedBitsPerSample + pFlac->currentFrame.subframes[0].wastedBitsPerSample) - 8;
+ shift1 = (unusedBitsPerSample + pFlac->currentFrame.subframes[1].wastedBitsPerSample) - 8;
+
+ for (i = 0; i < frameCount4; ++i) {
+ __m128i inputSample0 = _mm_loadu_si128((const __m128i*)pInputSamples0 + i);
+ __m128i inputSample1 = _mm_loadu_si128((const __m128i*)pInputSamples1 + i);
+
+ __m128i side = _mm_slli_epi32(inputSample0, shift0);
+ __m128i right = _mm_slli_epi32(inputSample1, shift1);
+ __m128i left = _mm_add_epi32(right, side);
+ __m128 leftf = _mm_mul_ps(_mm_cvtepi32_ps(left), factor);
+ __m128 rightf = _mm_mul_ps(_mm_cvtepi32_ps(right), factor);
+
+ pOutputSamples[i*8+0] = ((float*)&leftf)[0];
+ pOutputSamples[i*8+1] = ((float*)&rightf)[0];
+ pOutputSamples[i*8+2] = ((float*)&leftf)[1];
+ pOutputSamples[i*8+3] = ((float*)&rightf)[1];
+ pOutputSamples[i*8+4] = ((float*)&leftf)[2];
+ pOutputSamples[i*8+5] = ((float*)&rightf)[2];
+ pOutputSamples[i*8+6] = ((float*)&leftf)[3];
+ pOutputSamples[i*8+7] = ((float*)&rightf)[3];
+ }
+
+ for (i = (frameCount4 << 2); i < frameCount; ++i) {
+ int side = pInputSamples0[i] << shift0;
+ int right = pInputSamples1[i] << shift1;
+ int left = right + side;
+
+ pOutputSamples[i*2+0] = (float)(left / 8388608.0f);
+ pOutputSamples[i*2+1] = (float)(right / 8388608.0f);
+ }
+}
+#endif
+
+static DRFLAC_INLINE void drflac_read_pcm_frames_f32__decode_right_side(drflac* pFlac, drflac_uint64 frameCount, drflac_int32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, float* pOutputSamples)
+{
+#if defined(DRFLAC_SUPPORT_SSE2)
+ if (drflac__gIsSSE2Supported && pFlac->bitsPerSample <= 24) {
+ drflac_read_pcm_frames_f32__decode_right_side__sse2(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);
+ } else
+#endif
+ {
+ /* Scalar fallback. */
+#if 0
+ drflac_read_pcm_frames_f32__decode_right_side__reference(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);
+#else
+ drflac_read_pcm_frames_f32__decode_right_side__scalar(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);
+#endif
+ }
+}
+
+
+#if 0
+static DRFLAC_INLINE void drflac_read_pcm_frames_f32__decode_mid_side__reference(drflac* pFlac, drflac_uint64 frameCount, drflac_int32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, float* pOutputSamples)
+{
+ for (drflac_uint64 i = 0; i < frameCount; ++i) {
+ int mid = pInputSamples0[i] << pFlac->currentFrame.subframes[0].wastedBitsPerSample;
+ int side = pInputSamples1[i] << pFlac->currentFrame.subframes[1].wastedBitsPerSample;
+
+ mid = (((drflac_uint32)mid) << 1) | (side & 0x01);
+
+ pOutputSamples[i*2+0] = (float)((((mid + side) >> 1) << (unusedBitsPerSample)) / 2147483648.0);
+ pOutputSamples[i*2+1] = (float)((((mid - side) >> 1) << (unusedBitsPerSample)) / 2147483648.0);
+ }
+}
+#endif
+
+static DRFLAC_INLINE void drflac_read_pcm_frames_f32__decode_mid_side__scalar(drflac* pFlac, drflac_uint64 frameCount, drflac_int32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, float* pOutputSamples)
+{
+ drflac_uint64 i;
+ drflac_uint64 frameCount4 = frameCount >> 2;
+
+ float factor = 1 / 2147483648.0;
+
+ int shift = unusedBitsPerSample;
+ if (shift > 0) {
+ shift -= 1;
+ for (i = 0; i < frameCount4; ++i) {
+ int temp0L;
+ int temp1L;
+ int temp2L;
+ int temp3L;
+ int temp0R;
+ int temp1R;
+ int temp2R;
+ int temp3R;
+
+ int mid0 = pInputSamples0[i*4+0] << pFlac->currentFrame.subframes[0].wastedBitsPerSample;
+ int mid1 = pInputSamples0[i*4+1] << pFlac->currentFrame.subframes[0].wastedBitsPerSample;
+ int mid2 = pInputSamples0[i*4+2] << pFlac->currentFrame.subframes[0].wastedBitsPerSample;
+ int mid3 = pInputSamples0[i*4+3] << pFlac->currentFrame.subframes[0].wastedBitsPerSample;
+
+ int side0 = pInputSamples1[i*4+0] << pFlac->currentFrame.subframes[1].wastedBitsPerSample;
+ int side1 = pInputSamples1[i*4+1] << pFlac->currentFrame.subframes[1].wastedBitsPerSample;
+ int side2 = pInputSamples1[i*4+2] << pFlac->currentFrame.subframes[1].wastedBitsPerSample;
+ int side3 = pInputSamples1[i*4+3] << pFlac->currentFrame.subframes[1].wastedBitsPerSample;
+
+ mid0 = (((drflac_uint32)mid0) << 1) | (side0 & 0x01);
+ mid1 = (((drflac_uint32)mid1) << 1) | (side1 & 0x01);
+ mid2 = (((drflac_uint32)mid2) << 1) | (side2 & 0x01);
+ mid3 = (((drflac_uint32)mid3) << 1) | (side3 & 0x01);
+
+ temp0L = ((mid0 + side0) << shift);
+ temp1L = ((mid1 + side1) << shift);
+ temp2L = ((mid2 + side2) << shift);
+ temp3L = ((mid3 + side3) << shift);
+
+ temp0R = ((mid0 - side0) << shift);
+ temp1R = ((mid1 - side1) << shift);
+ temp2R = ((mid2 - side2) << shift);
+ temp3R = ((mid3 - side3) << shift);
+
+ pOutputSamples[i*8+0] = (float)(temp0L * factor);
+ pOutputSamples[i*8+1] = (float)(temp0R * factor);
+ pOutputSamples[i*8+2] = (float)(temp1L * factor);
+ pOutputSamples[i*8+3] = (float)(temp1R * factor);
+ pOutputSamples[i*8+4] = (float)(temp2L * factor);
+ pOutputSamples[i*8+5] = (float)(temp2R * factor);
+ pOutputSamples[i*8+6] = (float)(temp3L * factor);
+ pOutputSamples[i*8+7] = (float)(temp3R * factor);
+ }
+ } else {
+ for (i = 0; i < frameCount4; ++i) {
+ int temp0L;
+ int temp1L;
+ int temp2L;
+ int temp3L;
+ int temp0R;
+ int temp1R;
+ int temp2R;
+ int temp3R;
+
+ int mid0 = pInputSamples0[i*4+0] << pFlac->currentFrame.subframes[0].wastedBitsPerSample;
+ int mid1 = pInputSamples0[i*4+1] << pFlac->currentFrame.subframes[0].wastedBitsPerSample;
+ int mid2 = pInputSamples0[i*4+2] << pFlac->currentFrame.subframes[0].wastedBitsPerSample;
+ int mid3 = pInputSamples0[i*4+3] << pFlac->currentFrame.subframes[0].wastedBitsPerSample;
+
+ int side0 = pInputSamples1[i*4+0] << pFlac->currentFrame.subframes[1].wastedBitsPerSample;
+ int side1 = pInputSamples1[i*4+1] << pFlac->currentFrame.subframes[1].wastedBitsPerSample;
+ int side2 = pInputSamples1[i*4+2] << pFlac->currentFrame.subframes[1].wastedBitsPerSample;
+ int side3 = pInputSamples1[i*4+3] << pFlac->currentFrame.subframes[1].wastedBitsPerSample;
+
+ mid0 = (((drflac_uint32)mid0) << 1) | (side0 & 0x01);
+ mid1 = (((drflac_uint32)mid1) << 1) | (side1 & 0x01);
+ mid2 = (((drflac_uint32)mid2) << 1) | (side2 & 0x01);
+ mid3 = (((drflac_uint32)mid3) << 1) | (side3 & 0x01);
+
+ temp0L = ((mid0 + side0) >> 1);
+ temp1L = ((mid1 + side1) >> 1);
+ temp2L = ((mid2 + side2) >> 1);
+ temp3L = ((mid3 + side3) >> 1);
+
+ temp0R = ((mid0 - side0) >> 1);
+ temp1R = ((mid1 - side1) >> 1);
+ temp2R = ((mid2 - side2) >> 1);
+ temp3R = ((mid3 - side3) >> 1);
+
+ pOutputSamples[i*8+0] = (float)(temp0L * factor);
+ pOutputSamples[i*8+1] = (float)(temp0R * factor);
+ pOutputSamples[i*8+2] = (float)(temp1L * factor);
+ pOutputSamples[i*8+3] = (float)(temp1R * factor);
+ pOutputSamples[i*8+4] = (float)(temp2L * factor);
+ pOutputSamples[i*8+5] = (float)(temp2R * factor);
+ pOutputSamples[i*8+6] = (float)(temp3L * factor);
+ pOutputSamples[i*8+7] = (float)(temp3R * factor);
+ }
+ }
+
+ for (i = (frameCount4 << 2); i < frameCount; ++i) {
+ int mid = pInputSamples0[i] << pFlac->currentFrame.subframes[0].wastedBitsPerSample;
+ int side = pInputSamples1[i] << pFlac->currentFrame.subframes[1].wastedBitsPerSample;
+
+ mid = (((drflac_uint32)mid) << 1) | (side & 0x01);
+
+ pOutputSamples[i*2+0] = (float)((((mid + side) >> 1) << unusedBitsPerSample) * factor);
+ pOutputSamples[i*2+1] = (float)((((mid - side) >> 1) << unusedBitsPerSample) * factor);
+ }
+}
+
+#if defined(DRFLAC_SUPPORT_SSE2)
+static DRFLAC_INLINE void drflac_read_pcm_frames_f32__decode_mid_side__sse2(drflac* pFlac, drflac_uint64 frameCount, drflac_int32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, float* pOutputSamples)
+{
+ drflac_uint64 i;
+ drflac_uint64 frameCount4;
+ float factor;
+ int shift;
+ __m128 factor128;
+
+ drflac_assert(pFlac->bitsPerSample <= 24);
+
+ frameCount4 = frameCount >> 2;
+
+ factor = 1.0f / 8388608.0f;
+ factor128 = _mm_set1_ps(1.0f / 8388608.0f);
+
+ shift = unusedBitsPerSample - 8;
+ if (shift == 0) {
+ for (i = 0; i < frameCount4; ++i) {
+ __m128i tempL;
+ __m128i tempR;
+ __m128 leftf;
+ __m128 rightf;
+
+ __m128i inputSample0 = _mm_loadu_si128((const __m128i*)pInputSamples0 + i);
+ __m128i inputSample1 = _mm_loadu_si128((const __m128i*)pInputSamples1 + i);
+
+ __m128i mid = _mm_slli_epi32(inputSample0, pFlac->currentFrame.subframes[0].wastedBitsPerSample);
+ __m128i side = _mm_slli_epi32(inputSample1, pFlac->currentFrame.subframes[1].wastedBitsPerSample);
+
+ mid = _mm_or_si128(_mm_slli_epi32(mid, 1), _mm_and_si128(side, _mm_set1_epi32(0x01)));
+
+ tempL = _mm_add_epi32(mid, side);
+ tempR = _mm_sub_epi32(mid, side);
+
+ /* Signed bit shift. */
+ tempL = _mm_or_si128(_mm_srli_epi32(tempL, 1), _mm_and_si128(tempL, _mm_set1_epi32(0x80000000)));
+ tempR = _mm_or_si128(_mm_srli_epi32(tempR, 1), _mm_and_si128(tempR, _mm_set1_epi32(0x80000000)));
+
+ leftf = _mm_mul_ps(_mm_cvtepi32_ps(tempL), factor128);
+ rightf = _mm_mul_ps(_mm_cvtepi32_ps(tempR), factor128);
+
+ pOutputSamples[i*8+0] = ((float*)&leftf)[0];
+ pOutputSamples[i*8+1] = ((float*)&rightf)[0];
+ pOutputSamples[i*8+2] = ((float*)&leftf)[1];
+ pOutputSamples[i*8+3] = ((float*)&rightf)[1];
+ pOutputSamples[i*8+4] = ((float*)&leftf)[2];
+ pOutputSamples[i*8+5] = ((float*)&rightf)[2];
+ pOutputSamples[i*8+6] = ((float*)&leftf)[3];
+ pOutputSamples[i*8+7] = ((float*)&rightf)[3];
+ }
+
+ for (i = (frameCount4 << 2); i < frameCount; ++i) {
+ int mid = pInputSamples0[i] << pFlac->currentFrame.subframes[0].wastedBitsPerSample;
+ int side = pInputSamples1[i] << pFlac->currentFrame.subframes[1].wastedBitsPerSample;
+
+ mid = (((drflac_uint32)mid) << 1) | (side & 0x01);
+
+ pOutputSamples[i*2+0] = (float)(((mid + side) >> 1) * factor);
+ pOutputSamples[i*2+1] = (float)(((mid - side) >> 1) * factor);
+ }
+ } else {
+ for (i = 0; i < frameCount4; ++i) {
+ __m128i inputSample0;
+ __m128i inputSample1;
+ __m128i mid;
+ __m128i side;
+ __m128i tempL;
+ __m128i tempR;
+ __m128 leftf;
+ __m128 rightf;
+
+ inputSample0 = _mm_loadu_si128((const __m128i*)pInputSamples0 + i);
+ inputSample1 = _mm_loadu_si128((const __m128i*)pInputSamples1 + i);
+
+ mid = _mm_slli_epi32(inputSample0, pFlac->currentFrame.subframes[0].wastedBitsPerSample);
+ side = _mm_slli_epi32(inputSample1, pFlac->currentFrame.subframes[1].wastedBitsPerSample);
+
+ mid = _mm_or_si128(_mm_slli_epi32(mid, 1), _mm_and_si128(side, _mm_set1_epi32(0x01)));
+
+ tempL = _mm_slli_epi32(_mm_srli_epi32(_mm_add_epi32(mid, side), 1), shift);
+ tempR = _mm_slli_epi32(_mm_srli_epi32(_mm_sub_epi32(mid, side), 1), shift);
+
+ leftf = _mm_mul_ps(_mm_cvtepi32_ps(tempL), factor128);
+ rightf = _mm_mul_ps(_mm_cvtepi32_ps(tempR), factor128);
+
+ pOutputSamples[i*8+0] = ((float*)&leftf)[0];
+ pOutputSamples[i*8+1] = ((float*)&rightf)[0];
+ pOutputSamples[i*8+2] = ((float*)&leftf)[1];
+ pOutputSamples[i*8+3] = ((float*)&rightf)[1];
+ pOutputSamples[i*8+4] = ((float*)&leftf)[2];
+ pOutputSamples[i*8+5] = ((float*)&rightf)[2];
+ pOutputSamples[i*8+6] = ((float*)&leftf)[3];
+ pOutputSamples[i*8+7] = ((float*)&rightf)[3];
+ }
+
+ for (i = (frameCount4 << 2); i < frameCount; ++i) {
+ int mid = pInputSamples0[i] << pFlac->currentFrame.subframes[0].wastedBitsPerSample;
+ int side = pInputSamples1[i] << pFlac->currentFrame.subframes[1].wastedBitsPerSample;
+
+ mid = (((drflac_uint32)mid) << 1) | (side & 0x01);
+
+ pOutputSamples[i*2+0] = (float)((((mid + side) >> 1) << shift) * factor);
+ pOutputSamples[i*2+1] = (float)((((mid - side) >> 1) << shift) * factor);
+ }
+ }
+}
+#endif
+
+
+static DRFLAC_INLINE void drflac_read_pcm_frames_f32__decode_mid_side(drflac* pFlac, drflac_uint64 frameCount, drflac_int32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, float* pOutputSamples)
+{
+#if defined(DRFLAC_SUPPORT_SSE2)
+ if (drflac__gIsSSE2Supported && pFlac->bitsPerSample <= 24) {
+ drflac_read_pcm_frames_f32__decode_mid_side__sse2(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);
+ } else
+#endif
+ {
+ /* Scalar fallback. */
+#if 0
+ drflac_read_pcm_frames_f32__decode_mid_side__reference(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);
+#else
+ drflac_read_pcm_frames_f32__decode_mid_side__scalar(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);
+#endif
+ }
+}
+
+#if 0
+static DRFLAC_INLINE void drflac_read_pcm_frames_f32__decode_independent_stereo__reference(drflac* pFlac, drflac_uint64 frameCount, drflac_int32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, float* pOutputSamples)
+{
+ for (drflac_uint64 i = 0; i < frameCount; ++i) {
+ pOutputSamples[i*2+0] = (float)((pInputSamples0[i] << (unusedBitsPerSample + pFlac->currentFrame.subframes[0].wastedBitsPerSample)) / 2147483648.0);
+ pOutputSamples[i*2+1] = (float)((pInputSamples1[i] << (unusedBitsPerSample + pFlac->currentFrame.subframes[1].wastedBitsPerSample)) / 2147483648.0);
+ }
+}
+#endif
+
+static DRFLAC_INLINE void drflac_read_pcm_frames_f32__decode_independent_stereo__scalar(drflac* pFlac, drflac_uint64 frameCount, drflac_int32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, float* pOutputSamples)
+{
+ drflac_uint64 i;
+ drflac_uint64 frameCount4 = frameCount >> 2;
+
+ float factor = 1 / 2147483648.0;
+
+ int shift0 = (unusedBitsPerSample + pFlac->currentFrame.subframes[0].wastedBitsPerSample);
+ int shift1 = (unusedBitsPerSample + pFlac->currentFrame.subframes[1].wastedBitsPerSample);
+
+ for (i = 0; i < frameCount4; ++i) {
+ int tempL0 = pInputSamples0[i*4+0] << shift0;
+ int tempL1 = pInputSamples0[i*4+1] << shift0;
+ int tempL2 = pInputSamples0[i*4+2] << shift0;
+ int tempL3 = pInputSamples0[i*4+3] << shift0;
+
+ int tempR0 = pInputSamples1[i*4+0] << shift1;
+ int tempR1 = pInputSamples1[i*4+1] << shift1;
+ int tempR2 = pInputSamples1[i*4+2] << shift1;
+ int tempR3 = pInputSamples1[i*4+3] << shift1;
+
+ pOutputSamples[i*8+0] = (float)(tempL0 * factor);
+ pOutputSamples[i*8+1] = (float)(tempR0 * factor);
+ pOutputSamples[i*8+2] = (float)(tempL1 * factor);
+ pOutputSamples[i*8+3] = (float)(tempR1 * factor);
+ pOutputSamples[i*8+4] = (float)(tempL2 * factor);
+ pOutputSamples[i*8+5] = (float)(tempR2 * factor);
+ pOutputSamples[i*8+6] = (float)(tempL3 * factor);
+ pOutputSamples[i*8+7] = (float)(tempR3 * factor);
+ }
+
+ for (i = (frameCount4 << 2); i < frameCount; ++i) {
+ pOutputSamples[i*2+0] = (float)((pInputSamples0[i] << shift0) * factor);
+ pOutputSamples[i*2+1] = (float)((pInputSamples1[i] << shift1) * factor);
+ }
+}
+
+#if defined(DRFLAC_SUPPORT_SSE2)
+static DRFLAC_INLINE void drflac_read_pcm_frames_f32__decode_independent_stereo__sse2(drflac* pFlac, drflac_uint64 frameCount, drflac_int32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, float* pOutputSamples)
+{
+ drflac_uint64 i;
+ drflac_uint64 frameCount4 = frameCount >> 2;
+
+ float factor = 1.0f / 8388608.0f;
+ __m128 factor128 = _mm_set1_ps(1.0f / 8388608.0f);
+
+ int shift0 = (unusedBitsPerSample + pFlac->currentFrame.subframes[0].wastedBitsPerSample) - 8;
+ int shift1 = (unusedBitsPerSample + pFlac->currentFrame.subframes[1].wastedBitsPerSample) - 8;
+
+ for (i = 0; i < frameCount4; ++i) {
+ __m128i inputSample0 = _mm_loadu_si128((const __m128i*)pInputSamples0 + i);
+ __m128i inputSample1 = _mm_loadu_si128((const __m128i*)pInputSamples1 + i);
+
+ __m128i i32L = _mm_slli_epi32(inputSample0, shift0);
+ __m128i i32R = _mm_slli_epi32(inputSample1, shift1);
+
+ __m128 f32L = _mm_mul_ps(_mm_cvtepi32_ps(i32L), factor128);
+ __m128 f32R = _mm_mul_ps(_mm_cvtepi32_ps(i32R), factor128);
+
+ pOutputSamples[i*8+0] = ((float*)&f32L)[0];
+ pOutputSamples[i*8+1] = ((float*)&f32R)[0];
+ pOutputSamples[i*8+2] = ((float*)&f32L)[1];
+ pOutputSamples[i*8+3] = ((float*)&f32R)[1];
+ pOutputSamples[i*8+4] = ((float*)&f32L)[2];
+ pOutputSamples[i*8+5] = ((float*)&f32R)[2];
+ pOutputSamples[i*8+6] = ((float*)&f32L)[3];
+ pOutputSamples[i*8+7] = ((float*)&f32R)[3];
+ }
+
+ for (i = (frameCount4 << 2); i < frameCount; ++i) {
+ pOutputSamples[i*2+0] = (float)((pInputSamples0[i] << shift0) * factor);
+ pOutputSamples[i*2+1] = (float)((pInputSamples1[i] << shift1) * factor);
+ }
+}
+#endif
+
+static DRFLAC_INLINE void drflac_read_pcm_frames_f32__decode_independent_stereo(drflac* pFlac, drflac_uint64 frameCount, drflac_int32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, float* pOutputSamples)
+{
+#if defined(DRFLAC_SUPPORT_SSE2)
+ if (drflac__gIsSSE2Supported && pFlac->bitsPerSample <= 24) {
+ drflac_read_pcm_frames_f32__decode_independent_stereo__sse2(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);
+ } else
+#endif
+ {
+ /* Scalar fallback. */
+#if 0
+ drflac_read_pcm_frames_f32__decode_independent_stereo__reference(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);
+#else
+ drflac_read_pcm_frames_f32__decode_independent_stereo__scalar(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);
+#endif
+ }
+}
+
+drflac_uint64 drflac_read_pcm_frames_f32(drflac* pFlac, drflac_uint64 framesToRead, float* pBufferOut)
+{
+ drflac_uint64 framesRead;
+
+ if (pFlac == NULL || framesToRead == 0) {
+ return 0;
+ }
+
+ if (pBufferOut == NULL) {
+ return drflac__seek_forward_by_pcm_frames(pFlac, framesToRead);
+ }
+
+ framesRead = 0;
+ while (framesToRead > 0) {
+ /* If we've run out of samples in this frame, go to the next. */
+ if (pFlac->currentFrame.samplesRemaining == 0) {
+ if (!drflac__read_and_decode_next_flac_frame(pFlac)) {
+ break; /* Couldn't read the next frame, so just break from the loop and return. */
+ }
+ } else {
+ unsigned int channelCount = drflac__get_channel_count_from_channel_assignment(pFlac->currentFrame.header.channelAssignment);
+ drflac_uint64 totalFramesInPacket = pFlac->currentFrame.header.blockSize;
+ drflac_uint64 framesReadFromPacketSoFar = totalFramesInPacket - (pFlac->currentFrame.samplesRemaining/channelCount);
+ drflac_uint64 iFirstPCMFrame = framesReadFromPacketSoFar;
+ drflac_int32 unusedBitsPerSample = 32 - pFlac->bitsPerSample;
+ drflac_uint64 frameCountThisIteration = framesToRead;
+ drflac_uint64 samplesReadThisIteration;
+
+ if (frameCountThisIteration > pFlac->currentFrame.samplesRemaining / channelCount) {
+ frameCountThisIteration = pFlac->currentFrame.samplesRemaining / channelCount;
+ }
+
+ if (channelCount == 2) {
+ const drflac_int32* pDecodedSamples0 = pFlac->currentFrame.subframes[0].pDecodedSamples + iFirstPCMFrame;
+ const drflac_int32* pDecodedSamples1 = pFlac->currentFrame.subframes[1].pDecodedSamples + iFirstPCMFrame;
+
+ switch (pFlac->currentFrame.header.channelAssignment)
+ {
+ case DRFLAC_CHANNEL_ASSIGNMENT_LEFT_SIDE:
+ {
+ drflac_read_pcm_frames_f32__decode_left_side(pFlac, frameCountThisIteration, unusedBitsPerSample, pDecodedSamples0, pDecodedSamples1, pBufferOut);
+ } break;
+
+ case DRFLAC_CHANNEL_ASSIGNMENT_RIGHT_SIDE:
+ {
+ drflac_read_pcm_frames_f32__decode_right_side(pFlac, frameCountThisIteration, unusedBitsPerSample, pDecodedSamples0, pDecodedSamples1, pBufferOut);
+ } break;
+
+ case DRFLAC_CHANNEL_ASSIGNMENT_MID_SIDE:
+ {
+ drflac_read_pcm_frames_f32__decode_mid_side(pFlac, frameCountThisIteration, unusedBitsPerSample, pDecodedSamples0, pDecodedSamples1, pBufferOut);
+ } break;
+
+ case DRFLAC_CHANNEL_ASSIGNMENT_INDEPENDENT:
+ default:
+ {
+ drflac_read_pcm_frames_f32__decode_independent_stereo(pFlac, frameCountThisIteration, unusedBitsPerSample, pDecodedSamples0, pDecodedSamples1, pBufferOut);
+ } break;
+ }
+ } else {
+ /* Generic interleaving. */
+ drflac_uint64 i;
+ for (i = 0; i < frameCountThisIteration; ++i) {
+ unsigned int j;
+ for (j = 0; j < channelCount; ++j) {
+ pBufferOut[(i*channelCount)+j] = (float)(((pFlac->currentFrame.subframes[j].pDecodedSamples[iFirstPCMFrame + i]) << (unusedBitsPerSample + pFlac->currentFrame.subframes[j].wastedBitsPerSample)) / 2147483648.0);
+ }
+ }
+ }
+
+ samplesReadThisIteration = frameCountThisIteration * channelCount;
+ framesRead += frameCountThisIteration;
+ framesReadFromPacketSoFar += frameCountThisIteration;
+ pBufferOut += samplesReadThisIteration;
+ framesToRead -= frameCountThisIteration;
+ pFlac->currentSample += samplesReadThisIteration;
+ pFlac->currentFrame.samplesRemaining -= (unsigned int)samplesReadThisIteration;
+ }
+ }
+
+ return framesRead;
+}
+
drflac_bool32 drflac_seek_to_sample(drflac* pFlac, drflac_uint64 sampleIndex)
{
if (pFlac == NULL) {
return DRFLAC_FALSE;
}
- // If we don't know where the first frame begins then we can't seek. This will happen when the STREAMINFO block was not present
- // when the decoder was opened.
+ /*
+ If we don't know where the first frame begins then we can't seek. This will happen when the STREAMINFO block was not present
+ when the decoder was opened.
+ */
if (pFlac->firstFramePos == 0) {
return DRFLAC_FALSE;
}
@@ -5419,14 +7818,14 @@ drflac_bool32 drflac_seek_to_sample(drflac* pFlac, drflac_uint64 sampleIndex)
} else {
drflac_bool32 wasSuccessful = DRFLAC_FALSE;
- // Clamp the sample to the end.
+ /* Clamp the sample to the end. */
if (sampleIndex >= pFlac->totalSampleCount) {
sampleIndex = pFlac->totalSampleCount - 1;
}
- // If the target sample and the current sample are in the same frame we just move the position forward.
+ /* If the target sample and the current sample are in the same frame we just move the position forward. */
if (sampleIndex > pFlac->currentSample) {
- // Forward.
+ /* Forward. */
drflac_uint32 offset = (drflac_uint32)(sampleIndex - pFlac->currentSample);
if (pFlac->currentFrame.samplesRemaining > offset) {
pFlac->currentFrame.samplesRemaining -= offset;
@@ -5434,7 +7833,7 @@ drflac_bool32 drflac_seek_to_sample(drflac* pFlac, drflac_uint64 sampleIndex)
return DRFLAC_TRUE;
}
} else {
- // Backward.
+ /* Backward. */
drflac_uint32 offsetAbs = (drflac_uint32)(pFlac->currentSample - sampleIndex);
drflac_uint32 currentFrameSampleCount = pFlac->currentFrame.header.blockSize * drflac__get_channel_count_from_channel_assignment(pFlac->currentFrame.header.channelAssignment);
drflac_uint32 currentFrameSamplesConsumed = (drflac_uint32)(currentFrameSampleCount - pFlac->currentFrame.samplesRemaining);
@@ -5445,17 +7844,19 @@ drflac_bool32 drflac_seek_to_sample(drflac* pFlac, drflac_uint64 sampleIndex)
}
}
- // Different techniques depending on encapsulation. Using the native FLAC seektable with Ogg encapsulation is a bit awkward so
- // we'll instead use Ogg's natural seeking facility.
- #ifndef DR_FLAC_NO_OGG
+ /*
+ Different techniques depending on encapsulation. Using the native FLAC seektable with Ogg encapsulation is a bit awkward so
+ we'll instead use Ogg's natural seeking facility.
+ */
+#ifndef DR_FLAC_NO_OGG
if (pFlac->container == drflac_container_ogg)
{
wasSuccessful = drflac_ogg__seek_to_sample(pFlac, sampleIndex);
}
else
- #endif
+#endif
{
- // First try seeking via the seek table. If this fails, fall back to a brute force seek which is much slower.
+ /* First try seeking via the seek table. If this fails, fall back to a brute force seek which is much slower. */
wasSuccessful = drflac__seek_to_sample__seek_table(pFlac, sampleIndex);
if (!wasSuccessful) {
wasSuccessful = drflac__seek_to_sample__brute_force(pFlac, sampleIndex);
@@ -5467,42 +7868,118 @@ drflac_bool32 drflac_seek_to_sample(drflac* pFlac, drflac_uint64 sampleIndex)
}
}
+drflac_bool32 drflac_seek_to_pcm_frame(drflac* pFlac, drflac_uint64 pcmFrameIndex)
+{
+ if (pFlac == NULL) {
+ return DRFLAC_FALSE;
+ }
+
+ /*
+ If we don't know where the first frame begins then we can't seek. This will happen when the STREAMINFO block was not present
+ when the decoder was opened.
+ */
+ if (pFlac->firstFramePos == 0) {
+ return DRFLAC_FALSE;
+ }
+ if (pcmFrameIndex == 0) {
+ pFlac->currentSample = 0;
+ return drflac__seek_to_first_frame(pFlac);
+ } else {
+ drflac_bool32 wasSuccessful = DRFLAC_FALSE;
-//// High Level APIs ////
+ /* Clamp the sample to the end. */
+ if (pcmFrameIndex >= pFlac->totalPCMFrameCount) {
+ pcmFrameIndex = pFlac->totalPCMFrameCount - 1;
+ }
-// I couldn't figure out where SIZE_MAX was defined for VC6. If anybody knows, let me know.
-#if defined(_MSC_VER) && _MSC_VER <= 1200
-#ifdef DRFLAC_64BIT
-#define SIZE_MAX ((drflac_uint64)0xFFFFFFFFFFFFFFFF)
-#else
-#define SIZE_MAX 0xFFFFFFFF
+ /* If the target sample and the current sample are in the same frame we just move the position forward. */
+ if (pcmFrameIndex*pFlac->channels > pFlac->currentSample) {
+ /* Forward. */
+ drflac_uint32 offset = (drflac_uint32)(pcmFrameIndex*pFlac->channels - pFlac->currentSample);
+ if (pFlac->currentFrame.samplesRemaining > offset) {
+ pFlac->currentFrame.samplesRemaining -= offset;
+ pFlac->currentSample = pcmFrameIndex*pFlac->channels;
+ return DRFLAC_TRUE;
+ }
+ } else {
+ /* Backward. */
+ drflac_uint32 offsetAbs = (drflac_uint32)(pFlac->currentSample - pcmFrameIndex*pFlac->channels);
+ drflac_uint32 currentFrameSampleCount = pFlac->currentFrame.header.blockSize * drflac__get_channel_count_from_channel_assignment(pFlac->currentFrame.header.channelAssignment);
+ drflac_uint32 currentFrameSamplesConsumed = (drflac_uint32)(currentFrameSampleCount - pFlac->currentFrame.samplesRemaining);
+ if (currentFrameSamplesConsumed > offsetAbs) {
+ pFlac->currentFrame.samplesRemaining += offsetAbs;
+ pFlac->currentSample = pcmFrameIndex*pFlac->channels;
+ return DRFLAC_TRUE;
+ }
+ }
+
+ /*
+ Different techniques depending on encapsulation. Using the native FLAC seektable with Ogg encapsulation is a bit awkward so
+ we'll instead use Ogg's natural seeking facility.
+ */
+#ifndef DR_FLAC_NO_OGG
+ if (pFlac->container == drflac_container_ogg)
+ {
+ wasSuccessful = drflac_ogg__seek_to_sample(pFlac, pcmFrameIndex*pFlac->channels);
+ }
+ else
#endif
+ {
+ /* First try seeking via the seek table. If this fails, fall back to a brute force seek which is much slower. */
+ wasSuccessful = drflac__seek_to_sample__seek_table(pFlac, pcmFrameIndex*pFlac->channels);
+ if (!wasSuccessful) {
+ wasSuccessful = drflac__seek_to_sample__brute_force(pFlac, pcmFrameIndex*pFlac->channels);
+ }
+ }
+
+ pFlac->currentSample = pcmFrameIndex*pFlac->channels;
+ return wasSuccessful;
+ }
+}
+
+
+
+/* High Level APIs */
+
+#if defined(SIZE_MAX)
+ #define DRFLAC_SIZE_MAX SIZE_MAX
+#else
+ #if defined(DRFLAC_64BIT)
+ #define DRFLAC_SIZE_MAX ((drflac_uint64)0xFFFFFFFFFFFFFFFF)
+ #else
+ #define DRFLAC_SIZE_MAX 0xFFFFFFFF
+ #endif
#endif
-// Using a macro as the definition of the drflac__full_decode_and_close_*() API family. Sue me.
-#define DRFLAC_DEFINE_FULL_DECODE_AND_CLOSE(extension, type) \
-static type* drflac__full_decode_and_close_ ## extension (drflac* pFlac, unsigned int* channelsOut, unsigned int* sampleRateOut, drflac_uint64* totalSampleCountOut)\
+
+/* Using a macro as the definition of the drflac__full_decode_and_close_*() API family. Sue me. */
+#define DRFLAC_DEFINE_FULL_READ_AND_CLOSE(extension, type) \
+static type* drflac__full_read_and_close_ ## extension (drflac* pFlac, unsigned int* channelsOut, unsigned int* sampleRateOut, drflac_uint64* totalPCMFrameCountOut)\
{ \
+ type* pSampleData = NULL; \
+ drflac_uint64 totalPCMFrameCount; \
+ \
drflac_assert(pFlac != NULL); \
\
- type* pSampleData = NULL; \
- drflac_uint64 totalSampleCount = pFlac->totalSampleCount; \
+ totalPCMFrameCount = pFlac->totalPCMFrameCount; \
\
- if (totalSampleCount == 0) { \
+ if (totalPCMFrameCount == 0) { \
type buffer[4096]; \
- \
+ drflac_uint64 pcmFramesRead; \
size_t sampleDataBufferSize = sizeof(buffer); \
+ \
pSampleData = (type*)DRFLAC_MALLOC(sampleDataBufferSize); \
if (pSampleData == NULL) { \
goto on_error; \
} \
\
- drflac_uint64 samplesRead; \
- while ((samplesRead = (drflac_uint64)drflac_read_##extension(pFlac, sizeof(buffer)/sizeof(buffer[0]), buffer)) > 0) { \
- if (((totalSampleCount + samplesRead) * sizeof(type)) > sampleDataBufferSize) { \
+ while ((pcmFramesRead = (drflac_uint64)drflac_read_pcm_frames_##extension(pFlac, sizeof(buffer)/sizeof(buffer[0])/pFlac->channels, buffer)) > 0) { \
+ if (((totalPCMFrameCount + pcmFramesRead) * pFlac->channels * sizeof(type)) > sampleDataBufferSize) { \
+ type* pNewSampleData; \
+ \
sampleDataBufferSize *= 2; \
- type* pNewSampleData = (type*)DRFLAC_REALLOC(pSampleData, sampleDataBufferSize); \
+ pNewSampleData = (type*)DRFLAC_REALLOC(pSampleData, sampleDataBufferSize); \
if (pNewSampleData == NULL) { \
DRFLAC_FREE(pSampleData); \
goto on_error; \
@@ -5511,16 +7988,16 @@ static type* drflac__full_decode_and_close_ ## extension (drflac* pFlac, unsigne
pSampleData = pNewSampleData; \
} \
\
- drflac_copy_memory(pSampleData + totalSampleCount, buffer, (size_t)(samplesRead*sizeof(type))); \
- totalSampleCount += samplesRead; \
+ drflac_copy_memory(pSampleData + (totalPCMFrameCount*pFlac->channels), buffer, (size_t)(pcmFramesRead*pFlac->channels*sizeof(type))); \
+ totalPCMFrameCount += pcmFramesRead; \
} \
\
/* At this point everything should be decoded, but we just want to fill the unused part buffer with silence - need to \
protect those ears from random noise! */ \
- drflac_zero_memory(pSampleData + totalSampleCount, (size_t)(sampleDataBufferSize - totalSampleCount*sizeof(type))); \
+ drflac_zero_memory(pSampleData + (totalPCMFrameCount*pFlac->channels), (size_t)(sampleDataBufferSize - totalPCMFrameCount*pFlac->channels*sizeof(type))); \
} else { \
- drflac_uint64 dataSize = totalSampleCount * sizeof(type); \
- if (dataSize > SIZE_MAX) { \
+ drflac_uint64 dataSize = totalPCMFrameCount*pFlac->channels*sizeof(type); \
+ if (dataSize > DRFLAC_SIZE_MAX) { \
goto on_error; /* The decoded data is too big. */ \
} \
\
@@ -5529,12 +8006,12 @@ static type* drflac__full_decode_and_close_ ## extension (drflac* pFlac, unsigne
goto on_error; \
} \
\
- totalSampleCount = drflac_read_##extension(pFlac, pFlac->totalSampleCount, pSampleData); \
+ totalPCMFrameCount = drflac_read_pcm_frames_##extension(pFlac, pFlac->totalPCMFrameCount, pSampleData); \
} \
\
if (sampleRateOut) *sampleRateOut = pFlac->sampleRate; \
if (channelsOut) *channelsOut = pFlac->channels; \
- if (totalSampleCountOut) *totalSampleCountOut = totalSampleCount; \
+ if (totalPCMFrameCountOut) *totalPCMFrameCountOut = totalPCMFrameCount; \
\
drflac_close(pFlac); \
return pSampleData; \
@@ -5544,141 +8021,533 @@ on_error:
return NULL; \
}
-DRFLAC_DEFINE_FULL_DECODE_AND_CLOSE(s32, drflac_int32)
-DRFLAC_DEFINE_FULL_DECODE_AND_CLOSE(s16, drflac_int16)
-DRFLAC_DEFINE_FULL_DECODE_AND_CLOSE(f32, float)
+DRFLAC_DEFINE_FULL_READ_AND_CLOSE(s32, drflac_int32)
+DRFLAC_DEFINE_FULL_READ_AND_CLOSE(s16, drflac_int16)
+DRFLAC_DEFINE_FULL_READ_AND_CLOSE(f32, float)
-drflac_int32* drflac_open_and_decode_s32(drflac_read_proc onRead, drflac_seek_proc onSeek, void* pUserData, unsigned int* channels, unsigned int* sampleRate, drflac_uint64* totalSampleCount)
+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)
{
- // Safety.
- if (sampleRate) *sampleRate = 0;
- if (channels) *channels = 0;
- if (totalSampleCount) *totalSampleCount = 0;
+ drflac* pFlac;
- drflac* pFlac = drflac_open(onRead, onSeek, pUserData);
+ if (channelsOut) {
+ *channelsOut = 0;
+ }
+ if (sampleRateOut) {
+ *sampleRateOut = 0;
+ }
+ if (totalPCMFrameCountOut) {
+ *totalPCMFrameCountOut = 0;
+ }
+
+ pFlac = drflac_open(onRead, onSeek, pUserData);
if (pFlac == NULL) {
return NULL;
}
- return drflac__full_decode_and_close_s32(pFlac, channels, sampleRate, totalSampleCount);
+ return drflac__full_read_and_close_s32(pFlac, channelsOut, sampleRateOut, totalPCMFrameCountOut);
}
-drflac_int16* drflac_open_and_decode_s16(drflac_read_proc onRead, drflac_seek_proc onSeek, void* pUserData, unsigned int* channels, unsigned int* sampleRate, drflac_uint64* totalSampleCount)
+drflac_int32* drflac_open_and_decode_s32(drflac_read_proc onRead, drflac_seek_proc onSeek, void* pUserData, unsigned int* channelsOut, unsigned int* sampleRateOut, drflac_uint64* totalSampleCountOut)
+{
+ unsigned int channels;
+ unsigned int sampleRate;
+ drflac_uint64 totalPCMFrameCount;
+ drflac_int32* pResult;
+
+ if (channelsOut) {
+ *channelsOut = 0;
+ }
+ if (sampleRateOut) {
+ *sampleRateOut = 0;
+ }
+ if (totalSampleCountOut) {
+ *totalSampleCountOut = 0;
+ }
+
+ pResult = drflac_open_and_read_pcm_frames_s32(onRead, onSeek, pUserData, &channels, &sampleRate, &totalPCMFrameCount);
+ if (pResult == NULL) {
+ return NULL;
+ }
+
+ if (channelsOut) {
+ *channelsOut = channels;
+ }
+ if (sampleRateOut) {
+ *sampleRateOut = sampleRate;
+ }
+ if (totalSampleCountOut) {
+ *totalSampleCountOut = totalPCMFrameCount * channels;
+ }
+
+ return pResult;
+}
+
+
+
+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)
{
- // Safety.
- if (sampleRate) *sampleRate = 0;
- if (channels) *channels = 0;
- if (totalSampleCount) *totalSampleCount = 0;
+ drflac* pFlac;
- drflac* pFlac = drflac_open(onRead, onSeek, pUserData);
+ if (channelsOut) {
+ *channelsOut = 0;
+ }
+ if (sampleRateOut) {
+ *sampleRateOut = 0;
+ }
+ if (totalPCMFrameCountOut) {
+ *totalPCMFrameCountOut = 0;
+ }
+
+ pFlac = drflac_open(onRead, onSeek, pUserData);
if (pFlac == NULL) {
return NULL;
}
- return drflac__full_decode_and_close_s16(pFlac, channels, sampleRate, totalSampleCount);
+ return drflac__full_read_and_close_s16(pFlac, channelsOut, sampleRateOut, totalPCMFrameCountOut);
}
-float* drflac_open_and_decode_f32(drflac_read_proc onRead, drflac_seek_proc onSeek, void* pUserData, unsigned int* channels, unsigned int* sampleRate, drflac_uint64* totalSampleCount)
+drflac_int16* drflac_open_and_decode_s16(drflac_read_proc onRead, drflac_seek_proc onSeek, void* pUserData, unsigned int* channelsOut, unsigned int* sampleRateOut, drflac_uint64* totalSampleCountOut)
{
- // Safety.
- if (sampleRate) *sampleRate = 0;
- if (channels) *channels = 0;
- if (totalSampleCount) *totalSampleCount = 0;
+ unsigned int channels;
+ unsigned int sampleRate;
+ drflac_uint64 totalPCMFrameCount;
+ drflac_int16* pResult;
- drflac* pFlac = drflac_open(onRead, onSeek, pUserData);
+ if (channelsOut) {
+ *channelsOut = 0;
+ }
+ if (sampleRateOut) {
+ *sampleRateOut = 0;
+ }
+ if (totalSampleCountOut) {
+ *totalSampleCountOut = 0;
+ }
+
+ pResult = drflac_open_and_read_pcm_frames_s16(onRead, onSeek, pUserData, &channels, &sampleRate, &totalPCMFrameCount);
+ if (pResult == NULL) {
+ return NULL;
+ }
+
+ if (channelsOut) {
+ *channelsOut = channels;
+ }
+ if (sampleRateOut) {
+ *sampleRateOut = sampleRate;
+ }
+ if (totalSampleCountOut) {
+ *totalSampleCountOut = totalPCMFrameCount * channels;
+ }
+
+ return pResult;
+}
+
+
+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)
+{
+ drflac* pFlac;
+
+ if (channelsOut) {
+ *channelsOut = 0;
+ }
+ if (sampleRateOut) {
+ *sampleRateOut = 0;
+ }
+ if (totalPCMFrameCountOut) {
+ *totalPCMFrameCountOut = 0;
+ }
+
+ pFlac = drflac_open(onRead, onSeek, pUserData);
if (pFlac == NULL) {
return NULL;
}
- return drflac__full_decode_and_close_f32(pFlac, channels, sampleRate, totalSampleCount);
+ return drflac__full_read_and_close_f32(pFlac, channelsOut, sampleRateOut, totalPCMFrameCountOut);
+}
+
+float* drflac_open_and_decode_f32(drflac_read_proc onRead, drflac_seek_proc onSeek, void* pUserData, unsigned int* channelsOut, unsigned int* sampleRateOut, drflac_uint64* totalSampleCountOut)
+{
+ unsigned int channels;
+ unsigned int sampleRate;
+ drflac_uint64 totalPCMFrameCount;
+ float* pResult;
+
+ if (channelsOut) {
+ *channelsOut = 0;
+ }
+ if (sampleRateOut) {
+ *sampleRateOut = 0;
+ }
+ if (totalSampleCountOut) {
+ *totalSampleCountOut = 0;
+ }
+
+ pResult = drflac_open_and_read_pcm_frames_f32(onRead, onSeek, pUserData, &channels, &sampleRate, &totalPCMFrameCount);
+ if (pResult == NULL) {
+ return NULL;
+ }
+
+ if (channelsOut) {
+ *channelsOut = channels;
+ }
+ if (sampleRateOut) {
+ *sampleRateOut = sampleRate;
+ }
+ if (totalSampleCountOut) {
+ *totalSampleCountOut = totalPCMFrameCount * channels;
+ }
+
+ return pResult;
}
#ifndef DR_FLAC_NO_STDIO
-drflac_int32* drflac_open_and_decode_file_s32(const char* filename, unsigned int* channels, unsigned int* sampleRate, drflac_uint64* totalSampleCount)
+drflac_int32* drflac_open_file_and_read_pcm_frames_s32(const char* filename, unsigned int* channels, unsigned int* sampleRate, drflac_uint64* totalPCMFrameCount)
{
- if (sampleRate) *sampleRate = 0;
- if (channels) *channels = 0;
- if (totalSampleCount) *totalSampleCount = 0;
+ drflac* pFlac;
+
+ if (sampleRate) {
+ *sampleRate = 0;
+ }
+ if (channels) {
+ *channels = 0;
+ }
+ if (totalPCMFrameCount) {
+ *totalPCMFrameCount = 0;
+ }
- drflac* pFlac = drflac_open_file(filename);
+ pFlac = drflac_open_file(filename);
if (pFlac == NULL) {
return NULL;
}
- return drflac__full_decode_and_close_s32(pFlac, channels, sampleRate, totalSampleCount);
+ return drflac__full_read_and_close_s32(pFlac, channels, sampleRate, totalPCMFrameCount);
}
-drflac_int16* drflac_open_and_decode_file_s16(const char* filename, unsigned int* channels, unsigned int* sampleRate, drflac_uint64* totalSampleCount)
+drflac_int32* drflac_open_and_decode_file_s32(const char* filename, unsigned int* channelsOut, unsigned int* sampleRateOut, drflac_uint64* totalSampleCountOut)
{
- if (sampleRate) *sampleRate = 0;
- if (channels) *channels = 0;
- if (totalSampleCount) *totalSampleCount = 0;
+ unsigned int channels;
+ unsigned int sampleRate;
+ drflac_uint64 totalPCMFrameCount;
+ drflac_int32* pResult;
+
+ if (channelsOut) {
+ *channelsOut = 0;
+ }
+ if (sampleRateOut) {
+ *sampleRateOut = 0;
+ }
+ if (totalSampleCountOut) {
+ *totalSampleCountOut = 0;
+ }
+
+ pResult = drflac_open_file_and_read_pcm_frames_s32(filename, &channels, &sampleRate, &totalPCMFrameCount);
+ if (pResult == NULL) {
+ return NULL;
+ }
- drflac* pFlac = drflac_open_file(filename);
+ if (channelsOut) {
+ *channelsOut = channels;
+ }
+ if (sampleRateOut) {
+ *sampleRateOut = sampleRate;
+ }
+ if (totalSampleCountOut) {
+ *totalSampleCountOut = totalPCMFrameCount * channels;
+ }
+
+ return pResult;
+}
+
+
+drflac_int16* drflac_open_file_and_read_pcm_frames_s16(const char* filename, unsigned int* channels, unsigned int* sampleRate, drflac_uint64* totalPCMFrameCount)
+{
+ drflac* pFlac;
+
+ if (sampleRate) {
+ *sampleRate = 0;
+ }
+ if (channels) {
+ *channels = 0;
+ }
+ if (totalPCMFrameCount) {
+ *totalPCMFrameCount = 0;
+ }
+
+ pFlac = drflac_open_file(filename);
if (pFlac == NULL) {
return NULL;
}
- return drflac__full_decode_and_close_s16(pFlac, channels, sampleRate, totalSampleCount);
+ return drflac__full_read_and_close_s16(pFlac, channels, sampleRate, totalPCMFrameCount);
+}
+
+drflac_int16* drflac_open_and_decode_file_s16(const char* filename, unsigned int* channelsOut, unsigned int* sampleRateOut, drflac_uint64* totalSampleCountOut)
+{
+ unsigned int channels;
+ unsigned int sampleRate;
+ drflac_uint64 totalPCMFrameCount;
+ drflac_int16* pResult;
+
+ if (channelsOut) {
+ *channelsOut = 0;
+ }
+ if (sampleRateOut) {
+ *sampleRateOut = 0;
+ }
+ if (totalSampleCountOut) {
+ *totalSampleCountOut = 0;
+ }
+
+ pResult = drflac_open_file_and_read_pcm_frames_s16(filename, &channels, &sampleRate, &totalPCMFrameCount);
+ if (pResult == NULL) {
+ return NULL;
+ }
+
+ if (channelsOut) {
+ *channelsOut = channels;
+ }
+ if (sampleRateOut) {
+ *sampleRateOut = sampleRate;
+ }
+ if (totalSampleCountOut) {
+ *totalSampleCountOut = totalPCMFrameCount * channels;
+ }
+
+ return pResult;
}
-float* drflac_open_and_decode_file_f32(const char* filename, unsigned int* channels, unsigned int* sampleRate, drflac_uint64* totalSampleCount)
+
+float* drflac_open_file_and_read_pcm_frames_f32(const char* filename, unsigned int* channels, unsigned int* sampleRate, drflac_uint64* totalPCMFrameCount)
{
- if (sampleRate) *sampleRate = 0;
- if (channels) *channels = 0;
- if (totalSampleCount) *totalSampleCount = 0;
+ drflac* pFlac;
- drflac* pFlac = drflac_open_file(filename);
+ if (sampleRate) {
+ *sampleRate = 0;
+ }
+ if (channels) {
+ *channels = 0;
+ }
+ if (totalPCMFrameCount) {
+ *totalPCMFrameCount = 0;
+ }
+
+ pFlac = drflac_open_file(filename);
if (pFlac == NULL) {
return NULL;
}
- return drflac__full_decode_and_close_f32(pFlac, channels, sampleRate, totalSampleCount);
+ return drflac__full_read_and_close_f32(pFlac, channels, sampleRate, totalPCMFrameCount);
+}
+
+float* drflac_open_and_decode_file_f32(const char* filename, unsigned int* channelsOut, unsigned int* sampleRateOut, drflac_uint64* totalSampleCountOut)
+{
+ unsigned int channels;
+ unsigned int sampleRate;
+ drflac_uint64 totalPCMFrameCount;
+ float* pResult;
+
+ if (channelsOut) {
+ *channelsOut = 0;
+ }
+ if (sampleRateOut) {
+ *sampleRateOut = 0;
+ }
+ if (totalSampleCountOut) {
+ *totalSampleCountOut = 0;
+ }
+
+ pResult = drflac_open_file_and_read_pcm_frames_f32(filename, &channels, &sampleRate, &totalPCMFrameCount);
+ if (pResult == NULL) {
+ return NULL;
+ }
+
+ if (channelsOut) {
+ *channelsOut = channels;
+ }
+ if (sampleRateOut) {
+ *sampleRateOut = sampleRate;
+ }
+ if (totalSampleCountOut) {
+ *totalSampleCountOut = totalPCMFrameCount * channels;
+ }
+
+ return pResult;
}
#endif
-drflac_int32* drflac_open_and_decode_memory_s32(const void* data, size_t dataSize, unsigned int* channels, unsigned int* sampleRate, drflac_uint64* totalSampleCount)
+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)
{
- if (sampleRate) *sampleRate = 0;
- if (channels) *channels = 0;
- if (totalSampleCount) *totalSampleCount = 0;
+ drflac* pFlac;
+
+ if (sampleRate) {
+ *sampleRate = 0;
+ }
+ if (channels) {
+ *channels = 0;
+ }
+ if (totalPCMFrameCount) {
+ *totalPCMFrameCount = 0;
+ }
- drflac* pFlac = drflac_open_memory(data, dataSize);
+ pFlac = drflac_open_memory(data, dataSize);
if (pFlac == NULL) {
return NULL;
}
- return drflac__full_decode_and_close_s32(pFlac, channels, sampleRate, totalSampleCount);
+ return drflac__full_read_and_close_s32(pFlac, channels, sampleRate, totalPCMFrameCount);
}
-drflac_int16* drflac_open_and_decode_memory_s16(const void* data, size_t dataSize, unsigned int* channels, unsigned int* sampleRate, drflac_uint64* totalSampleCount)
+drflac_int32* drflac_open_and_decode_memory_s32(const void* data, size_t dataSize, unsigned int* channelsOut, unsigned int* sampleRateOut, drflac_uint64* totalSampleCountOut)
{
- if (sampleRate) *sampleRate = 0;
- if (channels) *channels = 0;
- if (totalSampleCount) *totalSampleCount = 0;
+ unsigned int channels;
+ unsigned int sampleRate;
+ drflac_uint64 totalPCMFrameCount;
+ drflac_int32* pResult;
+
+ if (channelsOut) {
+ *channelsOut = 0;
+ }
+ if (sampleRateOut) {
+ *sampleRateOut = 0;
+ }
+ if (totalSampleCountOut) {
+ *totalSampleCountOut = 0;
+ }
+
+ pResult = drflac_open_memory_and_read_pcm_frames_s32(data, dataSize, &channels, &sampleRate, &totalPCMFrameCount);
+ if (pResult == NULL) {
+ return NULL;
+ }
+
+ if (channelsOut) {
+ *channelsOut = channels;
+ }
+ if (sampleRateOut) {
+ *sampleRateOut = sampleRate;
+ }
+ if (totalSampleCountOut) {
+ *totalSampleCountOut = totalPCMFrameCount * channels;
+ }
- drflac* pFlac = drflac_open_memory(data, dataSize);
+ return pResult;
+}
+
+
+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)
+{
+ drflac* pFlac;
+
+ if (sampleRate) {
+ *sampleRate = 0;
+ }
+ if (channels) {
+ *channels = 0;
+ }
+ if (totalPCMFrameCount) {
+ *totalPCMFrameCount = 0;
+ }
+
+ pFlac = drflac_open_memory(data, dataSize);
if (pFlac == NULL) {
return NULL;
}
- return drflac__full_decode_and_close_s16(pFlac, channels, sampleRate, totalSampleCount);
+ return drflac__full_read_and_close_s16(pFlac, channels, sampleRate, totalPCMFrameCount);
+}
+
+drflac_int16* drflac_open_and_decode_memory_s16(const void* data, size_t dataSize, unsigned int* channelsOut, unsigned int* sampleRateOut, drflac_uint64* totalSampleCountOut)
+{
+ unsigned int channels;
+ unsigned int sampleRate;
+ drflac_uint64 totalPCMFrameCount;
+ drflac_int16* pResult;
+
+ if (channelsOut) {
+ *channelsOut = 0;
+ }
+ if (sampleRateOut) {
+ *sampleRateOut = 0;
+ }
+ if (totalSampleCountOut) {
+ *totalSampleCountOut = 0;
+ }
+
+ pResult = drflac_open_memory_and_read_pcm_frames_s16(data, dataSize, &channels, &sampleRate, &totalPCMFrameCount);
+ if (pResult == NULL) {
+ return NULL;
+ }
+
+ if (channelsOut) {
+ *channelsOut = channels;
+ }
+ if (sampleRateOut) {
+ *sampleRateOut = sampleRate;
+ }
+ if (totalSampleCountOut) {
+ *totalSampleCountOut = totalPCMFrameCount * channels;
+ }
+
+ return pResult;
}
-float* drflac_open_and_decode_memory_f32(const void* data, size_t dataSize, unsigned int* channels, unsigned int* sampleRate, drflac_uint64* totalSampleCount)
+
+float* drflac_open_memory_and_read_pcm_frames_f32(const void* data, size_t dataSize, unsigned int* channels, unsigned int* sampleRate, drflac_uint64* totalPCMFrameCount)
{
- if (sampleRate) *sampleRate = 0;
- if (channels) *channels = 0;
- if (totalSampleCount) *totalSampleCount = 0;
+ drflac* pFlac;
- drflac* pFlac = drflac_open_memory(data, dataSize);
+ if (sampleRate) {
+ *sampleRate = 0;
+ }
+ if (channels) {
+ *channels = 0;
+ }
+ if (totalPCMFrameCount) {
+ *totalPCMFrameCount = 0;
+ }
+
+ pFlac = drflac_open_memory(data, dataSize);
if (pFlac == NULL) {
return NULL;
}
- return drflac__full_decode_and_close_f32(pFlac, channels, sampleRate, totalSampleCount);
+ return drflac__full_read_and_close_f32(pFlac, channels, sampleRate, totalPCMFrameCount);
}
+float* drflac_open_and_decode_memory_f32(const void* data, size_t dataSize, unsigned int* channelsOut, unsigned int* sampleRateOut, drflac_uint64* totalSampleCountOut)
+{
+ unsigned int channels;
+ unsigned int sampleRate;
+ drflac_uint64 totalPCMFrameCount;
+ float* pResult;
+
+ if (channelsOut) {
+ *channelsOut = 0;
+ }
+ if (sampleRateOut) {
+ *sampleRateOut = 0;
+ }
+ if (totalSampleCountOut) {
+ *totalSampleCountOut = 0;
+ }
+
+ pResult = drflac_open_memory_and_read_pcm_frames_f32(data, dataSize, &channels, &sampleRate, &totalPCMFrameCount);
+ if (pResult == NULL) {
+ return NULL;
+ }
+
+ if (channelsOut) {
+ *channelsOut = channels;
+ }
+ if (sampleRateOut) {
+ *sampleRateOut = sampleRate;
+ }
+ if (totalSampleCountOut) {
+ *totalSampleCountOut = totalPCMFrameCount * channels;
+ }
+
+ return pResult;
+}
+
+
void drflac_free(void* pSampleDataReturnedByOpenAndDecode)
{
DRFLAC_FREE(pSampleDataReturnedByOpenAndDecode);
@@ -5687,208 +8556,347 @@ void drflac_free(void* pSampleDataReturnedByOpenAndDecode)
-void drflac_init_vorbis_comment_iterator(drflac_vorbis_comment_iterator* pIter, drflac_uint32 commentCount, const char* pComments)
+void drflac_init_vorbis_comment_iterator(drflac_vorbis_comment_iterator* pIter, drflac_uint32 commentCount, const void* pComments)
{
if (pIter == NULL) {
return;
}
pIter->countRemaining = commentCount;
- pIter->pRunningData = pComments;
+ pIter->pRunningData = (const char*)pComments;
}
const char* drflac_next_vorbis_comment(drflac_vorbis_comment_iterator* pIter, drflac_uint32* pCommentLengthOut)
{
- // Safety.
- if (pCommentLengthOut) *pCommentLengthOut = 0;
+ drflac_int32 length;
+ const char* pComment;
+
+ /* Safety. */
+ if (pCommentLengthOut) {
+ *pCommentLengthOut = 0;
+ }
if (pIter == NULL || pIter->countRemaining == 0 || pIter->pRunningData == NULL) {
return NULL;
}
- drflac_uint32 length = drflac__le2host_32(*(drflac_uint32*)pIter->pRunningData);
+ length = drflac__le2host_32(*(const drflac_uint32*)pIter->pRunningData);
pIter->pRunningData += 4;
- const char* pComment = pIter->pRunningData;
+ pComment = pIter->pRunningData;
pIter->pRunningData += length;
pIter->countRemaining -= 1;
- if (pCommentLengthOut) *pCommentLengthOut = length;
+ if (pCommentLengthOut) {
+ *pCommentLengthOut = length;
+ }
+
return pComment;
}
-#endif //DR_FLAC_IMPLEMENTATION
-
-
-// REVISION HISTORY
-//
-// v0.9.7 - 2018-07-05
-// - Fix a warning.
-//
-// v0.9.6 - 2018-06-29
-// - Fix some typos.
-//
-// v0.9.5 - 2018-06-23
-// - Fix some warnings.
-//
-// v0.9.4 - 2018-06-14
-// - Optimizations to seeking.
-// - Clean up.
-//
-// v0.9.3 - 2018-05-22
-// - Bug fix.
-//
-// v0.9.2 - 2018-05-12
-// - Fix a compilation error due to a missing break statement.
-//
-// v0.9.1 - 2018-04-29
-// - Fix compilation error with Clang.
-//
-// v0.9 - 2018-04-24
-// - Fix Clang build.
-// - Start using major.minor.revision versioning.
-//
-// v0.8g - 2018-04-19
-// - Fix build on non-x86/x64 architectures.
-//
-// v0.8f - 2018-02-02
-// - Stop pretending to support changing rate/channels mid stream.
-//
-// v0.8e - 2018-02-01
-// - Fix a crash when the block size of a frame is larger than the maximum block size defined by the FLAC stream.
-// - Fix a crash the the Rice partition order is invalid.
-//
-// v0.8d - 2017-09-22
-// - Add support for decoding streams with ID3 tags. ID3 tags are just skipped.
-//
-// v0.8c - 2017-09-07
-// - Fix warning on non-x86/x64 architectures.
-//
-// v0.8b - 2017-08-19
-// - Fix build on non-x86/x64 architectures.
-//
-// v0.8a - 2017-08-13
-// - A small optimization for the Clang build.
-//
-// v0.8 - 2017-08-12
-// - API CHANGE: Rename dr_* types to drflac_*.
-// - Optimizations. This brings dr_flac back to about the same class of efficiency as the reference implementation.
-// - Add support for custom implementations of malloc(), realloc(), etc.
-// - Add CRC checking to Ogg encapsulated streams.
-// - Fix VC++ 6 build. This is only for the C++ compiler. The C compiler is not currently supported.
-// - Bug fixes.
-//
-// v0.7 - 2017-07-23
-// - Add support for opening a stream without a header block. To do this, use drflac_open_relaxed() / drflac_open_with_metadata_relaxed().
-//
-// v0.6 - 2017-07-22
-// - Add support for recovering from invalid frames. With this change, dr_flac will simply skip over invalid frames as if they
-// never existed. Frames are checked against their sync code, the CRC-8 of the frame header and the CRC-16 of the whole frame.
-//
-// v0.5 - 2017-07-16
-// - Fix typos.
-// - Change drflac_bool* types to unsigned.
-// - Add CRC checking. This makes dr_flac slower, but can be disabled with #define DR_FLAC_NO_CRC.
-//
-// v0.4f - 2017-03-10
-// - Fix a couple of bugs with the bitstreaming code.
-//
-// v0.4e - 2017-02-17
-// - Fix some warnings.
-//
-// v0.4d - 2016-12-26
-// - Add support for 32-bit floating-point PCM decoding.
-// - Use drflac_int*/drflac_uint* sized types to improve compiler support.
-// - Minor improvements to documentation.
-//
-// v0.4c - 2016-12-26
-// - Add support for signed 16-bit integer PCM decoding.
-//
-// v0.4b - 2016-10-23
-// - A minor change to drflac_bool8 and drflac_bool32 types.
-//
-// v0.4a - 2016-10-11
-// - Rename drBool32 to drflac_bool32 for styling consistency.
-//
-// v0.4 - 2016-09-29
-// - API/ABI CHANGE: Use fixed size 32-bit booleans instead of the built-in bool type.
-// - API CHANGE: Rename drflac_open_and_decode*() to drflac_open_and_decode*_s32().
-// - API CHANGE: Swap the order of "channels" and "sampleRate" parameters in drflac_open_and_decode*(). Rationale for this is to
-// keep it consistent with drflac_audio.
-//
-// v0.3f - 2016-09-21
-// - Fix a warning with GCC.
-//
-// v0.3e - 2016-09-18
-// - Fixed a bug where GCC 4.3+ was not getting properly identified.
-// - Fixed a few typos.
-// - Changed date formats to ISO 8601 (YYYY-MM-DD).
-//
-// v0.3d - 2016-06-11
-// - Minor clean up.
-//
-// v0.3c - 2016-05-28
-// - Fixed compilation error.
-//
-// v0.3b - 2016-05-16
-// - Fixed Linux/GCC build.
-// - Updated documentation.
-//
-// v0.3a - 2016-05-15
-// - Minor fixes to documentation.
-//
-// v0.3 - 2016-05-11
-// - Optimizations. Now at about parity with the reference implementation on 32-bit builds.
-// - Lots of clean up.
-//
-// v0.2b - 2016-05-10
-// - Bug fixes.
-//
-// v0.2a - 2016-05-10
-// - Made drflac_open_and_decode() more robust.
-// - Removed an unused debugging variable
-//
-// v0.2 - 2016-05-09
-// - Added support for Ogg encapsulation.
-// - API CHANGE. Have the onSeek callback take a third argument which specifies whether or not the seek
-// should be relative to the start or the current position. Also changes the seeking rules such that
-// seeking offsets will never be negative.
-// - Have drflac_open_and_decode() fail gracefully if the stream has an unknown total sample count.
-//
-// v0.1b - 2016-05-07
-// - Properly close the file handle in drflac_open_file() and family when the decoder fails to initialize.
-// - Removed a stale comment.
-//
-// v0.1a - 2016-05-05
-// - Minor formatting changes.
-// - Fixed a warning on the GCC build.
-//
-// v0.1 - 2016-05-03
-// - Initial versioned release.
+
+
+
+
+void drflac_init_cuesheet_track_iterator(drflac_cuesheet_track_iterator* pIter, drflac_uint32 trackCount, const void* pTrackData)
+{
+ if (pIter == NULL) {
+ return;
+ }
+
+ pIter->countRemaining = trackCount;
+ pIter->pRunningData = (const char*)pTrackData;
+}
+
+drflac_bool32 drflac_next_cuesheet_track(drflac_cuesheet_track_iterator* pIter, drflac_cuesheet_track* pCuesheetTrack)
+{
+ drflac_cuesheet_track cuesheetTrack;
+ const char* pRunningData;
+ drflac_uint64 offsetHi;
+ drflac_uint64 offsetLo;
+
+ if (pIter == NULL || pIter->countRemaining == 0 || pIter->pRunningData == NULL) {
+ return DRFLAC_FALSE;
+ }
+
+ pRunningData = pIter->pRunningData;
+
+ offsetHi = drflac__be2host_32(*(const drflac_uint32*)pRunningData); pRunningData += 4;
+ offsetLo = drflac__be2host_32(*(const drflac_uint32*)pRunningData); pRunningData += 4;
+ cuesheetTrack.offset = offsetLo | (offsetHi << 32);
+ cuesheetTrack.trackNumber = pRunningData[0]; pRunningData += 1;
+ drflac_copy_memory(cuesheetTrack.ISRC, pRunningData, sizeof(cuesheetTrack.ISRC)); pRunningData += 12;
+ cuesheetTrack.isAudio = (pRunningData[0] & 0x80) != 0;
+ cuesheetTrack.preEmphasis = (pRunningData[0] & 0x40) != 0; pRunningData += 14;
+ cuesheetTrack.indexCount = pRunningData[0]; pRunningData += 1;
+ cuesheetTrack.pIndexPoints = (const drflac_cuesheet_track_index*)pRunningData; pRunningData += cuesheetTrack.indexCount * sizeof(drflac_cuesheet_track_index);
+
+ pIter->pRunningData = pRunningData;
+ pIter->countRemaining -= 1;
+
+ if (pCuesheetTrack) {
+ *pCuesheetTrack = cuesheetTrack;
+ }
+
+ return DRFLAC_TRUE;
+}
+
+#if defined(__GNUC__)
+ #pragma GCC diagnostic pop
+#endif
+#endif /* DR_FLAC_IMPLEMENTATION */
/*
+REVISION HISTORY
+================
+v0.11.7 - 2019-05-06
+ - C89 fixes.
+
+v0.11.6 - 2019-05-05
+ - Add support for C89.
+ - Fix a compiler warning when CRC is disabled.
+ - Change license to choice of public domain or MIT-0.
+
+v0.11.5 - 2019-04-19
+ - Fix a compiler error with GCC.
+
+v0.11.4 - 2019-04-17
+ - Fix some warnings with GCC when compiling with -std=c99.
+
+v0.11.3 - 2019-04-07
+ - Silence warnings with GCC.
+
+v0.11.2 - 2019-03-10
+ - Fix a warning.
+
+v0.11.1 - 2019-02-17
+ - Fix a potential bug with seeking.
+
+v0.11.0 - 2018-12-16
+ - API CHANGE: Deprecated drflac_read_s32(), drflac_read_s16() and drflac_read_f32() and replaced them with
+ drflac_read_pcm_frames_s32(), drflac_read_pcm_frames_s16() and drflac_read_pcm_frames_f32(). The new APIs take
+ and return PCM frame counts instead of sample counts. To upgrade you will need to change the input count by
+ dividing it by the channel count, and then do the same with the return value.
+ - API_CHANGE: Deprecated drflac_seek_to_sample() and replaced with drflac_seek_to_pcm_frame(). Same rules as
+ the changes to drflac_read_*() apply.
+ - API CHANGE: Deprecated drflac_open_and_decode_*() and replaced with drflac_open_*_and_read_*(). Same rules as
+ the changes to drflac_read_*() apply.
+ - Optimizations.
+
+v0.10.0 - 2018-09-11
+ - Remove the DR_FLAC_NO_WIN32_IO option and the Win32 file IO functionality. If you need to use Win32 file IO you
+ need to do it yourself via the callback API.
+ - Fix the clang build.
+ - Fix undefined behavior.
+ - Fix errors with CUESHEET metdata blocks.
+ - Add an API for iterating over each cuesheet track in the CUESHEET metadata block. This works the same way as the
+ Vorbis comment API.
+ - Other miscellaneous bug fixes, mostly relating to invalid FLAC streams.
+ - Minor optimizations.
+
+v0.9.11 - 2018-08-29
+ - Fix a bug with sample reconstruction.
+
+v0.9.10 - 2018-08-07
+ - Improve 64-bit detection.
+
+v0.9.9 - 2018-08-05
+ - Fix C++ build on older versions of GCC.
+
+v0.9.8 - 2018-07-24
+ - Fix compilation errors.
+
+v0.9.7 - 2018-07-05
+ - Fix a warning.
+
+v0.9.6 - 2018-06-29
+ - Fix some typos.
+
+v0.9.5 - 2018-06-23
+ - Fix some warnings.
+
+v0.9.4 - 2018-06-14
+ - Optimizations to seeking.
+ - Clean up.
+
+v0.9.3 - 2018-05-22
+ - Bug fix.
+
+v0.9.2 - 2018-05-12
+ - Fix a compilation error due to a missing break statement.
+
+v0.9.1 - 2018-04-29
+ - Fix compilation error with Clang.
+
+v0.9 - 2018-04-24
+ - Fix Clang build.
+ - Start using major.minor.revision versioning.
+
+v0.8g - 2018-04-19
+ - Fix build on non-x86/x64 architectures.
+
+v0.8f - 2018-02-02
+ - Stop pretending to support changing rate/channels mid stream.
+
+v0.8e - 2018-02-01
+ - Fix a crash when the block size of a frame is larger than the maximum block size defined by the FLAC stream.
+ - Fix a crash the the Rice partition order is invalid.
+
+v0.8d - 2017-09-22
+ - Add support for decoding streams with ID3 tags. ID3 tags are just skipped.
+
+v0.8c - 2017-09-07
+ - Fix warning on non-x86/x64 architectures.
+
+v0.8b - 2017-08-19
+ - Fix build on non-x86/x64 architectures.
+
+v0.8a - 2017-08-13
+ - A small optimization for the Clang build.
+
+v0.8 - 2017-08-12
+ - API CHANGE: Rename dr_* types to drflac_*.
+ - Optimizations. This brings dr_flac back to about the same class of efficiency as the reference implementation.
+ - Add support for custom implementations of malloc(), realloc(), etc.
+ - Add CRC checking to Ogg encapsulated streams.
+ - Fix VC++ 6 build. This is only for the C++ compiler. The C compiler is not currently supported.
+ - Bug fixes.
+
+v0.7 - 2017-07-23
+ - Add support for opening a stream without a header block. To do this, use drflac_open_relaxed() / drflac_open_with_metadata_relaxed().
+
+v0.6 - 2017-07-22
+ - Add support for recovering from invalid frames. With this change, dr_flac will simply skip over invalid frames as if they
+ never existed. Frames are checked against their sync code, the CRC-8 of the frame header and the CRC-16 of the whole frame.
+
+v0.5 - 2017-07-16
+ - Fix typos.
+ - Change drflac_bool* types to unsigned.
+ - Add CRC checking. This makes dr_flac slower, but can be disabled with #define DR_FLAC_NO_CRC.
+
+v0.4f - 2017-03-10
+ - Fix a couple of bugs with the bitstreaming code.
+
+v0.4e - 2017-02-17
+ - Fix some warnings.
+
+v0.4d - 2016-12-26
+ - Add support for 32-bit floating-point PCM decoding.
+ - Use drflac_int* and drflac_uint* sized types to improve compiler support.
+ - Minor improvements to documentation.
+
+v0.4c - 2016-12-26
+ - Add support for signed 16-bit integer PCM decoding.
+
+v0.4b - 2016-10-23
+ - A minor change to drflac_bool8 and drflac_bool32 types.
+
+v0.4a - 2016-10-11
+ - Rename drBool32 to drflac_bool32 for styling consistency.
+
+v0.4 - 2016-09-29
+ - API/ABI CHANGE: Use fixed size 32-bit booleans instead of the built-in bool type.
+ - API CHANGE: Rename drflac_open_and_decode*() to drflac_open_and_decode*_s32().
+ - API CHANGE: Swap the order of "channels" and "sampleRate" parameters in drflac_open_and_decode*(). Rationale for this is to
+ keep it consistent with drflac_audio.
+
+v0.3f - 2016-09-21
+ - Fix a warning with GCC.
+
+v0.3e - 2016-09-18
+ - Fixed a bug where GCC 4.3+ was not getting properly identified.
+ - Fixed a few typos.
+ - Changed date formats to ISO 8601 (YYYY-MM-DD).
+
+v0.3d - 2016-06-11
+ - Minor clean up.
+
+v0.3c - 2016-05-28
+ - Fixed compilation error.
+
+v0.3b - 2016-05-16
+ - Fixed Linux/GCC build.
+ - Updated documentation.
+
+v0.3a - 2016-05-15
+ - Minor fixes to documentation.
+
+v0.3 - 2016-05-11
+ - Optimizations. Now at about parity with the reference implementation on 32-bit builds.
+ - Lots of clean up.
+
+v0.2b - 2016-05-10
+ - Bug fixes.
+
+v0.2a - 2016-05-10
+ - Made drflac_open_and_decode() more robust.
+ - Removed an unused debugging variable
+
+v0.2 - 2016-05-09
+ - Added support for Ogg encapsulation.
+ - API CHANGE. Have the onSeek callback take a third argument which specifies whether or not the seek
+ should be relative to the start or the current position. Also changes the seeking rules such that
+ seeking offsets will never be negative.
+ - Have drflac_open_and_decode() fail gracefully if the stream has an unknown total sample count.
+
+v0.1b - 2016-05-07
+ - Properly close the file handle in drflac_open_file() and family when the decoder fails to initialize.
+ - Removed a stale comment.
+
+v0.1a - 2016-05-05
+ - Minor formatting changes.
+ - Fixed a warning on the GCC build.
+
+v0.1 - 2016-05-03
+ - Initial versioned release.
+*/
+
+/*
+This software is available as a choice of the following licenses. Choose
+whichever you prefer.
+
+===============================================================================
+ALTERNATIVE 1 - Public Domain (www.unlicense.org)
+===============================================================================
This is free and unencumbered software released into the public domain.
-Anyone is free to copy, modify, publish, use, compile, sell, or
-distribute this software, either in source code form or as a compiled
-binary, for any purpose, commercial or non-commercial, and by any
-means.
-
-In jurisdictions that recognize copyright laws, the author or authors
-of this software dedicate any and all copyright interest in the
-software to the public domain. We make this dedication for the benefit
-of the public at large and to the detriment of our heirs and
-successors. We intend this dedication to be an overt act of
-relinquishment in perpetuity of all present and future rights to this
-software under copyright law.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
-EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
-MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
-IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
-OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
-ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
-OTHER DEALINGS IN THE SOFTWARE.
+Anyone is free to copy, modify, publish, use, compile, sell, or distribute this
+software, either in source code form or as a compiled binary, for any purpose,
+commercial or non-commercial, and by any means.
+
+In jurisdictions that recognize copyright laws, the author or authors of this
+software dedicate any and all copyright interest in the software to the public
+domain. We make this dedication for the benefit of the public at large and to
+the detriment of our heirs and successors. We intend this dedication to be an
+overt act of relinquishment in perpetuity of all present and future rights to
+this software under copyright law.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
For more information, please refer to <http://unlicense.org/>
+
+===============================================================================
+ALTERNATIVE 2 - MIT No Attribution
+===============================================================================
+Copyright 2018 David Reid
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of
+this software and associated documentation files (the "Software"), to deal in
+the Software without restriction, including without limitation the rights to
+use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+of the Software, and to permit persons to whom the Software is furnished to do
+so.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
*/
diff --git a/src/external/dr_mp3.h b/src/external/dr_mp3.h
index 070f0c15..26aeec56 100644
--- a/src/external/dr_mp3.h
+++ b/src/external/dr_mp3.h
@@ -1,57 +1,61 @@
-// MP3 audio decoder. Public domain. See "unlicense" statement at the end of this file.
-// dr_mp3 - v0.4.0 - 2018-xx-xx
-//
-// David Reid - mackron@gmail.com
-//
-// Based off minimp3 (https://github.com/lieff/minimp3) which is where the real work was done. See the bottom of this file for
-// differences between minimp3 and dr_mp3.
-
-// USAGE
-// =====
-// dr_mp3 is a single-file library. To use it, do something like the following in one .c file.
-// #define DR_MP3_IMPLEMENTATION
-// #include "dr_mp3.h"
-//
-// You can then #include this file in other parts of the program as you would with any other header file. To decode audio data,
-// do something like the following:
-//
-// drmp3 mp3;
-// if (!drmp3_init_file(&mp3, "MySong.mp3", NULL)) {
-// // Failed to open file
-// }
-//
-// ...
-//
-// drmp3_uint64 framesRead = drmp3_read_f32(pMP3, framesToRead, pFrames);
-//
-// The drmp3 object is transparent so you can get access to the channel count and sample rate like so:
-//
-// drmp3_uint32 channels = mp3.channels;
-// drmp3_uint32 sampleRate = mp3.sampleRate;
-//
-// The third parameter of drmp3_init_file() in the example above allows you to control the output channel count and sample rate. It
-// is a pointer to a drmp3_config object. Setting any of the variables of this object to 0 will cause dr_mp3 to use defaults.
-//
-// The example above initializes a decoder from a file, but you can also initialize it from a block of memory and read and seek
-// callbacks with drmp3_init_memory() and drmp3_init() respectively.
-//
-// You do need to do any annoying memory management when reading PCM frames - this is all managed internally. You can request
-// any number of PCM frames in each call to drmp3_read_f32() and it will return as many PCM frames as it can, up to the requested
-// amount.
-//
-// You can also decode an entire file in one go with drmp3_open_and_decode_f32(), drmp3_open_and_decode_memory_f32() and
-// drmp3_open_and_decode_file_f32().
-//
-//
-// OPTIONS
-// =======
-// #define these options before including this file.
-//
-// #define DR_MP3_NO_STDIO
-// Disable drmp3_init_file(), etc.
-//
-// #define DR_MP3_NO_SIMD
-// Disable SIMD optimizations.
+/*
+MP3 audio decoder. Choice of public domain or MIT-0. See license statements at the end of this file.
+dr_mp3 - v0.4.4 - 2019-05-06
+
+David Reid - mackron@gmail.com
+
+Based off minimp3 (https://github.com/lieff/minimp3) which is where the real work was done. See the bottom of this file for
+differences between minimp3 and dr_mp3.
+*/
+
+/*
+USAGE
+=====
+dr_mp3 is a single-file library. To use it, do something like the following in one .c file.
+ #define DR_MP3_IMPLEMENTATION
+ #include "dr_mp3.h"
+
+You can then #include this file in other parts of the program as you would with any other header file. To decode audio data,
+do something like the following:
+
+ drmp3 mp3;
+ if (!drmp3_init_file(&mp3, "MySong.mp3", NULL)) {
+ // Failed to open file
+ }
+
+ ...
+
+ drmp3_uint64 framesRead = drmp3_read_pcm_frames_f32(pMP3, framesToRead, pFrames);
+
+The drmp3 object is transparent so you can get access to the channel count and sample rate like so:
+
+ drmp3_uint32 channels = mp3.channels;
+ drmp3_uint32 sampleRate = mp3.sampleRate;
+
+The third parameter of drmp3_init_file() in the example above allows you to control the output channel count and sample rate. It
+is a pointer to a drmp3_config object. Setting any of the variables of this object to 0 will cause dr_mp3 to use defaults.
+
+The example above initializes a decoder from a file, but you can also initialize it from a block of memory and read and seek
+callbacks with drmp3_init_memory() and drmp3_init() respectively.
+
+You do not need to do any annoying memory management when reading PCM frames - this is all managed internally. You can request
+any number of PCM frames in each call to drmp3_read_pcm_frames_f32() and it will return as many PCM frames as it can, up to the
+requested amount.
+
+You can also decode an entire file in one go with drmp3_open_and_read_f32(), drmp3_open_memory_and_read_f32() and
+drmp3_open_file_and_read_f32().
+
+
+OPTIONS
+=======
+#define these options before including this file.
+
+#define DR_MP3_NO_STDIO
+ Disable drmp3_init_file(), etc.
+
+#define DR_MP3_NO_SIMD
+ Disable SIMD optimizations.
+*/
#ifndef dr_mp3_h
#define dr_mp3_h
@@ -90,9 +94,20 @@ typedef drmp3_uint32 drmp3_bool32;
#define DRMP3_MAX_PCM_FRAMES_PER_MP3_FRAME 1152
#define DRMP3_MAX_SAMPLES_PER_FRAME (DRMP3_MAX_PCM_FRAMES_PER_MP3_FRAME*2)
+#ifdef _MSC_VER
+#define DRMP3_INLINE __forceinline
+#else
+#ifdef __GNUC__
+#define DRMP3_INLINE __inline__ __attribute__((always_inline))
+#else
+#define DRMP3_INLINE
+#endif
+#endif
-// Low Level Push API
-// ==================
+/*
+Low Level Push API
+==================
+*/
typedef struct
{
int frame_bytes, channels, hz, layer, bitrate_kbps;
@@ -105,23 +120,30 @@ typedef struct
unsigned char header[4], reserv_buf[511];
} drmp3dec;
-// Initializes a low level decoder.
+/* Initializes a low level decoder. */
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);
-// 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);
-
-// Main API (Pull API)
-// ===================
+/*
+Main API (Pull API)
+===================
+*/
+#ifndef DR_MP3_DEFAULT_CHANNELS
+#define DR_MP3_DEFAULT_CHANNELS 2
+#endif
+#ifndef DR_MP3_DEFAULT_SAMPLE_RATE
+#define DR_MP3_DEFAULT_SAMPLE_RATE 44100
+#endif
typedef struct drmp3_src drmp3_src;
-typedef drmp3_uint64 (* drmp3_src_read_proc)(drmp3_src* pSRC, drmp3_uint64 frameCount, void* pFramesOut, void* pUserData); // Returns the number of frames that were read.
+typedef 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
{
@@ -144,7 +166,7 @@ typedef struct
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_uint32 cacheSizeInFrames; /* The number of frames to read from the client at a time. */
} drmp3_src_config;
struct drmp3_src
@@ -153,12 +175,12 @@ struct drmp3_src
drmp3_src_read_proc onRead;
void* pUserData;
float bin[256];
- drmp3_src_cache cache; // <-- For simplifying and optimizing client -> memory reading.
+ drmp3_src_cache cache; /* <-- For simplifying and optimizing client -> memory reading. */
union
{
struct
{
- float alpha;
+ double alpha;
drmp3_bool32 isPrevFramesLoaded : 1;
drmp3_bool32 isNextFramesLoaded : 1;
} linear;
@@ -171,28 +193,40 @@ typedef enum
drmp3_seek_origin_current
} drmp3_seek_origin;
-// Callback for when data is read. Return value is the number of bytes actually read.
-//
-// pUserData [in] The user data that was passed to drmp3_init(), drmp3_open() and family.
-// pBufferOut [out] The output buffer.
-// bytesToRead [in] The number of bytes to read.
-//
-// Returns the number of bytes actually read.
-//
-// A return value of less than bytesToRead indicates the end of the stream. Do _not_ return from this callback until
-// either the entire bytesToRead is filled or you have reached the end of the stream.
+typedef struct
+{
+ drmp3_uint64 seekPosInBytes; /* Points to the first byte of an MP3 frame. */
+ drmp3_uint64 pcmFrameIndex; /* The index of the PCM frame this seek point targets. */
+ drmp3_uint16 mp3FramesToDiscard; /* The number of whole MP3 frames to be discarded before pcmFramesToDiscard. */
+ drmp3_uint16 pcmFramesToDiscard; /* The number of leading samples to read and discard. These are discarded after mp3FramesToDiscard. */
+} drmp3_seek_point;
+
+/*
+Callback for when data is read. Return value is the number of bytes actually read.
+
+pUserData [in] The user data that was passed to drmp3_init(), drmp3_open() and family.
+pBufferOut [out] The output buffer.
+bytesToRead [in] The number of bytes to read.
+
+Returns the number of bytes actually read.
+
+A return value of less than bytesToRead indicates the end of the stream. Do _not_ return from this callback until
+either the entire bytesToRead is filled or you have reached the end of the stream.
+*/
typedef size_t (* drmp3_read_proc)(void* pUserData, void* pBufferOut, size_t bytesToRead);
-// Callback for when data needs to be seeked.
-//
-// pUserData [in] The user data that was passed to drmp3_init(), drmp3_open() and family.
-// offset [in] The number of bytes to move, relative to the origin. Will never be negative.
-// origin [in] The origin of the seek - the current position or the start of the stream.
-//
-// Returns whether or not the seek was successful.
-//
-// Whether or not it is relative to the beginning or current position is determined by the "origin" parameter which
-// will be either drmp3_seek_origin_start or drmp3_seek_origin_current.
+/*
+Callback for when data needs to be seeked.
+
+pUserData [in] The user data that was passed to drmp3_init(), drmp3_open() and family.
+offset [in] The number of bytes to move, relative to the origin. Will never be negative.
+origin [in] The origin of the seek - the current position or the start of the stream.
+
+Returns whether or not the seek was successful.
+
+Whether or not it is relative to the beginning or current position is determined by the "origin" parameter which
+will be either drmp3_seek_origin_start or drmp3_seek_origin_current.
+*/
typedef drmp3_bool32 (* drmp3_seek_proc)(void* pUserData, int offset, drmp3_seek_origin origin);
typedef struct
@@ -210,13 +244,16 @@ typedef struct
drmp3_read_proc onRead;
drmp3_seek_proc onSeek;
void* pUserData;
- drmp3_uint32 mp3FrameChannels; // The number of channels in the currently loaded MP3 frame. Internal use only.
- drmp3_uint32 mp3FrameSampleRate; // The sample rate of the currently loaded MP3 frame. Internal use only.
+ drmp3_uint32 mp3FrameChannels; /* The number of channels in the currently loaded MP3 frame. Internal use only. */
+ drmp3_uint32 mp3FrameSampleRate; /* The sample rate of the currently loaded MP3 frame. Internal use only. */
drmp3_uint32 pcmFramesConsumedInMP3Frame;
drmp3_uint32 pcmFramesRemainingInMP3Frame;
- drmp3_uint8 pcmFrames[sizeof(float)*DRMP3_MAX_SAMPLES_PER_FRAME]; // <-- Multipled by sizeof(float) to ensure there's enough room for DR_MP3_FLOAT_OUTPUT.
- drmp3_uint64 currentPCMFrame; // The current PCM frame, globally, based on the output sample rate. Mainly used for seeking.
+ drmp3_uint8 pcmFrames[sizeof(float)*DRMP3_MAX_SAMPLES_PER_FRAME]; /* <-- Multipled by sizeof(float) to ensure there's enough room for DR_MP3_FLOAT_OUTPUT. */
+ drmp3_uint64 currentPCMFrame; /* The current PCM frame, globally, based on the output sample rate. Mainly used for seeking. */
+ drmp3_uint64 streamCursor; /* The current byte the decoder is sitting on in the raw stream. */
drmp3_src src;
+ drmp3_seek_point* pSeekPoints; /* NULL by default. Set with drmp3_bind_seek_table(). Memory is owned by the client. dr_mp3 will never attempt to free this pointer. */
+ drmp3_uint32 seekPointCount; /* The number of items in pSeekPoints. When set to 0 assumes to no seek table. Defaults to zero. */
size_t dataSize;
size_t dataCapacity;
drmp3_uint8* pData;
@@ -226,94 +263,155 @@ typedef struct
const drmp3_uint8* pData;
size_t dataSize;
size_t currentReadPos;
- } memory; // Only used for decoders that were opened against a block of memory.
+ } memory; /* Only used for decoders that were opened against a block of memory. */
} drmp3;
-// Initializes an MP3 decoder.
-//
-// onRead [in] The function to call when data needs to be read from the client.
-// onSeek [in] The function to call when the read position of the client data needs to move.
-// pUserData [in, optional] A pointer to application defined data that will be passed to onRead and onSeek.
-//
-// Returns true if successful; false otherwise.
-//
-// Close the loader with drmp3_uninit().
-//
-// See also: drmp3_init_file(), drmp3_init_memory(), drmp3_uninit()
+/*
+Initializes an MP3 decoder.
+
+onRead [in] The function to call when data needs to be read from the client.
+onSeek [in] The function to call when the read position of the client data needs to move.
+pUserData [in, optional] A pointer to application defined data that will be passed to onRead and onSeek.
+
+Returns true if successful; false otherwise.
+
+Close the loader with drmp3_uninit().
+
+See also: drmp3_init_file(), drmp3_init_memory(), drmp3_uninit()
+*/
drmp3_bool32 drmp3_init(drmp3* pMP3, drmp3_read_proc onRead, drmp3_seek_proc onSeek, void* pUserData, const drmp3_config* pConfig);
-// Initializes an MP3 decoder from a block of memory.
-//
-// This does not create a copy of the data. It is up to the application to ensure the buffer remains valid for
-// the lifetime of the drmp3 object.
-//
-// The buffer should contain the contents of the entire MP3 file.
+/*
+Initializes an MP3 decoder from a block of memory.
+
+This does not create a copy of the data. It is up to the application to ensure the buffer remains valid for
+the lifetime of the drmp3 object.
+
+The buffer should contain the contents of the entire MP3 file.
+*/
drmp3_bool32 drmp3_init_memory(drmp3* pMP3, const void* pData, size_t dataSize, const drmp3_config* pConfig);
#ifndef DR_MP3_NO_STDIO
-// Initializes an MP3 decoder from a file.
-//
-// This holds the internal FILE object until drmp3_uninit() is called. Keep this in mind if you're caching drmp3
-// objects because the operating system may restrict the number of file handles an application can have open at
-// any given time.
+/*
+Initializes an MP3 decoder from a file.
+
+This holds the internal FILE object until drmp3_uninit() is called. Keep this in mind if you're caching drmp3
+objects because the operating system may restrict the number of file handles an application can have open at
+any given time.
+*/
drmp3_bool32 drmp3_init_file(drmp3* pMP3, const char* filePath, const drmp3_config* pConfig);
#endif
-// Uninitializes an MP3 decoder.
+/*
+Uninitializes an MP3 decoder.
+*/
void drmp3_uninit(drmp3* pMP3);
-// Reads PCM frames as interleaved 32-bit IEEE floating point PCM.
-//
-// Note that framesToRead specifies the number of PCM frames to read, _not_ the number of MP3 frames.
+/*
+Reads PCM frames as interleaved 32-bit IEEE floating point PCM.
+
+Note that framesToRead specifies the number of PCM frames to read, _not_ the number of MP3 frames.
+*/
drmp3_uint64 drmp3_read_pcm_frames_f32(drmp3* pMP3, drmp3_uint64 framesToRead, float* pBufferOut);
-// Seeks to a specific frame.
-//
-// Note that this is _not_ an MP3 frame, but rather a PCM frame.
+/*
+Reads PCM frames as interleaved signed 16-bit integer PCM.
+
+Note that framesToRead specifies the number of PCM frames to read, _not_ the number of MP3 frames.
+*/
+drmp3_uint64 drmp3_read_pcm_frames_s16(drmp3* pMP3, drmp3_uint64 framesToRead, drmp3_int16* pBufferOut);
+
+/*
+Seeks to a specific frame.
+
+Note that this is _not_ an MP3 frame, but rather a PCM frame.
+*/
drmp3_bool32 drmp3_seek_to_pcm_frame(drmp3* pMP3, drmp3_uint64 frameIndex);
-// 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.
+/*
+Calculates the total number of PCM frames in the MP3 stream. Cannot be used for infinite streams such as internet
+radio. Runs in linear time. Returns 0 on error.
+*/
drmp3_uint64 drmp3_get_pcm_frame_count(drmp3* pMP3);
-// 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.
+/*
+Calculates the total number of MP3 frames in the MP3 stream. Cannot be used for infinite streams such as internet
+radio. Runs in linear time. Returns 0 on error.
+*/
drmp3_uint64 drmp3_get_mp3_frame_count(drmp3* pMP3);
+/*
+Calculates the total number of MP3 and PCM frames in the MP3 stream. Cannot be used for infinite streams such as internet
+radio. Runs in linear time. Returns 0 on error.
+This is equivalent to calling drmp3_get_mp3_frame_count() and drmp3_get_pcm_frame_count() except that it's more efficient.
+*/
+drmp3_bool32 drmp3_get_mp3_and_pcm_frame_count(drmp3* pMP3, drmp3_uint64* pMP3FrameCount, drmp3_uint64* pPCMFrameCount);
+
+/*
+Calculates the seekpoints based on PCM frames. This is slow.
+
+pSeekpoint count is a pointer to a uint32 containing the seekpoint count. On input it contains the desired count.
+On output it contains the actual count. The reason for this design is that the client may request too many
+seekpoints, in which case dr_mp3 will return a corrected count.
+
+Note that seektable seeking is not quite sample exact when the MP3 stream contains inconsistent sample rates.
+*/
+drmp3_bool32 drmp3_calculate_seek_points(drmp3* pMP3, drmp3_uint32* pSeekPointCount, drmp3_seek_point* pSeekPoints);
+
+/*
+Binds a seek table to the decoder.
+
+This does _not_ make a copy of pSeekPoints - it only references it. It is up to the application to ensure this
+remains valid while it is bound to the decoder.
+
+Use drmp3_calculate_seek_points() to calculate the seek points.
+*/
+drmp3_bool32 drmp3_bind_seek_table(drmp3* pMP3, drmp3_uint32 seekPointCount, drmp3_seek_point* pSeekPoints);
+
+
+/*
+Opens an decodes an entire MP3 stream as a single operation.
+
+pConfig is both an input and output. On input it contains what you want. On output it contains what you got.
-// 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.
-//
-// Free the returned pointer with drmp3_free().
+Free the returned pointer with drmp3_free().
+*/
float* drmp3_open_and_read_f32(drmp3_read_proc onRead, drmp3_seek_proc onSeek, void* pUserData, drmp3_config* pConfig, drmp3_uint64* pTotalFrameCount);
+drmp3_int16* drmp3_open_and_read_s16(drmp3_read_proc onRead, drmp3_seek_proc onSeek, void* pUserData, drmp3_config* pConfig, drmp3_uint64* pTotalFrameCount);
+
float* drmp3_open_memory_and_read_f32(const void* pData, size_t dataSize, drmp3_config* pConfig, drmp3_uint64* pTotalFrameCount);
+drmp3_int16* drmp3_open_memory_and_read_s16(const void* pData, size_t dataSize, drmp3_config* pConfig, drmp3_uint64* pTotalFrameCount);
+
#ifndef DR_MP3_NO_STDIO
float* drmp3_open_file_and_read_f32(const char* filePath, drmp3_config* pConfig, drmp3_uint64* pTotalFrameCount);
+drmp3_int16* drmp3_open_file_and_read_s16(const char* filePath, drmp3_config* pConfig, drmp3_uint64* pTotalFrameCount);
#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);
#ifdef __cplusplus
}
#endif
-#endif // dr_mp3_h
+#endif /* dr_mp3_h */
-/////////////////////////////////////////////////////
-//
-// IMPLEMENTATION
-//
-/////////////////////////////////////////////////////
+/************************************************************************************************************************************************************
+ ************************************************************************************************************************************************************
+
+ IMPLEMENTATION
+
+ ************************************************************************************************************************************************************
+ ************************************************************************************************************************************************************/
#ifdef DR_MP3_IMPLEMENTATION
#include <stdlib.h>
#include <string.h>
-#include <stdint.h>
-#include <limits.h> // For INT_MAX
+#include <limits.h> /* For INT_MAX */
-// Disable SIMD when compiling with TCC for now.
+/* Disable SIMD when compiling with TCC for now. */
#if defined(__TINYC__)
#define DR_MP3_NO_SIMD
#endif
@@ -365,7 +463,7 @@ void drmp3_free(void* p);
#define DR_MP3_ONLY_SIMD
#endif
-#if (defined(_MSC_VER) && (defined(_M_IX86) || defined(_M_X64))) || ((defined(__i386__) || defined(__x86_64__)) && defined(__SSE2__))
+#if ((defined(_MSC_VER) && _MSC_VER >= 1400) && (defined(_M_IX86) || defined(_M_X64))) || ((defined(__i386__) || defined(__x86_64__)) && defined(__SSE2__))
#if defined(_MSC_VER)
#include <intrin.h>
#endif
@@ -780,8 +878,8 @@ static int drmp3_L3_read_side_info(drmp3_bs *bs, drmp3_L3_gr_info *gr, const drm
unsigned tables, scfsi = 0;
int main_data_begin, part_23_sum = 0;
- int sr_idx = DRMP3_HDR_GET_MY_SAMPLE_RATE(hdr); sr_idx -= (sr_idx != 0);
int gr_count = DRMP3_HDR_IS_MONO(hdr) ? 1 : 2;
+ int sr_idx = DRMP3_HDR_GET_MY_SAMPLE_RATE(hdr); sr_idx -= (sr_idx != 0);
if (DRMP3_HDR_TEST_MPEG1(hdr))
{
@@ -1070,7 +1168,7 @@ static void drmp3_L3_huffman(float *dst, drmp3_bs *bs, const drmp3_L3_gr_info *g
lsb += DRMP3_PEEK_BITS(linbits);
DRMP3_FLUSH_BITS(linbits);
DRMP3_CHECK_BITS;
- *dst = one*drmp3_L3_pow_43(lsb)*((int32_t)bs_cache < 0 ? -1: 1);
+ *dst = one*drmp3_L3_pow_43(lsb)*((drmp3_int32)bs_cache < 0 ? -1: 1);
} else
{
*dst = g_drmp3_pow43[16 + lsb - 16*(bs_cache >> 31)]*one;
@@ -1654,9 +1752,10 @@ typedef drmp3_int16 drmp3d_sample_t;
static drmp3_int16 drmp3d_scale_pcm(float sample)
{
+ drmp3_int16 s;
if (sample >= 32766.5) return (drmp3_int16) 32767;
if (sample <= -32767.5) return (drmp3_int16)-32768;
- drmp3_int16 s = (drmp3_int16)(sample + .5f);
+ s = (drmp3_int16)(sample + .5f);
s -= (s < 0); /* away from zero, to be compliant */
return (drmp3_int16)s;
}
@@ -1964,11 +2063,6 @@ int drmp3dec_decode_frame(drmp3dec *dec, const unsigned char *mp3, int mp3_bytes
info->layer = 4 - DRMP3_HDR_GET_LAYER(hdr);
info->bitrate_kbps = drmp3_hdr_bitrate_kbps(hdr);
- if (!pcm)
- {
- return drmp3_hdr_frame_samples(hdr);
- }
-
drmp3_bs_init(bs_frame, hdr + DRMP3_HDR_SIZE, frame_size - DRMP3_HDR_SIZE);
if (DRMP3_HDR_IS_CRC(hdr))
{
@@ -1984,7 +2078,7 @@ int drmp3dec_decode_frame(drmp3dec *dec, const unsigned char *mp3, int mp3_bytes
return 0;
}
success = drmp3_L3_restore_reservoir(dec, bs_frame, &scratch, main_data_begin);
- if (success)
+ if (success && pcm != NULL)
{
for (igr = 0; igr < (DRMP3_HDR_TEST_MPEG1(hdr) ? 2 : 1); igr++, pcm = DRMP3_OFFSET_PTR(pcm, sizeof(drmp3d_sample_t)*576*info->channels))
{
@@ -2000,6 +2094,11 @@ int drmp3dec_decode_frame(drmp3dec *dec, const unsigned char *mp3, int mp3_bytes
return 0;
#else
drmp3_L12_scale_info sci[1];
+
+ if (pcm == NULL) {
+ return drmp3_hdr_frame_samples(hdr);
+ }
+
drmp3_L12_read_scale_info(hdr, bs_frame, sci);
memset(scratch.grbuf[0], 0, 576*2*sizeof(float));
@@ -2021,6 +2120,7 @@ int drmp3dec_decode_frame(drmp3dec *dec, const unsigned char *mp3, int mp3_bytes
}
#endif
}
+
return success*drmp3_hdr_frame_samples(dec->header);
}
@@ -2085,11 +2185,11 @@ void drmp3dec_f32_to_s16(const float *in, drmp3_int16 *out, int num_samples)
-///////////////////////////////////////////////////////////////////////////////
-//
-// Main Public API
-//
-///////////////////////////////////////////////////////////////////////////////
+/************************************************************************************************************************************************************
+
+ Main Public API
+
+ ************************************************************************************************************************************************************/
#if defined(SIZE_MAX)
#define DRMP3_SIZE_MAX SIZE_MAX
@@ -2101,16 +2201,13 @@ void drmp3dec_f32_to_s16(const float *in, drmp3_int16 *out, int num_samples)
#endif
#endif
-// Options.
-#ifndef DR_MP3_DEFAULT_CHANNELS
-#define DR_MP3_DEFAULT_CHANNELS 2
-#endif
-#ifndef DR_MP3_DEFAULT_SAMPLE_RATE
-#define DR_MP3_DEFAULT_SAMPLE_RATE 44100
+/* Options. */
+#ifndef DRMP3_SEEK_LEADING_MP3_FRAMES
+#define DRMP3_SEEK_LEADING_MP3_FRAMES 2
#endif
-// Standard library stuff.
+/* Standard library stuff. */
#ifndef DRMP3_ASSERT
#include <assert.h>
#define DRMP3_ASSERT(expression) assert(expression)
@@ -2143,16 +2240,17 @@ void drmp3dec_f32_to_s16(const float *in, drmp3_int16 *out, int num_samples)
#define drmp3_max(x, y) (((x) > (y)) ? (x) : (y))
#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. */
-static 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;
}
static void drmp3_blend_f32(float* pOut, float* pInA, float* pInB, float factor, drmp3_uint32 channels)
{
- for (drmp3_uint32 i = 0; i < channels; ++i) {
+ drmp3_uint32 i;
+ for (i = 0; i < channels; ++i) {
pOut[i] = drmp3_mix_f32(pInA[i], pInB[i], factor);
}
}
@@ -2169,17 +2267,20 @@ void drmp3_src_cache_init(drmp3_src* pSRC, drmp3_src_cache* pCache)
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);
- drmp3_uint32 channels = pCache->pSRC->config.channels;
+ channels = pCache->pSRC->config.channels;
- drmp3_uint64 totalFramesRead = 0;
while (frameCount > 0) {
- // If there's anything in memory go ahead and copy that over first.
+ /* 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) {
@@ -2196,14 +2297,14 @@ drmp3_uint64 drmp3_src_cache_read_frames(drmp3_src_cache* pCache, drmp3_uint64 f
}
- // At this point there are still more frames to read from the client, so we'll need to reload the cache with fresh data.
+ /* 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;
- drmp3_uint32 framesToReadFromClient = drmp3_countof(pCache->pCachedFrames) / pCache->pSRC->config.channels;
+ framesToReadFromClient = drmp3_countof(pCache->pCachedFrames) / pCache->pSRC->config.channels;
if (framesToReadFromClient > pCache->pSRC->config.cacheSizeInFrames) {
framesToReadFromClient = pCache->pSRC->config.cacheSizeInFrames;
}
@@ -2211,7 +2312,7 @@ drmp3_uint64 drmp3_src_cache_read_frames(drmp3_src_cache* pCache, drmp3_uint64 f
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.
+ /* Get out of this loop if nothing was able to be retrieved. */
if (pCache->cachedFrameCount == 0) {
break;
}
@@ -2226,11 +2327,19 @@ drmp3_uint64 drmp3_src_read_frames_linear(drmp3_src* pSRC, drmp3_uint64 frameCou
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;
+ 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;
+ if (pConfig == NULL || onRead == NULL) {
+ return DRMP3_FALSE;
+ }
+
+ if (pConfig->channels == 0 || pConfig->channels > 2) {
+ return DRMP3_FALSE;
+ }
pSRC->config = *pConfig;
pSRC->onRead = onRead;
@@ -2246,9 +2355,11 @@ drmp3_bool32 drmp3_src_init(const drmp3_src_config* pConfig, drmp3_src_read_proc
drmp3_bool32 drmp3_src_set_input_sample_rate(drmp3_src* pSRC, drmp3_uint32 sampleRateIn)
{
- if (pSRC == NULL) return DRMP3_FALSE;
+ if (pSRC == NULL) {
+ return DRMP3_FALSE;
+ }
- // Must have a sample rate of > 0.
+ /* Must have a sample rate of > 0. */
if (sampleRateIn == 0) {
return DRMP3_FALSE;
}
@@ -2259,9 +2370,11 @@ drmp3_bool32 drmp3_src_set_input_sample_rate(drmp3_src* pSRC, drmp3_uint32 sampl
drmp3_bool32 drmp3_src_set_output_sample_rate(drmp3_src* pSRC, drmp3_uint32 sampleRateOut)
{
- if (pSRC == NULL) return DRMP3_FALSE;
+ if (pSRC == NULL) {
+ return DRMP3_FALSE;
+ }
- // Must have a sample rate of > 0.
+ /* Must have a sample rate of > 0. */
if (sampleRateOut == 0) {
return DRMP3_FALSE;
}
@@ -2272,16 +2385,20 @@ drmp3_bool32 drmp3_src_set_output_sample_rate(drmp3_src* pSRC, drmp3_uint32 samp
drmp3_uint64 drmp3_src_read_frames_ex(drmp3_src* pSRC, drmp3_uint64 frameCount, void* pFramesOut, drmp3_bool32 flush)
{
- if (pSRC == NULL || frameCount == 0 || pFramesOut == NULL) return 0;
+ drmp3_src_algorithm algorithm;
+
+ if (pSRC == NULL || frameCount == 0 || pFramesOut == NULL) {
+ return 0;
+ }
- drmp3_src_algorithm algorithm = pSRC->config.algorithm;
+ algorithm = pSRC->config.algorithm;
- // Always use passthrough if the sample rates are the same.
+ /* 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...
+ /* 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);
@@ -2301,19 +2418,22 @@ drmp3_uint64 drmp3_src_read_frames_passthrough(drmp3_src* pSRC, drmp3_uint64 fra
drmp3_assert(frameCount > 0);
drmp3_assert(pFramesOut != NULL);
- (void)flush; // Passthrough need not care about flushing.
+ (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.
+ /* For linear SRC, the bin is only 2 frames: 1 prior, 1 future. */
- // Load the bin if necessary.
+ /* 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) {
@@ -2329,31 +2449,38 @@ drmp3_uint64 drmp3_src_read_frames_linear(drmp3_src* pSRC, drmp3_uint64 frameCou
pSRC->algo.linear.isNextFramesLoaded = DRMP3_TRUE;
}
- float factor = (float)pSRC->config.sampleRateIn / pSRC->config.sampleRateOut;
+ factor = (double)pSRC->config.sampleRateIn / pSRC->config.sampleRateOut;
- drmp3_uint64 totalFramesRead = 0;
+ totalFramesRead = 0;
while (frameCount > 0) {
- // The bin is where the previous and next frames are located.
+ 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, pSRC->algo.linear.alpha, 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.
- drmp3_uint32 framesToReadFromClient = (drmp3_uint32)pSRC->algo.linear.alpha;
+ /* 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 (drmp3_uint32 i = 0; i < framesToReadFromClient; ++i) {
- for (drmp3_uint32 j = 0; j < pSRC->config.channels; ++j) {
+ for (i = 0; i < framesToReadFromClient; ++i) {
+ drmp3_uint64 framesRead;
+ drmp3_uint32 j;
+
+ for (j = 0; j < pSRC->config.channels; ++j) {
pPrevFrame[j] = pNextFrame[j];
}
- drmp3_uint64 framesRead = drmp3_src_cache_read_frames(&pSRC->cache, 1, pNextFrame);
+ framesRead = drmp3_src_cache_read_frames(&pSRC->cache, 1, pNextFrame);
if (framesRead == 0) {
- for (drmp3_uint32 j = 0; j < pSRC->config.channels; ++j) {
- pNextFrame[j] = 0;
+ drmp3_uint32 k;
+ for (k = 0; k < pSRC->config.channels; ++k) {
+ pNextFrame[k] = 0;
}
if (pSRC->algo.linear.isNextFramesLoaded) {
@@ -2372,7 +2499,7 @@ drmp3_uint64 drmp3_src_read_frames_linear(drmp3_src* pSRC, drmp3_uint64 frameCou
frameCount -= 1;
totalFramesRead += 1;
- // If there's no frames available we need to get out of this loop.
+ /* 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;
}
@@ -2384,152 +2511,93 @@ drmp3_uint64 drmp3_src_read_frames_linear(drmp3_src* pSRC, drmp3_uint64 frameCou
static size_t drmp3__on_read(drmp3* pMP3, void* pBufferOut, size_t bytesToRead)
{
- return pMP3->onRead(pMP3->pUserData, pBufferOut, bytesToRead);
+ size_t bytesRead = pMP3->onRead(pMP3->pUserData, pBufferOut, bytesToRead);
+ pMP3->streamCursor += bytesRead;
+ return bytesRead;
}
static drmp3_bool32 drmp3__on_seek(drmp3* pMP3, int offset, drmp3_seek_origin origin)
{
drmp3_assert(offset >= 0);
- return pMP3->onSeek(pMP3->pUserData, offset, origin);
-}
-
-
-static drmp3_uint32 drmp3_decode_next_frame_ex(drmp3* pMP3, drmp3d_sample_t* pPCMFrames)
-{
- drmp3_assert(pMP3 != NULL);
- drmp3_assert(pMP3->onRead != NULL);
- if (pMP3->atEnd) {
- return 0;
+ if (!pMP3->onSeek(pMP3->pUserData, offset, origin)) {
+ return DRMP3_FALSE;
}
- drmp3_uint32 pcmFramesRead = 0;
- do {
- // minimp3 recommends doing data submission in 16K chunks. If we don't have at least 16K bytes available, get more.
- if (pMP3->dataSize < DRMP3_DATA_CHUNK_SIZE) {
- if (pMP3->dataCapacity < DRMP3_DATA_CHUNK_SIZE) {
- pMP3->dataCapacity = DRMP3_DATA_CHUNK_SIZE;
- drmp3_uint8* pNewData = (drmp3_uint8*)drmp3_realloc(pMP3->pData, pMP3->dataCapacity);
- if (pNewData == NULL) {
- return 0; // Out of memory.
- }
+ if (origin == drmp3_seek_origin_start) {
+ pMP3->streamCursor = (drmp3_uint64)offset;
+ } else {
+ pMP3->streamCursor += offset;
+ }
- pMP3->pData = pNewData;
- }
+ return DRMP3_TRUE;
+}
- size_t bytesRead = drmp3__on_read(pMP3, pMP3->pData + pMP3->dataSize, (pMP3->dataCapacity - pMP3->dataSize));
- if (bytesRead == 0) {
- if (pMP3->dataSize == 0) {
- pMP3->atEnd = DRMP3_TRUE;
- return 0; // No data.
- }
- }
+static drmp3_bool32 drmp3__on_seek_64(drmp3* pMP3, drmp3_uint64 offset, drmp3_seek_origin origin)
+{
+ if (offset <= 0x7FFFFFFF) {
+ return drmp3__on_seek(pMP3, (int)offset, origin);
+ }
- pMP3->dataSize += bytesRead;
- }
- if (pMP3->dataSize > INT_MAX) {
- pMP3->atEnd = DRMP3_TRUE;
- return 0; // File too big.
- }
+ /* Getting here "offset" is too large for a 32-bit integer. We just keep seeking forward until we hit the offset. */
+ if (!drmp3__on_seek(pMP3, 0x7FFFFFFF, drmp3_seek_origin_start)) {
+ return DRMP3_FALSE;
+ }
- drmp3dec_frame_info info;
- pcmFramesRead = drmp3dec_decode_frame(&pMP3->decoder, pMP3->pData, (int)pMP3->dataSize, pPCMFrames, &info); // <-- Safe size_t -> int conversion thanks to the check above.
- if (pcmFramesRead != 0) {
- size_t leftoverDataSize = (pMP3->dataSize - (size_t)info.frame_bytes);
- for (size_t i = 0; i < leftoverDataSize; ++i) {
- pMP3->pData[i] = pMP3->pData[i + (size_t)info.frame_bytes];
+ offset -= 0x7FFFFFFF;
+ while (offset > 0) {
+ if (offset <= 0x7FFFFFFF) {
+ if (!drmp3__on_seek(pMP3, (int)offset, drmp3_seek_origin_current)) {
+ return DRMP3_FALSE;
}
-
- pMP3->dataSize = leftoverDataSize;
- pMP3->pcmFramesConsumedInMP3Frame = 0;
- pMP3->pcmFramesRemainingInMP3Frame = pcmFramesRead;
- pMP3->mp3FrameChannels = info.channels;
- pMP3->mp3FrameSampleRate = info.hz;
- drmp3_src_set_input_sample_rate(&pMP3->src, pMP3->mp3FrameSampleRate);
- break;
+ offset = 0;
} else {
- // Need more data. minimp3 recommends doing data submission in 16K chunks.
- if (pMP3->dataCapacity == pMP3->dataSize) {
- // No room. Expand.
- pMP3->dataCapacity += DRMP3_DATA_CHUNK_SIZE;
- drmp3_uint8* pNewData = (drmp3_uint8*)drmp3_realloc(pMP3->pData, pMP3->dataCapacity);
- if (pNewData == NULL) {
- return 0; // Out of memory.
- }
-
- pMP3->pData = pNewData;
- }
-
- // Fill in a chunk.
- size_t bytesRead = drmp3__on_read(pMP3, pMP3->pData + pMP3->dataSize, (pMP3->dataCapacity - pMP3->dataSize));
- if (bytesRead == 0) {
- pMP3->atEnd = DRMP3_TRUE;
- return 0; // Error reading more data.
+ if (!drmp3__on_seek(pMP3, 0x7FFFFFFF, drmp3_seek_origin_current)) {
+ return DRMP3_FALSE;
}
-
- pMP3->dataSize += bytesRead;
+ offset -= 0x7FFFFFFF;
}
- } while (DRMP3_TRUE);
-
- return pcmFramesRead;
-}
-
-static drmp3_uint32 drmp3_decode_next_frame(drmp3* pMP3)
-{
- drmp3_assert(pMP3 != NULL);
- return drmp3_decode_next_frame_ex(pMP3, (drmp3d_sample_t*)pMP3->pcmFrames);
-}
-
-static drmp3_uint32 drmp3_seek_next_frame(drmp3* pMP3)
-{
- drmp3_assert(pMP3 != NULL);
-
- drmp3_uint32 pcmFrameCount = drmp3_decode_next_frame_ex(pMP3, NULL);
- if (pcmFrameCount == 0) {
- return 0;
}
- // We have essentially just skipped past the frame, so just set the remaining samples to 0.
- pMP3->currentPCMFrame += pcmFrameCount;
- pMP3->pcmFramesConsumedInMP3Frame = pcmFrameCount;
- pMP3->pcmFramesRemainingInMP3Frame = 0;
-
- return pcmFrameCount;
+ return DRMP3_TRUE;
}
+static drmp3_uint32 drmp3_decode_next_frame_ex(drmp3* pMP3, drmp3d_sample_t* pPCMFrames, drmp3_bool32 discard);
+static drmp3_uint32 drmp3_decode_next_frame(drmp3* pMP3);
+
static drmp3_uint64 drmp3_read_src(drmp3_src* pSRC, drmp3_uint64 frameCount, void* pFramesOut, void* pUserData)
{
drmp3* pMP3 = (drmp3*)pUserData;
- drmp3_assert(pMP3 != NULL);
- drmp3_assert(pMP3->onRead != NULL);
-
float* pFramesOutF = (float*)pFramesOut;
drmp3_uint64 totalFramesRead = 0;
+ drmp3_assert(pMP3 != NULL);
+ drmp3_assert(pMP3->onRead != NULL);
+
while (frameCount > 0) {
- // Read from the in-memory buffer first.
+ /* Read from the in-memory buffer first. */
while (pMP3->pcmFramesRemainingInMP3Frame > 0 && frameCount > 0) {
drmp3d_sample_t* frames = (drmp3d_sample_t*)pMP3->pcmFrames;
#ifndef DR_MP3_FLOAT_OUTPUT
if (pMP3->mp3FrameChannels == 1) {
if (pMP3->channels == 1) {
- // Mono -> Mono.
+ /* Mono -> Mono. */
pFramesOutF[0] = frames[pMP3->pcmFramesConsumedInMP3Frame] / 32768.0f;
} else {
- // Mono -> Stereo.
+ /* Mono -> Stereo. */
pFramesOutF[0] = frames[pMP3->pcmFramesConsumedInMP3Frame] / 32768.0f;
pFramesOutF[1] = frames[pMP3->pcmFramesConsumedInMP3Frame] / 32768.0f;
}
} else {
if (pMP3->channels == 1) {
- // Stereo -> Mono
+ /* 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
+ /* Stereo -> Stereo */
pFramesOutF[0] = frames[(pMP3->pcmFramesConsumedInMP3Frame*pMP3->mp3FrameChannels)+0] / 32768.0f;
pFramesOutF[1] = frames[(pMP3->pcmFramesConsumedInMP3Frame*pMP3->mp3FrameChannels)+1] / 32768.0f;
}
@@ -2537,22 +2605,22 @@ static drmp3_uint64 drmp3_read_src(drmp3_src* pSRC, drmp3_uint64 frameCount, voi
#else
if (pMP3->mp3FrameChannels == 1) {
if (pMP3->channels == 1) {
- // Mono -> Mono.
+ /* Mono -> Mono. */
pFramesOutF[0] = frames[pMP3->pcmFramesConsumedInMP3Frame];
} else {
- // Mono -> Stereo.
+ /* Mono -> Stereo. */
pFramesOutF[0] = frames[pMP3->pcmFramesConsumedInMP3Frame];
pFramesOutF[1] = frames[pMP3->pcmFramesConsumedInMP3Frame];
}
} else {
if (pMP3->channels == 1) {
- // Stereo -> Mono
+ /* 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
+ /* Stereo -> Stereo */
pFramesOutF[0] = frames[(pMP3->pcmFramesConsumedInMP3Frame*pMP3->mp3FrameChannels)+0];
pFramesOutF[1] = frames[(pMP3->pcmFramesConsumedInMP3Frame*pMP3->mp3FrameChannels)+1];
}
@@ -2572,8 +2640,10 @@ static drmp3_uint64 drmp3_read_src(drmp3_src* pSRC, drmp3_uint64 frameCount, voi
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.
+ /*
+ 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;
}
@@ -2582,16 +2652,171 @@ static drmp3_uint64 drmp3_read_src(drmp3_src* pSRC, drmp3_uint64 frameCount, voi
return totalFramesRead;
}
+static drmp3_bool32 drmp3_init_src(drmp3* pMP3)
+{
+ drmp3_src_config srcConfig;
+ drmp3_zero_object(&srcConfig);
+ srcConfig.sampleRateIn = DR_MP3_DEFAULT_SAMPLE_RATE;
+ srcConfig.sampleRateOut = pMP3->sampleRate;
+ srcConfig.channels = pMP3->channels;
+ srcConfig.algorithm = drmp3_src_algorithm_linear;
+ if (!drmp3_src_init(&srcConfig, drmp3_read_src, pMP3, &pMP3->src)) {
+ drmp3_uninit(pMP3);
+ return DRMP3_FALSE;
+ }
+
+ return DRMP3_TRUE;
+}
+
+static drmp3_uint32 drmp3_decode_next_frame_ex(drmp3* pMP3, drmp3d_sample_t* pPCMFrames, drmp3_bool32 discard)
+{
+ drmp3_uint32 pcmFramesRead = 0;
+
+ drmp3_assert(pMP3 != NULL);
+ drmp3_assert(pMP3->onRead != NULL);
+
+ if (pMP3->atEnd) {
+ return 0;
+ }
+
+ do {
+ drmp3dec_frame_info info;
+ size_t leftoverDataSize;
+
+ /* minimp3 recommends doing data submission in 16K chunks. If we don't have at least 16K bytes available, get more. */
+ if (pMP3->dataSize < DRMP3_DATA_CHUNK_SIZE) {
+ size_t bytesRead;
+
+ if (pMP3->dataCapacity < DRMP3_DATA_CHUNK_SIZE) {
+ drmp3_uint8* pNewData;
+
+ pMP3->dataCapacity = DRMP3_DATA_CHUNK_SIZE;
+ pNewData = (drmp3_uint8*)drmp3_realloc(pMP3->pData, pMP3->dataCapacity);
+ if (pNewData == NULL) {
+ return 0; /* Out of memory. */
+ }
+
+ pMP3->pData = pNewData;
+ }
+
+ bytesRead = drmp3__on_read(pMP3, pMP3->pData + pMP3->dataSize, (pMP3->dataCapacity - pMP3->dataSize));
+ if (bytesRead == 0) {
+ if (pMP3->dataSize == 0) {
+ pMP3->atEnd = DRMP3_TRUE;
+ return 0; /* No data. */
+ }
+ }
+
+ pMP3->dataSize += bytesRead;
+ }
+
+ if (pMP3->dataSize > INT_MAX) {
+ pMP3->atEnd = DRMP3_TRUE;
+ return 0; /* File too big. */
+ }
+
+ pcmFramesRead = drmp3dec_decode_frame(&pMP3->decoder, pMP3->pData, (int)pMP3->dataSize, pPCMFrames, &info); /* <-- Safe size_t -> int conversion thanks to the check above. */
+
+ /* Consume the data. */
+ leftoverDataSize = (pMP3->dataSize - (size_t)info.frame_bytes);
+ if (info.frame_bytes > 0) {
+ memmove(pMP3->pData, pMP3->pData + info.frame_bytes, leftoverDataSize);
+ pMP3->dataSize = leftoverDataSize;
+ }
+
+ /*
+ pcmFramesRead will be equal to 0 if decoding failed. If it is zero and info.frame_bytes > 0 then we have successfully
+ decoded the frame. A special case is if we are wanting to discard the frame, in which case we return successfully.
+ */
+ if (pcmFramesRead > 0 || (info.frame_bytes > 0 && discard)) {
+ pcmFramesRead = drmp3_hdr_frame_samples(pMP3->decoder.header);
+ pMP3->pcmFramesConsumedInMP3Frame = 0;
+ pMP3->pcmFramesRemainingInMP3Frame = pcmFramesRead;
+ pMP3->mp3FrameChannels = info.channels;
+ pMP3->mp3FrameSampleRate = info.hz;
+
+ /* We need to initialize the resampler if we don't yet have the channel count or sample rate. */
+ if (pMP3->channels == 0 || pMP3->sampleRate == 0) {
+ if (pMP3->channels == 0) {
+ pMP3->channels = info.channels;
+ }
+ if (pMP3->sampleRate == 0) {
+ pMP3->sampleRate = info.hz;
+ }
+ drmp3_init_src(pMP3);
+ }
+
+ drmp3_src_set_input_sample_rate(&pMP3->src, pMP3->mp3FrameSampleRate);
+ break;
+ } else if (info.frame_bytes == 0) {
+ size_t bytesRead;
+
+ /* Need more data. minimp3 recommends doing data submission in 16K chunks. */
+ if (pMP3->dataCapacity == pMP3->dataSize) {
+ drmp3_uint8* pNewData;
+
+ /* No room. Expand. */
+ pMP3->dataCapacity += DRMP3_DATA_CHUNK_SIZE;
+ pNewData = (drmp3_uint8*)drmp3_realloc(pMP3->pData, pMP3->dataCapacity);
+ if (pNewData == NULL) {
+ return 0; /* Out of memory. */
+ }
+
+ pMP3->pData = pNewData;
+ }
+
+ /* Fill in a chunk. */
+ bytesRead = drmp3__on_read(pMP3, pMP3->pData + pMP3->dataSize, (pMP3->dataCapacity - pMP3->dataSize));
+ if (bytesRead == 0) {
+ pMP3->atEnd = DRMP3_TRUE;
+ return 0; /* Error reading more data. */
+ }
+
+ pMP3->dataSize += bytesRead;
+ }
+ } while (DRMP3_TRUE);
+
+ return pcmFramesRead;
+}
+
+static drmp3_uint32 drmp3_decode_next_frame(drmp3* pMP3)
+{
+ drmp3_assert(pMP3 != NULL);
+ return drmp3_decode_next_frame_ex(pMP3, (drmp3d_sample_t*)pMP3->pcmFrames, DRMP3_FALSE);
+}
+
+#if 0
+static drmp3_uint32 drmp3_seek_next_frame(drmp3* pMP3)
+{
+ drmp3_uint32 pcmFrameCount;
+
+ drmp3_assert(pMP3 != NULL);
+
+ pcmFrameCount = drmp3_decode_next_frame_ex(pMP3, NULL);
+ if (pcmFrameCount == 0) {
+ return 0;
+ }
+
+ /* We have essentially just skipped past the frame, so just set the remaining samples to 0. */
+ pMP3->currentPCMFrame += pcmFrameCount;
+ pMP3->pcmFramesConsumedInMP3Frame = pcmFrameCount;
+ pMP3->pcmFramesRemainingInMP3Frame = 0;
+
+ return pcmFrameCount;
+}
+#endif
+
drmp3_bool32 drmp3_init_internal(drmp3* pMP3, drmp3_read_proc onRead, drmp3_seek_proc onSeek, void* pUserData, const drmp3_config* pConfig)
{
+ drmp3_config config;
+
drmp3_assert(pMP3 != NULL);
drmp3_assert(onRead != NULL);
- // This function assumes the output object has already been reset to 0. Do not do that here, otherwise things will break.
+ /* This function assumes the output object has already been reset to 0. Do not do that here, otherwise things will break. */
drmp3dec_init(&pMP3->decoder);
- // The config can be null in which case we use defaults.
- drmp3_config config;
+ /* The config can be null in which case we use defaults. */
if (pConfig != NULL) {
config = *pConfig;
} else {
@@ -2599,40 +2824,30 @@ drmp3_bool32 drmp3_init_internal(drmp3* pMP3, drmp3_read_proc onRead, drmp3_seek
}
pMP3->channels = config.outputChannels;
- if (pMP3->channels == 0) {
- pMP3->channels = DR_MP3_DEFAULT_CHANNELS;
- }
- // Cannot have more than 2 channels.
+ /* Cannot have more than 2 channels. */
if (pMP3->channels > 2) {
pMP3->channels = 2;
}
pMP3->sampleRate = config.outputSampleRate;
- if (pMP3->sampleRate == 0) {
- pMP3->sampleRate = DR_MP3_DEFAULT_SAMPLE_RATE;
- }
pMP3->onRead = onRead;
pMP3->onSeek = onSeek;
pMP3->pUserData = pUserData;
- // We need a sample rate converter for converting the sample rate from the MP3 frames to the requested output sample rate.
- 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;
+ /*
+ 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)) {
drmp3_uninit(pMP3);
- return DRMP3_FALSE; // Not a valid MP3 stream.
+ return DRMP3_FALSE; /* Not a valid MP3 stream. */
}
return DRMP3_TRUE;
@@ -2652,10 +2867,12 @@ drmp3_bool32 drmp3_init(drmp3* pMP3, drmp3_read_proc onRead, drmp3_seek_proc onS
static size_t drmp3__on_read_memory(void* pUserData, void* pBufferOut, size_t bytesToRead)
{
drmp3* pMP3 = (drmp3*)pUserData;
+ size_t bytesRemaining;
+
drmp3_assert(pMP3 != NULL);
drmp3_assert(pMP3->memory.dataSize >= pMP3->memory.currentReadPos);
- size_t bytesRemaining = pMP3->memory.dataSize - pMP3->memory.currentReadPos;
+ bytesRemaining = pMP3->memory.dataSize - pMP3->memory.currentReadPos;
if (bytesToRead > bytesRemaining) {
bytesToRead = bytesRemaining;
}
@@ -2671,26 +2888,27 @@ static size_t drmp3__on_read_memory(void* pUserData, void* pBufferOut, size_t by
static drmp3_bool32 drmp3__on_seek_memory(void* pUserData, int byteOffset, drmp3_seek_origin origin)
{
drmp3* pMP3 = (drmp3*)pUserData;
+
drmp3_assert(pMP3 != NULL);
if (origin == drmp3_seek_origin_current) {
if (byteOffset > 0) {
if (pMP3->memory.currentReadPos + byteOffset > pMP3->memory.dataSize) {
- byteOffset = (int)(pMP3->memory.dataSize - pMP3->memory.currentReadPos); // Trying to seek too far forward.
+ byteOffset = (int)(pMP3->memory.dataSize - pMP3->memory.currentReadPos); /* Trying to seek too far forward. */
}
} else {
if (pMP3->memory.currentReadPos < (size_t)-byteOffset) {
- byteOffset = -(int)pMP3->memory.currentReadPos; // Trying to seek too far backwards.
+ byteOffset = -(int)pMP3->memory.currentReadPos; /* Trying to seek too far backwards. */
}
}
- // This will never underflow thanks to the clamps above.
+ /* This will never underflow thanks to the clamps above. */
pMP3->memory.currentReadPos += byteOffset;
} else {
if ((drmp3_uint32)byteOffset <= pMP3->memory.dataSize) {
pMP3->memory.currentReadPos = byteOffset;
} else {
- pMP3->memory.currentReadPos = pMP3->memory.dataSize; // Trying to seek too far forward.
+ pMP3->memory.currentReadPos = pMP3->memory.dataSize; /* Trying to seek too far forward. */
}
}
@@ -2765,21 +2983,22 @@ void drmp3_uninit(drmp3* pMP3)
drmp3_uint64 drmp3_read_pcm_frames_f32(drmp3* pMP3, drmp3_uint64 framesToRead, float* pBufferOut)
{
+ drmp3_uint64 totalFramesRead = 0;
+
if (pMP3 == NULL || pMP3->onRead == NULL) {
return 0;
}
- drmp3_uint64 totalFramesRead = 0;
-
if (pBufferOut == NULL) {
float temp[4096];
while (framesToRead > 0) {
+ drmp3_uint64 framesJustRead;
drmp3_uint64 framesToReadRightNow = sizeof(temp)/sizeof(temp[0]) / pMP3->channels;
if (framesToReadRightNow > framesToRead) {
framesToReadRightNow = framesToRead;
}
- drmp3_uint64 framesJustRead = drmp3_read_pcm_frames_f32(pMP3, framesToReadRightNow, temp);
+ framesJustRead = drmp3_read_pcm_frames_f32(pMP3, framesToReadRightNow, temp);
if (framesJustRead == 0) {
break;
}
@@ -2795,22 +3014,144 @@ drmp3_uint64 drmp3_read_pcm_frames_f32(drmp3* pMP3, drmp3_uint64 framesToRead, f
return totalFramesRead;
}
-drmp3_bool32 drmp3_seek_to_start_of_stream(drmp3* pMP3)
+drmp3_uint64 drmp3_read_pcm_frames_s16(drmp3* pMP3, drmp3_uint64 framesToRead, drmp3_int16* pBufferOut)
{
- drmp3_assert(pMP3 != NULL);
- drmp3_assert(pMP3->onSeek != NULL);
+ float tempF32[4096];
+ drmp3_uint64 pcmFramesJustRead;
+ drmp3_uint64 totalPCMFramesRead = 0;
- // Seek to the start of the stream to begin with.
- if (!drmp3__on_seek(pMP3, 0, drmp3_seek_origin_start)) {
- return DRMP3_FALSE;
+ if (pMP3 == NULL || pMP3->onRead == NULL) {
+ return 0;
+ }
+
+ /* Naive implementation: read into a temp f32 buffer, then convert. */
+ for (;;) {
+ drmp3_uint64 pcmFramesToReadThisIteration = (framesToRead - totalPCMFramesRead);
+ if (pcmFramesToReadThisIteration > drmp3_countof(tempF32)/pMP3->channels) {
+ pcmFramesToReadThisIteration = drmp3_countof(tempF32)/pMP3->channels;
+ }
+
+ pcmFramesJustRead = drmp3_read_pcm_frames_f32(pMP3, pcmFramesToReadThisIteration, tempF32);
+ if (pcmFramesJustRead == 0) {
+ break;
+ }
+
+ drmp3dec_f32_to_s16(tempF32, pBufferOut, (int)(pcmFramesJustRead * pMP3->channels)); /* <-- Safe cast since pcmFramesJustRead will be clamped based on the size of tempF32 which is always small. */
+ pBufferOut += pcmFramesJustRead * pMP3->channels;
+
+ totalPCMFramesRead += pcmFramesJustRead;
+
+ if (pcmFramesJustRead < pcmFramesToReadThisIteration) {
+ break;
+ }
}
- // Clear any cached data.
+ return totalPCMFramesRead;
+}
+
+void drmp3_reset(drmp3* pMP3)
+{
+ drmp3_assert(pMP3 != NULL);
+
pMP3->pcmFramesConsumedInMP3Frame = 0;
pMP3->pcmFramesRemainingInMP3Frame = 0;
pMP3->currentPCMFrame = 0;
pMP3->dataSize = 0;
pMP3->atEnd = DRMP3_FALSE;
+ pMP3->src.bin[0] = 0;
+ pMP3->src.bin[1] = 0;
+ pMP3->src.bin[2] = 0;
+ pMP3->src.bin[3] = 0;
+ pMP3->src.cache.cachedFrameCount = 0;
+ pMP3->src.cache.iNextFrame = 0;
+ pMP3->src.algo.linear.alpha = 0;
+ pMP3->src.algo.linear.isNextFramesLoaded = 0;
+ pMP3->src.algo.linear.isPrevFramesLoaded = 0;
+ drmp3dec_init(&pMP3->decoder);
+}
+
+drmp3_bool32 drmp3_seek_to_start_of_stream(drmp3* pMP3)
+{
+ drmp3_assert(pMP3 != NULL);
+ drmp3_assert(pMP3->onSeek != NULL);
+
+ /* Seek to the start of the stream to begin with. */
+ if (!drmp3__on_seek(pMP3, 0, drmp3_seek_origin_start)) {
+ return DRMP3_FALSE;
+ }
+
+ /* Clear any cached data. */
+ drmp3_reset(pMP3);
+ return DRMP3_TRUE;
+}
+
+float drmp3_get_cached_pcm_frame_count_from_src(drmp3* pMP3)
+{
+ return (pMP3->src.cache.cachedFrameCount - pMP3->src.cache.iNextFrame) + (float)pMP3->src.algo.linear.alpha;
+}
+
+float drmp3_get_pcm_frames_remaining_in_mp3_frame(drmp3* pMP3)
+{
+ float factor = (float)pMP3->src.config.sampleRateOut / (float)pMP3->src.config.sampleRateIn;
+ float frameCountPreSRC = drmp3_get_cached_pcm_frame_count_from_src(pMP3) + pMP3->pcmFramesRemainingInMP3Frame;
+ return frameCountPreSRC * factor;
+}
+
+/*
+NOTE ON SEEKING
+===============
+The seeking code below is a complete mess and is broken for cases when the sample rate changes. The problem
+is with the resampling and the crappy resampler used by dr_mp3. What needs to happen is the following:
+
+1) The resampler needs to be replaced.
+2) The resampler has state which needs to be updated whenever an MP3 frame is decoded outside of
+ drmp3_read_pcm_frames_f32(). The resampler needs an API to "flush" some imaginary input so that it's
+ state is updated accordingly.
+*/
+drmp3_bool32 drmp3_seek_forward_by_pcm_frames__brute_force(drmp3* pMP3, drmp3_uint64 frameOffset)
+{
+ drmp3_uint64 framesRead;
+
+#if 0
+ /*
+ MP3 is a bit annoying when it comes to seeking because of the bit reservoir. It basically means that an MP3 frame can possibly
+ depend on some of the data of prior frames. This means it's not as simple as seeking to the first byte of the MP3 frame that
+ contains the sample because that MP3 frame will need the data from the previous MP3 frame (which we just seeked past!). To
+ resolve this we seek past a number of MP3 frames up to a point, and then read-and-discard the remainder.
+ */
+ drmp3_uint64 maxFramesToReadAndDiscard = (drmp3_uint64)(DRMP3_MAX_PCM_FRAMES_PER_MP3_FRAME * 3 * ((float)pMP3->src.config.sampleRateOut / (float)pMP3->src.config.sampleRateIn));
+
+ /* Now get rid of leading whole frames. */
+ while (frameOffset > maxFramesToReadAndDiscard) {
+ float pcmFramesRemainingInCurrentMP3FrameF = drmp3_get_pcm_frames_remaining_in_mp3_frame(pMP3);
+ drmp3_uint32 pcmFramesRemainingInCurrentMP3Frame = (drmp3_uint32)pcmFramesRemainingInCurrentMP3FrameF;
+ if (frameOffset > pcmFramesRemainingInCurrentMP3Frame) {
+ frameOffset -= pcmFramesRemainingInCurrentMP3Frame;
+ pMP3->currentPCMFrame += pcmFramesRemainingInCurrentMP3Frame;
+ pMP3->pcmFramesConsumedInMP3Frame += pMP3->pcmFramesRemainingInMP3Frame;
+ pMP3->pcmFramesRemainingInMP3Frame = 0;
+ } else {
+ break;
+ }
+
+ drmp3_uint32 pcmFrameCount = drmp3_decode_next_frame_ex(pMP3, pMP3->pcmFrames, DRMP3_FALSE);
+ if (pcmFrameCount == 0) {
+ break;
+ }
+ }
+
+ /* The last step is to read-and-discard any remaining PCM frames to make it sample-exact. */
+ framesRead = drmp3_read_pcm_frames_f32(pMP3, frameOffset, NULL);
+ if (framesRead != frameOffset) {
+ return DRMP3_FALSE;
+ }
+#else
+ /* Just using a dumb read-and-discard for now pending updates to the resampler. */
+ framesRead = drmp3_read_pcm_frames_f32(pMP3, frameOffset, NULL);
+ if (framesRead != frameOffset) {
+ return DRMP3_FALSE;
+ }
+#endif
return DRMP3_TRUE;
}
@@ -2823,51 +3164,112 @@ drmp3_bool32 drmp3_seek_to_pcm_frame__brute_force(drmp3* pMP3, drmp3_uint64 fram
return DRMP3_TRUE;
}
- // If we're moving foward we just read from where we're at. Otherwise we need to move back to the start of
- // the stream and read from the beginning.
- drmp3_uint64 framesToReadAndDiscard;
- if (frameIndex >= pMP3->currentPCMFrame) {
- // Moving foward.
- framesToReadAndDiscard = frameIndex - pMP3->currentPCMFrame;
- } else {
- // Moving backward. Move to the start of the stream and then move forward.
- framesToReadAndDiscard = frameIndex;
+ /*
+ If we're moving foward we just read from where we're at. Otherwise we need to move back to the start of
+ the stream and read from the beginning.
+ */
+ if (frameIndex < pMP3->currentPCMFrame) {
+ /* Moving backward. Move to the start of the stream and then move forward. */
if (!drmp3_seek_to_start_of_stream(pMP3)) {
return DRMP3_FALSE;
}
}
- // MP3 is a bit annoying when it comes to seeking because of the bit reservoir. It basically means that an MP3 frame can possibly
- // depend on some of the data of prior frames. This means it's not as simple as seeking to the first byte of the MP3 frame that
- // contains the sample because that MP3 frame will need the data from the previous MP3 frame (which we just seeked past!). To
- // resolve this we seek past a number of MP3 frames up to a point, and then read-and-discard the remainder.
- drmp3_uint64 maxFramesToReadAndDiscard = DRMP3_MAX_PCM_FRAMES_PER_MP3_FRAME * 3;
+ drmp3_assert(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)
+{
+ drmp3_uint32 iSeekPoint;
+
+ drmp3_assert(pSeekPointIndex != NULL);
- // First get rid of anything that's still sitting in the buffer.
- if (framesToReadAndDiscard > maxFramesToReadAndDiscard && framesToReadAndDiscard > pMP3->pcmFramesRemainingInMP3Frame) {
- framesToReadAndDiscard -= pMP3->pcmFramesRemainingInMP3Frame;
- pMP3->currentPCMFrame += pMP3->pcmFramesRemainingInMP3Frame;
- pMP3->pcmFramesConsumedInMP3Frame += pMP3->pcmFramesRemainingInMP3Frame;
- pMP3->pcmFramesRemainingInMP3Frame = 0;
+ *pSeekPointIndex = 0;
+
+ if (frameIndex < pMP3->pSeekPoints[0].pcmFrameIndex) {
+ return DRMP3_FALSE;
}
- // Now get rid of leading whole frames.
- while (framesToReadAndDiscard > maxFramesToReadAndDiscard) {
- drmp3_uint32 pcmFramesSeeked = drmp3_seek_next_frame(pMP3);
- if (pcmFramesSeeked == 0) {
- break;
+ /* Linear search for simplicity to begin with while I'm getting this thing working. Once it's all working change this to a binary search. */
+ for (iSeekPoint = 0; iSeekPoint < pMP3->seekPointCount; ++iSeekPoint) {
+ if (pMP3->pSeekPoints[iSeekPoint].pcmFrameIndex > frameIndex) {
+ break; /* Found it. */
}
- framesToReadAndDiscard -= pcmFramesSeeked;
+ *pSeekPointIndex = iSeekPoint;
}
- // The last step is to read-and-discard any remaining PCM frames to make it sample-exact.
- drmp3_uint64 framesRead = drmp3_read_pcm_frames_f32(pMP3, framesToReadAndDiscard, NULL);
- if (framesRead != framesToReadAndDiscard) {
- return DRMP3_FALSE;
+ return DRMP3_TRUE;
+}
+
+drmp3_bool32 drmp3_seek_to_pcm_frame__seek_table(drmp3* pMP3, drmp3_uint64 frameIndex)
+{
+ drmp3_seek_point seekPoint;
+ drmp3_uint32 priorSeekPointIndex;
+ drmp3_uint16 iMP3Frame;
+ drmp3_uint64 leftoverFrames;
+
+ drmp3_assert(pMP3 != NULL);
+ drmp3_assert(pMP3->pSeekPoints != NULL);
+ drmp3_assert(pMP3->seekPointCount > 0);
+
+ /* If there is no prior seekpoint it means the target PCM frame comes before the first seek point. Just assume a seekpoint at the start of the file in this case. */
+ if (drmp3_find_closest_seek_point(pMP3, frameIndex, &priorSeekPointIndex)) {
+ seekPoint = pMP3->pSeekPoints[priorSeekPointIndex];
+ } else {
+ seekPoint.seekPosInBytes = 0;
+ seekPoint.pcmFrameIndex = 0;
+ seekPoint.mp3FramesToDiscard = 0;
+ seekPoint.pcmFramesToDiscard = 0;
}
- return DRMP3_TRUE;
+ /* First thing to do is seek to the first byte of the relevant MP3 frame. */
+ if (!drmp3__on_seek_64(pMP3, seekPoint.seekPosInBytes, drmp3_seek_origin_start)) {
+ return DRMP3_FALSE; /* Failed to seek. */
+ }
+
+ /* Clear any cached data. */
+ drmp3_reset(pMP3);
+
+ /* Whole MP3 frames need to be discarded first. */
+ for (iMP3Frame = 0; iMP3Frame < seekPoint.mp3FramesToDiscard; ++iMP3Frame) {
+ drmp3_uint32 pcmFramesReadPreSRC;
+ drmp3d_sample_t* pPCMFrames;
+
+ /* Pass in non-null for the last frame because we want to ensure the sample rate converter is preloaded correctly. */
+ pPCMFrames = NULL;
+ if (iMP3Frame == seekPoint.mp3FramesToDiscard-1) {
+ pPCMFrames = (drmp3d_sample_t*)pMP3->pcmFrames;
+ }
+
+ /* We first need to decode the next frame, and then we need to flush the resampler. */
+ pcmFramesReadPreSRC = drmp3_decode_next_frame_ex(pMP3, pPCMFrames, DRMP3_TRUE);
+ if (pcmFramesReadPreSRC == 0) {
+ return DRMP3_FALSE;
+ }
+ }
+
+ /* We seeked to an MP3 frame in the raw stream so we need to make sure the current PCM frame is set correctly. */
+ pMP3->currentPCMFrame = seekPoint.pcmFrameIndex - seekPoint.pcmFramesToDiscard;
+
+ /*
+ Update resampler. This is wrong. Need to instead update it on a per MP3 frame basis. Also broken for cases when
+ the sample rate is being reduced in my testing. Should work fine when the input and output sample rate is the same
+ or a clean multiple.
+ */
+ pMP3->src.algo.linear.alpha = (drmp3_int64)pMP3->currentPCMFrame * ((double)pMP3->src.config.sampleRateIn / pMP3->src.config.sampleRateOut); /* <-- Cast to int64 is required for VC6. */
+ pMP3->src.algo.linear.alpha = pMP3->src.algo.linear.alpha - (drmp3_uint32)(pMP3->src.algo.linear.alpha);
+ if (pMP3->src.algo.linear.alpha > 0) {
+ pMP3->src.algo.linear.isPrevFramesLoaded = 1;
+ }
+
+ /*
+ Now at this point we can follow the same process as the brute force technique where we just skip over unnecessary MP3 frames and then
+ read-and-discard at least 2 whole MP3 frames.
+ */
+ leftoverFrames = frameIndex - pMP3->currentPCMFrame;
+ return drmp3_seek_forward_by_pcm_frames__brute_force(pMP3, leftoverFrames);
}
drmp3_bool32 drmp3_seek_to_pcm_frame(drmp3* pMP3, drmp3_uint64 frameIndex)
@@ -2876,54 +3278,94 @@ drmp3_bool32 drmp3_seek_to_pcm_frame(drmp3* pMP3, drmp3_uint64 frameIndex)
return DRMP3_FALSE;
}
- // We currently only support brute force seeking.
- return drmp3_seek_to_pcm_frame__brute_force(pMP3, frameIndex);
+ if (frameIndex == 0) {
+ return drmp3_seek_to_start_of_stream(pMP3);
+ }
+
+ /* Use the seek table if we have one. */
+ if (pMP3->pSeekPoints != NULL && pMP3->seekPointCount > 0) {
+ return drmp3_seek_to_pcm_frame__seek_table(pMP3, frameIndex);
+ } else {
+ return drmp3_seek_to_pcm_frame__brute_force(pMP3, frameIndex);
+ }
}
-drmp3_uint64 drmp3_get_pcm_frame_count(drmp3* pMP3)
+drmp3_bool32 drmp3_get_mp3_and_pcm_frame_count(drmp3* pMP3, drmp3_uint64* pMP3FrameCount, drmp3_uint64* pPCMFrameCount)
{
+ drmp3_uint64 currentPCMFrame;
+ drmp3_uint64 totalPCMFrameCount;
+ drmp3_uint64 totalMP3FrameCount;
+ float totalPCMFrameCountFractionalPart;
+
if (pMP3 == NULL) {
- return 0;
+ return DRMP3_FALSE;
}
- // The way this works is we move back to the start of the stream, iterate over each MP3 frame and calculate the frame count based
- // on our output sample rate, the seek back to the PCM frame we were sitting on before calling this function.
+ /*
+ The way this works is we move back to the start of the stream, iterate over each MP3 frame and calculate the frame count based
+ on our output sample rate, the seek back to the PCM frame we were sitting on before calling this function.
+ */
- // The stream must support seeking for this to work.
+ /* The stream must support seeking for this to work. */
if (pMP3->onSeek == NULL) {
- return 0;
+ return DRMP3_FALSE;
}
- // We'll need to seek back to where we were, so grab the PCM frame we're currently sitting on so we can restore later.
- drmp3_uint64 currentPCMFrame = pMP3->currentPCMFrame;
+ /* We'll need to seek back to where we were, so grab the PCM frame we're currently sitting on so we can restore later. */
+ currentPCMFrame = pMP3->currentPCMFrame;
if (!drmp3_seek_to_start_of_stream(pMP3)) {
- return 0;
+ return DRMP3_FALSE;
}
- drmp3_uint64 totalPCMFrameCount = 0;
- float totalPCMFrameCountFractionalPart = 0; // <-- With resampling there will be a fractional part to each MP3 frame that we need to accumulate.
+ totalPCMFrameCount = 0;
+ totalMP3FrameCount = 0;
+
+ totalPCMFrameCountFractionalPart = 0; /* <-- With resampling there will be a fractional part to each MP3 frame that we need to accumulate. */
for (;;) {
- drmp3_uint32 pcmFramesInCurrentMP3FrameIn = drmp3_decode_next_frame_ex(pMP3, NULL); // <-- Passing in NULL here will prevent decoding of the MP3 frame which should save time.
+ drmp3_uint32 pcmFramesInCurrentMP3FrameIn;
+ float srcRatio;
+ float pcmFramesInCurrentMP3FrameOutF;
+ drmp3_uint32 pcmFramesInCurrentMP3FrameOut;
+
+ pcmFramesInCurrentMP3FrameIn = drmp3_decode_next_frame_ex(pMP3, NULL, DRMP3_FALSE);
if (pcmFramesInCurrentMP3FrameIn == 0) {
break;
}
- float srcRatio = (float)pMP3->mp3FrameSampleRate / (float)pMP3->sampleRate;
+ srcRatio = (float)pMP3->mp3FrameSampleRate / (float)pMP3->sampleRate;
drmp3_assert(srcRatio > 0);
- float pcmFramesInCurrentMP3FrameOutF = totalPCMFrameCountFractionalPart + (pcmFramesInCurrentMP3FrameIn / srcRatio);
- drmp3_uint32 pcmFramesInCurrentMP3FrameOut = (drmp3_uint32)pcmFramesInCurrentMP3FrameOutF;
+ pcmFramesInCurrentMP3FrameOutF = totalPCMFrameCountFractionalPart + (pcmFramesInCurrentMP3FrameIn / srcRatio);
+ pcmFramesInCurrentMP3FrameOut = (drmp3_uint32)pcmFramesInCurrentMP3FrameOutF;
totalPCMFrameCountFractionalPart = pcmFramesInCurrentMP3FrameOutF - pcmFramesInCurrentMP3FrameOut;
totalPCMFrameCount += pcmFramesInCurrentMP3FrameOut;
+ totalMP3FrameCount += 1;
}
- // Finally, we need to seek back to where we were.
+ /* Finally, we need to seek back to where we were. */
if (!drmp3_seek_to_start_of_stream(pMP3)) {
- return 0;
+ return DRMP3_FALSE;
}
if (!drmp3_seek_to_pcm_frame(pMP3, currentPCMFrame)) {
+ return DRMP3_FALSE;
+ }
+
+ if (pMP3FrameCount != NULL) {
+ *pMP3FrameCount = totalMP3FrameCount;
+ }
+ if (pPCMFrameCount != NULL) {
+ *pPCMFrameCount = totalPCMFrameCount;
+ }
+
+ return DRMP3_TRUE;
+}
+
+drmp3_uint64 drmp3_get_pcm_frame_count(drmp3* pMP3)
+{
+ drmp3_uint64 totalPCMFrameCount;
+ if (!drmp3_get_mp3_and_pcm_frame_count(pMP3, NULL, &totalPCMFrameCount)) {
return 0;
}
@@ -2932,56 +3374,204 @@ drmp3_uint64 drmp3_get_pcm_frame_count(drmp3* pMP3)
drmp3_uint64 drmp3_get_mp3_frame_count(drmp3* pMP3)
{
- if (pMP3 == NULL) {
+ drmp3_uint64 totalMP3FrameCount;
+ if (!drmp3_get_mp3_and_pcm_frame_count(pMP3, &totalMP3FrameCount, NULL)) {
return 0;
}
- // This works the same way as drmp3_get_pcm_frame_count() - move to the start, count MP3 frames, move back to the previous position.
+ return totalMP3FrameCount;
+}
- // The stream must support seeking for this to work.
- if (pMP3->onSeek == NULL) {
- return 0;
+void drmp3__accumulate_running_pcm_frame_count(drmp3* pMP3, drmp3_uint32 pcmFrameCountIn, drmp3_uint64* pRunningPCMFrameCount, float* pRunningPCMFrameCountFractionalPart)
+{
+ float srcRatio;
+ float pcmFrameCountOutF;
+ drmp3_uint32 pcmFrameCountOut;
+
+ srcRatio = (float)pMP3->mp3FrameSampleRate / (float)pMP3->sampleRate;
+ drmp3_assert(srcRatio > 0);
+
+ pcmFrameCountOutF = *pRunningPCMFrameCountFractionalPart + (pcmFrameCountIn / srcRatio);
+ pcmFrameCountOut = (drmp3_uint32)pcmFrameCountOutF;
+ *pRunningPCMFrameCountFractionalPart = pcmFrameCountOutF - pcmFrameCountOut;
+ *pRunningPCMFrameCount += pcmFrameCountOut;
+}
+
+typedef struct
+{
+ drmp3_uint64 bytePos;
+ drmp3_uint64 pcmFrameIndex; /* <-- After sample rate conversion. */
+} drmp3__seeking_mp3_frame_info;
+
+drmp3_bool32 drmp3_calculate_seek_points(drmp3* pMP3, drmp3_uint32* pSeekPointCount, drmp3_seek_point* pSeekPoints)
+{
+ drmp3_uint32 seekPointCount;
+ drmp3_uint64 currentPCMFrame;
+ drmp3_uint64 totalMP3FrameCount;
+ drmp3_uint64 totalPCMFrameCount;
+
+ if (pMP3 == NULL || pSeekPointCount == NULL || pSeekPoints == NULL) {
+ return DRMP3_FALSE; /* Invalid args. */
}
- // We'll need to seek back to where we were, so grab the PCM frame we're currently sitting on so we can restore later.
- drmp3_uint64 currentPCMFrame = pMP3->currentPCMFrame;
+ seekPointCount = *pSeekPointCount;
+ if (seekPointCount == 0) {
+ return DRMP3_FALSE; /* The client has requested no seek points. Consider this to be invalid arguments since the client has probably not intended this. */
+ }
+
+ /* We'll need to seek back to the current sample after calculating the seekpoints so we need to go ahead and grab the current location at the top. */
+ currentPCMFrame = pMP3->currentPCMFrame;
- if (!drmp3_seek_to_start_of_stream(pMP3)) {
- return 0;
+ /* We never do more than the total number of MP3 frames and we limit it to 32-bits. */
+ if (!drmp3_get_mp3_and_pcm_frame_count(pMP3, &totalMP3FrameCount, &totalPCMFrameCount)) {
+ return DRMP3_FALSE;
}
- drmp3_uint64 totalMP3FrameCount = 0;
- for (;;) {
- drmp3_uint32 pcmFramesInCurrentMP3FrameIn = drmp3_decode_next_frame_ex(pMP3, NULL);
- if (pcmFramesInCurrentMP3FrameIn == 0) {
- break;
+ /* If there's less than DRMP3_SEEK_LEADING_MP3_FRAMES+1 frames we just report 1 seek point which will be the very start of the stream. */
+ if (totalMP3FrameCount < DRMP3_SEEK_LEADING_MP3_FRAMES+1) {
+ seekPointCount = 1;
+ pSeekPoints[0].seekPosInBytes = 0;
+ pSeekPoints[0].pcmFrameIndex = 0;
+ pSeekPoints[0].mp3FramesToDiscard = 0;
+ pSeekPoints[0].pcmFramesToDiscard = 0;
+ } else {
+ drmp3_uint64 pcmFramesBetweenSeekPoints;
+ drmp3__seeking_mp3_frame_info mp3FrameInfo[DRMP3_SEEK_LEADING_MP3_FRAMES+1];
+ drmp3_uint64 runningPCMFrameCount = 0;
+ float runningPCMFrameCountFractionalPart = 0;
+ drmp3_uint64 nextTargetPCMFrame;
+ drmp3_uint32 iMP3Frame;
+ drmp3_uint32 iSeekPoint;
+
+ if (seekPointCount > totalMP3FrameCount-1) {
+ seekPointCount = (drmp3_uint32)totalMP3FrameCount-1;
}
- totalMP3FrameCount += 1;
+ pcmFramesBetweenSeekPoints = totalPCMFrameCount / (seekPointCount+1);
+
+ /*
+ Here is where we actually calculate the seek points. We need to start by moving the start of the stream. We then enumerate over each
+ MP3 frame.
+ */
+ if (!drmp3_seek_to_start_of_stream(pMP3)) {
+ return DRMP3_FALSE;
+ }
+
+ /*
+ We need to cache the byte positions of the previous MP3 frames. As a new MP3 frame is iterated, we cycle the byte positions in this
+ array. The value in the first item in this array is the byte position that will be reported in the next seek point.
+ */
+
+ /* We need to initialize the array of MP3 byte positions for the leading MP3 frames. */
+ for (iMP3Frame = 0; iMP3Frame < DRMP3_SEEK_LEADING_MP3_FRAMES+1; ++iMP3Frame) {
+ drmp3_uint32 pcmFramesInCurrentMP3FrameIn;
+
+ /* The byte position of the next frame will be the stream's cursor position, minus whatever is sitting in the buffer. */
+ drmp3_assert(pMP3->streamCursor >= pMP3->dataSize);
+ mp3FrameInfo[iMP3Frame].bytePos = pMP3->streamCursor - pMP3->dataSize;
+ mp3FrameInfo[iMP3Frame].pcmFrameIndex = runningPCMFrameCount;
+
+ /* We need to get information about this frame so we can know how many samples it contained. */
+ pcmFramesInCurrentMP3FrameIn = drmp3_decode_next_frame_ex(pMP3, NULL, DRMP3_FALSE);
+ if (pcmFramesInCurrentMP3FrameIn == 0) {
+ return DRMP3_FALSE; /* This should never happen. */
+ }
+
+ drmp3__accumulate_running_pcm_frame_count(pMP3, pcmFramesInCurrentMP3FrameIn, &runningPCMFrameCount, &runningPCMFrameCountFractionalPart);
+ }
+
+ /*
+ At this point we will have extracted the byte positions of the leading MP3 frames. We can now start iterating over each seek point and
+ calculate them.
+ */
+ nextTargetPCMFrame = 0;
+ for (iSeekPoint = 0; iSeekPoint < seekPointCount; ++iSeekPoint) {
+ nextTargetPCMFrame += pcmFramesBetweenSeekPoints;
+
+ for (;;) {
+ if (nextTargetPCMFrame < runningPCMFrameCount) {
+ /* The next seek point is in the current MP3 frame. */
+ pSeekPoints[iSeekPoint].seekPosInBytes = mp3FrameInfo[0].bytePos;
+ pSeekPoints[iSeekPoint].pcmFrameIndex = nextTargetPCMFrame;
+ pSeekPoints[iSeekPoint].mp3FramesToDiscard = DRMP3_SEEK_LEADING_MP3_FRAMES;
+ pSeekPoints[iSeekPoint].pcmFramesToDiscard = (drmp3_uint16)(nextTargetPCMFrame - mp3FrameInfo[DRMP3_SEEK_LEADING_MP3_FRAMES-1].pcmFrameIndex);
+ break;
+ } else {
+ size_t i;
+ drmp3_uint32 pcmFramesInCurrentMP3FrameIn;
+
+ /*
+ The next seek point is not in the current MP3 frame, so continue on to the next one. The first thing to do is cycle the cached
+ MP3 frame info.
+ */
+ for (i = 0; i < drmp3_countof(mp3FrameInfo)-1; ++i) {
+ mp3FrameInfo[i] = mp3FrameInfo[i+1];
+ }
+
+ /* Cache previous MP3 frame info. */
+ mp3FrameInfo[drmp3_countof(mp3FrameInfo)-1].bytePos = pMP3->streamCursor - pMP3->dataSize;
+ mp3FrameInfo[drmp3_countof(mp3FrameInfo)-1].pcmFrameIndex = runningPCMFrameCount;
+
+ /*
+ Go to the next MP3 frame. This shouldn't ever fail, but just in case it does we just set the seek point and break. If it happens, it
+ should only ever do it for the last seek point.
+ */
+ pcmFramesInCurrentMP3FrameIn = drmp3_decode_next_frame_ex(pMP3, NULL, DRMP3_TRUE);
+ if (pcmFramesInCurrentMP3FrameIn == 0) {
+ pSeekPoints[iSeekPoint].seekPosInBytes = mp3FrameInfo[0].bytePos;
+ pSeekPoints[iSeekPoint].pcmFrameIndex = nextTargetPCMFrame;
+ pSeekPoints[iSeekPoint].mp3FramesToDiscard = DRMP3_SEEK_LEADING_MP3_FRAMES;
+ pSeekPoints[iSeekPoint].pcmFramesToDiscard = (drmp3_uint16)(nextTargetPCMFrame - mp3FrameInfo[DRMP3_SEEK_LEADING_MP3_FRAMES-1].pcmFrameIndex);
+ break;
+ }
+
+ drmp3__accumulate_running_pcm_frame_count(pMP3, pcmFramesInCurrentMP3FrameIn, &runningPCMFrameCount, &runningPCMFrameCountFractionalPart);
+ }
+ }
+ }
+
+ /* Finally, we need to seek back to where we were. */
+ if (!drmp3_seek_to_start_of_stream(pMP3)) {
+ return DRMP3_FALSE;
+ }
+ if (!drmp3_seek_to_pcm_frame(pMP3, currentPCMFrame)) {
+ return DRMP3_FALSE;
+ }
}
- // Finally, we need to seek back to where we were.
- if (!drmp3_seek_to_start_of_stream(pMP3)) {
- return 0;
+ *pSeekPointCount = seekPointCount;
+ return DRMP3_TRUE;
+}
+
+drmp3_bool32 drmp3_bind_seek_table(drmp3* pMP3, drmp3_uint32 seekPointCount, drmp3_seek_point* pSeekPoints)
+{
+ if (pMP3 == NULL) {
+ return DRMP3_FALSE;
}
- if (!drmp3_seek_to_pcm_frame(pMP3, currentPCMFrame)) {
- return 0;
+ if (seekPointCount == 0 || pSeekPoints == NULL) {
+ /* Unbinding. */
+ pMP3->seekPointCount = 0;
+ pMP3->pSeekPoints = NULL;
+ } else {
+ /* Binding. */
+ pMP3->seekPointCount = seekPointCount;
+ pMP3->pSeekPoints = pSeekPoints;
}
- return totalMP3FrameCount;
+ return DRMP3_TRUE;
}
float* drmp3__full_read_and_close_f32(drmp3* pMP3, drmp3_config* pConfig, drmp3_uint64* pTotalFrameCount)
{
- drmp3_assert(pMP3 != NULL);
-
drmp3_uint64 totalFramesRead = 0;
drmp3_uint64 framesCapacity = 0;
float* pFrames = NULL;
-
float temp[4096];
+
+ drmp3_assert(pMP3 != NULL);
+
for (;;) {
drmp3_uint64 framesToReadRightNow = drmp3_countof(temp) / pMP3->channels;
drmp3_uint64 framesJustRead = drmp3_read_pcm_frames_f32(pMP3, framesToReadRightNow, temp);
@@ -2989,19 +3579,22 @@ float* drmp3__full_read_and_close_f32(drmp3* pMP3, drmp3_config* pConfig, drmp3_
break;
}
- // Reallocate the output buffer if there's not enough room.
+ /* Reallocate the output buffer if there's not enough room. */
if (framesCapacity < totalFramesRead + framesJustRead) {
+ drmp3_uint64 newFramesBufferSize;
+ float* pNewFrames;
+
framesCapacity *= 2;
if (framesCapacity < totalFramesRead + framesJustRead) {
framesCapacity = totalFramesRead + framesJustRead;
}
- drmp3_uint64 newFramesBufferSize = framesCapacity*pMP3->channels*sizeof(float);
+ newFramesBufferSize = framesCapacity*pMP3->channels*sizeof(float);
if (newFramesBufferSize > DRMP3_SIZE_MAX) {
break;
}
- float* pNewFrames = (float*)drmp3_realloc(pFrames, (size_t)newFramesBufferSize);
+ pNewFrames = (float*)drmp3_realloc(pFrames, (size_t)newFramesBufferSize);
if (pNewFrames == NULL) {
drmp3_free(pFrames);
break;
@@ -3013,7 +3606,70 @@ float* drmp3__full_read_and_close_f32(drmp3* pMP3, drmp3_config* pConfig, drmp3_
drmp3_copy_memory(pFrames + totalFramesRead*pMP3->channels, temp, (size_t)(framesJustRead*pMP3->channels*sizeof(float)));
totalFramesRead += framesJustRead;
- // If the number of frames we asked for is less that what we actually read it means we've reached the end.
+ /* If the number of frames we asked for is less that what we actually read it means we've reached the end. */
+ if (framesJustRead != framesToReadRightNow) {
+ break;
+ }
+ }
+
+ if (pConfig != NULL) {
+ pConfig->outputChannels = pMP3->channels;
+ pConfig->outputSampleRate = pMP3->sampleRate;
+ }
+
+ drmp3_uninit(pMP3);
+
+ if (pTotalFrameCount) {
+ *pTotalFrameCount = totalFramesRead;
+ }
+
+ return pFrames;
+}
+
+drmp3_int16* drmp3__full_read_and_close_s16(drmp3* pMP3, drmp3_config* pConfig, drmp3_uint64* pTotalFrameCount)
+{
+ drmp3_uint64 totalFramesRead = 0;
+ drmp3_uint64 framesCapacity = 0;
+ drmp3_int16* pFrames = NULL;
+ drmp3_int16 temp[4096];
+
+ drmp3_assert(pMP3 != NULL);
+
+ for (;;) {
+ drmp3_uint64 framesToReadRightNow = drmp3_countof(temp) / pMP3->channels;
+ drmp3_uint64 framesJustRead = drmp3_read_pcm_frames_s16(pMP3, framesToReadRightNow, temp);
+ if (framesJustRead == 0) {
+ break;
+ }
+
+ /* Reallocate the output buffer if there's not enough room. */
+ if (framesCapacity < totalFramesRead + framesJustRead) {
+ drmp3_uint64 newFramesBufferSize;
+ drmp3_int16* pNewFrames;
+
+ framesCapacity *= 2;
+ if (framesCapacity < totalFramesRead + framesJustRead) {
+ framesCapacity = totalFramesRead + framesJustRead;
+ }
+
+ newFramesBufferSize = framesCapacity*pMP3->channels*sizeof(drmp3_int16);
+ if (newFramesBufferSize > DRMP3_SIZE_MAX) {
+ break;
+ }
+
+ pNewFrames = (drmp3_int16*)drmp3_realloc(pFrames, (size_t)newFramesBufferSize);
+ if (pNewFrames == NULL) {
+ drmp3_free(pFrames);
+ break;
+ }
+
+ pFrames = pNewFrames;
+ }
+
+ drmp3_copy_memory(pFrames + totalFramesRead*pMP3->channels, temp, (size_t)(framesJustRead*pMP3->channels*sizeof(drmp3_int16)));
+ totalFramesRead += framesJustRead;
+
+ /* If the number of frames we asked for is less that what we actually read it means we've reached the end. */
if (framesJustRead != framesToReadRightNow) {
break;
}
@@ -3026,10 +3682,14 @@ float* drmp3__full_read_and_close_f32(drmp3* pMP3, drmp3_config* pConfig, drmp3_
drmp3_uninit(pMP3);
- if (pTotalFrameCount) *pTotalFrameCount = totalFramesRead;
+ if (pTotalFrameCount) {
+ *pTotalFrameCount = totalFramesRead;
+ }
+
return pFrames;
}
+
float* drmp3_open_and_read_f32(drmp3_read_proc onRead, drmp3_seek_proc onSeek, void* pUserData, drmp3_config* pConfig, drmp3_uint64* pTotalFrameCount)
{
drmp3 mp3;
@@ -3040,6 +3700,17 @@ float* drmp3_open_and_read_f32(drmp3_read_proc onRead, drmp3_seek_proc onSeek, v
return drmp3__full_read_and_close_f32(&mp3, pConfig, pTotalFrameCount);
}
+drmp3_int16* drmp3_open_and_read_s16(drmp3_read_proc onRead, drmp3_seek_proc onSeek, void* pUserData, drmp3_config* pConfig, drmp3_uint64* pTotalFrameCount)
+{
+ drmp3 mp3;
+ if (!drmp3_init(&mp3, onRead, onSeek, pUserData, pConfig)) {
+ return NULL;
+ }
+
+ return drmp3__full_read_and_close_s16(&mp3, pConfig, pTotalFrameCount);
+}
+
+
float* drmp3_open_memory_and_read_f32(const void* pData, size_t dataSize, drmp3_config* pConfig, drmp3_uint64* pTotalFrameCount)
{
drmp3 mp3;
@@ -3050,6 +3721,17 @@ float* drmp3_open_memory_and_read_f32(const void* pData, size_t dataSize, drmp3_
return drmp3__full_read_and_close_f32(&mp3, pConfig, pTotalFrameCount);
}
+drmp3_int16* drmp3_open_memory_and_read_s16(const void* pData, size_t dataSize, drmp3_config* pConfig, drmp3_uint64* pTotalFrameCount)
+{
+ drmp3 mp3;
+ if (!drmp3_init_memory(&mp3, pData, dataSize, pConfig)) {
+ return NULL;
+ }
+
+ return drmp3__full_read_and_close_s16(&mp3, pConfig, pTotalFrameCount);
+}
+
+
#ifndef DR_MP3_NO_STDIO
float* drmp3_open_file_and_read_f32(const char* filePath, drmp3_config* pConfig, drmp3_uint64* pTotalFrameCount)
{
@@ -3060,6 +3742,16 @@ float* drmp3_open_file_and_read_f32(const char* filePath, drmp3_config* pConfig,
return drmp3__full_read_and_close_f32(&mp3, pConfig, pTotalFrameCount);
}
+
+drmp3_int16* drmp3_open_file_and_read_s16(const char* filePath, drmp3_config* pConfig, drmp3_uint64* pTotalFrameCount)
+{
+ drmp3 mp3;
+ if (!drmp3_init_file(&mp3, filePath, pConfig)) {
+ return NULL;
+ }
+
+ return drmp3__full_read_and_close_s16(&mp3, pConfig, pTotalFrameCount);
+}
#endif
void drmp3_free(void* p)
@@ -3069,127 +3761,172 @@ void drmp3_free(void* p)
#endif /*DR_MP3_IMPLEMENTATION*/
+/*
+DIFFERENCES BETWEEN minimp3 AND dr_mp3
+======================================
+- First, keep in mind that minimp3 (https://github.com/lieff/minimp3) is where all the real work was done. All of the
+ code relating to the actual decoding remains mostly unmodified, apart from some namespacing changes.
+- dr_mp3 adds a pulling style API which allows you to deliver raw data via callbacks. So, rather than pushing data
+ to the decoder, the decoder _pulls_ data from your callbacks.
+- In addition to callbacks, a decoder can be initialized from a block of memory and a file.
+- The dr_mp3 pull API reads PCM frames rather than whole MP3 frames.
+- dr_mp3 adds convenience APIs for opening and decoding entire files in one go.
+- dr_mp3 is fully namespaced, including the implementation section, which is more suitable when compiling projects
+ as a single translation unit (aka unity builds). At the time of writing this, a unity build is not possible when
+ using minimp3 in conjunction with stb_vorbis. dr_mp3 addresses this.
+*/
-// DIFFERENCES BETWEEN minimp3 AND dr_mp3
-// ======================================
-// - First, keep in mind that minimp3 (https://github.com/lieff/minimp3) is where all the real work was done. All of the
-// code relating to the actual decoding remains mostly unmodified, apart from some namespacing changes.
-// - dr_mp3 adds a pulling style API which allows you to deliver raw data via callbacks. So, rather than pushing data
-// to the decoder, the decoder _pulls_ data from your callbacks.
-// - In addition to callbacks, a decoder can be initialized from a block of memory and a file.
-// - The dr_mp3 pull API reads PCM frames rather than whole MP3 frames.
-// - dr_mp3 adds convenience APIs for opening and decoding entire files in one go.
-// - dr_mp3 is fully namespaced, including the implementation section, which is more suitable when compiling projects
-// as a single translation unit (aka unity builds). At the time of writing this, a unity build is not possible when
-// using minimp3 in conjunction with stb_vorbis. dr_mp3 addresses this.
-
-
-// REVISION HISTORY
-// ================
-//
-// v0.4.0 - 2018-xx-xx
-// - API CHANGE: Rename some APIs:
-// - drmp3_read_f32 -> to drmp3_read_pcm_frames_f32
-// - drmp3_seek_to_frame -> drmp3_seek_to_pcm_frame
-// - drmp3_open_and_decode_f32 -> drmp3_open_and_read_f32
-// - drmp3_open_and_decode_memory_f32 -> drmp3_open_memory_and_read_f32
-// - drmp3_open_and_decode_file_f32 -> drmp3_open_file_and_read_f32
-// - Add drmp3_get_pcm_frame_count().
-// - Add drmp3_get_mp3_frame_count().
-// - Improve seeking performance.
-//
-// v0.3.2 - 2018-09-11
-// - Fix a couple of memory leaks.
-// - Bring up to date with minimp3.
-//
-// v0.3.1 - 2018-08-25
-// - Fix C++ build.
-//
-// v0.3.0 - 2018-08-25
-// - Bring up to date with minimp3. This has a minor API change: the "pcm" parameter of drmp3dec_decode_frame() has
-// been changed from short* to void* because it can now output both s16 and f32 samples, depending on whether or
-// not the DR_MP3_FLOAT_OUTPUT option is set.
-//
-// v0.2.11 - 2018-08-08
-// - Fix a bug where the last part of a file is not read.
-//
-// v0.2.10 - 2018-08-07
-// - Improve 64-bit detection.
-//
-// v0.2.9 - 2018-08-05
-// - Fix C++ build on older versions of GCC.
-// - Bring up to date with minimp3.
-//
-// v0.2.8 - 2018-08-02
-// - Fix compilation errors with older versions of GCC.
-//
-// v0.2.7 - 2018-07-13
-// - Bring up to date with minimp3.
-//
-// v0.2.6 - 2018-07-12
-// - Bring up to date with minimp3.
-//
-// v0.2.5 - 2018-06-22
-// - Bring up to date with minimp3.
-//
-// v0.2.4 - 2018-05-12
-// - Bring up to date with minimp3.
-//
-// v0.2.3 - 2018-04-29
-// - Fix TCC build.
-//
-// v0.2.2 - 2018-04-28
-// - Fix bug when opening a decoder from memory.
-//
-// v0.2.1 - 2018-04-27
-// - Efficiency improvements when the decoder reaches the end of the stream.
-//
-// v0.2 - 2018-04-21
-// - Bring up to date with minimp3.
-// - Start using major.minor.revision versioning.
-//
-// v0.1d - 2018-03-30
-// - Bring up to date with minimp3.
-//
-// v0.1c - 2018-03-11
-// - Fix C++ build error.
-//
-// v0.1b - 2018-03-07
-// - Bring up to date with minimp3.
-//
-// v0.1a - 2018-02-28
-// - Fix compilation error on GCC/Clang.
-// - Fix some warnings.
-//
-// v0.1 - 2018-02-xx
-// - Initial versioned release.
-
+/*
+REVISION HISTORY
+================
+v0.4.4 - 2019-05-06
+ - Fixes to the VC6 build.
+
+v0.4.3 - 2019-05-05
+ - Use the channel count and/or sample rate of the first MP3 frame instead of DR_MP3_DEFAULT_CHANNELS and
+ DR_MP3_DEFAULT_SAMPLE_RATE when they are set to 0. To use the old behaviour, just set the relevant property to
+ DR_MP3_DEFAULT_CHANNELS or DR_MP3_DEFAULT_SAMPLE_RATE.
+ - Add s16 reading APIs
+ - drmp3_read_pcm_frames_s16
+ - drmp3_open_memory_and_read_s16
+ - drmp3_open_and_read_s16
+ - drmp3_open_file_and_read_s16
+ - Add drmp3_get_mp3_and_pcm_frame_count() to the public header section.
+ - Add support for C89.
+ - Change license to choice of public domain or MIT-0.
+
+v0.4.2 - 2019-02-21
+ - Fix a warning.
+
+v0.4.1 - 2018-12-30
+ - Fix a warning.
+
+v0.4.0 - 2018-12-16
+ - API CHANGE: Rename some APIs:
+ - drmp3_read_f32 -> to drmp3_read_pcm_frames_f32
+ - drmp3_seek_to_frame -> drmp3_seek_to_pcm_frame
+ - drmp3_open_and_decode_f32 -> drmp3_open_and_read_f32
+ - drmp3_open_and_decode_memory_f32 -> drmp3_open_memory_and_read_f32
+ - drmp3_open_and_decode_file_f32 -> drmp3_open_file_and_read_f32
+ - Add drmp3_get_pcm_frame_count().
+ - Add drmp3_get_mp3_frame_count().
+ - Improve seeking performance.
+
+v0.3.2 - 2018-09-11
+ - Fix a couple of memory leaks.
+ - Bring up to date with minimp3.
+
+v0.3.1 - 2018-08-25
+ - Fix C++ build.
+
+v0.3.0 - 2018-08-25
+ - Bring up to date with minimp3. This has a minor API change: the "pcm" parameter of drmp3dec_decode_frame() has
+ been changed from short* to void* because it can now output both s16 and f32 samples, depending on whether or
+ not the DR_MP3_FLOAT_OUTPUT option is set.
+
+v0.2.11 - 2018-08-08
+ - Fix a bug where the last part of a file is not read.
+
+v0.2.10 - 2018-08-07
+ - Improve 64-bit detection.
+
+v0.2.9 - 2018-08-05
+ - Fix C++ build on older versions of GCC.
+ - Bring up to date with minimp3.
+
+v0.2.8 - 2018-08-02
+ - Fix compilation errors with older versions of GCC.
+
+v0.2.7 - 2018-07-13
+ - Bring up to date with minimp3.
+
+v0.2.6 - 2018-07-12
+ - Bring up to date with minimp3.
+
+v0.2.5 - 2018-06-22
+ - Bring up to date with minimp3.
+
+v0.2.4 - 2018-05-12
+ - Bring up to date with minimp3.
+
+v0.2.3 - 2018-04-29
+ - Fix TCC build.
+
+v0.2.2 - 2018-04-28
+ - Fix bug when opening a decoder from memory.
+
+v0.2.1 - 2018-04-27
+ - Efficiency improvements when the decoder reaches the end of the stream.
+
+v0.2 - 2018-04-21
+ - Bring up to date with minimp3.
+ - Start using major.minor.revision versioning.
+
+v0.1d - 2018-03-30
+ - Bring up to date with minimp3.
+
+v0.1c - 2018-03-11
+ - Fix C++ build error.
+
+v0.1b - 2018-03-07
+ - Bring up to date with minimp3.
+
+v0.1a - 2018-02-28
+ - Fix compilation error on GCC/Clang.
+ - Fix some warnings.
+
+v0.1 - 2018-02-xx
+ - Initial versioned release.
+*/
/*
+This software is available as a choice of the following licenses. Choose
+whichever you prefer.
+
+===============================================================================
+ALTERNATIVE 1 - Public Domain (www.unlicense.org)
+===============================================================================
This is free and unencumbered software released into the public domain.
-Anyone is free to copy, modify, publish, use, compile, sell, or
-distribute this software, either in source code form or as a compiled
-binary, for any purpose, commercial or non-commercial, and by any
-means.
-
-In jurisdictions that recognize copyright laws, the author or authors
-of this software dedicate any and all copyright interest in the
-software to the public domain. We make this dedication for the benefit
-of the public at large and to the detriment of our heirs and
-successors. We intend this dedication to be an overt act of
-relinquishment in perpetuity of all present and future rights to this
-software under copyright law.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
-EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
-MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
-IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
-OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
-ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
-OTHER DEALINGS IN THE SOFTWARE.
+Anyone is free to copy, modify, publish, use, compile, sell, or distribute this
+software, either in source code form or as a compiled binary, for any purpose,
+commercial or non-commercial, and by any means.
+
+In jurisdictions that recognize copyright laws, the author or authors of this
+software dedicate any and all copyright interest in the software to the public
+domain. We make this dedication for the benefit of the public at large and to
+the detriment of our heirs and successors. We intend this dedication to be an
+overt act of relinquishment in perpetuity of all present and future rights to
+this software under copyright law.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
For more information, please refer to <http://unlicense.org/>
+
+===============================================================================
+ALTERNATIVE 2 - MIT No Attribution
+===============================================================================
+Copyright 2018 David Reid
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of
+this software and associated documentation files (the "Software"), to deal in
+the Software without restriction, including without limitation the rights to
+use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+of the Software, and to permit persons to whom the Software is furnished to do
+so.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
*/
/*
diff --git a/src/external/dr_wav.h b/src/external/dr_wav.h
index b071d00e..b2395bfb 100644
--- a/src/external/dr_wav.h
+++ b/src/external/dr_wav.h
@@ -1,115 +1,154 @@
-// WAV audio loader and writer. Public domain. See "unlicense" statement at the end of this file.
-// dr_wav - v0.8.1 - 2018-06-29
-//
-// David Reid - mackron@gmail.com
-
-// USAGE
-//
-// This is a single-file library. To use it, do something like the following in one .c file.
-// #define DR_WAV_IMPLEMENTATION
-// #include "dr_wav.h"
-//
-// You can then #include this file in other parts of the program as you would with any other header file. Do something
-// like the following to read audio data:
-//
-// drwav wav;
-// if (!drwav_init_file(&wav, "my_song.wav")) {
-// // Error opening WAV file.
-// }
-//
-// drwav_int32* pDecodedInterleavedSamples = malloc(wav.totalSampleCount * sizeof(drwav_int32));
-// size_t numberOfSamplesActuallyDecoded = drwav_read_s32(&wav, wav.totalSampleCount, pDecodedInterleavedSamples);
-//
-// ...
-//
-// drwav_uninit(&wav);
-//
-// You can also use drwav_open() to allocate and initialize the loader for you:
-//
-// drwav* pWav = drwav_open_file("my_song.wav");
-// if (pWav == NULL) {
-// // Error opening WAV file.
-// }
-//
-// ...
-//
-// drwav_close(pWav);
-//
-// If you just want to quickly open and read the audio data in a single operation you can do something like this:
-//
-// unsigned int channels;
-// unsigned int sampleRate;
-// drwav_uint64 totalSampleCount;
-// float* pSampleData = drwav_open_and_read_file_s32("my_song.wav", &channels, &sampleRate, &totalSampleCount);
-// if (pSampleData == NULL) {
-// // Error opening and reading WAV file.
-// }
-//
-// ...
-//
-// drwav_free(pSampleData);
-//
-// The examples above use versions of the API that convert the audio data to a consistent format (32-bit signed PCM, in
-// this case), but you can still output the audio data in its internal format (see notes below for supported formats):
-//
-// size_t samplesRead = drwav_read(&wav, wav.totalSampleCount, pDecodedInterleavedSamples);
-//
-// You can also read the raw bytes of audio data, which could be useful if dr_wav does not have native support for
-// a particular data format:
-//
-// size_t bytesRead = drwav_read_raw(&wav, bytesToRead, pRawDataBuffer);
-//
-//
-// dr_wav has seamless support the Sony Wave64 format. The decoder will automatically detect it and it should Just Work
-// without any manual intervention.
-//
-//
-// dr_wav can also be used to output WAV files. This does not currently support compressed formats. To use this, look at
-// drwav_open_write(), drwav_open_file_write(), etc. Use drwav_write() to write samples, or drwav_write_raw() to write
-// raw data in the "data" chunk.
-//
-// drwav_data_format format;
-// format.container = drwav_container_riff; // <-- drwav_container_riff = normal WAV files, drwav_container_w64 = Sony Wave64.
-// format.format = DR_WAVE_FORMAT_PCM; // <-- Any of the DR_WAVE_FORMAT_* codes.
-// format.channels = 2;
-// format.sampleRate = 44100;
-// format.bitsPerSample = 16;
-// drwav* pWav = drwav_open_file_write("data/recording.wav", &format);
-//
-// ...
-//
-// drwav_uint64 samplesWritten = drwav_write(pWav, sampleCount, pSamples);
-//
-//
-//
-// OPTIONS
-// #define these options before including this file.
-//
-// #define DR_WAV_NO_CONVERSION_API
-// Disables conversion APIs such as drwav_read_f32() and drwav_s16_to_f32().
-//
-// #define DR_WAV_NO_STDIO
-// Disables drwav_open_file(), drwav_open_file_write(), etc.
-//
-//
-//
-// QUICK NOTES
-// - Samples are always interleaved.
-// - The default read function does not do any data conversion. Use drwav_read_f32() to read and convert audio data
-// to IEEE 32-bit floating point samples, drwav_read_s32() to read samples as signed 32-bit PCM and drwav_read_s16()
-// to read samples as signed 16-bit PCM. Tested and supported internal formats include the following:
-// - Unsigned 8-bit PCM
-// - Signed 12-bit PCM
-// - Signed 16-bit PCM
-// - Signed 24-bit PCM
-// - Signed 32-bit PCM
-// - IEEE 32-bit floating point
-// - IEEE 64-bit floating point
-// - A-law and u-law
-// - Microsoft ADPCM
-// - IMA ADPCM (DVI, format code 0x11)
-// - dr_wav will try to read the WAV file as best it can, even if it's not strictly conformant to the WAV format.
+/*
+WAV audio loader and writer. Choice of public domain or MIT-0. See license statements at the end of this file.
+dr_wav - v0.9.1 - 2019-05-05
+
+David Reid - mackron@gmail.com
+*/
+
+/*
+DEPRECATED APIS
+===============
+Version 0.9.0 deprecated the per-sample reading and seeking APIs and replaced them with versions that work on the resolution
+of a PCM frame instead. For example, given a stereo WAV file, previously you would pass 2 to drwav_read_f32() to read one
+PCM frame, whereas now you would pass in 1 to drwav_read_pcm_frames_f32(). The old APIs would return the number of samples
+read, whereas now it will return the number of PCM frames. Below is a list of APIs that have been deprecated and their
+replacements.
+
+ drwav_read() -> drwav_read_pcm_frames()
+ drwav_read_s16() -> drwav_read_pcm_frames_s16()
+ drwav_read_f32() -> drwav_read_pcm_frames_f32()
+ drwav_read_s32() -> drwav_read_pcm_frames_s32()
+ drwav_seek_to_sample() -> drwav_seek_to_pcm_frame()
+ drwav_write() -> drwav_write_pcm_frames()
+ drwav_open_and_read_s16() -> drwav_open_and_read_pcm_frames_s16()
+ drwav_open_and_read_f32() -> drwav_open_and_read_pcm_frames_f32()
+ drwav_open_and_read_s32() -> drwav_open_and_read_pcm_frames_s32()
+ drwav_open_file_and_read_s16() -> drwav_open_file_and_read_pcm_frames_s16()
+ drwav_open_file_and_read_f32() -> drwav_open_file_and_read_pcm_frames_f32()
+ drwav_open_file_and_read_s32() -> drwav_open_file_and_read_pcm_frames_s32()
+ drwav_open_memory_and_read_s16() -> drwav_open_memory_and_read_pcm_frames_s16()
+ drwav_open_memory_and_read_f32() -> drwav_open_memory_and_read_pcm_frames_f32()
+ drwav_open_memory_and_read_s32() -> drwav_open_memory_and_read_pcm_frames_s32()
+ drwav::totalSampleCount -> drwav::totalPCMFrameCount
+
+Rationale:
+ 1) Most programs will want to read in multiples of the channel count which demands a per-frame reading API. Per-sample
+ reading just adds complexity and maintenance costs for no practical benefit.
+ 2) This is consistent with my other decoders - dr_flac and dr_mp3.
+
+These APIs will be removed completely in version 0.10.0. You can continue to use drwav_read_raw() if you need per-sample
+reading.
+*/
+
+/*
+USAGE
+=====
+This is a single-file library. To use it, do something like the following in one .c file.
+ #define DR_WAV_IMPLEMENTATION
+ #include "dr_wav.h"
+
+You can then #include this file in other parts of the program as you would with any other header file. Do something
+like the following to read audio data:
+
+ drwav wav;
+ if (!drwav_init_file(&wav, "my_song.wav")) {
+ // Error opening WAV file.
+ }
+
+ drwav_int32* pDecodedInterleavedSamples = malloc(wav.totalPCMFrameCount * wav.channels * sizeof(drwav_int32));
+ size_t numberOfSamplesActuallyDecoded = drwav_read_pcm_frames_s32(&wav, wav.totalPCMFrameCount, pDecodedInterleavedSamples);
+
+ ...
+
+ drwav_uninit(&wav);
+
+You can also use drwav_open() to allocate and initialize the loader for you:
+
+ drwav* pWav = drwav_open_file("my_song.wav");
+ if (pWav == NULL) {
+ // Error opening WAV file.
+ }
+
+ ...
+
+ drwav_close(pWav);
+
+If you just want to quickly open and read the audio data in a single operation you can do something like this:
+
+ unsigned int channels;
+ unsigned int sampleRate;
+ drwav_uint64 totalPCMFrameCount;
+ float* pSampleData = drwav_open_file_and_read_pcm_frames_f32("my_song.wav", &channels, &sampleRate, &totalPCMFrameCount);
+ if (pSampleData == NULL) {
+ // Error opening and reading WAV file.
+ }
+ ...
+
+ drwav_free(pSampleData);
+
+The examples above use versions of the API that convert the audio data to a consistent format (32-bit signed PCM, in
+this case), but you can still output the audio data in its internal format (see notes below for supported formats):
+
+ size_t samplesRead = drwav_read_pcm_frames(&wav, wav.totalPCMFrameCount, pDecodedInterleavedSamples);
+
+You can also read the raw bytes of audio data, which could be useful if dr_wav does not have native support for
+a particular data format:
+
+ size_t bytesRead = drwav_read_raw(&wav, bytesToRead, pRawDataBuffer);
+
+
+dr_wav can also be used to output WAV files. This does not currently support compressed formats. To use this, look at
+drwav_open_write(), drwav_open_file_write(), etc. Use drwav_write_pcm_frames() to write samples, or drwav_write_raw()
+to write raw data in the "data" chunk.
+
+ drwav_data_format format;
+ format.container = drwav_container_riff; // <-- drwav_container_riff = normal WAV files, drwav_container_w64 = Sony Wave64.
+ format.format = DR_WAVE_FORMAT_PCM; // <-- Any of the DR_WAVE_FORMAT_* codes.
+ format.channels = 2;
+ format.sampleRate = 44100;
+ format.bitsPerSample = 16;
+ drwav* pWav = drwav_open_file_write("data/recording.wav", &format);
+
+ ...
+
+ drwav_uint64 samplesWritten = drwav_write_pcm_frames(pWav, frameCount, pSamples);
+
+
+dr_wav has seamless support the Sony Wave64 format. The decoder will automatically detect it and it should Just Work
+without any manual intervention.
+
+
+OPTIONS
+=======
+#define these options before including this file.
+
+#define DR_WAV_NO_CONVERSION_API
+ Disables conversion APIs such as drwav_read_pcm_frames_f32() and drwav_s16_to_f32().
+
+#define DR_WAV_NO_STDIO
+ Disables drwav_open_file(), drwav_open_file_write(), etc.
+
+
+
+QUICK NOTES
+===========
+- Samples are always interleaved.
+- The default read function does not do any data conversion. Use drwav_read_pcm_frames_f32(), drwav_read_pcm_frames_s32()
+ and drwav_read_pcm_frames_s16() to read and convert audio data to 32-bit floating point, signed 32-bit integer and
+ signed 16-bit integer samples respectively. Tested and supported internal formats include the following:
+ - Unsigned 8-bit PCM
+ - Signed 12-bit PCM
+ - Signed 16-bit PCM
+ - Signed 24-bit PCM
+ - Signed 32-bit PCM
+ - IEEE 32-bit floating point
+ - IEEE 64-bit floating point
+ - A-law and u-law
+ - Microsoft ADPCM
+ - IMA ADPCM (DVI, format code 0x11)
+- dr_wav will try to read the WAV file as best it can, even if it's not strictly conformant to the WAV format.
+*/
#ifndef dr_wav_h
#define dr_wav_h
@@ -145,7 +184,7 @@ typedef drwav_uint32 drwav_bool32;
extern "C" {
#endif
-// Common data formats.
+/* Common data formats. */
#define DR_WAVE_FORMAT_PCM 0x1
#define DR_WAVE_FORMAT_ADPCM 0x2
#define DR_WAVE_FORMAT_IEEE_FLOAT 0x3
@@ -154,6 +193,14 @@ extern "C" {
#define DR_WAVE_FORMAT_DVI_ADPCM 0x11
#define DR_WAVE_FORMAT_EXTENSIBLE 0xFFFE
+/* Constants. */
+#ifndef DRWAV_MAX_SMPL_LOOPS
+#define DRWAV_MAX_SMPL_LOOPS 1
+#endif
+
+/* Flags to pass into drwav_init_ex(), etc. */
+#define DRWAV_SEQUENTIAL 0x00000001
+
typedef enum
{
drwav_seek_origin_start,
@@ -166,42 +213,84 @@ typedef enum
drwav_container_w64
} drwav_container;
-// Callback for when data is read. Return value is the number of bytes actually read.
-//
-// pUserData [in] The user data that was passed to drwav_init(), drwav_open() and family.
-// pBufferOut [out] The output buffer.
-// bytesToRead [in] The number of bytes to read.
-//
-// Returns the number of bytes actually read.
-//
-// A return value of less than bytesToRead indicates the end of the stream. Do _not_ return from this callback until
-// either the entire bytesToRead is filled or you have reached the end of the stream.
+typedef struct
+{
+ union
+ {
+ drwav_uint8 fourcc[4];
+ drwav_uint8 guid[16];
+ } id;
+
+ /* The size in bytes of the chunk. */
+ drwav_uint64 sizeInBytes;
+
+ /*
+ RIFF = 2 byte alignment.
+ W64 = 8 byte alignment.
+ */
+ unsigned int paddingSize;
+} drwav_chunk_header;
+
+/*
+Callback for when data is read. Return value is the number of bytes actually read.
+
+pUserData [in] The user data that was passed to drwav_init(), drwav_open() and family.
+pBufferOut [out] The output buffer.
+bytesToRead [in] The number of bytes to read.
+
+Returns the number of bytes actually read.
+
+A return value of less than bytesToRead indicates the end of the stream. Do _not_ return from this callback until
+either the entire bytesToRead is filled or you have reached the end of the stream.
+*/
typedef size_t (* drwav_read_proc)(void* pUserData, void* pBufferOut, size_t bytesToRead);
-// Callback for when data is written. Returns value is the number of bytes actually written.
-//
-// pUserData [in] The user data that was passed to drwav_init_write(), drwav_open_write() and family.
-// pData [out] A pointer to the data to write.
-// bytesToWrite [in] The number of bytes to write.
-//
-// Returns the number of bytes actually written.
-//
-// If the return value differs from bytesToWrite, it indicates an error.
+/*
+Callback for when data is written. Returns value is the number of bytes actually written.
+
+pUserData [in] The user data that was passed to drwav_init_write(), drwav_open_write() and family.
+pData [out] A pointer to the data to write.
+bytesToWrite [in] The number of bytes to write.
+
+Returns the number of bytes actually written.
+
+If the return value differs from bytesToWrite, it indicates an error.
+*/
typedef size_t (* drwav_write_proc)(void* pUserData, const void* pData, size_t bytesToWrite);
-// Callback for when data needs to be seeked.
-//
-// pUserData [in] The user data that was passed to drwav_init(), drwav_open() and family.
-// offset [in] The number of bytes to move, relative to the origin. Will never be negative.
-// origin [in] The origin of the seek - the current position or the start of the stream.
-//
-// Returns whether or not the seek was successful.
-//
-// Whether or not it is relative to the beginning or current position is determined by the "origin" parameter which
-// will be either drwav_seek_origin_start or drwav_seek_origin_current.
+/*
+Callback for when data needs to be seeked.
+
+pUserData [in] The user data that was passed to drwav_init(), drwav_open() and family.
+offset [in] The number of bytes to move, relative to the origin. Will never be negative.
+origin [in] The origin of the seek - the current position or the start of the stream.
+
+Returns whether or not the seek was successful.
+
+Whether or not it is relative to the beginning or current position is determined by the "origin" parameter which
+will be either drwav_seek_origin_start or drwav_seek_origin_current.
+*/
typedef drwav_bool32 (* drwav_seek_proc)(void* pUserData, int offset, drwav_seek_origin origin);
-// Structure for internal use. Only used for loaders opened with drwav_open_memory().
+/*
+Callback for when drwav_init_ex/drwav_open_ex finds a chunk.
+
+pChunkUserData [in] The user data that was passed to the pChunkUserData parameter of drwav_init_ex(), drwav_open_ex() and family.
+onRead [in] A pointer to the function to call when reading.
+onSeek [in] A pointer to the function to call when seeking.
+pReadSeekUserData [in] The user data that was passed to the pReadSeekUserData parameter of drwav_init_ex(), drwav_open_ex() and family.
+pChunkHeader [in] A pointer to an object containing basic header information about the chunk. Use this to identify the chunk.
+
+Returns the number of bytes read + seeked.
+
+To read data from the chunk, call onRead(), passing in pReadSeekUserData as the first parameter. Do the same
+for seeking with onSeek(). The return value must be the total number of bytes you have read _plus_ seeked.
+
+You must not attempt to read beyond the boundary of the chunk.
+*/
+typedef drwav_uint64 (* drwav_chunk_proc)(void* pChunkUserData, drwav_read_proc onRead, drwav_seek_proc onSeek, void* pReadSeekUserData, const drwav_chunk_header* pChunkHeader);
+
+/* Structure for internal use. Only used for loaders opened with drwav_open_memory(). */
typedef struct
{
const drwav_uint8* data;
@@ -209,7 +298,7 @@ typedef struct
size_t currentReadPos;
} drwav__memory_stream;
-// Structure for internal use. Only used for writers opened with drwav_open_memory_write().
+/* Structure for internal use. Only used for writers opened with drwav_open_memory_write(). */
typedef struct
{
void** ppData;
@@ -221,8 +310,8 @@ typedef struct
typedef struct
{
- drwav_container container; // RIFF, W64.
- drwav_uint32 format; // DR_WAVE_FORMAT_*
+ drwav_container container; /* RIFF, W64. */
+ drwav_uint32 format; /* DR_WAVE_FORMAT_* */
drwav_uint32 channels;
drwav_uint32 sampleRate;
drwav_uint32 bitsPerSample;
@@ -230,473 +319,591 @@ typedef struct
typedef struct
{
- // The format tag exactly as specified in the wave file's "fmt" chunk. This can be used by applications
- // that require support for data formats not natively supported by dr_wav.
+ /*
+ The format tag exactly as specified in the wave file's "fmt" chunk. This can be used by applications
+ that require support for data formats not natively supported by dr_wav.
+ */
drwav_uint16 formatTag;
- // The number of channels making up the audio data. When this is set to 1 it is mono, 2 is stereo, etc.
+ /* The number of channels making up the audio data. When this is set to 1 it is mono, 2 is stereo, etc. */
drwav_uint16 channels;
- // The sample rate. Usually set to something like 44100.
+ /* The sample rate. Usually set to something like 44100. */
drwav_uint32 sampleRate;
- // Average bytes per second. You probably don't need this, but it's left here for informational purposes.
+ /* Average bytes per second. You probably don't need this, but it's left here for informational purposes. */
drwav_uint32 avgBytesPerSec;
- // Block align. This is equal to the number of channels * bytes per sample.
+ /* Block align. This is equal to the number of channels * bytes per sample. */
drwav_uint16 blockAlign;
- // Bits per sample.
+ /* Bits per sample. */
drwav_uint16 bitsPerSample;
- // The size of the extended data. Only used internally for validation, but left here for informational purposes.
+ /* The size of the extended data. Only used internally for validation, but left here for informational purposes. */
drwav_uint16 extendedSize;
- // The number of valid bits per sample. When <formatTag> is equal to WAVE_FORMAT_EXTENSIBLE, <bitsPerSample>
- // is always rounded up to the nearest multiple of 8. This variable contains information about exactly how
- // many bits a valid per sample. Mainly used for informational purposes.
+ /*
+ The number of valid bits per sample. When <formatTag> is equal to WAVE_FORMAT_EXTENSIBLE, <bitsPerSample>
+ is always rounded up to the nearest multiple of 8. This variable contains information about exactly how
+ many bits a valid per sample. Mainly used for informational purposes.
+ */
drwav_uint16 validBitsPerSample;
- // The channel mask. Not used at the moment.
+ /* The channel mask. Not used at the moment. */
drwav_uint32 channelMask;
- // The sub-format, exactly as specified by the wave file.
+ /* The sub-format, exactly as specified by the wave file. */
drwav_uint8 subFormat[16];
} drwav_fmt;
typedef struct
{
- // A pointer to the function to call when more data is needed.
+ drwav_uint32 cuePointId;
+ drwav_uint32 type;
+ drwav_uint32 start;
+ drwav_uint32 end;
+ drwav_uint32 fraction;
+ drwav_uint32 playCount;
+} drwav_smpl_loop;
+
+ typedef struct
+{
+ drwav_uint32 manufacturer;
+ drwav_uint32 product;
+ drwav_uint32 samplePeriod;
+ drwav_uint32 midiUnityNotes;
+ drwav_uint32 midiPitchFraction;
+ drwav_uint32 smpteFormat;
+ drwav_uint32 smpteOffset;
+ drwav_uint32 numSampleLoops;
+ drwav_uint32 samplerData;
+ drwav_smpl_loop loops[DRWAV_MAX_SMPL_LOOPS];
+} drwav_smpl;
+
+typedef struct
+{
+ /* A pointer to the function to call when more data is needed. */
drwav_read_proc onRead;
- // A pointer to the function to call when data needs to be written. Only used when the drwav object is opened in write mode.
+ /* A pointer to the function to call when data needs to be written. Only used when the drwav object is opened in write mode. */
drwav_write_proc onWrite;
- // A pointer to the function to call when the wav file needs to be seeked.
+ /* A pointer to the function to call when the wav file needs to be seeked. */
drwav_seek_proc onSeek;
- // The user data to pass to callbacks.
+ /* The user data to pass to callbacks. */
void* pUserData;
- // Whether or not the WAV file is formatted as a standard RIFF file or W64.
+ /* Whether or not the WAV file is formatted as a standard RIFF file or W64. */
drwav_container container;
- // Structure containing format information exactly as specified by the wav file.
+ /* Structure containing format information exactly as specified by the wav file. */
drwav_fmt fmt;
- // The sample rate. Will be set to something like 44100.
+ /* The sample rate. Will be set to something like 44100. */
drwav_uint32 sampleRate;
- // The number of channels. This will be set to 1 for monaural streams, 2 for stereo, etc.
+ /* The number of channels. This will be set to 1 for monaural streams, 2 for stereo, etc. */
drwav_uint16 channels;
- // The bits per sample. Will be set to something like 16, 24, etc.
+ /* The bits per sample. Will be set to something like 16, 24, etc. */
drwav_uint16 bitsPerSample;
- // The number of bytes per sample.
- drwav_uint16 bytesPerSample;
-
- // Equal to fmt.formatTag, or the value specified by fmt.subFormat if fmt.formatTag is equal to 65534 (WAVE_FORMAT_EXTENSIBLE).
+ /* Equal to fmt.formatTag, or the value specified by fmt.subFormat if fmt.formatTag is equal to 65534 (WAVE_FORMAT_EXTENSIBLE). */
drwav_uint16 translatedFormatTag;
- // The total number of samples making up the audio data. Use <totalSampleCount> * <bytesPerSample> to calculate
- // the required size of a buffer to hold the entire audio data.
- drwav_uint64 totalSampleCount;
+ /* The total number of PCM frames making up the audio data. */
+ drwav_uint64 totalPCMFrameCount;
- // The size in bytes of the data chunk.
+ /* The size in bytes of the data chunk. */
drwav_uint64 dataChunkDataSize;
- // The position in the stream of the first byte of the data chunk. This is used for seeking.
+ /* The position in the stream of the first byte of the data chunk. This is used for seeking. */
drwav_uint64 dataChunkDataPos;
- // The number of bytes remaining in the data chunk.
+ /* The number of bytes remaining in the data chunk. */
drwav_uint64 bytesRemaining;
- // Only used in sequential write mode. Keeps track of the desired size of the "data" chunk at the point of initialization time. Always
- // set to 0 for non-sequential writes and when the drwav object is opened in read mode. Used for validation.
+ /*
+ Only used in sequential write mode. Keeps track of the desired size of the "data" chunk at the point of initialization time. Always
+ set to 0 for non-sequential writes and when the drwav object is opened in read mode. Used for validation.
+ */
drwav_uint64 dataChunkDataSizeTargetWrite;
- // Keeps track of whether or not the wav writer was initialized in sequential mode.
+ /* Keeps track of whether or not the wav writer was initialized in sequential mode. */
drwav_bool32 isSequentialWrite;
- // A hack to avoid a DRWAV_MALLOC() when opening a decoder with drwav_open_memory().
+ /* smpl chunk. */
+ drwav_smpl smpl;
+
+
+ /* A hack to avoid a DRWAV_MALLOC() when opening a decoder with drwav_open_memory(). */
drwav__memory_stream memoryStream;
drwav__memory_stream_write memoryStreamWrite;
- // Generic data for compressed formats. This data is shared across all block-compressed formats.
+ /* Generic data for compressed formats. This data is shared across all block-compressed formats. */
struct
{
- drwav_uint64 iCurrentSample; // The index of the next sample that will be read by drwav_read_*(). This is used with "totalSampleCount" to ensure we don't read excess samples at the end of the last block.
+ drwav_uint64 iCurrentSample; /* The index of the next sample that will be read by drwav_read_*(). This is used with "totalSampleCount" to ensure we don't read excess samples at the end of the last block. */
} compressed;
- // Microsoft ADPCM specific data.
+ /* Microsoft ADPCM specific data. */
struct
{
drwav_uint32 bytesRemainingInBlock;
drwav_uint16 predictor[2];
drwav_int32 delta[2];
- drwav_int32 cachedSamples[4]; // Samples are stored in this cache during decoding.
+ drwav_int32 cachedSamples[4]; /* Samples are stored in this cache during decoding. */
drwav_uint32 cachedSampleCount;
- drwav_int32 prevSamples[2][2]; // The previous 2 samples for each channel (2 channels at most).
+ drwav_int32 prevSamples[2][2]; /* The previous 2 samples for each channel (2 channels at most). */
} msadpcm;
- // IMA ADPCM specific data.
+ /* IMA ADPCM specific data. */
struct
{
drwav_uint32 bytesRemainingInBlock;
drwav_int32 predictor[2];
drwav_int32 stepIndex[2];
- drwav_int32 cachedSamples[16]; // Samples are stored in this cache during decoding.
+ drwav_int32 cachedSamples[16]; /* Samples are stored in this cache during decoding. */
drwav_uint32 cachedSampleCount;
} ima;
+
+
+ drwav_uint64 totalSampleCount; /* <-- DEPRECATED. Will be removed in a future version. */
} drwav;
-// Initializes a pre-allocated drwav object.
-//
-// onRead [in] The function to call when data needs to be read from the client.
-// onSeek [in] The function to call when the read position of the client data needs to move.
-// pUserData [in, optional] A pointer to application defined data that will be passed to onRead and onSeek.
-//
-// Returns true if successful; false otherwise.
-//
-// Close the loader with drwav_uninit().
-//
-// This is the lowest level function for initializing a WAV file. You can also use drwav_init_file() and drwav_init_memory()
-// to open the stream from a file or from a block of memory respectively.
-//
-// If you want dr_wav to manage the memory allocation for you, consider using drwav_open() instead. This will allocate
-// a drwav object on the heap and return a pointer to it.
-//
-// See also: drwav_init_file(), drwav_init_memory(), drwav_uninit()
+/*
+Initializes a pre-allocated drwav object.
+
+pWav [out] A pointer to the drwav object being initialized.
+onRead [in] The function to call when data needs to be read from the client.
+onSeek [in] The function to call when the read position of the client data needs to move.
+onChunk [in, optional] The function to call when a chunk is enumerated at initialized time.
+pUserData, pReadSeekUserData [in, optional] A pointer to application defined data that will be passed to onRead and onSeek.
+pChunkUserData [in, optional] A pointer to application defined data that will be passed to onChunk.
+flags [in, optional] A set of flags for controlling how things are loaded.
+
+Returns true if successful; false otherwise.
+
+Close the loader with drwav_uninit().
+
+This is the lowest level function for initializing a WAV file. You can also use drwav_init_file() and drwav_init_memory()
+to open the stream from a file or from a block of memory respectively.
+
+If you want dr_wav to manage the memory allocation for you, consider using drwav_open() instead. This will allocate
+a drwav object on the heap and return a pointer to it.
+
+Possible values for flags:
+ DRWAV_SEQUENTIAL: Never perform a backwards seek while loading. This disables the chunk callback and will cause this function
+ to return as soon as the data chunk is found. Any chunks after the data chunk will be ignored.
+
+drwav_init() is equivalent to "drwav_init_ex(pWav, onRead, onSeek, NULL, pUserData, NULL, 0);".
+
+The onChunk callback is not called for the WAVE or FMT chunks. The contents of the FMT chunk can be read from pWav->fmt
+after the function returns.
+
+See also: drwav_init_file(), drwav_init_memory(), drwav_uninit()
+*/
drwav_bool32 drwav_init(drwav* pWav, drwav_read_proc onRead, drwav_seek_proc onSeek, void* pUserData);
+drwav_bool32 drwav_init_ex(drwav* pWav, drwav_read_proc onRead, drwav_seek_proc onSeek, drwav_chunk_proc onChunk, void* pReadSeekUserData, void* pChunkUserData, drwav_uint32 flags);
+
+/*
+Initializes a pre-allocated drwav object for writing.
+
+onWrite [in] The function to call when data needs to be written.
+onSeek [in] The function to call when the write position needs to move.
+pUserData [in, optional] A pointer to application defined data that will be passed to onWrite and onSeek.
+
+Returns true if successful; false otherwise.
-// Initializes a pre-allocated drwav object for writing.
-//
-// onWrite [in] The function to call when data needs to be written.
-// onSeek [in] The function to call when the write position needs to move.
-// pUserData [in, optional] A pointer to application defined data that will be passed to onWrite and onSeek.
-//
-// Returns true if successful; false otherwise.
-//
-// Close the writer with drwav_uninit().
-//
-// This is the lowest level function for initializing a WAV file. You can also use drwav_init_file() and drwav_init_memory()
-// to open the stream from a file or from a block of memory respectively.
-//
-// If the total sample count is known, you can use drwav_init_write_sequential(). This avoids the need for dr_wav to perform
-// a post-processing step for storing the total sample count and the size of the data chunk which requires a backwards seek.
-//
-// If you want dr_wav to manage the memory allocation for you, consider using drwav_open() instead. This will allocate
-// a drwav object on the heap and return a pointer to it.
-//
-// See also: drwav_init_file_write(), drwav_init_memory_write(), drwav_uninit()
+Close the writer with drwav_uninit().
+
+This is the lowest level function for initializing a WAV file. You can also use drwav_init_file() and drwav_init_memory()
+to open the stream from a file or from a block of memory respectively.
+
+If the total sample count is known, you can use drwav_init_write_sequential(). This avoids the need for dr_wav to perform
+a post-processing step for storing the total sample count and the size of the data chunk which requires a backwards seek.
+
+If you want dr_wav to manage the memory allocation for you, consider using drwav_open() instead. This will allocate
+a drwav object on the heap and return a pointer to it.
+
+See also: drwav_init_file_write(), drwav_init_memory_write(), drwav_uninit()
+*/
drwav_bool32 drwav_init_write(drwav* pWav, const drwav_data_format* pFormat, drwav_write_proc onWrite, drwav_seek_proc onSeek, void* pUserData);
drwav_bool32 drwav_init_write_sequential(drwav* pWav, const drwav_data_format* pFormat, drwav_uint64 totalSampleCount, drwav_write_proc onWrite, void* pUserData);
-// Uninitializes the given drwav object.
-//
-// Use this only for objects initialized with drwav_init().
+/*
+Uninitializes the given drwav object.
+
+Use this only for objects initialized with drwav_init().
+*/
void drwav_uninit(drwav* pWav);
-// Opens a wav file using the given callbacks.
-//
-// onRead [in] The function to call when data needs to be read from the client.
-// onSeek [in] The function to call when the read position of the client data needs to move.
-// pUserData [in, optional] A pointer to application defined data that will be passed to onRead and onSeek.
-//
-// Returns null on error.
-//
-// Close the loader with drwav_close().
-//
-// You can also use drwav_open_file() and drwav_open_memory() to open the stream from a file or from a block of
-// memory respectively.
-//
-// This is different from drwav_init() in that it will allocate the drwav object for you via DRWAV_MALLOC() before
-// initializing it.
-//
-// See also: drwav_open_file(), drwav_open_memory(), drwav_close()
+/*
+Opens a wav file using the given callbacks.
+
+onRead [in] The function to call when data needs to be read from the client.
+onSeek [in] The function to call when the read position of the client data needs to move.
+pUserData [in, optional] A pointer to application defined data that will be passed to onRead and onSeek.
+
+Returns null on error.
+
+Close the loader with drwav_close().
+
+You can also use drwav_open_file() and drwav_open_memory() to open the stream from a file or from a block of
+memory respectively.
+
+This is different from drwav_init() in that it will allocate the drwav object for you via DRWAV_MALLOC() before
+initializing it.
+
+See also: drwav_init(), drwav_open_file(), drwav_open_memory(), drwav_close()
+*/
drwav* drwav_open(drwav_read_proc onRead, drwav_seek_proc onSeek, void* pUserData);
+drwav* drwav_open_ex(drwav_read_proc onRead, drwav_seek_proc onSeek, drwav_chunk_proc onChunk, void* pReadSeekUserData, void* pChunkUserData, drwav_uint32 flags);
+
+/*
+Opens a wav file for writing using the given callbacks.
-// Opens a wav file for writing using the given callbacks.
-//
-// onWrite [in] The function to call when data needs to be written.
-// onSeek [in] The function to call when the write position needs to move.
-// pUserData [in, optional] A pointer to application defined data that will be passed to onWrite and onSeek.
-//
-// Returns null on error.
-//
-// Close the loader with drwav_close().
-//
-// You can also use drwav_open_file_write() and drwav_open_memory_write() to open the stream from a file or from a block
-// of memory respectively.
-//
-// This is different from drwav_init_write() in that it will allocate the drwav object for you via DRWAV_MALLOC() before
-// initializing it.
-//
-// See also: drwav_open_file_write(), drwav_open_memory_write(), drwav_close()
+onWrite [in] The function to call when data needs to be written.
+onSeek [in] The function to call when the write position needs to move.
+pUserData [in, optional] A pointer to application defined data that will be passed to onWrite and onSeek.
+
+Returns null on error.
+
+Close the loader with drwav_close().
+
+You can also use drwav_open_file_write() and drwav_open_memory_write() to open the stream from a file or from a block
+of memory respectively.
+
+This is different from drwav_init_write() in that it will allocate the drwav object for you via DRWAV_MALLOC() before
+initializing it.
+
+See also: drwav_open_file_write(), drwav_open_memory_write(), drwav_close()
+*/
drwav* drwav_open_write(const drwav_data_format* pFormat, drwav_write_proc onWrite, drwav_seek_proc onSeek, void* pUserData);
drwav* drwav_open_write_sequential(const drwav_data_format* pFormat, drwav_uint64 totalSampleCount, drwav_write_proc onWrite, void* pUserData);
-// Uninitializes and deletes the the given drwav object.
-//
-// Use this only for objects created with drwav_open().
+/*
+Uninitializes and deletes the the given drwav object.
+
+Use this only for objects created with drwav_open().
+*/
void drwav_close(drwav* pWav);
-// Reads raw audio data.
-//
-// This is the lowest level function for reading audio data. It simply reads the given number of
-// bytes of the raw internal sample data.
-//
-// Consider using drwav_read_s16(), drwav_read_s32() or drwav_read_f32() for reading sample data in
-// a consistent format.
-//
-// Returns the number of bytes actually read.
+/*
+Reads raw audio data.
+
+This is the lowest level function for reading audio data. It simply reads the given number of
+bytes of the raw internal sample data.
+
+Consider using drwav_read_pcm_frames_s16(), drwav_read_pcm_frames_s32() or drwav_read_pcm_frames_f32() for
+reading sample data in a consistent format.
+
+Returns the number of bytes actually read.
+*/
size_t drwav_read_raw(drwav* pWav, size_t bytesToRead, void* pBufferOut);
-// Reads a chunk of audio data in the native internal format.
-//
-// This is typically the most efficient way to retrieve audio data, but it does not do any format
-// conversions which means you'll need to convert the data manually if required.
-//
-// If the return value is less than <samplesToRead> it means the end of the file has been reached or
-// you have requested more samples than can possibly fit in the output buffer.
-//
-// This function will only work when sample data is of a fixed size and uncompressed. If you are
-// using a compressed format consider using drwav_read_raw() or drwav_read_s16/s32/f32/etc().
-drwav_uint64 drwav_read(drwav* pWav, drwav_uint64 samplesToRead, void* pBufferOut);
+/*
+Reads a chunk of audio data in the native internal format.
-// Seeks to the given sample.
-//
-// Returns true if successful; false otherwise.
-drwav_bool32 drwav_seek_to_sample(drwav* pWav, drwav_uint64 sample);
+This is typically the most efficient way to retrieve audio data, but it does not do any format
+conversions which means you'll need to convert the data manually if required.
+
+If the return value is less than <framesToRead> it means the end of the file has been reached or
+you have requested more samples than can possibly fit in the output buffer.
+
+This function will only work when sample data is of a fixed size and uncompressed. If you are
+using a compressed format consider using drwav_read_raw() or drwav_read_pcm_frames_s16/s32/f32/etc().
+*/
+drwav_uint64 drwav_read_pcm_frames(drwav* pWav, drwav_uint64 framesToRead, void* pBufferOut);
+
+/*
+Seeks to the given PCM frame.
+
+Returns true if successful; false otherwise.
+*/
+drwav_bool32 drwav_seek_to_pcm_frame(drwav* pWav, drwav_uint64 targetFrameIndex);
-// Writes raw audio data.
-//
-// Returns the number of bytes actually written. If this differs from bytesToWrite, it indicates an error.
+/*
+Writes raw audio data.
+
+Returns the number of bytes actually written. If this differs from bytesToWrite, it indicates an error.
+*/
size_t drwav_write_raw(drwav* pWav, size_t bytesToWrite, const void* pData);
-// Writes audio data based on sample counts.
-//
-// Returns the number of samples written.
-drwav_uint64 drwav_write(drwav* pWav, drwav_uint64 samplesToWrite, const void* pData);
+/*
+Writes PCM frames.
+Returns the number of PCM frames written.
+*/
+drwav_uint64 drwav_write_pcm_frames(drwav* pWav, drwav_uint64 framesToWrite, const void* pData);
-//// Conversion Utilities ////
+/* Conversion Utilities */
#ifndef DR_WAV_NO_CONVERSION_API
-// Reads a chunk of audio data and converts it to signed 16-bit PCM samples.
-//
-// Returns the number of samples actually read.
-//
-// If the return value is less than <samplesToRead> it means the end of the file has been reached.
-drwav_uint64 drwav_read_s16(drwav* pWav, drwav_uint64 samplesToRead, drwav_int16* pBufferOut);
+/*
+Reads a chunk of audio data and converts it to signed 16-bit PCM samples.
-// Low-level function for converting unsigned 8-bit PCM samples to signed 16-bit PCM samples.
+Returns the number of PCM frames actually read.
+
+If the return value is less than <framesToRead> it means the end of the file has been reached.
+*/
+drwav_uint64 drwav_read_pcm_frames_s16(drwav* pWav, drwav_uint64 framesToRead, drwav_int16* pBufferOut);
+
+/* Low-level function for converting unsigned 8-bit PCM samples to signed 16-bit PCM samples. */
void drwav_u8_to_s16(drwav_int16* pOut, const drwav_uint8* pIn, size_t sampleCount);
-// Low-level function for converting signed 24-bit PCM samples to signed 16-bit PCM samples.
+/* Low-level function for converting signed 24-bit PCM samples to signed 16-bit PCM samples. */
void drwav_s24_to_s16(drwav_int16* pOut, const drwav_uint8* pIn, size_t sampleCount);
-// Low-level function for converting signed 32-bit PCM samples to signed 16-bit PCM samples.
+/* Low-level function for converting signed 32-bit PCM samples to signed 16-bit PCM samples. */
void drwav_s32_to_s16(drwav_int16* pOut, const drwav_int32* pIn, size_t sampleCount);
-// Low-level function for converting IEEE 32-bit floating point samples to signed 16-bit PCM samples.
+/* Low-level function for converting IEEE 32-bit floating point samples to signed 16-bit PCM samples. */
void drwav_f32_to_s16(drwav_int16* pOut, const float* pIn, size_t sampleCount);
-// Low-level function for converting IEEE 64-bit floating point samples to signed 16-bit PCM samples.
+/* Low-level function for converting IEEE 64-bit floating point samples to signed 16-bit PCM samples. */
void drwav_f64_to_s16(drwav_int16* pOut, const double* pIn, size_t sampleCount);
-// Low-level function for converting A-law samples to signed 16-bit PCM samples.
+/* Low-level function for converting A-law samples to signed 16-bit PCM samples. */
void drwav_alaw_to_s16(drwav_int16* pOut, const drwav_uint8* pIn, size_t sampleCount);
-// Low-level function for converting u-law samples to signed 16-bit PCM samples.
+/* Low-level function for converting u-law samples to signed 16-bit PCM samples. */
void drwav_mulaw_to_s16(drwav_int16* pOut, const drwav_uint8* pIn, size_t sampleCount);
-// Reads a chunk of audio data and converts it to IEEE 32-bit floating point samples.
-//
-// Returns the number of samples actually read.
-//
-// If the return value is less than <samplesToRead> it means the end of the file has been reached.
-drwav_uint64 drwav_read_f32(drwav* pWav, drwav_uint64 samplesToRead, float* pBufferOut);
+/*
+Reads a chunk of audio data and converts it to IEEE 32-bit floating point samples.
+
+Returns the number of PCM frames actually read.
-// Low-level function for converting unsigned 8-bit PCM samples to IEEE 32-bit floating point samples.
+If the return value is less than <framesToRead> it means the end of the file has been reached.
+*/
+drwav_uint64 drwav_read_pcm_frames_f32(drwav* pWav, drwav_uint64 framesToRead, float* pBufferOut);
+
+/* Low-level function for converting unsigned 8-bit PCM samples to IEEE 32-bit floating point samples. */
void drwav_u8_to_f32(float* pOut, const drwav_uint8* pIn, size_t sampleCount);
-// Low-level function for converting signed 16-bit PCM samples to IEEE 32-bit floating point samples.
+/* Low-level function for converting signed 16-bit PCM samples to IEEE 32-bit floating point samples. */
void drwav_s16_to_f32(float* pOut, const drwav_int16* pIn, size_t sampleCount);
-// Low-level function for converting signed 24-bit PCM samples to IEEE 32-bit floating point samples.
+/* Low-level function for converting signed 24-bit PCM samples to IEEE 32-bit floating point samples. */
void drwav_s24_to_f32(float* pOut, const drwav_uint8* pIn, size_t sampleCount);
-// Low-level function for converting signed 32-bit PCM samples to IEEE 32-bit floating point samples.
+/* Low-level function for converting signed 32-bit PCM samples to IEEE 32-bit floating point samples. */
void drwav_s32_to_f32(float* pOut, const drwav_int32* pIn, size_t sampleCount);
-// Low-level function for converting IEEE 64-bit floating point samples to IEEE 32-bit floating point samples.
+/* Low-level function for converting IEEE 64-bit floating point samples to IEEE 32-bit floating point samples. */
void drwav_f64_to_f32(float* pOut, const double* pIn, size_t sampleCount);
-// Low-level function for converting A-law samples to IEEE 32-bit floating point samples.
+/* Low-level function for converting A-law samples to IEEE 32-bit floating point samples. */
void drwav_alaw_to_f32(float* pOut, const drwav_uint8* pIn, size_t sampleCount);
-// Low-level function for converting u-law samples to IEEE 32-bit floating point samples.
+/* Low-level function for converting u-law samples to IEEE 32-bit floating point samples. */
void drwav_mulaw_to_f32(float* pOut, const drwav_uint8* pIn, size_t sampleCount);
-// Reads a chunk of audio data and converts it to signed 32-bit PCM samples.
-//
-// Returns the number of samples actually read.
-//
-// If the return value is less than <samplesToRead> it means the end of the file has been reached.
-drwav_uint64 drwav_read_s32(drwav* pWav, drwav_uint64 samplesToRead, drwav_int32* pBufferOut);
+/*
+Reads a chunk of audio data and converts it to signed 32-bit PCM samples.
+
+Returns the number of PCM frames actually read.
+
+If the return value is less than <framesToRead> it means the end of the file has been reached.
+*/
+drwav_uint64 drwav_read_pcm_frames_s32(drwav* pWav, drwav_uint64 framesToRead, drwav_int32* pBufferOut);
-// Low-level function for converting unsigned 8-bit PCM samples to signed 32-bit PCM samples.
+/* Low-level function for converting unsigned 8-bit PCM samples to signed 32-bit PCM samples. */
void drwav_u8_to_s32(drwav_int32* pOut, const drwav_uint8* pIn, size_t sampleCount);
-// Low-level function for converting signed 16-bit PCM samples to signed 32-bit PCM samples.
+/* Low-level function for converting signed 16-bit PCM samples to signed 32-bit PCM samples. */
void drwav_s16_to_s32(drwav_int32* pOut, const drwav_int16* pIn, size_t sampleCount);
-// Low-level function for converting signed 24-bit PCM samples to signed 32-bit PCM samples.
+/* Low-level function for converting signed 24-bit PCM samples to signed 32-bit PCM samples. */
void drwav_s24_to_s32(drwav_int32* pOut, const drwav_uint8* pIn, size_t sampleCount);
-// Low-level function for converting IEEE 32-bit floating point samples to signed 32-bit PCM samples.
+/* Low-level function for converting IEEE 32-bit floating point samples to signed 32-bit PCM samples. */
void drwav_f32_to_s32(drwav_int32* pOut, const float* pIn, size_t sampleCount);
-// Low-level function for converting IEEE 64-bit floating point samples to signed 32-bit PCM samples.
+/* Low-level function for converting IEEE 64-bit floating point samples to signed 32-bit PCM samples. */
void drwav_f64_to_s32(drwav_int32* pOut, const double* pIn, size_t sampleCount);
-// Low-level function for converting A-law samples to signed 32-bit PCM samples.
+/* Low-level function for converting A-law samples to signed 32-bit PCM samples. */
void drwav_alaw_to_s32(drwav_int32* pOut, const drwav_uint8* pIn, size_t sampleCount);
-// Low-level function for converting u-law samples to signed 32-bit PCM samples.
+/* Low-level function for converting u-law samples to signed 32-bit PCM samples. */
void drwav_mulaw_to_s32(drwav_int32* pOut, const drwav_uint8* pIn, size_t sampleCount);
-#endif //DR_WAV_NO_CONVERSION_API
+#endif /* DR_WAV_NO_CONVERSION_API */
-//// High-Level Convenience Helpers ////
+/* High-Level Convenience Helpers */
#ifndef DR_WAV_NO_STDIO
+/*
+Helper for initializing a wave file using stdio.
-// Helper for initializing a wave file using stdio.
-//
-// This holds the internal FILE object until drwav_uninit() is called. Keep this in mind if you're caching drwav
-// objects because the operating system may restrict the number of file handles an application can have open at
-// any given time.
+This holds the internal FILE object until drwav_uninit() is called. Keep this in mind if you're caching drwav
+objects because the operating system may restrict the number of file handles an application can have open at
+any given time.
+*/
drwav_bool32 drwav_init_file(drwav* pWav, const char* filename);
+drwav_bool32 drwav_init_file_ex(drwav* pWav, const char* filename, drwav_chunk_proc onChunk, void* pChunkUserData, drwav_uint32 flags);
-// Helper for initializing a wave file for writing using stdio.
-//
-// This holds the internal FILE object until drwav_uninit() is called. Keep this in mind if you're caching drwav
-// objects because the operating system may restrict the number of file handles an application can have open at
-// any given time.
+/*
+Helper for initializing a wave file for writing using stdio.
+
+This holds the internal FILE object until drwav_uninit() is called. Keep this in mind if you're caching drwav
+objects because the operating system may restrict the number of file handles an application can have open at
+any given time.
+*/
drwav_bool32 drwav_init_file_write(drwav* pWav, const char* filename, const drwav_data_format* pFormat);
drwav_bool32 drwav_init_file_write_sequential(drwav* pWav, const char* filename, const drwav_data_format* pFormat, drwav_uint64 totalSampleCount);
-// Helper for opening a wave file using stdio.
-//
-// This holds the internal FILE object until drwav_close() is called. Keep this in mind if you're caching drwav
-// objects because the operating system may restrict the number of file handles an application can have open at
-// any given time.
+/*
+Helper for opening a wave file using stdio.
+
+This holds the internal FILE object until drwav_close() is called. Keep this in mind if you're caching drwav
+objects because the operating system may restrict the number of file handles an application can have open at
+any given time.
+*/
drwav* drwav_open_file(const char* filename);
+drwav* drwav_open_file_ex(const char* filename, drwav_chunk_proc onChunk, void* pChunkUserData, drwav_uint32 flags);
-// Helper for opening a wave file for writing using stdio.
-//
-// This holds the internal FILE object until drwav_close() is called. Keep this in mind if you're caching drwav
-// objects because the operating system may restrict the number of file handles an application can have open at
-// any given time.
+/*
+Helper for opening a wave file for writing using stdio.
+
+This holds the internal FILE object until drwav_close() is called. Keep this in mind if you're caching drwav
+objects because the operating system may restrict the number of file handles an application can have open at
+any given time.
+*/
drwav* drwav_open_file_write(const char* filename, const drwav_data_format* pFormat);
drwav* drwav_open_file_write_sequential(const char* filename, const drwav_data_format* pFormat, drwav_uint64 totalSampleCount);
-#endif //DR_WAV_NO_STDIO
+#endif /* DR_WAV_NO_STDIO */
-// Helper for initializing a loader from a pre-allocated memory buffer.
-//
-// This does not create a copy of the data. It is up to the application to ensure the buffer remains valid for
-// the lifetime of the drwav object.
-//
-// The buffer should contain the contents of the entire wave file, not just the sample data.
+/*
+Helper for initializing a loader from a pre-allocated memory buffer.
+
+This does not create a copy of the data. It is up to the application to ensure the buffer remains valid for
+the lifetime of the drwav object.
+
+The buffer should contain the contents of the entire wave file, not just the sample data.
+*/
drwav_bool32 drwav_init_memory(drwav* pWav, const void* data, size_t dataSize);
+drwav_bool32 drwav_init_memory_ex(drwav* pWav, const void* data, size_t dataSize, drwav_chunk_proc onChunk, void* pChunkUserData, drwav_uint32 flags);
+
+/*
+Helper for initializing a writer which outputs data to a memory buffer.
-// Helper for initializing a writer which outputs data to a memory buffer.
-//
-// dr_wav will manage the memory allocations, however it is up to the caller to free the data with drwav_free().
-//
-// The buffer will remain allocated even after drwav_uninit() is called. Indeed, the buffer should not be
-// considered valid until after drwav_uninit() has been called anyway.
+dr_wav will manage the memory allocations, however it is up to the caller to free the data with drwav_free().
+
+The buffer will remain allocated even after drwav_uninit() is called. Indeed, the buffer should not be
+considered valid until after drwav_uninit() has been called anyway.
+*/
drwav_bool32 drwav_init_memory_write(drwav* pWav, void** ppData, size_t* pDataSize, const drwav_data_format* pFormat);
drwav_bool32 drwav_init_memory_write_sequential(drwav* pWav, void** ppData, size_t* pDataSize, const drwav_data_format* pFormat, drwav_uint64 totalSampleCount);
-// Helper for opening a loader from a pre-allocated memory buffer.
-//
-// This does not create a copy of the data. It is up to the application to ensure the buffer remains valid for
-// the lifetime of the drwav object.
-//
-// The buffer should contain the contents of the entire wave file, not just the sample data.
+/*
+Helper for opening a loader from a pre-allocated memory buffer.
+
+This does not create a copy of the data. It is up to the application to ensure the buffer remains valid for
+the lifetime of the drwav object.
+
+The buffer should contain the contents of the entire wave file, not just the sample data.
+*/
drwav* drwav_open_memory(const void* data, size_t dataSize);
+drwav* drwav_open_memory_ex(const void* data, size_t dataSize, drwav_chunk_proc onChunk, void* pChunkUserData, drwav_uint32 flags);
-// Helper for opening a writer which outputs data to a memory buffer.
-//
-// dr_wav will manage the memory allocations, however it is up to the caller to free the data with drwav_free().
-//
-// The buffer will remain allocated even after drwav_close() is called. Indeed, the buffer should not be
-// considered valid until after drwav_close() has been called anyway.
+/*
+Helper for opening a writer which outputs data to a memory buffer.
+
+dr_wav will manage the memory allocations, however it is up to the caller to free the data with drwav_free().
+
+The buffer will remain allocated even after drwav_close() is called. Indeed, the buffer should not be
+considered valid until after drwav_close() has been called anyway.
+*/
drwav* drwav_open_memory_write(void** ppData, size_t* pDataSize, const drwav_data_format* pFormat);
drwav* drwav_open_memory_write_sequential(void** ppData, size_t* pDataSize, const drwav_data_format* pFormat, drwav_uint64 totalSampleCount);
#ifndef DR_WAV_NO_CONVERSION_API
-// Opens and reads a wav file in a single operation.
+/* Opens and reads a wav file in a single operation. */
+drwav_int16* drwav_open_and_read_pcm_frames_s16(drwav_read_proc onRead, drwav_seek_proc onSeek, void* pUserData, unsigned int* channels, unsigned int* sampleRate, drwav_uint64* totalFrameCount);
+float* drwav_open_and_read_pcm_frames_f32(drwav_read_proc onRead, drwav_seek_proc onSeek, void* pUserData, unsigned int* channels, unsigned int* sampleRate, drwav_uint64* totalFrameCount);
+drwav_int32* drwav_open_and_read_pcm_frames_s32(drwav_read_proc onRead, drwav_seek_proc onSeek, void* pUserData, unsigned int* channels, unsigned int* sampleRate, drwav_uint64* totalFrameCount);
+#ifndef DR_WAV_NO_STDIO
+/* Opens and decodes a wav file in a single operation. */
+drwav_int16* drwav_open_file_and_read_pcm_frames_s16(const char* filename, unsigned int* channels, unsigned int* sampleRate, drwav_uint64* totalFrameCount);
+float* drwav_open_file_and_read_pcm_frames_f32(const char* filename, unsigned int* channels, unsigned int* sampleRate, drwav_uint64* totalFrameCount);
+drwav_int32* drwav_open_file_and_read_pcm_frames_s32(const char* filename, unsigned int* channels, unsigned int* sampleRate, drwav_uint64* totalFrameCount);
+#endif
+
+/* Opens and decodes a wav file from a block of memory in a single operation. */
+drwav_int16* drwav_open_memory_and_read_pcm_frames_s16(const void* data, size_t dataSize, unsigned int* channels, unsigned int* sampleRate, drwav_uint64* totalFrameCount);
+float* drwav_open_memory_and_read_pcm_frames_f32(const void* data, size_t dataSize, unsigned int* channels, unsigned int* sampleRate, drwav_uint64* totalFrameCount);
+drwav_int32* drwav_open_memory_and_read_pcm_frames_s32(const void* data, size_t dataSize, unsigned int* channels, unsigned int* sampleRate, drwav_uint64* totalFrameCount);
+#endif
+
+/* Frees data that was allocated internally by dr_wav. */
+void drwav_free(void* pDataReturnedByOpenAndRead);
+
+
+/* DEPRECATED APIS */
+drwav_uint64 drwav_read(drwav* pWav, drwav_uint64 samplesToRead, void* pBufferOut);
+drwav_uint64 drwav_read_s16(drwav* pWav, drwav_uint64 samplesToRead, drwav_int16* pBufferOut);
+drwav_uint64 drwav_read_f32(drwav* pWav, drwav_uint64 samplesToRead, float* pBufferOut);
+drwav_uint64 drwav_read_s32(drwav* pWav, drwav_uint64 samplesToRead, drwav_int32* pBufferOut);
+drwav_bool32 drwav_seek_to_sample(drwav* pWav, drwav_uint64 sample);
+drwav_uint64 drwav_write(drwav* pWav, drwav_uint64 samplesToWrite, const void* pData);
+#ifndef DR_WAV_NO_CONVERSION_API
drwav_int16* drwav_open_and_read_s16(drwav_read_proc onRead, drwav_seek_proc onSeek, void* pUserData, unsigned int* channels, unsigned int* sampleRate, drwav_uint64* totalSampleCount);
float* drwav_open_and_read_f32(drwav_read_proc onRead, drwav_seek_proc onSeek, void* pUserData, unsigned int* channels, unsigned int* sampleRate, drwav_uint64* totalSampleCount);
drwav_int32* drwav_open_and_read_s32(drwav_read_proc onRead, drwav_seek_proc onSeek, void* pUserData, unsigned int* channels, unsigned int* sampleRate, drwav_uint64* totalSampleCount);
#ifndef DR_WAV_NO_STDIO
-// Opens and decodes a wav file in a single operation.
-drwav_int16* drwav_open_and_read_file_s16(const char* filename, unsigned int* channels, unsigned int* sampleRate, drwav_uint64* totalSampleCount);
-float* drwav_open_and_read_file_f32(const char* filename, unsigned int* channels, unsigned int* sampleRate, drwav_uint64* totalSampleCount);
-drwav_int32* drwav_open_and_read_file_s32(const char* filename, unsigned int* channels, unsigned int* sampleRate, drwav_uint64* totalSampleCount);
+drwav_int16* drwav_open_memory_and_read_s16(const void* data, size_t dataSize, unsigned int* channels, unsigned int* sampleRate, drwav_uint64* totalSampleCount);
+float* drwav_open_file_and_read_f32(const char* filename, unsigned int* channels, unsigned int* sampleRate, drwav_uint64* totalSampleCount);
+drwav_int32* drwav_open_file_and_read_s32(const char* filename, unsigned int* channels, unsigned int* sampleRate, drwav_uint64* totalSampleCount);
#endif
-
-// Opens and decodes a wav file from a block of memory in a single operation.
-drwav_int16* drwav_open_and_read_memory_s16(const void* data, size_t dataSize, unsigned int* channels, unsigned int* sampleRate, drwav_uint64* totalSampleCount);
-float* drwav_open_and_read_memory_f32(const void* data, size_t dataSize, unsigned int* channels, unsigned int* sampleRate, drwav_uint64* totalSampleCount);
-drwav_int32* drwav_open_and_read_memory_s32(const void* data, size_t dataSize, unsigned int* channels, unsigned int* sampleRate, drwav_uint64* totalSampleCount);
+drwav_int16* drwav_open_memory_and_read_s16(const void* data, size_t dataSize, unsigned int* channels, unsigned int* sampleRate, drwav_uint64* totalSampleCount);
+float* drwav_open_memory_and_read_f32(const void* data, size_t dataSize, unsigned int* channels, unsigned int* sampleRate, drwav_uint64* totalSampleCount);
+drwav_int32* drwav_open_memory_and_read_s32(const void* data, size_t dataSize, unsigned int* channels, unsigned int* sampleRate, drwav_uint64* totalSampleCount);
#endif
-// Frees data that was allocated internally by dr_wav.
-void drwav_free(void* pDataReturnedByOpenAndRead);
#ifdef __cplusplus
}
#endif
-#endif // dr_wav_h
+#endif /* dr_wav_h */
+
+/************************************************************************************************************************************************************
+ ************************************************************************************************************************************************************
-/////////////////////////////////////////////////////
-//
-// IMPLEMENTATION
-//
-/////////////////////////////////////////////////////
+ IMPLEMENTATION
+ ************************************************************************************************************************************************************
+ ************************************************************************************************************************************************************/
#ifdef DR_WAV_IMPLEMENTATION
#include <stdlib.h>
-#include <string.h> // For memcpy(), memset()
-#include <limits.h> // For INT_MAX
+#include <string.h> /* For memcpy(), memset() */
+#include <limits.h> /* For INT_MAX */
#ifndef DR_WAV_NO_STDIO
#include <stdio.h>
#endif
-// Standard library stuff.
+/* Standard library stuff. */
#ifndef DRWAV_ASSERT
#include <assert.h>
#define DRWAV_ASSERT(expression) assert(expression)
@@ -727,34 +934,43 @@ void drwav_free(void* pDataReturnedByOpenAndRead);
#define drwav_copy_memory DRWAV_COPY_MEMORY
#define drwav_zero_memory DRWAV_ZERO_MEMORY
+typedef drwav_int32 drwav_result;
+#define DRWAV_SUCCESS 0
+#define DRWAV_ERROR -1
+#define DRWAV_INVALID_ARGS -2
+#define DRWAV_INVALID_OPERATION -3
+#define DRWAV_INVALID_FILE -100
+#define DRWAV_EOF -101
-#define DRWAV_MAX_SIMD_VECTOR_SIZE 64 // 64 for AVX-512 in the future.
+#define DRWAV_MAX_SIMD_VECTOR_SIZE 64 /* 64 for AVX-512 in the future. */
#ifdef _MSC_VER
#define DRWAV_INLINE __forceinline
#else
#ifdef __GNUC__
-#define DRWAV_INLINE inline __attribute__((always_inline))
+#define DRWAV_INLINE __inline__ __attribute__((always_inline))
#else
-#define DRWAV_INLINE inline
+#define DRWAV_INLINE
#endif
#endif
-// I couldn't figure out where SIZE_MAX was defined for VC6. If anybody knows, let me know.
-#if defined(_MSC_VER) && _MSC_VER <= 1200
- #if defined(_WIN64)
- #define SIZE_MAX ((drwav_uint64)0xFFFFFFFFFFFFFFFF)
+#if defined(SIZE_MAX)
+ #define DRWAV_SIZE_MAX SIZE_MAX
+#else
+ #if defined(_WIN64) || defined(_LP64) || defined(__LP64__)
+ #define DRWAV_SIZE_MAX ((drwav_uint64)0xFFFFFFFFFFFFFFFF)
#else
- #define SIZE_MAX 0xFFFFFFFF
+ #define DRWAV_SIZE_MAX 0xFFFFFFFF
#endif
#endif
-static const drwav_uint8 drwavGUID_W64_RIFF[16] = {0x72,0x69,0x66,0x66, 0x2E,0x91, 0xCF,0x11, 0xA5,0xD6, 0x28,0xDB,0x04,0xC1,0x00,0x00}; // 66666972-912E-11CF-A5D6-28DB04C10000
-static const drwav_uint8 drwavGUID_W64_WAVE[16] = {0x77,0x61,0x76,0x65, 0xF3,0xAC, 0xD3,0x11, 0x8C,0xD1, 0x00,0xC0,0x4F,0x8E,0xDB,0x8A}; // 65766177-ACF3-11D3-8CD1-00C04F8EDB8A
-static const drwav_uint8 drwavGUID_W64_JUNK[16] = {0x6A,0x75,0x6E,0x6B, 0xF3,0xAC, 0xD3,0x11, 0x8C,0xD1, 0x00,0xC0,0x4F,0x8E,0xDB,0x8A}; // 6B6E756A-ACF3-11D3-8CD1-00C04F8EDB8A
-static const drwav_uint8 drwavGUID_W64_FMT [16] = {0x66,0x6D,0x74,0x20, 0xF3,0xAC, 0xD3,0x11, 0x8C,0xD1, 0x00,0xC0,0x4F,0x8E,0xDB,0x8A}; // 20746D66-ACF3-11D3-8CD1-00C04F8EDB8A
-static const drwav_uint8 drwavGUID_W64_FACT[16] = {0x66,0x61,0x63,0x74, 0xF3,0xAC, 0xD3,0x11, 0x8C,0xD1, 0x00,0xC0,0x4F,0x8E,0xDB,0x8A}; // 74636166-ACF3-11D3-8CD1-00C04F8EDB8A
-static const drwav_uint8 drwavGUID_W64_DATA[16] = {0x64,0x61,0x74,0x61, 0xF3,0xAC, 0xD3,0x11, 0x8C,0xD1, 0x00,0xC0,0x4F,0x8E,0xDB,0x8A}; // 61746164-ACF3-11D3-8CD1-00C04F8EDB8A
+static const drwav_uint8 drwavGUID_W64_RIFF[16] = {0x72,0x69,0x66,0x66, 0x2E,0x91, 0xCF,0x11, 0xA5,0xD6, 0x28,0xDB,0x04,0xC1,0x00,0x00}; /* 66666972-912E-11CF-A5D6-28DB04C10000 */
+static const drwav_uint8 drwavGUID_W64_WAVE[16] = {0x77,0x61,0x76,0x65, 0xF3,0xAC, 0xD3,0x11, 0x8C,0xD1, 0x00,0xC0,0x4F,0x8E,0xDB,0x8A}; /* 65766177-ACF3-11D3-8CD1-00C04F8EDB8A */
+static const drwav_uint8 drwavGUID_W64_JUNK[16] = {0x6A,0x75,0x6E,0x6B, 0xF3,0xAC, 0xD3,0x11, 0x8C,0xD1, 0x00,0xC0,0x4F,0x8E,0xDB,0x8A}; /* 6B6E756A-ACF3-11D3-8CD1-00C04F8EDB8A */
+static const drwav_uint8 drwavGUID_W64_FMT [16] = {0x66,0x6D,0x74,0x20, 0xF3,0xAC, 0xD3,0x11, 0x8C,0xD1, 0x00,0xC0,0x4F,0x8E,0xDB,0x8A}; /* 20746D66-ACF3-11D3-8CD1-00C04F8EDB8A */
+static const drwav_uint8 drwavGUID_W64_FACT[16] = {0x66,0x61,0x63,0x74, 0xF3,0xAC, 0xD3,0x11, 0x8C,0xD1, 0x00,0xC0,0x4F,0x8E,0xDB,0x8A}; /* 74636166-ACF3-11D3-8CD1-00C04F8EDB8A */
+static const drwav_uint8 drwavGUID_W64_DATA[16] = {0x64,0x61,0x74,0x61, 0xF3,0xAC, 0xD3,0x11, 0x8C,0xD1, 0x00,0xC0,0x4F,0x8E,0xDB,0x8A}; /* 61746164-ACF3-11D3-8CD1-00C04F8EDB8A */
+static const drwav_uint8 drwavGUID_W64_SMPL[16] = {0x73,0x6D,0x70,0x6C, 0xF3,0xAC, 0xD3,0x11, 0x8C,0xD1, 0x00,0xC0,0x4F,0x8E,0xDB,0x8A}; /* 6C706D73-ACF3-11D3-8CD1-00C04F8EDB8A */
static DRWAV_INLINE drwav_bool32 drwav__guid_equal(const drwav_uint8 a[16], const drwav_uint8 b[16])
{
@@ -787,11 +1003,7 @@ static DRWAV_INLINE int drwav__is_little_endian()
static DRWAV_INLINE unsigned short drwav__bytes_to_u16(const unsigned char* data)
{
- if (drwav__is_little_endian()) {
- return (data[0] << 0) | (data[1] << 8);
- } else {
- return (data[1] << 0) | (data[0] << 8);
- }
+ return (data[0] << 0) | (data[1] << 8);
}
static DRWAV_INLINE short drwav__bytes_to_s16(const unsigned char* data)
@@ -801,29 +1013,20 @@ static DRWAV_INLINE short drwav__bytes_to_s16(const unsigned char* data)
static DRWAV_INLINE unsigned int drwav__bytes_to_u32(const unsigned char* data)
{
- if (drwav__is_little_endian()) {
- return (data[0] << 0) | (data[1] << 8) | (data[2] << 16) | (data[3] << 24);
- } else {
- return (data[3] << 0) | (data[2] << 8) | (data[1] << 16) | (data[0] << 24);
- }
+ return (data[0] << 0) | (data[1] << 8) | (data[2] << 16) | (data[3] << 24);
}
static DRWAV_INLINE drwav_uint64 drwav__bytes_to_u64(const unsigned char* data)
{
- if (drwav__is_little_endian()) {
- return
- ((drwav_uint64)data[0] << 0) | ((drwav_uint64)data[1] << 8) | ((drwav_uint64)data[2] << 16) | ((drwav_uint64)data[3] << 24) |
- ((drwav_uint64)data[4] << 32) | ((drwav_uint64)data[5] << 40) | ((drwav_uint64)data[6] << 48) | ((drwav_uint64)data[7] << 56);
- } else {
- return
- ((drwav_uint64)data[7] << 0) | ((drwav_uint64)data[6] << 8) | ((drwav_uint64)data[5] << 16) | ((drwav_uint64)data[4] << 24) |
- ((drwav_uint64)data[3] << 32) | ((drwav_uint64)data[2] << 40) | ((drwav_uint64)data[1] << 48) | ((drwav_uint64)data[0] << 56);
- }
+ return
+ ((drwav_uint64)data[0] << 0) | ((drwav_uint64)data[1] << 8) | ((drwav_uint64)data[2] << 16) | ((drwav_uint64)data[3] << 24) |
+ ((drwav_uint64)data[4] << 32) | ((drwav_uint64)data[5] << 40) | ((drwav_uint64)data[6] << 48) | ((drwav_uint64)data[7] << 56);
}
static DRWAV_INLINE void drwav__bytes_to_guid(const unsigned char* data, drwav_uint8* guid)
{
- for (int i = 0; i < 16; ++i) {
+ int i;
+ for (i = 0; i < 16; ++i) {
guid[i] = data[i];
}
}
@@ -836,60 +1039,44 @@ static DRWAV_INLINE drwav_bool32 drwav__is_compressed_format_tag(drwav_uint16 fo
formatTag == DR_WAVE_FORMAT_DVI_ADPCM;
}
-
drwav_uint64 drwav_read_s16__msadpcm(drwav* pWav, drwav_uint64 samplesToRead, drwav_int16* pBufferOut);
drwav_uint64 drwav_read_s16__ima(drwav* pWav, drwav_uint64 samplesToRead, drwav_int16* pBufferOut);
drwav_bool32 drwav_init_write__internal(drwav* pWav, const drwav_data_format* pFormat, drwav_uint64 totalSampleCount, drwav_bool32 isSequential, drwav_write_proc onWrite, drwav_seek_proc onSeek, void* pUserData);
drwav* drwav_open_write__internal(const drwav_data_format* pFormat, drwav_uint64 totalSampleCount, drwav_bool32 isSequential, drwav_write_proc onWrite, drwav_seek_proc onSeek, void* pUserData);
-typedef struct
-{
- union
- {
- drwav_uint8 fourcc[4];
- drwav_uint8 guid[16];
- } id;
-
- // The size in bytes of the chunk.
- drwav_uint64 sizeInBytes;
-
- // RIFF = 2 byte alignment.
- // W64 = 8 byte alignment.
- unsigned int paddingSize;
-
-} drwav__chunk_header;
-
-static drwav_bool32 drwav__read_chunk_header(drwav_read_proc onRead, void* pUserData, drwav_container container, drwav_uint64* pRunningBytesReadOut, drwav__chunk_header* pHeaderOut)
+static drwav_result drwav__read_chunk_header(drwav_read_proc onRead, void* pUserData, drwav_container container, drwav_uint64* pRunningBytesReadOut, drwav_chunk_header* pHeaderOut)
{
if (container == drwav_container_riff) {
+ unsigned char sizeInBytes[4];
+
if (onRead(pUserData, pHeaderOut->id.fourcc, 4) != 4) {
- return DRWAV_FALSE;
+ return DRWAV_EOF;
}
- unsigned char sizeInBytes[4];
if (onRead(pUserData, sizeInBytes, 4) != 4) {
- return DRWAV_FALSE;
+ return DRWAV_INVALID_FILE;
}
pHeaderOut->sizeInBytes = drwav__bytes_to_u32(sizeInBytes);
pHeaderOut->paddingSize = (unsigned int)(pHeaderOut->sizeInBytes % 2);
*pRunningBytesReadOut += 8;
} else {
+ unsigned char sizeInBytes[8];
+
if (onRead(pUserData, pHeaderOut->id.guid, 16) != 16) {
- return DRWAV_FALSE;
+ return DRWAV_EOF;
}
- unsigned char sizeInBytes[8];
if (onRead(pUserData, sizeInBytes, 8) != 8) {
- return DRWAV_FALSE;
+ return DRWAV_INVALID_FILE;
}
- pHeaderOut->sizeInBytes = drwav__bytes_to_u64(sizeInBytes) - 24; // <-- Subtract 24 because w64 includes the size of the header.
+ pHeaderOut->sizeInBytes = drwav__bytes_to_u64(sizeInBytes) - 24; /* <-- Subtract 24 because w64 includes the size of the header. */
pHeaderOut->paddingSize = (unsigned int)(pHeaderOut->sizeInBytes % 8);
*pRunningBytesReadOut += 24;
}
- return DRWAV_TRUE;
+ return DRWAV_SUCCESS;
}
static drwav_bool32 drwav__seek_forward(drwav_seek_proc onSeek, drwav_uint64 offset, void* pUserData)
@@ -912,27 +1099,59 @@ static drwav_bool32 drwav__seek_forward(drwav_seek_proc onSeek, drwav_uint64 off
return DRWAV_TRUE;
}
+static drwav_bool32 drwav__seek_from_start(drwav_seek_proc onSeek, drwav_uint64 offset, void* pUserData)
+{
+ if (offset <= 0x7FFFFFFF) {
+ return onSeek(pUserData, (int)offset, drwav_seek_origin_start);
+ }
+
+ /* Larger than 32-bit seek. */
+ if (!onSeek(pUserData, 0x7FFFFFFF, drwav_seek_origin_start)) {
+ return DRWAV_FALSE;
+ }
+ offset -= 0x7FFFFFFF;
+
+ for (;;) {
+ if (offset <= 0x7FFFFFFF) {
+ return onSeek(pUserData, (int)offset, drwav_seek_origin_current);
+ }
+
+ if (!onSeek(pUserData, 0x7FFFFFFF, drwav_seek_origin_current)) {
+ return DRWAV_FALSE;
+ }
+ offset -= 0x7FFFFFFF;
+ }
+
+ /* Should never get here. */
+ /*return DRWAV_TRUE; */
+}
+
static drwav_bool32 drwav__read_fmt(drwav_read_proc onRead, drwav_seek_proc onSeek, void* pUserData, drwav_container container, drwav_uint64* pRunningBytesReadOut, drwav_fmt* fmtOut)
{
- drwav__chunk_header header;
- if (!drwav__read_chunk_header(onRead, pUserData, container, pRunningBytesReadOut, &header)) {
+ drwav_chunk_header header;
+ unsigned char fmt[16];
+
+ if (drwav__read_chunk_header(onRead, pUserData, container, pRunningBytesReadOut, &header) != DRWAV_SUCCESS) {
return DRWAV_FALSE;
}
- // Skip non-fmt chunks.
- if ((container == drwav_container_riff && !drwav__fourcc_equal(header.id.fourcc, "fmt ")) || (container == drwav_container_w64 && !drwav__guid_equal(header.id.guid, drwavGUID_W64_FMT))) {
+ /* Skip non-fmt chunks. */
+ while ((container == drwav_container_riff && !drwav__fourcc_equal(header.id.fourcc, "fmt ")) || (container == drwav_container_w64 && !drwav__guid_equal(header.id.guid, drwavGUID_W64_FMT))) {
if (!drwav__seek_forward(onSeek, header.sizeInBytes + header.paddingSize, pUserData)) {
return DRWAV_FALSE;
}
*pRunningBytesReadOut += header.sizeInBytes + header.paddingSize;
- return drwav__read_fmt(onRead, onSeek, pUserData, container, pRunningBytesReadOut, fmtOut);
+ /* Try the next header. */
+ if (drwav__read_chunk_header(onRead, pUserData, container, pRunningBytesReadOut, &header) != DRWAV_SUCCESS) {
+ return DRWAV_FALSE;
+ }
}
- // Validation.
+ /* Validation. */
if (container == drwav_container_riff) {
if (!drwav__fourcc_equal(header.id.fourcc, "fmt ")) {
return DRWAV_FALSE;
@@ -944,7 +1163,6 @@ static drwav_bool32 drwav__read_fmt(drwav_read_proc onRead, drwav_seek_proc onSe
}
- unsigned char fmt[16];
if (onRead(pUserData, fmt, sizeof(fmt)) != sizeof(fmt)) {
return DRWAV_FALSE;
}
@@ -964,16 +1182,18 @@ static drwav_bool32 drwav__read_fmt(drwav_read_proc onRead, drwav_seek_proc onSe
if (header.sizeInBytes > 16) {
unsigned char fmt_cbSize[2];
+ int bytesReadSoFar = 0;
+
if (onRead(pUserData, fmt_cbSize, sizeof(fmt_cbSize)) != sizeof(fmt_cbSize)) {
- return DRWAV_FALSE; // Expecting more data.
+ return DRWAV_FALSE; /* Expecting more data. */
}
*pRunningBytesReadOut += sizeof(fmt_cbSize);
- int bytesReadSoFar = 18;
+ bytesReadSoFar = 18;
fmtOut->extendedSize = drwav__bytes_to_u16(fmt_cbSize);
if (fmtOut->extendedSize > 0) {
- // Simple validation.
+ /* Simple validation. */
if (fmtOut->formatTag == DR_WAVE_FORMAT_EXTENSIBLE) {
if (fmtOut->extendedSize != 22) {
return DRWAV_FALSE;
@@ -983,7 +1203,7 @@ static drwav_bool32 drwav__read_fmt(drwav_read_proc onRead, drwav_seek_proc onSe
if (fmtOut->formatTag == DR_WAVE_FORMAT_EXTENSIBLE) {
unsigned char fmtext[22];
if (onRead(pUserData, fmtext, fmtOut->extendedSize) != fmtOut->extendedSize) {
- return DRWAV_FALSE; // Expecting more data.
+ return DRWAV_FALSE; /* Expecting more data. */
}
fmtOut->validBitsPerSample = drwav__bytes_to_u16(fmtext + 0);
@@ -999,7 +1219,7 @@ static drwav_bool32 drwav__read_fmt(drwav_read_proc onRead, drwav_seek_proc onSe
bytesReadSoFar += fmtOut->extendedSize;
}
- // Seek past any leftover bytes. For w64 the leftover will be defined based on the chunk size.
+ /* Seek past any leftover bytes. For w64 the leftover will be defined based on the chunk size. */
if (!onSeek(pUserData, (int)(header.sizeInBytes - bytesReadSoFar), drwav_seek_origin_current)) {
return DRWAV_FALSE;
}
@@ -1052,12 +1272,17 @@ static drwav_bool32 drwav__on_seek_stdio(void* pUserData, int offset, drwav_seek
drwav_bool32 drwav_init_file(drwav* pWav, const char* filename)
{
+ return drwav_init_file_ex(pWav, filename, NULL, NULL, 0);
+}
+
+drwav_bool32 drwav_init_file_ex(drwav* pWav, const char* filename, drwav_chunk_proc onChunk, void* pChunkUserData, drwav_uint32 flags)
+{
FILE* pFile = drwav_fopen(filename, "rb");
if (pFile == NULL) {
return DRWAV_FALSE;
}
- return drwav_init(pWav, drwav__on_read_stdio, drwav__on_seek_stdio, (void*)pFile);
+ return drwav_init_ex(pWav, drwav__on_read_stdio, drwav__on_seek_stdio, onChunk, (void*)pFile, pChunkUserData, flags);
}
@@ -1083,12 +1308,20 @@ drwav_bool32 drwav_init_file_write_sequential(drwav* pWav, const char* filename,
drwav* drwav_open_file(const char* filename)
{
- FILE* pFile = drwav_fopen(filename, "rb");
+ return drwav_open_file_ex(filename, NULL, NULL, 0);
+}
+
+drwav* drwav_open_file_ex(const char* filename, drwav_chunk_proc onChunk, void* pChunkUserData, drwav_uint32 flags)
+{
+ FILE* pFile;
+ drwav* pWav;
+
+ pFile = drwav_fopen(filename, "rb");
if (pFile == NULL) {
return DRWAV_FALSE;
}
- drwav* pWav = drwav_open(drwav__on_read_stdio, drwav__on_seek_stdio, (void*)pFile);
+ pWav = drwav_open_ex(drwav__on_read_stdio, drwav__on_seek_stdio, onChunk, (void*)pFile, pChunkUserData, flags);
if (pWav == NULL) {
fclose(pFile);
return NULL;
@@ -1100,12 +1333,15 @@ drwav* drwav_open_file(const char* filename)
drwav* drwav_open_file_write__internal(const char* filename, const drwav_data_format* pFormat, drwav_uint64 totalSampleCount, drwav_bool32 isSequential)
{
- FILE* pFile = drwav_fopen(filename, "wb");
+ FILE* pFile;
+ drwav* pWav;
+
+ pFile = drwav_fopen(filename, "wb");
if (pFile == NULL) {
return DRWAV_FALSE;
}
- drwav* pWav = drwav_open_write__internal(pFormat, totalSampleCount, isSequential, drwav__on_write_stdio, drwav__on_seek_stdio, (void*)pFile);
+ pWav = drwav_open_write__internal(pFormat, totalSampleCount, isSequential, drwav__on_write_stdio, drwav__on_seek_stdio, (void*)pFile);
if (pWav == NULL) {
fclose(pFile);
return NULL;
@@ -1123,16 +1359,18 @@ drwav* drwav_open_file_write_sequential(const char* filename, const drwav_data_f
{
return drwav_open_file_write__internal(filename, pFormat, totalSampleCount, DRWAV_TRUE);
}
-#endif //DR_WAV_NO_STDIO
+#endif /* DR_WAV_NO_STDIO */
static size_t drwav__on_read_memory(void* pUserData, void* pBufferOut, size_t bytesToRead)
{
drwav__memory_stream* memory = (drwav__memory_stream*)pUserData;
+ size_t bytesRemaining;
+
drwav_assert(memory != NULL);
drwav_assert(memory->dataSize >= memory->currentReadPos);
- size_t bytesRemaining = memory->dataSize - memory->currentReadPos;
+ bytesRemaining = memory->dataSize - memory->currentReadPos;
if (bytesToRead > bytesRemaining) {
bytesToRead = bytesRemaining;
}
@@ -1153,21 +1391,21 @@ static drwav_bool32 drwav__on_seek_memory(void* pUserData, int offset, drwav_see
if (origin == drwav_seek_origin_current) {
if (offset > 0) {
if (memory->currentReadPos + offset > memory->dataSize) {
- offset = (int)(memory->dataSize - memory->currentReadPos); // Trying to seek too far forward.
+ return DRWAV_FALSE; /* Trying to seek too far forward. */
}
} else {
if (memory->currentReadPos < (size_t)-offset) {
- offset = -(int)memory->currentReadPos; // Trying to seek too far backwards.
+ return DRWAV_FALSE; /* Trying to seek too far backwards. */
}
}
- // This will never underflow thanks to the clamps above.
+ /* This will never underflow thanks to the clamps above. */
memory->currentReadPos += offset;
} else {
if ((drwav_uint32)offset <= memory->dataSize) {
memory->currentReadPos = offset;
} else {
- memory->currentReadPos = memory->dataSize; // Trying to seek too far forward.
+ return DRWAV_FALSE; /* Trying to seek too far forward. */
}
}
@@ -1177,20 +1415,23 @@ static drwav_bool32 drwav__on_seek_memory(void* pUserData, int offset, drwav_see
static size_t drwav__on_write_memory(void* pUserData, const void* pDataIn, size_t bytesToWrite)
{
drwav__memory_stream_write* memory = (drwav__memory_stream_write*)pUserData;
+ size_t bytesRemaining;
+
drwav_assert(memory != NULL);
drwav_assert(memory->dataCapacity >= memory->currentWritePos);
- size_t bytesRemaining = memory->dataCapacity - memory->currentWritePos;
+ bytesRemaining = memory->dataCapacity - memory->currentWritePos;
if (bytesRemaining < bytesToWrite) {
- // Need to reallocate.
+ /* Need to reallocate. */
+ void* pNewData;
size_t newDataCapacity = (memory->dataCapacity == 0) ? 256 : memory->dataCapacity * 2;
- // If doubling wasn't enough, just make it the minimum required size to write the data.
+ /* If doubling wasn't enough, just make it the minimum required size to write the data. */
if ((newDataCapacity - memory->currentWritePos) < bytesToWrite) {
newDataCapacity = memory->currentWritePos + bytesToWrite;
}
- void* pNewData = DRWAV_REALLOC(*memory->ppData, newDataCapacity);
+ pNewData = DRWAV_REALLOC(*memory->ppData, newDataCapacity);
if (pNewData == NULL) {
return 0;
}
@@ -1199,8 +1440,7 @@ static size_t drwav__on_write_memory(void* pUserData, const void* pDataIn, size_
memory->dataCapacity = newDataCapacity;
}
- drwav_uint8* pDataOut = (drwav_uint8*)(*memory->ppData);
- DRWAV_COPY_MEMORY(pDataOut + memory->currentWritePos, pDataIn, bytesToWrite);
+ DRWAV_COPY_MEMORY(((drwav_uint8*)(*memory->ppData)) + memory->currentWritePos, pDataIn, bytesToWrite);
memory->currentWritePos += bytesToWrite;
if (memory->dataSize < memory->currentWritePos) {
@@ -1220,21 +1460,21 @@ static drwav_bool32 drwav__on_seek_memory_write(void* pUserData, int offset, drw
if (origin == drwav_seek_origin_current) {
if (offset > 0) {
if (memory->currentWritePos + offset > memory->dataSize) {
- offset = (int)(memory->dataSize - memory->currentWritePos); // Trying to seek too far forward.
+ offset = (int)(memory->dataSize - memory->currentWritePos); /* Trying to seek too far forward. */
}
} else {
if (memory->currentWritePos < (size_t)-offset) {
- offset = -(int)memory->currentWritePos; // Trying to seek too far backwards.
+ offset = -(int)memory->currentWritePos; /* Trying to seek too far backwards. */
}
}
- // This will never underflow thanks to the clamps above.
+ /* This will never underflow thanks to the clamps above. */
memory->currentWritePos += offset;
} else {
if ((drwav_uint32)offset <= memory->dataSize) {
memory->currentWritePos = offset;
} else {
- memory->currentWritePos = memory->dataSize; // Trying to seek too far forward.
+ memory->currentWritePos = memory->dataSize; /* Trying to seek too far forward. */
}
}
@@ -1243,17 +1483,23 @@ static drwav_bool32 drwav__on_seek_memory_write(void* pUserData, int offset, drw
drwav_bool32 drwav_init_memory(drwav* pWav, const void* data, size_t dataSize)
{
+ return drwav_init_memory_ex(pWav, data, dataSize, NULL, NULL, 0);
+}
+
+drwav_bool32 drwav_init_memory_ex(drwav* pWav, const void* data, size_t dataSize, drwav_chunk_proc onChunk, void* pChunkUserData, drwav_uint32 flags)
+{
+ drwav__memory_stream memoryStream;
+
if (data == NULL || dataSize == 0) {
return DRWAV_FALSE;
}
- drwav__memory_stream memoryStream;
drwav_zero_memory(&memoryStream, sizeof(memoryStream));
memoryStream.data = (const unsigned char*)data;
memoryStream.dataSize = dataSize;
memoryStream.currentReadPos = 0;
- if (!drwav_init(pWav, drwav__on_read_memory, drwav__on_seek_memory, (void*)&memoryStream)) {
+ if (!drwav_init_ex(pWav, drwav__on_read_memory, drwav__on_seek_memory, onChunk, (void*)&memoryStream, pChunkUserData, flags)) {
return DRWAV_FALSE;
}
@@ -1265,14 +1511,15 @@ drwav_bool32 drwav_init_memory(drwav* pWav, const void* data, size_t dataSize)
drwav_bool32 drwav_init_memory_write__internal(drwav* pWav, void** ppData, size_t* pDataSize, const drwav_data_format* pFormat, drwav_uint64 totalSampleCount, drwav_bool32 isSequential)
{
+ drwav__memory_stream_write memoryStreamWrite;
+
if (ppData == NULL) {
return DRWAV_FALSE;
}
- *ppData = NULL; // Important because we're using realloc()!
+ *ppData = NULL; /* Important because we're using realloc()! */
*pDataSize = 0;
- drwav__memory_stream_write memoryStreamWrite;
drwav_zero_memory(&memoryStreamWrite, sizeof(memoryStreamWrite));
memoryStreamWrite.ppData = ppData;
memoryStreamWrite.pDataSize = pDataSize;
@@ -1302,17 +1549,24 @@ drwav_bool32 drwav_init_memory_write_sequential(drwav* pWav, void** ppData, size
drwav* drwav_open_memory(const void* data, size_t dataSize)
{
+ return drwav_open_memory_ex(data, dataSize, NULL, NULL, 0);
+}
+
+drwav* drwav_open_memory_ex(const void* data, size_t dataSize, drwav_chunk_proc onChunk, void* pChunkUserData, drwav_uint32 flags)
+{
+ drwav__memory_stream memoryStream;
+ drwav* pWav;
+
if (data == NULL || dataSize == 0) {
return NULL;
}
- drwav__memory_stream memoryStream;
drwav_zero_memory(&memoryStream, sizeof(memoryStream));
memoryStream.data = (const unsigned char*)data;
memoryStream.dataSize = dataSize;
memoryStream.currentReadPos = 0;
- drwav* pWav = drwav_open(drwav__on_read_memory, drwav__on_seek_memory, (void*)&memoryStream);
+ pWav = drwav_open_ex(drwav__on_read_memory, drwav__on_seek_memory, onChunk, (void*)&memoryStream, pChunkUserData, flags);
if (pWav == NULL) {
return NULL;
}
@@ -1325,14 +1579,16 @@ drwav* drwav_open_memory(const void* data, size_t dataSize)
drwav* drwav_open_memory_write__internal(void** ppData, size_t* pDataSize, const drwav_data_format* pFormat, drwav_uint64 totalSampleCount, drwav_bool32 isSequential)
{
+ drwav__memory_stream_write memoryStreamWrite;
+ drwav* pWav;
+
if (ppData == NULL) {
return NULL;
}
- *ppData = NULL; // Important because we're using realloc()!
+ *ppData = NULL; /* Important because we're using realloc()! */
*pDataSize = 0;
- drwav__memory_stream_write memoryStreamWrite;
drwav_zero_memory(&memoryStreamWrite, sizeof(memoryStreamWrite));
memoryStreamWrite.ppData = ppData;
memoryStreamWrite.pDataSize = pDataSize;
@@ -1340,7 +1596,7 @@ drwav* drwav_open_memory_write__internal(void** ppData, size_t* pDataSize, const
memoryStreamWrite.dataCapacity = 0;
memoryStreamWrite.currentWritePos = 0;
- drwav* pWav = drwav_open_write__internal(pFormat, totalSampleCount, isSequential, drwav__on_write_memory, drwav__on_seek_memory_write, (void*)&memoryStreamWrite);
+ pWav = drwav_open_write__internal(pFormat, totalSampleCount, isSequential, drwav__on_write_memory, drwav__on_seek_memory_write, (void*)&memoryStreamWrite);
if (pWav == NULL) {
return NULL;
}
@@ -1361,143 +1617,274 @@ drwav* drwav_open_memory_write_sequential(void** ppData, size_t* pDataSize, cons
}
+size_t drwav__on_read(drwav_read_proc onRead, void* pUserData, void* pBufferOut, size_t bytesToRead, drwav_uint64* pCursor)
+{
+ size_t bytesRead;
+
+ drwav_assert(onRead != NULL);
+ drwav_assert(pCursor != NULL);
+
+ bytesRead = onRead(pUserData, pBufferOut, bytesToRead);
+ *pCursor += bytesRead;
+ return bytesRead;
+}
+
+drwav_bool32 drwav__on_seek(drwav_seek_proc onSeek, void* pUserData, int offset, drwav_seek_origin origin, drwav_uint64* pCursor)
+{
+ drwav_assert(onSeek != NULL);
+ drwav_assert(pCursor != NULL);
+
+ if (!onSeek(pUserData, offset, origin)) {
+ return DRWAV_FALSE;
+ }
+
+ if (origin == drwav_seek_origin_start) {
+ *pCursor = offset;
+ } else {
+ *pCursor += offset;
+ }
+
+ return DRWAV_TRUE;
+}
+
+
+static drwav_uint32 drwav_get_bytes_per_sample(drwav* pWav)
+{
+ /*
+ The number of bytes per sample is based on the bits per sample or the block align. We prioritize floor(bitsPerSample/8), but if
+ this is zero or the bits per sample is not a multiple of 8 we need to fall back to the block align.
+ */
+ drwav_uint32 bytesPerSample = pWav->bitsPerSample >> 3;
+ if (bytesPerSample == 0 || (pWav->bitsPerSample & 0x7) != 0) {
+ bytesPerSample = pWav->fmt.blockAlign/pWav->fmt.channels;
+ }
+
+ return bytesPerSample;
+}
+
+static drwav_uint32 drwav_get_bytes_per_pcm_frame(drwav* pWav)
+{
+ /*
+ The number of bytes per frame is based on the bits per sample or the block align. We prioritize floor(bitsPerSample*channels/8), but if
+ this is zero or the bits per frame is not a multiple of 8 we need to fall back to the block align.
+ */
+ drwav_uint32 bitsPerFrame = pWav->bitsPerSample * pWav->fmt.channels;
+ drwav_uint32 bytesPerFrame = bitsPerFrame >> 3;
+ if (bytesPerFrame == 0 || (bitsPerFrame & 0x7) != 0) {
+ bytesPerFrame = pWav->fmt.blockAlign;
+ }
+
+ return bytesPerFrame;
+}
+
+
drwav_bool32 drwav_init(drwav* pWav, drwav_read_proc onRead, drwav_seek_proc onSeek, void* pUserData)
{
+ return drwav_init_ex(pWav, onRead, onSeek, NULL, pUserData, NULL, 0);
+}
+
+drwav_bool32 drwav_init_ex(drwav* pWav, drwav_read_proc onRead, drwav_seek_proc onSeek, drwav_chunk_proc onChunk, void* pReadSeekUserData, void* pChunkUserData, drwav_uint32 flags)
+{
+ drwav_uint64 cursor; /* <-- Keeps track of the byte position so we can seek to specific locations. */
+ drwav_bool32 sequential;
+ unsigned char riff[4];
+ drwav_fmt fmt;
+ unsigned short translatedFormatTag;
+ drwav_uint64 sampleCountFromFactChunk;
+ drwav_bool32 foundDataChunk;
+ drwav_uint64 dataChunkSize;
+ drwav_uint64 chunkSize;
+
if (onRead == NULL || onSeek == NULL) {
return DRWAV_FALSE;
}
- drwav_zero_memory(pWav, sizeof(*pWav));
+ cursor = 0;
+ sequential = (flags & DRWAV_SEQUENTIAL) != 0;
+ drwav_zero_memory(pWav, sizeof(*pWav));
+ pWav->onRead = onRead;
+ pWav->onSeek = onSeek;
+ pWav->pUserData = pReadSeekUserData;
- // The first 4 bytes should be the RIFF identifier.
- unsigned char riff[4];
- if (onRead(pUserData, riff, sizeof(riff)) != sizeof(riff)) {
- return DRWAV_FALSE; // Failed to read data.
+ /* The first 4 bytes should be the RIFF identifier. */
+ if (drwav__on_read(onRead, pReadSeekUserData, riff, sizeof(riff), &cursor) != sizeof(riff)) {
+ return DRWAV_FALSE;
}
- // The first 4 bytes can be used to identify the container. For RIFF files it will start with "RIFF" and for
- // w64 it will start with "riff".
+ /*
+ The first 4 bytes can be used to identify the container. For RIFF files it will start with "RIFF" and for
+ w64 it will start with "riff".
+ */
if (drwav__fourcc_equal(riff, "RIFF")) {
pWav->container = drwav_container_riff;
} else if (drwav__fourcc_equal(riff, "riff")) {
+ int i;
+ drwav_uint8 riff2[12];
+
pWav->container = drwav_container_w64;
- // Check the rest of the GUID for validity.
- drwav_uint8 riff2[12];
- if (onRead(pUserData, riff2, sizeof(riff2)) != sizeof(riff2)) {
+ /* Check the rest of the GUID for validity. */
+ if (drwav__on_read(onRead, pReadSeekUserData, riff2, sizeof(riff2), &cursor) != sizeof(riff2)) {
return DRWAV_FALSE;
}
- for (int i = 0; i < 12; ++i) {
+ for (i = 0; i < 12; ++i) {
if (riff2[i] != drwavGUID_W64_RIFF[i+4]) {
return DRWAV_FALSE;
}
}
} else {
- return DRWAV_FALSE; // Unknown or unsupported container.
+ return DRWAV_FALSE; /* Unknown or unsupported container. */
}
if (pWav->container == drwav_container_riff) {
- // RIFF/WAVE
unsigned char chunkSizeBytes[4];
- if (onRead(pUserData, chunkSizeBytes, sizeof(chunkSizeBytes)) != sizeof(chunkSizeBytes)) {
+ unsigned char wave[4];
+
+ /* RIFF/WAVE */
+ if (drwav__on_read(onRead, pReadSeekUserData, chunkSizeBytes, sizeof(chunkSizeBytes), &cursor) != sizeof(chunkSizeBytes)) {
return DRWAV_FALSE;
}
- unsigned int chunkSize = drwav__bytes_to_u32(chunkSizeBytes);
- if (chunkSize < 36) {
- return DRWAV_FALSE; // Chunk size should always be at least 36 bytes.
+ if (drwav__bytes_to_u32(chunkSizeBytes) < 36) {
+ return DRWAV_FALSE; /* Chunk size should always be at least 36 bytes. */
}
- unsigned char wave[4];
- if (onRead(pUserData, wave, sizeof(wave)) != sizeof(wave)) {
+ if (drwav__on_read(onRead, pReadSeekUserData, wave, sizeof(wave), &cursor) != sizeof(wave)) {
return DRWAV_FALSE;
}
if (!drwav__fourcc_equal(wave, "WAVE")) {
- return DRWAV_FALSE; // Expecting "WAVE".
+ return DRWAV_FALSE; /* Expecting "WAVE". */
}
-
- pWav->dataChunkDataPos = 4 + sizeof(chunkSizeBytes) + sizeof(wave);
} else {
- // W64
- unsigned char chunkSize[8];
- if (onRead(pUserData, chunkSize, sizeof(chunkSize)) != sizeof(chunkSize)) {
+ unsigned char chunkSizeBytes[8];
+ drwav_uint8 wave[16];
+
+ /* W64 */
+ if (drwav__on_read(onRead, pReadSeekUserData, chunkSizeBytes, sizeof(chunkSizeBytes), &cursor) != sizeof(chunkSizeBytes)) {
return DRWAV_FALSE;
}
- if (drwav__bytes_to_u64(chunkSize) < 80) {
+ if (drwav__bytes_to_u64(chunkSizeBytes) < 80) {
return DRWAV_FALSE;
}
- drwav_uint8 wave[16];
- if (onRead(pUserData, wave, sizeof(wave)) != sizeof(wave)) {
+ if (drwav__on_read(onRead, pReadSeekUserData, wave, sizeof(wave), &cursor) != sizeof(wave)) {
return DRWAV_FALSE;
}
if (!drwav__guid_equal(wave, drwavGUID_W64_WAVE)) {
return DRWAV_FALSE;
}
-
- pWav->dataChunkDataPos = 16 + sizeof(chunkSize) + sizeof(wave);
}
- // The next bytes should be the "fmt " chunk.
- drwav_fmt fmt;
- if (!drwav__read_fmt(onRead, onSeek, pUserData, pWav->container, &pWav->dataChunkDataPos, &fmt)) {
- return DRWAV_FALSE; // Failed to read the "fmt " chunk.
+ /* The next bytes should be the "fmt " chunk. */
+ if (!drwav__read_fmt(onRead, onSeek, pReadSeekUserData, pWav->container, &cursor, &fmt)) {
+ return DRWAV_FALSE; /* Failed to read the "fmt " chunk. */
}
- // Basic validation.
+ /* Basic validation. */
if (fmt.sampleRate == 0 || fmt.channels == 0 || fmt.bitsPerSample == 0 || fmt.blockAlign == 0) {
- return DRWAV_FALSE; // Invalid channel count. Probably an invalid WAV file.
+ return DRWAV_FALSE; /* Invalid channel count. Probably an invalid WAV file. */
}
- // Translate the internal format.
- unsigned short translatedFormatTag = fmt.formatTag;
+ /* Translate the internal format. */
+ translatedFormatTag = fmt.formatTag;
if (translatedFormatTag == DR_WAVE_FORMAT_EXTENSIBLE) {
translatedFormatTag = drwav__bytes_to_u16(fmt.subFormat + 0);
}
- drwav_uint64 sampleCountFromFactChunk = 0;
- // The next chunk we care about is the "data" chunk. This is not necessarily the next chunk so we'll need to loop.
- drwav_uint64 dataSize;
+ sampleCountFromFactChunk = 0;
+
+ /*
+ We need to enumerate over each chunk for two reasons:
+ 1) The "data" chunk may not be the next one
+ 2) We may want to report each chunk back to the client
+
+ In order to correctly report each chunk back to the client we will need to keep looping until the end of the file.
+ */
+ foundDataChunk = DRWAV_FALSE;
+ dataChunkSize = 0;
+
+ /* The next chunk we care about is the "data" chunk. This is not necessarily the next chunk so we'll need to loop. */
+ chunkSize = 0;
for (;;)
{
- drwav__chunk_header header;
- if (!drwav__read_chunk_header(onRead, pUserData, pWav->container, &pWav->dataChunkDataPos, &header)) {
- return DRWAV_FALSE;
+ drwav_chunk_header header;
+ drwav_result result = drwav__read_chunk_header(onRead, pReadSeekUserData, pWav->container, &cursor, &header);
+ if (result != DRWAV_SUCCESS) {
+ if (!foundDataChunk) {
+ return DRWAV_FALSE;
+ } else {
+ break; /* Probably at the end of the file. Get out of the loop. */
+ }
}
- dataSize = header.sizeInBytes;
+ /* Tell the client about this chunk. */
+ if (!sequential && onChunk != NULL) {
+ drwav_uint64 callbackBytesRead = onChunk(pChunkUserData, onRead, onSeek, pReadSeekUserData, &header);
+
+ /*
+ dr_wav may need to read the contents of the chunk, so we now need to seek back to the position before
+ we called the callback.
+ */
+ if (callbackBytesRead > 0) {
+ if (!drwav__seek_from_start(onSeek, cursor, pReadSeekUserData)) {
+ return DRWAV_FALSE;
+ }
+ }
+ }
+
+
+ if (!foundDataChunk) {
+ pWav->dataChunkDataPos = cursor;
+ }
+
+ chunkSize = header.sizeInBytes;
if (pWav->container == drwav_container_riff) {
if (drwav__fourcc_equal(header.id.fourcc, "data")) {
- break;
+ foundDataChunk = DRWAV_TRUE;
+ dataChunkSize = chunkSize;
}
} else {
if (drwav__guid_equal(header.id.guid, drwavGUID_W64_DATA)) {
- break;
+ foundDataChunk = DRWAV_TRUE;
+ dataChunkSize = chunkSize;
}
}
- // Optional. Get the total sample count from the FACT chunk. This is useful for compressed formats.
+ /*
+ If at this point we have found the data chunk and we're running in sequential mode, we need to break out of this loop. The reason for
+ this is that we would otherwise require a backwards seek which sequential mode forbids.
+ */
+ if (foundDataChunk && sequential) {
+ break;
+ }
+
+ /* Optional. Get the total sample count from the FACT chunk. This is useful for compressed formats. */
if (pWav->container == drwav_container_riff) {
if (drwav__fourcc_equal(header.id.fourcc, "fact")) {
drwav_uint32 sampleCount;
- if (onRead(pUserData, &sampleCount, 4) != 4) {
+ if (drwav__on_read(onRead, pReadSeekUserData, &sampleCount, 4, &cursor) != 4) {
return DRWAV_FALSE;
}
- pWav->dataChunkDataPos += 4;
- dataSize -= 4;
+ chunkSize -= 4;
+
+ if (!foundDataChunk) {
+ pWav->dataChunkDataPos = cursor;
+ }
- // The sample count in the "fact" chunk is either unreliable, or I'm not understanding it properly. For now I am only enabling this
- // for Microsoft ADPCM formats.
+ /*
+ The sample count in the "fact" chunk is either unreliable, or I'm not understanding it properly. For now I am only enabling this
+ for Microsoft ADPCM formats.
+ */
if (pWav->translatedFormatTag == DR_WAVE_FORMAT_ADPCM) {
sampleCountFromFactChunk = sampleCount;
} else {
@@ -1506,62 +1893,120 @@ drwav_bool32 drwav_init(drwav* pWav, drwav_read_proc onRead, drwav_seek_proc onS
}
} else {
if (drwav__guid_equal(header.id.guid, drwavGUID_W64_FACT)) {
- if (onRead(pUserData, &sampleCountFromFactChunk, 8) != 8) {
+ if (drwav__on_read(onRead, pReadSeekUserData, &sampleCountFromFactChunk, 8, &cursor) != 8) {
return DRWAV_FALSE;
}
- pWav->dataChunkDataPos += 8;
- dataSize -= 8;
+ chunkSize -= 8;
+
+ if (!foundDataChunk) {
+ pWav->dataChunkDataPos = cursor;
+ }
}
}
- // If we get here it means we didn't find the "data" chunk. Seek past it.
+ /* "smpl" chunk. */
+ if (pWav->container == drwav_container_riff) {
+ if (drwav__fourcc_equal(header.id.fourcc, "smpl")) {
+ unsigned char smplHeaderData[36]; /* 36 = size of the smpl header section, not including the loop data. */
+ if (chunkSize >= sizeof(smplHeaderData)) {
+ drwav_uint64 bytesJustRead = drwav__on_read(onRead, pReadSeekUserData, smplHeaderData, sizeof(smplHeaderData), &cursor);
+ chunkSize -= bytesJustRead;
+
+ if (bytesJustRead == sizeof(smplHeaderData)) {
+ drwav_uint32 iLoop;
+
+ pWav->smpl.manufacturer = drwav__bytes_to_u32(smplHeaderData+0);
+ pWav->smpl.product = drwav__bytes_to_u32(smplHeaderData+4);
+ pWav->smpl.samplePeriod = drwav__bytes_to_u32(smplHeaderData+8);
+ pWav->smpl.midiUnityNotes = drwav__bytes_to_u32(smplHeaderData+12);
+ pWav->smpl.midiPitchFraction = drwav__bytes_to_u32(smplHeaderData+16);
+ pWav->smpl.smpteFormat = drwav__bytes_to_u32(smplHeaderData+20);
+ pWav->smpl.smpteOffset = drwav__bytes_to_u32(smplHeaderData+24);
+ pWav->smpl.numSampleLoops = drwav__bytes_to_u32(smplHeaderData+28);
+ pWav->smpl.samplerData = drwav__bytes_to_u32(smplHeaderData+32);
+
+ for (iLoop = 0; iLoop < pWav->smpl.numSampleLoops && iLoop < drwav_countof(pWav->smpl.loops); ++iLoop) {
+ unsigned char smplLoopData[24]; /* 24 = size of a loop section in the smpl chunk. */
+ bytesJustRead = drwav__on_read(onRead, pReadSeekUserData, smplLoopData, sizeof(smplLoopData), &cursor);
+ chunkSize -= bytesJustRead;
+
+ if (bytesJustRead == sizeof(smplLoopData)) {
+ pWav->smpl.loops[iLoop].cuePointId = drwav__bytes_to_u32(smplLoopData+0);
+ pWav->smpl.loops[iLoop].type = drwav__bytes_to_u32(smplLoopData+4);
+ pWav->smpl.loops[iLoop].start = drwav__bytes_to_u32(smplLoopData+8);
+ pWav->smpl.loops[iLoop].end = drwav__bytes_to_u32(smplLoopData+12);
+ pWav->smpl.loops[iLoop].fraction = drwav__bytes_to_u32(smplLoopData+16);
+ pWav->smpl.loops[iLoop].playCount = drwav__bytes_to_u32(smplLoopData+20);
+ } else {
+ break; /* Break from the smpl loop for loop. */
+ }
+ }
+ }
+ } else {
+ /* Looks like invalid data. Ignore the chunk. */
+ }
+ }
+ } else {
+ if (drwav__guid_equal(header.id.guid, drwavGUID_W64_SMPL)) {
+ /*
+ This path will be hit when a W64 WAV file contains a smpl chunk. I don't have a sample file to test this path, so a contribution
+ is welcome to add support for this.
+ */
+ }
+ }
+
+ /* Make sure we seek past the padding. */
+ chunkSize += header.paddingSize;
+ if (!drwav__seek_forward(onSeek, chunkSize, pReadSeekUserData)) {
+ break;
+ }
+ cursor += chunkSize;
+
+ if (!foundDataChunk) {
+ pWav->dataChunkDataPos = cursor;
+ }
+ }
+
+ /* If we haven't found a data chunk, return an error. */
+ if (!foundDataChunk) {
+ return DRWAV_FALSE;
+ }
- // Make sure we seek past the padding.
- dataSize += header.paddingSize;
- drwav__seek_forward(onSeek, dataSize, pUserData);
- pWav->dataChunkDataPos += dataSize;
+ /* We may have moved passed the data chunk. If so we need to move back. If running in sequential mode we can assume we are already sitting on the data chunk. */
+ if (!sequential) {
+ if (!drwav__seek_from_start(onSeek, pWav->dataChunkDataPos, pReadSeekUserData)) {
+ return DRWAV_FALSE;
+ }
+ cursor = pWav->dataChunkDataPos;
}
+
- // At this point we should be sitting on the first byte of the raw audio data.
+ /* At this point we should be sitting on the first byte of the raw audio data. */
- pWav->onRead = onRead;
- pWav->onSeek = onSeek;
- pWav->pUserData = pUserData;
pWav->fmt = fmt;
pWav->sampleRate = fmt.sampleRate;
pWav->channels = fmt.channels;
pWav->bitsPerSample = fmt.bitsPerSample;
- pWav->bytesPerSample = fmt.blockAlign / fmt.channels;
- pWav->bytesRemaining = dataSize;
+ pWav->bytesRemaining = dataChunkSize;
pWav->translatedFormatTag = translatedFormatTag;
- pWav->dataChunkDataSize = dataSize;
-
- // The bytes per sample should never be 0 at this point. This would indicate an invalid WAV file.
- if (pWav->bytesPerSample == 0) {
- return DRWAV_FALSE;
- }
+ pWav->dataChunkDataSize = dataChunkSize;
if (sampleCountFromFactChunk != 0) {
- pWav->totalSampleCount = sampleCountFromFactChunk * fmt.channels;
+ pWav->totalPCMFrameCount = sampleCountFromFactChunk;
} else {
- pWav->totalSampleCount = dataSize / pWav->bytesPerSample;
+ pWav->totalPCMFrameCount = dataChunkSize / drwav_get_bytes_per_pcm_frame(pWav);
if (pWav->translatedFormatTag == DR_WAVE_FORMAT_ADPCM) {
- drwav_uint64 blockCount = dataSize / fmt.blockAlign;
- pWav->totalSampleCount = (blockCount * (fmt.blockAlign - (6*pWav->channels))) * 2; // x2 because two samples per byte.
+ drwav_uint64 blockCount = dataChunkSize / fmt.blockAlign;
+ pWav->totalPCMFrameCount = (((blockCount * (fmt.blockAlign - (6*pWav->channels))) * 2)) / fmt.channels; /* x2 because two samples per byte. */
}
if (pWav->translatedFormatTag == DR_WAVE_FORMAT_DVI_ADPCM) {
- drwav_uint64 blockCount = dataSize / fmt.blockAlign;
- pWav->totalSampleCount = ((blockCount * (fmt.blockAlign - (4*pWav->channels))) * 2) + (blockCount * pWav->channels);
+ drwav_uint64 blockCount = dataChunkSize / fmt.blockAlign;
+ pWav->totalPCMFrameCount = (((blockCount * (fmt.blockAlign - (4*pWav->channels))) * 2) + (blockCount * pWav->channels)) / fmt.channels;
}
}
- // The way we calculate the bytes per sample does not make sense for compressed formats so we just set it to 0.
- if (drwav__is_compressed_format_tag(pWav->translatedFormatTag)) {
- pWav->bytesPerSample = 0;
- }
-
- // Some formats only support a certain number of channels.
+ /* Some formats only support a certain number of channels. */
if (pWav->translatedFormatTag == DR_WAVE_FORMAT_ADPCM || pWav->translatedFormatTag == DR_WAVE_FORMAT_DVI_ADPCM) {
if (pWav->channels > 2) {
return DRWAV_FALSE;
@@ -1569,22 +2014,26 @@ drwav_bool32 drwav_init(drwav* pWav, drwav_read_proc onRead, drwav_seek_proc onS
}
#ifdef DR_WAV_LIBSNDFILE_COMPAT
- // I use libsndfile as a benchmark for testing, however in the version I'm using (from the Windows installer on the libsndfile website),
- // it appears the total sample count libsndfile uses for MS-ADPCM is incorrect. It would seem they are computing the total sample count
- // from the number of blocks, however this results in the inclusion of extra silent samples at the end of the last block. The correct
- // way to know the total sample count is to inspect the "fact" chunk, which should always be present for compressed formats, and should
- // always include the sample count. This little block of code below is only used to emulate the libsndfile logic so I can properly run my
- // correctness tests against libsndfile, and is disabled by default.
+ /*
+ I use libsndfile as a benchmark for testing, however in the version I'm using (from the Windows installer on the libsndfile website),
+ it appears the total sample count libsndfile uses for MS-ADPCM is incorrect. It would seem they are computing the total sample count
+ from the number of blocks, however this results in the inclusion of extra silent samples at the end of the last block. The correct
+ way to know the total sample count is to inspect the "fact" chunk, which should always be present for compressed formats, and should
+ always include the sample count. This little block of code below is only used to emulate the libsndfile logic so I can properly run my
+ correctness tests against libsndfile, and is disabled by default.
+ */
if (pWav->translatedFormatTag == DR_WAVE_FORMAT_ADPCM) {
- drwav_uint64 blockCount = dataSize / fmt.blockAlign;
- pWav->totalSampleCount = (blockCount * (fmt.blockAlign - (6*pWav->channels))) * 2; // x2 because two samples per byte.
+ drwav_uint64 blockCount = dataChunkSize / fmt.blockAlign;
+ pWav->totalPCMFrameCount = (((blockCount * (fmt.blockAlign - (6*pWav->channels))) * 2)) / fmt.channels; /* x2 because two samples per byte. */
}
if (pWav->translatedFormatTag == DR_WAVE_FORMAT_DVI_ADPCM) {
- drwav_uint64 blockCount = dataSize / fmt.blockAlign;
- pWav->totalSampleCount = ((blockCount * (fmt.blockAlign - (4*pWav->channels))) * 2) + (blockCount * pWav->channels);
+ drwav_uint64 blockCount = dataChunkSize / fmt.blockAlign;
+ pWav->totalPCMFrameCount = (((blockCount * (fmt.blockAlign - (4*pWav->channels))) * 2) + (blockCount * pWav->channels)) / fmt.channels;
}
#endif
+ pWav->totalSampleCount = pWav->totalPCMFrameCount * pWav->channels;
+
return DRWAV_TRUE;
}
@@ -1609,17 +2058,21 @@ drwav_uint32 drwav_data_chunk_size_riff(drwav_uint64 dataChunkSize)
drwav_uint64 drwav_riff_chunk_size_w64(drwav_uint64 dataChunkSize)
{
- return 80 + 24 + dataChunkSize; // +24 because W64 includes the size of the GUID and size fields.
+ return 80 + 24 + dataChunkSize; /* +24 because W64 includes the size of the GUID and size fields. */
}
drwav_uint64 drwav_data_chunk_size_w64(drwav_uint64 dataChunkSize)
{
- return 24 + dataChunkSize; // +24 because W64 includes the size of the GUID and size fields.
+ return 24 + dataChunkSize; /* +24 because W64 includes the size of the GUID and size fields. */
}
drwav_bool32 drwav_init_write__internal(drwav* pWav, const drwav_data_format* pFormat, drwav_uint64 totalSampleCount, drwav_bool32 isSequential, drwav_write_proc onWrite, drwav_seek_proc onSeek, void* pUserData)
{
+ size_t runningPos = 0;
+ drwav_uint64 initialDataChunkSize = 0;
+ drwav_uint64 chunkSizeFMT;
+
if (pWav == NULL) {
return DRWAV_FALSE;
}
@@ -1629,11 +2082,11 @@ drwav_bool32 drwav_init_write__internal(drwav* pWav, const drwav_data_format* pF
}
if (!isSequential && onSeek == NULL) {
- return DRWAV_FALSE; // <-- onSeek is required when in non-sequential mode.
+ return DRWAV_FALSE; /* <-- onSeek is required when in non-sequential mode. */
}
- // Not currently supporting compressed formats. Will need to add support for the "fact" chunk before we enable this.
+ /* Not currently supporting compressed formats. Will need to add support for the "fact" chunk before we enable this. */
if (pFormat->format == DR_WAVE_FORMAT_EXTENSIBLE) {
return DRWAV_FALSE;
}
@@ -1655,21 +2108,21 @@ drwav_bool32 drwav_init_write__internal(drwav* pWav, const drwav_data_format* pF
pWav->fmt.extendedSize = 0;
pWav->isSequentialWrite = isSequential;
-
- size_t runningPos = 0;
-
- // The initial values for the "RIFF" and "data" chunks depends on whether or not we are initializing in sequential mode or not. In
- // sequential mode we set this to its final values straight away since they can be calculated from the total sample count. In non-
- // sequential mode we initialize it all to zero and fill it out in drwav_uninit() using a backwards seek.
- drwav_uint64 initialDataChunkSize = 0;
+ /*
+ The initial values for the "RIFF" and "data" chunks depends on whether or not we are initializing in sequential mode or not. In
+ sequential mode we set this to its final values straight away since they can be calculated from the total sample count. In non-
+ sequential mode we initialize it all to zero and fill it out in drwav_uninit() using a backwards seek.
+ */
if (isSequential) {
initialDataChunkSize = (totalSampleCount * pWav->fmt.bitsPerSample) / 8;
- // The RIFF container has a limit on the number of samples. drwav is not allowing this. There's no practical limits for Wave64
- // so for the sake of simplicity I'm not doing any validation for that.
+ /*
+ The RIFF container has a limit on the number of samples. drwav is not allowing this. There's no practical limits for Wave64
+ so for the sake of simplicity I'm not doing any validation for that.
+ */
if (pFormat->container == drwav_container_riff) {
if (initialDataChunkSize > (0xFFFFFFFF - 36)) {
- return DRWAV_FALSE; // Not enough room to store every sample.
+ return DRWAV_FALSE; /* Not enough room to store every sample. */
}
}
}
@@ -1677,21 +2130,20 @@ drwav_bool32 drwav_init_write__internal(drwav* pWav, const drwav_data_format* pF
pWav->dataChunkDataSizeTargetWrite = initialDataChunkSize;
- // "RIFF" chunk.
+ /* "RIFF" chunk. */
if (pFormat->container == drwav_container_riff) {
- drwav_uint32 chunkSizeRIFF = 36 + (drwav_uint32)initialDataChunkSize; // +36 = "RIFF"+[RIFF Chunk Size]+"WAVE" + [sizeof "fmt " chunk]
+ drwav_uint32 chunkSizeRIFF = 36 + (drwav_uint32)initialDataChunkSize; /* +36 = "RIFF"+[RIFF Chunk Size]+"WAVE" + [sizeof "fmt " chunk] */
runningPos += pWav->onWrite(pUserData, "RIFF", 4);
runningPos += pWav->onWrite(pUserData, &chunkSizeRIFF, 4);
runningPos += pWav->onWrite(pUserData, "WAVE", 4);
} else {
- drwav_uint64 chunkSizeRIFF = 80 + 24 + initialDataChunkSize; // +24 because W64 includes the size of the GUID and size fields.
+ drwav_uint64 chunkSizeRIFF = 80 + 24 + initialDataChunkSize; /* +24 because W64 includes the size of the GUID and size fields. */
runningPos += pWav->onWrite(pUserData, drwavGUID_W64_RIFF, 16);
runningPos += pWav->onWrite(pUserData, &chunkSizeRIFF, 8);
runningPos += pWav->onWrite(pUserData, drwavGUID_W64_WAVE, 16);
}
- // "fmt " chunk.
- drwav_uint64 chunkSizeFMT;
+ /* "fmt " chunk. */
if (pFormat->container == drwav_container_riff) {
chunkSizeFMT = 16;
runningPos += pWav->onWrite(pUserData, "fmt ", 4);
@@ -1711,19 +2163,19 @@ drwav_bool32 drwav_init_write__internal(drwav* pWav, const drwav_data_format* pF
pWav->dataChunkDataPos = runningPos;
- // "data" chunk.
+ /* "data" chunk. */
if (pFormat->container == drwav_container_riff) {
drwav_uint32 chunkSizeDATA = (drwav_uint32)initialDataChunkSize;
runningPos += pWav->onWrite(pUserData, "data", 4);
runningPos += pWav->onWrite(pUserData, &chunkSizeDATA, 4);
} else {
- drwav_uint64 chunkSizeDATA = 24 + initialDataChunkSize; // +24 because W64 includes the size of the GUID and size fields.
+ drwav_uint64 chunkSizeDATA = 24 + initialDataChunkSize; /* +24 because W64 includes the size of the GUID and size fields. */
runningPos += pWav->onWrite(pUserData, drwavGUID_W64_DATA, 16);
runningPos += pWav->onWrite(pUserData, &chunkSizeDATA, 8);
}
- // Simple validation.
+ /* Simple validation. */
if (pFormat->container == drwav_container_riff) {
if (runningPos != 20 + chunkSizeFMT + 8) {
return DRWAV_FALSE;
@@ -1736,12 +2188,11 @@ drwav_bool32 drwav_init_write__internal(drwav* pWav, const drwav_data_format* pF
- // Set some properties for the client's convenience.
+ /* Set some properties for the client's convenience. */
pWav->container = pFormat->container;
pWav->channels = (drwav_uint16)pFormat->channels;
pWav->sampleRate = pFormat->sampleRate;
pWav->bitsPerSample = (drwav_uint16)pFormat->bitsPerSample;
- pWav->bytesPerSample = (drwav_uint16)(pFormat->bitsPerSample >> 3);
pWav->translatedFormatTag = (drwav_uint16)pFormat->format;
return DRWAV_TRUE;
@@ -1750,12 +2201,12 @@ drwav_bool32 drwav_init_write__internal(drwav* pWav, const drwav_data_format* pF
drwav_bool32 drwav_init_write(drwav* pWav, const drwav_data_format* pFormat, drwav_write_proc onWrite, drwav_seek_proc onSeek, void* pUserData)
{
- return drwav_init_write__internal(pWav, pFormat, 0, DRWAV_FALSE, onWrite, onSeek, pUserData); // DRWAV_FALSE = Not Sequential
+ return drwav_init_write__internal(pWav, pFormat, 0, DRWAV_FALSE, onWrite, onSeek, pUserData); /* DRWAV_FALSE = Not Sequential */
}
drwav_bool32 drwav_init_write_sequential(drwav* pWav, const drwav_data_format* pFormat, drwav_uint64 totalSampleCount, drwav_write_proc onWrite, void* pUserData)
{
- return drwav_init_write__internal(pWav, pFormat, totalSampleCount, DRWAV_TRUE, onWrite, NULL, pUserData); // DRWAV_TRUE = Sequential
+ return drwav_init_write__internal(pWav, pFormat, totalSampleCount, DRWAV_TRUE, onWrite, NULL, pUserData); /* DRWAV_TRUE = Sequential */
}
void drwav_uninit(drwav* pWav)
@@ -1764,17 +2215,20 @@ void drwav_uninit(drwav* pWav)
return;
}
- // If the drwav object was opened in write mode we'll need to finalize a few things:
- // - Make sure the "data" chunk is aligned to 16-bits for RIFF containers, or 64 bits for W64 containers.
- // - Set the size of the "data" chunk.
+ /*
+ If the drwav object was opened in write mode we'll need to finalize a few things:
+ - Make sure the "data" chunk is aligned to 16-bits for RIFF containers, or 64 bits for W64 containers.
+ - Set the size of the "data" chunk.
+ */
if (pWav->onWrite != NULL) {
- // Validation for sequential mode.
+ drwav_uint32 paddingSize = 0;
+
+ /* Validation for sequential mode. */
if (pWav->isSequentialWrite) {
drwav_assert(pWav->dataChunkDataSize == pWav->dataChunkDataSizeTargetWrite);
}
- // Padding. Do not adjust pWav->dataChunkDataSize - this should not include the padding.
- drwav_uint32 paddingSize = 0;
+ /* Padding. Do not adjust pWav->dataChunkDataSize - this should not include the padding. */
if (pWav->container == drwav_container_riff) {
paddingSize = (drwav_uint32)(pWav->dataChunkDataSize % 2);
} else {
@@ -1786,30 +2240,31 @@ void drwav_uninit(drwav* pWav)
pWav->onWrite(pWav->pUserData, &paddingData, paddingSize);
}
-
- // Chunk sizes. When using sequential mode, these will have been filled in at initialization time. We only need
- // to do this when using non-sequential mode.
+ /*
+ Chunk sizes. When using sequential mode, these will have been filled in at initialization time. We only need
+ to do this when using non-sequential mode.
+ */
if (pWav->onSeek && !pWav->isSequentialWrite) {
if (pWav->container == drwav_container_riff) {
- // The "RIFF" chunk size.
+ /* The "RIFF" chunk size. */
if (pWav->onSeek(pWav->pUserData, 4, drwav_seek_origin_start)) {
drwav_uint32 riffChunkSize = drwav_riff_chunk_size_riff(pWav->dataChunkDataSize);
pWav->onWrite(pWav->pUserData, &riffChunkSize, 4);
}
- // the "data" chunk size.
+ /* the "data" chunk size. */
if (pWav->onSeek(pWav->pUserData, (int)pWav->dataChunkDataPos + 4, drwav_seek_origin_start)) {
drwav_uint32 dataChunkSize = drwav_data_chunk_size_riff(pWav->dataChunkDataSize);
pWav->onWrite(pWav->pUserData, &dataChunkSize, 4);
}
} else {
- // The "RIFF" chunk size.
+ /* The "RIFF" chunk size. */
if (pWav->onSeek(pWav->pUserData, 16, drwav_seek_origin_start)) {
drwav_uint64 riffChunkSize = drwav_riff_chunk_size_w64(pWav->dataChunkDataSize);
pWav->onWrite(pWav->pUserData, &riffChunkSize, 8);
}
- // The "data" chunk size.
+ /* The "data" chunk size. */
if (pWav->onSeek(pWav->pUserData, (int)pWav->dataChunkDataPos + 16, drwav_seek_origin_start)) {
drwav_uint64 dataChunkSize = drwav_data_chunk_size_w64(pWav->dataChunkDataSize);
pWav->onWrite(pWav->pUserData, &dataChunkSize, 8);
@@ -1819,8 +2274,10 @@ void drwav_uninit(drwav* pWav)
}
#ifndef DR_WAV_NO_STDIO
- // If we opened the file with drwav_open_file() we will want to close the file handle. We can know whether or not drwav_open_file()
- // was used by looking at the onRead and onSeek callbacks.
+ /*
+ If we opened the file with drwav_open_file() we will want to close the file handle. We can know whether or not drwav_open_file()
+ was used by looking at the onRead and onSeek callbacks.
+ */
if (pWav->onRead == drwav__on_read_stdio || pWav->onWrite == drwav__on_write_stdio) {
fclose((FILE*)pWav->pUserData);
}
@@ -1830,12 +2287,17 @@ void drwav_uninit(drwav* pWav)
drwav* drwav_open(drwav_read_proc onRead, drwav_seek_proc onSeek, void* pUserData)
{
+ return drwav_open_ex(onRead, onSeek, NULL, pUserData, NULL, 0);
+}
+
+drwav* drwav_open_ex(drwav_read_proc onRead, drwav_seek_proc onSeek, drwav_chunk_proc onChunk, void* pReadSeekUserData, void* pChunkUserData, drwav_uint32 flags)
+{
drwav* pWav = (drwav*)DRWAV_MALLOC(sizeof(*pWav));
if (pWav == NULL) {
return NULL;
}
- if (!drwav_init(pWav, onRead, onSeek, pUserData)) {
+ if (!drwav_init_ex(pWav, onRead, onSeek, onChunk, pReadSeekUserData, pChunkUserData, flags)) {
DRWAV_FREE(pWav);
return NULL;
}
@@ -1878,6 +2340,8 @@ void drwav_close(drwav* pWav)
size_t drwav_read_raw(drwav* pWav, size_t bytesToRead, void* pBufferOut)
{
+ size_t bytesRead;
+
if (pWav == NULL || bytesToRead == 0 || pBufferOut == NULL) {
return 0;
}
@@ -1886,7 +2350,7 @@ size_t drwav_read_raw(drwav* pWav, size_t bytesToRead, void* pBufferOut)
bytesToRead = (size_t)pWav->bytesRemaining;
}
- size_t bytesRead = pWav->onRead(pWav->pUserData, pBufferOut, bytesToRead);
+ bytesRead = pWav->onRead(pWav->pUserData, pBufferOut, bytesToRead);
pWav->bytesRemaining -= bytesRead;
return bytesRead;
@@ -1894,28 +2358,64 @@ size_t drwav_read_raw(drwav* pWav, size_t bytesToRead, void* pBufferOut)
drwav_uint64 drwav_read(drwav* pWav, drwav_uint64 samplesToRead, void* pBufferOut)
{
+ drwav_uint32 bytesPerSample;
+ size_t bytesRead;
+
if (pWav == NULL || samplesToRead == 0 || pBufferOut == NULL) {
return 0;
}
- // Cannot use this function for compressed formats.
+ /* Cannot use this function for compressed formats. */
+ if (drwav__is_compressed_format_tag(pWav->translatedFormatTag)) {
+ return 0;
+ }
+
+ bytesPerSample = drwav_get_bytes_per_sample(pWav);
+ if (bytesPerSample == 0) {
+ return 0;
+ }
+
+ /* Don't try to read more samples than can potentially fit in the output buffer. */
+ if (samplesToRead * bytesPerSample > DRWAV_SIZE_MAX) {
+ samplesToRead = DRWAV_SIZE_MAX / bytesPerSample;
+ }
+
+ bytesRead = drwav_read_raw(pWav, (size_t)(samplesToRead * bytesPerSample), pBufferOut);
+ return bytesRead / bytesPerSample;
+}
+
+drwav_uint64 drwav_read_pcm_frames(drwav* pWav, drwav_uint64 framesToRead, void* pBufferOut)
+{
+ drwav_uint32 bytesPerFrame;
+ size_t bytesRead;
+
+ if (pWav == NULL || framesToRead == 0 || pBufferOut == NULL) {
+ return 0;
+ }
+
+ /* Cannot use this function for compressed formats. */
if (drwav__is_compressed_format_tag(pWav->translatedFormatTag)) {
return 0;
}
- // Don't try to read more samples than can potentially fit in the output buffer.
- if (samplesToRead * pWav->bytesPerSample > SIZE_MAX) {
- samplesToRead = SIZE_MAX / pWav->bytesPerSample;
+ bytesPerFrame = drwav_get_bytes_per_pcm_frame(pWav);
+ if (bytesPerFrame == 0) {
+ return 0;
}
- size_t bytesRead = drwav_read_raw(pWav, (size_t)(samplesToRead * pWav->bytesPerSample), pBufferOut);
- return bytesRead / pWav->bytesPerSample;
+ /* Don't try to read more samples than can potentially fit in the output buffer. */
+ if (framesToRead * bytesPerFrame > DRWAV_SIZE_MAX) {
+ framesToRead = DRWAV_SIZE_MAX / bytesPerFrame;
+ }
+
+ bytesRead = drwav_read_raw(pWav, (size_t)(framesToRead * bytesPerFrame), pBufferOut);
+ return bytesRead / bytesPerFrame;
}
-drwav_bool32 drwav_seek_to_first_sample(drwav* pWav)
+drwav_bool32 drwav_seek_to_first_pcm_frame(drwav* pWav)
{
if (pWav->onWrite != NULL) {
- return DRWAV_FALSE; // No seeking in write mode.
+ return DRWAV_FALSE; /* No seeking in write mode. */
}
if (!pWav->onSeek(pWav->pUserData, (int)pWav->dataChunkDataPos, drwav_seek_origin_start)) {
@@ -1932,36 +2432,39 @@ drwav_bool32 drwav_seek_to_first_sample(drwav* pWav)
drwav_bool32 drwav_seek_to_sample(drwav* pWav, drwav_uint64 sample)
{
- // Seeking should be compatible with wave files > 2GB.
+ /* Seeking should be compatible with wave files > 2GB. */
if (pWav->onWrite != NULL) {
- return DRWAV_FALSE; // No seeking in write mode.
+ return DRWAV_FALSE; /* No seeking in write mode. */
}
if (pWav == NULL || pWav->onSeek == NULL) {
return DRWAV_FALSE;
}
- // If there are no samples, just return DRWAV_TRUE without doing anything.
+ /* If there are no samples, just return DRWAV_TRUE without doing anything. */
if (pWav->totalSampleCount == 0) {
return DRWAV_TRUE;
}
- // Make sure the sample is clamped.
+ /* Make sure the sample is clamped. */
if (sample >= pWav->totalSampleCount) {
sample = pWav->totalSampleCount - 1;
}
-
- // For compressed formats we just use a slow generic seek. If we are seeking forward we just seek forward. If we are going backwards we need
- // to seek back to the start.
+ /*
+ For compressed formats we just use a slow generic seek. If we are seeking forward we just seek forward. If we are going backwards we need
+ to seek back to the start.
+ */
if (drwav__is_compressed_format_tag(pWav->translatedFormatTag)) {
- // TODO: This can be optimized.
+ /* TODO: This can be optimized. */
- // If we're seeking forward it's simple - just keep reading samples until we hit the sample we're requesting. If we're seeking backwards,
- // we first need to seek back to the start and then just do the same thing as a forward seek.
+ /*
+ If we're seeking forward it's simple - just keep reading samples until we hit the sample we're requesting. If we're seeking backwards,
+ we first need to seek back to the start and then just do the same thing as a forward seek.
+ */
if (sample < pWav->compressed.iCurrentSample) {
- if (!drwav_seek_to_first_sample(pWav)) {
+ if (!drwav_seek_to_first_pcm_frame(pWav)) {
return DRWAV_FALSE;
}
}
@@ -1971,18 +2474,18 @@ drwav_bool32 drwav_seek_to_sample(drwav* pWav, drwav_uint64 sample)
drwav_int16 devnull[2048];
while (offset > 0) {
+ drwav_uint64 samplesRead = 0;
drwav_uint64 samplesToRead = offset;
if (samplesToRead > 2048) {
samplesToRead = 2048;
}
- drwav_uint64 samplesRead = 0;
if (pWav->translatedFormatTag == DR_WAVE_FORMAT_ADPCM) {
samplesRead = drwav_read_s16__msadpcm(pWav, samplesToRead, devnull);
} else if (pWav->translatedFormatTag == DR_WAVE_FORMAT_DVI_ADPCM) {
samplesRead = drwav_read_s16__ima(pWav, samplesToRead, devnull);
} else {
- assert(DRWAV_FALSE); // If this assertion is triggered it means I've implemented a new compressed format but forgot to add a branch for it here.
+ assert(DRWAV_FALSE); /* If this assertion is triggered it means I've implemented a new compressed format but forgot to add a branch for it here. */
}
if (samplesRead != samplesToRead) {
@@ -1993,19 +2496,23 @@ drwav_bool32 drwav_seek_to_sample(drwav* pWav, drwav_uint64 sample)
}
}
} else {
- drwav_uint64 totalSizeInBytes = pWav->totalSampleCount * pWav->bytesPerSample;
+ drwav_uint64 totalSizeInBytes;
+ drwav_uint64 currentBytePos;
+ drwav_uint64 targetBytePos;
+ drwav_uint64 offset;
+
+ totalSizeInBytes = pWav->totalPCMFrameCount * drwav_get_bytes_per_pcm_frame(pWav);
drwav_assert(totalSizeInBytes >= pWav->bytesRemaining);
- drwav_uint64 currentBytePos = totalSizeInBytes - pWav->bytesRemaining;
- drwav_uint64 targetBytePos = sample * pWav->bytesPerSample;
+ currentBytePos = totalSizeInBytes - pWav->bytesRemaining;
+ targetBytePos = sample * drwav_get_bytes_per_sample(pWav);
- drwav_uint64 offset;
if (currentBytePos < targetBytePos) {
- // Offset forwards.
+ /* Offset forwards. */
offset = (targetBytePos - currentBytePos);
} else {
- // Offset backwards.
- if (!drwav_seek_to_first_sample(pWav)) {
+ /* Offset backwards. */
+ if (!drwav_seek_to_first_pcm_frame(pWav)) {
return DRWAV_FALSE;
}
offset = targetBytePos;
@@ -2025,14 +2532,21 @@ drwav_bool32 drwav_seek_to_sample(drwav* pWav, drwav_uint64 sample)
return DRWAV_TRUE;
}
+drwav_bool32 drwav_seek_to_pcm_frame(drwav* pWav, drwav_uint64 targetFrameIndex)
+{
+ return drwav_seek_to_sample(pWav, targetFrameIndex * pWav->channels);
+}
+
size_t drwav_write_raw(drwav* pWav, size_t bytesToWrite, const void* pData)
{
+ size_t bytesWritten;
+
if (pWav == NULL || bytesToWrite == 0 || pData == NULL) {
return 0;
}
- size_t bytesWritten = pWav->onWrite(pWav->pUserData, pData, bytesToWrite);
+ bytesWritten = pWav->onWrite(pWav->pUserData, pData, bytesToWrite);
pWav->dataChunkDataSize += bytesWritten;
return bytesWritten;
@@ -2040,24 +2554,29 @@ size_t drwav_write_raw(drwav* pWav, size_t bytesToWrite, const void* pData)
drwav_uint64 drwav_write(drwav* pWav, drwav_uint64 samplesToWrite, const void* pData)
{
+ drwav_uint64 bytesToWrite;
+ drwav_uint64 bytesWritten;
+ const drwav_uint8* pRunningData;
+
if (pWav == NULL || samplesToWrite == 0 || pData == NULL) {
return 0;
}
- drwav_uint64 bytesToWrite = ((samplesToWrite * pWav->bitsPerSample) / 8);
- if (bytesToWrite > SIZE_MAX) {
+ bytesToWrite = ((samplesToWrite * pWav->bitsPerSample) / 8);
+ if (bytesToWrite > DRWAV_SIZE_MAX) {
return 0;
}
- drwav_uint64 bytesWritten = 0;
- const drwav_uint8* pRunningData = (const drwav_uint8*)pData;
+ bytesWritten = 0;
+ pRunningData = (const drwav_uint8*)pData;
while (bytesToWrite > 0) {
+ size_t bytesJustWritten;
drwav_uint64 bytesToWriteThisIteration = bytesToWrite;
- if (bytesToWriteThisIteration > SIZE_MAX) {
- bytesToWriteThisIteration = SIZE_MAX;
+ if (bytesToWriteThisIteration > DRWAV_SIZE_MAX) {
+ bytesToWriteThisIteration = DRWAV_SIZE_MAX;
}
- size_t bytesJustWritten = drwav_write_raw(pWav, (size_t)bytesToWriteThisIteration, pRunningData);
+ bytesJustWritten = drwav_write_raw(pWav, (size_t)bytesToWriteThisIteration, pRunningData);
if (bytesJustWritten == 0) {
break;
}
@@ -2070,23 +2589,28 @@ drwav_uint64 drwav_write(drwav* pWav, drwav_uint64 samplesToWrite, const void* p
return (bytesWritten * 8) / pWav->bitsPerSample;
}
+drwav_uint64 drwav_write_pcm_frames(drwav* pWav, drwav_uint64 framesToWrite, const void* pData)
+{
+ return drwav_write(pWav, framesToWrite * pWav->channels, pData) / pWav->channels;
+}
+
drwav_uint64 drwav_read_s16__msadpcm(drwav* pWav, drwav_uint64 samplesToRead, drwav_int16* pBufferOut)
{
+ drwav_uint64 totalSamplesRead = 0;
+
drwav_assert(pWav != NULL);
drwav_assert(samplesToRead > 0);
drwav_assert(pBufferOut != NULL);
- // TODO: Lots of room for optimization here.
-
- drwav_uint64 totalSamplesRead = 0;
+ /* TODO: Lots of room for optimization here. */
while (samplesToRead > 0 && pWav->compressed.iCurrentSample < pWav->totalSampleCount) {
- // If there are no cached samples we need to load a new block.
+ /* If there are no cached samples we need to load a new block. */
if (pWav->msadpcm.cachedSampleCount == 0 && pWav->msadpcm.bytesRemainingInBlock == 0) {
if (pWav->channels == 1) {
- // Mono.
+ /* Mono. */
drwav_uint8 header[7];
if (pWav->onRead(pWav->pUserData, header, sizeof(header)) != sizeof(header)) {
return totalSamplesRead;
@@ -2101,7 +2625,7 @@ drwav_uint64 drwav_read_s16__msadpcm(drwav* pWav, drwav_uint64 samplesToRead, dr
pWav->msadpcm.cachedSamples[3] = pWav->msadpcm.prevSamples[0][1];
pWav->msadpcm.cachedSampleCount = 2;
} else {
- // Stereo.
+ /* Stereo. */
drwav_uint8 header[14];
if (pWav->onRead(pWav->pUserData, header, sizeof(header)) != sizeof(header)) {
return totalSamplesRead;
@@ -2125,7 +2649,7 @@ drwav_uint64 drwav_read_s16__msadpcm(drwav* pWav, drwav_uint64 samplesToRead, dr
}
}
- // Output anything that's cached.
+ /* Output anything that's cached. */
while (samplesToRead > 0 && pWav->msadpcm.cachedSampleCount > 0 && pWav->compressed.iCurrentSample < pWav->totalSampleCount) {
pBufferOut[0] = (drwav_int16)pWav->msadpcm.cachedSamples[drwav_countof(pWav->msadpcm.cachedSamples) - pWav->msadpcm.cachedSampleCount];
pWav->msadpcm.cachedSampleCount -= 1;
@@ -2141,22 +2665,14 @@ drwav_uint64 drwav_read_s16__msadpcm(drwav* pWav, drwav_uint64 samplesToRead, dr
}
- // If there's nothing left in the cache, just go ahead and load more. If there's nothing left to load in the current block we just continue to the next
- // loop iteration which will trigger the loading of a new block.
+ /*
+ If there's nothing left in the cache, just go ahead and load more. If there's nothing left to load in the current block we just continue to the next
+ loop iteration which will trigger the loading of a new block.
+ */
if (pWav->msadpcm.cachedSampleCount == 0) {
if (pWav->msadpcm.bytesRemainingInBlock == 0) {
continue;
} else {
- drwav_uint8 nibbles;
- if (pWav->onRead(pWav->pUserData, &nibbles, 1) != 1) {
- return totalSamplesRead;
- }
- pWav->msadpcm.bytesRemainingInBlock -= 1;
-
- // TODO: Optimize away these if statements.
- drwav_int32 nibble0 = ((nibbles & 0xF0) >> 4); if ((nibbles & 0x80)) { nibble0 |= 0xFFFFFFF0UL; }
- drwav_int32 nibble1 = ((nibbles & 0x0F) >> 0); if ((nibbles & 0x08)) { nibble1 |= 0xFFFFFFF0UL; }
-
static drwav_int32 adaptationTable[] = {
230, 230, 230, 230, 307, 409, 512, 614,
768, 614, 512, 409, 307, 230, 230, 230
@@ -2164,9 +2680,24 @@ drwav_uint64 drwav_read_s16__msadpcm(drwav* pWav, drwav_uint64 samplesToRead, dr
static drwav_int32 coeff1Table[] = { 256, 512, 0, 192, 240, 460, 392 };
static drwav_int32 coeff2Table[] = { 0, -256, 0, 64, 0, -208, -232 };
+ drwav_uint8 nibbles;
+ drwav_int32 nibble0;
+ drwav_int32 nibble1;
+
+ if (pWav->onRead(pWav->pUserData, &nibbles, 1) != 1) {
+ return totalSamplesRead;
+ }
+ pWav->msadpcm.bytesRemainingInBlock -= 1;
+
+ /* TODO: Optimize away these if statements. */
+ nibble0 = ((nibbles & 0xF0) >> 4); if ((nibbles & 0x80)) { nibble0 |= 0xFFFFFFF0UL; }
+ nibble1 = ((nibbles & 0x0F) >> 0); if ((nibbles & 0x08)) { nibble1 |= 0xFFFFFFF0UL; }
+
if (pWav->channels == 1) {
- // Mono.
+ /* Mono. */
drwav_int32 newSample0;
+ drwav_int32 newSample1;
+
newSample0 = ((pWav->msadpcm.prevSamples[0][1] * coeff1Table[pWav->msadpcm.predictor[0]]) + (pWav->msadpcm.prevSamples[0][0] * coeff2Table[pWav->msadpcm.predictor[0]])) >> 8;
newSample0 += nibble0 * pWav->msadpcm.delta[0];
newSample0 = drwav_clamp(newSample0, -32768, 32767);
@@ -2180,7 +2711,6 @@ drwav_uint64 drwav_read_s16__msadpcm(drwav* pWav, drwav_uint64 samplesToRead, dr
pWav->msadpcm.prevSamples[0][1] = newSample0;
- drwav_int32 newSample1;
newSample1 = ((pWav->msadpcm.prevSamples[0][1] * coeff1Table[pWav->msadpcm.predictor[0]]) + (pWav->msadpcm.prevSamples[0][0] * coeff2Table[pWav->msadpcm.predictor[0]])) >> 8;
newSample1 += nibble1 * pWav->msadpcm.delta[0];
newSample1 = drwav_clamp(newSample1, -32768, 32767);
@@ -2198,10 +2728,11 @@ drwav_uint64 drwav_read_s16__msadpcm(drwav* pWav, drwav_uint64 samplesToRead, dr
pWav->msadpcm.cachedSamples[3] = newSample1;
pWav->msadpcm.cachedSampleCount = 2;
} else {
- // Stereo.
-
- // Left.
+ /* Stereo. */
drwav_int32 newSample0;
+ drwav_int32 newSample1;
+
+ /* Left. */
newSample0 = ((pWav->msadpcm.prevSamples[0][1] * coeff1Table[pWav->msadpcm.predictor[0]]) + (pWav->msadpcm.prevSamples[0][0] * coeff2Table[pWav->msadpcm.predictor[0]])) >> 8;
newSample0 += nibble0 * pWav->msadpcm.delta[0];
newSample0 = drwav_clamp(newSample0, -32768, 32767);
@@ -2215,8 +2746,7 @@ drwav_uint64 drwav_read_s16__msadpcm(drwav* pWav, drwav_uint64 samplesToRead, dr
pWav->msadpcm.prevSamples[0][1] = newSample0;
- // Right.
- drwav_int32 newSample1;
+ /* Right. */
newSample1 = ((pWav->msadpcm.prevSamples[1][1] * coeff1Table[pWav->msadpcm.predictor[1]]) + (pWav->msadpcm.prevSamples[1][0] * coeff2Table[pWav->msadpcm.predictor[1]])) >> 8;
newSample1 += nibble1 * pWav->msadpcm.delta[1];
newSample1 = drwav_clamp(newSample1, -32768, 32767);
@@ -2242,19 +2772,19 @@ drwav_uint64 drwav_read_s16__msadpcm(drwav* pWav, drwav_uint64 samplesToRead, dr
drwav_uint64 drwav_read_s16__ima(drwav* pWav, drwav_uint64 samplesToRead, drwav_int16* pBufferOut)
{
+ drwav_uint64 totalSamplesRead = 0;
+
drwav_assert(pWav != NULL);
drwav_assert(samplesToRead > 0);
drwav_assert(pBufferOut != NULL);
- // TODO: Lots of room for optimization here.
-
- drwav_uint64 totalSamplesRead = 0;
+ /* TODO: Lots of room for optimization here. */
while (samplesToRead > 0 && pWav->compressed.iCurrentSample < pWav->totalSampleCount) {
- // If there are no cached samples we need to load a new block.
+ /* If there are no cached samples we need to load a new block. */
if (pWav->ima.cachedSampleCount == 0 && pWav->ima.bytesRemainingInBlock == 0) {
if (pWav->channels == 1) {
- // Mono.
+ /* Mono. */
drwav_uint8 header[4];
if (pWav->onRead(pWav->pUserData, header, sizeof(header)) != sizeof(header)) {
return totalSamplesRead;
@@ -2266,7 +2796,7 @@ drwav_uint64 drwav_read_s16__ima(drwav* pWav, drwav_uint64 samplesToRead, drwav_
pWav->ima.cachedSamples[drwav_countof(pWav->ima.cachedSamples) - 1] = pWav->ima.predictor[0];
pWav->ima.cachedSampleCount = 1;
} else {
- // Stereo.
+ /* Stereo. */
drwav_uint8 header[8];
if (pWav->onRead(pWav->pUserData, header, sizeof(header)) != sizeof(header)) {
return totalSamplesRead;
@@ -2284,7 +2814,7 @@ drwav_uint64 drwav_read_s16__ima(drwav* pWav, drwav_uint64 samplesToRead, drwav_
}
}
- // Output anything that's cached.
+ /* Output anything that's cached. */
while (samplesToRead > 0 && pWav->ima.cachedSampleCount > 0 && pWav->compressed.iCurrentSample < pWav->totalSampleCount) {
pBufferOut[0] = (drwav_int16)pWav->ima.cachedSamples[drwav_countof(pWav->ima.cachedSamples) - pWav->ima.cachedSampleCount];
pWav->ima.cachedSampleCount -= 1;
@@ -2299,8 +2829,10 @@ drwav_uint64 drwav_read_s16__ima(drwav* pWav, drwav_uint64 samplesToRead, drwav_
return totalSamplesRead;
}
- // If there's nothing left in the cache, just go ahead and load more. If there's nothing left to load in the current block we just continue to the next
- // loop iteration which will trigger the loading of a new block.
+ /*
+ If there's nothing left in the cache, just go ahead and load more. If there's nothing left to load in the current block we just continue to the next
+ loop iteration which will trigger the loading of a new block.
+ */
if (pWav->ima.cachedSampleCount == 0) {
if (pWav->ima.bytesRemainingInBlock == 0) {
continue;
@@ -2322,17 +2854,22 @@ drwav_uint64 drwav_read_s16__ima(drwav* pWav, drwav_uint64 samplesToRead, drwav_
15289, 16818, 18500, 20350, 22385, 24623, 27086, 29794, 32767
};
- // From what I can tell with stereo streams, it looks like every 4 bytes (8 samples) is for one channel. So it goes 4 bytes for the
- // left channel, 4 bytes for the right channel.
+ drwav_uint32 iChannel;
+
+ /*
+ From what I can tell with stereo streams, it looks like every 4 bytes (8 samples) is for one channel. So it goes 4 bytes for the
+ left channel, 4 bytes for the right channel.
+ */
pWav->ima.cachedSampleCount = 8 * pWav->channels;
- for (drwav_uint32 iChannel = 0; iChannel < pWav->channels; ++iChannel) {
+ for (iChannel = 0; iChannel < pWav->channels; ++iChannel) {
+ drwav_uint32 iByte;
drwav_uint8 nibbles[4];
if (pWav->onRead(pWav->pUserData, &nibbles, 4) != 4) {
return totalSamplesRead;
}
pWav->ima.bytesRemainingInBlock -= 4;
- for (drwav_uint32 iByte = 0; iByte < 4; ++iByte) {
+ for (iByte = 0; iByte < 4; ++iByte) {
drwav_uint8 nibble0 = ((nibbles[iByte] & 0x0F) >> 0);
drwav_uint8 nibble1 = ((nibbles[iByte] & 0xF0) >> 4);
@@ -2425,19 +2962,21 @@ static DRWAV_INLINE drwav_int16 drwav__mulaw_to_s16(drwav_uint8 sampleIn)
-static void drwav__pcm_to_s16(drwav_int16* pOut, const unsigned char* pIn, size_t totalSampleCount, unsigned short bytesPerSample)
+static void drwav__pcm_to_s16(drwav_int16* pOut, const unsigned char* pIn, size_t totalSampleCount, unsigned int bytesPerSample)
{
- // Special case for 8-bit sample data because it's treated as unsigned.
+ unsigned int i;
+
+ /* Special case for 8-bit sample data because it's treated as unsigned. */
if (bytesPerSample == 1) {
drwav_u8_to_s16(pOut, pIn, totalSampleCount);
return;
}
- // Slightly more optimal implementation for common formats.
+ /* Slightly more optimal implementation for common formats. */
if (bytesPerSample == 2) {
- for (unsigned int i = 0; i < totalSampleCount; ++i) {
- *pOut++ = ((drwav_int16*)pIn)[i];
+ for (i = 0; i < totalSampleCount; ++i) {
+ *pOut++ = ((const drwav_int16*)pIn)[i];
}
return;
}
@@ -2451,15 +2990,15 @@ static void drwav__pcm_to_s16(drwav_int16* pOut, const unsigned char* pIn, size_
}
- // Anything more than 64 bits per sample is not supported.
+ /* Anything more than 64 bits per sample is not supported. */
if (bytesPerSample > 8) {
drwav_zero_memory(pOut, totalSampleCount * sizeof(*pOut));
return;
}
- // Generic, slow converter.
- for (unsigned int i = 0; i < totalSampleCount; ++i) {
+ /* Generic, slow converter. */
+ for (i = 0; i < totalSampleCount; ++i) {
drwav_uint64 sample = 0;
unsigned int shift = (8 - bytesPerSample) * 8;
@@ -2474,16 +3013,16 @@ static void drwav__pcm_to_s16(drwav_int16* pOut, const unsigned char* pIn, size_
}
}
-static void drwav__ieee_to_s16(drwav_int16* pOut, const unsigned char* pIn, size_t totalSampleCount, unsigned short bytesPerSample)
+static void drwav__ieee_to_s16(drwav_int16* pOut, const unsigned char* pIn, size_t totalSampleCount, unsigned int bytesPerSample)
{
if (bytesPerSample == 4) {
- drwav_f32_to_s16(pOut, (float*)pIn, totalSampleCount);
+ drwav_f32_to_s16(pOut, (const float*)pIn, totalSampleCount);
return;
} else if (bytesPerSample == 8) {
- drwav_f64_to_s16(pOut, (double*)pIn, totalSampleCount);
+ drwav_f64_to_s16(pOut, (const double*)pIn, totalSampleCount);
return;
} else {
- // Only supporting 32- and 64-bit float. Output silence in all other cases. Contributions welcome for 16-bit float.
+ /* Only supporting 32- and 64-bit float. Output silence in all other cases. Contributions welcome for 16-bit float. */
drwav_zero_memory(pOut, totalSampleCount * sizeof(*pOut));
return;
}
@@ -2491,20 +3030,29 @@ static void drwav__ieee_to_s16(drwav_int16* pOut, const unsigned char* pIn, size
drwav_uint64 drwav_read_s16__pcm(drwav* pWav, drwav_uint64 samplesToRead, drwav_int16* pBufferOut)
{
- // Fast path.
- if (pWav->bytesPerSample == 2) {
+ drwav_uint32 bytesPerSample;
+ drwav_uint64 totalSamplesRead;
+ unsigned char sampleData[4096];
+
+ /* Fast path. */
+ if (pWav->translatedFormatTag == DR_WAVE_FORMAT_PCM && pWav->bitsPerSample == 16) {
return drwav_read(pWav, samplesToRead, pBufferOut);
}
+
+ bytesPerSample = drwav_get_bytes_per_sample(pWav);
+ if (bytesPerSample == 0) {
+ return 0;
+ }
- drwav_uint64 totalSamplesRead = 0;
- unsigned char sampleData[4096];
+ totalSamplesRead = 0;
+
while (samplesToRead > 0) {
- drwav_uint64 samplesRead = drwav_read(pWav, drwav_min(samplesToRead, sizeof(sampleData)/pWav->bytesPerSample), sampleData);
+ drwav_uint64 samplesRead = drwav_read(pWav, drwav_min(samplesToRead, sizeof(sampleData)/bytesPerSample), sampleData);
if (samplesRead == 0) {
break;
}
- drwav__pcm_to_s16(pBufferOut, sampleData, (size_t)samplesRead, pWav->bytesPerSample);
+ drwav__pcm_to_s16(pBufferOut, sampleData, (size_t)samplesRead, bytesPerSample);
pBufferOut += samplesRead;
samplesToRead -= samplesRead;
@@ -2516,15 +3064,23 @@ drwav_uint64 drwav_read_s16__pcm(drwav* pWav, drwav_uint64 samplesToRead, drwav_
drwav_uint64 drwav_read_s16__ieee(drwav* pWav, drwav_uint64 samplesToRead, drwav_int16* pBufferOut)
{
- drwav_uint64 totalSamplesRead = 0;
+ drwav_uint64 totalSamplesRead;
unsigned char sampleData[4096];
+
+ drwav_uint32 bytesPerSample = drwav_get_bytes_per_sample(pWav);
+ if (bytesPerSample == 0) {
+ return 0;
+ }
+
+ totalSamplesRead = 0;
+
while (samplesToRead > 0) {
- drwav_uint64 samplesRead = drwav_read(pWav, drwav_min(samplesToRead, sizeof(sampleData)/pWav->bytesPerSample), sampleData);
+ drwav_uint64 samplesRead = drwav_read(pWav, drwav_min(samplesToRead, sizeof(sampleData)/bytesPerSample), sampleData);
if (samplesRead == 0) {
break;
}
- drwav__ieee_to_s16(pBufferOut, sampleData, (size_t)samplesRead, pWav->bytesPerSample);
+ drwav__ieee_to_s16(pBufferOut, sampleData, (size_t)samplesRead, bytesPerSample);
pBufferOut += samplesRead;
samplesToRead -= samplesRead;
@@ -2536,10 +3092,18 @@ drwav_uint64 drwav_read_s16__ieee(drwav* pWav, drwav_uint64 samplesToRead, drwav
drwav_uint64 drwav_read_s16__alaw(drwav* pWav, drwav_uint64 samplesToRead, drwav_int16* pBufferOut)
{
- drwav_uint64 totalSamplesRead = 0;
+ drwav_uint64 totalSamplesRead;
unsigned char sampleData[4096];
+
+ drwav_uint32 bytesPerSample = drwav_get_bytes_per_sample(pWav);
+ if (bytesPerSample == 0) {
+ return 0;
+ }
+
+ totalSamplesRead = 0;
+
while (samplesToRead > 0) {
- drwav_uint64 samplesRead = drwav_read(pWav, drwav_min(samplesToRead, sizeof(sampleData)/pWav->bytesPerSample), sampleData);
+ drwav_uint64 samplesRead = drwav_read(pWav, drwav_min(samplesToRead, sizeof(sampleData)/bytesPerSample), sampleData);
if (samplesRead == 0) {
break;
}
@@ -2556,10 +3120,18 @@ drwav_uint64 drwav_read_s16__alaw(drwav* pWav, drwav_uint64 samplesToRead, drwav
drwav_uint64 drwav_read_s16__mulaw(drwav* pWav, drwav_uint64 samplesToRead, drwav_int16* pBufferOut)
{
- drwav_uint64 totalSamplesRead = 0;
+ drwav_uint64 totalSamplesRead;
unsigned char sampleData[4096];
+
+ drwav_uint32 bytesPerSample = drwav_get_bytes_per_sample(pWav);
+ if (bytesPerSample == 0) {
+ return 0;
+ }
+
+ totalSamplesRead = 0;
+
while (samplesToRead > 0) {
- drwav_uint64 samplesRead = drwav_read(pWav, drwav_min(samplesToRead, sizeof(sampleData)/pWav->bytesPerSample), sampleData);
+ drwav_uint64 samplesRead = drwav_read(pWav, drwav_min(samplesToRead, sizeof(sampleData)/bytesPerSample), sampleData);
if (samplesRead == 0) {
break;
}
@@ -2580,9 +3152,9 @@ drwav_uint64 drwav_read_s16(drwav* pWav, drwav_uint64 samplesToRead, drwav_int16
return 0;
}
- // Don't try to read more samples than can potentially fit in the output buffer.
- if (samplesToRead * sizeof(drwav_int16) > SIZE_MAX) {
- samplesToRead = SIZE_MAX / sizeof(drwav_int16);
+ /* Don't try to read more samples than can potentially fit in the output buffer. */
+ if (samplesToRead * sizeof(drwav_int16) > DRWAV_SIZE_MAX) {
+ samplesToRead = DRWAV_SIZE_MAX / sizeof(drwav_int16);
}
if (pWav->translatedFormatTag == DR_WAVE_FORMAT_PCM) {
@@ -2612,10 +3184,16 @@ drwav_uint64 drwav_read_s16(drwav* pWav, drwav_uint64 samplesToRead, drwav_int16
return 0;
}
+drwav_uint64 drwav_read_pcm_frames_s16(drwav* pWav, drwav_uint64 framesToRead, drwav_int16* pBufferOut)
+{
+ return drwav_read_s16(pWav, framesToRead * pWav->channels, pBufferOut) / pWav->channels;
+}
+
void drwav_u8_to_s16(drwav_int16* pOut, const drwav_uint8* pIn, size_t sampleCount)
{
int r;
- for (size_t i = 0; i < sampleCount; ++i) {
+ size_t i;
+ for (i = 0; i < sampleCount; ++i) {
int x = pIn[i];
r = x - 128;
r = r << 8;
@@ -2626,8 +3204,9 @@ void drwav_u8_to_s16(drwav_int16* pOut, const drwav_uint8* pIn, size_t sampleCou
void drwav_s24_to_s16(drwav_int16* pOut, const drwav_uint8* pIn, size_t sampleCount)
{
int r;
- for (size_t i = 0; i < sampleCount; ++i) {
- int x = ((int)(((unsigned int)(((unsigned char*)pIn)[i*3+0]) << 8) | ((unsigned int)(((unsigned char*)pIn)[i*3+1]) << 16) | ((unsigned int)(((unsigned char*)pIn)[i*3+2])) << 24)) >> 8;
+ size_t i;
+ for (i = 0; i < sampleCount; ++i) {
+ int x = ((int)(((unsigned int)(((const unsigned char*)pIn)[i*3+0]) << 8) | ((unsigned int)(((const unsigned char*)pIn)[i*3+1]) << 16) | ((unsigned int)(((const unsigned char*)pIn)[i*3+2])) << 24)) >> 8;
r = x >> 8;
pOut[i] = (short)r;
}
@@ -2636,7 +3215,8 @@ void drwav_s24_to_s16(drwav_int16* pOut, const drwav_uint8* pIn, size_t sampleCo
void drwav_s32_to_s16(drwav_int16* pOut, const drwav_int32* pIn, size_t sampleCount)
{
int r;
- for (size_t i = 0; i < sampleCount; ++i) {
+ size_t i;
+ for (i = 0; i < sampleCount; ++i) {
int x = pIn[i];
r = x >> 16;
pOut[i] = (short)r;
@@ -2646,7 +3226,8 @@ void drwav_s32_to_s16(drwav_int16* pOut, const drwav_int32* pIn, size_t sampleCo
void drwav_f32_to_s16(drwav_int16* pOut, const float* pIn, size_t sampleCount)
{
int r;
- for (size_t i = 0; i < sampleCount; ++i) {
+ size_t i;
+ for (i = 0; i < sampleCount; ++i) {
float x = pIn[i];
float c;
c = ((x < -1) ? -1 : ((x > 1) ? 1 : x));
@@ -2660,7 +3241,8 @@ void drwav_f32_to_s16(drwav_int16* pOut, const float* pIn, size_t sampleCount)
void drwav_f64_to_s16(drwav_int16* pOut, const double* pIn, size_t sampleCount)
{
int r;
- for (size_t i = 0; i < sampleCount; ++i) {
+ size_t i;
+ for (i = 0; i < sampleCount; ++i) {
double x = pIn[i];
double c;
c = ((x < -1) ? -1 : ((x > 1) ? 1 : x));
@@ -2673,29 +3255,33 @@ void drwav_f64_to_s16(drwav_int16* pOut, const double* pIn, size_t sampleCount)
void drwav_alaw_to_s16(drwav_int16* pOut, const drwav_uint8* pIn, size_t sampleCount)
{
- for (size_t i = 0; i < sampleCount; ++i) {
+ size_t i;
+ for (i = 0; i < sampleCount; ++i) {
pOut[i] = drwav__alaw_to_s16(pIn[i]);
}
}
void drwav_mulaw_to_s16(drwav_int16* pOut, const drwav_uint8* pIn, size_t sampleCount)
{
- for (size_t i = 0; i < sampleCount; ++i) {
+ size_t i;
+ for (i = 0; i < sampleCount; ++i) {
pOut[i] = drwav__mulaw_to_s16(pIn[i]);
}
}
-static void drwav__pcm_to_f32(float* pOut, const unsigned char* pIn, size_t sampleCount, unsigned short bytesPerSample)
+static void drwav__pcm_to_f32(float* pOut, const unsigned char* pIn, size_t sampleCount, unsigned int bytesPerSample)
{
- // Special case for 8-bit sample data because it's treated as unsigned.
+ unsigned int i;
+
+ /* Special case for 8-bit sample data because it's treated as unsigned. */
if (bytesPerSample == 1) {
drwav_u8_to_f32(pOut, pIn, sampleCount);
return;
}
- // Slightly more optimal implementation for common formats.
+ /* Slightly more optimal implementation for common formats. */
if (bytesPerSample == 2) {
drwav_s16_to_f32(pOut, (const drwav_int16*)pIn, sampleCount);
return;
@@ -2710,15 +3296,15 @@ static void drwav__pcm_to_f32(float* pOut, const unsigned char* pIn, size_t samp
}
- // Anything more than 64 bits per sample is not supported.
+ /* Anything more than 64 bits per sample is not supported. */
if (bytesPerSample > 8) {
drwav_zero_memory(pOut, sampleCount * sizeof(*pOut));
return;
}
- // Generic, slow converter.
- for (unsigned int i = 0; i < sampleCount; ++i) {
+ /* Generic, slow converter. */
+ for (i = 0; i < sampleCount; ++i) {
drwav_uint64 sample = 0;
unsigned int shift = (8 - bytesPerSample) * 8;
@@ -2733,18 +3319,19 @@ static void drwav__pcm_to_f32(float* pOut, const unsigned char* pIn, size_t samp
}
}
-static void drwav__ieee_to_f32(float* pOut, const unsigned char* pIn, size_t sampleCount, unsigned short bytesPerSample)
+static void drwav__ieee_to_f32(float* pOut, const unsigned char* pIn, size_t sampleCount, unsigned int bytesPerSample)
{
if (bytesPerSample == 4) {
- for (unsigned int i = 0; i < sampleCount; ++i) {
- *pOut++ = ((float*)pIn)[i];
+ unsigned int i;
+ for (i = 0; i < sampleCount; ++i) {
+ *pOut++ = ((const float*)pIn)[i];
}
return;
} else if (bytesPerSample == 8) {
- drwav_f64_to_f32(pOut, (double*)pIn, sampleCount);
+ drwav_f64_to_f32(pOut, (const double*)pIn, sampleCount);
return;
} else {
- // Only supporting 32- and 64-bit float. Output silence in all other cases. Contributions welcome for 16-bit float.
+ /* Only supporting 32- and 64-bit float. Output silence in all other cases. Contributions welcome for 16-bit float. */
drwav_zero_memory(pOut, sampleCount * sizeof(*pOut));
return;
}
@@ -2753,19 +3340,23 @@ static void drwav__ieee_to_f32(float* pOut, const unsigned char* pIn, size_t sam
drwav_uint64 drwav_read_f32__pcm(drwav* pWav, drwav_uint64 samplesToRead, float* pBufferOut)
{
- if (pWav->bytesPerSample == 0) {
+ drwav_uint64 totalSamplesRead;
+ unsigned char sampleData[4096];
+
+ drwav_uint32 bytesPerSample = drwav_get_bytes_per_sample(pWav);
+ if (bytesPerSample == 0) {
return 0;
}
- drwav_uint64 totalSamplesRead = 0;
- unsigned char sampleData[4096];
+ totalSamplesRead = 0;
+
while (samplesToRead > 0) {
- drwav_uint64 samplesRead = drwav_read(pWav, drwav_min(samplesToRead, sizeof(sampleData)/pWav->bytesPerSample), sampleData);
+ drwav_uint64 samplesRead = drwav_read(pWav, drwav_min(samplesToRead, sizeof(sampleData)/bytesPerSample), sampleData);
if (samplesRead == 0) {
break;
}
- drwav__pcm_to_f32(pBufferOut, sampleData, (size_t)samplesRead, pWav->bytesPerSample);
+ drwav__pcm_to_f32(pBufferOut, sampleData, (size_t)samplesRead, bytesPerSample);
pBufferOut += samplesRead;
samplesToRead -= samplesRead;
@@ -2777,8 +3368,10 @@ drwav_uint64 drwav_read_f32__pcm(drwav* pWav, drwav_uint64 samplesToRead, float*
drwav_uint64 drwav_read_f32__msadpcm(drwav* pWav, drwav_uint64 samplesToRead, float* pBufferOut)
{
- // We're just going to borrow the implementation from the drwav_read_s16() since ADPCM is a little bit more complicated than other formats and I don't
- // want to duplicate that code.
+ /*
+ We're just going to borrow the implementation from the drwav_read_s16() since ADPCM is a little bit more complicated than other formats and I don't
+ want to duplicate that code.
+ */
drwav_uint64 totalSamplesRead = 0;
drwav_int16 samples16[2048];
while (samplesToRead > 0) {
@@ -2787,7 +3380,7 @@ drwav_uint64 drwav_read_f32__msadpcm(drwav* pWav, drwav_uint64 samplesToRead, fl
break;
}
- drwav_s16_to_f32(pBufferOut, samples16, (size_t)samplesRead); // <-- Safe cast because we're clamping to 2048.
+ drwav_s16_to_f32(pBufferOut, samples16, (size_t)samplesRead); /* <-- Safe cast because we're clamping to 2048. */
pBufferOut += samplesRead;
samplesToRead -= samplesRead;
@@ -2799,8 +3392,10 @@ drwav_uint64 drwav_read_f32__msadpcm(drwav* pWav, drwav_uint64 samplesToRead, fl
drwav_uint64 drwav_read_f32__ima(drwav* pWav, drwav_uint64 samplesToRead, float* pBufferOut)
{
- // We're just going to borrow the implementation from the drwav_read_s16() since IMA-ADPCM is a little bit more complicated than other formats and I don't
- // want to duplicate that code.
+ /*
+ We're just going to borrow the implementation from the drwav_read_s16() since IMA-ADPCM is a little bit more complicated than other formats and I don't
+ want to duplicate that code.
+ */
drwav_uint64 totalSamplesRead = 0;
drwav_int16 samples16[2048];
while (samplesToRead > 0) {
@@ -2809,7 +3404,7 @@ drwav_uint64 drwav_read_f32__ima(drwav* pWav, drwav_uint64 samplesToRead, float*
break;
}
- drwav_s16_to_f32(pBufferOut, samples16, (size_t)samplesRead); // <-- Safe cast because we're clamping to 2048.
+ drwav_s16_to_f32(pBufferOut, samples16, (size_t)samplesRead); /* <-- Safe cast because we're clamping to 2048. */
pBufferOut += samplesRead;
samplesToRead -= samplesRead;
@@ -2821,24 +3416,29 @@ drwav_uint64 drwav_read_f32__ima(drwav* pWav, drwav_uint64 samplesToRead, float*
drwav_uint64 drwav_read_f32__ieee(drwav* pWav, drwav_uint64 samplesToRead, float* pBufferOut)
{
- // Fast path.
- if (pWav->translatedFormatTag == DR_WAVE_FORMAT_IEEE_FLOAT && pWav->bytesPerSample == 4) {
+ drwav_uint64 totalSamplesRead;
+ unsigned char sampleData[4096];
+ drwav_uint32 bytesPerSample;
+
+ /* Fast path. */
+ if (pWav->translatedFormatTag == DR_WAVE_FORMAT_IEEE_FLOAT && pWav->bitsPerSample == 32) {
return drwav_read(pWav, samplesToRead, pBufferOut);
}
-
- if (pWav->bytesPerSample == 0) {
+
+ bytesPerSample = drwav_get_bytes_per_sample(pWav);
+ if (bytesPerSample == 0) {
return 0;
}
- drwav_uint64 totalSamplesRead = 0;
- unsigned char sampleData[4096];
+ totalSamplesRead = 0;
+
while (samplesToRead > 0) {
- drwav_uint64 samplesRead = drwav_read(pWav, drwav_min(samplesToRead, sizeof(sampleData)/pWav->bytesPerSample), sampleData);
+ drwav_uint64 samplesRead = drwav_read(pWav, drwav_min(samplesToRead, sizeof(sampleData)/bytesPerSample), sampleData);
if (samplesRead == 0) {
break;
}
- drwav__ieee_to_f32(pBufferOut, sampleData, (size_t)samplesRead, pWav->bytesPerSample);
+ drwav__ieee_to_f32(pBufferOut, sampleData, (size_t)samplesRead, bytesPerSample);
pBufferOut += samplesRead;
samplesToRead -= samplesRead;
@@ -2850,14 +3450,17 @@ drwav_uint64 drwav_read_f32__ieee(drwav* pWav, drwav_uint64 samplesToRead, float
drwav_uint64 drwav_read_f32__alaw(drwav* pWav, drwav_uint64 samplesToRead, float* pBufferOut)
{
- if (pWav->bytesPerSample == 0) {
+ drwav_uint64 totalSamplesRead;
+ unsigned char sampleData[4096];
+ drwav_uint32 bytesPerSample = drwav_get_bytes_per_sample(pWav);
+ if (bytesPerSample == 0) {
return 0;
}
- drwav_uint64 totalSamplesRead = 0;
- unsigned char sampleData[4096];
+ totalSamplesRead = 0;
+
while (samplesToRead > 0) {
- drwav_uint64 samplesRead = drwav_read(pWav, drwav_min(samplesToRead, sizeof(sampleData)/pWav->bytesPerSample), sampleData);
+ drwav_uint64 samplesRead = drwav_read(pWav, drwav_min(samplesToRead, sizeof(sampleData)/bytesPerSample), sampleData);
if (samplesRead == 0) {
break;
}
@@ -2874,14 +3477,18 @@ drwav_uint64 drwav_read_f32__alaw(drwav* pWav, drwav_uint64 samplesToRead, float
drwav_uint64 drwav_read_f32__mulaw(drwav* pWav, drwav_uint64 samplesToRead, float* pBufferOut)
{
- if (pWav->bytesPerSample == 0) {
+ drwav_uint64 totalSamplesRead;
+ unsigned char sampleData[4096];
+
+ drwav_uint32 bytesPerSample = drwav_get_bytes_per_sample(pWav);
+ if (bytesPerSample == 0) {
return 0;
}
- drwav_uint64 totalSamplesRead = 0;
- unsigned char sampleData[4096];
+ totalSamplesRead = 0;
+
while (samplesToRead > 0) {
- drwav_uint64 samplesRead = drwav_read(pWav, drwav_min(samplesToRead, sizeof(sampleData)/pWav->bytesPerSample), sampleData);
+ drwav_uint64 samplesRead = drwav_read(pWav, drwav_min(samplesToRead, sizeof(sampleData)/bytesPerSample), sampleData);
if (samplesRead == 0) {
break;
}
@@ -2902,9 +3509,9 @@ drwav_uint64 drwav_read_f32(drwav* pWav, drwav_uint64 samplesToRead, float* pBuf
return 0;
}
- // Don't try to read more samples than can potentially fit in the output buffer.
- if (samplesToRead * sizeof(float) > SIZE_MAX) {
- samplesToRead = SIZE_MAX / sizeof(float);
+ /* Don't try to read more samples than can potentially fit in the output buffer. */
+ if (samplesToRead * sizeof(float) > DRWAV_SIZE_MAX) {
+ samplesToRead = DRWAV_SIZE_MAX / sizeof(float);
}
if (pWav->translatedFormatTag == DR_WAVE_FORMAT_PCM) {
@@ -2934,22 +3541,31 @@ drwav_uint64 drwav_read_f32(drwav* pWav, drwav_uint64 samplesToRead, float* pBuf
return 0;
}
+drwav_uint64 drwav_read_pcm_frames_f32(drwav* pWav, drwav_uint64 framesToRead, float* pBufferOut)
+{
+ return drwav_read_f32(pWav, framesToRead * pWav->channels, pBufferOut) / pWav->channels;
+}
+
void drwav_u8_to_f32(float* pOut, const drwav_uint8* pIn, size_t sampleCount)
{
+ size_t i;
+
if (pOut == NULL || pIn == NULL) {
return;
}
#ifdef DR_WAV_LIBSNDFILE_COMPAT
- // It appears libsndfile uses slightly different logic for the u8 -> f32 conversion to dr_wav, which in my opinion is incorrect. It appears
- // libsndfile performs the conversion something like "f32 = (u8 / 256) * 2 - 1", however I think it should be "f32 = (u8 / 255) * 2 - 1" (note
- // the divisor of 256 vs 255). I use libsndfile as a benchmark for testing, so I'm therefore leaving this block here just for my automated
- // correctness testing. This is disabled by default.
- for (size_t i = 0; i < sampleCount; ++i) {
+ /*
+ It appears libsndfile uses slightly different logic for the u8 -> f32 conversion to dr_wav, which in my opinion is incorrect. It appears
+ libsndfile performs the conversion something like "f32 = (u8 / 256) * 2 - 1", however I think it should be "f32 = (u8 / 255) * 2 - 1" (note
+ the divisor of 256 vs 255). I use libsndfile as a benchmark for testing, so I'm therefore leaving this block here just for my automated
+ correctness testing. This is disabled by default.
+ */
+ for (i = 0; i < sampleCount; ++i) {
*pOut++ = (pIn[i] / 256.0f) * 2 - 1;
}
#else
- for (size_t i = 0; i < sampleCount; ++i) {
+ for (i = 0; i < sampleCount; ++i) {
*pOut++ = (pIn[i] / 255.0f) * 2 - 1;
}
#endif
@@ -2957,22 +3573,26 @@ void drwav_u8_to_f32(float* pOut, const drwav_uint8* pIn, size_t sampleCount)
void drwav_s16_to_f32(float* pOut, const drwav_int16* pIn, size_t sampleCount)
{
+ size_t i;
+
if (pOut == NULL || pIn == NULL) {
return;
}
- for (size_t i = 0; i < sampleCount; ++i) {
+ for (i = 0; i < sampleCount; ++i) {
*pOut++ = pIn[i] / 32768.0f;
}
}
void drwav_s24_to_f32(float* pOut, const drwav_uint8* pIn, size_t sampleCount)
{
+ size_t i;
+
if (pOut == NULL || pIn == NULL) {
return;
}
- for (size_t i = 0; i < sampleCount; ++i) {
+ for (i = 0; i < sampleCount; ++i) {
unsigned int s0 = pIn[i*3 + 0];
unsigned int s1 = pIn[i*3 + 1];
unsigned int s2 = pIn[i*3 + 2];
@@ -2984,59 +3604,68 @@ void drwav_s24_to_f32(float* pOut, const drwav_uint8* pIn, size_t sampleCount)
void drwav_s32_to_f32(float* pOut, const drwav_int32* pIn, size_t sampleCount)
{
+ size_t i;
if (pOut == NULL || pIn == NULL) {
return;
}
- for (size_t i = 0; i < sampleCount; ++i) {
+ for (i = 0; i < sampleCount; ++i) {
*pOut++ = (float)(pIn[i] / 2147483648.0);
}
}
void drwav_f64_to_f32(float* pOut, const double* pIn, size_t sampleCount)
{
+ size_t i;
+
if (pOut == NULL || pIn == NULL) {
return;
}
- for (size_t i = 0; i < sampleCount; ++i) {
+ for (i = 0; i < sampleCount; ++i) {
*pOut++ = (float)pIn[i];
}
}
void drwav_alaw_to_f32(float* pOut, const drwav_uint8* pIn, size_t sampleCount)
{
+ size_t i;
+
if (pOut == NULL || pIn == NULL) {
return;
}
- for (size_t i = 0; i < sampleCount; ++i) {
+ for (i = 0; i < sampleCount; ++i) {
*pOut++ = drwav__alaw_to_s16(pIn[i]) / 32768.0f;
}
}
void drwav_mulaw_to_f32(float* pOut, const drwav_uint8* pIn, size_t sampleCount)
{
+ size_t i;
+
if (pOut == NULL || pIn == NULL) {
return;
}
- for (size_t i = 0; i < sampleCount; ++i) {
+ for (i = 0; i < sampleCount; ++i) {
*pOut++ = drwav__mulaw_to_s16(pIn[i]) / 32768.0f;
}
}
-static void drwav__pcm_to_s32(drwav_int32* pOut, const unsigned char* pIn, size_t totalSampleCount, unsigned short bytesPerSample)
+static void drwav__pcm_to_s32(drwav_int32* pOut, const unsigned char* pIn, size_t totalSampleCount, unsigned int bytesPerSample)
{
- // Special case for 8-bit sample data because it's treated as unsigned.
+ unsigned int i;
+
+ /* Special case for 8-bit sample data because it's treated as unsigned. */
if (bytesPerSample == 1) {
drwav_u8_to_s32(pOut, pIn, totalSampleCount);
return;
}
- // Slightly more optimal implementation for common formats.
+ /* Slightly more optimal implementation for common formats. */
if (bytesPerSample == 2) {
drwav_s16_to_s32(pOut, (const drwav_int16*)pIn, totalSampleCount);
return;
@@ -3046,22 +3675,22 @@ static void drwav__pcm_to_s32(drwav_int32* pOut, const unsigned char* pIn, size_
return;
}
if (bytesPerSample == 4) {
- for (unsigned int i = 0; i < totalSampleCount; ++i) {
- *pOut++ = ((drwav_int32*)pIn)[i];
+ for (i = 0; i < totalSampleCount; ++i) {
+ *pOut++ = ((const drwav_int32*)pIn)[i];
}
return;
}
- // Anything more than 64 bits per sample is not supported.
+ /* Anything more than 64 bits per sample is not supported. */
if (bytesPerSample > 8) {
drwav_zero_memory(pOut, totalSampleCount * sizeof(*pOut));
return;
}
- // Generic, slow converter.
- for (unsigned int i = 0; i < totalSampleCount; ++i) {
+ /* Generic, slow converter. */
+ for (i = 0; i < totalSampleCount; ++i) {
drwav_uint64 sample = 0;
unsigned int shift = (8 - bytesPerSample) * 8;
@@ -3076,16 +3705,16 @@ static void drwav__pcm_to_s32(drwav_int32* pOut, const unsigned char* pIn, size_
}
}
-static void drwav__ieee_to_s32(drwav_int32* pOut, const unsigned char* pIn, size_t totalSampleCount, unsigned short bytesPerSample)
+static void drwav__ieee_to_s32(drwav_int32* pOut, const unsigned char* pIn, size_t totalSampleCount, unsigned int bytesPerSample)
{
if (bytesPerSample == 4) {
- drwav_f32_to_s32(pOut, (float*)pIn, totalSampleCount);
+ drwav_f32_to_s32(pOut, (const float*)pIn, totalSampleCount);
return;
} else if (bytesPerSample == 8) {
- drwav_f64_to_s32(pOut, (double*)pIn, totalSampleCount);
+ drwav_f64_to_s32(pOut, (const double*)pIn, totalSampleCount);
return;
} else {
- // Only supporting 32- and 64-bit float. Output silence in all other cases. Contributions welcome for 16-bit float.
+ /* Only supporting 32- and 64-bit float. Output silence in all other cases. Contributions welcome for 16-bit float. */
drwav_zero_memory(pOut, totalSampleCount * sizeof(*pOut));
return;
}
@@ -3094,24 +3723,29 @@ static void drwav__ieee_to_s32(drwav_int32* pOut, const unsigned char* pIn, size
drwav_uint64 drwav_read_s32__pcm(drwav* pWav, drwav_uint64 samplesToRead, drwav_int32* pBufferOut)
{
- // Fast path.
- if (pWav->translatedFormatTag == DR_WAVE_FORMAT_PCM && pWav->bytesPerSample == 4) {
+ drwav_uint64 totalSamplesRead;
+ unsigned char sampleData[4096];
+ drwav_uint32 bytesPerSample;
+
+ /* Fast path. */
+ if (pWav->translatedFormatTag == DR_WAVE_FORMAT_PCM && pWav->bitsPerSample == 32) {
return drwav_read(pWav, samplesToRead, pBufferOut);
}
-
- if (pWav->bytesPerSample == 0) {
+
+ bytesPerSample = drwav_get_bytes_per_sample(pWav);
+ if (bytesPerSample == 0) {
return 0;
}
- drwav_uint64 totalSamplesRead = 0;
- unsigned char sampleData[4096];
+ totalSamplesRead = 0;
+
while (samplesToRead > 0) {
- drwav_uint64 samplesRead = drwav_read(pWav, drwav_min(samplesToRead, sizeof(sampleData)/pWav->bytesPerSample), sampleData);
+ drwav_uint64 samplesRead = drwav_read(pWav, drwav_min(samplesToRead, sizeof(sampleData)/bytesPerSample), sampleData);
if (samplesRead == 0) {
break;
}
- drwav__pcm_to_s32(pBufferOut, sampleData, (size_t)samplesRead, pWav->bytesPerSample);
+ drwav__pcm_to_s32(pBufferOut, sampleData, (size_t)samplesRead, bytesPerSample);
pBufferOut += samplesRead;
samplesToRead -= samplesRead;
@@ -3123,8 +3757,10 @@ drwav_uint64 drwav_read_s32__pcm(drwav* pWav, drwav_uint64 samplesToRead, drwav_
drwav_uint64 drwav_read_s32__msadpcm(drwav* pWav, drwav_uint64 samplesToRead, drwav_int32* pBufferOut)
{
- // We're just going to borrow the implementation from the drwav_read_s16() since ADPCM is a little bit more complicated than other formats and I don't
- // want to duplicate that code.
+ /*
+ We're just going to borrow the implementation from the drwav_read_s16() since ADPCM is a little bit more complicated than other formats and I don't
+ want to duplicate that code.
+ */
drwav_uint64 totalSamplesRead = 0;
drwav_int16 samples16[2048];
while (samplesToRead > 0) {
@@ -3133,7 +3769,7 @@ drwav_uint64 drwav_read_s32__msadpcm(drwav* pWav, drwav_uint64 samplesToRead, dr
break;
}
- drwav_s16_to_s32(pBufferOut, samples16, (size_t)samplesRead); // <-- Safe cast because we're clamping to 2048.
+ drwav_s16_to_s32(pBufferOut, samples16, (size_t)samplesRead); /* <-- Safe cast because we're clamping to 2048. */
pBufferOut += samplesRead;
samplesToRead -= samplesRead;
@@ -3145,8 +3781,10 @@ drwav_uint64 drwav_read_s32__msadpcm(drwav* pWav, drwav_uint64 samplesToRead, dr
drwav_uint64 drwav_read_s32__ima(drwav* pWav, drwav_uint64 samplesToRead, drwav_int32* pBufferOut)
{
- // We're just going to borrow the implementation from the drwav_read_s16() since IMA-ADPCM is a little bit more complicated than other formats and I don't
- // want to duplicate that code.
+ /*
+ We're just going to borrow the implementation from the drwav_read_s16() since IMA-ADPCM is a little bit more complicated than other formats and I don't
+ want to duplicate that code.
+ */
drwav_uint64 totalSamplesRead = 0;
drwav_int16 samples16[2048];
while (samplesToRead > 0) {
@@ -3155,7 +3793,7 @@ drwav_uint64 drwav_read_s32__ima(drwav* pWav, drwav_uint64 samplesToRead, drwav_
break;
}
- drwav_s16_to_s32(pBufferOut, samples16, (size_t)samplesRead); // <-- Safe cast because we're clamping to 2048.
+ drwav_s16_to_s32(pBufferOut, samples16, (size_t)samplesRead); /* <-- Safe cast because we're clamping to 2048. */
pBufferOut += samplesRead;
samplesToRead -= samplesRead;
@@ -3167,19 +3805,23 @@ drwav_uint64 drwav_read_s32__ima(drwav* pWav, drwav_uint64 samplesToRead, drwav_
drwav_uint64 drwav_read_s32__ieee(drwav* pWav, drwav_uint64 samplesToRead, drwav_int32* pBufferOut)
{
- if (pWav->bytesPerSample == 0) {
+ drwav_uint64 totalSamplesRead;
+ unsigned char sampleData[4096];
+
+ drwav_uint32 bytesPerSample = drwav_get_bytes_per_sample(pWav);
+ if (bytesPerSample == 0) {
return 0;
}
- drwav_uint64 totalSamplesRead = 0;
- unsigned char sampleData[4096];
+ totalSamplesRead = 0;
+
while (samplesToRead > 0) {
- drwav_uint64 samplesRead = drwav_read(pWav, drwav_min(samplesToRead, sizeof(sampleData)/pWav->bytesPerSample), sampleData);
+ drwav_uint64 samplesRead = drwav_read(pWav, drwav_min(samplesToRead, sizeof(sampleData)/bytesPerSample), sampleData);
if (samplesRead == 0) {
break;
}
- drwav__ieee_to_s32(pBufferOut, sampleData, (size_t)samplesRead, pWav->bytesPerSample);
+ drwav__ieee_to_s32(pBufferOut, sampleData, (size_t)samplesRead, bytesPerSample);
pBufferOut += samplesRead;
samplesToRead -= samplesRead;
@@ -3191,14 +3833,18 @@ drwav_uint64 drwav_read_s32__ieee(drwav* pWav, drwav_uint64 samplesToRead, drwav
drwav_uint64 drwav_read_s32__alaw(drwav* pWav, drwav_uint64 samplesToRead, drwav_int32* pBufferOut)
{
- if (pWav->bytesPerSample == 0) {
+ drwav_uint64 totalSamplesRead;
+ unsigned char sampleData[4096];
+
+ drwav_uint32 bytesPerSample = drwav_get_bytes_per_sample(pWav);
+ if (bytesPerSample == 0) {
return 0;
}
- drwav_uint64 totalSamplesRead = 0;
- unsigned char sampleData[4096];
+ totalSamplesRead = 0;
+
while (samplesToRead > 0) {
- drwav_uint64 samplesRead = drwav_read(pWav, drwav_min(samplesToRead, sizeof(sampleData)/pWav->bytesPerSample), sampleData);
+ drwav_uint64 samplesRead = drwav_read(pWav, drwav_min(samplesToRead, sizeof(sampleData)/bytesPerSample), sampleData);
if (samplesRead == 0) {
break;
}
@@ -3215,14 +3861,18 @@ drwav_uint64 drwav_read_s32__alaw(drwav* pWav, drwav_uint64 samplesToRead, drwav
drwav_uint64 drwav_read_s32__mulaw(drwav* pWav, drwav_uint64 samplesToRead, drwav_int32* pBufferOut)
{
- if (pWav->bytesPerSample == 0) {
+ drwav_uint64 totalSamplesRead;
+ unsigned char sampleData[4096];
+
+ drwav_uint32 bytesPerSample = drwav_get_bytes_per_sample(pWav);
+ if (bytesPerSample == 0) {
return 0;
}
- drwav_uint64 totalSamplesRead = 0;
- unsigned char sampleData[4096];
+ totalSamplesRead = 0;
+
while (samplesToRead > 0) {
- drwav_uint64 samplesRead = drwav_read(pWav, drwav_min(samplesToRead, sizeof(sampleData)/pWav->bytesPerSample), sampleData);
+ drwav_uint64 samplesRead = drwav_read(pWav, drwav_min(samplesToRead, sizeof(sampleData)/bytesPerSample), sampleData);
if (samplesRead == 0) {
break;
}
@@ -3243,9 +3893,9 @@ drwav_uint64 drwav_read_s32(drwav* pWav, drwav_uint64 samplesToRead, drwav_int32
return 0;
}
- // Don't try to read more samples than can potentially fit in the output buffer.
- if (samplesToRead * sizeof(drwav_int32) > SIZE_MAX) {
- samplesToRead = SIZE_MAX / sizeof(drwav_int32);
+ /* Don't try to read more samples than can potentially fit in the output buffer. */
+ if (samplesToRead * sizeof(drwav_int32) > DRWAV_SIZE_MAX) {
+ samplesToRead = DRWAV_SIZE_MAX / sizeof(drwav_int32);
}
@@ -3276,35 +3926,46 @@ drwav_uint64 drwav_read_s32(drwav* pWav, drwav_uint64 samplesToRead, drwav_int32
return 0;
}
+drwav_uint64 drwav_read_pcm_frames_s32(drwav* pWav, drwav_uint64 framesToRead, drwav_int32* pBufferOut)
+{
+ return drwav_read_s32(pWav, framesToRead * pWav->channels, pBufferOut) / pWav->channels;
+}
+
void drwav_u8_to_s32(drwav_int32* pOut, const drwav_uint8* pIn, size_t sampleCount)
{
+ size_t i;
+
if (pOut == NULL || pIn == NULL) {
return;
}
- for (size_t i = 0; i < sampleCount; ++i) {
+ for (i = 0; i < sampleCount; ++i) {
*pOut++ = ((int)pIn[i] - 128) << 24;
}
}
void drwav_s16_to_s32(drwav_int32* pOut, const drwav_int16* pIn, size_t sampleCount)
{
+ size_t i;
+
if (pOut == NULL || pIn == NULL) {
return;
}
- for (size_t i = 0; i < sampleCount; ++i) {
+ for (i = 0; i < sampleCount; ++i) {
*pOut++ = pIn[i] << 16;
}
}
void drwav_s24_to_s32(drwav_int32* pOut, const drwav_uint8* pIn, size_t sampleCount)
{
+ size_t i;
+
if (pOut == NULL || pIn == NULL) {
return;
}
- for (size_t i = 0; i < sampleCount; ++i) {
+ for (i = 0; i < sampleCount; ++i) {
unsigned int s0 = pIn[i*3 + 0];
unsigned int s1 = pIn[i*3 + 1];
unsigned int s2 = pIn[i*3 + 2];
@@ -3316,44 +3977,52 @@ void drwav_s24_to_s32(drwav_int32* pOut, const drwav_uint8* pIn, size_t sampleCo
void drwav_f32_to_s32(drwav_int32* pOut, const float* pIn, size_t sampleCount)
{
+ size_t i;
+
if (pOut == NULL || pIn == NULL) {
return;
}
- for (size_t i = 0; i < sampleCount; ++i) {
+ for (i = 0; i < sampleCount; ++i) {
*pOut++ = (drwav_int32)(2147483648.0 * pIn[i]);
}
}
void drwav_f64_to_s32(drwav_int32* pOut, const double* pIn, size_t sampleCount)
{
+ size_t i;
+
if (pOut == NULL || pIn == NULL) {
return;
}
- for (size_t i = 0; i < sampleCount; ++i) {
+ for (i = 0; i < sampleCount; ++i) {
*pOut++ = (drwav_int32)(2147483648.0 * pIn[i]);
}
}
void drwav_alaw_to_s32(drwav_int32* pOut, const drwav_uint8* pIn, size_t sampleCount)
{
+ size_t i;
+
if (pOut == NULL || pIn == NULL) {
return;
}
- for (size_t i = 0; i < sampleCount; ++i) {
+ for (i = 0; i < sampleCount; ++i) {
*pOut++ = ((drwav_int32)drwav__alaw_to_s16(pIn[i])) << 16;
}
}
void drwav_mulaw_to_s32(drwav_int32* pOut, const drwav_uint8* pIn, size_t sampleCount)
{
+ size_t i;
+
if (pOut == NULL || pIn == NULL) {
return;
}
- for (size_t i= 0; i < sampleCount; ++i) {
+ for (i= 0; i < sampleCount; ++i) {
*pOut++ = ((drwav_int32)drwav__mulaw_to_s16(pIn[i])) << 16;
}
}
@@ -3362,105 +4031,145 @@ void drwav_mulaw_to_s32(drwav_int32* pOut, const drwav_uint8* pIn, size_t sample
drwav_int16* drwav__read_and_close_s16(drwav* pWav, unsigned int* channels, unsigned int* sampleRate, drwav_uint64* totalSampleCount)
{
+ drwav_uint64 sampleDataSize;
+ drwav_int16* pSampleData;
+ drwav_uint64 samplesRead;
+
drwav_assert(pWav != NULL);
- drwav_uint64 sampleDataSize = pWav->totalSampleCount * sizeof(drwav_int16);
- if (sampleDataSize > SIZE_MAX) {
+ sampleDataSize = pWav->totalSampleCount * sizeof(drwav_int16);
+ if (sampleDataSize > DRWAV_SIZE_MAX) {
drwav_uninit(pWav);
- return NULL; // File's too big.
+ return NULL; /* File's too big. */
}
- drwav_int16* pSampleData = (drwav_int16*)DRWAV_MALLOC((size_t)sampleDataSize); // <-- Safe cast due to the check above.
+ pSampleData = (drwav_int16*)DRWAV_MALLOC((size_t)sampleDataSize); /* <-- Safe cast due to the check above. */
if (pSampleData == NULL) {
drwav_uninit(pWav);
- return NULL; // Failed to allocate memory.
+ return NULL; /* Failed to allocate memory. */
}
- drwav_uint64 samplesRead = drwav_read_s16(pWav, (size_t)pWav->totalSampleCount, pSampleData);
+ samplesRead = drwav_read_s16(pWav, (size_t)pWav->totalSampleCount, pSampleData);
if (samplesRead != pWav->totalSampleCount) {
DRWAV_FREE(pSampleData);
drwav_uninit(pWav);
- return NULL; // There was an error reading the samples.
+ return NULL; /* There was an error reading the samples. */
}
drwav_uninit(pWav);
- if (sampleRate) *sampleRate = pWav->sampleRate;
- if (channels) *channels = pWav->channels;
- if (totalSampleCount) *totalSampleCount = pWav->totalSampleCount;
+ if (sampleRate) {
+ *sampleRate = pWav->sampleRate;
+ }
+ if (channels) {
+ *channels = pWav->channels;
+ }
+ if (totalSampleCount) {
+ *totalSampleCount = pWav->totalSampleCount;
+ }
+
return pSampleData;
}
float* drwav__read_and_close_f32(drwav* pWav, unsigned int* channels, unsigned int* sampleRate, drwav_uint64* totalSampleCount)
{
+ drwav_uint64 sampleDataSize;
+ float* pSampleData;
+ drwav_uint64 samplesRead;
+
drwav_assert(pWav != NULL);
- drwav_uint64 sampleDataSize = pWav->totalSampleCount * sizeof(float);
- if (sampleDataSize > SIZE_MAX) {
+ sampleDataSize = pWav->totalSampleCount * sizeof(float);
+ if (sampleDataSize > DRWAV_SIZE_MAX) {
drwav_uninit(pWav);
- return NULL; // File's too big.
+ return NULL; /* File's too big. */
}
- float* pSampleData = (float*)DRWAV_MALLOC((size_t)sampleDataSize); // <-- Safe cast due to the check above.
+ pSampleData = (float*)DRWAV_MALLOC((size_t)sampleDataSize); /* <-- Safe cast due to the check above. */
if (pSampleData == NULL) {
drwav_uninit(pWav);
- return NULL; // Failed to allocate memory.
+ return NULL; /* Failed to allocate memory. */
}
- drwav_uint64 samplesRead = drwav_read_f32(pWav, (size_t)pWav->totalSampleCount, pSampleData);
+ samplesRead = drwav_read_f32(pWav, (size_t)pWav->totalSampleCount, pSampleData);
if (samplesRead != pWav->totalSampleCount) {
DRWAV_FREE(pSampleData);
drwav_uninit(pWav);
- return NULL; // There was an error reading the samples.
+ return NULL; /* There was an error reading the samples. */
}
drwav_uninit(pWav);
- if (sampleRate) *sampleRate = pWav->sampleRate;
- if (channels) *channels = pWav->channels;
- if (totalSampleCount) *totalSampleCount = pWav->totalSampleCount;
+ if (sampleRate) {
+ *sampleRate = pWav->sampleRate;
+ }
+ if (channels) {
+ *channels = pWav->channels;
+ }
+ if (totalSampleCount) {
+ *totalSampleCount = pWav->totalSampleCount;
+ }
+
return pSampleData;
}
drwav_int32* drwav__read_and_close_s32(drwav* pWav, unsigned int* channels, unsigned int* sampleRate, drwav_uint64* totalSampleCount)
{
+ drwav_uint64 sampleDataSize;
+ drwav_int32* pSampleData;
+ drwav_uint64 samplesRead;
+
drwav_assert(pWav != NULL);
- drwav_uint64 sampleDataSize = pWav->totalSampleCount * sizeof(drwav_int32);
- if (sampleDataSize > SIZE_MAX) {
+ sampleDataSize = pWav->totalSampleCount * sizeof(drwav_int32);
+ if (sampleDataSize > DRWAV_SIZE_MAX) {
drwav_uninit(pWav);
- return NULL; // File's too big.
+ return NULL; /* File's too big. */
}
- drwav_int32* pSampleData = (drwav_int32*)DRWAV_MALLOC((size_t)sampleDataSize); // <-- Safe cast due to the check above.
+ pSampleData = (drwav_int32*)DRWAV_MALLOC((size_t)sampleDataSize); /* <-- Safe cast due to the check above. */
if (pSampleData == NULL) {
drwav_uninit(pWav);
- return NULL; // Failed to allocate memory.
+ return NULL; /* Failed to allocate memory. */
}
- drwav_uint64 samplesRead = drwav_read_s32(pWav, (size_t)pWav->totalSampleCount, pSampleData);
+ samplesRead = drwav_read_s32(pWav, (size_t)pWav->totalSampleCount, pSampleData);
if (samplesRead != pWav->totalSampleCount) {
DRWAV_FREE(pSampleData);
drwav_uninit(pWav);
- return NULL; // There was an error reading the samples.
+ return NULL; /* There was an error reading the samples. */
}
drwav_uninit(pWav);
- if (sampleRate) *sampleRate = pWav->sampleRate;
- if (channels) *channels = pWav->channels;
- if (totalSampleCount) *totalSampleCount = pWav->totalSampleCount;
+ if (sampleRate) {
+ *sampleRate = pWav->sampleRate;
+ }
+ if (channels) {
+ *channels = pWav->channels;
+ }
+ if (totalSampleCount) {
+ *totalSampleCount = pWav->totalSampleCount;
+ }
+
return pSampleData;
}
drwav_int16* drwav_open_and_read_s16(drwav_read_proc onRead, drwav_seek_proc onSeek, void* pUserData, unsigned int* channels, unsigned int* sampleRate, drwav_uint64* totalSampleCount)
{
- if (sampleRate) *sampleRate = 0;
- if (channels) *channels = 0;
- if (totalSampleCount) *totalSampleCount = 0;
-
drwav wav;
+
+ if (channels) {
+ *channels = 0;
+ }
+ if (sampleRate) {
+ *sampleRate = 0;
+ }
+ if (totalSampleCount) {
+ *totalSampleCount = 0;
+ }
+
if (!drwav_init(&wav, onRead, onSeek, pUserData)) {
return NULL;
}
@@ -3468,13 +4177,55 @@ drwav_int16* drwav_open_and_read_s16(drwav_read_proc onRead, drwav_seek_proc onS
return drwav__read_and_close_s16(&wav, channels, sampleRate, totalSampleCount);
}
-float* drwav_open_and_read_f32(drwav_read_proc onRead, drwav_seek_proc onSeek, void* pUserData, unsigned int* channels, unsigned int* sampleRate, drwav_uint64* totalSampleCount)
+drwav_int16* drwav_open_and_read_pcm_frames_s16(drwav_read_proc onRead, drwav_seek_proc onSeek, void* pUserData, unsigned int* channelsOut, unsigned int* sampleRateOut, drwav_uint64* totalFrameCountOut)
{
- if (sampleRate) *sampleRate = 0;
- if (channels) *channels = 0;
- if (totalSampleCount) *totalSampleCount = 0;
+ unsigned int channels;
+ unsigned int sampleRate;
+ drwav_uint64 totalSampleCount;
+ drwav_int16* result;
+
+ if (channelsOut) {
+ *channelsOut = 0;
+ }
+ if (sampleRateOut) {
+ *sampleRateOut = 0;
+ }
+ if (totalFrameCountOut) {
+ *totalFrameCountOut = 0;
+ }
+ result = drwav_open_and_read_s16(onRead, onSeek, pUserData, &channels, &sampleRate, &totalSampleCount);
+ if (result == NULL) {
+ return NULL;
+ }
+
+ if (channelsOut) {
+ *channelsOut = channels;
+ }
+ if (sampleRateOut) {
+ *sampleRateOut = sampleRate;
+ }
+ if (totalFrameCountOut) {
+ *totalFrameCountOut = totalSampleCount / channels;
+ }
+
+ return result;
+}
+
+float* drwav_open_and_read_f32(drwav_read_proc onRead, drwav_seek_proc onSeek, void* pUserData, unsigned int* channels, unsigned int* sampleRate, drwav_uint64* totalSampleCount)
+{
drwav wav;
+
+ if (sampleRate) {
+ *sampleRate = 0;
+ }
+ if (channels) {
+ *channels = 0;
+ }
+ if (totalSampleCount) {
+ *totalSampleCount = 0;
+ }
+
if (!drwav_init(&wav, onRead, onSeek, pUserData)) {
return NULL;
}
@@ -3482,13 +4233,55 @@ float* drwav_open_and_read_f32(drwav_read_proc onRead, drwav_seek_proc onSeek, v
return drwav__read_and_close_f32(&wav, channels, sampleRate, totalSampleCount);
}
-drwav_int32* drwav_open_and_read_s32(drwav_read_proc onRead, drwav_seek_proc onSeek, void* pUserData, unsigned int* channels, unsigned int* sampleRate, drwav_uint64* totalSampleCount)
+float* drwav_open_and_read_pcm_frames_f32(drwav_read_proc onRead, drwav_seek_proc onSeek, void* pUserData, unsigned int* channelsOut, unsigned int* sampleRateOut, drwav_uint64* totalFrameCountOut)
{
- if (sampleRate) *sampleRate = 0;
- if (channels) *channels = 0;
- if (totalSampleCount) *totalSampleCount = 0;
+ unsigned int channels;
+ unsigned int sampleRate;
+ drwav_uint64 totalSampleCount;
+ float* result;
+ if (channelsOut) {
+ *channelsOut = 0;
+ }
+ if (sampleRateOut) {
+ *sampleRateOut = 0;
+ }
+ if (totalFrameCountOut) {
+ *totalFrameCountOut = 0;
+ }
+
+ result = drwav_open_and_read_f32(onRead, onSeek, pUserData, &channels, &sampleRate, &totalSampleCount);
+ if (result == NULL) {
+ return NULL;
+ }
+
+ if (channelsOut) {
+ *channelsOut = channels;
+ }
+ if (sampleRateOut) {
+ *sampleRateOut = sampleRate;
+ }
+ if (totalFrameCountOut) {
+ *totalFrameCountOut = totalSampleCount / channels;
+ }
+
+ return result;
+}
+
+drwav_int32* drwav_open_and_read_s32(drwav_read_proc onRead, drwav_seek_proc onSeek, void* pUserData, unsigned int* channels, unsigned int* sampleRate, drwav_uint64* totalSampleCount)
+{
drwav wav;
+
+ if (sampleRate) {
+ *sampleRate = 0;
+ }
+ if (channels) {
+ *channels = 0;
+ }
+ if (totalSampleCount) {
+ *totalSampleCount = 0;
+ }
+
if (!drwav_init(&wav, onRead, onSeek, pUserData)) {
return NULL;
}
@@ -3496,14 +4289,56 @@ drwav_int32* drwav_open_and_read_s32(drwav_read_proc onRead, drwav_seek_proc onS
return drwav__read_and_close_s32(&wav, channels, sampleRate, totalSampleCount);
}
-#ifndef DR_WAV_NO_STDIO
-drwav_int16* drwav_open_and_read_file_s16(const char* filename, unsigned int* channels, unsigned int* sampleRate, drwav_uint64* totalSampleCount)
+drwav_int32* drwav_open_and_read_pcm_frames_s32(drwav_read_proc onRead, drwav_seek_proc onSeek, void* pUserData, unsigned int* channelsOut, unsigned int* sampleRateOut, drwav_uint64* totalFrameCountOut)
{
- if (sampleRate) *sampleRate = 0;
- if (channels) *channels = 0;
- if (totalSampleCount) *totalSampleCount = 0;
+ unsigned int channels;
+ unsigned int sampleRate;
+ drwav_uint64 totalSampleCount;
+ drwav_int32* result;
+
+ if (channelsOut) {
+ *channelsOut = 0;
+ }
+ if (sampleRateOut) {
+ *sampleRateOut = 0;
+ }
+ if (totalFrameCountOut) {
+ *totalFrameCountOut = 0;
+ }
+
+ result = drwav_open_and_read_s32(onRead, onSeek, pUserData, &channels, &sampleRate, &totalSampleCount);
+ if (result == NULL) {
+ return NULL;
+ }
+ if (channelsOut) {
+ *channelsOut = channels;
+ }
+ if (sampleRateOut) {
+ *sampleRateOut = sampleRate;
+ }
+ if (totalFrameCountOut) {
+ *totalFrameCountOut = totalSampleCount / channels;
+ }
+
+ return result;
+}
+
+#ifndef DR_WAV_NO_STDIO
+drwav_int16* drwav_open_file_and_read_s16(const char* filename, unsigned int* channels, unsigned int* sampleRate, drwav_uint64* totalSampleCount)
+{
drwav wav;
+
+ if (sampleRate) {
+ *sampleRate = 0;
+ }
+ if (channels) {
+ *channels = 0;
+ }
+ if (totalSampleCount) {
+ *totalSampleCount = 0;
+ }
+
if (!drwav_init_file(&wav, filename)) {
return NULL;
}
@@ -3511,13 +4346,55 @@ drwav_int16* drwav_open_and_read_file_s16(const char* filename, unsigned int* ch
return drwav__read_and_close_s16(&wav, channels, sampleRate, totalSampleCount);
}
-float* drwav_open_and_read_file_f32(const char* filename, unsigned int* channels, unsigned int* sampleRate, drwav_uint64* totalSampleCount)
+drwav_int16* drwav_open_file_and_read_pcm_frames_s16(const char* filename, unsigned int* channelsOut, unsigned int* sampleRateOut, drwav_uint64* totalFrameCountOut)
{
- if (sampleRate) *sampleRate = 0;
- if (channels) *channels = 0;
- if (totalSampleCount) *totalSampleCount = 0;
+ unsigned int channels;
+ unsigned int sampleRate;
+ drwav_uint64 totalSampleCount;
+ drwav_int16* result;
+
+ if (channelsOut) {
+ *channelsOut = 0;
+ }
+ if (sampleRateOut) {
+ *sampleRateOut = 0;
+ }
+ if (totalFrameCountOut) {
+ *totalFrameCountOut = 0;
+ }
+
+ result = drwav_open_file_and_read_s16(filename, &channels, &sampleRate, &totalSampleCount);
+ if (result == NULL) {
+ return NULL;
+ }
+ if (channelsOut) {
+ *channelsOut = channels;
+ }
+ if (sampleRateOut) {
+ *sampleRateOut = sampleRate;
+ }
+ if (totalFrameCountOut) {
+ *totalFrameCountOut = totalSampleCount / channels;
+ }
+
+ return result;
+}
+
+float* drwav_open_file_and_read_f32(const char* filename, unsigned int* channels, unsigned int* sampleRate, drwav_uint64* totalSampleCount)
+{
drwav wav;
+
+ if (sampleRate) {
+ *sampleRate = 0;
+ }
+ if (channels) {
+ *channels = 0;
+ }
+ if (totalSampleCount) {
+ *totalSampleCount = 0;
+ }
+
if (!drwav_init_file(&wav, filename)) {
return NULL;
}
@@ -3525,28 +4402,112 @@ float* drwav_open_and_read_file_f32(const char* filename, unsigned int* channels
return drwav__read_and_close_f32(&wav, channels, sampleRate, totalSampleCount);
}
-drwav_int32* drwav_open_and_read_file_s32(const char* filename, unsigned int* channels, unsigned int* sampleRate, drwav_uint64* totalSampleCount)
+float* drwav_open_file_and_read_pcm_frames_f32(const char* filename, unsigned int* channelsOut, unsigned int* sampleRateOut, drwav_uint64* totalFrameCountOut)
{
- if (sampleRate) *sampleRate = 0;
- if (channels) *channels = 0;
- if (totalSampleCount) *totalSampleCount = 0;
+ unsigned int channels;
+ unsigned int sampleRate;
+ drwav_uint64 totalSampleCount;
+ float* result;
+ if (channelsOut) {
+ *channelsOut = 0;
+ }
+ if (sampleRateOut) {
+ *sampleRateOut = 0;
+ }
+ if (totalFrameCountOut) {
+ *totalFrameCountOut = 0;
+ }
+
+ result = drwav_open_file_and_read_f32(filename, &channels, &sampleRate, &totalSampleCount);
+ if (result == NULL) {
+ return NULL;
+ }
+
+ if (channelsOut) {
+ *channelsOut = channels;
+ }
+ if (sampleRateOut) {
+ *sampleRateOut = sampleRate;
+ }
+ if (totalFrameCountOut) {
+ *totalFrameCountOut = totalSampleCount / channels;
+ }
+
+ return result;
+}
+
+drwav_int32* drwav_open_file_and_read_s32(const char* filename, unsigned int* channels, unsigned int* sampleRate, drwav_uint64* totalSampleCount)
+{
drwav wav;
+
+ if (sampleRate) {
+ *sampleRate = 0;
+ }
+ if (channels) {
+ *channels = 0;
+ }
+ if (totalSampleCount) {
+ *totalSampleCount = 0;
+ }
+
if (!drwav_init_file(&wav, filename)) {
return NULL;
}
return drwav__read_and_close_s32(&wav, channels, sampleRate, totalSampleCount);
}
-#endif
-drwav_int16* drwav_open_and_read_memory_s16(const void* data, size_t dataSize, unsigned int* channels, unsigned int* sampleRate, drwav_uint64* totalSampleCount)
+drwav_int32* drwav_open_file_and_read_pcm_frames_s32(const char* filename, unsigned int* channelsOut, unsigned int* sampleRateOut, drwav_uint64* totalFrameCountOut)
{
- if (sampleRate) *sampleRate = 0;
- if (channels) *channels = 0;
- if (totalSampleCount) *totalSampleCount = 0;
+ unsigned int channels;
+ unsigned int sampleRate;
+ drwav_uint64 totalSampleCount;
+ drwav_int32* result;
+
+ if (channelsOut) {
+ *channelsOut = 0;
+ }
+ if (sampleRateOut) {
+ *sampleRateOut = 0;
+ }
+ if (totalFrameCountOut) {
+ *totalFrameCountOut = 0;
+ }
+ result = drwav_open_file_and_read_s32(filename, &channels, &sampleRate, &totalSampleCount);
+ if (result == NULL) {
+ return NULL;
+ }
+
+ if (channelsOut) {
+ *channelsOut = channels;
+ }
+ if (sampleRateOut) {
+ *sampleRateOut = sampleRate;
+ }
+ if (totalFrameCountOut) {
+ *totalFrameCountOut = totalSampleCount / channels;
+ }
+
+ return result;
+}
+#endif
+
+drwav_int16* drwav_open_memory_and_read_s16(const void* data, size_t dataSize, unsigned int* channels, unsigned int* sampleRate, drwav_uint64* totalSampleCount)
+{
drwav wav;
+
+ if (sampleRate) {
+ *sampleRate = 0;
+ }
+ if (channels) {
+ *channels = 0;
+ }
+ if (totalSampleCount) {
+ *totalSampleCount = 0;
+ }
+
if (!drwav_init_memory(&wav, data, dataSize)) {
return NULL;
}
@@ -3554,13 +4515,55 @@ drwav_int16* drwav_open_and_read_memory_s16(const void* data, size_t dataSize, u
return drwav__read_and_close_s16(&wav, channels, sampleRate, totalSampleCount);
}
-float* drwav_open_and_read_memory_f32(const void* data, size_t dataSize, unsigned int* channels, unsigned int* sampleRate, drwav_uint64* totalSampleCount)
+drwav_int16* drwav_open_memory_and_read_pcm_frames_s16(const void* data, size_t dataSize, unsigned int* channelsOut, unsigned int* sampleRateOut, drwav_uint64* totalFrameCountOut)
{
- if (sampleRate) *sampleRate = 0;
- if (channels) *channels = 0;
- if (totalSampleCount) *totalSampleCount = 0;
+ unsigned int channels;
+ unsigned int sampleRate;
+ drwav_uint64 totalSampleCount;
+ drwav_int16* result;
+ if (channelsOut) {
+ *channelsOut = 0;
+ }
+ if (sampleRateOut) {
+ *sampleRateOut = 0;
+ }
+ if (totalFrameCountOut) {
+ *totalFrameCountOut = 0;
+ }
+
+ result = drwav_open_memory_and_read_s16(data, dataSize, &channels, &sampleRate, &totalSampleCount);
+ if (result == NULL) {
+ return NULL;
+ }
+
+ if (channelsOut) {
+ *channelsOut = channels;
+ }
+ if (sampleRateOut) {
+ *sampleRateOut = sampleRate;
+ }
+ if (totalFrameCountOut) {
+ *totalFrameCountOut = totalSampleCount / channels;
+ }
+
+ return result;
+}
+
+float* drwav_open_memory_and_read_f32(const void* data, size_t dataSize, unsigned int* channels, unsigned int* sampleRate, drwav_uint64* totalSampleCount)
+{
drwav wav;
+
+ if (sampleRate) {
+ *sampleRate = 0;
+ }
+ if (channels) {
+ *channels = 0;
+ }
+ if (totalSampleCount) {
+ *totalSampleCount = 0;
+ }
+
if (!drwav_init_memory(&wav, data, dataSize)) {
return NULL;
}
@@ -3568,20 +4571,97 @@ float* drwav_open_and_read_memory_f32(const void* data, size_t dataSize, unsigne
return drwav__read_and_close_f32(&wav, channels, sampleRate, totalSampleCount);
}
-drwav_int32* drwav_open_and_read_memory_s32(const void* data, size_t dataSize, unsigned int* channels, unsigned int* sampleRate, drwav_uint64* totalSampleCount)
+float* drwav_open_memory_and_read_pcm_frames_f32(const void* data, size_t dataSize, unsigned int* channelsOut, unsigned int* sampleRateOut, drwav_uint64* totalFrameCountOut)
{
- if (sampleRate) *sampleRate = 0;
- if (channels) *channels = 0;
- if (totalSampleCount) *totalSampleCount = 0;
+ unsigned int channels;
+ unsigned int sampleRate;
+ drwav_uint64 totalSampleCount;
+ float* result;
+ if (channelsOut) {
+ *channelsOut = 0;
+ }
+ if (sampleRateOut) {
+ *sampleRateOut = 0;
+ }
+ if (totalFrameCountOut) {
+ *totalFrameCountOut = 0;
+ }
+
+ result = drwav_open_memory_and_read_f32(data, dataSize, &channels, &sampleRate, &totalSampleCount);
+ if (result == NULL) {
+ return NULL;
+ }
+
+ if (channelsOut) {
+ *channelsOut = channels;
+ }
+ if (sampleRateOut) {
+ *sampleRateOut = sampleRate;
+ }
+ if (totalFrameCountOut) {
+ *totalFrameCountOut = totalSampleCount / channels;
+ }
+
+ return result;
+}
+
+drwav_int32* drwav_open_memory_and_read_s32(const void* data, size_t dataSize, unsigned int* channels, unsigned int* sampleRate, drwav_uint64* totalSampleCount)
+{
drwav wav;
+
+ if (sampleRate) {
+ *sampleRate = 0;
+ }
+ if (channels) {
+ *channels = 0;
+ }
+ if (totalSampleCount) {
+ *totalSampleCount = 0;
+ }
+
if (!drwav_init_memory(&wav, data, dataSize)) {
return NULL;
}
return drwav__read_and_close_s32(&wav, channels, sampleRate, totalSampleCount);
}
-#endif //DR_WAV_NO_CONVERSION_API
+
+drwav_int32* drwav_open_memory_and_read_pcm_frames_s32(const void* data, size_t dataSize, unsigned int* channelsOut, unsigned int* sampleRateOut, drwav_uint64* totalFrameCountOut)
+{
+ unsigned int channels;
+ unsigned int sampleRate;
+ drwav_uint64 totalSampleCount;
+ drwav_int32* result;
+
+ if (channelsOut) {
+ *channelsOut = 0;
+ }
+ if (sampleRateOut) {
+ *sampleRateOut = 0;
+ }
+ if (totalFrameCountOut) {
+ *totalFrameCountOut = 0;
+ }
+
+ result = drwav_open_memory_and_read_s32(data, dataSize, &channels, &sampleRate, &totalSampleCount);
+ if (result == NULL) {
+ return NULL;
+ }
+
+ if (channelsOut) {
+ *channelsOut = channels;
+ }
+ if (sampleRateOut) {
+ *sampleRateOut = sampleRate;
+ }
+ if (totalFrameCountOut) {
+ *totalFrameCountOut = totalSampleCount / channels;
+ }
+
+ return result;
+}
+#endif /* DR_WAV_NO_CONVERSION_API */
void drwav_free(void* pDataReturnedByOpenAndRead)
@@ -3589,136 +4669,203 @@ void drwav_free(void* pDataReturnedByOpenAndRead)
DRWAV_FREE(pDataReturnedByOpenAndRead);
}
-#endif //DR_WAV_IMPLEMENTATION
-
-
-// REVISION HISTORY
-//
-// v0.8.1 - 2018-06-29
-// - Add support for sequential writing APIs.
-// - Disable seeking in write mode.
-// - Fix bugs with Wave64.
-// - Fix typos.
-//
-// v0.8 - 2018-04-27
-// - Bug fix.
-// - Start using major.minor.revision versioning.
-//
-// v0.7f - 2018-02-05
-// - Restrict ADPCM formats to a maximum of 2 channels.
-//
-// v0.7e - 2018-02-02
-// - Fix a crash.
-//
-// v0.7d - 2018-02-01
-// - Fix a crash.
-//
-// v0.7c - 2018-02-01
-// - Set drwav.bytesPerSample to 0 for all compressed formats.
-// - Fix a crash when reading 16-bit floating point WAV files. In this case dr_wav will output silence for
-// all format conversion reading APIs (*_s16, *_s32, *_f32 APIs).
-// - Fix some divide-by-zero errors.
-//
-// v0.7b - 2018-01-22
-// - Fix errors with seeking of compressed formats.
-// - Fix compilation error when DR_WAV_NO_CONVERSION_API
-//
-// v0.7a - 2017-11-17
-// - Fix some GCC warnings.
-//
-// v0.7 - 2017-11-04
-// - Add writing APIs.
-//
-// v0.6 - 2017-08-16
-// - API CHANGE: Rename dr_* types to drwav_*.
-// - Add support for custom implementations of malloc(), realloc(), etc.
-// - Add support for Microsoft ADPCM.
-// - Add support for IMA ADPCM (DVI, format code 0x11).
-// - Optimizations to drwav_read_s16().
-// - Bug fixes.
-//
-// v0.5g - 2017-07-16
-// - Change underlying type for booleans to unsigned.
-//
-// v0.5f - 2017-04-04
-// - Fix a minor bug with drwav_open_and_read_s16() and family.
-//
-// v0.5e - 2016-12-29
-// - Added support for reading samples as signed 16-bit integers. Use the _s16() family of APIs for this.
-// - Minor fixes to documentation.
-//
-// v0.5d - 2016-12-28
-// - Use drwav_int*/drwav_uint* sized types to improve compiler support.
-//
-// v0.5c - 2016-11-11
-// - Properly handle JUNK chunks that come before the FMT chunk.
-//
-// v0.5b - 2016-10-23
-// - A minor change to drwav_bool8 and drwav_bool32 types.
-//
-// v0.5a - 2016-10-11
-// - Fixed a bug with drwav_open_and_read() and family due to incorrect argument ordering.
-// - Improve A-law and mu-law efficiency.
-//
-// v0.5 - 2016-09-29
-// - API CHANGE. Swap the order of "channels" and "sampleRate" parameters in drwav_open_and_read*(). Rationale for this is to
-// keep it consistent with dr_audio and dr_flac.
-//
-// v0.4b - 2016-09-18
-// - Fixed a typo in documentation.
-//
-// v0.4a - 2016-09-18
-// - Fixed a typo.
-// - Change date format to ISO 8601 (YYYY-MM-DD)
-//
-// v0.4 - 2016-07-13
-// - API CHANGE. Make onSeek consistent with dr_flac.
-// - API CHANGE. Rename drwav_seek() to drwav_seek_to_sample() for clarity and consistency with dr_flac.
-// - Added support for Sony Wave64.
-//
-// v0.3a - 2016-05-28
-// - API CHANGE. Return drwav_bool32 instead of int in onSeek callback.
-// - Fixed a memory leak.
-//
-// v0.3 - 2016-05-22
-// - Lots of API changes for consistency.
-//
-// v0.2a - 2016-05-16
-// - Fixed Linux/GCC build.
-//
-// v0.2 - 2016-05-11
-// - Added support for reading data as signed 32-bit PCM for consistency with dr_flac.
-//
-// v0.1a - 2016-05-07
-// - Fixed a bug in drwav_open_file() where the file handle would not be closed if the loader failed to initialize.
-//
-// v0.1 - 2016-05-04
-// - Initial versioned release.
+#endif /* DR_WAV_IMPLEMENTATION */
+
+
+/*
+REVISION HISTORY
+================
+v0.9.1 - 2019-05-05
+ - Add support for C89.
+ - Change license to choice of public domain or MIT-0.
+
+v0.9.0 - 2018-12-16
+ - API CHANGE: Add new reading APIs for reading by PCM frames instead of samples. Old APIs have been deprecated and
+ will be removed in v0.10.0. Deprecated APIs and their replacements:
+ drwav_read() -> drwav_read_pcm_frames()
+ drwav_read_s16() -> drwav_read_pcm_frames_s16()
+ drwav_read_f32() -> drwav_read_pcm_frames_f32()
+ drwav_read_s32() -> drwav_read_pcm_frames_s32()
+ drwav_seek_to_sample() -> drwav_seek_to_pcm_frame()
+ drwav_write() -> drwav_write_pcm_frames()
+ drwav_open_and_read_s16() -> drwav_open_and_read_pcm_frames_s16()
+ drwav_open_and_read_f32() -> drwav_open_and_read_pcm_frames_f32()
+ drwav_open_and_read_s32() -> drwav_open_and_read_pcm_frames_s32()
+ drwav_open_file_and_read_s16() -> drwav_open_file_and_read_pcm_frames_s16()
+ drwav_open_file_and_read_f32() -> drwav_open_file_and_read_pcm_frames_f32()
+ drwav_open_file_and_read_s32() -> drwav_open_file_and_read_pcm_frames_s32()
+ drwav_open_memory_and_read_s16() -> drwav_open_memory_and_read_pcm_frames_s16()
+ drwav_open_memory_and_read_f32() -> drwav_open_memory_and_read_pcm_frames_f32()
+ drwav_open_memory_and_read_s32() -> drwav_open_memory_and_read_pcm_frames_s32()
+ drwav::totalSampleCount -> drwav::totalPCMFrameCount
+ - API CHANGE: Rename drwav_open_and_read_file_*() to drwav_open_file_and_read_*().
+ - API CHANGE: Rename drwav_open_and_read_memory_*() to drwav_open_memory_and_read_*().
+ - Add built-in support for smpl chunks.
+ - Add support for firing a callback for each chunk in the file at initialization time.
+ - This is enabled through the drwav_init_ex(), etc. family of APIs.
+ - Handle invalid FMT chunks more robustly.
+
+v0.8.5 - 2018-09-11
+ - Const correctness.
+ - Fix a potential stack overflow.
+
+v0.8.4 - 2018-08-07
+ - Improve 64-bit detection.
+
+v0.8.3 - 2018-08-05
+ - Fix C++ build on older versions of GCC.
+
+v0.8.2 - 2018-08-02
+ - Fix some big-endian bugs.
+
+v0.8.1 - 2018-06-29
+ - Add support for sequential writing APIs.
+ - Disable seeking in write mode.
+ - Fix bugs with Wave64.
+ - Fix typos.
+
+v0.8 - 2018-04-27
+ - Bug fix.
+ - Start using major.minor.revision versioning.
+
+v0.7f - 2018-02-05
+ - Restrict ADPCM formats to a maximum of 2 channels.
+
+v0.7e - 2018-02-02
+ - Fix a crash.
+
+v0.7d - 2018-02-01
+ - Fix a crash.
+
+v0.7c - 2018-02-01
+ - Set drwav.bytesPerSample to 0 for all compressed formats.
+ - Fix a crash when reading 16-bit floating point WAV files. In this case dr_wav will output silence for
+ all format conversion reading APIs (*_s16, *_s32, *_f32 APIs).
+ - Fix some divide-by-zero errors.
+
+v0.7b - 2018-01-22
+ - Fix errors with seeking of compressed formats.
+ - Fix compilation error when DR_WAV_NO_CONVERSION_API
+
+v0.7a - 2017-11-17
+ - Fix some GCC warnings.
+
+v0.7 - 2017-11-04
+ - Add writing APIs.
+
+v0.6 - 2017-08-16
+ - API CHANGE: Rename dr_* types to drwav_*.
+ - Add support for custom implementations of malloc(), realloc(), etc.
+ - Add support for Microsoft ADPCM.
+ - Add support for IMA ADPCM (DVI, format code 0x11).
+ - Optimizations to drwav_read_s16().
+ - Bug fixes.
+
+v0.5g - 2017-07-16
+ - Change underlying type for booleans to unsigned.
+
+v0.5f - 2017-04-04
+ - Fix a minor bug with drwav_open_and_read_s16() and family.
+
+v0.5e - 2016-12-29
+ - Added support for reading samples as signed 16-bit integers. Use the _s16() family of APIs for this.
+ - Minor fixes to documentation.
+
+v0.5d - 2016-12-28
+ - Use drwav_int* and drwav_uint* sized types to improve compiler support.
+
+v0.5c - 2016-11-11
+ - Properly handle JUNK chunks that come before the FMT chunk.
+
+v0.5b - 2016-10-23
+ - A minor change to drwav_bool8 and drwav_bool32 types.
+
+v0.5a - 2016-10-11
+ - Fixed a bug with drwav_open_and_read() and family due to incorrect argument ordering.
+ - Improve A-law and mu-law efficiency.
+
+v0.5 - 2016-09-29
+ - API CHANGE. Swap the order of "channels" and "sampleRate" parameters in drwav_open_and_read*(). Rationale for this is to
+ keep it consistent with dr_audio and dr_flac.
+
+v0.4b - 2016-09-18
+ - Fixed a typo in documentation.
+
+v0.4a - 2016-09-18
+ - Fixed a typo.
+ - Change date format to ISO 8601 (YYYY-MM-DD)
+
+v0.4 - 2016-07-13
+ - API CHANGE. Make onSeek consistent with dr_flac.
+ - API CHANGE. Rename drwav_seek() to drwav_seek_to_sample() for clarity and consistency with dr_flac.
+ - Added support for Sony Wave64.
+
+v0.3a - 2016-05-28
+ - API CHANGE. Return drwav_bool32 instead of int in onSeek callback.
+ - Fixed a memory leak.
+
+v0.3 - 2016-05-22
+ - Lots of API changes for consistency.
+
+v0.2a - 2016-05-16
+ - Fixed Linux/GCC build.
+
+v0.2 - 2016-05-11
+ - Added support for reading data as signed 32-bit PCM for consistency with dr_flac.
+v0.1a - 2016-05-07
+ - Fixed a bug in drwav_open_file() where the file handle would not be closed if the loader failed to initialize.
+
+v0.1 - 2016-05-04
+ - Initial versioned release.
+*/
/*
+This software is available as a choice of the following licenses. Choose
+whichever you prefer.
+
+===============================================================================
+ALTERNATIVE 1 - Public Domain (www.unlicense.org)
+===============================================================================
This is free and unencumbered software released into the public domain.
-Anyone is free to copy, modify, publish, use, compile, sell, or
-distribute this software, either in source code form or as a compiled
-binary, for any purpose, commercial or non-commercial, and by any
-means.
-
-In jurisdictions that recognize copyright laws, the author or authors
-of this software dedicate any and all copyright interest in the
-software to the public domain. We make this dedication for the benefit
-of the public at large and to the detriment of our heirs and
-successors. We intend this dedication to be an overt act of
-relinquishment in perpetuity of all present and future rights to this
-software under copyright law.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
-EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
-MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
-IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
-OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
-ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
-OTHER DEALINGS IN THE SOFTWARE.
+Anyone is free to copy, modify, publish, use, compile, sell, or distribute this
+software, either in source code form or as a compiled binary, for any purpose,
+commercial or non-commercial, and by any means.
+
+In jurisdictions that recognize copyright laws, the author or authors of this
+software dedicate any and all copyright interest in the software to the public
+domain. We make this dedication for the benefit of the public at large and to
+the detriment of our heirs and successors. We intend this dedication to be an
+overt act of relinquishment in perpetuity of all present and future rights to
+this software under copyright law.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
For more information, please refer to <http://unlicense.org/>
+
+===============================================================================
+ALTERNATIVE 2 - MIT No Attribution
+===============================================================================
+Copyright 2018 David Reid
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of
+this software and associated documentation files (the "Software"), to deal in
+the Software without restriction, including without limitation the rights to
+use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+of the Software, and to permit persons to whom the Software is furnished to do
+so.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
*/