0/*
1WAV audio loader and writer. Choice of public domain or MIT-0. See license statements at the end of this file.
2dr_wav - v0.14.0 - TBD
4David Reid - mackron@gmail.com
6GitHub: https://github.com/mackron/dr_libs
7*/
9/*
10Introduction
11============
12This is a single file library. To use it, do something like the following in one .c file.
14 ```c
15 #define DR_WAV_IMPLEMENTATION
16 #include "dr_wav.h"
17 ```
19You 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:
21 ```c
22 drwav wav;
23 if (!drwav_init_file(&wav, "my_song.wav", NULL)) {
24 // Error opening WAV file.
25 }
27 drwav_int32* pDecodedInterleavedPCMFrames = malloc(wav.totalPCMFrameCount * wav.channels * sizeof(drwav_int32));
28 size_t numberOfSamplesActuallyDecoded = drwav_read_pcm_frames_s32(&wav, wav.totalPCMFrameCount, pDecodedInterleavedPCMFrames);
30 ...
32 drwav_uninit(&wav);
33 ```
35If you just want to quickly open and read the audio data in a single operation you can do something like this:
37 ```c
38 unsigned int channels;
39 unsigned int sampleRate;
40 drwav_uint64 totalPCMFrameCount;
41 float* pSampleData = drwav_open_file_and_read_pcm_frames_f32("my_song.wav", &channels, &sampleRate, &totalPCMFrameCount, NULL);
42 if (pSampleData == NULL) {
43 // Error opening and reading WAV file.
44 }
46 ...
48 drwav_free(pSampleData, NULL);
49 ```
51The 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
52audio data in its internal format (see notes below for supported formats):
54 ```c
55 size_t framesRead = drwav_read_pcm_frames(&wav, wav.totalPCMFrameCount, pDecodedInterleavedPCMFrames);
56 ```
58You 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:
60 ```c
61 size_t bytesRead = drwav_read_raw(&wav, bytesToRead, pRawDataBuffer);
62 ```
64dr_wav can also be used to output WAV files. This does not currently support compressed formats. To use this, look at `drwav_init_write()`,
65`drwav_init_file_write()`, etc. Use `drwav_write_pcm_frames()` to write samples, or `drwav_write_raw()` to write raw data in the "data" chunk.
67 ```c
68 drwav_data_format format;
69 format.container = drwav_container_riff; // <-- drwav_container_riff = normal WAV files, drwav_container_w64 = Sony Wave64.
70 format.format = DR_WAVE_FORMAT_PCM; // <-- Any of the DR_WAVE_FORMAT_* codes.
71 format.channels = 2;
72 format.sampleRate = 44100;
73 format.bitsPerSample = 16;
74 drwav_init_file_write(&wav, "data/recording.wav", &format, NULL);
76 ...
78 drwav_uint64 framesWritten = drwav_write_pcm_frames(pWav, frameCount, pSamples);
79 ```
81Note that writing to AIFF or RIFX is not supported.
83dr_wav has support for decoding from a number of different encapsulation formats. See below for details.
86Build Options
87=============
88#define these options before including this file.
90#define DR_WAV_NO_CONVERSION_API
91 Disables conversion APIs such as `drwav_read_pcm_frames_f32()` and `drwav_s16_to_f32()`.
93#define DR_WAV_NO_STDIO
94 Disables APIs that initialize a decoder from a file such as `drwav_init_file()`, `drwav_init_file_write()`, etc.
96#define DR_WAV_NO_WCHAR
97 Disables all functions ending with `_w`. Use this if your compiler does not provide wchar.h. Not required if DR_WAV_NO_STDIO is also defined.
100Supported Encapsulations
101========================
102- RIFF (Regular WAV)
103- RIFX (Big-Endian)
104- AIFF (Does not currently support ADPCM)
105- RF64
106- W64
108Note that AIFF and RIFX do not support write mode, nor do they support reading of metadata.
111Supported Encodings
112===================
113- Unsigned 8-bit PCM
114- Signed 12-bit PCM
115- Signed 16-bit PCM
116- Signed 24-bit PCM
117- Signed 32-bit PCM
118- IEEE 32-bit floating point
119- IEEE 64-bit floating point
120- A-law and u-law
121- Microsoft ADPCM
122- IMA ADPCM (DVI, format code 0x11)
1248-bit PCM encodings are always assumed to be unsigned. Signed 8-bit encoding can only be read with `drwav_read_raw()`.
126Note that ADPCM is not currently supported with AIFF. Contributions welcome.
129Notes
130=====
131- Samples are always interleaved.
132- 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()`
133 to read and convert audio data to 32-bit floating point, signed 32-bit integer and signed 16-bit integer samples respectively.
134- dr_wav will try to read the WAV file as best it can, even if it's not strictly conformant to the WAV format.
135*/
137#ifndef dr_wav_h
138#define dr_wav_h
140#ifdef __cplusplus
141extern "C" {
142#endif
144#define DRWAV_STRINGIFY(x) #x
145#define DRWAV_XSTRINGIFY(x) DRWAV_STRINGIFY(x)
147#define DRWAV_VERSION_MAJOR 0
148#define DRWAV_VERSION_MINOR 14
149#define DRWAV_VERSION_REVISION 0
150#define DRWAV_VERSION_STRING DRWAV_XSTRINGIFY(DRWAV_VERSION_MAJOR) "." DRWAV_XSTRINGIFY(DRWAV_VERSION_MINOR) "." DRWAV_XSTRINGIFY(DRWAV_VERSION_REVISION)
152#include <stddef.h> /* For size_t. */
154/* Sized Types */
155typedef signed char drwav_int8;
156typedef unsigned char drwav_uint8;
157typedef signed short drwav_int16;
158typedef unsigned short drwav_uint16;
159typedef signed int drwav_int32;
160typedef unsigned int drwav_uint32;
161#if defined(_MSC_VER) && !defined(__clang__)
162 typedef signed __int64 drwav_int64;
163 typedef unsigned __int64 drwav_uint64;
164#else
165 #if defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6)))
166 #pragma GCC diagnostic push
167 #pragma GCC diagnostic ignored "-Wlong-long"
168 #if defined(__clang__)
169 #pragma GCC diagnostic ignored "-Wc++11-long-long"
170 #endif
171 #endif
172 typedef signed long long drwav_int64;
173 typedef unsigned long long drwav_uint64;
174 #if defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6)))
175 #pragma GCC diagnostic pop
176 #endif
177#endif
178#if defined(__LP64__) || defined(_WIN64) || (defined(__x86_64__) && !defined(__ILP32__)) || defined(_M_X64) || defined(__ia64) || defined (_M_IA64) || defined(__aarch64__) || defined(_M_ARM64) || defined(_M_ARM64EC) || defined(__powerpc64__)
179 typedef drwav_uint64 drwav_uintptr;
180#else
181 typedef drwav_uint32 drwav_uintptr;
182#endif
183typedef drwav_uint8 drwav_bool8;
184typedef drwav_uint32 drwav_bool32;
185#define DRWAV_TRUE 1
186#define DRWAV_FALSE 0
187/* End Sized Types */
189/* Decorations */
190#if !defined(DRWAV_API)
191 #if defined(DRWAV_DLL)
192 #if defined(_WIN32)
193 #define DRWAV_DLL_IMPORT __declspec(dllimport)
194 #define DRWAV_DLL_EXPORT __declspec(dllexport)
195 #define DRWAV_DLL_PRIVATE static
196 #else
197 #if defined(__GNUC__) && __GNUC__ >= 4
198 #define DRWAV_DLL_IMPORT __attribute__((visibility("default")))
199 #define DRWAV_DLL_EXPORT __attribute__((visibility("default")))
200 #define DRWAV_DLL_PRIVATE __attribute__((visibility("hidden")))
201 #else
202 #define DRWAV_DLL_IMPORT
203 #define DRWAV_DLL_EXPORT
204 #define DRWAV_DLL_PRIVATE static
205 #endif
206 #endif
208 #if defined(DR_WAV_IMPLEMENTATION) || defined(DRWAV_IMPLEMENTATION)
209 #define DRWAV_API DRWAV_DLL_EXPORT
210 #else
211 #define DRWAV_API DRWAV_DLL_IMPORT
212 #endif
213 #define DRWAV_PRIVATE DRWAV_DLL_PRIVATE
214 #else
215 #define DRWAV_API extern
216 #define DRWAV_PRIVATE static
217 #endif
218#endif
219/* End Decorations */
221/* Result Codes */
222typedef drwav_int32 drwav_result;
223#define DRWAV_SUCCESS 0
224#define DRWAV_ERROR -1 /* A generic error. */
225#define DRWAV_INVALID_ARGS -2
226#define DRWAV_INVALID_OPERATION -3
227#define DRWAV_OUT_OF_MEMORY -4
228#define DRWAV_OUT_OF_RANGE -5
229#define DRWAV_ACCESS_DENIED -6
230#define DRWAV_DOES_NOT_EXIST -7
231#define DRWAV_ALREADY_EXISTS -8
232#define DRWAV_TOO_MANY_OPEN_FILES -9
233#define DRWAV_INVALID_FILE -10
234#define DRWAV_TOO_BIG -11
235#define DRWAV_PATH_TOO_LONG -12
236#define DRWAV_NAME_TOO_LONG -13
237#define DRWAV_NOT_DIRECTORY -14
238#define DRWAV_IS_DIRECTORY -15
239#define DRWAV_DIRECTORY_NOT_EMPTY -16
240#define DRWAV_END_OF_FILE -17
241#define DRWAV_NO_SPACE -18
242#define DRWAV_BUSY -19
243#define DRWAV_IO_ERROR -20
244#define DRWAV_INTERRUPT -21
245#define DRWAV_UNAVAILABLE -22
246#define DRWAV_ALREADY_IN_USE -23
247#define DRWAV_BAD_ADDRESS -24
248#define DRWAV_BAD_SEEK -25
249#define DRWAV_BAD_PIPE -26
250#define DRWAV_DEADLOCK -27
251#define DRWAV_TOO_MANY_LINKS -28
252#define DRWAV_NOT_IMPLEMENTED -29
253#define DRWAV_NO_MESSAGE -30
254#define DRWAV_BAD_MESSAGE -31
255#define DRWAV_NO_DATA_AVAILABLE -32
256#define DRWAV_INVALID_DATA -33
257#define DRWAV_TIMEOUT -34
258#define DRWAV_NO_NETWORK -35
259#define DRWAV_NOT_UNIQUE -36
260#define DRWAV_NOT_SOCKET -37
261#define DRWAV_NO_ADDRESS -38
262#define DRWAV_BAD_PROTOCOL -39
263#define DRWAV_PROTOCOL_UNAVAILABLE -40
264#define DRWAV_PROTOCOL_NOT_SUPPORTED -41
265#define DRWAV_PROTOCOL_FAMILY_NOT_SUPPORTED -42
266#define DRWAV_ADDRESS_FAMILY_NOT_SUPPORTED -43
267#define DRWAV_SOCKET_NOT_SUPPORTED -44
268#define DRWAV_CONNECTION_RESET -45
269#define DRWAV_ALREADY_CONNECTED -46
270#define DRWAV_NOT_CONNECTED -47
271#define DRWAV_CONNECTION_REFUSED -48
272#define DRWAV_NO_HOST -49
273#define DRWAV_IN_PROGRESS -50
274#define DRWAV_CANCELLED -51
275#define DRWAV_MEMORY_ALREADY_MAPPED -52
276#define DRWAV_AT_END -53
277/* End Result Codes */
279/* Common data formats. */
280#define DR_WAVE_FORMAT_PCM 0x1
281#define DR_WAVE_FORMAT_ADPCM 0x2
282#define DR_WAVE_FORMAT_IEEE_FLOAT 0x3
283#define DR_WAVE_FORMAT_ALAW 0x6
284#define DR_WAVE_FORMAT_MULAW 0x7
285#define DR_WAVE_FORMAT_DVI_ADPCM 0x11
286#define DR_WAVE_FORMAT_EXTENSIBLE 0xFFFE
288/* Flags to pass into drwav_init_ex(), etc. */
289#define DRWAV_SEQUENTIAL 0x00000001
290#define DRWAV_WITH_METADATA 0x00000002
292DRWAV_API void drwav_version(drwav_uint32* pMajor, drwav_uint32* pMinor, drwav_uint32* pRevision);
293DRWAV_API const char* drwav_version_string(void);
295/* Allocation Callbacks */
296typedef struct
297{
298 void* pUserData;
299 void* (* onMalloc)(size_t sz, void* pUserData);
300 void* (* onRealloc)(void* p, size_t sz, void* pUserData);
301 void (* onFree)(void* p, void* pUserData);
302} drwav_allocation_callbacks;
303/* End Allocation Callbacks */
305typedef enum
306{
307 DRWAV_SEEK_SET,
308 DRWAV_SEEK_CUR,
309 DRWAV_SEEK_END
310} drwav_seek_origin;
312typedef enum
313{
314 drwav_container_riff,
315 drwav_container_rifx,
316 drwav_container_w64,
317 drwav_container_rf64,
318 drwav_container_aiff
319} drwav_container;
321typedef struct
322{
323 union
324 {
325 drwav_uint8 fourcc[4];
326 drwav_uint8 guid[16];
327 } id;
329 /* The size in bytes of the chunk. */
330 drwav_uint64 sizeInBytes;
332 /*
333 RIFF = 2 byte alignment.
334 W64 = 8 byte alignment.
335 */
336 unsigned int paddingSize;
337} drwav_chunk_header;
339typedef struct
340{
341 /*
342 The format tag exactly as specified in the wave file's "fmt" chunk. This can be used by applications
343 that require support for data formats not natively supported by dr_wav.
344 */
345 drwav_uint16 formatTag;
347 /* The number of channels making up the audio data. When this is set to 1 it is mono, 2 is stereo, etc. */
348 drwav_uint16 channels;
350 /* The sample rate. Usually set to something like 44100. */
351 drwav_uint32 sampleRate;
353 /* Average bytes per second. You probably don't need this, but it's left here for informational purposes. */
354 drwav_uint32 avgBytesPerSec;
356 /* Block align. This is equal to the number of channels * bytes per sample. */
357 drwav_uint16 blockAlign;
359 /* Bits per sample. */
360 drwav_uint16 bitsPerSample;
362 /* The size of the extended data. Only used internally for validation, but left here for informational purposes. */
363 drwav_uint16 extendedSize;
365 /*
366 The number of valid bits per sample. When <formatTag> is equal to WAVE_FORMAT_EXTENSIBLE, <bitsPerSample>
367 is always rounded up to the nearest multiple of 8. This variable contains information about exactly how
368 many bits are valid per sample. Mainly used for informational purposes.
369 */
370 drwav_uint16 validBitsPerSample;
372 /* The channel mask. Not used at the moment. */
373 drwav_uint32 channelMask;
375 /* The sub-format, exactly as specified by the wave file. */
376 drwav_uint8 subFormat[16];
377} drwav_fmt;
379DRWAV_API drwav_uint16 drwav_fmt_get_format(const drwav_fmt* pFMT);
382/*
383Callback for when data is read. Return value is the number of bytes actually read.
385pUserData [in] The user data that was passed to drwav_init() and family.
386pBufferOut [out] The output buffer.
387bytesToRead [in] The number of bytes to read.
389Returns the number of bytes actually read.
391A return value of less than bytesToRead indicates the end of the stream. Do _not_ return from this callback until
392either the entire bytesToRead is filled or you have reached the end of the stream.
393*/
394typedef size_t (* drwav_read_proc)(void* pUserData, void* pBufferOut, size_t bytesToRead);
396/*
397Callback for when data is written. Returns value is the number of bytes actually written.
399pUserData [in] The user data that was passed to drwav_init_write() and family.
400pData [out] A pointer to the data to write.
401bytesToWrite [in] The number of bytes to write.
403Returns the number of bytes actually written.
405If the return value differs from bytesToWrite, it indicates an error.
406*/
407typedef size_t (* drwav_write_proc)(void* pUserData, const void* pData, size_t bytesToWrite);
409/*
410Callback for when data needs to be seeked.
412pUserData [in] The user data that was passed to drwav_init() and family.
413offset [in] The number of bytes to move, relative to the origin. Will never be negative.
414origin [in] The origin of the seek - the current position or the start of the stream.
416Returns whether or not the seek was successful.
418Whether or not it is relative to the beginning or current position is determined by the "origin" parameter which will be either DRWAV_SEEK_SET or
419DRWAV_SEEK_CUR.
420*/
421typedef drwav_bool32 (* drwav_seek_proc)(void* pUserData, int offset, drwav_seek_origin origin);
423/*
424Callback for when the current position in the stream needs to be retrieved.
426pUserData [in] The user data that was passed to drwav_init() and family.
427pCursor [out] A pointer to a variable to receive the current position in the stream.
429Returns whether or not the operation was successful.
430*/
431typedef drwav_bool32 (* drwav_tell_proc)(void* pUserData, drwav_int64* pCursor);
433/*
434Callback for when drwav_init_ex() finds a chunk.
436pChunkUserData [in] The user data that was passed to the pChunkUserData parameter of drwav_init_ex() and family.
437onRead [in] A pointer to the function to call when reading.
438onSeek [in] A pointer to the function to call when seeking.
439pReadSeekUserData [in] The user data that was passed to the pReadSeekUserData parameter of drwav_init_ex() and family.
440pChunkHeader [in] A pointer to an object containing basic header information about the chunk. Use this to identify the chunk.
441container [in] Whether or not the WAV file is a RIFF or Wave64 container. If you're unsure of the difference, assume RIFF.
442pFMT [in] A pointer to the object containing the contents of the "fmt" chunk.
444Returns the number of bytes read + seeked.
446To 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
447be the total number of bytes you have read _plus_ seeked.
449Use the `container` argument to discriminate the fields in `pChunkHeader->id`. If the container is `drwav_container_riff` or `drwav_container_rf64` you should
450use `id.fourcc`, otherwise you should use `id.guid`.
452The `pFMT` parameter can be used to determine the data format of the wave file. Use `drwav_fmt_get_format()` to get the sample format, which will be one of the
453`DR_WAVE_FORMAT_*` identifiers.
455The read pointer will be sitting on the first byte after the chunk's header. You must not attempt to read beyond the boundary of the chunk.
456*/
457typedef drwav_uint64 (* drwav_chunk_proc)(void* pChunkUserData, drwav_read_proc onRead, drwav_seek_proc onSeek, void* pReadSeekUserData, const drwav_chunk_header* pChunkHeader, drwav_container container, const drwav_fmt* pFMT);
460/* Structure for internal use. Only used for loaders opened with drwav_init_memory(). */
461typedef struct
462{
463 const drwav_uint8* data;
464 size_t dataSize;
465 size_t currentReadPos;
466} drwav__memory_stream;
468/* Structure for internal use. Only used for writers opened with drwav_init_memory_write(). */
469typedef struct
470{
471 void** ppData;
472 size_t* pDataSize;
473 size_t dataSize;
474 size_t dataCapacity;
475 size_t currentWritePos;
476} drwav__memory_stream_write;
478typedef struct
479{
480 drwav_container container; /* RIFF, W64. */
481 drwav_uint32 format; /* DR_WAVE_FORMAT_* */
482 drwav_uint32 channels;
483 drwav_uint32 sampleRate;
484 drwav_uint32 bitsPerSample;
485} drwav_data_format;
487typedef enum
488{
489 drwav_metadata_type_none = 0,
491 /*
492 Unknown simply means a chunk that drwav does not handle specifically. You can still ask to
493 receive these chunks as metadata objects. It is then up to you to interpret the chunk's data.
494 You can also write unknown metadata to a wav file. Be careful writing unknown chunks if you
495 have also edited the audio data. The unknown chunks could represent offsets/sizes that no
496 longer correctly correspond to the audio data.
497 */
498 drwav_metadata_type_unknown = 1 << 0,
500 /* Only 1 of each of these metadata items are allowed in a wav file. */
501 drwav_metadata_type_smpl = 1 << 1,
502 drwav_metadata_type_inst = 1 << 2,
503 drwav_metadata_type_cue = 1 << 3,
504 drwav_metadata_type_acid = 1 << 4,
505 drwav_metadata_type_bext = 1 << 5,
507 /*
508 Wav files often have a LIST chunk. This is a chunk that contains a set of subchunks. For this
509 higher-level metadata API, we don't make a distinction between a regular chunk and a LIST
510 subchunk. Instead, they are all just 'metadata' items.
512 There can be multiple of these metadata items in a wav file.
513 */
514 drwav_metadata_type_list_label = 1 << 6,
515 drwav_metadata_type_list_note = 1 << 7,
516 drwav_metadata_type_list_labelled_cue_region = 1 << 8,
518 drwav_metadata_type_list_info_software = 1 << 9,
519 drwav_metadata_type_list_info_copyright = 1 << 10,
520 drwav_metadata_type_list_info_title = 1 << 11,
521 drwav_metadata_type_list_info_artist = 1 << 12,
522 drwav_metadata_type_list_info_comment = 1 << 13,
523 drwav_metadata_type_list_info_date = 1 << 14,
524 drwav_metadata_type_list_info_genre = 1 << 15,
525 drwav_metadata_type_list_info_album = 1 << 16,
526 drwav_metadata_type_list_info_tracknumber = 1 << 17,
527 drwav_metadata_type_list_info_location = 1 << 18,
528 drwav_metadata_type_list_info_organization = 1 << 19,
529 drwav_metadata_type_list_info_keywords = 1 << 20,
530 drwav_metadata_type_list_info_medium = 1 << 21,
531 drwav_metadata_type_list_info_description = 1 << 22,
533 /* Other type constants for convenience. */
534 drwav_metadata_type_list_all_info_strings = drwav_metadata_type_list_info_software
535 | drwav_metadata_type_list_info_copyright
536 | drwav_metadata_type_list_info_title
537 | drwav_metadata_type_list_info_artist
538 | drwav_metadata_type_list_info_comment
539 | drwav_metadata_type_list_info_date
540 | drwav_metadata_type_list_info_genre
541 | drwav_metadata_type_list_info_album
542 | drwav_metadata_type_list_info_tracknumber
543 | drwav_metadata_type_list_info_location
544 | drwav_metadata_type_list_info_organization
545 | drwav_metadata_type_list_info_keywords
546 | drwav_metadata_type_list_info_medium
547 | drwav_metadata_type_list_info_description,
549 drwav_metadata_type_list_all_adtl = drwav_metadata_type_list_label
550 | drwav_metadata_type_list_note
551 | drwav_metadata_type_list_labelled_cue_region,
553 drwav_metadata_type_all = -2, /*0xFFFFFFFF & ~drwav_metadata_type_unknown,*/
554 drwav_metadata_type_all_including_unknown = -1 /*0xFFFFFFFF,*/
555} drwav_metadata_type;
557/*
558Sampler Metadata
560The sampler chunk contains information about how a sound should be played in the context of a whole
561audio production, and when used in a sampler. See https://en.wikipedia.org/wiki/Sample-based_synthesis.
562*/
563typedef enum
564{
565 drwav_smpl_loop_type_forward = 0,
566 drwav_smpl_loop_type_pingpong = 1,
567 drwav_smpl_loop_type_backward = 2
568} drwav_smpl_loop_type;
570typedef struct
571{
572 /* The ID of the associated cue point, see drwav_cue and drwav_cue_point. As with all cue point IDs, this can correspond to a label chunk to give this loop a name, see drwav_list_label_or_note. */
573 drwav_uint32 cuePointId;
575 /* See drwav_smpl_loop_type. */
576 drwav_uint32 type;
578 /* The offset of the first sample to be played in the loop. */
579 drwav_uint32 firstSampleOffset;
581 /* The offset into the audio data of the last sample to be played in the loop. */
582 drwav_uint32 lastSampleOffset;
584 /* A value to represent that playback should occur at a point between samples. This value ranges from 0 to UINT32_MAX. Where a value of 0 means no fraction, and a value of (UINT32_MAX / 2) would mean half a sample. */
585 drwav_uint32 sampleFraction;
587 /* Number of times to play the loop. 0 means loop infinitely. */
588 drwav_uint32 playCount;
589} drwav_smpl_loop;
591typedef struct
592{
593 /* IDs for a particular MIDI manufacturer. 0 if not used. */
594 drwav_uint32 manufacturerId;
595 drwav_uint32 productId;
597 /* The period of 1 sample in nanoseconds. */
598 drwav_uint32 samplePeriodNanoseconds;
600 /* The MIDI root note of this file. 0 to 127. */
601 drwav_uint32 midiUnityNote;
603 /* The fraction of a semitone up from the given MIDI note. This is a value from 0 to UINT32_MAX, where 0 means no change and (UINT32_MAX / 2) is half a semitone (AKA 50 cents). */
604 drwav_uint32 midiPitchFraction;
606 /* Data relating to SMPTE standards which are used for syncing audio and video. 0 if not used. */
607 drwav_uint32 smpteFormat;
608 drwav_uint32 smpteOffset;
610 /* drwav_smpl_loop loops. */
611 drwav_uint32 sampleLoopCount;
613 /* Optional sampler-specific data. */
614 drwav_uint32 samplerSpecificDataSizeInBytes;
616 drwav_smpl_loop* pLoops;
617 drwav_uint8* pSamplerSpecificData;
618} drwav_smpl;
620/*
621Instrument Metadata
623The inst metadata contains data about how a sound should be played as part of an instrument. This
624commonly read by samplers. See https://en.wikipedia.org/wiki/Sample-based_synthesis.
625*/
626typedef struct
627{
628 drwav_int8 midiUnityNote; /* The root note of the audio as a MIDI note number. 0 to 127. */
629 drwav_int8 fineTuneCents; /* -50 to +50 */
630 drwav_int8 gainDecibels; /* -64 to +64 */
631 drwav_int8 lowNote; /* 0 to 127 */
632 drwav_int8 highNote; /* 0 to 127 */
633 drwav_int8 lowVelocity; /* 1 to 127 */
634 drwav_int8 highVelocity; /* 1 to 127 */
635} drwav_inst;
637/*
638Cue Metadata
640Cue points are markers at specific points in the audio. They often come with an associated piece of
641drwav_list_label_or_note metadata which contains the text for the marker.
642*/
643typedef struct
644{
645 /* Unique identification value. */
646 drwav_uint32 id;
648 /* Set to 0. This is only relevant if there is a 'playlist' chunk - which is not supported by dr_wav. */
649 drwav_uint32 playOrderPosition;
651 /* Should always be "data". This represents the fourcc value of the chunk that this cue point corresponds to. dr_wav only supports a single data chunk so this should always be "data". */
652 drwav_uint8 dataChunkId[4];
654 /* Set to 0. This is only relevant if there is a wave list chunk. dr_wav, like lots of readers/writers, do not support this. */
655 drwav_uint32 chunkStart;
657 /* Set to 0 for uncompressed formats. Else the last byte in compressed wave data where decompression can begin to find the value of the corresponding sample value. */
658 drwav_uint32 blockStart;
660 /* For uncompressed formats this is the offset of the cue point into the audio data. For compressed formats this is relative to the block specified with blockStart. */
661 drwav_uint32 sampleOffset;
662} drwav_cue_point;
664typedef struct
665{
666 drwav_uint32 cuePointCount;
667 drwav_cue_point *pCuePoints;
668} drwav_cue;
670/*
671Acid Metadata
673This chunk contains some information about the time signature and the tempo of the audio.
674*/
675typedef enum
676{
677 drwav_acid_flag_one_shot = 1, /* If this is not set, then it is a loop instead of a one-shot. */
678 drwav_acid_flag_root_note_set = 2,
679 drwav_acid_flag_stretch = 4,
680 drwav_acid_flag_disk_based = 8,
681 drwav_acid_flag_acidizer = 16 /* Not sure what this means. */
682} drwav_acid_flag;
684typedef struct
685{
686 /* A bit-field, see drwav_acid_flag. */
687 drwav_uint32 flags;
689 /* Valid if flags contains drwav_acid_flag_root_note_set. It represents the MIDI root note the file - a value from 0 to 127. */
690 drwav_uint16 midiUnityNote;
692 /* Reserved values that should probably be ignored. reserved1 seems to often be 128 and reserved2 is 0. */
693 drwav_uint16 reserved1;
694 float reserved2;
696 /* Number of beats. */
697 drwav_uint32 numBeats;
699 /* The time signature of the audio. */
700 drwav_uint16 meterDenominator;
701 drwav_uint16 meterNumerator;
703 /* Beats per minute of the track. Setting a value of 0 suggests that there is no tempo. */
704 float tempo;
705} drwav_acid;
707/*
708Cue Label or Note metadata
710These are 2 different types of metadata, but they have the exact same format. Labels tend to be the
711more common and represent a short name for a cue point. Notes might be used to represent a longer
712comment.
713*/
714typedef struct
715{
716 /* The ID of a cue point that this label or note corresponds to. */
717 drwav_uint32 cuePointId;
719 /* Size of the string not including any null terminator. */
720 drwav_uint32 stringLength;
722 /* The string. The *init_with_metadata functions null terminate this for convenience. */
723 char* pString;
724} drwav_list_label_or_note;
726/*
727BEXT metadata, also known as Broadcast Wave Format (BWF)
729This metadata adds some extra description to an audio file. You must check the version field to
730determine if the UMID or the loudness fields are valid.
731*/
732typedef struct
733{
734 /*
735 These top 3 fields, and the umid field are actually defined in the standard as a statically
736 sized buffers. In order to reduce the size of this struct (and therefore the union in the
737 metadata struct), we instead store these as pointers.
738 */
739 char* pDescription; /* Can be NULL or a null-terminated string, must be <= 256 characters. */
740 char* pOriginatorName; /* Can be NULL or a null-terminated string, must be <= 32 characters. */
741 char* pOriginatorReference; /* Can be NULL or a null-terminated string, must be <= 32 characters. */
742 char pOriginationDate[10]; /* ASCII "yyyy:mm:dd". */
743 char pOriginationTime[8]; /* ASCII "hh:mm:ss". */
744 drwav_uint64 timeReference; /* First sample count since midnight. */
745 drwav_uint16 version; /* Version of the BWF, check this to see if the fields below are valid. */
747 /*
748 Unrestricted ASCII characters containing a collection of strings terminated by CR/LF. Each
749 string shall contain a description of a coding process applied to the audio data.
750 */
751 char* pCodingHistory;
752 drwav_uint32 codingHistorySize;
754 /* Fields below this point are only valid if the version is 1 or above. */
755 drwav_uint8* pUMID; /* Exactly 64 bytes of SMPTE UMID */
757 /* Fields below this point are only valid if the version is 2 or above. */
758 drwav_uint16 loudnessValue; /* Integrated Loudness Value of the file in LUFS (multiplied by 100). */
759 drwav_uint16 loudnessRange; /* Loudness Range of the file in LU (multiplied by 100). */
760 drwav_uint16 maxTruePeakLevel; /* Maximum True Peak Level of the file expressed as dBTP (multiplied by 100). */
761 drwav_uint16 maxMomentaryLoudness; /* Highest value of the Momentary Loudness Level of the file in LUFS (multiplied by 100). */
762 drwav_uint16 maxShortTermLoudness; /* Highest value of the Short-Term Loudness Level of the file in LUFS (multiplied by 100). */
763} drwav_bext;
765/*
766Info Text Metadata
768There a many different types of information text that can be saved in this format. This is where
769things like the album name, the artists, the year it was produced, etc are saved. See
770drwav_metadata_type for the full list of types that dr_wav supports.
771*/
772typedef struct
773{
774 /* Size of the string not including any null terminator. */
775 drwav_uint32 stringLength;
777 /* The string. The *init_with_metadata functions null terminate this for convenience. */
778 char* pString;
779} drwav_list_info_text;
781/*
782Labelled Cue Region Metadata
784The labelled cue region metadata is used to associate some region of audio with text. The region
785starts at a cue point, and extends for the given number of samples.
786*/
787typedef struct
788{
789 /* The ID of a cue point that this object corresponds to. */
790 drwav_uint32 cuePointId;
792 /* The number of samples from the cue point forwards that should be considered this region */
793 drwav_uint32 sampleLength;
795 /* Four characters used to say what the purpose of this region is. */
796 drwav_uint8 purposeId[4];
798 /* Unsure of the exact meanings of these. It appears to be acceptable to set them all to 0. */
799 drwav_uint16 country;
800 drwav_uint16 language;
801 drwav_uint16 dialect;
802 drwav_uint16 codePage;
804 /* Size of the string not including any null terminator. */
805 drwav_uint32 stringLength;
807 /* The string. The *init_with_metadata functions null terminate this for convenience. */
808 char* pString;
809} drwav_list_labelled_cue_region;
811/*
812Unknown Metadata
814This chunk just represents a type of chunk that dr_wav does not understand.
816Unknown metadata has a location attached to it. This is because wav files can have a LIST chunk
817that contains subchunks. These LIST chunks can be one of two types. An adtl list, or an INFO
818list. This enum is used to specify the location of a chunk that dr_wav currently doesn't support.
819*/
820typedef enum
821{
822 drwav_metadata_location_invalid,
823 drwav_metadata_location_top_level,
824 drwav_metadata_location_inside_info_list,
825 drwav_metadata_location_inside_adtl_list
826} drwav_metadata_location;
828typedef struct
829{
830 drwav_uint8 id[4];
831 drwav_metadata_location chunkLocation;
832 drwav_uint32 dataSizeInBytes;
833 drwav_uint8* pData;
834} drwav_unknown_metadata;
836/*
837Metadata is saved as a union of all the supported types.
838*/
839typedef struct
840{
841 /* Determines which item in the union is valid. */
842 drwav_metadata_type type;
844 union
845 {
846 drwav_cue cue;
847 drwav_smpl smpl;
848 drwav_acid acid;
849 drwav_inst inst;
850 drwav_bext bext;
851 drwav_list_label_or_note labelOrNote; /* List label or list note. */
852 drwav_list_labelled_cue_region labelledCueRegion;
853 drwav_list_info_text infoText; /* Any of the list info types. */
854 drwav_unknown_metadata unknown;
855 } data;
856} drwav_metadata;
858typedef struct
859{
860 /* A pointer to the function to call when more data is needed. */
861 drwav_read_proc onRead;
863 /* A pointer to the function to call when data needs to be written. Only used when the drwav object is opened in write mode. */
864 drwav_write_proc onWrite;
866 /* A pointer to the function to call when the wav file needs to be seeked. */
867 drwav_seek_proc onSeek;
869 /* A pointer to the function to call when the position of the stream needs to be retrieved. */
870 drwav_tell_proc onTell;
872 /* The user data to pass to callbacks. */
873 void* pUserData;
875 /* Allocation callbacks. */
876 drwav_allocation_callbacks allocationCallbacks;
879 /* Whether or not the WAV file is formatted as a standard RIFF file or W64. */
880 drwav_container container;
883 /* Structure containing format information exactly as specified by the wav file. */
884 drwav_fmt fmt;
886 /* The sample rate. Will be set to something like 44100. */
887 drwav_uint32 sampleRate;
889 /* The number of channels. This will be set to 1 for monaural streams, 2 for stereo, etc. */
890 drwav_uint16 channels;
892 /* The bits per sample. Will be set to something like 16, 24, etc. */
893 drwav_uint16 bitsPerSample;
895 /* Equal to fmt.formatTag, or the value specified by fmt.subFormat if fmt.formatTag is equal to 65534 (WAVE_FORMAT_EXTENSIBLE). */
896 drwav_uint16 translatedFormatTag;
898 /* The total number of PCM frames making up the audio data. */
899 drwav_uint64 totalPCMFrameCount;
902 /* The size in bytes of the data chunk. */
903 drwav_uint64 dataChunkDataSize;
905 /* The position in the stream of the first data byte of the data chunk. This is used for seeking. */
906 drwav_uint64 dataChunkDataPos;
908 /* The number of bytes remaining in the data chunk. */
909 drwav_uint64 bytesRemaining;
911 /* The current read position in PCM frames. */
912 drwav_uint64 readCursorInPCMFrames;
915 /*
916 Only used in sequential write mode. Keeps track of the desired size of the "data" chunk at the point of initialization time. Always
917 set to 0 for non-sequential writes and when the drwav object is opened in read mode. Used for validation.
918 */
919 drwav_uint64 dataChunkDataSizeTargetWrite;
921 /* Keeps track of whether or not the wav writer was initialized in sequential mode. */
922 drwav_bool32 isSequentialWrite;
925 /* A array of metadata. This is valid after the *init_with_metadata call returns. It will be valid until drwav_uninit() is called. You can take ownership of this data with drwav_take_ownership_of_metadata(). */
926 drwav_metadata* pMetadata;
927 drwav_uint32 metadataCount;
930 /* A hack to avoid a DRWAV_MALLOC() when opening a decoder with drwav_init_memory(). */
931 drwav__memory_stream memoryStream;
932 drwav__memory_stream_write memoryStreamWrite;
935 /* Microsoft ADPCM specific data. */
936 struct
937 {
938 drwav_uint32 bytesRemainingInBlock;
939 drwav_uint16 predictor[2];
940 drwav_int32 delta[2];
941 drwav_int32 cachedFrames[4]; /* Samples are stored in this cache during decoding. */
942 drwav_uint32 cachedFrameCount;
943 drwav_int32 prevFrames[2][2]; /* The previous 2 samples for each channel (2 channels at most). */
944 } msadpcm;
946 /* IMA ADPCM specific data. */
947 struct
948 {
949 drwav_uint32 bytesRemainingInBlock;
950 drwav_int32 predictor[2];
951 drwav_int32 stepIndex[2];
952 drwav_int32 cachedFrames[16]; /* Samples are stored in this cache during decoding. */
953 drwav_uint32 cachedFrameCount;
954 } ima;
956 /* AIFF specific data. */
957 struct
958 {
959 drwav_bool8 isLE; /* Will be set to true if the audio data is little-endian encoded. */
960 drwav_bool8 isUnsigned; /* Only used for 8-bit samples. When set to true, will be treated as unsigned. */
961 } aiff;
962} drwav;
965/*
966Initializes a pre-allocated drwav object for reading.
968pWav [out] A pointer to the drwav object being initialized.
969onRead [in] The function to call when data needs to be read from the client.
970onSeek [in] The function to call when the read position of the client data needs to move.
971onChunk [in, optional] The function to call when a chunk is enumerated at initialized time.
972pUserData, pReadSeekUserData [in, optional] A pointer to application defined data that will be passed to onRead and onSeek.
973pChunkUserData [in, optional] A pointer to application defined data that will be passed to onChunk.
974flags [in, optional] A set of flags for controlling how things are loaded.
976Returns true if successful; false otherwise.
978Close the loader with drwav_uninit().
980This is the lowest level function for initializing a WAV file. You can also use drwav_init_file() and drwav_init_memory()
981to open the stream from a file or from a block of memory respectively.
983Possible values for flags:
984 DRWAV_SEQUENTIAL: Never perform a backwards seek while loading. This disables the chunk callback and will cause this function
985 to return as soon as the data chunk is found. Any chunks after the data chunk will be ignored.
987drwav_init() is equivalent to "drwav_init_ex(pWav, onRead, onSeek, NULL, pUserData, NULL, 0);".
989The onChunk callback is not called for the WAVE or FMT chunks. The contents of the FMT chunk can be read from pWav->fmt
990after the function returns.
992See also: drwav_init_file(), drwav_init_memory(), drwav_uninit()
993*/
994DRWAV_API drwav_bool32 drwav_init(drwav* pWav, drwav_read_proc onRead, drwav_seek_proc onSeek, drwav_tell_proc onTell, void* pUserData, const drwav_allocation_callbacks* pAllocationCallbacks);
995DRWAV_API drwav_bool32 drwav_init_ex(drwav* pWav, drwav_read_proc onRead, drwav_seek_proc onSeek, drwav_tell_proc onTell, drwav_chunk_proc onChunk, void* pReadSeekTellUserData, void* pChunkUserData, drwav_uint32 flags, const drwav_allocation_callbacks* pAllocationCallbacks);
996DRWAV_API drwav_bool32 drwav_init_with_metadata(drwav* pWav, drwav_read_proc onRead, drwav_seek_proc onSeek, drwav_tell_proc onTell, void* pUserData, drwav_uint32 flags, const drwav_allocation_callbacks* pAllocationCallbacks);
998/*
999Initializes a pre-allocated drwav object for writing.
1001onWrite [in] The function to call when data needs to be written.
1002onSeek [in] The function to call when the write position needs to move.
1003pUserData [in, optional] A pointer to application defined data that will be passed to onWrite and onSeek.
1004metadata, numMetadata [in, optional] An array of metadata objects that should be written to the file. The array is not edited. You are responsible for this metadata memory and it must maintain valid until drwav_uninit() is called.
1006Returns true if successful; false otherwise.
1008Close the writer with drwav_uninit().
1010This is the lowest level function for initializing a WAV file. You can also use drwav_init_file_write() and drwav_init_memory_write()
1011to open the stream from a file or from a block of memory respectively.
1013If the total sample count is known, you can use drwav_init_write_sequential(). This avoids the need for dr_wav to perform
1014a post-processing step for storing the total sample count and the size of the data chunk which requires a backwards seek.
1016See also: drwav_init_file_write(), drwav_init_memory_write(), drwav_uninit()
1017*/
1018DRWAV_API drwav_bool32 drwav_init_write(drwav* pWav, const drwav_data_format* pFormat, drwav_write_proc onWrite, drwav_seek_proc onSeek, void* pUserData, const drwav_allocation_callbacks* pAllocationCallbacks);
1019DRWAV_API drwav_bool32 drwav_init_write_sequential(drwav* pWav, const drwav_data_format* pFormat, drwav_uint64 totalSampleCount, drwav_write_proc onWrite, void* pUserData, const drwav_allocation_callbacks* pAllocationCallbacks);
1020DRWAV_API drwav_bool32 drwav_init_write_sequential_pcm_frames(drwav* pWav, const drwav_data_format* pFormat, drwav_uint64 totalPCMFrameCount, drwav_write_proc onWrite, void* pUserData, const drwav_allocation_callbacks* pAllocationCallbacks);
1021DRWAV_API drwav_bool32 drwav_init_write_with_metadata(drwav* pWav, const drwav_data_format* pFormat, drwav_write_proc onWrite, drwav_seek_proc onSeek, void* pUserData, const drwav_allocation_callbacks* pAllocationCallbacks, drwav_metadata* pMetadata, drwav_uint32 metadataCount);
1023/*
1024Utility function to determine the target size of the entire data to be written (including all headers and chunks).
1026Returns the target size in bytes.
1028The metadata argument can be NULL meaning no metadata exists.
1030Useful if the application needs to know the size to allocate.
1032Only writing to the RIFF chunk and one data chunk is currently supported.
1034See also: drwav_init_write(), drwav_init_file_write(), drwav_init_memory_write()
1035*/
1036DRWAV_API drwav_uint64 drwav_target_write_size_bytes(const drwav_data_format* pFormat, drwav_uint64 totalFrameCount, drwav_metadata* pMetadata, drwav_uint32 metadataCount);
1038/*
1039Take ownership of the metadata objects that were allocated via one of the init_with_metadata() function calls. The init_with_metdata functions perform a single heap allocation for this metadata.
1041Useful if you want the data to persist beyond the lifetime of the drwav object.
1043You must free the data returned from this function using drwav_free().
1044*/
1045DRWAV_API drwav_metadata* drwav_take_ownership_of_metadata(drwav* pWav);
1047/*
1048Uninitializes the given drwav object.
1050Use this only for objects initialized with drwav_init*() functions (drwav_init(), drwav_init_ex(), drwav_init_write(), drwav_init_write_sequential()).
1051*/
1052DRWAV_API drwav_result drwav_uninit(drwav* pWav);
1055/*
1056Reads raw audio data.
1058This is the lowest level function for reading audio data. It simply reads the given number of
1059bytes of the raw internal sample data.
1061Consider using drwav_read_pcm_frames_s16(), drwav_read_pcm_frames_s32() or drwav_read_pcm_frames_f32() for
1062reading sample data in a consistent format.
1064pBufferOut can be NULL in which case a seek will be performed.
1066Returns the number of bytes actually read.
1067*/
1068DRWAV_API size_t drwav_read_raw(drwav* pWav, size_t bytesToRead, void* pBufferOut);
1070/*
1071Reads up to the specified number of PCM frames from the WAV file.
1073The output data will be in the file's internal format, converted to native-endian byte order. Use
1074drwav_read_pcm_frames_s16/f32/s32() to read data in a specific format.
1076If the return value is less than <framesToRead> it means the end of the file has been reached or
1077you have requested more PCM frames than can possibly fit in the output buffer.
1079This function will only work when sample data is of a fixed size and uncompressed. If you are
1080using a compressed format consider using drwav_read_raw() or drwav_read_pcm_frames_s16/s32/f32().
1082pBufferOut can be NULL in which case a seek will be performed.
1083*/
1084DRWAV_API drwav_uint64 drwav_read_pcm_frames(drwav* pWav, drwav_uint64 framesToRead, void* pBufferOut);
1085DRWAV_API drwav_uint64 drwav_read_pcm_frames_le(drwav* pWav, drwav_uint64 framesToRead, void* pBufferOut);
1086DRWAV_API drwav_uint64 drwav_read_pcm_frames_be(drwav* pWav, drwav_uint64 framesToRead, void* pBufferOut);
1088/*
1089Seeks to the given PCM frame.
1091Returns true if successful; false otherwise.
1092*/
1093DRWAV_API drwav_bool32 drwav_seek_to_pcm_frame(drwav* pWav, drwav_uint64 targetFrameIndex);
1095/*
1096Retrieves the current read position in pcm frames.
1097*/
1098DRWAV_API drwav_result drwav_get_cursor_in_pcm_frames(drwav* pWav, drwav_uint64* pCursor);
1100/*
1101Retrieves the length of the file.
1102*/
1103DRWAV_API drwav_result drwav_get_length_in_pcm_frames(drwav* pWav, drwav_uint64* pLength);
1106/*
1107Writes raw audio data.
1109Returns the number of bytes actually written. If this differs from bytesToWrite, it indicates an error.
1110*/
1111DRWAV_API size_t drwav_write_raw(drwav* pWav, size_t bytesToWrite, const void* pData);
1113/*
1114Writes PCM frames.
1116Returns the number of PCM frames written.
1118Input samples need to be in native-endian byte order. On big-endian architectures the input data will be converted to
1119little-endian. Use drwav_write_raw() to write raw audio data without performing any conversion.
1120*/
1121DRWAV_API drwav_uint64 drwav_write_pcm_frames(drwav* pWav, drwav_uint64 framesToWrite, const void* pData);
1122DRWAV_API drwav_uint64 drwav_write_pcm_frames_le(drwav* pWav, drwav_uint64 framesToWrite, const void* pData);
1123DRWAV_API drwav_uint64 drwav_write_pcm_frames_be(drwav* pWav, drwav_uint64 framesToWrite, const void* pData);
1125/* Conversion Utilities */
1126#ifndef DR_WAV_NO_CONVERSION_API
1128/*
1129Reads a chunk of audio data and converts it to signed 16-bit PCM samples.
1131pBufferOut can be NULL in which case a seek will be performed.
1133Returns the number of PCM frames actually read.
1135If the return value is less than <framesToRead> it means the end of the file has been reached.
1136*/
1137DRWAV_API drwav_uint64 drwav_read_pcm_frames_s16(drwav* pWav, drwav_uint64 framesToRead, drwav_int16* pBufferOut);
1138DRWAV_API drwav_uint64 drwav_read_pcm_frames_s16le(drwav* pWav, drwav_uint64 framesToRead, drwav_int16* pBufferOut);
1139DRWAV_API drwav_uint64 drwav_read_pcm_frames_s16be(drwav* pWav, drwav_uint64 framesToRead, drwav_int16* pBufferOut);
1141/* Low-level function for converting unsigned 8-bit PCM samples to signed 16-bit PCM samples. */
1142DRWAV_API void drwav_u8_to_s16(drwav_int16* pOut, const drwav_uint8* pIn, size_t sampleCount);
1144/* Low-level function for converting signed 24-bit PCM samples to signed 16-bit PCM samples. */
1145DRWAV_API void drwav_s24_to_s16(drwav_int16* pOut, const drwav_uint8* pIn, size_t sampleCount);
1147/* Low-level function for converting signed 32-bit PCM samples to signed 16-bit PCM samples. */
1148DRWAV_API void drwav_s32_to_s16(drwav_int16* pOut, const drwav_int32* pIn, size_t sampleCount);
1150/* Low-level function for converting IEEE 32-bit floating point samples to signed 16-bit PCM samples. */
1151DRWAV_API void drwav_f32_to_s16(drwav_int16* pOut, const float* pIn, size_t sampleCount);
1153/* Low-level function for converting IEEE 64-bit floating point samples to signed 16-bit PCM samples. */
1154DRWAV_API void drwav_f64_to_s16(drwav_int16* pOut, const double* pIn, size_t sampleCount);
1156/* Low-level function for converting A-law samples to signed 16-bit PCM samples. */
1157DRWAV_API void drwav_alaw_to_s16(drwav_int16* pOut, const drwav_uint8* pIn, size_t sampleCount);
1159/* Low-level function for converting u-law samples to signed 16-bit PCM samples. */
1160DRWAV_API void drwav_mulaw_to_s16(drwav_int16* pOut, const drwav_uint8* pIn, size_t sampleCount);
1163/*
1164Reads a chunk of audio data and converts it to IEEE 32-bit floating point samples.
1166pBufferOut can be NULL in which case a seek will be performed.
1168Returns the number of PCM frames actually read.
1170If the return value is less than <framesToRead> it means the end of the file has been reached.
1171*/
1172DRWAV_API drwav_uint64 drwav_read_pcm_frames_f32(drwav* pWav, drwav_uint64 framesToRead, float* pBufferOut);
1173DRWAV_API drwav_uint64 drwav_read_pcm_frames_f32le(drwav* pWav, drwav_uint64 framesToRead, float* pBufferOut);
1174DRWAV_API drwav_uint64 drwav_read_pcm_frames_f32be(drwav* pWav, drwav_uint64 framesToRead, float* pBufferOut);
1176/* Low-level function for converting unsigned 8-bit PCM samples to IEEE 32-bit floating point samples. */
1177DRWAV_API void drwav_u8_to_f32(float* pOut, const drwav_uint8* pIn, size_t sampleCount);
1179/* Low-level function for converting signed 16-bit PCM samples to IEEE 32-bit floating point samples. */
1180DRWAV_API void drwav_s16_to_f32(float* pOut, const drwav_int16* pIn, size_t sampleCount);
1182/* Low-level function for converting signed 24-bit PCM samples to IEEE 32-bit floating point samples. */
1183DRWAV_API void drwav_s24_to_f32(float* pOut, const drwav_uint8* pIn, size_t sampleCount);
1185/* Low-level function for converting signed 32-bit PCM samples to IEEE 32-bit floating point samples. */
1186DRWAV_API void drwav_s32_to_f32(float* pOut, const drwav_int32* pIn, size_t sampleCount);
1188/* Low-level function for converting IEEE 64-bit floating point samples to IEEE 32-bit floating point samples. */
1189DRWAV_API void drwav_f64_to_f32(float* pOut, const double* pIn, size_t sampleCount);
1191/* Low-level function for converting A-law samples to IEEE 32-bit floating point samples. */
1192DRWAV_API void drwav_alaw_to_f32(float* pOut, const drwav_uint8* pIn, size_t sampleCount);
1194/* Low-level function for converting u-law samples to IEEE 32-bit floating point samples. */
1195DRWAV_API void drwav_mulaw_to_f32(float* pOut, const drwav_uint8* pIn, size_t sampleCount);
1198/*
1199Reads a chunk of audio data and converts it to signed 32-bit PCM samples.
1201pBufferOut can be NULL in which case a seek will be performed.
1203Returns the number of PCM frames actually read.
1205If the return value is less than <framesToRead> it means the end of the file has been reached.
1206*/
1207DRWAV_API drwav_uint64 drwav_read_pcm_frames_s32(drwav* pWav, drwav_uint64 framesToRead, drwav_int32* pBufferOut);
1208DRWAV_API drwav_uint64 drwav_read_pcm_frames_s32le(drwav* pWav, drwav_uint64 framesToRead, drwav_int32* pBufferOut);
1209DRWAV_API drwav_uint64 drwav_read_pcm_frames_s32be(drwav* pWav, drwav_uint64 framesToRead, drwav_int32* pBufferOut);
1211/* Low-level function for converting unsigned 8-bit PCM samples to signed 32-bit PCM samples. */
1212DRWAV_API void drwav_u8_to_s32(drwav_int32* pOut, const drwav_uint8* pIn, size_t sampleCount);
1214/* Low-level function for converting signed 16-bit PCM samples to signed 32-bit PCM samples. */
1215DRWAV_API void drwav_s16_to_s32(drwav_int32* pOut, const drwav_int16* pIn, size_t sampleCount);
1217/* Low-level function for converting signed 24-bit PCM samples to signed 32-bit PCM samples. */
1218DRWAV_API void drwav_s24_to_s32(drwav_int32* pOut, const drwav_uint8* pIn, size_t sampleCount);
1220/* Low-level function for converting IEEE 32-bit floating point samples to signed 32-bit PCM samples. */
1221DRWAV_API void drwav_f32_to_s32(drwav_int32* pOut, const float* pIn, size_t sampleCount);
1223/* Low-level function for converting IEEE 64-bit floating point samples to signed 32-bit PCM samples. */
1224DRWAV_API void drwav_f64_to_s32(drwav_int32* pOut, const double* pIn, size_t sampleCount);
1226/* Low-level function for converting A-law samples to signed 32-bit PCM samples. */
1227DRWAV_API void drwav_alaw_to_s32(drwav_int32* pOut, const drwav_uint8* pIn, size_t sampleCount);
1229/* Low-level function for converting u-law samples to signed 32-bit PCM samples. */
1230DRWAV_API void drwav_mulaw_to_s32(drwav_int32* pOut, const drwav_uint8* pIn, size_t sampleCount);
1232#endif /* DR_WAV_NO_CONVERSION_API */
1235/* High-Level Convenience Helpers */
1237#ifndef DR_WAV_NO_STDIO
1238/*
1239Helper for initializing a wave file for reading using stdio.
1241This holds the internal FILE object until drwav_uninit() is called. Keep this in mind if you're caching drwav
1242objects because the operating system may restrict the number of file handles an application can have open at
1243any given time.
1244*/
1245DRWAV_API drwav_bool32 drwav_init_file(drwav* pWav, const char* filename, const drwav_allocation_callbacks* pAllocationCallbacks);
1246DRWAV_API drwav_bool32 drwav_init_file_ex(drwav* pWav, const char* filename, drwav_chunk_proc onChunk, void* pChunkUserData, drwav_uint32 flags, const drwav_allocation_callbacks* pAllocationCallbacks);
1247DRWAV_API drwav_bool32 drwav_init_file_w(drwav* pWav, const wchar_t* filename, const drwav_allocation_callbacks* pAllocationCallbacks);
1248DRWAV_API drwav_bool32 drwav_init_file_ex_w(drwav* pWav, const wchar_t* filename, drwav_chunk_proc onChunk, void* pChunkUserData, drwav_uint32 flags, const drwav_allocation_callbacks* pAllocationCallbacks);
1249DRWAV_API drwav_bool32 drwav_init_file_with_metadata(drwav* pWav, const char* filename, drwav_uint32 flags, const drwav_allocation_callbacks* pAllocationCallbacks);
1250DRWAV_API drwav_bool32 drwav_init_file_with_metadata_w(drwav* pWav, const wchar_t* filename, drwav_uint32 flags, const drwav_allocation_callbacks* pAllocationCallbacks);
1253/*
1254Helper for initializing a wave file for writing using stdio.
1256This holds the internal FILE object until drwav_uninit() is called. Keep this in mind if you're caching drwav
1257objects because the operating system may restrict the number of file handles an application can have open at
1258any given time.
1259*/
1260DRWAV_API drwav_bool32 drwav_init_file_write(drwav* pWav, const char* filename, const drwav_data_format* pFormat, const drwav_allocation_callbacks* pAllocationCallbacks);
1261DRWAV_API drwav_bool32 drwav_init_file_write_sequential(drwav* pWav, const char* filename, const drwav_data_format* pFormat, drwav_uint64 totalSampleCount, const drwav_allocation_callbacks* pAllocationCallbacks);
1262DRWAV_API drwav_bool32 drwav_init_file_write_sequential_pcm_frames(drwav* pWav, const char* filename, const drwav_data_format* pFormat, drwav_uint64 totalPCMFrameCount, const drwav_allocation_callbacks* pAllocationCallbacks);
1263DRWAV_API drwav_bool32 drwav_init_file_write_w(drwav* pWav, const wchar_t* filename, const drwav_data_format* pFormat, const drwav_allocation_callbacks* pAllocationCallbacks);
1264DRWAV_API drwav_bool32 drwav_init_file_write_sequential_w(drwav* pWav, const wchar_t* filename, const drwav_data_format* pFormat, drwav_uint64 totalSampleCount, const drwav_allocation_callbacks* pAllocationCallbacks);
1265DRWAV_API drwav_bool32 drwav_init_file_write_sequential_pcm_frames_w(drwav* pWav, const wchar_t* filename, const drwav_data_format* pFormat, drwav_uint64 totalPCMFrameCount, const drwav_allocation_callbacks* pAllocationCallbacks);
1266#endif /* DR_WAV_NO_STDIO */
1268/*
1269Helper for initializing a loader from a pre-allocated memory buffer.
1271This does not create a copy of the data. It is up to the application to ensure the buffer remains valid for
1272the lifetime of the drwav object.
1274The buffer should contain the contents of the entire wave file, not just the sample data.
1275*/
1276DRWAV_API drwav_bool32 drwav_init_memory(drwav* pWav, const void* data, size_t dataSize, const drwav_allocation_callbacks* pAllocationCallbacks);
1277DRWAV_API drwav_bool32 drwav_init_memory_ex(drwav* pWav, const void* data, size_t dataSize, drwav_chunk_proc onChunk, void* pChunkUserData, drwav_uint32 flags, const drwav_allocation_callbacks* pAllocationCallbacks);
1278DRWAV_API drwav_bool32 drwav_init_memory_with_metadata(drwav* pWav, const void* data, size_t dataSize, drwav_uint32 flags, const drwav_allocation_callbacks* pAllocationCallbacks);
1280/*
1281Helper for initializing a writer which outputs data to a memory buffer.
1283dr_wav will manage the memory allocations, however it is up to the caller to free the data with drwav_free().
1285The buffer will remain allocated even after drwav_uninit() is called. The buffer should not be considered valid
1286until after drwav_uninit() has been called.
1287*/
1288DRWAV_API drwav_bool32 drwav_init_memory_write(drwav* pWav, void** ppData, size_t* pDataSize, const drwav_data_format* pFormat, const drwav_allocation_callbacks* pAllocationCallbacks);
1289DRWAV_API drwav_bool32 drwav_init_memory_write_sequential(drwav* pWav, void** ppData, size_t* pDataSize, const drwav_data_format* pFormat, drwav_uint64 totalSampleCount, const drwav_allocation_callbacks* pAllocationCallbacks);
1290DRWAV_API drwav_bool32 drwav_init_memory_write_sequential_pcm_frames(drwav* pWav, void** ppData, size_t* pDataSize, const drwav_data_format* pFormat, drwav_uint64 totalPCMFrameCount, const drwav_allocation_callbacks* pAllocationCallbacks);
1293#ifndef DR_WAV_NO_CONVERSION_API
1294/*
1295Opens and reads an entire wav file in a single operation.
1297The return value is a heap-allocated buffer containing the audio data. Use drwav_free() to free the buffer.
1298*/
1299DRWAV_API drwav_int16* drwav_open_and_read_pcm_frames_s16(drwav_read_proc onRead, drwav_seek_proc onSeek, drwav_tell_proc onTell, void* pUserData, unsigned int* channelsOut, unsigned int* sampleRateOut, drwav_uint64* totalFrameCountOut, const drwav_allocation_callbacks* pAllocationCallbacks);
1300DRWAV_API float* drwav_open_and_read_pcm_frames_f32(drwav_read_proc onRead, drwav_seek_proc onSeek, drwav_tell_proc onTell, void* pUserData, unsigned int* channelsOut, unsigned int* sampleRateOut, drwav_uint64* totalFrameCountOut, const drwav_allocation_callbacks* pAllocationCallbacks);
1301DRWAV_API drwav_int32* drwav_open_and_read_pcm_frames_s32(drwav_read_proc onRead, drwav_seek_proc onSeek, drwav_tell_proc onTell, void* pUserData, unsigned int* channelsOut, unsigned int* sampleRateOut, drwav_uint64* totalFrameCountOut, const drwav_allocation_callbacks* pAllocationCallbacks);
1302#ifndef DR_WAV_NO_STDIO
1303/*
1304Opens and decodes an entire wav file in a single operation.
1306The return value is a heap-allocated buffer containing the audio data. Use drwav_free() to free the buffer.
1307*/
1308DRWAV_API drwav_int16* drwav_open_file_and_read_pcm_frames_s16(const char* filename, unsigned int* channelsOut, unsigned int* sampleRateOut, drwav_uint64* totalFrameCountOut, const drwav_allocation_callbacks* pAllocationCallbacks);
1309DRWAV_API float* drwav_open_file_and_read_pcm_frames_f32(const char* filename, unsigned int* channelsOut, unsigned int* sampleRateOut, drwav_uint64* totalFrameCountOut, const drwav_allocation_callbacks* pAllocationCallbacks);
1310DRWAV_API drwav_int32* drwav_open_file_and_read_pcm_frames_s32(const char* filename, unsigned int* channelsOut, unsigned int* sampleRateOut, drwav_uint64* totalFrameCountOut, const drwav_allocation_callbacks* pAllocationCallbacks);
1311DRWAV_API drwav_int16* drwav_open_file_and_read_pcm_frames_s16_w(const wchar_t* filename, unsigned int* channelsOut, unsigned int* sampleRateOut, drwav_uint64* totalFrameCountOut, const drwav_allocation_callbacks* pAllocationCallbacks);
1312DRWAV_API float* drwav_open_file_and_read_pcm_frames_f32_w(const wchar_t* filename, unsigned int* channelsOut, unsigned int* sampleRateOut, drwav_uint64* totalFrameCountOut, const drwav_allocation_callbacks* pAllocationCallbacks);
1313DRWAV_API drwav_int32* drwav_open_file_and_read_pcm_frames_s32_w(const wchar_t* filename, unsigned int* channelsOut, unsigned int* sampleRateOut, drwav_uint64* totalFrameCountOut, const drwav_allocation_callbacks* pAllocationCallbacks);
1314#endif
1315/*
1316Opens and decodes an entire wav file from a block of memory in a single operation.
1318The return value is a heap-allocated buffer containing the audio data. Use drwav_free() to free the buffer.
1319*/
1320DRWAV_API drwav_int16* drwav_open_memory_and_read_pcm_frames_s16(const void* data, size_t dataSize, unsigned int* channelsOut, unsigned int* sampleRateOut, drwav_uint64* totalFrameCountOut, const drwav_allocation_callbacks* pAllocationCallbacks);
1321DRWAV_API float* drwav_open_memory_and_read_pcm_frames_f32(const void* data, size_t dataSize, unsigned int* channelsOut, unsigned int* sampleRateOut, drwav_uint64* totalFrameCountOut, const drwav_allocation_callbacks* pAllocationCallbacks);
1322DRWAV_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, const drwav_allocation_callbacks* pAllocationCallbacks);
1323#endif
1325/* Frees data that was allocated internally by dr_wav. */
1326DRWAV_API void drwav_free(void* p, const drwav_allocation_callbacks* pAllocationCallbacks);
1328/* Converts bytes from a wav stream to a sized type of native endian. */
1329DRWAV_API drwav_uint16 drwav_bytes_to_u16(const drwav_uint8* data);
1330DRWAV_API drwav_int16 drwav_bytes_to_s16(const drwav_uint8* data);
1331DRWAV_API drwav_uint32 drwav_bytes_to_u32(const drwav_uint8* data);
1332DRWAV_API drwav_int32 drwav_bytes_to_s32(const drwav_uint8* data);
1333DRWAV_API drwav_uint64 drwav_bytes_to_u64(const drwav_uint8* data);
1334DRWAV_API drwav_int64 drwav_bytes_to_s64(const drwav_uint8* data);
1335DRWAV_API float drwav_bytes_to_f32(const drwav_uint8* data);
1337/* Compares a GUID for the purpose of checking the type of a Wave64 chunk. */
1338DRWAV_API drwav_bool32 drwav_guid_equal(const drwav_uint8 a[16], const drwav_uint8 b[16]);
1340/* Compares a four-character-code for the purpose of checking the type of a RIFF chunk. */
1341DRWAV_API drwav_bool32 drwav_fourcc_equal(const drwav_uint8* a, const char* b);
1343#ifdef __cplusplus
1344}
1345#endif
1346#endif /* dr_wav_h */
1349/************************************************************************************************************************************************************
1350 ************************************************************************************************************************************************************
1352 IMPLEMENTATION
1354 ************************************************************************************************************************************************************
1355 ************************************************************************************************************************************************************/
1356#if defined(DR_WAV_IMPLEMENTATION) || defined(DRWAV_IMPLEMENTATION)
1357#ifndef dr_wav_c
1358#define dr_wav_c
1360#ifdef __MRC__
1361/* MrC currently doesn't compile dr_wav correctly with any optimizations enabled. */
1362#pragma options opt off
1363#endif
1365#include <stdlib.h>
1366#include <string.h>
1367#include <limits.h> /* For INT_MAX */
1369#ifndef DR_WAV_NO_STDIO
1370#include <stdio.h>
1371#ifndef DR_WAV_NO_WCHAR
1372#include <wchar.h>
1373#endif
1374#endif
1376/* Standard library stuff. */
1377#ifndef DRWAV_ASSERT
1378#include <assert.h>
1379#define DRWAV_ASSERT(expression) assert(expression)
1380#endif
1381#ifndef DRWAV_MALLOC
1382#define DRWAV_MALLOC(sz) malloc((sz))
1383#endif
1384#ifndef DRWAV_REALLOC
1385#define DRWAV_REALLOC(p, sz) realloc((p), (sz))
1386#endif
1387#ifndef DRWAV_FREE
1388#define DRWAV_FREE(p) free((p))
1389#endif
1390#ifndef DRWAV_COPY_MEMORY
1391#define DRWAV_COPY_MEMORY(dst, src, sz) memcpy((dst), (src), (sz))
1392#endif
1393#ifndef DRWAV_ZERO_MEMORY
1394#define DRWAV_ZERO_MEMORY(p, sz) memset((p), 0, (sz))
1395#endif
1396#ifndef DRWAV_ZERO_OBJECT
1397#define DRWAV_ZERO_OBJECT(p) DRWAV_ZERO_MEMORY((p), sizeof(*p))
1398#endif
1400#define drwav_countof(x) (sizeof(x) / sizeof(x[0]))
1401#define drwav_align(x, a) ((((x) + (a) - 1) / (a)) * (a))
1402#define drwav_min(a, b) (((a) < (b)) ? (a) : (b))
1403#define drwav_max(a, b) (((a) > (b)) ? (a) : (b))
1404#define drwav_clamp(x, lo, hi) (drwav_max((lo), drwav_min((hi), (x))))
1405#define drwav_offset_ptr(p, offset) (((drwav_uint8*)(p)) + (offset))
1407#define DRWAV_MAX_SIMD_VECTOR_SIZE 32
1409/* Architecture Detection */
1410#if defined(__x86_64__) || (defined(_M_X64) && !defined(_M_ARM64EC))
1411 #define DRWAV_X64
1412#elif defined(__i386) || defined(_M_IX86)
1413 #define DRWAV_X86
1414#elif defined(__arm__) || defined(_M_ARM)
1415 #define DRWAV_ARM
1416#endif
1417/* End Architecture Detection */
1419/* Inline */
1420#ifdef _MSC_VER
1421 #define DRWAV_INLINE __forceinline
1422#elif defined(__GNUC__)
1423 /*
1424 I've had a bug report where GCC is emitting warnings about functions possibly not being inlineable. This warning happens when
1425 the __attribute__((always_inline)) attribute is defined without an "inline" statement. I think therefore there must be some
1426 case where "__inline__" is not always defined, thus the compiler emitting these warnings. When using -std=c89 or -ansi on the
1427 command line, we cannot use the "inline" keyword and instead need to use "__inline__". In an attempt to work around this issue
1428 I am using "__inline__" only when we're compiling in strict ANSI mode.
1429 */
1430 #if defined(__STRICT_ANSI__)
1431 #define DRWAV_GNUC_INLINE_HINT __inline__
1432 #else
1433 #define DRWAV_GNUC_INLINE_HINT inline
1434 #endif
1436 #if (__GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 2)) || defined(__clang__)
1437 #define DRWAV_INLINE DRWAV_GNUC_INLINE_HINT __attribute__((always_inline))
1438 #else
1439 #define DRWAV_INLINE DRWAV_GNUC_INLINE_HINT
1440 #endif
1441#elif defined(__WATCOMC__)
1442 #define DRWAV_INLINE __inline
1443#else
1444 #define DRWAV_INLINE
1445#endif
1446/* End Inline */
1448/* SIZE_MAX */
1449#if defined(SIZE_MAX)
1450 #define DRWAV_SIZE_MAX SIZE_MAX
1451#else
1452 #if defined(_WIN64) || defined(_LP64) || defined(__LP64__)
1453 #define DRWAV_SIZE_MAX ((drwav_uint64)0xFFFFFFFFFFFFFFFF)
1454 #else
1455 #define DRWAV_SIZE_MAX 0xFFFFFFFF
1456 #endif
1457#endif
1458/* End SIZE_MAX */
1460/* Weird bit manipulation is for C89 compatibility (no direct support for 64-bit integers). */
1461#define DRWAV_INT64_MIN ((drwav_int64) ((drwav_uint64)0x80000000 << 32))
1462#define DRWAV_INT64_MAX ((drwav_int64)(((drwav_uint64)0x7FFFFFFF << 32) | 0xFFFFFFFF))
1464#if defined(_MSC_VER) && _MSC_VER >= 1400
1465 #define DRWAV_HAS_BYTESWAP16_INTRINSIC
1466 #define DRWAV_HAS_BYTESWAP32_INTRINSIC
1467 #define DRWAV_HAS_BYTESWAP64_INTRINSIC
1468#elif defined(__clang__)
1469 #if defined(__has_builtin)
1470 #if __has_builtin(__builtin_bswap16)
1471 #define DRWAV_HAS_BYTESWAP16_INTRINSIC
1472 #endif
1473 #if __has_builtin(__builtin_bswap32)
1474 #define DRWAV_HAS_BYTESWAP32_INTRINSIC
1475 #endif
1476 #if __has_builtin(__builtin_bswap64)
1477 #define DRWAV_HAS_BYTESWAP64_INTRINSIC
1478 #endif
1479 #endif
1480#elif defined(__GNUC__)
1481 #if ((__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 3))
1482 #define DRWAV_HAS_BYTESWAP32_INTRINSIC
1483 #define DRWAV_HAS_BYTESWAP64_INTRINSIC
1484 #endif
1485 #if ((__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8))
1486 #define DRWAV_HAS_BYTESWAP16_INTRINSIC
1487 #endif
1488#endif
1490DRWAV_API void drwav_version(drwav_uint32* pMajor, drwav_uint32* pMinor, drwav_uint32* pRevision)
1491{
1492 if (pMajor) {
1493 *pMajor = DRWAV_VERSION_MAJOR;
1494 }
1496 if (pMinor) {
1497 *pMinor = DRWAV_VERSION_MINOR;
1498 }
1500 if (pRevision) {
1501 *pRevision = DRWAV_VERSION_REVISION;
1502 }
1503}
1505DRWAV_API const char* drwav_version_string(void)
1506{
1507 return DRWAV_VERSION_STRING;
1508}
1510/*
1511These limits are used for basic validation when initializing the decoder. If you exceed these limits, first of all: what on Earth are
1512you doing?! (Let me know, I'd be curious!) Second, you can adjust these by #define-ing them before the dr_wav implementation.
1513*/
1514#ifndef DRWAV_MAX_SAMPLE_RATE
1515#define DRWAV_MAX_SAMPLE_RATE 384000
1516#endif
1517#ifndef DRWAV_MAX_CHANNELS
1518#define DRWAV_MAX_CHANNELS 256
1519#endif
1520#ifndef DRWAV_MAX_BITS_PER_SAMPLE
1521#define DRWAV_MAX_BITS_PER_SAMPLE 64
1522#endif
1524static 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 */
1525static 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 */
1526/*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 */
1527static 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 */
1528static 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 */
1529static 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 */
1530/*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 */
1533static DRWAV_INLINE int drwav__is_little_endian(void)
1534{
1535#if defined(DRWAV_X86) || defined(DRWAV_X64)
1536 return DRWAV_TRUE;
1537#elif defined(__BYTE_ORDER) && defined(__LITTLE_ENDIAN) && __BYTE_ORDER == __LITTLE_ENDIAN
1538 return DRWAV_TRUE;
1539#else
1540 int n = 1;
1541 return (*(char*)&n) == 1;
1542#endif
1543}
1546static DRWAV_INLINE void drwav_bytes_to_guid(const drwav_uint8* data, drwav_uint8* guid)
1547{
1548 int i;
1549 for (i = 0; i < 16; ++i) {
1550 guid[i] = data[i];
1551 }
1552}
1555static DRWAV_INLINE drwav_uint16 drwav__bswap16(drwav_uint16 n)
1556{
1557#ifdef DRWAV_HAS_BYTESWAP16_INTRINSIC
1558 #if defined(_MSC_VER)
1559 return _byteswap_ushort(n);
1560 #elif defined(__GNUC__) || defined(__clang__)
1561 return __builtin_bswap16(n);
1562 #else
1563 #error "This compiler does not support the byte swap intrinsic."
1564 #endif
1565#else
1566 return ((n & 0xFF00) >> 8) |
1567 ((n & 0x00FF) << 8);
1568#endif
1569}
1571static DRWAV_INLINE drwav_uint32 drwav__bswap32(drwav_uint32 n)
1572{
1573#ifdef DRWAV_HAS_BYTESWAP32_INTRINSIC
1574 #if defined(_MSC_VER)
1575 return _byteswap_ulong(n);
1576 #elif defined(__GNUC__) || defined(__clang__)
1577 #if defined(DRWAV_ARM) && (defined(__ARM_ARCH) && __ARM_ARCH >= 6) && !defined(DRWAV_64BIT) /* <-- 64-bit inline assembly has not been tested, so disabling for now. */
1578 /* Inline assembly optimized implementation for ARM. In my testing, GCC does not generate optimized code with __builtin_bswap32(). */
1579 drwav_uint32 r;
1580 __asm__ __volatile__ (
1581 #if defined(DRWAV_64BIT)
1582 "rev %w[out], %w[in]" : [out]"=r"(r) : [in]"r"(n) /* <-- This is untested. If someone in the community could test this, that would be appreciated! */
1583 #else
1584 "rev %[out], %[in]" : [out]"=r"(r) : [in]"r"(n)
1585 #endif
1586 );
1587 return r;
1588 #else
1589 return __builtin_bswap32(n);
1590 #endif
1591 #else
1592 #error "This compiler does not support the byte swap intrinsic."
1593 #endif
1594#else
1595 return ((n & 0xFF000000) >> 24) |
1596 ((n & 0x00FF0000) >> 8) |
1597 ((n & 0x0000FF00) << 8) |
1598 ((n & 0x000000FF) << 24);
1599#endif
1600}
1602static DRWAV_INLINE drwav_uint64 drwav__bswap64(drwav_uint64 n)
1603{
1604#ifdef DRWAV_HAS_BYTESWAP64_INTRINSIC
1605 #if defined(_MSC_VER)
1606 return _byteswap_uint64(n);
1607 #elif defined(__GNUC__) || defined(__clang__)
1608 return __builtin_bswap64(n);
1609 #else
1610 #error "This compiler does not support the byte swap intrinsic."
1611 #endif
1612#else
1613 /* Weird "<< 32" bitshift is required for C89 because it doesn't support 64-bit constants. Should be optimized out by a good compiler. */
1614 return ((n & ((drwav_uint64)0xFF000000 << 32)) >> 56) |
1615 ((n & ((drwav_uint64)0x00FF0000 << 32)) >> 40) |
1616 ((n & ((drwav_uint64)0x0000FF00 << 32)) >> 24) |
1617 ((n & ((drwav_uint64)0x000000FF << 32)) >> 8) |
1618 ((n & ((drwav_uint64)0xFF000000 )) << 8) |
1619 ((n & ((drwav_uint64)0x00FF0000 )) << 24) |
1620 ((n & ((drwav_uint64)0x0000FF00 )) << 40) |
1621 ((n & ((drwav_uint64)0x000000FF )) << 56);
1622#endif
1623}
1626static DRWAV_INLINE drwav_int16 drwav__bswap_s16(drwav_int16 n)
1627{
1628 return (drwav_int16)drwav__bswap16((drwav_uint16)n);
1629}
1631static DRWAV_INLINE void drwav__bswap_samples_s16(drwav_int16* pSamples, drwav_uint64 sampleCount)
1632{
1633 drwav_uint64 iSample;
1634 for (iSample = 0; iSample < sampleCount; iSample += 1) {
1635 pSamples[iSample] = drwav__bswap_s16(pSamples[iSample]);
1636 }
1637}
1640static DRWAV_INLINE void drwav__bswap_s24(drwav_uint8* p)
1641{
1642 drwav_uint8 t;
1643 t = p[0];
1644 p[0] = p[2];
1645 p[2] = t;
1646}
1648static DRWAV_INLINE void drwav__bswap_samples_s24(drwav_uint8* pSamples, drwav_uint64 sampleCount)
1649{
1650 drwav_uint64 iSample;
1651 for (iSample = 0; iSample < sampleCount; iSample += 1) {
1652 drwav_uint8* pSample = pSamples + (iSample*3);
1653 drwav__bswap_s24(pSample);
1654 }
1655}
1658static DRWAV_INLINE drwav_int32 drwav__bswap_s32(drwav_int32 n)
1659{
1660 return (drwav_int32)drwav__bswap32((drwav_uint32)n);
1661}
1663static DRWAV_INLINE void drwav__bswap_samples_s32(drwav_int32* pSamples, drwav_uint64 sampleCount)
1664{
1665 drwav_uint64 iSample;
1666 for (iSample = 0; iSample < sampleCount; iSample += 1) {
1667 pSamples[iSample] = drwav__bswap_s32(pSamples[iSample]);
1668 }
1669}
1672static DRWAV_INLINE drwav_int64 drwav__bswap_s64(drwav_int64 n)
1673{
1674 return (drwav_int64)drwav__bswap64((drwav_uint64)n);
1675}
1677static DRWAV_INLINE void drwav__bswap_samples_s64(drwav_int64* pSamples, drwav_uint64 sampleCount)
1678{
1679 drwav_uint64 iSample;
1680 for (iSample = 0; iSample < sampleCount; iSample += 1) {
1681 pSamples[iSample] = drwav__bswap_s64(pSamples[iSample]);
1682 }
1683}
1686static DRWAV_INLINE float drwav__bswap_f32(float n)
1687{
1688 union {
1689 drwav_uint32 i;
1690 float f;
1691 } x;
1692 x.f = n;
1693 x.i = drwav__bswap32(x.i);
1695 return x.f;
1696}
1698static DRWAV_INLINE void drwav__bswap_samples_f32(float* pSamples, drwav_uint64 sampleCount)
1699{
1700 drwav_uint64 iSample;
1701 for (iSample = 0; iSample < sampleCount; iSample += 1) {
1702 pSamples[iSample] = drwav__bswap_f32(pSamples[iSample]);
1703 }
1704}
1707static DRWAV_INLINE void drwav__bswap_samples(void* pSamples, drwav_uint64 sampleCount, drwav_uint32 bytesPerSample)
1708{
1709 switch (bytesPerSample)
1710 {
1711 case 1:
1712 {
1713 /* No-op. */
1714 } break;
1715 case 2:
1716 {
1717 drwav__bswap_samples_s16((drwav_int16*)pSamples, sampleCount);
1718 } break;
1719 case 3:
1720 {
1721 drwav__bswap_samples_s24((drwav_uint8*)pSamples, sampleCount);
1722 } break;
1723 case 4:
1724 {
1725 drwav__bswap_samples_s32((drwav_int32*)pSamples, sampleCount);
1726 } break;
1727 case 8:
1728 {
1729 drwav__bswap_samples_s64((drwav_int64*)pSamples, sampleCount);
1730 } break;
1731 default:
1732 {
1733 /* Unsupported format. */
1734 DRWAV_ASSERT(DRWAV_FALSE);
1735 } break;
1736 }
1737}
1741DRWAV_PRIVATE DRWAV_INLINE drwav_bool32 drwav_is_container_be(drwav_container container)
1742{
1743 if (container == drwav_container_rifx || container == drwav_container_aiff) {
1744 return DRWAV_TRUE;
1745 } else {
1746 return DRWAV_FALSE;
1747 }
1748}
1751DRWAV_PRIVATE DRWAV_INLINE drwav_uint16 drwav_bytes_to_u16_le(const drwav_uint8* data)
1752{
1753 return ((drwav_uint16)data[0] << 0) | ((drwav_uint16)data[1] << 8);
1754}
1756DRWAV_PRIVATE DRWAV_INLINE drwav_uint16 drwav_bytes_to_u16_be(const drwav_uint8* data)
1757{
1758 return ((drwav_uint16)data[1] << 0) | ((drwav_uint16)data[0] << 8);
1759}
1761DRWAV_PRIVATE DRWAV_INLINE drwav_uint16 drwav_bytes_to_u16_ex(const drwav_uint8* data, drwav_container container)
1762{
1763 if (drwav_is_container_be(container)) {
1764 return drwav_bytes_to_u16_be(data);
1765 } else {
1766 return drwav_bytes_to_u16_le(data);
1767 }
1768}
1771DRWAV_PRIVATE DRWAV_INLINE drwav_uint32 drwav_bytes_to_u32_le(const drwav_uint8* data)
1772{
1773 return ((drwav_uint32)data[0] << 0) | ((drwav_uint32)data[1] << 8) | ((drwav_uint32)data[2] << 16) | ((drwav_uint32)data[3] << 24);
1774}
1776DRWAV_PRIVATE DRWAV_INLINE drwav_uint32 drwav_bytes_to_u32_be(const drwav_uint8* data)
1777{
1778 return ((drwav_uint32)data[3] << 0) | ((drwav_uint32)data[2] << 8) | ((drwav_uint32)data[1] << 16) | ((drwav_uint32)data[0] << 24);
1779}
1781DRWAV_PRIVATE DRWAV_INLINE drwav_uint32 drwav_bytes_to_u32_ex(const drwav_uint8* data, drwav_container container)
1782{
1783 if (drwav_is_container_be(container)) {
1784 return drwav_bytes_to_u32_be(data);
1785 } else {
1786 return drwav_bytes_to_u32_le(data);
1787 }
1788}
1792DRWAV_PRIVATE drwav_int64 drwav_aiff_extented_to_s64(const drwav_uint8* data)
1793{
1794 drwav_uint32 exponent = ((drwav_uint32)data[0] << 8) | data[1];
1795 drwav_uint64 hi = ((drwav_uint64)data[2] << 24) | ((drwav_uint64)data[3] << 16) | ((drwav_uint64)data[4] << 8) | ((drwav_uint64)data[5] << 0);
1796 drwav_uint64 lo = ((drwav_uint64)data[6] << 24) | ((drwav_uint64)data[7] << 16) | ((drwav_uint64)data[8] << 8) | ((drwav_uint64)data[9] << 0);
1797 drwav_uint64 significand = (hi << 32) | lo;
1798 int sign = exponent >> 15;
1800 /* Remove sign bit. */
1801 exponent &= 0x7FFF;
1803 /* Special cases. */
1804 if (exponent == 0 && significand == 0) {
1805 return 0;
1806 } else if (exponent == 0x7FFF) {
1807 return sign ? DRWAV_INT64_MIN : DRWAV_INT64_MAX; /* Infinite. */
1808 }
1810 exponent -= 16383;
1812 if (exponent > 63) {
1813 return sign ? DRWAV_INT64_MIN : DRWAV_INT64_MAX; /* Too big for a 64-bit integer. */
1814 } else if (exponent < 1) {
1815 return 0; /* Number is less than 1, so rounds down to 0. */
1816 }
1818 significand >>= (63 - exponent);
1820 if (sign) {
1821 return -(drwav_int64)significand;
1822 } else {
1823 return (drwav_int64)significand;
1824 }
1825}
1828DRWAV_PRIVATE void* drwav__malloc_default(size_t sz, void* pUserData)
1829{
1830 (void)pUserData;
1831 return DRWAV_MALLOC(sz);
1832}
1834DRWAV_PRIVATE void* drwav__realloc_default(void* p, size_t sz, void* pUserData)
1835{
1836 (void)pUserData;
1837 return DRWAV_REALLOC(p, sz);
1838}
1840DRWAV_PRIVATE void drwav__free_default(void* p, void* pUserData)
1841{
1842 (void)pUserData;
1843 DRWAV_FREE(p);
1844}
1847DRWAV_PRIVATE void* drwav__malloc_from_callbacks(size_t sz, const drwav_allocation_callbacks* pAllocationCallbacks)
1848{
1849 if (pAllocationCallbacks == NULL) {
1850 return NULL;
1851 }
1853 if (pAllocationCallbacks->onMalloc != NULL) {
1854 return pAllocationCallbacks->onMalloc(sz, pAllocationCallbacks->pUserData);
1855 }
1857 /* Try using realloc(). */
1858 if (pAllocationCallbacks->onRealloc != NULL) {
1859 return pAllocationCallbacks->onRealloc(NULL, sz, pAllocationCallbacks->pUserData);
1860 }
1862 return NULL;
1863}
1865DRWAV_PRIVATE void* drwav__realloc_from_callbacks(void* p, size_t szNew, size_t szOld, const drwav_allocation_callbacks* pAllocationCallbacks)
1866{
1867 if (pAllocationCallbacks == NULL) {
1868 return NULL;
1869 }
1871 if (pAllocationCallbacks->onRealloc != NULL) {
1872 return pAllocationCallbacks->onRealloc(p, szNew, pAllocationCallbacks->pUserData);
1873 }
1875 /* Try emulating realloc() in terms of malloc()/free(). */
1876 if (pAllocationCallbacks->onMalloc != NULL && pAllocationCallbacks->onFree != NULL) {
1877 void* p2;
1879 p2 = pAllocationCallbacks->onMalloc(szNew, pAllocationCallbacks->pUserData);
1880 if (p2 == NULL) {
1881 return NULL;
1882 }
1884 if (p != NULL) {
1885 DRWAV_COPY_MEMORY(p2, p, szOld);
1886 pAllocationCallbacks->onFree(p, pAllocationCallbacks->pUserData);
1887 }
1889 return p2;
1890 }
1892 return NULL;
1893}
1895DRWAV_PRIVATE void drwav__free_from_callbacks(void* p, const drwav_allocation_callbacks* pAllocationCallbacks)
1896{
1897 if (p == NULL || pAllocationCallbacks == NULL) {
1898 return;
1899 }
1901 if (pAllocationCallbacks->onFree != NULL) {
1902 pAllocationCallbacks->onFree(p, pAllocationCallbacks->pUserData);
1903 }
1904}
1907DRWAV_PRIVATE drwav_allocation_callbacks drwav_copy_allocation_callbacks_or_defaults(const drwav_allocation_callbacks* pAllocationCallbacks)
1908{
1909 if (pAllocationCallbacks != NULL) {
1910 /* Copy. */
1911 return *pAllocationCallbacks;
1912 } else {
1913 /* Defaults. */
1914 drwav_allocation_callbacks allocationCallbacks;
1915 allocationCallbacks.pUserData = NULL;
1916 allocationCallbacks.onMalloc = drwav__malloc_default;
1917 allocationCallbacks.onRealloc = drwav__realloc_default;
1918 allocationCallbacks.onFree = drwav__free_default;
1919 return allocationCallbacks;
1920 }
1921}
1924static DRWAV_INLINE drwav_bool32 drwav__is_compressed_format_tag(drwav_uint16 formatTag)
1925{
1926 return
1927 formatTag == DR_WAVE_FORMAT_ADPCM ||
1928 formatTag == DR_WAVE_FORMAT_DVI_ADPCM;
1929}
1931DRWAV_PRIVATE unsigned int drwav__chunk_padding_size_riff(drwav_uint64 chunkSize)
1932{
1933 return (unsigned int)(chunkSize % 2);
1934}
1936DRWAV_PRIVATE unsigned int drwav__chunk_padding_size_w64(drwav_uint64 chunkSize)
1937{
1938 return (unsigned int)(chunkSize % 8);
1939}
1941DRWAV_PRIVATE drwav_uint64 drwav_read_pcm_frames_s16__msadpcm(drwav* pWav, drwav_uint64 samplesToRead, drwav_int16* pBufferOut);
1942DRWAV_PRIVATE drwav_uint64 drwav_read_pcm_frames_s16__ima(drwav* pWav, drwav_uint64 samplesToRead, drwav_int16* pBufferOut);
1943DRWAV_PRIVATE drwav_bool32 drwav_init_write__internal(drwav* pWav, const drwav_data_format* pFormat, drwav_uint64 totalSampleCount);
1945DRWAV_PRIVATE drwav_result drwav__read_chunk_header(drwav_read_proc onRead, void* pUserData, drwav_container container, drwav_uint64* pRunningBytesReadOut, drwav_chunk_header* pHeaderOut)
1946{
1947 if (container == drwav_container_riff || container == drwav_container_rifx || container == drwav_container_rf64 || container == drwav_container_aiff) {
1948 drwav_uint8 sizeInBytes[4];
1950 if (onRead(pUserData, pHeaderOut->id.fourcc, 4) != 4) {
1951 return DRWAV_AT_END;
1952 }
1954 if (onRead(pUserData, sizeInBytes, 4) != 4) {
1955 return DRWAV_INVALID_FILE;
1956 }
1958 pHeaderOut->sizeInBytes = drwav_bytes_to_u32_ex(sizeInBytes, container);
1959 pHeaderOut->paddingSize = drwav__chunk_padding_size_riff(pHeaderOut->sizeInBytes);
1961 *pRunningBytesReadOut += 8;
1962 } else if (container == drwav_container_w64) {
1963 drwav_uint8 sizeInBytes[8];
1965 if (onRead(pUserData, pHeaderOut->id.guid, 16) != 16) {
1966 return DRWAV_AT_END;
1967 }
1969 if (onRead(pUserData, sizeInBytes, 8) != 8) {
1970 return DRWAV_INVALID_FILE;
1971 }
1973 pHeaderOut->sizeInBytes = drwav_bytes_to_u64(sizeInBytes) - 24; /* <-- Subtract 24 because w64 includes the size of the header. */
1974 pHeaderOut->paddingSize = drwav__chunk_padding_size_w64(pHeaderOut->sizeInBytes);
1975 *pRunningBytesReadOut += 24;
1976 } else {
1977 return DRWAV_INVALID_FILE;
1978 }
1980 return DRWAV_SUCCESS;
1981}
1983DRWAV_PRIVATE drwav_bool32 drwav__seek_forward(drwav_seek_proc onSeek, drwav_uint64 offset, void* pUserData)
1984{
1985 drwav_uint64 bytesRemainingToSeek = offset;
1986 while (bytesRemainingToSeek > 0) {
1987 if (bytesRemainingToSeek > 0x7FFFFFFF) {
1988 if (!onSeek(pUserData, 0x7FFFFFFF, DRWAV_SEEK_CUR)) {
1989 return DRWAV_FALSE;
1990 }
1991 bytesRemainingToSeek -= 0x7FFFFFFF;
1992 } else {
1993 if (!onSeek(pUserData, (int)bytesRemainingToSeek, DRWAV_SEEK_CUR)) {
1994 return DRWAV_FALSE;
1995 }
1996 bytesRemainingToSeek = 0;
1997 }
1998 }
2000 return DRWAV_TRUE;
2001}
2003DRWAV_PRIVATE drwav_bool32 drwav__seek_from_start(drwav_seek_proc onSeek, drwav_uint64 offset, void* pUserData)
2004{
2005 if (offset <= 0x7FFFFFFF) {
2006 return onSeek(pUserData, (int)offset, DRWAV_SEEK_SET);
2007 }
2009 /* Larger than 32-bit seek. */
2010 if (!onSeek(pUserData, 0x7FFFFFFF, DRWAV_SEEK_SET)) {
2011 return DRWAV_FALSE;
2012 }
2013 offset -= 0x7FFFFFFF;
2015 for (;;) {
2016 if (offset <= 0x7FFFFFFF) {
2017 return onSeek(pUserData, (int)offset, DRWAV_SEEK_CUR);
2018 }
2020 if (!onSeek(pUserData, 0x7FFFFFFF, DRWAV_SEEK_CUR)) {
2021 return DRWAV_FALSE;
2022 }
2023 offset -= 0x7FFFFFFF;
2024 }
2026 /* Should never get here. */
2027 /*return DRWAV_TRUE; */
2028}
2032DRWAV_PRIVATE size_t drwav__on_read(drwav_read_proc onRead, void* pUserData, void* pBufferOut, size_t bytesToRead, drwav_uint64* pCursor)
2033{
2034 size_t bytesRead;
2036 DRWAV_ASSERT(onRead != NULL);
2037 DRWAV_ASSERT(pCursor != NULL);
2039 bytesRead = onRead(pUserData, pBufferOut, bytesToRead);
2040 *pCursor += bytesRead;
2041 return bytesRead;
2042}
2044#if 0
2045DRWAV_PRIVATE drwav_bool32 drwav__on_seek(drwav_seek_proc onSeek, void* pUserData, int offset, drwav_seek_origin origin, drwav_uint64* pCursor)
2046{
2047 DRWAV_ASSERT(onSeek != NULL);
2048 DRWAV_ASSERT(pCursor != NULL);
2050 if (!onSeek(pUserData, offset, origin)) {
2051 return DRWAV_FALSE;
2052 }
2054 if (origin == DRWAV_SEEK_SET) {
2055 *pCursor = offset;
2056 } else {
2057 *pCursor += offset;
2058 }
2060 return DRWAV_TRUE;
2061}
2062#endif
2065#define DRWAV_SMPL_BYTES 36
2066#define DRWAV_SMPL_LOOP_BYTES 24
2067#define DRWAV_INST_BYTES 7
2068#define DRWAV_ACID_BYTES 24
2069#define DRWAV_CUE_BYTES 4
2070#define DRWAV_BEXT_BYTES 602
2071#define DRWAV_BEXT_DESCRIPTION_BYTES 256
2072#define DRWAV_BEXT_ORIGINATOR_NAME_BYTES 32
2073#define DRWAV_BEXT_ORIGINATOR_REF_BYTES 32
2074#define DRWAV_BEXT_RESERVED_BYTES 180
2075#define DRWAV_BEXT_UMID_BYTES 64
2076#define DRWAV_CUE_POINT_BYTES 24
2077#define DRWAV_LIST_LABEL_OR_NOTE_BYTES 4
2078#define DRWAV_LIST_LABELLED_TEXT_BYTES 20
2080#define DRWAV_METADATA_ALIGNMENT 8
2082typedef enum
2083{
2084 drwav__metadata_parser_stage_count,
2085 drwav__metadata_parser_stage_read
2086} drwav__metadata_parser_stage;
2088typedef struct
2089{
2090 drwav_read_proc onRead;
2091 drwav_seek_proc onSeek;
2092 void *pReadSeekUserData;
2093 drwav__metadata_parser_stage stage;
2094 drwav_metadata *pMetadata;
2095 drwav_uint32 metadataCount;
2096 drwav_uint8 *pData;
2097 drwav_uint8 *pDataCursor;
2098 drwav_uint64 metadataCursor;
2099 drwav_uint64 extraCapacity;
2100} drwav__metadata_parser;
2102DRWAV_PRIVATE size_t drwav__metadata_memory_capacity(drwav__metadata_parser* pParser)
2103{
2104 drwav_uint64 cap = sizeof(drwav_metadata) * (drwav_uint64)pParser->metadataCount + pParser->extraCapacity;
2105 if (cap > DRWAV_SIZE_MAX) {
2106 return 0; /* Too big. */
2107 }
2109 return (size_t)cap; /* Safe cast thanks to the check above. */
2110}
2112DRWAV_PRIVATE drwav_uint8* drwav__metadata_get_memory(drwav__metadata_parser* pParser, size_t size, size_t align)
2113{
2114 drwav_uint8* pResult;
2116 if (align) {
2117 drwav_uintptr modulo = (drwav_uintptr)pParser->pDataCursor % align;
2118 if (modulo != 0) {
2119 pParser->pDataCursor += align - modulo;
2120 }
2121 }
2123 pResult = pParser->pDataCursor;
2125 /*
2126 Getting to the point where this function is called means there should always be memory
2127 available. Out of memory checks should have been done at an earlier stage.
2128 */
2129 DRWAV_ASSERT((pResult + size) <= (pParser->pData + drwav__metadata_memory_capacity(pParser)));
2131 pParser->pDataCursor += size;
2132 return pResult;
2133}
2135DRWAV_PRIVATE void drwav__metadata_request_extra_memory_for_stage_2(drwav__metadata_parser* pParser, size_t bytes, size_t align)
2136{
2137 size_t extra = bytes + (align ? (align - 1) : 0);
2138 pParser->extraCapacity += extra;
2139}
2141DRWAV_PRIVATE drwav_result drwav__metadata_alloc(drwav__metadata_parser* pParser, drwav_allocation_callbacks* pAllocationCallbacks)
2142{
2143 if (pParser->extraCapacity != 0 || pParser->metadataCount != 0) {
2144 pAllocationCallbacks->onFree(pParser->pData, pAllocationCallbacks->pUserData);
2146 pParser->pData = (drwav_uint8*)pAllocationCallbacks->onMalloc(drwav__metadata_memory_capacity(pParser), pAllocationCallbacks->pUserData);
2147 pParser->pDataCursor = pParser->pData;
2149 if (pParser->pData == NULL) {
2150 return DRWAV_OUT_OF_MEMORY;
2151 }
2153 /*
2154 We don't need to worry about specifying an alignment here because malloc always returns something
2155 of suitable alignment. This also means pParser->pMetadata is all that we need to store in order
2156 for us to free when we are done.
2157 */
2158 pParser->pMetadata = (drwav_metadata*)drwav__metadata_get_memory(pParser, sizeof(drwav_metadata) * pParser->metadataCount, 1);
2159 pParser->metadataCursor = 0;
2160 }
2162 return DRWAV_SUCCESS;
2163}
2165DRWAV_PRIVATE size_t drwav__metadata_parser_read(drwav__metadata_parser* pParser, void* pBufferOut, size_t bytesToRead, drwav_uint64* pCursor)
2166{
2167 if (pCursor != NULL) {
2168 return drwav__on_read(pParser->onRead, pParser->pReadSeekUserData, pBufferOut, bytesToRead, pCursor);
2169 } else {
2170 return pParser->onRead(pParser->pReadSeekUserData, pBufferOut, bytesToRead);
2171 }
2172}
2174DRWAV_PRIVATE drwav_uint64 drwav__read_smpl_to_metadata_obj(drwav__metadata_parser* pParser, const drwav_chunk_header* pChunkHeader, drwav_metadata* pMetadata)
2175{
2176 drwav_uint8 smplHeaderData[DRWAV_SMPL_BYTES];
2177 drwav_uint64 totalBytesRead = 0;
2178 size_t bytesJustRead;
2180 if (pMetadata == NULL) {
2181 return 0;
2182 }
2184 bytesJustRead = drwav__metadata_parser_read(pParser, smplHeaderData, sizeof(smplHeaderData), &totalBytesRead);
2186 DRWAV_ASSERT(pParser->stage == drwav__metadata_parser_stage_read);
2187 DRWAV_ASSERT(pChunkHeader != NULL);
2189 if (pMetadata != NULL && bytesJustRead == sizeof(smplHeaderData)) {
2190 drwav_uint32 iSampleLoop;
2192 pMetadata->type = drwav_metadata_type_smpl;
2193 pMetadata->data.smpl.manufacturerId = drwav_bytes_to_u32(smplHeaderData + 0);
2194 pMetadata->data.smpl.productId = drwav_bytes_to_u32(smplHeaderData + 4);
2195 pMetadata->data.smpl.samplePeriodNanoseconds = drwav_bytes_to_u32(smplHeaderData + 8);
2196 pMetadata->data.smpl.midiUnityNote = drwav_bytes_to_u32(smplHeaderData + 12);
2197 pMetadata->data.smpl.midiPitchFraction = drwav_bytes_to_u32(smplHeaderData + 16);
2198 pMetadata->data.smpl.smpteFormat = drwav_bytes_to_u32(smplHeaderData + 20);
2199 pMetadata->data.smpl.smpteOffset = drwav_bytes_to_u32(smplHeaderData + 24);
2200 pMetadata->data.smpl.sampleLoopCount = drwav_bytes_to_u32(smplHeaderData + 28);
2201 pMetadata->data.smpl.samplerSpecificDataSizeInBytes = drwav_bytes_to_u32(smplHeaderData + 32);
2203 /*
2204 The loop count needs to be validated against the size of the chunk for safety so we don't
2205 attempt to read over the boundary of the chunk.
2206 */
2207 if (pMetadata->data.smpl.sampleLoopCount == (pChunkHeader->sizeInBytes - DRWAV_SMPL_BYTES) / DRWAV_SMPL_LOOP_BYTES) {
2208 pMetadata->data.smpl.pLoops = (drwav_smpl_loop*)drwav__metadata_get_memory(pParser, sizeof(drwav_smpl_loop) * pMetadata->data.smpl.sampleLoopCount, DRWAV_METADATA_ALIGNMENT);
2210 for (iSampleLoop = 0; iSampleLoop < pMetadata->data.smpl.sampleLoopCount; ++iSampleLoop) {
2211 drwav_uint8 smplLoopData[DRWAV_SMPL_LOOP_BYTES];
2212 bytesJustRead = drwav__metadata_parser_read(pParser, smplLoopData, sizeof(smplLoopData), &totalBytesRead);
2214 if (bytesJustRead == sizeof(smplLoopData)) {
2215 pMetadata->data.smpl.pLoops[iSampleLoop].cuePointId = drwav_bytes_to_u32(smplLoopData + 0);
2216 pMetadata->data.smpl.pLoops[iSampleLoop].type = drwav_bytes_to_u32(smplLoopData + 4);
2217 pMetadata->data.smpl.pLoops[iSampleLoop].firstSampleOffset = drwav_bytes_to_u32(smplLoopData + 8);
2218 pMetadata->data.smpl.pLoops[iSampleLoop].lastSampleOffset = drwav_bytes_to_u32(smplLoopData + 12);
2219 pMetadata->data.smpl.pLoops[iSampleLoop].sampleFraction = drwav_bytes_to_u32(smplLoopData + 16);
2220 pMetadata->data.smpl.pLoops[iSampleLoop].playCount = drwav_bytes_to_u32(smplLoopData + 20);
2221 } else {
2222 break;
2223 }
2224 }
2226 if (pMetadata->data.smpl.samplerSpecificDataSizeInBytes > 0) {
2227 pMetadata->data.smpl.pSamplerSpecificData = drwav__metadata_get_memory(pParser, pMetadata->data.smpl.samplerSpecificDataSizeInBytes, 1);
2228 DRWAV_ASSERT(pMetadata->data.smpl.pSamplerSpecificData != NULL);
2230 drwav__metadata_parser_read(pParser, pMetadata->data.smpl.pSamplerSpecificData, pMetadata->data.smpl.samplerSpecificDataSizeInBytes, &totalBytesRead);
2231 }
2232 }
2233 }
2235 return totalBytesRead;
2236}
2238DRWAV_PRIVATE drwav_uint64 drwav__read_cue_to_metadata_obj(drwav__metadata_parser* pParser, const drwav_chunk_header* pChunkHeader, drwav_metadata* pMetadata)
2239{
2240 drwav_uint8 cueHeaderSectionData[DRWAV_CUE_BYTES];
2241 drwav_uint64 totalBytesRead = 0;
2242 size_t bytesJustRead;
2244 if (pMetadata == NULL) {
2245 return 0;
2246 }
2248 bytesJustRead = drwav__metadata_parser_read(pParser, cueHeaderSectionData, sizeof(cueHeaderSectionData), &totalBytesRead);
2250 DRWAV_ASSERT(pParser->stage == drwav__metadata_parser_stage_read);
2252 if (bytesJustRead == sizeof(cueHeaderSectionData)) {
2253 pMetadata->type = drwav_metadata_type_cue;
2254 pMetadata->data.cue.cuePointCount = drwav_bytes_to_u32(cueHeaderSectionData);
2256 /*
2257 We need to validate the cue point count against the size of the chunk so we don't read
2258 beyond the chunk.
2259 */
2260 if (pMetadata->data.cue.cuePointCount == (pChunkHeader->sizeInBytes - DRWAV_CUE_BYTES) / DRWAV_CUE_POINT_BYTES) {
2261 pMetadata->data.cue.pCuePoints = (drwav_cue_point*)drwav__metadata_get_memory(pParser, sizeof(drwav_cue_point) * pMetadata->data.cue.cuePointCount, DRWAV_METADATA_ALIGNMENT);
2262 DRWAV_ASSERT(pMetadata->data.cue.pCuePoints != NULL);
2264 if (pMetadata->data.cue.cuePointCount > 0) {
2265 drwav_uint32 iCuePoint;
2267 for (iCuePoint = 0; iCuePoint < pMetadata->data.cue.cuePointCount; ++iCuePoint) {
2268 drwav_uint8 cuePointData[DRWAV_CUE_POINT_BYTES];
2269 bytesJustRead = drwav__metadata_parser_read(pParser, cuePointData, sizeof(cuePointData), &totalBytesRead);
2271 if (bytesJustRead == sizeof(cuePointData)) {
2272 pMetadata->data.cue.pCuePoints[iCuePoint].id = drwav_bytes_to_u32(cuePointData + 0);
2273 pMetadata->data.cue.pCuePoints[iCuePoint].playOrderPosition = drwav_bytes_to_u32(cuePointData + 4);
2274 pMetadata->data.cue.pCuePoints[iCuePoint].dataChunkId[0] = cuePointData[8];
2275 pMetadata->data.cue.pCuePoints[iCuePoint].dataChunkId[1] = cuePointData[9];
2276 pMetadata->data.cue.pCuePoints[iCuePoint].dataChunkId[2] = cuePointData[10];
2277 pMetadata->data.cue.pCuePoints[iCuePoint].dataChunkId[3] = cuePointData[11];
2278 pMetadata->data.cue.pCuePoints[iCuePoint].chunkStart = drwav_bytes_to_u32(cuePointData + 12);
2279 pMetadata->data.cue.pCuePoints[iCuePoint].blockStart = drwav_bytes_to_u32(cuePointData + 16);
2280 pMetadata->data.cue.pCuePoints[iCuePoint].sampleOffset = drwav_bytes_to_u32(cuePointData + 20);
2281 } else {
2282 break;
2283 }
2284 }
2285 }
2286 }
2287 }
2289 return totalBytesRead;
2290}
2292DRWAV_PRIVATE drwav_uint64 drwav__read_inst_to_metadata_obj(drwav__metadata_parser* pParser, drwav_metadata* pMetadata)
2293{
2294 drwav_uint8 instData[DRWAV_INST_BYTES];
2295 drwav_uint64 bytesRead;
2297 if (pMetadata == NULL) {
2298 return 0;
2299 }
2301 bytesRead = drwav__metadata_parser_read(pParser, instData, sizeof(instData), NULL);
2303 DRWAV_ASSERT(pParser->stage == drwav__metadata_parser_stage_read);
2305 if (bytesRead == sizeof(instData)) {
2306 pMetadata->type = drwav_metadata_type_inst;
2307 pMetadata->data.inst.midiUnityNote = (drwav_int8)instData[0];
2308 pMetadata->data.inst.fineTuneCents = (drwav_int8)instData[1];
2309 pMetadata->data.inst.gainDecibels = (drwav_int8)instData[2];
2310 pMetadata->data.inst.lowNote = (drwav_int8)instData[3];
2311 pMetadata->data.inst.highNote = (drwav_int8)instData[4];
2312 pMetadata->data.inst.lowVelocity = (drwav_int8)instData[5];
2313 pMetadata->data.inst.highVelocity = (drwav_int8)instData[6];
2314 }
2316 return bytesRead;
2317}
2319DRWAV_PRIVATE drwav_uint64 drwav__read_acid_to_metadata_obj(drwav__metadata_parser* pParser, drwav_metadata* pMetadata)
2320{
2321 drwav_uint8 acidData[DRWAV_ACID_BYTES];
2322 drwav_uint64 bytesRead;
2324 if (pMetadata == NULL) {
2325 return 0;
2326 }
2328 bytesRead = drwav__metadata_parser_read(pParser, acidData, sizeof(acidData), NULL);
2330 DRWAV_ASSERT(pParser->stage == drwav__metadata_parser_stage_read);
2332 if (bytesRead == sizeof(acidData)) {
2333 pMetadata->type = drwav_metadata_type_acid;
2334 pMetadata->data.acid.flags = drwav_bytes_to_u32(acidData + 0);
2335 pMetadata->data.acid.midiUnityNote = drwav_bytes_to_u16(acidData + 4);
2336 pMetadata->data.acid.reserved1 = drwav_bytes_to_u16(acidData + 6);
2337 pMetadata->data.acid.reserved2 = drwav_bytes_to_f32(acidData + 8);
2338 pMetadata->data.acid.numBeats = drwav_bytes_to_u32(acidData + 12);
2339 pMetadata->data.acid.meterDenominator = drwav_bytes_to_u16(acidData + 16);
2340 pMetadata->data.acid.meterNumerator = drwav_bytes_to_u16(acidData + 18);
2341 pMetadata->data.acid.tempo = drwav_bytes_to_f32(acidData + 20);
2342 }
2344 return bytesRead;
2345}
2347DRWAV_PRIVATE size_t drwav__strlen(const char* str)
2348{
2349 size_t result = 0;
2351 while (*str++) {
2352 result += 1;
2353 }
2355 return result;
2356}
2358DRWAV_PRIVATE size_t drwav__strlen_clamped(const char* str, size_t maxToRead)
2359{
2360 size_t result = 0;
2362 while (*str++ && result < maxToRead) {
2363 result += 1;
2364 }
2366 return result;
2367}
2369DRWAV_PRIVATE char* drwav__metadata_copy_string(drwav__metadata_parser* pParser, const char* str, size_t maxToRead)
2370{
2371 size_t len = drwav__strlen_clamped(str, maxToRead);
2373 if (len) {
2374 char* result = (char*)drwav__metadata_get_memory(pParser, len + 1, 1);
2375 DRWAV_ASSERT(result != NULL);
2377 DRWAV_COPY_MEMORY(result, str, len);
2378 result[len] = '\0';
2380 return result;
2381 } else {
2382 return NULL;
2383 }
2384}
2386typedef struct
2387{
2388 const void* pBuffer;
2389 size_t sizeInBytes;
2390 size_t cursor;
2391} drwav_buffer_reader;
2393DRWAV_PRIVATE drwav_result drwav_buffer_reader_init(const void* pBuffer, size_t sizeInBytes, drwav_buffer_reader* pReader)
2394{
2395 DRWAV_ASSERT(pBuffer != NULL);
2396 DRWAV_ASSERT(pReader != NULL);
2398 DRWAV_ZERO_OBJECT(pReader);
2400 pReader->pBuffer = pBuffer;
2401 pReader->sizeInBytes = sizeInBytes;
2402 pReader->cursor = 0;
2404 return DRWAV_SUCCESS;
2405}
2407DRWAV_PRIVATE const void* drwav_buffer_reader_ptr(const drwav_buffer_reader* pReader)
2408{
2409 DRWAV_ASSERT(pReader != NULL);
2411 return drwav_offset_ptr(pReader->pBuffer, pReader->cursor);
2412}
2414DRWAV_PRIVATE drwav_result drwav_buffer_reader_seek(drwav_buffer_reader* pReader, size_t bytesToSeek)
2415{
2416 DRWAV_ASSERT(pReader != NULL);
2418 if (pReader->cursor + bytesToSeek > pReader->sizeInBytes) {
2419 return DRWAV_BAD_SEEK; /* Seeking too far forward. */
2420 }
2422 pReader->cursor += bytesToSeek;
2424 return DRWAV_SUCCESS;
2425}
2427DRWAV_PRIVATE drwav_result drwav_buffer_reader_read(drwav_buffer_reader* pReader, void* pDst, size_t bytesToRead, size_t* pBytesRead)
2428{
2429 drwav_result result = DRWAV_SUCCESS;
2430 size_t bytesRemaining;
2432 DRWAV_ASSERT(pReader != NULL);
2434 if (pBytesRead != NULL) {
2435 *pBytesRead = 0;
2436 }
2438 bytesRemaining = (pReader->sizeInBytes - pReader->cursor);
2439 if (bytesToRead > bytesRemaining) {
2440 bytesToRead = bytesRemaining;
2441 }
2443 if (pDst == NULL) {
2444 /* Seek. */
2445 result = drwav_buffer_reader_seek(pReader, bytesToRead);
2446 } else {
2447 /* Read. */
2448 DRWAV_COPY_MEMORY(pDst, drwav_buffer_reader_ptr(pReader), bytesToRead);
2449 pReader->cursor += bytesToRead;
2450 }
2452 DRWAV_ASSERT(pReader->cursor <= pReader->sizeInBytes);
2454 if (result == DRWAV_SUCCESS) {
2455 if (pBytesRead != NULL) {
2456 *pBytesRead = bytesToRead;
2457 }
2458 }
2460 return DRWAV_SUCCESS;
2461}
2463DRWAV_PRIVATE drwav_result drwav_buffer_reader_read_u16(drwav_buffer_reader* pReader, drwav_uint16* pDst)
2464{
2465 drwav_result result;
2466 size_t bytesRead;
2467 drwav_uint8 data[2];
2469 DRWAV_ASSERT(pReader != NULL);
2470 DRWAV_ASSERT(pDst != NULL);
2472 *pDst = 0; /* Safety. */
2474 result = drwav_buffer_reader_read(pReader, data, sizeof(*pDst), &bytesRead);
2475 if (result != DRWAV_SUCCESS || bytesRead != sizeof(*pDst)) {
2476 return result;
2477 }
2479 *pDst = drwav_bytes_to_u16(data);
2481 return DRWAV_SUCCESS;
2482}
2484DRWAV_PRIVATE drwav_result drwav_buffer_reader_read_u32(drwav_buffer_reader* pReader, drwav_uint32* pDst)
2485{
2486 drwav_result result;
2487 size_t bytesRead;
2488 drwav_uint8 data[4];
2490 DRWAV_ASSERT(pReader != NULL);
2491 DRWAV_ASSERT(pDst != NULL);
2493 *pDst = 0; /* Safety. */
2495 result = drwav_buffer_reader_read(pReader, data, sizeof(*pDst), &bytesRead);
2496 if (result != DRWAV_SUCCESS || bytesRead != sizeof(*pDst)) {
2497 return result;
2498 }
2500 *pDst = drwav_bytes_to_u32(data);
2502 return DRWAV_SUCCESS;
2503}
2507DRWAV_PRIVATE drwav_uint64 drwav__read_bext_to_metadata_obj(drwav__metadata_parser* pParser, drwav_metadata* pMetadata, drwav_uint64 chunkSize)
2508{
2509 drwav_uint8 bextData[DRWAV_BEXT_BYTES];
2510 size_t bytesRead = drwav__metadata_parser_read(pParser, bextData, sizeof(bextData), NULL);
2512 DRWAV_ASSERT(pParser->stage == drwav__metadata_parser_stage_read);
2514 if (bytesRead == sizeof(bextData)) {
2515 drwav_buffer_reader reader;
2516 drwav_uint32 timeReferenceLow;
2517 drwav_uint32 timeReferenceHigh;
2518 size_t extraBytes;
2520 pMetadata->type = drwav_metadata_type_bext;
2522 if (drwav_buffer_reader_init(bextData, bytesRead, &reader) == DRWAV_SUCCESS) {
2523 pMetadata->data.bext.pDescription = drwav__metadata_copy_string(pParser, (const char*)drwav_buffer_reader_ptr(&reader), DRWAV_BEXT_DESCRIPTION_BYTES);
2524 drwav_buffer_reader_seek(&reader, DRWAV_BEXT_DESCRIPTION_BYTES);
2526 pMetadata->data.bext.pOriginatorName = drwav__metadata_copy_string(pParser, (const char*)drwav_buffer_reader_ptr(&reader), DRWAV_BEXT_ORIGINATOR_NAME_BYTES);
2527 drwav_buffer_reader_seek(&reader, DRWAV_BEXT_ORIGINATOR_NAME_BYTES);
2529 pMetadata->data.bext.pOriginatorReference = drwav__metadata_copy_string(pParser, (const char*)drwav_buffer_reader_ptr(&reader), DRWAV_BEXT_ORIGINATOR_REF_BYTES);
2530 drwav_buffer_reader_seek(&reader, DRWAV_BEXT_ORIGINATOR_REF_BYTES);
2532 drwav_buffer_reader_read(&reader, pMetadata->data.bext.pOriginationDate, sizeof(pMetadata->data.bext.pOriginationDate), NULL);
2533 drwav_buffer_reader_read(&reader, pMetadata->data.bext.pOriginationTime, sizeof(pMetadata->data.bext.pOriginationTime), NULL);
2535 drwav_buffer_reader_read_u32(&reader, &timeReferenceLow);
2536 drwav_buffer_reader_read_u32(&reader, &timeReferenceHigh);
2537 pMetadata->data.bext.timeReference = ((drwav_uint64)timeReferenceHigh << 32) + timeReferenceLow;
2539 drwav_buffer_reader_read_u16(&reader, &pMetadata->data.bext.version);
2541 pMetadata->data.bext.pUMID = drwav__metadata_get_memory(pParser, DRWAV_BEXT_UMID_BYTES, 1);
2542 drwav_buffer_reader_read(&reader, pMetadata->data.bext.pUMID, DRWAV_BEXT_UMID_BYTES, NULL);
2544 drwav_buffer_reader_read_u16(&reader, &pMetadata->data.bext.loudnessValue);
2545 drwav_buffer_reader_read_u16(&reader, &pMetadata->data.bext.loudnessRange);
2546 drwav_buffer_reader_read_u16(&reader, &pMetadata->data.bext.maxTruePeakLevel);
2547 drwav_buffer_reader_read_u16(&reader, &pMetadata->data.bext.maxMomentaryLoudness);
2548 drwav_buffer_reader_read_u16(&reader, &pMetadata->data.bext.maxShortTermLoudness);
2550 DRWAV_ASSERT((drwav_offset_ptr(drwav_buffer_reader_ptr(&reader), DRWAV_BEXT_RESERVED_BYTES)) == (bextData + DRWAV_BEXT_BYTES));
2552 extraBytes = (size_t)(chunkSize - DRWAV_BEXT_BYTES);
2553 if (extraBytes > 0) {
2554 pMetadata->data.bext.pCodingHistory = (char*)drwav__metadata_get_memory(pParser, extraBytes + 1, 1);
2555 DRWAV_ASSERT(pMetadata->data.bext.pCodingHistory != NULL);
2557 bytesRead += drwav__metadata_parser_read(pParser, pMetadata->data.bext.pCodingHistory, extraBytes, NULL);
2558 pMetadata->data.bext.codingHistorySize = (drwav_uint32)drwav__strlen(pMetadata->data.bext.pCodingHistory);
2559 } else {
2560 pMetadata->data.bext.pCodingHistory = NULL;
2561 pMetadata->data.bext.codingHistorySize = 0;
2562 }
2563 }
2564 }
2566 return bytesRead;
2567}
2569DRWAV_PRIVATE drwav_uint64 drwav__read_list_label_or_note_to_metadata_obj(drwav__metadata_parser* pParser, drwav_metadata* pMetadata, drwav_uint64 chunkSize, drwav_metadata_type type)
2570{
2571 drwav_uint8 cueIDBuffer[DRWAV_LIST_LABEL_OR_NOTE_BYTES];
2572 drwav_uint64 totalBytesRead = 0;
2573 size_t bytesJustRead = drwav__metadata_parser_read(pParser, cueIDBuffer, sizeof(cueIDBuffer), &totalBytesRead);
2575 DRWAV_ASSERT(pParser->stage == drwav__metadata_parser_stage_read);
2577 if (bytesJustRead == sizeof(cueIDBuffer)) {
2578 drwav_uint32 sizeIncludingNullTerminator;
2580 pMetadata->type = type;
2581 pMetadata->data.labelOrNote.cuePointId = drwav_bytes_to_u32(cueIDBuffer);
2583 sizeIncludingNullTerminator = (drwav_uint32)chunkSize - DRWAV_LIST_LABEL_OR_NOTE_BYTES;
2584 if (sizeIncludingNullTerminator > 0) {
2585 pMetadata->data.labelOrNote.stringLength = sizeIncludingNullTerminator - 1;
2586 pMetadata->data.labelOrNote.pString = (char*)drwav__metadata_get_memory(pParser, sizeIncludingNullTerminator, 1);
2587 DRWAV_ASSERT(pMetadata->data.labelOrNote.pString != NULL);
2589 drwav__metadata_parser_read(pParser, pMetadata->data.labelOrNote.pString, sizeIncludingNullTerminator, &totalBytesRead);
2590 } else {
2591 pMetadata->data.labelOrNote.stringLength = 0;
2592 pMetadata->data.labelOrNote.pString = NULL;
2593 }
2594 }
2596 return totalBytesRead;
2597}
2599DRWAV_PRIVATE drwav_uint64 drwav__read_list_labelled_cue_region_to_metadata_obj(drwav__metadata_parser* pParser, drwav_metadata* pMetadata, drwav_uint64 chunkSize)
2600{
2601 drwav_uint8 buffer[DRWAV_LIST_LABELLED_TEXT_BYTES];
2602 drwav_uint64 totalBytesRead = 0;
2603 size_t bytesJustRead = drwav__metadata_parser_read(pParser, buffer, sizeof(buffer), &totalBytesRead);
2605 DRWAV_ASSERT(pParser->stage == drwav__metadata_parser_stage_read);
2607 if (bytesJustRead == sizeof(buffer)) {
2608 drwav_uint32 sizeIncludingNullTerminator;
2610 pMetadata->type = drwav_metadata_type_list_labelled_cue_region;
2611 pMetadata->data.labelledCueRegion.cuePointId = drwav_bytes_to_u32(buffer + 0);
2612 pMetadata->data.labelledCueRegion.sampleLength = drwav_bytes_to_u32(buffer + 4);
2613 pMetadata->data.labelledCueRegion.purposeId[0] = buffer[8];
2614 pMetadata->data.labelledCueRegion.purposeId[1] = buffer[9];
2615 pMetadata->data.labelledCueRegion.purposeId[2] = buffer[10];
2616 pMetadata->data.labelledCueRegion.purposeId[3] = buffer[11];
2617 pMetadata->data.labelledCueRegion.country = drwav_bytes_to_u16(buffer + 12);
2618 pMetadata->data.labelledCueRegion.language = drwav_bytes_to_u16(buffer + 14);
2619 pMetadata->data.labelledCueRegion.dialect = drwav_bytes_to_u16(buffer + 16);
2620 pMetadata->data.labelledCueRegion.codePage = drwav_bytes_to_u16(buffer + 18);
2622 sizeIncludingNullTerminator = (drwav_uint32)chunkSize - DRWAV_LIST_LABELLED_TEXT_BYTES;
2623 if (sizeIncludingNullTerminator > 0) {
2624 pMetadata->data.labelledCueRegion.stringLength = sizeIncludingNullTerminator - 1;
2625 pMetadata->data.labelledCueRegion.pString = (char*)drwav__metadata_get_memory(pParser, sizeIncludingNullTerminator, 1);
2626 DRWAV_ASSERT(pMetadata->data.labelledCueRegion.pString != NULL);
2628 drwav__metadata_parser_read(pParser, pMetadata->data.labelledCueRegion.pString, sizeIncludingNullTerminator, &totalBytesRead);
2629 } else {
2630 pMetadata->data.labelledCueRegion.stringLength = 0;
2631 pMetadata->data.labelledCueRegion.pString = NULL;
2632 }
2633 }
2635 return totalBytesRead;
2636}
2638DRWAV_PRIVATE drwav_uint64 drwav__metadata_process_info_text_chunk(drwav__metadata_parser* pParser, drwav_uint64 chunkSize, drwav_metadata_type type)
2639{
2640 drwav_uint64 bytesRead = 0;
2641 drwav_uint32 stringSizeWithNullTerminator = (drwav_uint32)chunkSize;
2643 if (pParser->stage == drwav__metadata_parser_stage_count) {
2644 pParser->metadataCount += 1;
2645 drwav__metadata_request_extra_memory_for_stage_2(pParser, stringSizeWithNullTerminator, 1);
2646 } else {
2647 drwav_metadata* pMetadata = &pParser->pMetadata[pParser->metadataCursor];
2648 pMetadata->type = type;
2649 if (stringSizeWithNullTerminator > 0) {
2650 pMetadata->data.infoText.stringLength = stringSizeWithNullTerminator - 1;
2651 pMetadata->data.infoText.pString = (char*)drwav__metadata_get_memory(pParser, stringSizeWithNullTerminator, 1);
2652 DRWAV_ASSERT(pMetadata->data.infoText.pString != NULL);
2654 bytesRead = drwav__metadata_parser_read(pParser, pMetadata->data.infoText.pString, (size_t)stringSizeWithNullTerminator, NULL);
2655 if (bytesRead == chunkSize) {
2656 pParser->metadataCursor += 1;
2657 } else {
2658 /* Failed to parse. */
2659 }
2660 } else {
2661 pMetadata->data.infoText.stringLength = 0;
2662 pMetadata->data.infoText.pString = NULL;
2663 pParser->metadataCursor += 1;
2664 }
2665 }
2667 return bytesRead;
2668}
2670DRWAV_PRIVATE drwav_uint64 drwav__metadata_process_unknown_chunk(drwav__metadata_parser* pParser, const drwav_uint8* pChunkId, drwav_uint64 chunkSize, drwav_metadata_location location)
2671{
2672 drwav_uint64 bytesRead = 0;
2674 if (location == drwav_metadata_location_invalid) {
2675 return 0;
2676 }
2678 if (drwav_fourcc_equal(pChunkId, "data") || drwav_fourcc_equal(pChunkId, "fmt ") || drwav_fourcc_equal(pChunkId, "fact")) {
2679 return 0;
2680 }
2682 if (pParser->stage == drwav__metadata_parser_stage_count) {
2683 pParser->metadataCount += 1;
2684 drwav__metadata_request_extra_memory_for_stage_2(pParser, (size_t)chunkSize, 1);
2685 } else {
2686 drwav_metadata* pMetadata = &pParser->pMetadata[pParser->metadataCursor];
2687 pMetadata->type = drwav_metadata_type_unknown;
2688 pMetadata->data.unknown.chunkLocation = location;
2689 pMetadata->data.unknown.id[0] = pChunkId[0];
2690 pMetadata->data.unknown.id[1] = pChunkId[1];
2691 pMetadata->data.unknown.id[2] = pChunkId[2];
2692 pMetadata->data.unknown.id[3] = pChunkId[3];
2693 pMetadata->data.unknown.dataSizeInBytes = (drwav_uint32)chunkSize;
2694 pMetadata->data.unknown.pData = (drwav_uint8 *)drwav__metadata_get_memory(pParser, (size_t)chunkSize, 1);
2695 DRWAV_ASSERT(pMetadata->data.unknown.pData != NULL);
2697 bytesRead = drwav__metadata_parser_read(pParser, pMetadata->data.unknown.pData, pMetadata->data.unknown.dataSizeInBytes, NULL);
2698 if (bytesRead == pMetadata->data.unknown.dataSizeInBytes) {
2699 pParser->metadataCursor += 1;
2700 } else {
2701 /* Failed to read. */
2702 }
2703 }
2705 return bytesRead;
2706}
2708DRWAV_PRIVATE drwav_bool32 drwav__chunk_matches(drwav_metadata_type allowedMetadataTypes, const drwav_uint8* pChunkID, drwav_metadata_type type, const char* pID)
2709{
2710 return (allowedMetadataTypes & type) && drwav_fourcc_equal(pChunkID, pID);
2711}
2713DRWAV_PRIVATE drwav_uint64 drwav__metadata_process_chunk(drwav__metadata_parser* pParser, const drwav_chunk_header* pChunkHeader, drwav_metadata_type allowedMetadataTypes)
2714{
2715 const drwav_uint8 *pChunkID = pChunkHeader->id.fourcc;
2716 drwav_uint64 bytesRead = 0;
2718 if (drwav__chunk_matches(allowedMetadataTypes, pChunkID, drwav_metadata_type_smpl, "smpl")) {
2719 if (pChunkHeader->sizeInBytes >= DRWAV_SMPL_BYTES) {
2720 if (pParser->stage == drwav__metadata_parser_stage_count) {
2721 drwav_uint8 buffer[4];
2722 size_t bytesJustRead;
2724 if (!pParser->onSeek(pParser->pReadSeekUserData, 28, DRWAV_SEEK_CUR)) {
2725 return bytesRead;
2726 }
2727 bytesRead += 28;
2729 bytesJustRead = drwav__metadata_parser_read(pParser, buffer, sizeof(buffer), &bytesRead);
2730 if (bytesJustRead == sizeof(buffer)) {
2731 drwav_uint32 loopCount = drwav_bytes_to_u32(buffer);
2732 drwav_uint64 calculatedLoopCount;
2734 /* The loop count must be validated against the size of the chunk. */
2735 calculatedLoopCount = (pChunkHeader->sizeInBytes - DRWAV_SMPL_BYTES) / DRWAV_SMPL_LOOP_BYTES;
2736 if (calculatedLoopCount == loopCount) {
2737 bytesJustRead = drwav__metadata_parser_read(pParser, buffer, sizeof(buffer), &bytesRead);
2738 if (bytesJustRead == sizeof(buffer)) {
2739 drwav_uint32 samplerSpecificDataSizeInBytes = drwav_bytes_to_u32(buffer);
2741 pParser->metadataCount += 1;
2742 drwav__metadata_request_extra_memory_for_stage_2(pParser, sizeof(drwav_smpl_loop) * loopCount, DRWAV_METADATA_ALIGNMENT);
2743 drwav__metadata_request_extra_memory_for_stage_2(pParser, samplerSpecificDataSizeInBytes, 1);
2744 }
2745 } else {
2746 /* Loop count in header does not match the size of the chunk. */
2747 }
2748 }
2749 } else {
2750 bytesRead = drwav__read_smpl_to_metadata_obj(pParser, pChunkHeader, &pParser->pMetadata[pParser->metadataCursor]);
2751 if (bytesRead == pChunkHeader->sizeInBytes) {
2752 pParser->metadataCursor += 1;
2753 } else {
2754 /* Failed to parse. */
2755 }
2756 }
2757 } else {
2758 /* Incorrectly formed chunk. */
2759 }
2760 } else if (drwav__chunk_matches(allowedMetadataTypes, pChunkID, drwav_metadata_type_inst, "inst")) {
2761 if (pChunkHeader->sizeInBytes == DRWAV_INST_BYTES) {
2762 if (pParser->stage == drwav__metadata_parser_stage_count) {
2763 pParser->metadataCount += 1;
2764 } else {
2765 bytesRead = drwav__read_inst_to_metadata_obj(pParser, &pParser->pMetadata[pParser->metadataCursor]);
2766 if (bytesRead == pChunkHeader->sizeInBytes) {
2767 pParser->metadataCursor += 1;
2768 } else {
2769 /* Failed to parse. */
2770 }
2771 }
2772 } else {
2773 /* Incorrectly formed chunk. */
2774 }
2775 } else if (drwav__chunk_matches(allowedMetadataTypes, pChunkID, drwav_metadata_type_acid, "acid")) {
2776 if (pChunkHeader->sizeInBytes == DRWAV_ACID_BYTES) {
2777 if (pParser->stage == drwav__metadata_parser_stage_count) {
2778 pParser->metadataCount += 1;
2779 } else {
2780 bytesRead = drwav__read_acid_to_metadata_obj(pParser, &pParser->pMetadata[pParser->metadataCursor]);
2781 if (bytesRead == pChunkHeader->sizeInBytes) {
2782 pParser->metadataCursor += 1;
2783 } else {
2784 /* Failed to parse. */
2785 }
2786 }
2787 } else {
2788 /* Incorrectly formed chunk. */
2789 }
2790 } else if (drwav__chunk_matches(allowedMetadataTypes, pChunkID, drwav_metadata_type_cue, "cue ")) {
2791 if (pChunkHeader->sizeInBytes >= DRWAV_CUE_BYTES) {
2792 if (pParser->stage == drwav__metadata_parser_stage_count) {
2793 size_t cueCount;
2795 pParser->metadataCount += 1;
2796 cueCount = (size_t)(pChunkHeader->sizeInBytes - DRWAV_CUE_BYTES) / DRWAV_CUE_POINT_BYTES;
2797 drwav__metadata_request_extra_memory_for_stage_2(pParser, sizeof(drwav_cue_point) * cueCount, DRWAV_METADATA_ALIGNMENT);
2798 } else {
2799 bytesRead = drwav__read_cue_to_metadata_obj(pParser, pChunkHeader, &pParser->pMetadata[pParser->metadataCursor]);
2800 if (bytesRead == pChunkHeader->sizeInBytes) {
2801 pParser->metadataCursor += 1;
2802 } else {
2803 /* Failed to parse. */
2804 }
2805 }
2806 } else {
2807 /* Incorrectly formed chunk. */
2808 }
2809 } else if (drwav__chunk_matches(allowedMetadataTypes, pChunkID, drwav_metadata_type_bext, "bext")) {
2810 if (pChunkHeader->sizeInBytes >= DRWAV_BEXT_BYTES) {
2811 if (pParser->stage == drwav__metadata_parser_stage_count) {
2812 /* The description field is the largest one in a bext chunk, so that is the max size of this temporary buffer. */
2813 char buffer[DRWAV_BEXT_DESCRIPTION_BYTES + 1];
2814 size_t allocSizeNeeded = DRWAV_BEXT_UMID_BYTES; /* We know we will need SMPTE umid size. */
2815 size_t bytesJustRead;
2817 buffer[DRWAV_BEXT_DESCRIPTION_BYTES] = '\0';
2818 bytesJustRead = drwav__metadata_parser_read(pParser, buffer, DRWAV_BEXT_DESCRIPTION_BYTES, &bytesRead);
2819 if (bytesJustRead != DRWAV_BEXT_DESCRIPTION_BYTES) {
2820 return bytesRead;
2821 }
2822 allocSizeNeeded += drwav__strlen(buffer) + 1;
2824 buffer[DRWAV_BEXT_ORIGINATOR_NAME_BYTES] = '\0';
2825 bytesJustRead = drwav__metadata_parser_read(pParser, buffer, DRWAV_BEXT_ORIGINATOR_NAME_BYTES, &bytesRead);
2826 if (bytesJustRead != DRWAV_BEXT_ORIGINATOR_NAME_BYTES) {
2827 return bytesRead;
2828 }
2829 allocSizeNeeded += drwav__strlen(buffer) + 1;
2831 buffer[DRWAV_BEXT_ORIGINATOR_REF_BYTES] = '\0';
2832 bytesJustRead = drwav__metadata_parser_read(pParser, buffer, DRWAV_BEXT_ORIGINATOR_REF_BYTES, &bytesRead);
2833 if (bytesJustRead != DRWAV_BEXT_ORIGINATOR_REF_BYTES) {
2834 return bytesRead;
2835 }
2836 allocSizeNeeded += drwav__strlen(buffer) + 1;
2837 allocSizeNeeded += (size_t)pChunkHeader->sizeInBytes - DRWAV_BEXT_BYTES + 1; /* Coding history. */
2839 drwav__metadata_request_extra_memory_for_stage_2(pParser, allocSizeNeeded, 1);
2841 pParser->metadataCount += 1;
2842 } else {
2843 bytesRead = drwav__read_bext_to_metadata_obj(pParser, &pParser->pMetadata[pParser->metadataCursor], pChunkHeader->sizeInBytes);
2844 if (bytesRead == pChunkHeader->sizeInBytes) {
2845 pParser->metadataCursor += 1;
2846 } else {
2847 /* Failed to parse. */
2848 }
2849 }
2850 } else {
2851 /* Incorrectly formed chunk. */
2852 }
2853 } else if (drwav_fourcc_equal(pChunkID, "LIST") || drwav_fourcc_equal(pChunkID, "list")) {
2854 drwav_metadata_location listType = drwav_metadata_location_invalid;
2855 while (bytesRead < pChunkHeader->sizeInBytes) {
2856 drwav_uint8 subchunkId[4];
2857 drwav_uint8 subchunkSizeBuffer[4];
2858 drwav_uint64 subchunkDataSize;
2859 drwav_uint64 subchunkBytesRead = 0;
2860 drwav_uint64 bytesJustRead = drwav__metadata_parser_read(pParser, subchunkId, sizeof(subchunkId), &bytesRead);
2861 if (bytesJustRead != sizeof(subchunkId)) {
2862 break;
2863 }
2865 /*
2866 The first thing in a list chunk should be "adtl" or "INFO".
2868 - adtl means this list is a Associated Data List Chunk and will contain labels, notes
2869 or labelled cue regions.
2870 - INFO means this list is an Info List Chunk containing info text chunks such as IPRD
2871 which would specifies the album of this wav file.
2873 No data follows the adtl or INFO id so we just make note of what type this list is and
2874 continue.
2875 */
2876 if (drwav_fourcc_equal(subchunkId, "adtl")) {
2877 listType = drwav_metadata_location_inside_adtl_list;
2878 continue;
2879 } else if (drwav_fourcc_equal(subchunkId, "INFO")) {
2880 listType = drwav_metadata_location_inside_info_list;
2881 continue;
2882 }
2884 bytesJustRead = drwav__metadata_parser_read(pParser, subchunkSizeBuffer, sizeof(subchunkSizeBuffer), &bytesRead);
2885 if (bytesJustRead != sizeof(subchunkSizeBuffer)) {
2886 break;
2887 }
2888 subchunkDataSize = drwav_bytes_to_u32(subchunkSizeBuffer);
2890 if (drwav__chunk_matches(allowedMetadataTypes, subchunkId, drwav_metadata_type_list_label, "labl") || drwav__chunk_matches(allowedMetadataTypes, subchunkId, drwav_metadata_type_list_note, "note")) {
2891 if (subchunkDataSize >= DRWAV_LIST_LABEL_OR_NOTE_BYTES) {
2892 drwav_uint64 stringSizeWithNullTerm = subchunkDataSize - DRWAV_LIST_LABEL_OR_NOTE_BYTES;
2893 if (pParser->stage == drwav__metadata_parser_stage_count) {
2894 pParser->metadataCount += 1;
2895 drwav__metadata_request_extra_memory_for_stage_2(pParser, (size_t)stringSizeWithNullTerm, 1);
2896 } else {
2897 subchunkBytesRead = drwav__read_list_label_or_note_to_metadata_obj(pParser, &pParser->pMetadata[pParser->metadataCursor], subchunkDataSize, drwav_fourcc_equal(subchunkId, "labl") ? drwav_metadata_type_list_label : drwav_metadata_type_list_note);
2898 if (subchunkBytesRead == subchunkDataSize) {
2899 pParser->metadataCursor += 1;
2900 } else {
2901 /* Failed to parse. */
2902 }
2903 }
2904 } else {
2905 /* Incorrectly formed chunk. */
2906 }
2907 } else if (drwav__chunk_matches(allowedMetadataTypes, subchunkId, drwav_metadata_type_list_labelled_cue_region, "ltxt")) {
2908 if (subchunkDataSize >= DRWAV_LIST_LABELLED_TEXT_BYTES) {
2909 drwav_uint64 stringSizeWithNullTerminator = subchunkDataSize - DRWAV_LIST_LABELLED_TEXT_BYTES;
2910 if (pParser->stage == drwav__metadata_parser_stage_count) {
2911 pParser->metadataCount += 1;
2912 drwav__metadata_request_extra_memory_for_stage_2(pParser, (size_t)stringSizeWithNullTerminator, 1);
2913 } else {
2914 subchunkBytesRead = drwav__read_list_labelled_cue_region_to_metadata_obj(pParser, &pParser->pMetadata[pParser->metadataCursor], subchunkDataSize);
2915 if (subchunkBytesRead == subchunkDataSize) {
2916 pParser->metadataCursor += 1;
2917 } else {
2918 /* Failed to parse. */
2919 }
2920 }
2921 } else {
2922 /* Incorrectly formed chunk. */
2923 }
2924 } else if (drwav__chunk_matches(allowedMetadataTypes, subchunkId, drwav_metadata_type_list_info_software, "ISFT")) {
2925 subchunkBytesRead = drwav__metadata_process_info_text_chunk(pParser, subchunkDataSize, drwav_metadata_type_list_info_software);
2926 } else if (drwav__chunk_matches(allowedMetadataTypes, subchunkId, drwav_metadata_type_list_info_copyright, "ICOP")) {
2927 subchunkBytesRead = drwav__metadata_process_info_text_chunk(pParser, subchunkDataSize, drwav_metadata_type_list_info_copyright);
2928 } else if (drwav__chunk_matches(allowedMetadataTypes, subchunkId, drwav_metadata_type_list_info_title, "INAM")) {
2929 subchunkBytesRead = drwav__metadata_process_info_text_chunk(pParser, subchunkDataSize, drwav_metadata_type_list_info_title);
2930 } else if (drwav__chunk_matches(allowedMetadataTypes, subchunkId, drwav_metadata_type_list_info_artist, "IART")) {
2931 subchunkBytesRead = drwav__metadata_process_info_text_chunk(pParser, subchunkDataSize, drwav_metadata_type_list_info_artist);
2932 } else if (drwav__chunk_matches(allowedMetadataTypes, subchunkId, drwav_metadata_type_list_info_comment, "ICMT")) {
2933 subchunkBytesRead = drwav__metadata_process_info_text_chunk(pParser, subchunkDataSize, drwav_metadata_type_list_info_comment);
2934 } else if (drwav__chunk_matches(allowedMetadataTypes, subchunkId, drwav_metadata_type_list_info_date, "ICRD")) {
2935 subchunkBytesRead = drwav__metadata_process_info_text_chunk(pParser, subchunkDataSize, drwav_metadata_type_list_info_date);
2936 } else if (drwav__chunk_matches(allowedMetadataTypes, subchunkId, drwav_metadata_type_list_info_genre, "IGNR")) {
2937 subchunkBytesRead = drwav__metadata_process_info_text_chunk(pParser, subchunkDataSize, drwav_metadata_type_list_info_genre);
2938 } else if (drwav__chunk_matches(allowedMetadataTypes, subchunkId, drwav_metadata_type_list_info_album, "IPRD")) {
2939 subchunkBytesRead = drwav__metadata_process_info_text_chunk(pParser, subchunkDataSize, drwav_metadata_type_list_info_album);
2940 } else if (drwav__chunk_matches(allowedMetadataTypes, subchunkId, drwav_metadata_type_list_info_tracknumber, "ITRK")) {
2941 subchunkBytesRead = drwav__metadata_process_info_text_chunk(pParser, subchunkDataSize, drwav_metadata_type_list_info_tracknumber);
2942 } else if (drwav__chunk_matches(allowedMetadataTypes, subchunkId, drwav_metadata_type_list_info_location, "IARL")) {
2943 subchunkBytesRead = drwav__metadata_process_info_text_chunk(pParser, subchunkDataSize, drwav_metadata_type_list_info_location);
2944 } else if (drwav__chunk_matches(allowedMetadataTypes, subchunkId, drwav_metadata_type_list_info_organization, "ICMS")) {
2945 subchunkBytesRead = drwav__metadata_process_info_text_chunk(pParser, subchunkDataSize, drwav_metadata_type_list_info_organization);
2946 } else if (drwav__chunk_matches(allowedMetadataTypes, subchunkId, drwav_metadata_type_list_info_keywords, "IKEY")) {
2947 subchunkBytesRead = drwav__metadata_process_info_text_chunk(pParser, subchunkDataSize, drwav_metadata_type_list_info_keywords);
2948 } else if (drwav__chunk_matches(allowedMetadataTypes, subchunkId, drwav_metadata_type_list_info_medium, "IMED")) {
2949 subchunkBytesRead = drwav__metadata_process_info_text_chunk(pParser, subchunkDataSize, drwav_metadata_type_list_info_medium);
2950 } else if (drwav__chunk_matches(allowedMetadataTypes, subchunkId, drwav_metadata_type_list_info_description, "ISBJ")) {
2951 subchunkBytesRead = drwav__metadata_process_info_text_chunk(pParser, subchunkDataSize, drwav_metadata_type_list_info_description);
2952 } else if ((allowedMetadataTypes & drwav_metadata_type_unknown) != 0) {
2953 subchunkBytesRead = drwav__metadata_process_unknown_chunk(pParser, subchunkId, subchunkDataSize, listType);
2954 }
2956 bytesRead += subchunkBytesRead;
2957 DRWAV_ASSERT(subchunkBytesRead <= subchunkDataSize);
2959 if (subchunkBytesRead < subchunkDataSize) {
2960 drwav_uint64 bytesToSeek = subchunkDataSize - subchunkBytesRead;
2962 if (!pParser->onSeek(pParser->pReadSeekUserData, (int)bytesToSeek, DRWAV_SEEK_CUR)) {
2963 break;
2964 }
2965 bytesRead += bytesToSeek;
2966 }
2968 if ((subchunkDataSize % 2) == 1) {
2969 if (!pParser->onSeek(pParser->pReadSeekUserData, 1, DRWAV_SEEK_CUR)) {
2970 break;
2971 }
2972 bytesRead += 1;
2973 }
2974 }
2975 } else if ((allowedMetadataTypes & drwav_metadata_type_unknown) != 0) {
2976 bytesRead = drwav__metadata_process_unknown_chunk(pParser, pChunkID, pChunkHeader->sizeInBytes, drwav_metadata_location_top_level);
2977 }
2979 return bytesRead;
2980}
2983DRWAV_PRIVATE drwav_uint32 drwav_get_bytes_per_pcm_frame(drwav* pWav)
2984{
2985 drwav_uint32 bytesPerFrame;
2987 /*
2988 The bytes per frame is a bit ambiguous. It can be either be based on the bits per sample, or the block align. The way I'm doing it here
2989 is that if the bits per sample is a multiple of 8, use floor(bitsPerSample*channels/8), otherwise fall back to the block align.
2990 */
2991 if ((pWav->bitsPerSample & 0x7) == 0) {
2992 /* Bits per sample is a multiple of 8. */
2993 bytesPerFrame = (pWav->bitsPerSample * pWav->fmt.channels) >> 3;
2994 } else {
2995 bytesPerFrame = pWav->fmt.blockAlign;
2996 }
2998 /* Validation for known formats. a-law and mu-law should be 1 byte per channel. If it's not, it's not decodable. */
2999 if (pWav->translatedFormatTag == DR_WAVE_FORMAT_ALAW || pWav->translatedFormatTag == DR_WAVE_FORMAT_MULAW) {
3000 if (bytesPerFrame != pWav->fmt.channels) {
3001 return 0; /* Invalid file. */
3002 }
3003 }
3005 return bytesPerFrame;
3006}
3008DRWAV_API drwav_uint16 drwav_fmt_get_format(const drwav_fmt* pFMT)
3009{
3010 if (pFMT == NULL) {
3011 return 0;
3012 }
3014 if (pFMT->formatTag != DR_WAVE_FORMAT_EXTENSIBLE) {
3015 return pFMT->formatTag;
3016 } else {
3017 return drwav_bytes_to_u16(pFMT->subFormat); /* Only the first two bytes are required. */
3018 }
3019}
3021DRWAV_PRIVATE drwav_bool32 drwav_preinit(drwav* pWav, drwav_read_proc onRead, drwav_seek_proc onSeek, drwav_tell_proc onTell, void* pReadSeekTellUserData, const drwav_allocation_callbacks* pAllocationCallbacks)
3022{
3023 if (pWav == NULL || onRead == NULL || onSeek == NULL) { /* <-- onTell is optional. */
3024 return DRWAV_FALSE;
3025 }
3027 DRWAV_ZERO_MEMORY(pWav, sizeof(*pWav));
3028 pWav->onRead = onRead;
3029 pWav->onSeek = onSeek;
3030 pWav->onTell = onTell;
3031 pWav->pUserData = pReadSeekTellUserData;
3032 pWav->allocationCallbacks = drwav_copy_allocation_callbacks_or_defaults(pAllocationCallbacks);
3034 if (pWav->allocationCallbacks.onFree == NULL || (pWav->allocationCallbacks.onMalloc == NULL && pWav->allocationCallbacks.onRealloc == NULL)) {
3035 return DRWAV_FALSE; /* Invalid allocation callbacks. */
3036 }
3038 return DRWAV_TRUE;
3039}
3041DRWAV_PRIVATE drwav_bool32 drwav_init__internal(drwav* pWav, drwav_chunk_proc onChunk, void* pChunkUserData, drwav_uint32 flags)
3042{
3043 /* This function assumes drwav_preinit() has been called beforehand. */
3044 drwav_result result;
3045 drwav_uint64 cursor; /* <-- Keeps track of the byte position so we can seek to specific locations. */
3046 drwav_bool32 sequential;
3047 drwav_uint8 riff[4];
3048 drwav_fmt fmt;
3049 unsigned short translatedFormatTag;
3050 drwav_uint64 dataChunkSize = 0; /* <-- Important! Don't explicitly set this to 0 anywhere else. Calculation of the size of the data chunk is performed in different paths depending on the container. */
3051 drwav_uint64 sampleCountFromFactChunk = 0; /* Same as dataChunkSize - make sure this is the only place this is initialized to 0. */
3052 drwav_uint64 metadataStartPos;
3053 drwav__metadata_parser metadataParser;
3054 drwav_bool8 isProcessingMetadata = DRWAV_FALSE;
3055 drwav_bool8 foundChunk_fmt = DRWAV_FALSE;
3056 drwav_bool8 foundChunk_data = DRWAV_FALSE;
3057 drwav_bool8 isAIFCFormType = DRWAV_FALSE; /* Only used with AIFF. */
3058 drwav_uint64 aiffFrameCount = 0;
3060 cursor = 0;
3061 sequential = (flags & DRWAV_SEQUENTIAL) != 0;
3062 DRWAV_ZERO_OBJECT(&fmt);
3064 /* The first 4 bytes should be the RIFF identifier. */
3065 if (drwav__on_read(pWav->onRead, pWav->pUserData, riff, sizeof(riff), &cursor) != sizeof(riff)) {
3066 return DRWAV_FALSE;
3067 }
3069 /*
3070 The first 4 bytes can be used to identify the container. For RIFF files it will start with "RIFF" and for
3071 w64 it will start with "riff".
3072 */
3073 if (drwav_fourcc_equal(riff, "RIFF")) {
3074 pWav->container = drwav_container_riff;
3075 } else if (drwav_fourcc_equal(riff, "RIFX")) {
3076 pWav->container = drwav_container_rifx;
3077 } else if (drwav_fourcc_equal(riff, "riff")) {
3078 int i;
3079 drwav_uint8 riff2[12];
3081 pWav->container = drwav_container_w64;
3083 /* Check the rest of the GUID for validity. */
3084 if (drwav__on_read(pWav->onRead, pWav->pUserData, riff2, sizeof(riff2), &cursor) != sizeof(riff2)) {
3085 return DRWAV_FALSE;
3086 }
3088 for (i = 0; i < 12; ++i) {
3089 if (riff2[i] != drwavGUID_W64_RIFF[i+4]) {
3090 return DRWAV_FALSE;
3091 }
3092 }
3093 } else if (drwav_fourcc_equal(riff, "RF64")) {
3094 pWav->container = drwav_container_rf64;
3095 } else if (drwav_fourcc_equal(riff, "FORM")) {
3096 pWav->container = drwav_container_aiff;
3097 } else {
3098 return DRWAV_FALSE; /* Unknown or unsupported container. */
3099 }
3102 if (pWav->container == drwav_container_riff || pWav->container == drwav_container_rifx || pWav->container == drwav_container_rf64) {
3103 drwav_uint8 chunkSizeBytes[4];
3104 drwav_uint8 wave[4];
3106 if (drwav__on_read(pWav->onRead, pWav->pUserData, chunkSizeBytes, sizeof(chunkSizeBytes), &cursor) != sizeof(chunkSizeBytes)) {
3107 return DRWAV_FALSE;
3108 }
3110 if (pWav->container == drwav_container_riff || pWav->container == drwav_container_rifx) {
3111 if (drwav_bytes_to_u32_ex(chunkSizeBytes, pWav->container) < 36) {
3112 /*
3113 I've had a report of a WAV file failing to load when the size of the WAVE chunk is not encoded
3114 and is instead just set to 0. I'm going to relax the validation here to allow these files to
3115 load. Considering the chunk size isn't actually used this should be safe. With this change my
3116 test suite still passes.
3117 */
3118 /*return DRWAV_FALSE;*/ /* Chunk size should always be at least 36 bytes. */
3119 }
3120 } else if (pWav->container == drwav_container_rf64) {
3121 if (drwav_bytes_to_u32_le(chunkSizeBytes) != 0xFFFFFFFF) {
3122 return DRWAV_FALSE; /* Chunk size should always be set to -1/0xFFFFFFFF for RF64. The actual size is retrieved later. */
3123 }
3124 } else {
3125 return DRWAV_FALSE; /* Should never hit this. */
3126 }
3128 if (drwav__on_read(pWav->onRead, pWav->pUserData, wave, sizeof(wave), &cursor) != sizeof(wave)) {
3129 return DRWAV_FALSE;
3130 }
3132 if (!drwav_fourcc_equal(wave, "WAVE")) {
3133 return DRWAV_FALSE; /* Expecting "WAVE". */
3134 }
3135 } else if (pWav->container == drwav_container_w64) {
3136 drwav_uint8 chunkSizeBytes[8];
3137 drwav_uint8 wave[16];
3139 if (drwav__on_read(pWav->onRead, pWav->pUserData, chunkSizeBytes, sizeof(chunkSizeBytes), &cursor) != sizeof(chunkSizeBytes)) {
3140 return DRWAV_FALSE;
3141 }
3143 if (drwav_bytes_to_u64(chunkSizeBytes) < 80) {
3144 return DRWAV_FALSE;
3145 }
3147 if (drwav__on_read(pWav->onRead, pWav->pUserData, wave, sizeof(wave), &cursor) != sizeof(wave)) {
3148 return DRWAV_FALSE;
3149 }
3151 if (!drwav_guid_equal(wave, drwavGUID_W64_WAVE)) {
3152 return DRWAV_FALSE;
3153 }
3154 } else if (pWav->container == drwav_container_aiff) {
3155 drwav_uint8 chunkSizeBytes[4];
3156 drwav_uint8 aiff[4];
3158 if (drwav__on_read(pWav->onRead, pWav->pUserData, chunkSizeBytes, sizeof(chunkSizeBytes), &cursor) != sizeof(chunkSizeBytes)) {
3159 return DRWAV_FALSE;
3160 }
3162 if (drwav_bytes_to_u32_be(chunkSizeBytes) < 18) {
3163 return DRWAV_FALSE;
3164 }
3166 if (drwav__on_read(pWav->onRead, pWav->pUserData, aiff, sizeof(aiff), &cursor) != sizeof(aiff)) {
3167 return DRWAV_FALSE;
3168 }
3170 if (drwav_fourcc_equal(aiff, "AIFF")) {
3171 isAIFCFormType = DRWAV_FALSE;
3172 } else if (drwav_fourcc_equal(aiff, "AIFC")) {
3173 isAIFCFormType = DRWAV_TRUE;
3174 } else {
3175 return DRWAV_FALSE; /* Expecting "AIFF" or "AIFC". */
3176 }
3177 } else {
3178 return DRWAV_FALSE;
3179 }
3182 /* For RF64, the "ds64" chunk must come next, before the "fmt " chunk. */
3183 if (pWav->container == drwav_container_rf64) {
3184 drwav_uint8 sizeBytes[8];
3185 drwav_uint64 bytesRemainingInChunk;
3186 drwav_chunk_header header;
3187 result = drwav__read_chunk_header(pWav->onRead, pWav->pUserData, pWav->container, &cursor, &header);
3188 if (result != DRWAV_SUCCESS) {
3189 return DRWAV_FALSE;
3190 }
3192 if (!drwav_fourcc_equal(header.id.fourcc, "ds64")) {
3193 return DRWAV_FALSE; /* Expecting "ds64". */
3194 }
3196 bytesRemainingInChunk = header.sizeInBytes + header.paddingSize;
3198 /* We don't care about the size of the RIFF chunk - skip it. */
3199 if (!drwav__seek_forward(pWav->onSeek, 8, pWav->pUserData)) {
3200 return DRWAV_FALSE;
3201 }
3202 bytesRemainingInChunk -= 8;
3203 cursor += 8;
3206 /* Next 8 bytes is the size of the "data" chunk. */
3207 if (drwav__on_read(pWav->onRead, pWav->pUserData, sizeBytes, sizeof(sizeBytes), &cursor) != sizeof(sizeBytes)) {
3208 return DRWAV_FALSE;
3209 }
3210 bytesRemainingInChunk -= 8;
3211 dataChunkSize = drwav_bytes_to_u64(sizeBytes);
3214 /* Next 8 bytes is the same count which we would usually derived from the FACT chunk if it was available. */
3215 if (drwav__on_read(pWav->onRead, pWav->pUserData, sizeBytes, sizeof(sizeBytes), &cursor) != sizeof(sizeBytes)) {
3216 return DRWAV_FALSE;
3217 }
3218 bytesRemainingInChunk -= 8;
3219 sampleCountFromFactChunk = drwav_bytes_to_u64(sizeBytes);
3222 /* Skip over everything else. */
3223 if (!drwav__seek_forward(pWav->onSeek, bytesRemainingInChunk, pWav->pUserData)) {
3224 return DRWAV_FALSE;
3225 }
3226 cursor += bytesRemainingInChunk;
3227 }
3230 metadataStartPos = cursor;
3232 /*
3233 Whether or not we are processing metadata controls how we load. We can load more efficiently when
3234 metadata is not being processed, but we also cannot process metadata for Wave64 because I have not
3235 been able to test it. If someone is able to test this and provide a patch I'm happy to enable it.
3237 Seqential mode cannot support metadata because it involves seeking backwards.
3238 */
3239 isProcessingMetadata = !sequential && ((flags & DRWAV_WITH_METADATA) != 0);
3241 /* Don't allow processing of metadata with untested containers. */
3242 if (pWav->container != drwav_container_riff && pWav->container != drwav_container_rf64) {
3243 isProcessingMetadata = DRWAV_FALSE;
3244 }
3246 DRWAV_ZERO_MEMORY(&metadataParser, sizeof(metadataParser));
3247 if (isProcessingMetadata) {
3248 metadataParser.onRead = pWav->onRead;
3249 metadataParser.onSeek = pWav->onSeek;
3250 metadataParser.pReadSeekUserData = pWav->pUserData;
3251 metadataParser.stage = drwav__metadata_parser_stage_count;
3252 }
3255 /*
3256 From here on out, chunks might be in any order. In order to robustly handle metadata we'll need
3257 to loop through every chunk and handle them as we find them. In sequential mode we need to get
3258 out of the loop as soon as we find the data chunk because we won't be able to seek back.
3259 */
3260 for (;;) { /* For each chunk... */
3261 drwav_chunk_header header;
3262 drwav_uint64 chunkSize;
3264 result = drwav__read_chunk_header(pWav->onRead, pWav->pUserData, pWav->container, &cursor, &header);
3265 if (result != DRWAV_SUCCESS) {
3266 break;
3267 }
3269 chunkSize = header.sizeInBytes;
3272 /*
3273 Always tell the caller about this chunk. We cannot do this in sequential mode because the
3274 callback is allowed to read from the file, in which case we'll need to rewind.
3275 */
3276 if (!sequential && onChunk != NULL) {
3277 drwav_uint64 callbackBytesRead = onChunk(pChunkUserData, pWav->onRead, pWav->onSeek, pWav->pUserData, &header, pWav->container, &fmt);
3279 /*
3280 dr_wav may need to read the contents of the chunk, so we now need to seek back to the position before
3281 we called the callback.
3282 */
3283 if (callbackBytesRead > 0) {
3284 if (drwav__seek_from_start(pWav->onSeek, cursor, pWav->pUserData) == DRWAV_FALSE) {
3285 return DRWAV_FALSE;
3286 }
3287 }
3288 }
3291 /* Explicitly handle known chunks first. */
3293 /* "fmt " */
3294 if (((pWav->container == drwav_container_riff || pWav->container == drwav_container_rifx || pWav->container == drwav_container_rf64) && drwav_fourcc_equal(header.id.fourcc, "fmt ")) ||
3295 ((pWav->container == drwav_container_w64) && drwav_guid_equal(header.id.guid, drwavGUID_W64_FMT))) {
3296 drwav_uint8 fmtData[16];
3298 foundChunk_fmt = DRWAV_TRUE;
3300 if (pWav->onRead(pWav->pUserData, fmtData, sizeof(fmtData)) != sizeof(fmtData)) {
3301 return DRWAV_FALSE;
3302 }
3303 cursor += sizeof(fmtData);
3305 fmt.formatTag = drwav_bytes_to_u16_ex(fmtData + 0, pWav->container);
3306 fmt.channels = drwav_bytes_to_u16_ex(fmtData + 2, pWav->container);
3307 fmt.sampleRate = drwav_bytes_to_u32_ex(fmtData + 4, pWav->container);
3308 fmt.avgBytesPerSec = drwav_bytes_to_u32_ex(fmtData + 8, pWav->container);
3309 fmt.blockAlign = drwav_bytes_to_u16_ex(fmtData + 12, pWav->container);
3310 fmt.bitsPerSample = drwav_bytes_to_u16_ex(fmtData + 14, pWav->container);
3312 fmt.extendedSize = 0;
3313 fmt.validBitsPerSample = 0;
3314 fmt.channelMask = 0;
3315 DRWAV_ZERO_MEMORY(fmt.subFormat, sizeof(fmt.subFormat));
3317 if (header.sizeInBytes > 16) {
3318 drwav_uint8 fmt_cbSize[2];
3319 int bytesReadSoFar = 0;
3321 if (pWav->onRead(pWav->pUserData, fmt_cbSize, sizeof(fmt_cbSize)) != sizeof(fmt_cbSize)) {
3322 return DRWAV_FALSE; /* Expecting more data. */
3323 }
3324 cursor += sizeof(fmt_cbSize);
3326 bytesReadSoFar = 18;
3328 fmt.extendedSize = drwav_bytes_to_u16_ex(fmt_cbSize, pWav->container);
3329 if (fmt.extendedSize > 0) {
3330 /* Simple validation. */
3331 if (fmt.formatTag == DR_WAVE_FORMAT_EXTENSIBLE) {
3332 if (fmt.extendedSize != 22) {
3333 return DRWAV_FALSE;
3334 }
3335 }
3337 if (fmt.formatTag == DR_WAVE_FORMAT_EXTENSIBLE) {
3338 drwav_uint8 fmtext[22];
3340 if (pWav->onRead(pWav->pUserData, fmtext, fmt.extendedSize) != fmt.extendedSize) {
3341 return DRWAV_FALSE; /* Expecting more data. */
3342 }
3344 fmt.validBitsPerSample = drwav_bytes_to_u16_ex(fmtext + 0, pWav->container);
3345 fmt.channelMask = drwav_bytes_to_u32_ex(fmtext + 2, pWav->container);
3346 drwav_bytes_to_guid(fmtext + 6, fmt.subFormat);
3347 } else {
3348 if (pWav->onSeek(pWav->pUserData, fmt.extendedSize, DRWAV_SEEK_CUR) == DRWAV_FALSE) {
3349 return DRWAV_FALSE;
3350 }
3351 }
3352 cursor += fmt.extendedSize;
3354 bytesReadSoFar += fmt.extendedSize;
3355 }
3357 /* Seek past any leftover bytes. For w64 the leftover will be defined based on the chunk size. */
3358 if (pWav->onSeek(pWav->pUserData, (int)(header.sizeInBytes - bytesReadSoFar), DRWAV_SEEK_CUR) == DRWAV_FALSE) {
3359 return DRWAV_FALSE;
3360 }
3361 cursor += (header.sizeInBytes - bytesReadSoFar);
3362 }
3364 if (header.paddingSize > 0) {
3365 if (drwav__seek_forward(pWav->onSeek, header.paddingSize, pWav->pUserData) == DRWAV_FALSE) {
3366 break;
3367 }
3368 cursor += header.paddingSize;
3369 }
3371 /* Go to the next chunk. Don't include this chunk in metadata. */
3372 continue;
3373 }
3375 /* "data" */
3376 if (((pWav->container == drwav_container_riff || pWav->container == drwav_container_rifx || pWav->container == drwav_container_rf64) && drwav_fourcc_equal(header.id.fourcc, "data")) ||
3377 ((pWav->container == drwav_container_w64) && drwav_guid_equal(header.id.guid, drwavGUID_W64_DATA))) {
3378 foundChunk_data = DRWAV_TRUE;
3380 pWav->dataChunkDataPos = cursor;
3382 if (pWav->container != drwav_container_rf64) { /* The data chunk size for RF64 will always be set to 0xFFFFFFFF here. It was set to it's true value earlier. */
3383 dataChunkSize = chunkSize;
3384 }
3386 /* If we're running in sequential mode, or we're not reading metadata, we have enough now that we can get out of the loop. */
3387 if (sequential || !isProcessingMetadata) {
3388 break; /* No need to keep reading beyond the data chunk. */
3389 } else {
3390 chunkSize += header.paddingSize; /* <-- Make sure we seek past the padding. */
3391 if (drwav__seek_forward(pWav->onSeek, chunkSize, pWav->pUserData) == DRWAV_FALSE) {
3392 break;
3393 }
3394 cursor += chunkSize;
3396 continue; /* There may be some more metadata to read. */
3397 }
3398 }
3400 /* "fact". This is optional. Can use this to get the sample count which is useful for compressed formats. For RF64 we retrieved the sample count from the ds64 chunk earlier. */
3401 if (((pWav->container == drwav_container_riff || pWav->container == drwav_container_rifx || pWav->container == drwav_container_rf64) && drwav_fourcc_equal(header.id.fourcc, "fact")) ||
3402 ((pWav->container == drwav_container_w64) && drwav_guid_equal(header.id.guid, drwavGUID_W64_FACT))) {
3403 if (pWav->container == drwav_container_riff || pWav->container == drwav_container_rifx) {
3404 drwav_uint8 sampleCount[4];
3405 if (drwav__on_read(pWav->onRead, pWav->pUserData, &sampleCount, 4, &cursor) != 4) {
3406 return DRWAV_FALSE;
3407 }
3409 chunkSize -= 4;
3411 /*
3412 The sample count in the "fact" chunk is either unreliable, or I'm not understanding it properly. For now I am only enabling this
3413 for Microsoft ADPCM formats.
3414 */
3415 if (pWav->translatedFormatTag == DR_WAVE_FORMAT_ADPCM) {
3416 sampleCountFromFactChunk = drwav_bytes_to_u32_ex(sampleCount, pWav->container);
3417 } else {
3418 sampleCountFromFactChunk = 0;
3419 }
3420 } else if (pWav->container == drwav_container_w64) {
3421 if (drwav__on_read(pWav->onRead, pWav->pUserData, &sampleCountFromFactChunk, 8, &cursor) != 8) {
3422 return DRWAV_FALSE;
3423 }
3425 chunkSize -= 8;
3426 } else if (pWav->container == drwav_container_rf64) {
3427 /* We retrieved the sample count from the ds64 chunk earlier so no need to do that here. */
3428 }
3430 /* Seek to the next chunk in preparation for the next iteration. */
3431 chunkSize += header.paddingSize; /* <-- Make sure we seek past the padding. */
3432 if (drwav__seek_forward(pWav->onSeek, chunkSize, pWav->pUserData) == DRWAV_FALSE) {
3433 break;
3434 }
3435 cursor += chunkSize;
3437 continue;
3438 }
3441 /* "COMM". AIFF/AIFC only. */
3442 if (pWav->container == drwav_container_aiff && drwav_fourcc_equal(header.id.fourcc, "COMM")) {
3443 drwav_uint8 commData[24];
3444 drwav_uint32 commDataBytesToRead;
3445 drwav_uint16 channels;
3446 drwav_uint32 frameCount;
3447 drwav_uint16 sampleSizeInBits;
3448 drwav_int64 sampleRate;
3449 drwav_uint16 compressionFormat;
3451 foundChunk_fmt = DRWAV_TRUE;
3453 if (isAIFCFormType) {
3454 commDataBytesToRead = 24;
3455 if (header.sizeInBytes < commDataBytesToRead) {
3456 return DRWAV_FALSE; /* Invalid COMM chunk. */
3457 }
3458 } else {
3459 commDataBytesToRead = 18;
3460 if (header.sizeInBytes != commDataBytesToRead) {
3461 return DRWAV_FALSE; /* INVALID COMM chunk. */
3462 }
3463 }
3465 if (drwav__on_read(pWav->onRead, pWav->pUserData, commData, commDataBytesToRead, &cursor) != commDataBytesToRead) {
3466 return DRWAV_FALSE;
3467 }
3470 channels = drwav_bytes_to_u16_ex (commData + 0, pWav->container);
3471 frameCount = drwav_bytes_to_u32_ex (commData + 2, pWav->container);
3472 sampleSizeInBits = drwav_bytes_to_u16_ex (commData + 6, pWav->container);
3473 sampleRate = drwav_aiff_extented_to_s64(commData + 8);
3475 if (sampleRate < 0 || sampleRate > 0xFFFFFFFF) {
3476 return DRWAV_FALSE; /* Invalid sample rate. */
3477 }
3479 if (isAIFCFormType) {
3480 const drwav_uint8* type = commData + 18;
3482 if (drwav_fourcc_equal(type, "NONE")) {
3483 compressionFormat = DR_WAVE_FORMAT_PCM; /* PCM, big-endian. */
3484 } else if (drwav_fourcc_equal(type, "raw ")) {
3485 compressionFormat = DR_WAVE_FORMAT_PCM;
3487 /* In my testing, it looks like when the "raw " compression type is used, 8-bit samples should be considered unsigned. */
3488 if (sampleSizeInBits == 8) {
3489 pWav->aiff.isUnsigned = DRWAV_TRUE;
3490 }
3491 } else if (drwav_fourcc_equal(type, "sowt")) {
3492 compressionFormat = DR_WAVE_FORMAT_PCM; /* PCM, little-endian. */
3493 pWav->aiff.isLE = DRWAV_TRUE;
3494 } else if (drwav_fourcc_equal(type, "fl32") || drwav_fourcc_equal(type, "fl64") || drwav_fourcc_equal(type, "FL32") || drwav_fourcc_equal(type, "FL64")) {
3495 compressionFormat = DR_WAVE_FORMAT_IEEE_FLOAT;
3496 } else if (drwav_fourcc_equal(type, "alaw") || drwav_fourcc_equal(type, "ALAW")) {
3497 compressionFormat = DR_WAVE_FORMAT_ALAW;
3498 } else if (drwav_fourcc_equal(type, "ulaw") || drwav_fourcc_equal(type, "ULAW")) {
3499 compressionFormat = DR_WAVE_FORMAT_MULAW;
3500 } else if (drwav_fourcc_equal(type, "ima4")) {
3501 compressionFormat = DR_WAVE_FORMAT_DVI_ADPCM;
3502 sampleSizeInBits = 4;
3504 /*
3505 I haven't been able to figure out how to get correct decoding for IMA ADPCM. Until this is figured out
3506 we'll need to abort when we encounter such an encoding. Advice welcome!
3507 */
3508 (void)compressionFormat;
3509 (void)sampleSizeInBits;
3511 return DRWAV_FALSE;
3512 } else {
3513 return DRWAV_FALSE; /* Unknown or unsupported compression format. Need to abort. */
3514 }
3515 } else {
3516 compressionFormat = DR_WAVE_FORMAT_PCM; /* It's a standard AIFF form which is always compressed. */
3517 }
3519 /* With AIFF we want to use the explicitly defined frame count rather than deriving it from the size of the chunk. */
3520 aiffFrameCount = frameCount;
3522 /* We should now have enough information to fill out our fmt structure. */
3523 fmt.formatTag = compressionFormat;
3524 fmt.channels = channels;
3525 fmt.sampleRate = (drwav_uint32)sampleRate;
3526 fmt.bitsPerSample = sampleSizeInBits;
3527 fmt.blockAlign = (drwav_uint16)(fmt.channels * fmt.bitsPerSample / 8);
3528 fmt.avgBytesPerSec = fmt.blockAlign * fmt.sampleRate;
3530 if (fmt.blockAlign == 0 && compressionFormat == DR_WAVE_FORMAT_DVI_ADPCM) {
3531 fmt.blockAlign = 34 * fmt.channels;
3532 }
3534 /*
3535 Weird one. I've seen some alaw and ulaw encoded files that for some reason set the bits per sample to 16 when
3536 it should be 8. To get this working I need to explicitly check for this and change it.
3537 */
3538 if (compressionFormat == DR_WAVE_FORMAT_ALAW || compressionFormat == DR_WAVE_FORMAT_MULAW) {
3539 if (fmt.bitsPerSample > 8) {
3540 fmt.bitsPerSample = 8;
3541 fmt.blockAlign = fmt.channels;
3542 }
3543 }
3545 /* In AIFF, samples are padded to 8 byte boundaries. We need to round up our bits per sample here. */
3546 fmt.bitsPerSample += (fmt.bitsPerSample & 7);
3549 /* If the form type is AIFC there will be some additional data in the chunk. We need to seek past it. */
3550 if (isAIFCFormType) {
3551 if (drwav__seek_forward(pWav->onSeek, (chunkSize - commDataBytesToRead), pWav->pUserData) == DRWAV_FALSE) {
3552 return DRWAV_FALSE;
3553 }
3554 cursor += (chunkSize - commDataBytesToRead);
3555 }
3557 /* Don't fall through or else we'll end up treating this chunk as metadata which is incorrect. */
3558 continue;
3559 }
3562 /* "SSND". AIFF/AIFC only. This is the AIFF equivalent of the "data" chunk. */
3563 if (pWav->container == drwav_container_aiff && drwav_fourcc_equal(header.id.fourcc, "SSND")) {
3564 drwav_uint8 offsetAndBlockSizeData[8];
3565 drwav_uint32 offset;
3567 foundChunk_data = DRWAV_TRUE;
3569 if (drwav__on_read(pWav->onRead, pWav->pUserData, offsetAndBlockSizeData, sizeof(offsetAndBlockSizeData), &cursor) != sizeof(offsetAndBlockSizeData)) {
3570 return DRWAV_FALSE;
3571 }
3573 /* The position of the audio data starts at an offset. */
3574 offset = drwav_bytes_to_u32_ex(offsetAndBlockSizeData + 0, pWav->container);
3575 pWav->dataChunkDataPos = cursor + offset;
3577 /* The data chunk size needs to be reduced by the offset or else seeking will break. */
3578 dataChunkSize = chunkSize;
3579 if (dataChunkSize > offset) {
3580 dataChunkSize -= offset;
3581 } else {
3582 dataChunkSize = 0;
3583 }
3585 if (sequential) {
3586 if (foundChunk_fmt) { /* <-- Name is misleading, but will be set to true if the COMM chunk has been parsed. */
3587 /*
3588 Getting here means we're opening in sequential mode and we've found the SSND (data) and COMM (fmt) chunks. We need
3589 to get out of the loop here or else we'll end up going past the data chunk and will have no way of getting back to
3590 it since we're not allowed to seek backwards.
3592 One subtle detail here is that there is an offset with the SSND chunk. We need to make sure we seek past this offset
3593 so we're left sitting on the first byte of actual audio data.
3594 */
3595 if (drwav__seek_forward(pWav->onSeek, offset, pWav->pUserData) == DRWAV_FALSE) {
3596 return DRWAV_FALSE;
3597 }
3598 cursor += offset;
3600 break;
3601 } else {
3602 /*
3603 Getting here means the COMM chunk was not found. In sequential mode, if we haven't yet found the COMM chunk
3604 we'll need to abort because we can't be doing a backwards seek back to the SSND chunk in order to read the
3605 data. For this reason, this configuration of AIFF files are not supported with sequential mode.
3606 */
3607 return DRWAV_FALSE;
3608 }
3609 } else {
3610 chunkSize += header.paddingSize; /* <-- Make sure we seek past the padding. */
3611 chunkSize -= sizeof(offsetAndBlockSizeData); /* <-- This was read earlier. */
3613 if (drwav__seek_forward(pWav->onSeek, chunkSize, pWav->pUserData) == DRWAV_FALSE) {
3614 break;
3615 }
3616 cursor += chunkSize;
3618 continue; /* There may be some more metadata to read. */
3619 }
3620 }
3623 /* Getting here means it's not a chunk that we care about internally, but might need to be handled as metadata by the caller. */
3624 if (isProcessingMetadata) {
3625 drwav__metadata_process_chunk(&metadataParser, &header, drwav_metadata_type_all_including_unknown);
3627 /* Go back to the start of the chunk so we can normalize the position of the cursor. */
3628 if (drwav__seek_from_start(pWav->onSeek, cursor, pWav->pUserData) == DRWAV_FALSE) {
3629 break; /* Failed to seek. Can't reliable read the remaining chunks. Get out. */
3630 }
3631 }
3634 /* Make sure we skip past the content of this chunk before we go to the next one. */
3635 chunkSize += header.paddingSize; /* <-- Make sure we seek past the padding. */
3636 if (drwav__seek_forward(pWav->onSeek, chunkSize, pWav->pUserData) == DRWAV_FALSE) {
3637 break;
3638 }
3639 cursor += chunkSize;
3640 }
3642 /* There's some mandatory chunks that must exist. If they were not found in the iteration above we must abort. */
3643 if (!foundChunk_fmt || !foundChunk_data) {
3644 return DRWAV_FALSE;
3645 }
3647 /* Basic validation. */
3648 if ((fmt.sampleRate == 0 || fmt.sampleRate > DRWAV_MAX_SAMPLE_RATE ) ||
3649 (fmt.channels == 0 || fmt.channels > DRWAV_MAX_CHANNELS ) ||
3650 (fmt.bitsPerSample == 0 || fmt.bitsPerSample > DRWAV_MAX_BITS_PER_SAMPLE) ||
3651 fmt.blockAlign == 0) {
3652 return DRWAV_FALSE; /* Probably an invalid WAV file. */
3653 }
3655 /* Translate the internal format. */
3656 translatedFormatTag = fmt.formatTag;
3657 if (translatedFormatTag == DR_WAVE_FORMAT_EXTENSIBLE) {
3658 translatedFormatTag = drwav_bytes_to_u16_ex(fmt.subFormat + 0, pWav->container);
3659 }
3661 /* 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. */
3662 if (!sequential) {
3663 if (!drwav__seek_from_start(pWav->onSeek, pWav->dataChunkDataPos, pWav->pUserData)) {
3664 return DRWAV_FALSE;
3665 }
3666 cursor = pWav->dataChunkDataPos;
3667 }
3670 /*
3671 At this point we should have done the initial parsing of each of our chunks, but we now need to
3672 do a second pass to extract the actual contents of the metadata (the first pass just calculated
3673 the length of the memory allocation).
3675 We only do this if we've actually got metadata to parse.
3676 */
3677 if (isProcessingMetadata && metadataParser.metadataCount > 0) {
3678 if (drwav__seek_from_start(pWav->onSeek, metadataStartPos, pWav->pUserData) == DRWAV_FALSE) {
3679 return DRWAV_FALSE;
3680 }
3682 result = drwav__metadata_alloc(&metadataParser, &pWav->allocationCallbacks);
3683 if (result != DRWAV_SUCCESS) {
3684 return DRWAV_FALSE;
3685 }
3687 metadataParser.stage = drwav__metadata_parser_stage_read;
3689 for (;;) {
3690 drwav_chunk_header header;
3691 drwav_uint64 metadataBytesRead;
3693 result = drwav__read_chunk_header(pWav->onRead, pWav->pUserData, pWav->container, &cursor, &header);
3694 if (result != DRWAV_SUCCESS) {
3695 break;
3696 }
3698 metadataBytesRead = drwav__metadata_process_chunk(&metadataParser, &header, drwav_metadata_type_all_including_unknown);
3700 /* Move to the end of the chunk so we can keep iterating. */
3701 if (drwav__seek_forward(pWav->onSeek, (header.sizeInBytes + header.paddingSize) - metadataBytesRead, pWav->pUserData) == DRWAV_FALSE) {
3702 drwav_free(metadataParser.pMetadata, &pWav->allocationCallbacks);
3703 return DRWAV_FALSE;
3704 }
3705 }
3707 /* Getting here means we're finished parsing the metadata. */
3708 pWav->pMetadata = metadataParser.pMetadata;
3709 pWav->metadataCount = metadataParser.metadataCount;
3710 }
3712 /*
3713 It's possible for the size reported in the data chunk to be greater than that of the file. We
3714 need to do a validation check here to make sure we don't exceed the file size. To skip this
3715 check, set the onTell callback to NULL.
3716 */
3717 if (pWav->onTell != NULL && pWav->onSeek != NULL) {
3718 if (pWav->onSeek(pWav->pUserData, 0, DRWAV_SEEK_END) == DRWAV_TRUE) {
3719 drwav_int64 fileSize;
3720 if (pWav->onTell(pWav->pUserData, &fileSize)) {
3721 if (dataChunkSize + pWav->dataChunkDataPos > (drwav_uint64)fileSize) {
3722 dataChunkSize = (drwav_uint64)fileSize - pWav->dataChunkDataPos;
3723 }
3724 }
3725 } else {
3726 /*
3727 Failed to seek to the end of the file. It might not be supported by the backend so in
3728 this case we cannot perform the validation check.
3729 */
3730 }
3731 }
3733 /*
3734 I've seen a WAV file in the wild where a RIFF-ecapsulated file has the size of it's "RIFF" and
3735 "data" chunks set to 0xFFFFFFFF when the file is definitely not that big. In this case we're
3736 going to have to calculate the size by reading and discarding bytes, and then seeking back. We
3737 cannot do this in sequential mode. We just assume that the rest of the file is audio data.
3738 */
3739 if (dataChunkSize == 0xFFFFFFFF && (pWav->container == drwav_container_riff || pWav->container == drwav_container_rifx) && pWav->isSequentialWrite == DRWAV_FALSE) {
3740 dataChunkSize = 0;
3742 for (;;) {
3743 drwav_uint8 temp[4096];
3744 size_t bytesRead = pWav->onRead(pWav->pUserData, temp, sizeof(temp));
3745 dataChunkSize += bytesRead;
3747 if (bytesRead < sizeof(temp)) {
3748 break;
3749 }
3750 }
3751 }
3753 /* At this point we want to be sitting on the first byte of the raw audio data. */
3754 if (drwav__seek_from_start(pWav->onSeek, pWav->dataChunkDataPos, pWav->pUserData) == DRWAV_FALSE) {
3755 drwav_free(pWav->pMetadata, &pWav->allocationCallbacks);
3756 return DRWAV_FALSE;
3757 }
3760 pWav->fmt = fmt;
3761 pWav->sampleRate = fmt.sampleRate;
3762 pWav->channels = fmt.channels;
3763 pWav->bitsPerSample = fmt.bitsPerSample;
3764 pWav->translatedFormatTag = translatedFormatTag;
3766 /*
3767 I've had a report where files would start glitching after seeking. The reason for this is the data
3768 chunk is not a clean multiple of the PCM frame size in bytes. Where this becomes a problem is when
3769 seeking, because the number of bytes remaining in the data chunk is used to calculate the current
3770 byte position. If this byte position is not aligned to the number of bytes in a PCM frame, it will
3771 result in the seek not being cleanly positioned at the start of the PCM frame thereby resulting in
3772 all decoded frames after that being corrupted.
3774 To address this, we need to round the data chunk size down to the nearest multiple of the frame size.
3775 */
3776 if (!drwav__is_compressed_format_tag(translatedFormatTag)) {
3777 drwav_uint32 bytesPerFrame = drwav_get_bytes_per_pcm_frame(pWav);
3778 if (bytesPerFrame > 0) {
3779 dataChunkSize -= (dataChunkSize % bytesPerFrame);
3780 }
3781 }
3783 pWav->bytesRemaining = dataChunkSize;
3784 pWav->dataChunkDataSize = dataChunkSize;
3786 if (sampleCountFromFactChunk != 0) {
3787 pWav->totalPCMFrameCount = sampleCountFromFactChunk;
3788 } else if (aiffFrameCount != 0) {
3789 pWav->totalPCMFrameCount = aiffFrameCount;
3790 } else {
3791 drwav_uint32 bytesPerFrame = drwav_get_bytes_per_pcm_frame(pWav);
3792 if (bytesPerFrame == 0) {
3793 drwav_free(pWav->pMetadata, &pWav->allocationCallbacks);
3794 return DRWAV_FALSE; /* Invalid file. */
3795 }
3797 pWav->totalPCMFrameCount = dataChunkSize / bytesPerFrame;
3799 if (pWav->translatedFormatTag == DR_WAVE_FORMAT_ADPCM) {
3800 drwav_uint64 totalBlockHeaderSizeInBytes;
3801 drwav_uint64 blockCount = dataChunkSize / fmt.blockAlign;
3803 /* Make sure any trailing partial block is accounted for. */
3804 if ((blockCount * fmt.blockAlign) < dataChunkSize) {
3805 blockCount += 1;
3806 }
3808 /* We decode two samples per byte. There will be blockCount headers in the data chunk. This is enough to know how to calculate the total PCM frame count. */
3809 totalBlockHeaderSizeInBytes = blockCount * (6*fmt.channels);
3810 pWav->totalPCMFrameCount = ((dataChunkSize - totalBlockHeaderSizeInBytes) * 2) / fmt.channels;
3811 }
3812 if (pWav->translatedFormatTag == DR_WAVE_FORMAT_DVI_ADPCM) {
3813 drwav_uint64 totalBlockHeaderSizeInBytes;
3814 drwav_uint64 blockCount = dataChunkSize / fmt.blockAlign;
3816 /* Make sure any trailing partial block is accounted for. */
3817 if ((blockCount * fmt.blockAlign) < dataChunkSize) {
3818 blockCount += 1;
3819 }
3821 /* We decode two samples per byte. There will be blockCount headers in the data chunk. This is enough to know how to calculate the total PCM frame count. */
3822 totalBlockHeaderSizeInBytes = blockCount * (4*fmt.channels);
3823 pWav->totalPCMFrameCount = ((dataChunkSize - totalBlockHeaderSizeInBytes) * 2) / fmt.channels;
3825 /* The header includes a decoded sample for each channel which acts as the initial predictor sample. */
3826 pWav->totalPCMFrameCount += blockCount;
3827 }
3828 }
3830 /* Some formats only support a certain number of channels. */
3831 if (pWav->translatedFormatTag == DR_WAVE_FORMAT_ADPCM || pWav->translatedFormatTag == DR_WAVE_FORMAT_DVI_ADPCM) {
3832 if (pWav->channels > 2) {
3833 drwav_free(pWav->pMetadata, &pWav->allocationCallbacks);
3834 return DRWAV_FALSE;
3835 }
3836 }
3838 /* The number of bytes per frame must be known. If not, it's an invalid file and not decodable. */
3839 if (drwav_get_bytes_per_pcm_frame(pWav) == 0) {
3840 drwav_free(pWav->pMetadata, &pWav->allocationCallbacks);
3841 return DRWAV_FALSE;
3842 }
3844#ifdef DR_WAV_LIBSNDFILE_COMPAT
3845 /*
3846 I use libsndfile as a benchmark for testing, however in the version I'm using (from the Windows installer on the libsndfile website),
3847 it appears the total sample count libsndfile uses for MS-ADPCM is incorrect. It would seem they are computing the total sample count
3848 from the number of blocks, however this results in the inclusion of extra silent samples at the end of the last block. The correct
3849 way to know the total sample count is to inspect the "fact" chunk, which should always be present for compressed formats, and should
3850 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
3851 correctness tests against libsndfile, and is disabled by default.
3852 */
3853 if (pWav->translatedFormatTag == DR_WAVE_FORMAT_ADPCM) {
3854 drwav_uint64 blockCount = dataChunkSize / fmt.blockAlign;
3855 pWav->totalPCMFrameCount = (((blockCount * (fmt.blockAlign - (6*pWav->channels))) * 2)) / fmt.channels; /* x2 because two samples per byte. */
3856 }
3857 if (pWav->translatedFormatTag == DR_WAVE_FORMAT_DVI_ADPCM) {
3858 drwav_uint64 blockCount = dataChunkSize / fmt.blockAlign;
3859 pWav->totalPCMFrameCount = (((blockCount * (fmt.blockAlign - (4*pWav->channels))) * 2) + (blockCount * pWav->channels)) / fmt.channels;
3860 }
3861#endif
3863 return DRWAV_TRUE;
3864}
3866DRWAV_API drwav_bool32 drwav_init(drwav* pWav, drwav_read_proc onRead, drwav_seek_proc onSeek, drwav_tell_proc onTell, void* pUserData, const drwav_allocation_callbacks* pAllocationCallbacks)
3867{
3868 return drwav_init_ex(pWav, onRead, onSeek, onTell, NULL, pUserData, NULL, 0, pAllocationCallbacks);
3869}
3871DRWAV_API drwav_bool32 drwav_init_ex(drwav* pWav, drwav_read_proc onRead, drwav_seek_proc onSeek, drwav_tell_proc onTell, drwav_chunk_proc onChunk, void* pReadSeekTellUserData, void* pChunkUserData, drwav_uint32 flags, const drwav_allocation_callbacks* pAllocationCallbacks)
3872{
3873 if (!drwav_preinit(pWav, onRead, onSeek, onTell, pReadSeekTellUserData, pAllocationCallbacks)) {
3874 return DRWAV_FALSE;
3875 }
3877 return drwav_init__internal(pWav, onChunk, pChunkUserData, flags);
3878}
3880DRWAV_API drwav_bool32 drwav_init_with_metadata(drwav* pWav, drwav_read_proc onRead, drwav_seek_proc onSeek, drwav_tell_proc onTell, void* pUserData, drwav_uint32 flags, const drwav_allocation_callbacks* pAllocationCallbacks)
3881{
3882 if (!drwav_preinit(pWav, onRead, onSeek, onTell, pUserData, pAllocationCallbacks)) {
3883 return DRWAV_FALSE;
3884 }
3886 return drwav_init__internal(pWav, NULL, NULL, flags | DRWAV_WITH_METADATA);
3887}
3889DRWAV_API drwav_metadata* drwav_take_ownership_of_metadata(drwav* pWav)
3890{
3891 drwav_metadata *result = pWav->pMetadata;
3893 pWav->pMetadata = NULL;
3894 pWav->metadataCount = 0;
3896 return result;
3897}
3900DRWAV_PRIVATE size_t drwav__write(drwav* pWav, const void* pData, size_t dataSize)
3901{
3902 DRWAV_ASSERT(pWav != NULL);
3903 DRWAV_ASSERT(pWav->onWrite != NULL);
3905 /* Generic write. Assumes no byte reordering required. */
3906 return pWav->onWrite(pWav->pUserData, pData, dataSize);
3907}
3909DRWAV_PRIVATE size_t drwav__write_byte(drwav* pWav, drwav_uint8 byte)
3910{
3911 DRWAV_ASSERT(pWav != NULL);
3912 DRWAV_ASSERT(pWav->onWrite != NULL);
3914 return pWav->onWrite(pWav->pUserData, &byte, 1);
3915}
3917DRWAV_PRIVATE size_t drwav__write_u16ne_to_le(drwav* pWav, drwav_uint16 value)
3918{
3919 DRWAV_ASSERT(pWav != NULL);
3920 DRWAV_ASSERT(pWav->onWrite != NULL);
3922 if (!drwav__is_little_endian()) {
3923 value = drwav__bswap16(value);
3924 }
3926 return drwav__write(pWav, &value, 2);
3927}
3929DRWAV_PRIVATE size_t drwav__write_u32ne_to_le(drwav* pWav, drwav_uint32 value)
3930{
3931 DRWAV_ASSERT(pWav != NULL);
3932 DRWAV_ASSERT(pWav->onWrite != NULL);
3934 if (!drwav__is_little_endian()) {
3935 value = drwav__bswap32(value);
3936 }
3938 return drwav__write(pWav, &value, 4);
3939}
3941DRWAV_PRIVATE size_t drwav__write_u64ne_to_le(drwav* pWav, drwav_uint64 value)
3942{
3943 DRWAV_ASSERT(pWav != NULL);
3944 DRWAV_ASSERT(pWav->onWrite != NULL);
3946 if (!drwav__is_little_endian()) {
3947 value = drwav__bswap64(value);
3948 }
3950 return drwav__write(pWav, &value, 8);
3951}
3953DRWAV_PRIVATE size_t drwav__write_f32ne_to_le(drwav* pWav, float value)
3954{
3955 union {
3956 drwav_uint32 u32;
3957 float f32;
3958 } u;
3960 DRWAV_ASSERT(pWav != NULL);
3961 DRWAV_ASSERT(pWav->onWrite != NULL);
3963 u.f32 = value;
3965 if (!drwav__is_little_endian()) {
3966 u.u32 = drwav__bswap32(u.u32);
3967 }
3969 return drwav__write(pWav, &u.u32, 4);
3970}
3972DRWAV_PRIVATE size_t drwav__write_or_count(drwav* pWav, const void* pData, size_t dataSize)
3973{
3974 if (pWav == NULL) {
3975 return dataSize;
3976 }
3978 return drwav__write(pWav, pData, dataSize);
3979}
3981DRWAV_PRIVATE size_t drwav__write_or_count_byte(drwav* pWav, drwav_uint8 byte)
3982{
3983 if (pWav == NULL) {
3984 return 1;
3985 }
3987 return drwav__write_byte(pWav, byte);
3988}
3990DRWAV_PRIVATE size_t drwav__write_or_count_u16ne_to_le(drwav* pWav, drwav_uint16 value)
3991{
3992 if (pWav == NULL) {
3993 return 2;
3994 }
3996 return drwav__write_u16ne_to_le(pWav, value);
3997}
3999DRWAV_PRIVATE size_t drwav__write_or_count_u32ne_to_le(drwav* pWav, drwav_uint32 value)
4000{
4001 if (pWav == NULL) {
4002 return 4;
4003 }
4005 return drwav__write_u32ne_to_le(pWav, value);
4006}
4008#if 0 /* Unused for now. */
4009DRWAV_PRIVATE size_t drwav__write_or_count_u64ne_to_le(drwav* pWav, drwav_uint64 value)
4010{
4011 if (pWav == NULL) {
4012 return 8;
4013 }
4015 return drwav__write_u64ne_to_le(pWav, value);
4016}
4017#endif
4019DRWAV_PRIVATE size_t drwav__write_or_count_f32ne_to_le(drwav* pWav, float value)
4020{
4021 if (pWav == NULL) {
4022 return 4;
4023 }
4025 return drwav__write_f32ne_to_le(pWav, value);
4026}
4028DRWAV_PRIVATE size_t drwav__write_or_count_string_to_fixed_size_buf(drwav* pWav, char* str, size_t bufFixedSize)
4029{
4030 size_t len;
4032 if (pWav == NULL) {
4033 return bufFixedSize;
4034 }
4036 len = drwav__strlen_clamped(str, bufFixedSize);
4037 drwav__write_or_count(pWav, str, len);
4039 if (len < bufFixedSize) {
4040 size_t i;
4041 for (i = 0; i < bufFixedSize - len; ++i) {
4042 drwav__write_byte(pWav, 0);
4043 }
4044 }
4046 return bufFixedSize;
4047}
4050/* pWav can be NULL meaning just count the bytes that would be written. */
4051DRWAV_PRIVATE size_t drwav__write_or_count_metadata(drwav* pWav, drwav_metadata* pMetadatas, drwav_uint32 metadataCount)
4052{
4053 size_t bytesWritten = 0;
4054 drwav_bool32 hasListAdtl = DRWAV_FALSE;
4055 drwav_bool32 hasListInfo = DRWAV_FALSE;
4056 drwav_uint32 iMetadata;
4058 if (pMetadatas == NULL || metadataCount == 0) {
4059 return 0;
4060 }
4062 for (iMetadata = 0; iMetadata < metadataCount; ++iMetadata) {
4063 drwav_metadata* pMetadata = &pMetadatas[iMetadata];
4064 drwav_uint32 chunkSize = 0;
4066 if ((pMetadata->type & drwav_metadata_type_list_all_info_strings) || (pMetadata->type == drwav_metadata_type_unknown && pMetadata->data.unknown.chunkLocation == drwav_metadata_location_inside_info_list)) {
4067 hasListInfo = DRWAV_TRUE;
4068 }
4070 if ((pMetadata->type & drwav_metadata_type_list_all_adtl) || (pMetadata->type == drwav_metadata_type_unknown && pMetadata->data.unknown.chunkLocation == drwav_metadata_location_inside_adtl_list)) {
4071 hasListAdtl = DRWAV_TRUE;
4072 }
4074 switch (pMetadata->type) {
4075 case drwav_metadata_type_smpl:
4076 {
4077 drwav_uint32 iLoop;
4079 chunkSize = DRWAV_SMPL_BYTES + DRWAV_SMPL_LOOP_BYTES * pMetadata->data.smpl.sampleLoopCount + pMetadata->data.smpl.samplerSpecificDataSizeInBytes;
4081 bytesWritten += drwav__write_or_count(pWav, "smpl", 4);
4082 bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, chunkSize);
4084 bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, pMetadata->data.smpl.manufacturerId);
4085 bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, pMetadata->data.smpl.productId);
4086 bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, pMetadata->data.smpl.samplePeriodNanoseconds);
4087 bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, pMetadata->data.smpl.midiUnityNote);
4088 bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, pMetadata->data.smpl.midiPitchFraction);
4089 bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, pMetadata->data.smpl.smpteFormat);
4090 bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, pMetadata->data.smpl.smpteOffset);
4091 bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, pMetadata->data.smpl.sampleLoopCount);
4092 bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, pMetadata->data.smpl.samplerSpecificDataSizeInBytes);
4094 for (iLoop = 0; iLoop < pMetadata->data.smpl.sampleLoopCount; ++iLoop) {
4095 bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, pMetadata->data.smpl.pLoops[iLoop].cuePointId);
4096 bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, pMetadata->data.smpl.pLoops[iLoop].type);
4097 bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, pMetadata->data.smpl.pLoops[iLoop].firstSampleOffset);
4098 bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, pMetadata->data.smpl.pLoops[iLoop].lastSampleOffset);
4099 bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, pMetadata->data.smpl.pLoops[iLoop].sampleFraction);
4100 bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, pMetadata->data.smpl.pLoops[iLoop].playCount);
4101 }
4103 if (pMetadata->data.smpl.samplerSpecificDataSizeInBytes > 0) {
4104 bytesWritten += drwav__write_or_count(pWav, pMetadata->data.smpl.pSamplerSpecificData, pMetadata->data.smpl.samplerSpecificDataSizeInBytes);
4105 }
4106 } break;
4108 case drwav_metadata_type_inst:
4109 {
4110 chunkSize = DRWAV_INST_BYTES;
4112 bytesWritten += drwav__write_or_count(pWav, "inst", 4);
4113 bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, chunkSize);
4114 bytesWritten += drwav__write_or_count(pWav, &pMetadata->data.inst.midiUnityNote, 1);
4115 bytesWritten += drwav__write_or_count(pWav, &pMetadata->data.inst.fineTuneCents, 1);
4116 bytesWritten += drwav__write_or_count(pWav, &pMetadata->data.inst.gainDecibels, 1);
4117 bytesWritten += drwav__write_or_count(pWav, &pMetadata->data.inst.lowNote, 1);
4118 bytesWritten += drwav__write_or_count(pWav, &pMetadata->data.inst.highNote, 1);
4119 bytesWritten += drwav__write_or_count(pWav, &pMetadata->data.inst.lowVelocity, 1);
4120 bytesWritten += drwav__write_or_count(pWav, &pMetadata->data.inst.highVelocity, 1);
4121 } break;
4123 case drwav_metadata_type_cue:
4124 {
4125 drwav_uint32 iCuePoint;
4127 chunkSize = DRWAV_CUE_BYTES + DRWAV_CUE_POINT_BYTES * pMetadata->data.cue.cuePointCount;
4129 bytesWritten += drwav__write_or_count(pWav, "cue ", 4);
4130 bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, chunkSize);
4131 bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, pMetadata->data.cue.cuePointCount);
4132 for (iCuePoint = 0; iCuePoint < pMetadata->data.cue.cuePointCount; ++iCuePoint) {
4133 bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, pMetadata->data.cue.pCuePoints[iCuePoint].id);
4134 bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, pMetadata->data.cue.pCuePoints[iCuePoint].playOrderPosition);
4135 bytesWritten += drwav__write_or_count(pWav, pMetadata->data.cue.pCuePoints[iCuePoint].dataChunkId, 4);
4136 bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, pMetadata->data.cue.pCuePoints[iCuePoint].chunkStart);
4137 bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, pMetadata->data.cue.pCuePoints[iCuePoint].blockStart);
4138 bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, pMetadata->data.cue.pCuePoints[iCuePoint].sampleOffset);
4139 }
4140 } break;
4142 case drwav_metadata_type_acid:
4143 {
4144 chunkSize = DRWAV_ACID_BYTES;
4146 bytesWritten += drwav__write_or_count(pWav, "acid", 4);
4147 bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, chunkSize);
4148 bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, pMetadata->data.acid.flags);
4149 bytesWritten += drwav__write_or_count_u16ne_to_le(pWav, pMetadata->data.acid.midiUnityNote);
4150 bytesWritten += drwav__write_or_count_u16ne_to_le(pWav, pMetadata->data.acid.reserved1);
4151 bytesWritten += drwav__write_or_count_f32ne_to_le(pWav, pMetadata->data.acid.reserved2);
4152 bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, pMetadata->data.acid.numBeats);
4153 bytesWritten += drwav__write_or_count_u16ne_to_le(pWav, pMetadata->data.acid.meterDenominator);
4154 bytesWritten += drwav__write_or_count_u16ne_to_le(pWav, pMetadata->data.acid.meterNumerator);
4155 bytesWritten += drwav__write_or_count_f32ne_to_le(pWav, pMetadata->data.acid.tempo);
4156 } break;
4158 case drwav_metadata_type_bext:
4159 {
4160 char reservedBuf[DRWAV_BEXT_RESERVED_BYTES];
4161 drwav_uint32 timeReferenceLow;
4162 drwav_uint32 timeReferenceHigh;
4164 chunkSize = DRWAV_BEXT_BYTES + pMetadata->data.bext.codingHistorySize;
4166 bytesWritten += drwav__write_or_count(pWav, "bext", 4);
4167 bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, chunkSize);
4169 bytesWritten += drwav__write_or_count_string_to_fixed_size_buf(pWav, pMetadata->data.bext.pDescription, DRWAV_BEXT_DESCRIPTION_BYTES);
4170 bytesWritten += drwav__write_or_count_string_to_fixed_size_buf(pWav, pMetadata->data.bext.pOriginatorName, DRWAV_BEXT_ORIGINATOR_NAME_BYTES);
4171 bytesWritten += drwav__write_or_count_string_to_fixed_size_buf(pWav, pMetadata->data.bext.pOriginatorReference, DRWAV_BEXT_ORIGINATOR_REF_BYTES);
4172 bytesWritten += drwav__write_or_count(pWav, pMetadata->data.bext.pOriginationDate, sizeof(pMetadata->data.bext.pOriginationDate));
4173 bytesWritten += drwav__write_or_count(pWav, pMetadata->data.bext.pOriginationTime, sizeof(pMetadata->data.bext.pOriginationTime));
4175 timeReferenceLow = (drwav_uint32)(pMetadata->data.bext.timeReference & 0xFFFFFFFF);
4176 timeReferenceHigh = (drwav_uint32)(pMetadata->data.bext.timeReference >> 32);
4177 bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, timeReferenceLow);
4178 bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, timeReferenceHigh);
4180 bytesWritten += drwav__write_or_count_u16ne_to_le(pWav, pMetadata->data.bext.version);
4181 bytesWritten += drwav__write_or_count(pWav, pMetadata->data.bext.pUMID, DRWAV_BEXT_UMID_BYTES);
4182 bytesWritten += drwav__write_or_count_u16ne_to_le(pWav, pMetadata->data.bext.loudnessValue);
4183 bytesWritten += drwav__write_or_count_u16ne_to_le(pWav, pMetadata->data.bext.loudnessRange);
4184 bytesWritten += drwav__write_or_count_u16ne_to_le(pWav, pMetadata->data.bext.maxTruePeakLevel);
4185 bytesWritten += drwav__write_or_count_u16ne_to_le(pWav, pMetadata->data.bext.maxMomentaryLoudness);
4186 bytesWritten += drwav__write_or_count_u16ne_to_le(pWav, pMetadata->data.bext.maxShortTermLoudness);
4188 DRWAV_ZERO_MEMORY(reservedBuf, sizeof(reservedBuf));
4189 bytesWritten += drwav__write_or_count(pWav, reservedBuf, sizeof(reservedBuf));
4191 if (pMetadata->data.bext.codingHistorySize > 0) {
4192 bytesWritten += drwav__write_or_count(pWav, pMetadata->data.bext.pCodingHistory, pMetadata->data.bext.codingHistorySize);
4193 }
4194 } break;
4196 case drwav_metadata_type_unknown:
4197 {
4198 if (pMetadata->data.unknown.chunkLocation == drwav_metadata_location_top_level) {
4199 chunkSize = pMetadata->data.unknown.dataSizeInBytes;
4201 bytesWritten += drwav__write_or_count(pWav, pMetadata->data.unknown.id, 4);
4202 bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, chunkSize);
4203 bytesWritten += drwav__write_or_count(pWav, pMetadata->data.unknown.pData, pMetadata->data.unknown.dataSizeInBytes);
4204 }
4205 } break;
4207 default: break;
4208 }
4209 if ((chunkSize % 2) != 0) {
4210 bytesWritten += drwav__write_or_count_byte(pWav, 0);
4211 }
4212 }
4214 if (hasListInfo) {
4215 drwav_uint32 chunkSize = 4; /* Start with 4 bytes for "INFO". */
4216 for (iMetadata = 0; iMetadata < metadataCount; ++iMetadata) {
4217 drwav_metadata* pMetadata = &pMetadatas[iMetadata];
4219 if ((pMetadata->type & drwav_metadata_type_list_all_info_strings)) {
4220 chunkSize += 8; /* For id and string size. */
4221 chunkSize += pMetadata->data.infoText.stringLength + 1; /* Include null terminator. */
4222 } else if (pMetadata->type == drwav_metadata_type_unknown && pMetadata->data.unknown.chunkLocation == drwav_metadata_location_inside_info_list) {
4223 chunkSize += 8; /* For id string size. */
4224 chunkSize += pMetadata->data.unknown.dataSizeInBytes;
4225 }
4227 if ((chunkSize % 2) != 0) {
4228 chunkSize += 1;
4229 }
4230 }
4232 bytesWritten += drwav__write_or_count(pWav, "LIST", 4);
4233 bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, chunkSize);
4234 bytesWritten += drwav__write_or_count(pWav, "INFO", 4);
4236 for (iMetadata = 0; iMetadata < metadataCount; ++iMetadata) {
4237 drwav_metadata* pMetadata = &pMetadatas[iMetadata];
4238 drwav_uint32 subchunkSize = 0;
4240 if (pMetadata->type & drwav_metadata_type_list_all_info_strings) {
4241 const char* pID = NULL;
4243 switch (pMetadata->type) {
4244 case drwav_metadata_type_list_info_software: pID = "ISFT"; break;
4245 case drwav_metadata_type_list_info_copyright: pID = "ICOP"; break;
4246 case drwav_metadata_type_list_info_title: pID = "INAM"; break;
4247 case drwav_metadata_type_list_info_artist: pID = "IART"; break;
4248 case drwav_metadata_type_list_info_comment: pID = "ICMT"; break;
4249 case drwav_metadata_type_list_info_date: pID = "ICRD"; break;
4250 case drwav_metadata_type_list_info_genre: pID = "IGNR"; break;
4251 case drwav_metadata_type_list_info_album: pID = "IPRD"; break;
4252 case drwav_metadata_type_list_info_tracknumber: pID = "ITRK"; break;
4253 case drwav_metadata_type_list_info_location: pID = "IARL"; break;
4254 case drwav_metadata_type_list_info_organization: pID = "ICMS"; break;
4255 case drwav_metadata_type_list_info_keywords: pID = "IKEY"; break;
4256 case drwav_metadata_type_list_info_medium: pID = "IMED"; break;
4257 case drwav_metadata_type_list_info_description: pID = "ISBJ"; break;
4258 default: break;
4259 }
4261 DRWAV_ASSERT(pID != NULL);
4263 if (pMetadata->data.infoText.stringLength) {
4264 subchunkSize = pMetadata->data.infoText.stringLength + 1;
4265 bytesWritten += drwav__write_or_count(pWav, pID, 4);
4266 bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, subchunkSize);
4267 bytesWritten += drwav__write_or_count(pWav, pMetadata->data.infoText.pString, pMetadata->data.infoText.stringLength);
4268 bytesWritten += drwav__write_or_count_byte(pWav, '\0');
4269 }
4270 } else if (pMetadata->type == drwav_metadata_type_unknown && pMetadata->data.unknown.chunkLocation == drwav_metadata_location_inside_info_list) {
4271 if (pMetadata->data.unknown.dataSizeInBytes) {
4272 subchunkSize = pMetadata->data.unknown.dataSizeInBytes;
4274 bytesWritten += drwav__write_or_count(pWav, pMetadata->data.unknown.id, 4);
4275 bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, pMetadata->data.unknown.dataSizeInBytes);
4276 bytesWritten += drwav__write_or_count(pWav, pMetadata->data.unknown.pData, subchunkSize);
4277 }
4278 }
4280 if ((subchunkSize % 2) != 0) {
4281 bytesWritten += drwav__write_or_count_byte(pWav, 0);
4282 }
4283 }
4284 }
4286 if (hasListAdtl) {
4287 drwav_uint32 chunkSize = 4; /* start with 4 bytes for "adtl" */
4289 for (iMetadata = 0; iMetadata < metadataCount; ++iMetadata) {
4290 drwav_metadata* pMetadata = &pMetadatas[iMetadata];
4292 switch (pMetadata->type)
4293 {
4294 case drwav_metadata_type_list_label:
4295 case drwav_metadata_type_list_note:
4296 {
4297 chunkSize += 8; /* for id and chunk size */
4298 chunkSize += DRWAV_LIST_LABEL_OR_NOTE_BYTES;
4300 if (pMetadata->data.labelOrNote.stringLength > 0) {
4301 chunkSize += pMetadata->data.labelOrNote.stringLength + 1;
4302 }
4303 } break;
4305 case drwav_metadata_type_list_labelled_cue_region:
4306 {
4307 chunkSize += 8; /* for id and chunk size */
4308 chunkSize += DRWAV_LIST_LABELLED_TEXT_BYTES;
4310 if (pMetadata->data.labelledCueRegion.stringLength > 0) {
4311 chunkSize += pMetadata->data.labelledCueRegion.stringLength + 1;
4312 }
4313 } break;
4315 case drwav_metadata_type_unknown:
4316 {
4317 if (pMetadata->data.unknown.chunkLocation == drwav_metadata_location_inside_adtl_list) {
4318 chunkSize += 8; /* for id and chunk size */
4319 chunkSize += pMetadata->data.unknown.dataSizeInBytes;
4320 }
4321 } break;
4323 default: break;
4324 }
4326 if ((chunkSize % 2) != 0) {
4327 chunkSize += 1;
4328 }
4329 }
4331 bytesWritten += drwav__write_or_count(pWav, "LIST", 4);
4332 bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, chunkSize);
4333 bytesWritten += drwav__write_or_count(pWav, "adtl", 4);
4335 for (iMetadata = 0; iMetadata < metadataCount; ++iMetadata) {
4336 drwav_metadata* pMetadata = &pMetadatas[iMetadata];
4337 drwav_uint32 subchunkSize = 0;
4339 switch (pMetadata->type)
4340 {
4341 case drwav_metadata_type_list_label:
4342 case drwav_metadata_type_list_note:
4343 {
4344 if (pMetadata->data.labelOrNote.stringLength > 0) {
4345 const char *pID = NULL;
4347 if (pMetadata->type == drwav_metadata_type_list_label) {
4348 pID = "labl";
4349 }
4350 else if (pMetadata->type == drwav_metadata_type_list_note) {
4351 pID = "note";
4352 }
4354 DRWAV_ASSERT(pID != NULL);
4355 DRWAV_ASSERT(pMetadata->data.labelOrNote.pString != NULL);
4357 subchunkSize = DRWAV_LIST_LABEL_OR_NOTE_BYTES;
4359 bytesWritten += drwav__write_or_count(pWav, pID, 4);
4360 subchunkSize += pMetadata->data.labelOrNote.stringLength + 1;
4361 bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, subchunkSize);
4363 bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, pMetadata->data.labelOrNote.cuePointId);
4364 bytesWritten += drwav__write_or_count(pWav, pMetadata->data.labelOrNote.pString, pMetadata->data.labelOrNote.stringLength);
4365 bytesWritten += drwav__write_or_count_byte(pWav, '\0');
4366 }
4367 } break;
4369 case drwav_metadata_type_list_labelled_cue_region:
4370 {
4371 subchunkSize = DRWAV_LIST_LABELLED_TEXT_BYTES;
4373 bytesWritten += drwav__write_or_count(pWav, "ltxt", 4);
4374 if (pMetadata->data.labelledCueRegion.stringLength > 0) {
4375 subchunkSize += pMetadata->data.labelledCueRegion.stringLength + 1;
4376 }
4377 bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, subchunkSize);
4378 bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, pMetadata->data.labelledCueRegion.cuePointId);
4379 bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, pMetadata->data.labelledCueRegion.sampleLength);
4380 bytesWritten += drwav__write_or_count(pWav, pMetadata->data.labelledCueRegion.purposeId, 4);
4381 bytesWritten += drwav__write_or_count_u16ne_to_le(pWav, pMetadata->data.labelledCueRegion.country);
4382 bytesWritten += drwav__write_or_count_u16ne_to_le(pWav, pMetadata->data.labelledCueRegion.language);
4383 bytesWritten += drwav__write_or_count_u16ne_to_le(pWav, pMetadata->data.labelledCueRegion.dialect);
4384 bytesWritten += drwav__write_or_count_u16ne_to_le(pWav, pMetadata->data.labelledCueRegion.codePage);
4386 if (pMetadata->data.labelledCueRegion.stringLength > 0) {
4387 DRWAV_ASSERT(pMetadata->data.labelledCueRegion.pString != NULL);
4389 bytesWritten += drwav__write_or_count(pWav, pMetadata->data.labelledCueRegion.pString, pMetadata->data.labelledCueRegion.stringLength);
4390 bytesWritten += drwav__write_or_count_byte(pWav, '\0');
4391 }
4392 } break;
4394 case drwav_metadata_type_unknown:
4395 {
4396 if (pMetadata->data.unknown.chunkLocation == drwav_metadata_location_inside_adtl_list) {
4397 subchunkSize = pMetadata->data.unknown.dataSizeInBytes;
4399 DRWAV_ASSERT(pMetadata->data.unknown.pData != NULL);
4400 bytesWritten += drwav__write_or_count(pWav, pMetadata->data.unknown.id, 4);
4401 bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, subchunkSize);
4402 bytesWritten += drwav__write_or_count(pWav, pMetadata->data.unknown.pData, subchunkSize);
4403 }
4404 } break;
4406 default: break;
4407 }
4409 if ((subchunkSize % 2) != 0) {
4410 bytesWritten += drwav__write_or_count_byte(pWav, 0);
4411 }
4412 }
4413 }
4415 DRWAV_ASSERT((bytesWritten % 2) == 0);
4417 return bytesWritten;
4418}
4420DRWAV_PRIVATE drwav_uint32 drwav__riff_chunk_size_riff(drwav_uint64 dataChunkSize, drwav_metadata* pMetadata, drwav_uint32 metadataCount)
4421{
4422 drwav_uint64 chunkSize = 4 + 24 + (drwav_uint64)drwav__write_or_count_metadata(NULL, pMetadata, metadataCount) + 8 + dataChunkSize + drwav__chunk_padding_size_riff(dataChunkSize); /* 4 = "WAVE". 24 = "fmt " chunk. 8 = "data" + u32 data size. */
4423 if (chunkSize > 0xFFFFFFFFUL) {
4424 chunkSize = 0xFFFFFFFFUL;
4425 }
4427 return (drwav_uint32)chunkSize; /* Safe cast due to the clamp above. */
4428}
4430DRWAV_PRIVATE drwav_uint32 drwav__data_chunk_size_riff(drwav_uint64 dataChunkSize)
4431{
4432 if (dataChunkSize <= 0xFFFFFFFFUL) {
4433 return (drwav_uint32)dataChunkSize;
4434 } else {
4435 return 0xFFFFFFFFUL;
4436 }
4437}
4439DRWAV_PRIVATE drwav_uint64 drwav__riff_chunk_size_w64(drwav_uint64 dataChunkSize)
4440{
4441 drwav_uint64 dataSubchunkPaddingSize = drwav__chunk_padding_size_w64(dataChunkSize);
4443 return 80 + 24 + dataChunkSize + dataSubchunkPaddingSize; /* +24 because W64 includes the size of the GUID and size fields. */
4444}
4446DRWAV_PRIVATE drwav_uint64 drwav__data_chunk_size_w64(drwav_uint64 dataChunkSize)
4447{
4448 return 24 + dataChunkSize; /* +24 because W64 includes the size of the GUID and size fields. */
4449}
4451DRWAV_PRIVATE drwav_uint64 drwav__riff_chunk_size_rf64(drwav_uint64 dataChunkSize, drwav_metadata *metadata, drwav_uint32 numMetadata)
4452{
4453 drwav_uint64 chunkSize = 4 + 36 + 24 + (drwav_uint64)drwav__write_or_count_metadata(NULL, metadata, numMetadata) + 8 + dataChunkSize + drwav__chunk_padding_size_riff(dataChunkSize); /* 4 = "WAVE". 36 = "ds64" chunk. 24 = "fmt " chunk. 8 = "data" + u32 data size. */
4454 if (chunkSize > 0xFFFFFFFFUL) {
4455 chunkSize = 0xFFFFFFFFUL;
4456 }
4458 return chunkSize;
4459}
4461DRWAV_PRIVATE drwav_uint64 drwav__data_chunk_size_rf64(drwav_uint64 dataChunkSize)
4462{
4463 return dataChunkSize;
4464}
4468DRWAV_PRIVATE drwav_bool32 drwav_preinit_write(drwav* pWav, const drwav_data_format* pFormat, drwav_bool32 isSequential, drwav_write_proc onWrite, drwav_seek_proc onSeek, void* pUserData, const drwav_allocation_callbacks* pAllocationCallbacks)
4469{
4470 if (pWav == NULL || onWrite == NULL) {
4471 return DRWAV_FALSE;
4472 }
4474 if (!isSequential && onSeek == NULL) {
4475 return DRWAV_FALSE; /* <-- onSeek is required when in non-sequential mode. */
4476 }
4478 /* Not currently supporting compressed formats. Will need to add support for the "fact" chunk before we enable this. */
4479 if (pFormat->format == DR_WAVE_FORMAT_EXTENSIBLE) {
4480 return DRWAV_FALSE;
4481 }
4482 if (pFormat->format == DR_WAVE_FORMAT_ADPCM || pFormat->format == DR_WAVE_FORMAT_DVI_ADPCM) {
4483 return DRWAV_FALSE;
4484 }
4486 DRWAV_ZERO_MEMORY(pWav, sizeof(*pWav));
4487 pWav->onWrite = onWrite;
4488 pWav->onSeek = onSeek;
4489 pWav->pUserData = pUserData;
4490 pWav->allocationCallbacks = drwav_copy_allocation_callbacks_or_defaults(pAllocationCallbacks);
4492 if (pWav->allocationCallbacks.onFree == NULL || (pWav->allocationCallbacks.onMalloc == NULL && pWav->allocationCallbacks.onRealloc == NULL)) {
4493 return DRWAV_FALSE; /* Invalid allocation callbacks. */
4494 }
4496 pWav->fmt.formatTag = (drwav_uint16)pFormat->format;
4497 pWav->fmt.channels = (drwav_uint16)pFormat->channels;
4498 pWav->fmt.sampleRate = pFormat->sampleRate;
4499 pWav->fmt.avgBytesPerSec = (drwav_uint32)((pFormat->bitsPerSample * pFormat->sampleRate * pFormat->channels) / 8);
4500 pWav->fmt.blockAlign = (drwav_uint16)((pFormat->channels * pFormat->bitsPerSample) / 8);
4501 pWav->fmt.bitsPerSample = (drwav_uint16)pFormat->bitsPerSample;
4502 pWav->fmt.extendedSize = 0;
4503 pWav->isSequentialWrite = isSequential;
4505 return DRWAV_TRUE;
4506}
4509DRWAV_PRIVATE drwav_bool32 drwav_init_write__internal(drwav* pWav, const drwav_data_format* pFormat, drwav_uint64 totalSampleCount)
4510{
4511 /* The function assumes drwav_preinit_write() was called beforehand. */
4513 size_t runningPos = 0;
4514 drwav_uint64 initialDataChunkSize = 0;
4515 drwav_uint64 chunkSizeFMT;
4517 /*
4518 The initial values for the "RIFF" and "data" chunks depends on whether or not we are initializing in sequential mode or not. In
4519 sequential mode we set this to its final values straight away since they can be calculated from the total sample count. In non-
4520 sequential mode we initialize it all to zero and fill it out in drwav_uninit() using a backwards seek.
4521 */
4522 if (pWav->isSequentialWrite) {
4523 initialDataChunkSize = (totalSampleCount * pWav->fmt.bitsPerSample) / 8;
4525 /*
4526 The RIFF container has a limit on the number of samples. drwav is not allowing this. There's no practical limits for Wave64
4527 so for the sake of simplicity I'm not doing any validation for that.
4528 */
4529 if (pFormat->container == drwav_container_riff) {
4530 if (initialDataChunkSize > (0xFFFFFFFFUL - 36)) {
4531 return DRWAV_FALSE; /* Not enough room to store every sample. */
4532 }
4533 }
4534 }
4536 pWav->dataChunkDataSizeTargetWrite = initialDataChunkSize;
4539 /* "RIFF" chunk. */
4540 if (pFormat->container == drwav_container_riff) {
4541 drwav_uint32 chunkSizeRIFF = 36 + (drwav_uint32)initialDataChunkSize; /* +36 = "WAVE" + [sizeof "fmt " chunk] + [data chunk header] */
4542 runningPos += drwav__write(pWav, "RIFF", 4);
4543 runningPos += drwav__write_u32ne_to_le(pWav, chunkSizeRIFF);
4544 runningPos += drwav__write(pWav, "WAVE", 4);
4545 } else if (pFormat->container == drwav_container_w64) {
4546 drwav_uint64 chunkSizeRIFF = 80 + 24 + initialDataChunkSize; /* +24 because W64 includes the size of the GUID and size fields. */
4547 runningPos += drwav__write(pWav, drwavGUID_W64_RIFF, 16);
4548 runningPos += drwav__write_u64ne_to_le(pWav, chunkSizeRIFF);
4549 runningPos += drwav__write(pWav, drwavGUID_W64_WAVE, 16);
4550 } else if (pFormat->container == drwav_container_rf64) {
4551 runningPos += drwav__write(pWav, "RF64", 4);
4552 runningPos += drwav__write_u32ne_to_le(pWav, 0xFFFFFFFF); /* Always 0xFFFFFFFF for RF64. Set to a proper value in the "ds64" chunk. */
4553 runningPos += drwav__write(pWav, "WAVE", 4);
4554 } else {
4555 return DRWAV_FALSE; /* Container not supported for writing. */
4556 }
4559 /* "ds64" chunk (RF64 only). */
4560 if (pFormat->container == drwav_container_rf64) {
4561 drwav_uint32 initialds64ChunkSize = 28; /* 28 = [Size of RIFF (8 bytes)] + [Size of DATA (8 bytes)] + [Sample Count (8 bytes)] + [Table Length (4 bytes)]. Table length always set to 0. */
4562 drwav_uint64 initialRiffChunkSize = 8 + initialds64ChunkSize + initialDataChunkSize; /* +8 for the ds64 header. */
4564 runningPos += drwav__write(pWav, "ds64", 4);
4565 runningPos += drwav__write_u32ne_to_le(pWav, initialds64ChunkSize); /* Size of ds64. */
4566 runningPos += drwav__write_u64ne_to_le(pWav, initialRiffChunkSize); /* Size of RIFF. Set to true value at the end. */
4567 runningPos += drwav__write_u64ne_to_le(pWav, initialDataChunkSize); /* Size of DATA. Set to true value at the end. */
4568 runningPos += drwav__write_u64ne_to_le(pWav, totalSampleCount); /* Sample count. */
4569 runningPos += drwav__write_u32ne_to_le(pWav, 0); /* Table length. Always set to zero in our case since we're not doing any other chunks than "DATA". */
4570 }
4573 /* "fmt " chunk. */
4574 if (pFormat->container == drwav_container_riff || pFormat->container == drwav_container_rf64) {
4575 chunkSizeFMT = 16;
4576 runningPos += drwav__write(pWav, "fmt ", 4);
4577 runningPos += drwav__write_u32ne_to_le(pWav, (drwav_uint32)chunkSizeFMT);
4578 } else if (pFormat->container == drwav_container_w64) {
4579 chunkSizeFMT = 40;
4580 runningPos += drwav__write(pWav, drwavGUID_W64_FMT, 16);
4581 runningPos += drwav__write_u64ne_to_le(pWav, chunkSizeFMT);
4582 }
4584 runningPos += drwav__write_u16ne_to_le(pWav, pWav->fmt.formatTag);
4585 runningPos += drwav__write_u16ne_to_le(pWav, pWav->fmt.channels);
4586 runningPos += drwav__write_u32ne_to_le(pWav, pWav->fmt.sampleRate);
4587 runningPos += drwav__write_u32ne_to_le(pWav, pWav->fmt.avgBytesPerSec);
4588 runningPos += drwav__write_u16ne_to_le(pWav, pWav->fmt.blockAlign);
4589 runningPos += drwav__write_u16ne_to_le(pWav, pWav->fmt.bitsPerSample);
4591 /* TODO: is a 'fact' chunk required for DR_WAVE_FORMAT_IEEE_FLOAT? */
4593 if (!pWav->isSequentialWrite && pWav->pMetadata != NULL && pWav->metadataCount > 0 && (pFormat->container == drwav_container_riff || pFormat->container == drwav_container_rf64)) {
4594 runningPos += drwav__write_or_count_metadata(pWav, pWav->pMetadata, pWav->metadataCount);
4595 }
4597 pWav->dataChunkDataPos = runningPos;
4599 /* "data" chunk. */
4600 if (pFormat->container == drwav_container_riff) {
4601 drwav_uint32 chunkSizeDATA = (drwav_uint32)initialDataChunkSize;
4602 runningPos += drwav__write(pWav, "data", 4);
4603 runningPos += drwav__write_u32ne_to_le(pWav, chunkSizeDATA);
4604 } else if (pFormat->container == drwav_container_w64) {
4605 drwav_uint64 chunkSizeDATA = 24 + initialDataChunkSize; /* +24 because W64 includes the size of the GUID and size fields. */
4606 runningPos += drwav__write(pWav, drwavGUID_W64_DATA, 16);
4607 runningPos += drwav__write_u64ne_to_le(pWav, chunkSizeDATA);
4608 } else if (pFormat->container == drwav_container_rf64) {
4609 runningPos += drwav__write(pWav, "data", 4);
4610 runningPos += drwav__write_u32ne_to_le(pWav, 0xFFFFFFFF); /* Always set to 0xFFFFFFFF for RF64. The true size of the data chunk is specified in the ds64 chunk. */
4611 }
4613 /* Set some properties for the client's convenience. */
4614 pWav->container = pFormat->container;
4615 pWav->channels = (drwav_uint16)pFormat->channels;
4616 pWav->sampleRate = pFormat->sampleRate;
4617 pWav->bitsPerSample = (drwav_uint16)pFormat->bitsPerSample;
4618 pWav->translatedFormatTag = (drwav_uint16)pFormat->format;
4619 pWav->dataChunkDataPos = runningPos;
4621 return DRWAV_TRUE;
4622}
4625DRWAV_API drwav_bool32 drwav_init_write(drwav* pWav, const drwav_data_format* pFormat, drwav_write_proc onWrite, drwav_seek_proc onSeek, void* pUserData, const drwav_allocation_callbacks* pAllocationCallbacks)
4626{
4627 if (!drwav_preinit_write(pWav, pFormat, DRWAV_FALSE, onWrite, onSeek, pUserData, pAllocationCallbacks)) {
4628 return DRWAV_FALSE;
4629 }
4631 return drwav_init_write__internal(pWav, pFormat, 0); /* DRWAV_FALSE = Not Sequential */
4632}
4634DRWAV_API drwav_bool32 drwav_init_write_sequential(drwav* pWav, const drwav_data_format* pFormat, drwav_uint64 totalSampleCount, drwav_write_proc onWrite, void* pUserData, const drwav_allocation_callbacks* pAllocationCallbacks)
4635{
4636 if (!drwav_preinit_write(pWav, pFormat, DRWAV_TRUE, onWrite, NULL, pUserData, pAllocationCallbacks)) {
4637 return DRWAV_FALSE;
4638 }
4640 return drwav_init_write__internal(pWav, pFormat, totalSampleCount); /* DRWAV_TRUE = Sequential */
4641}
4643DRWAV_API drwav_bool32 drwav_init_write_sequential_pcm_frames(drwav* pWav, const drwav_data_format* pFormat, drwav_uint64 totalPCMFrameCount, drwav_write_proc onWrite, void* pUserData, const drwav_allocation_callbacks* pAllocationCallbacks)
4644{
4645 if (pFormat == NULL) {
4646 return DRWAV_FALSE;
4647 }
4649 return drwav_init_write_sequential(pWav, pFormat, totalPCMFrameCount*pFormat->channels, onWrite, pUserData, pAllocationCallbacks);
4650}
4652DRWAV_API drwav_bool32 drwav_init_write_with_metadata(drwav* pWav, const drwav_data_format* pFormat, drwav_write_proc onWrite, drwav_seek_proc onSeek, void* pUserData, const drwav_allocation_callbacks* pAllocationCallbacks, drwav_metadata* pMetadata, drwav_uint32 metadataCount)
4653{
4654 if (!drwav_preinit_write(pWav, pFormat, DRWAV_FALSE, onWrite, onSeek, pUserData, pAllocationCallbacks)) {
4655 return DRWAV_FALSE;
4656 }
4658 pWav->pMetadata = pMetadata;
4659 pWav->metadataCount = metadataCount;
4661 return drwav_init_write__internal(pWav, pFormat, 0);
4662}
4665DRWAV_API drwav_uint64 drwav_target_write_size_bytes(const drwav_data_format* pFormat, drwav_uint64 totalFrameCount, drwav_metadata* pMetadata, drwav_uint32 metadataCount)
4666{
4667 /* Casting totalFrameCount to drwav_int64 for VC6 compatibility. No issues in practice because nobody is going to exhaust the whole 63 bits. */
4668 drwav_uint64 targetDataSizeBytes = (drwav_uint64)((drwav_int64)totalFrameCount * pFormat->channels * pFormat->bitsPerSample/8.0);
4669 drwav_uint64 riffChunkSizeBytes;
4670 drwav_uint64 fileSizeBytes = 0;
4672 if (pFormat->container == drwav_container_riff) {
4673 riffChunkSizeBytes = drwav__riff_chunk_size_riff(targetDataSizeBytes, pMetadata, metadataCount);
4674 fileSizeBytes = (8 + riffChunkSizeBytes); /* +8 because WAV doesn't include the size of the ChunkID and ChunkSize fields. */
4675 } else if (pFormat->container == drwav_container_w64) {
4676 riffChunkSizeBytes = drwav__riff_chunk_size_w64(targetDataSizeBytes);
4677 fileSizeBytes = riffChunkSizeBytes;
4678 } else if (pFormat->container == drwav_container_rf64) {
4679 riffChunkSizeBytes = drwav__riff_chunk_size_rf64(targetDataSizeBytes, pMetadata, metadataCount);
4680 fileSizeBytes = (8 + riffChunkSizeBytes); /* +8 because WAV doesn't include the size of the ChunkID and ChunkSize fields. */
4681 }
4683 return fileSizeBytes;
4684}
4687#ifndef DR_WAV_NO_STDIO
4689/* Errno */
4690/* drwav_result_from_errno() is only used for fopen() and wfopen() so putting it inside DR_WAV_NO_STDIO for now. If something else needs this later we can move it out. */
4691#include <errno.h>
4692DRWAV_PRIVATE drwav_result drwav_result_from_errno(int e)
4693{
4694 switch (e)
4695 {
4696 case 0: return DRWAV_SUCCESS;
4697 #ifdef EPERM
4698 case EPERM: return DRWAV_INVALID_OPERATION;
4699 #endif
4700 #ifdef ENOENT
4701 case ENOENT: return DRWAV_DOES_NOT_EXIST;
4702 #endif
4703 #ifdef ESRCH
4704 case ESRCH: return DRWAV_DOES_NOT_EXIST;
4705 #endif
4706 #ifdef EINTR
4707 case EINTR: return DRWAV_INTERRUPT;
4708 #endif
4709 #ifdef EIO
4710 case EIO: return DRWAV_IO_ERROR;
4711 #endif
4712 #ifdef ENXIO
4713 case ENXIO: return DRWAV_DOES_NOT_EXIST;
4714 #endif
4715 #ifdef E2BIG
4716 case E2BIG: return DRWAV_INVALID_ARGS;
4717 #endif
4718 #ifdef ENOEXEC
4719 case ENOEXEC: return DRWAV_INVALID_FILE;
4720 #endif
4721 #ifdef EBADF
4722 case EBADF: return DRWAV_INVALID_FILE;
4723 #endif
4724 #ifdef ECHILD
4725 case ECHILD: return DRWAV_ERROR;
4726 #endif
4727 #ifdef EAGAIN
4728 case EAGAIN: return DRWAV_UNAVAILABLE;
4729 #endif
4730 #ifdef ENOMEM
4731 case ENOMEM: return DRWAV_OUT_OF_MEMORY;
4732 #endif
4733 #ifdef EACCES
4734 case EACCES: return DRWAV_ACCESS_DENIED;
4735 #endif
4736 #ifdef EFAULT
4737 case EFAULT: return DRWAV_BAD_ADDRESS;
4738 #endif
4739 #ifdef ENOTBLK
4740 case ENOTBLK: return DRWAV_ERROR;
4741 #endif
4742 #ifdef EBUSY
4743 case EBUSY: return DRWAV_BUSY;
4744 #endif
4745 #ifdef EEXIST
4746 case EEXIST: return DRWAV_ALREADY_EXISTS;
4747 #endif
4748 #ifdef EXDEV
4749 case EXDEV: return DRWAV_ERROR;
4750 #endif
4751 #ifdef ENODEV
4752 case ENODEV: return DRWAV_DOES_NOT_EXIST;
4753 #endif
4754 #ifdef ENOTDIR
4755 case ENOTDIR: return DRWAV_NOT_DIRECTORY;
4756 #endif
4757 #ifdef EISDIR
4758 case EISDIR: return DRWAV_IS_DIRECTORY;
4759 #endif
4760 #ifdef EINVAL
4761 case EINVAL: return DRWAV_INVALID_ARGS;
4762 #endif
4763 #ifdef ENFILE
4764 case ENFILE: return DRWAV_TOO_MANY_OPEN_FILES;
4765 #endif
4766 #ifdef EMFILE
4767 case EMFILE: return DRWAV_TOO_MANY_OPEN_FILES;
4768 #endif
4769 #ifdef ENOTTY
4770 case ENOTTY: return DRWAV_INVALID_OPERATION;
4771 #endif
4772 #ifdef ETXTBSY
4773 case ETXTBSY: return DRWAV_BUSY;
4774 #endif
4775 #ifdef EFBIG
4776 case EFBIG: return DRWAV_TOO_BIG;
4777 #endif
4778 #ifdef ENOSPC
4779 case ENOSPC: return DRWAV_NO_SPACE;
4780 #endif
4781 #ifdef ESPIPE
4782 case ESPIPE: return DRWAV_BAD_SEEK;
4783 #endif
4784 #ifdef EROFS
4785 case EROFS: return DRWAV_ACCESS_DENIED;
4786 #endif
4787 #ifdef EMLINK
4788 case EMLINK: return DRWAV_TOO_MANY_LINKS;
4789 #endif
4790 #ifdef EPIPE
4791 case EPIPE: return DRWAV_BAD_PIPE;
4792 #endif
4793 #ifdef EDOM
4794 case EDOM: return DRWAV_OUT_OF_RANGE;
4795 #endif
4796 #ifdef ERANGE
4797 case ERANGE: return DRWAV_OUT_OF_RANGE;
4798 #endif
4799 #ifdef EDEADLK
4800 case EDEADLK: return DRWAV_DEADLOCK;
4801 #endif
4802 #ifdef ENAMETOOLONG
4803 case ENAMETOOLONG: return DRWAV_PATH_TOO_LONG;
4804 #endif
4805 #ifdef ENOLCK
4806 case ENOLCK: return DRWAV_ERROR;
4807 #endif
4808 #ifdef ENOSYS
4809 case ENOSYS: return DRWAV_NOT_IMPLEMENTED;
4810 #endif
4811 #if defined(ENOTEMPTY) && ENOTEMPTY != EEXIST /* In AIX, ENOTEMPTY and EEXIST use the same value. */
4812 case ENOTEMPTY: return DRWAV_DIRECTORY_NOT_EMPTY;
4813 #endif
4814 #ifdef ELOOP
4815 case ELOOP: return DRWAV_TOO_MANY_LINKS;
4816 #endif
4817 #ifdef ENOMSG
4818 case ENOMSG: return DRWAV_NO_MESSAGE;
4819 #endif
4820 #ifdef EIDRM
4821 case EIDRM: return DRWAV_ERROR;
4822 #endif
4823 #ifdef ECHRNG
4824 case ECHRNG: return DRWAV_ERROR;
4825 #endif
4826 #ifdef EL2NSYNC
4827 case EL2NSYNC: return DRWAV_ERROR;
4828 #endif
4829 #ifdef EL3HLT
4830 case EL3HLT: return DRWAV_ERROR;
4831 #endif
4832 #ifdef EL3RST
4833 case EL3RST: return DRWAV_ERROR;
4834 #endif
4835 #ifdef ELNRNG
4836 case ELNRNG: return DRWAV_OUT_OF_RANGE;
4837 #endif
4838 #ifdef EUNATCH
4839 case EUNATCH: return DRWAV_ERROR;
4840 #endif
4841 #ifdef ENOCSI
4842 case ENOCSI: return DRWAV_ERROR;
4843 #endif
4844 #ifdef EL2HLT
4845 case EL2HLT: return DRWAV_ERROR;
4846 #endif
4847 #ifdef EBADE
4848 case EBADE: return DRWAV_ERROR;
4849 #endif
4850 #ifdef EBADR
4851 case EBADR: return DRWAV_ERROR;
4852 #endif
4853 #ifdef EXFULL
4854 case EXFULL: return DRWAV_ERROR;
4855 #endif
4856 #ifdef ENOANO
4857 case ENOANO: return DRWAV_ERROR;
4858 #endif
4859 #ifdef EBADRQC
4860 case EBADRQC: return DRWAV_ERROR;
4861 #endif
4862 #ifdef EBADSLT
4863 case EBADSLT: return DRWAV_ERROR;
4864 #endif
4865 #ifdef EBFONT
4866 case EBFONT: return DRWAV_INVALID_FILE;
4867 #endif
4868 #ifdef ENOSTR
4869 case ENOSTR: return DRWAV_ERROR;
4870 #endif
4871 #ifdef ENODATA
4872 case ENODATA: return DRWAV_NO_DATA_AVAILABLE;
4873 #endif
4874 #ifdef ETIME
4875 case ETIME: return DRWAV_TIMEOUT;
4876 #endif
4877 #ifdef ENOSR
4878 case ENOSR: return DRWAV_NO_DATA_AVAILABLE;
4879 #endif
4880 #ifdef ENONET
4881 case ENONET: return DRWAV_NO_NETWORK;
4882 #endif
4883 #ifdef ENOPKG
4884 case ENOPKG: return DRWAV_ERROR;
4885 #endif
4886 #ifdef EREMOTE
4887 case EREMOTE: return DRWAV_ERROR;
4888 #endif
4889 #ifdef ENOLINK
4890 case ENOLINK: return DRWAV_ERROR;
4891 #endif
4892 #ifdef EADV
4893 case EADV: return DRWAV_ERROR;
4894 #endif
4895 #ifdef ESRMNT
4896 case ESRMNT: return DRWAV_ERROR;
4897 #endif
4898 #ifdef ECOMM
4899 case ECOMM: return DRWAV_ERROR;
4900 #endif
4901 #ifdef EPROTO
4902 case EPROTO: return DRWAV_ERROR;
4903 #endif
4904 #ifdef EMULTIHOP
4905 case EMULTIHOP: return DRWAV_ERROR;
4906 #endif
4907 #ifdef EDOTDOT
4908 case EDOTDOT: return DRWAV_ERROR;
4909 #endif
4910 #ifdef EBADMSG
4911 case EBADMSG: return DRWAV_BAD_MESSAGE;
4912 #endif
4913 #ifdef EOVERFLOW
4914 case EOVERFLOW: return DRWAV_TOO_BIG;
4915 #endif
4916 #ifdef ENOTUNIQ
4917 case ENOTUNIQ: return DRWAV_NOT_UNIQUE;
4918 #endif
4919 #ifdef EBADFD
4920 case EBADFD: return DRWAV_ERROR;
4921 #endif
4922 #ifdef EREMCHG
4923 case EREMCHG: return DRWAV_ERROR;
4924 #endif
4925 #ifdef ELIBACC
4926 case ELIBACC: return DRWAV_ACCESS_DENIED;
4927 #endif
4928 #ifdef ELIBBAD
4929 case ELIBBAD: return DRWAV_INVALID_FILE;
4930 #endif
4931 #ifdef ELIBSCN
4932 case ELIBSCN: return DRWAV_INVALID_FILE;
4933 #endif
4934 #ifdef ELIBMAX
4935 case ELIBMAX: return DRWAV_ERROR;
4936 #endif
4937 #ifdef ELIBEXEC
4938 case ELIBEXEC: return DRWAV_ERROR;
4939 #endif
4940 #ifdef EILSEQ
4941 case EILSEQ: return DRWAV_INVALID_DATA;
4942 #endif
4943 #ifdef ERESTART
4944 case ERESTART: return DRWAV_ERROR;
4945 #endif
4946 #ifdef ESTRPIPE
4947 case ESTRPIPE: return DRWAV_ERROR;
4948 #endif
4949 #ifdef EUSERS
4950 case EUSERS: return DRWAV_ERROR;
4951 #endif
4952 #ifdef ENOTSOCK
4953 case ENOTSOCK: return DRWAV_NOT_SOCKET;
4954 #endif
4955 #ifdef EDESTADDRREQ
4956 case EDESTADDRREQ: return DRWAV_NO_ADDRESS;
4957 #endif
4958 #ifdef EMSGSIZE
4959 case EMSGSIZE: return DRWAV_TOO_BIG;
4960 #endif
4961 #ifdef EPROTOTYPE
4962 case EPROTOTYPE: return DRWAV_BAD_PROTOCOL;
4963 #endif
4964 #ifdef ENOPROTOOPT
4965 case ENOPROTOOPT: return DRWAV_PROTOCOL_UNAVAILABLE;
4966 #endif
4967 #ifdef EPROTONOSUPPORT
4968 case EPROTONOSUPPORT: return DRWAV_PROTOCOL_NOT_SUPPORTED;
4969 #endif
4970 #ifdef ESOCKTNOSUPPORT
4971 case ESOCKTNOSUPPORT: return DRWAV_SOCKET_NOT_SUPPORTED;
4972 #endif
4973 #ifdef EOPNOTSUPP
4974 case EOPNOTSUPP: return DRWAV_INVALID_OPERATION;
4975 #endif
4976 #ifdef EPFNOSUPPORT
4977 case EPFNOSUPPORT: return DRWAV_PROTOCOL_FAMILY_NOT_SUPPORTED;
4978 #endif
4979 #ifdef EAFNOSUPPORT
4980 case EAFNOSUPPORT: return DRWAV_ADDRESS_FAMILY_NOT_SUPPORTED;
4981 #endif
4982 #ifdef EADDRINUSE
4983 case EADDRINUSE: return DRWAV_ALREADY_IN_USE;
4984 #endif
4985 #ifdef EADDRNOTAVAIL
4986 case EADDRNOTAVAIL: return DRWAV_ERROR;
4987 #endif
4988 #ifdef ENETDOWN
4989 case ENETDOWN: return DRWAV_NO_NETWORK;
4990 #endif
4991 #ifdef ENETUNREACH
4992 case ENETUNREACH: return DRWAV_NO_NETWORK;
4993 #endif
4994 #ifdef ENETRESET
4995 case ENETRESET: return DRWAV_NO_NETWORK;
4996 #endif
4997 #ifdef ECONNABORTED
4998 case ECONNABORTED: return DRWAV_NO_NETWORK;
4999 #endif
5000 #ifdef ECONNRESET
5001 case ECONNRESET: return DRWAV_CONNECTION_RESET;
5002 #endif
5003 #ifdef ENOBUFS
5004 case ENOBUFS: return DRWAV_NO_SPACE;
5005 #endif
5006 #ifdef EISCONN
5007 case EISCONN: return DRWAV_ALREADY_CONNECTED;
5008 #endif
5009 #ifdef ENOTCONN
5010 case ENOTCONN: return DRWAV_NOT_CONNECTED;
5011 #endif
5012 #ifdef ESHUTDOWN
5013 case ESHUTDOWN: return DRWAV_ERROR;
5014 #endif
5015 #ifdef ETOOMANYREFS
5016 case ETOOMANYREFS: return DRWAV_ERROR;
5017 #endif
5018 #ifdef ETIMEDOUT
5019 case ETIMEDOUT: return DRWAV_TIMEOUT;
5020 #endif
5021 #ifdef ECONNREFUSED
5022 case ECONNREFUSED: return DRWAV_CONNECTION_REFUSED;
5023 #endif
5024 #ifdef EHOSTDOWN
5025 case EHOSTDOWN: return DRWAV_NO_HOST;
5026 #endif
5027 #ifdef EHOSTUNREACH
5028 case EHOSTUNREACH: return DRWAV_NO_HOST;
5029 #endif
5030 #ifdef EALREADY
5031 case EALREADY: return DRWAV_IN_PROGRESS;
5032 #endif
5033 #ifdef EINPROGRESS
5034 case EINPROGRESS: return DRWAV_IN_PROGRESS;
5035 #endif
5036 #ifdef ESTALE
5037 case ESTALE: return DRWAV_INVALID_FILE;
5038 #endif
5039 #ifdef EUCLEAN
5040 case EUCLEAN: return DRWAV_ERROR;
5041 #endif
5042 #ifdef ENOTNAM
5043 case ENOTNAM: return DRWAV_ERROR;
5044 #endif
5045 #ifdef ENAVAIL
5046 case ENAVAIL: return DRWAV_ERROR;
5047 #endif
5048 #ifdef EISNAM
5049 case EISNAM: return DRWAV_ERROR;
5050 #endif
5051 #ifdef EREMOTEIO
5052 case EREMOTEIO: return DRWAV_IO_ERROR;
5053 #endif
5054 #ifdef EDQUOT
5055 case EDQUOT: return DRWAV_NO_SPACE;
5056 #endif
5057 #ifdef ENOMEDIUM
5058 case ENOMEDIUM: return DRWAV_DOES_NOT_EXIST;
5059 #endif
5060 #ifdef EMEDIUMTYPE
5061 case EMEDIUMTYPE: return DRWAV_ERROR;
5062 #endif
5063 #ifdef ECANCELED
5064 case ECANCELED: return DRWAV_CANCELLED;
5065 #endif
5066 #ifdef ENOKEY
5067 case ENOKEY: return DRWAV_ERROR;
5068 #endif
5069 #ifdef EKEYEXPIRED
5070 case EKEYEXPIRED: return DRWAV_ERROR;
5071 #endif
5072 #ifdef EKEYREVOKED
5073 case EKEYREVOKED: return DRWAV_ERROR;
5074 #endif
5075 #ifdef EKEYREJECTED
5076 case EKEYREJECTED: return DRWAV_ERROR;
5077 #endif
5078 #ifdef EOWNERDEAD
5079 case EOWNERDEAD: return DRWAV_ERROR;
5080 #endif
5081 #ifdef ENOTRECOVERABLE
5082 case ENOTRECOVERABLE: return DRWAV_ERROR;
5083 #endif
5084 #ifdef ERFKILL
5085 case ERFKILL: return DRWAV_ERROR;
5086 #endif
5087 #ifdef EHWPOISON
5088 case EHWPOISON: return DRWAV_ERROR;
5089 #endif
5090 default: return DRWAV_ERROR;
5091 }
5092}
5093/* End Errno */
5095/* fopen */
5096DRWAV_PRIVATE drwav_result drwav_fopen(FILE** ppFile, const char* pFilePath, const char* pOpenMode)
5097{
5098#if defined(_MSC_VER) && _MSC_VER >= 1400
5099 errno_t err;
5100#endif
5102 if (ppFile != NULL) {
5103 *ppFile = NULL; /* Safety. */
5104 }
5106 if (pFilePath == NULL || pOpenMode == NULL || ppFile == NULL) {
5107 return DRWAV_INVALID_ARGS;
5108 }
5110#if defined(_MSC_VER) && _MSC_VER >= 1400
5111 err = fopen_s(ppFile, pFilePath, pOpenMode);
5112 if (err != 0) {
5113 return drwav_result_from_errno(err);
5114 }
5115#else
5116#if defined(_WIN32) || defined(__APPLE__)
5117 *ppFile = fopen(pFilePath, pOpenMode);
5118#else
5119 #if defined(_FILE_OFFSET_BITS) && _FILE_OFFSET_BITS == 64 && defined(_LARGEFILE64_SOURCE)
5120 *ppFile = fopen64(pFilePath, pOpenMode);
5121 #else
5122 *ppFile = fopen(pFilePath, pOpenMode);
5123 #endif
5124#endif
5125 if (*ppFile == NULL) {
5126 drwav_result result = drwav_result_from_errno(errno);
5127 if (result == DRWAV_SUCCESS) {
5128 result = DRWAV_ERROR; /* Just a safety check to make sure we never ever return success when pFile == NULL. */
5129 }
5131 return result;
5132 }
5133#endif
5135 return DRWAV_SUCCESS;
5136}
5138/*
5139_wfopen() isn't always available in all compilation environments.
5141 * Windows only.
5142 * MSVC seems to support it universally as far back as VC6 from what I can tell (haven't checked further back).
5143 * MinGW-64 (both 32- and 64-bit) seems to support it.
5144 * MinGW wraps it in !defined(__STRICT_ANSI__).
5145 * OpenWatcom wraps it in !defined(_NO_EXT_KEYS).
5147This can be reviewed as compatibility issues arise. The preference is to use _wfopen_s() and _wfopen() as opposed to the wcsrtombs()
5148fallback, so if you notice your compiler not detecting this properly I'm happy to look at adding support.
5149*/
5150#if defined(_WIN32)
5151 #if defined(_MSC_VER) || defined(__MINGW64__) || (!defined(__STRICT_ANSI__) && !defined(_NO_EXT_KEYS))
5152 #define DRWAV_HAS_WFOPEN
5153 #endif
5154#endif
5156#ifndef DR_WAV_NO_WCHAR
5157DRWAV_PRIVATE drwav_result drwav_wfopen(FILE** ppFile, const wchar_t* pFilePath, const wchar_t* pOpenMode, const drwav_allocation_callbacks* pAllocationCallbacks)
5158{
5159 if (ppFile != NULL) {
5160 *ppFile = NULL; /* Safety. */
5161 }
5163 if (pFilePath == NULL || pOpenMode == NULL || ppFile == NULL) {
5164 return DRWAV_INVALID_ARGS;
5165 }
5167#if defined(DRWAV_HAS_WFOPEN)
5168 {
5169 /* Use _wfopen() on Windows. */
5170 #if defined(_MSC_VER) && _MSC_VER >= 1400
5171 errno_t err = _wfopen_s(ppFile, pFilePath, pOpenMode);
5172 if (err != 0) {
5173 return drwav_result_from_errno(err);
5174 }
5175 #else
5176 *ppFile = _wfopen(pFilePath, pOpenMode);
5177 if (*ppFile == NULL) {
5178 return drwav_result_from_errno(errno);
5179 }
5180 #endif
5181 (void)pAllocationCallbacks;
5182 }
5183#else
5184 /*
5185 Use fopen() on anything other than Windows. Requires a conversion. This is annoying because
5186 fopen() is locale specific. The only real way I can think of to do this is with wcsrtombs(). Note
5187 that wcstombs() is apparently not thread-safe because it uses a static global mbstate_t object for
5188 maintaining state. I've checked this with -std=c89 and it works, but if somebody get's a compiler
5189 error I'll look into improving compatibility.
5190 */
5192 /*
5193 Some compilers don't support wchar_t or wcsrtombs() which we're using below. In this case we just
5194 need to abort with an error. If you encounter a compiler lacking such support, add it to this list
5195 and submit a bug report and it'll be added to the library upstream.
5196 */
5197 #if defined(__DJGPP__)
5198 {
5199 /* Nothing to do here. This will fall through to the error check below. */
5200 }
5201 #else
5202 {
5203 mbstate_t mbs;
5204 size_t lenMB;
5205 const wchar_t* pFilePathTemp = pFilePath;
5206 char* pFilePathMB = NULL;
5207 char pOpenModeMB[32] = {0};
5209 /* Get the length first. */
5210 DRWAV_ZERO_OBJECT(&mbs);
5211 lenMB = wcsrtombs(NULL, &pFilePathTemp, 0, &mbs);
5212 if (lenMB == (size_t)-1) {
5213 return drwav_result_from_errno(errno);
5214 }
5216 pFilePathMB = (char*)drwav__malloc_from_callbacks(lenMB + 1, pAllocationCallbacks);
5217 if (pFilePathMB == NULL) {
5218 return DRWAV_OUT_OF_MEMORY;
5219 }
5221 pFilePathTemp = pFilePath;
5222 DRWAV_ZERO_OBJECT(&mbs);
5223 wcsrtombs(pFilePathMB, &pFilePathTemp, lenMB + 1, &mbs);
5225 /* The open mode should always consist of ASCII characters so we should be able to do a trivial conversion. */
5226 {
5227 size_t i = 0;
5228 for (;;) {
5229 if (pOpenMode[i] == 0) {
5230 pOpenModeMB[i] = '\0';
5231 break;
5232 }
5234 pOpenModeMB[i] = (char)pOpenMode[i];
5235 i += 1;
5236 }
5237 }
5239 *ppFile = fopen(pFilePathMB, pOpenModeMB);
5241 drwav__free_from_callbacks(pFilePathMB, pAllocationCallbacks);
5242 }
5243 #endif
5245 if (*ppFile == NULL) {
5246 return DRWAV_ERROR;
5247 }
5248#endif
5250 return DRWAV_SUCCESS;
5251}
5252#endif
5253/* End fopen */
5256DRWAV_PRIVATE size_t drwav__on_read_stdio(void* pUserData, void* pBufferOut, size_t bytesToRead)
5257{
5258 return fread(pBufferOut, 1, bytesToRead, (FILE*)pUserData);
5259}
5261DRWAV_PRIVATE size_t drwav__on_write_stdio(void* pUserData, const void* pData, size_t bytesToWrite)
5262{
5263 return fwrite(pData, 1, bytesToWrite, (FILE*)pUserData);
5264}
5266DRWAV_PRIVATE drwav_bool32 drwav__on_seek_stdio(void* pUserData, int offset, drwav_seek_origin origin)
5267{
5268 int whence = SEEK_SET;
5269 if (origin == DRWAV_SEEK_CUR) {
5270 whence = SEEK_CUR;
5271 } else if (origin == DRWAV_SEEK_END) {
5272 whence = SEEK_END;
5273 }
5275 return fseek((FILE*)pUserData, offset, whence) == 0;
5276}
5278DRWAV_PRIVATE drwav_bool32 drwav__on_tell_stdio(void* pUserData, drwav_int64* pCursor)
5279{
5280 FILE* pFileStdio = (FILE*)pUserData;
5281 drwav_int64 result;
5283 /* These were all validated at a higher level. */
5284 DRWAV_ASSERT(pFileStdio != NULL);
5285 DRWAV_ASSERT(pCursor != NULL);
5287#if defined(_WIN32)
5288 #if defined(_MSC_VER) && _MSC_VER > 1200
5289 result = _ftelli64(pFileStdio);
5290 #else
5291 result = ftell(pFileStdio);
5292 #endif
5293#else
5294 result = ftell(pFileStdio);
5295#endif
5297 *pCursor = result;
5299 return DRWAV_TRUE;
5300}
5302DRWAV_API drwav_bool32 drwav_init_file(drwav* pWav, const char* filename, const drwav_allocation_callbacks* pAllocationCallbacks)
5303{
5304 return drwav_init_file_ex(pWav, filename, NULL, NULL, 0, pAllocationCallbacks);
5305}
5308DRWAV_PRIVATE drwav_bool32 drwav_init_file__internal_FILE(drwav* pWav, FILE* pFile, drwav_chunk_proc onChunk, void* pChunkUserData, drwav_uint32 flags, const drwav_allocation_callbacks* pAllocationCallbacks)
5309{
5310 drwav_bool32 result;
5312 result = drwav_preinit(pWav, drwav__on_read_stdio, drwav__on_seek_stdio, drwav__on_tell_stdio, (void*)pFile, pAllocationCallbacks);
5313 if (result != DRWAV_TRUE) {
5314 fclose(pFile);
5315 return result;
5316 }
5318 result = drwav_init__internal(pWav, onChunk, pChunkUserData, flags);
5319 if (result != DRWAV_TRUE) {
5320 fclose(pFile);
5321 return result;
5322 }
5324 return DRWAV_TRUE;
5325}
5327DRWAV_API drwav_bool32 drwav_init_file_ex(drwav* pWav, const char* filename, drwav_chunk_proc onChunk, void* pChunkUserData, drwav_uint32 flags, const drwav_allocation_callbacks* pAllocationCallbacks)
5328{
5329 FILE* pFile;
5330 if (drwav_fopen(&pFile, filename, "rb") != DRWAV_SUCCESS) {
5331 return DRWAV_FALSE;
5332 }
5334 /* This takes ownership of the FILE* object. */
5335 return drwav_init_file__internal_FILE(pWav, pFile, onChunk, pChunkUserData, flags, pAllocationCallbacks);
5336}
5338#ifndef DR_WAV_NO_WCHAR
5339DRWAV_API drwav_bool32 drwav_init_file_w(drwav* pWav, const wchar_t* filename, const drwav_allocation_callbacks* pAllocationCallbacks)
5340{
5341 return drwav_init_file_ex_w(pWav, filename, NULL, NULL, 0, pAllocationCallbacks);
5342}
5344DRWAV_API drwav_bool32 drwav_init_file_ex_w(drwav* pWav, const wchar_t* filename, drwav_chunk_proc onChunk, void* pChunkUserData, drwav_uint32 flags, const drwav_allocation_callbacks* pAllocationCallbacks)
5345{
5346 FILE* pFile;
5347 if (drwav_wfopen(&pFile, filename, L"rb", pAllocationCallbacks) != DRWAV_SUCCESS) {
5348 return DRWAV_FALSE;
5349 }
5351 /* This takes ownership of the FILE* object. */
5352 return drwav_init_file__internal_FILE(pWav, pFile, onChunk, pChunkUserData, flags, pAllocationCallbacks);
5353}
5354#endif
5356DRWAV_API drwav_bool32 drwav_init_file_with_metadata(drwav* pWav, const char* filename, drwav_uint32 flags, const drwav_allocation_callbacks* pAllocationCallbacks)
5357{
5358 FILE* pFile;
5359 if (drwav_fopen(&pFile, filename, "rb") != DRWAV_SUCCESS) {
5360 return DRWAV_FALSE;
5361 }
5363 /* This takes ownership of the FILE* object. */
5364 return drwav_init_file__internal_FILE(pWav, pFile, NULL, NULL, flags | DRWAV_WITH_METADATA, pAllocationCallbacks);
5365}
5367#ifndef DR_WAV_NO_WCHAR
5368DRWAV_API drwav_bool32 drwav_init_file_with_metadata_w(drwav* pWav, const wchar_t* filename, drwav_uint32 flags, const drwav_allocation_callbacks* pAllocationCallbacks)
5369{
5370 FILE* pFile;
5371 if (drwav_wfopen(&pFile, filename, L"rb", pAllocationCallbacks) != DRWAV_SUCCESS) {
5372 return DRWAV_FALSE;
5373 }
5375 /* This takes ownership of the FILE* object. */
5376 return drwav_init_file__internal_FILE(pWav, pFile, NULL, NULL, flags | DRWAV_WITH_METADATA, pAllocationCallbacks);
5377}
5378#endif
5381DRWAV_PRIVATE drwav_bool32 drwav_init_file_write__internal_FILE(drwav* pWav, FILE* pFile, const drwav_data_format* pFormat, drwav_uint64 totalSampleCount, drwav_bool32 isSequential, const drwav_allocation_callbacks* pAllocationCallbacks)
5382{
5383 drwav_bool32 result;
5385 result = drwav_preinit_write(pWav, pFormat, isSequential, drwav__on_write_stdio, drwav__on_seek_stdio, (void*)pFile, pAllocationCallbacks);
5386 if (result != DRWAV_TRUE) {
5387 fclose(pFile);
5388 return result;
5389 }
5391 result = drwav_init_write__internal(pWav, pFormat, totalSampleCount);
5392 if (result != DRWAV_TRUE) {
5393 fclose(pFile);
5394 return result;
5395 }
5397 return DRWAV_TRUE;
5398}
5400DRWAV_PRIVATE drwav_bool32 drwav_init_file_write__internal(drwav* pWav, const char* filename, const drwav_data_format* pFormat, drwav_uint64 totalSampleCount, drwav_bool32 isSequential, const drwav_allocation_callbacks* pAllocationCallbacks)
5401{
5402 FILE* pFile;
5403 if (drwav_fopen(&pFile, filename, "wb") != DRWAV_SUCCESS) {
5404 return DRWAV_FALSE;
5405 }
5407 /* This takes ownership of the FILE* object. */
5408 return drwav_init_file_write__internal_FILE(pWav, pFile, pFormat, totalSampleCount, isSequential, pAllocationCallbacks);
5409}
5411#ifndef DR_WAV_NO_WCHAR
5412DRWAV_PRIVATE drwav_bool32 drwav_init_file_write_w__internal(drwav* pWav, const wchar_t* filename, const drwav_data_format* pFormat, drwav_uint64 totalSampleCount, drwav_bool32 isSequential, const drwav_allocation_callbacks* pAllocationCallbacks)
5413{
5414 FILE* pFile;
5415 if (drwav_wfopen(&pFile, filename, L"wb", pAllocationCallbacks) != DRWAV_SUCCESS) {
5416 return DRWAV_FALSE;
5417 }
5419 /* This takes ownership of the FILE* object. */
5420 return drwav_init_file_write__internal_FILE(pWav, pFile, pFormat, totalSampleCount, isSequential, pAllocationCallbacks);
5421}
5422#endif
5424DRWAV_API drwav_bool32 drwav_init_file_write(drwav* pWav, const char* filename, const drwav_data_format* pFormat, const drwav_allocation_callbacks* pAllocationCallbacks)
5425{
5426 return drwav_init_file_write__internal(pWav, filename, pFormat, 0, DRWAV_FALSE, pAllocationCallbacks);
5427}
5429DRWAV_API drwav_bool32 drwav_init_file_write_sequential(drwav* pWav, const char* filename, const drwav_data_format* pFormat, drwav_uint64 totalSampleCount, const drwav_allocation_callbacks* pAllocationCallbacks)
5430{
5431 return drwav_init_file_write__internal(pWav, filename, pFormat, totalSampleCount, DRWAV_TRUE, pAllocationCallbacks);
5432}
5434DRWAV_API drwav_bool32 drwav_init_file_write_sequential_pcm_frames(drwav* pWav, const char* filename, const drwav_data_format* pFormat, drwav_uint64 totalPCMFrameCount, const drwav_allocation_callbacks* pAllocationCallbacks)
5435{
5436 if (pFormat == NULL) {
5437 return DRWAV_FALSE;
5438 }
5440 return drwav_init_file_write_sequential(pWav, filename, pFormat, totalPCMFrameCount*pFormat->channels, pAllocationCallbacks);
5441}
5443#ifndef DR_WAV_NO_WCHAR
5444DRWAV_API drwav_bool32 drwav_init_file_write_w(drwav* pWav, const wchar_t* filename, const drwav_data_format* pFormat, const drwav_allocation_callbacks* pAllocationCallbacks)
5445{
5446 return drwav_init_file_write_w__internal(pWav, filename, pFormat, 0, DRWAV_FALSE, pAllocationCallbacks);
5447}
5449DRWAV_API drwav_bool32 drwav_init_file_write_sequential_w(drwav* pWav, const wchar_t* filename, const drwav_data_format* pFormat, drwav_uint64 totalSampleCount, const drwav_allocation_callbacks* pAllocationCallbacks)
5450{
5451 return drwav_init_file_write_w__internal(pWav, filename, pFormat, totalSampleCount, DRWAV_TRUE, pAllocationCallbacks);
5452}
5454DRWAV_API drwav_bool32 drwav_init_file_write_sequential_pcm_frames_w(drwav* pWav, const wchar_t* filename, const drwav_data_format* pFormat, drwav_uint64 totalPCMFrameCount, const drwav_allocation_callbacks* pAllocationCallbacks)
5455{
5456 if (pFormat == NULL) {
5457 return DRWAV_FALSE;
5458 }
5460 return drwav_init_file_write_sequential_w(pWav, filename, pFormat, totalPCMFrameCount*pFormat->channels, pAllocationCallbacks);
5461}
5462#endif
5463#endif /* DR_WAV_NO_STDIO */
5466DRWAV_PRIVATE size_t drwav__on_read_memory(void* pUserData, void* pBufferOut, size_t bytesToRead)
5467{
5468 drwav* pWav = (drwav*)pUserData;
5469 size_t bytesRemaining;
5471 DRWAV_ASSERT(pWav != NULL);
5472 DRWAV_ASSERT(pWav->memoryStream.dataSize >= pWav->memoryStream.currentReadPos);
5474 bytesRemaining = pWav->memoryStream.dataSize - pWav->memoryStream.currentReadPos;
5475 if (bytesToRead > bytesRemaining) {
5476 bytesToRead = bytesRemaining;
5477 }
5479 if (bytesToRead > 0) {
5480 DRWAV_COPY_MEMORY(pBufferOut, pWav->memoryStream.data + pWav->memoryStream.currentReadPos, bytesToRead);
5481 pWav->memoryStream.currentReadPos += bytesToRead;
5482 }
5484 return bytesToRead;
5485}
5487DRWAV_PRIVATE drwav_bool32 drwav__on_seek_memory(void* pUserData, int offset, drwav_seek_origin origin)
5488{
5489 drwav* pWav = (drwav*)pUserData;
5490 drwav_int64 newCursor;
5492 DRWAV_ASSERT(pWav != NULL);
5494 newCursor = pWav->memoryStream.currentReadPos;
5496 if (origin == DRWAV_SEEK_SET) {
5497 newCursor = 0;
5498 } else if (origin == DRWAV_SEEK_CUR) {
5499 newCursor = (drwav_int64)pWav->memoryStream.currentReadPos;
5500 } else if (origin == DRWAV_SEEK_END) {
5501 newCursor = (drwav_int64)pWav->memoryStream.dataSize;
5502 } else {
5503 DRWAV_ASSERT(!"Invalid seek origin");
5504 return DRWAV_FALSE;
5505 }
5507 newCursor += offset;
5509 if (newCursor < 0) {
5510 return DRWAV_FALSE; /* Trying to seek prior to the start of the buffer. */
5511 }
5512 if ((size_t)newCursor > pWav->memoryStream.dataSize) {
5513 return DRWAV_FALSE; /* Trying to seek beyond the end of the buffer. */
5514 }
5516 pWav->memoryStream.currentReadPos = (size_t)newCursor;
5518 return DRWAV_TRUE;
5519}
5521DRWAV_PRIVATE size_t drwav__on_write_memory(void* pUserData, const void* pDataIn, size_t bytesToWrite)
5522{
5523 drwav* pWav = (drwav*)pUserData;
5524 size_t bytesRemaining;
5526 DRWAV_ASSERT(pWav != NULL);
5527 DRWAV_ASSERT(pWav->memoryStreamWrite.dataCapacity >= pWav->memoryStreamWrite.currentWritePos);
5529 bytesRemaining = pWav->memoryStreamWrite.dataCapacity - pWav->memoryStreamWrite.currentWritePos;
5530 if (bytesRemaining < bytesToWrite) {
5531 /* Need to reallocate. */
5532 void* pNewData;
5533 size_t newDataCapacity = (pWav->memoryStreamWrite.dataCapacity == 0) ? 256 : pWav->memoryStreamWrite.dataCapacity * 2;
5535 /* If doubling wasn't enough, just make it the minimum required size to write the data. */
5536 if ((newDataCapacity - pWav->memoryStreamWrite.currentWritePos) < bytesToWrite) {
5537 newDataCapacity = pWav->memoryStreamWrite.currentWritePos + bytesToWrite;
5538 }
5540 pNewData = drwav__realloc_from_callbacks(*pWav->memoryStreamWrite.ppData, newDataCapacity, pWav->memoryStreamWrite.dataCapacity, &pWav->allocationCallbacks);
5541 if (pNewData == NULL) {
5542 return 0;
5543 }
5545 *pWav->memoryStreamWrite.ppData = pNewData;
5546 pWav->memoryStreamWrite.dataCapacity = newDataCapacity;
5547 }
5549 DRWAV_COPY_MEMORY(((drwav_uint8*)(*pWav->memoryStreamWrite.ppData)) + pWav->memoryStreamWrite.currentWritePos, pDataIn, bytesToWrite);
5551 pWav->memoryStreamWrite.currentWritePos += bytesToWrite;
5552 if (pWav->memoryStreamWrite.dataSize < pWav->memoryStreamWrite.currentWritePos) {
5553 pWav->memoryStreamWrite.dataSize = pWav->memoryStreamWrite.currentWritePos;
5554 }
5556 *pWav->memoryStreamWrite.pDataSize = pWav->memoryStreamWrite.dataSize;
5558 return bytesToWrite;
5559}
5561DRWAV_PRIVATE drwav_bool32 drwav__on_seek_memory_write(void* pUserData, int offset, drwav_seek_origin origin)
5562{
5563 drwav* pWav = (drwav*)pUserData;
5564 drwav_int64 newCursor;
5566 DRWAV_ASSERT(pWav != NULL);
5568 newCursor = pWav->memoryStreamWrite.currentWritePos;
5570 if (origin == DRWAV_SEEK_SET) {
5571 newCursor = 0;
5572 } else if (origin == DRWAV_SEEK_CUR) {
5573 newCursor = (drwav_int64)pWav->memoryStreamWrite.currentWritePos;
5574 } else if (origin == DRWAV_SEEK_END) {
5575 newCursor = (drwav_int64)pWav->memoryStreamWrite.dataSize;
5576 } else {
5577 DRWAV_ASSERT(!"Invalid seek origin");
5578 return DRWAV_INVALID_ARGS;
5579 }
5581 newCursor += offset;
5583 if (newCursor < 0) {
5584 return DRWAV_FALSE; /* Trying to seek prior to the start of the buffer. */
5585 }
5586 if ((size_t)newCursor > pWav->memoryStreamWrite.dataSize) {
5587 return DRWAV_FALSE; /* Trying to seek beyond the end of the buffer. */
5588 }
5590 pWav->memoryStreamWrite.currentWritePos = (size_t)newCursor;
5592 return DRWAV_TRUE;
5593}
5595DRWAV_PRIVATE drwav_bool32 drwav__on_tell_memory(void* pUserData, drwav_int64* pCursor)
5596{
5597 drwav* pWav = (drwav*)pUserData;
5599 DRWAV_ASSERT(pWav != NULL);
5600 DRWAV_ASSERT(pCursor != NULL);
5602 *pCursor = (drwav_int64)pWav->memoryStream.currentReadPos;
5603 return DRWAV_TRUE;
5604}
5606DRWAV_API drwav_bool32 drwav_init_memory(drwav* pWav, const void* data, size_t dataSize, const drwav_allocation_callbacks* pAllocationCallbacks)
5607{
5608 return drwav_init_memory_ex(pWav, data, dataSize, NULL, NULL, 0, pAllocationCallbacks);
5609}
5611DRWAV_API drwav_bool32 drwav_init_memory_ex(drwav* pWav, const void* data, size_t dataSize, drwav_chunk_proc onChunk, void* pChunkUserData, drwav_uint32 flags, const drwav_allocation_callbacks* pAllocationCallbacks)
5612{
5613 if (data == NULL || dataSize == 0) {
5614 return DRWAV_FALSE;
5615 }
5617 if (!drwav_preinit(pWav, drwav__on_read_memory, drwav__on_seek_memory, drwav__on_tell_memory, pWav, pAllocationCallbacks)) {
5618 return DRWAV_FALSE;
5619 }
5621 pWav->memoryStream.data = (const drwav_uint8*)data;
5622 pWav->memoryStream.dataSize = dataSize;
5623 pWav->memoryStream.currentReadPos = 0;
5625 return drwav_init__internal(pWav, onChunk, pChunkUserData, flags);
5626}
5628DRWAV_API drwav_bool32 drwav_init_memory_with_metadata(drwav* pWav, const void* data, size_t dataSize, drwav_uint32 flags, const drwav_allocation_callbacks* pAllocationCallbacks)
5629{
5630 if (data == NULL || dataSize == 0) {
5631 return DRWAV_FALSE;
5632 }
5634 if (!drwav_preinit(pWav, drwav__on_read_memory, drwav__on_seek_memory, drwav__on_tell_memory, pWav, pAllocationCallbacks)) {
5635 return DRWAV_FALSE;
5636 }
5638 pWav->memoryStream.data = (const drwav_uint8*)data;
5639 pWav->memoryStream.dataSize = dataSize;
5640 pWav->memoryStream.currentReadPos = 0;
5642 return drwav_init__internal(pWav, NULL, NULL, flags | DRWAV_WITH_METADATA);
5643}
5646DRWAV_PRIVATE drwav_bool32 drwav_init_memory_write__internal(drwav* pWav, void** ppData, size_t* pDataSize, const drwav_data_format* pFormat, drwav_uint64 totalSampleCount, drwav_bool32 isSequential, const drwav_allocation_callbacks* pAllocationCallbacks)
5647{
5648 if (ppData == NULL || pDataSize == NULL) {
5649 return DRWAV_FALSE;
5650 }
5652 *ppData = NULL; /* Important because we're using realloc()! */
5653 *pDataSize = 0;
5655 if (!drwav_preinit_write(pWav, pFormat, isSequential, drwav__on_write_memory, drwav__on_seek_memory_write, pWav, pAllocationCallbacks)) {
5656 return DRWAV_FALSE;
5657 }
5659 pWav->memoryStreamWrite.ppData = ppData;
5660 pWav->memoryStreamWrite.pDataSize = pDataSize;
5661 pWav->memoryStreamWrite.dataSize = 0;
5662 pWav->memoryStreamWrite.dataCapacity = 0;
5663 pWav->memoryStreamWrite.currentWritePos = 0;
5665 return drwav_init_write__internal(pWav, pFormat, totalSampleCount);
5666}
5668DRWAV_API drwav_bool32 drwav_init_memory_write(drwav* pWav, void** ppData, size_t* pDataSize, const drwav_data_format* pFormat, const drwav_allocation_callbacks* pAllocationCallbacks)
5669{
5670 return drwav_init_memory_write__internal(pWav, ppData, pDataSize, pFormat, 0, DRWAV_FALSE, pAllocationCallbacks);
5671}
5673DRWAV_API drwav_bool32 drwav_init_memory_write_sequential(drwav* pWav, void** ppData, size_t* pDataSize, const drwav_data_format* pFormat, drwav_uint64 totalSampleCount, const drwav_allocation_callbacks* pAllocationCallbacks)
5674{
5675 return drwav_init_memory_write__internal(pWav, ppData, pDataSize, pFormat, totalSampleCount, DRWAV_TRUE, pAllocationCallbacks);
5676}
5678DRWAV_API drwav_bool32 drwav_init_memory_write_sequential_pcm_frames(drwav* pWav, void** ppData, size_t* pDataSize, const drwav_data_format* pFormat, drwav_uint64 totalPCMFrameCount, const drwav_allocation_callbacks* pAllocationCallbacks)
5679{
5680 if (pFormat == NULL) {
5681 return DRWAV_FALSE;
5682 }
5684 return drwav_init_memory_write_sequential(pWav, ppData, pDataSize, pFormat, totalPCMFrameCount*pFormat->channels, pAllocationCallbacks);
5685}
5689DRWAV_API drwav_result drwav_uninit(drwav* pWav)
5690{
5691 drwav_result result = DRWAV_SUCCESS;
5693 if (pWav == NULL) {
5694 return DRWAV_INVALID_ARGS;
5695 }
5697 /*
5698 If the drwav object was opened in write mode we'll need to finalize a few things:
5699 - Make sure the "data" chunk is aligned to 16-bits for RIFF containers, or 64 bits for W64 containers.
5700 - Set the size of the "data" chunk.
5701 */
5702 if (pWav->onWrite != NULL) {
5703 drwav_uint32 paddingSize = 0;
5705 /* Padding. Do not adjust pWav->dataChunkDataSize - this should not include the padding. */
5706 if (pWav->container == drwav_container_riff || pWav->container == drwav_container_rf64) {
5707 paddingSize = drwav__chunk_padding_size_riff(pWav->dataChunkDataSize);
5708 } else {
5709 paddingSize = drwav__chunk_padding_size_w64(pWav->dataChunkDataSize);
5710 }
5712 if (paddingSize > 0) {
5713 drwav_uint64 paddingData = 0;
5714 drwav__write(pWav, &paddingData, paddingSize); /* Byte order does not matter for this. */
5715 }
5717 /*
5718 Chunk sizes. When using sequential mode, these will have been filled in at initialization time. We only need
5719 to do this when using non-sequential mode.
5720 */
5721 if (pWav->onSeek && !pWav->isSequentialWrite) {
5722 if (pWav->container == drwav_container_riff) {
5723 /* The "RIFF" chunk size. */
5724 if (pWav->onSeek(pWav->pUserData, 4, DRWAV_SEEK_SET)) {
5725 drwav_uint32 riffChunkSize = drwav__riff_chunk_size_riff(pWav->dataChunkDataSize, pWav->pMetadata, pWav->metadataCount);
5726 drwav__write_u32ne_to_le(pWav, riffChunkSize);
5727 }
5729 /* The "data" chunk size. */
5730 if (pWav->onSeek(pWav->pUserData, (int)pWav->dataChunkDataPos - 4, DRWAV_SEEK_SET)) {
5731 drwav_uint32 dataChunkSize = drwav__data_chunk_size_riff(pWav->dataChunkDataSize);
5732 drwav__write_u32ne_to_le(pWav, dataChunkSize);
5733 }
5734 } else if (pWav->container == drwav_container_w64) {
5735 /* The "RIFF" chunk size. */
5736 if (pWav->onSeek(pWav->pUserData, 16, DRWAV_SEEK_SET)) {
5737 drwav_uint64 riffChunkSize = drwav__riff_chunk_size_w64(pWav->dataChunkDataSize);
5738 drwav__write_u64ne_to_le(pWav, riffChunkSize);
5739 }
5741 /* The "data" chunk size. */
5742 if (pWav->onSeek(pWav->pUserData, (int)pWav->dataChunkDataPos - 8, DRWAV_SEEK_SET)) {
5743 drwav_uint64 dataChunkSize = drwav__data_chunk_size_w64(pWav->dataChunkDataSize);
5744 drwav__write_u64ne_to_le(pWav, dataChunkSize);
5745 }
5746 } else if (pWav->container == drwav_container_rf64) {
5747 /* We only need to update the ds64 chunk. The "RIFF" and "data" chunks always have their sizes set to 0xFFFFFFFF for RF64. */
5748 int ds64BodyPos = 12 + 8;
5750 /* The "RIFF" chunk size. */
5751 if (pWav->onSeek(pWav->pUserData, ds64BodyPos + 0, DRWAV_SEEK_SET)) {
5752 drwav_uint64 riffChunkSize = drwav__riff_chunk_size_rf64(pWav->dataChunkDataSize, pWav->pMetadata, pWav->metadataCount);
5753 drwav__write_u64ne_to_le(pWav, riffChunkSize);
5754 }
5756 /* The "data" chunk size. */
5757 if (pWav->onSeek(pWav->pUserData, ds64BodyPos + 8, DRWAV_SEEK_SET)) {
5758 drwav_uint64 dataChunkSize = drwav__data_chunk_size_rf64(pWav->dataChunkDataSize);
5759 drwav__write_u64ne_to_le(pWav, dataChunkSize);
5760 }
5761 }
5762 }
5764 /* Validation for sequential mode. */
5765 if (pWav->isSequentialWrite) {
5766 if (pWav->dataChunkDataSize != pWav->dataChunkDataSizeTargetWrite) {
5767 result = DRWAV_INVALID_FILE;
5768 }
5769 }
5770 } else {
5771 drwav_free(pWav->pMetadata, &pWav->allocationCallbacks);
5772 }
5774#ifndef DR_WAV_NO_STDIO
5775 /*
5776 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()
5777 was used by looking at the onRead and onSeek callbacks.
5778 */
5779 if (pWav->onRead == drwav__on_read_stdio || pWav->onWrite == drwav__on_write_stdio) {
5780 fclose((FILE*)pWav->pUserData);
5781 }
5782#endif
5784 return result;
5785}
5789DRWAV_API size_t drwav_read_raw(drwav* pWav, size_t bytesToRead, void* pBufferOut)
5790{
5791 size_t bytesRead;
5792 drwav_uint32 bytesPerFrame;
5794 if (pWav == NULL || bytesToRead == 0) {
5795 return 0; /* Invalid args. */
5796 }
5798 if (bytesToRead > pWav->bytesRemaining) {
5799 bytesToRead = (size_t)pWav->bytesRemaining;
5800 }
5802 if (bytesToRead == 0) {
5803 return 0; /* At end. */
5804 }
5806 bytesPerFrame = drwav_get_bytes_per_pcm_frame(pWav);
5807 if (bytesPerFrame == 0) {
5808 return 0; /* Could not determine the bytes per frame. */
5809 }
5811 if (pBufferOut != NULL) {
5812 bytesRead = pWav->onRead(pWav->pUserData, pBufferOut, bytesToRead);
5813 } else {
5814 /* We need to seek. If we fail, we need to read-and-discard to make sure we get a good byte count. */
5815 bytesRead = 0;
5816 while (bytesRead < bytesToRead) {
5817 size_t bytesToSeek = (bytesToRead - bytesRead);
5818 if (bytesToSeek > 0x7FFFFFFF) {
5819 bytesToSeek = 0x7FFFFFFF;
5820 }
5822 if (pWav->onSeek(pWav->pUserData, (int)bytesToSeek, DRWAV_SEEK_CUR) == DRWAV_FALSE) {
5823 break;
5824 }
5826 bytesRead += bytesToSeek;
5827 }
5829 /* When we get here we may need to read-and-discard some data. */
5830 while (bytesRead < bytesToRead) {
5831 drwav_uint8 buffer[4096];
5832 size_t bytesSeeked;
5833 size_t bytesToSeek = (bytesToRead - bytesRead);
5834 if (bytesToSeek > sizeof(buffer)) {
5835 bytesToSeek = sizeof(buffer);
5836 }
5838 bytesSeeked = pWav->onRead(pWav->pUserData, buffer, bytesToSeek);
5839 bytesRead += bytesSeeked;
5841 if (bytesSeeked < bytesToSeek) {
5842 break; /* Reached the end. */
5843 }
5844 }
5845 }
5847 pWav->readCursorInPCMFrames += bytesRead / bytesPerFrame;
5849 pWav->bytesRemaining -= bytesRead;
5850 return bytesRead;
5851}
5855DRWAV_API drwav_uint64 drwav_read_pcm_frames_le(drwav* pWav, drwav_uint64 framesToRead, void* pBufferOut)
5856{
5857 drwav_uint32 bytesPerFrame;
5858 drwav_uint64 bytesToRead; /* Intentionally uint64 instead of size_t so we can do a check that we're not reading too much on 32-bit builds. */
5859 drwav_uint64 framesRemainingInFile;
5861 if (pWav == NULL || framesToRead == 0) {
5862 return 0;
5863 }
5865 /* Cannot use this function for compressed formats. */
5866 if (drwav__is_compressed_format_tag(pWav->translatedFormatTag)) {
5867 return 0;
5868 }
5870 framesRemainingInFile = pWav->totalPCMFrameCount - pWav->readCursorInPCMFrames;
5871 if (framesToRead > framesRemainingInFile) {
5872 framesToRead = framesRemainingInFile;
5873 }
5875 bytesPerFrame = drwav_get_bytes_per_pcm_frame(pWav);
5876 if (bytesPerFrame == 0) {
5877 return 0;
5878 }
5880 /* Don't try to read more samples than can potentially fit in the output buffer. */
5881 bytesToRead = framesToRead * bytesPerFrame;
5882 if (bytesToRead > DRWAV_SIZE_MAX) {
5883 bytesToRead = (DRWAV_SIZE_MAX / bytesPerFrame) * bytesPerFrame; /* Round the number of bytes to read to a clean frame boundary. */
5884 }
5886 /*
5887 Doing an explicit check here just to make it clear that we don't want to be attempt to read anything if there's no bytes to read. There
5888 *could* be a time where it evaluates to 0 due to overflowing.
5889 */
5890 if (bytesToRead == 0) {
5891 return 0;
5892 }
5894 return drwav_read_raw(pWav, (size_t)bytesToRead, pBufferOut) / bytesPerFrame;
5895}
5897DRWAV_API drwav_uint64 drwav_read_pcm_frames_be(drwav* pWav, drwav_uint64 framesToRead, void* pBufferOut)
5898{
5899 drwav_uint64 framesRead = drwav_read_pcm_frames_le(pWav, framesToRead, pBufferOut);
5901 if (pBufferOut != NULL) {
5902 drwav_uint32 bytesPerFrame = drwav_get_bytes_per_pcm_frame(pWav);
5903 if (bytesPerFrame == 0) {
5904 return 0; /* Could not get the bytes per frame which means bytes per sample cannot be determined and we don't know how to byte swap. */
5905 }
5907 drwav__bswap_samples(pBufferOut, framesRead*pWav->channels, bytesPerFrame/pWav->channels);
5908 }
5910 return framesRead;
5911}
5913DRWAV_API drwav_uint64 drwav_read_pcm_frames(drwav* pWav, drwav_uint64 framesToRead, void* pBufferOut)
5914{
5915 drwav_uint64 framesRead = 0;
5917 if (drwav_is_container_be(pWav->container)) {
5918 /*
5919 Special case for AIFF. AIFF is a big-endian encoded format, but it supports a format that is
5920 PCM in little-endian encoding. In this case, we fall through this branch and treate it as
5921 little-endian.
5922 */
5923 if (pWav->container != drwav_container_aiff || pWav->aiff.isLE == DRWAV_FALSE) {
5924 if (drwav__is_little_endian()) {
5925 framesRead = drwav_read_pcm_frames_be(pWav, framesToRead, pBufferOut);
5926 } else {
5927 framesRead = drwav_read_pcm_frames_le(pWav, framesToRead, pBufferOut);
5928 }
5930 goto post_process;
5931 }
5932 }
5934 /* Getting here means the data should be considered little-endian. */
5935 if (drwav__is_little_endian()) {
5936 framesRead = drwav_read_pcm_frames_le(pWav, framesToRead, pBufferOut);
5937 } else {
5938 framesRead = drwav_read_pcm_frames_be(pWav, framesToRead, pBufferOut);
5939 }
5941 /*
5942 Here is where we check if we need to do a signed/unsigned conversion for AIFF. The reason we need to do this
5943 is because dr_wav always assumes an 8-bit sample is unsigned, whereas AIFF can have signed 8-bit formats.
5944 */
5945 post_process:
5946 {
5947 if (pWav->container == drwav_container_aiff && pWav->bitsPerSample == 8 && pWav->aiff.isUnsigned == DRWAV_FALSE) {
5948 if (pBufferOut != NULL) {
5949 drwav_uint64 iSample;
5951 for (iSample = 0; iSample < framesRead * pWav->channels; iSample += 1) {
5952 ((drwav_uint8*)pBufferOut)[iSample] += 128;
5953 }
5954 }
5955 }
5956 }
5958 return framesRead;
5959}
5963DRWAV_PRIVATE drwav_bool32 drwav_seek_to_first_pcm_frame(drwav* pWav)
5964{
5965 if (pWav->onWrite != NULL) {
5966 return DRWAV_FALSE; /* No seeking in write mode. */
5967 }
5969 if (!pWav->onSeek(pWav->pUserData, (int)pWav->dataChunkDataPos, DRWAV_SEEK_SET)) {
5970 return DRWAV_FALSE;
5971 }
5973 if (drwav__is_compressed_format_tag(pWav->translatedFormatTag)) {
5974 /* Cached data needs to be cleared for compressed formats. */
5975 if (pWav->translatedFormatTag == DR_WAVE_FORMAT_ADPCM) {
5976 DRWAV_ZERO_OBJECT(&pWav->msadpcm);
5977 } else if (pWav->translatedFormatTag == DR_WAVE_FORMAT_DVI_ADPCM) {
5978 DRWAV_ZERO_OBJECT(&pWav->ima);
5979 } else {
5980 DRWAV_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. */
5981 }
5982 }
5984 pWav->readCursorInPCMFrames = 0;
5985 pWav->bytesRemaining = pWav->dataChunkDataSize;
5987 return DRWAV_TRUE;
5988}
5990DRWAV_API drwav_bool32 drwav_seek_to_pcm_frame(drwav* pWav, drwav_uint64 targetFrameIndex)
5991{
5992 /* Seeking should be compatible with wave files > 2GB. */
5994 if (pWav == NULL || pWav->onSeek == NULL) {
5995 return DRWAV_FALSE;
5996 }
5998 /* No seeking in write mode. */
5999 if (pWav->onWrite != NULL) {
6000 return DRWAV_FALSE;
6001 }
6003 /* If there are no samples, just return DRWAV_TRUE without doing anything. */
6004 if (pWav->totalPCMFrameCount == 0) {
6005 return DRWAV_TRUE;
6006 }
6008 /* Make sure the sample is clamped. */
6009 if (targetFrameIndex > pWav->totalPCMFrameCount) {
6010 targetFrameIndex = pWav->totalPCMFrameCount;
6011 }
6013 /*
6014 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
6015 to seek back to the start.
6016 */
6017 if (drwav__is_compressed_format_tag(pWav->translatedFormatTag)) {
6018 /* TODO: This can be optimized. */
6020 /*
6021 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,
6022 we first need to seek back to the start and then just do the same thing as a forward seek.
6023 */
6024 if (targetFrameIndex < pWav->readCursorInPCMFrames) {
6025 if (!drwav_seek_to_first_pcm_frame(pWav)) {
6026 return DRWAV_FALSE;
6027 }
6028 }
6030 if (targetFrameIndex > pWav->readCursorInPCMFrames) {
6031 drwav_uint64 offsetInFrames = targetFrameIndex - pWav->readCursorInPCMFrames;
6033 drwav_int16 devnull[2048];
6034 while (offsetInFrames > 0) {
6035 drwav_uint64 framesRead = 0;
6036 drwav_uint64 framesToRead = offsetInFrames;
6037 if (framesToRead > drwav_countof(devnull)/pWav->channels) {
6038 framesToRead = drwav_countof(devnull)/pWav->channels;
6039 }
6041 if (pWav->translatedFormatTag == DR_WAVE_FORMAT_ADPCM) {
6042 framesRead = drwav_read_pcm_frames_s16__msadpcm(pWav, framesToRead, devnull);
6043 } else if (pWav->translatedFormatTag == DR_WAVE_FORMAT_DVI_ADPCM) {
6044 framesRead = drwav_read_pcm_frames_s16__ima(pWav, framesToRead, devnull);
6045 } else {
6046 DRWAV_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. */
6047 }
6049 if (framesRead != framesToRead) {
6050 return DRWAV_FALSE;
6051 }
6053 offsetInFrames -= framesRead;
6054 }
6055 }
6056 } else {
6057 drwav_uint64 totalSizeInBytes;
6058 drwav_uint64 currentBytePos;
6059 drwav_uint64 targetBytePos;
6060 drwav_uint64 offset;
6061 drwav_uint32 bytesPerFrame;
6063 bytesPerFrame = drwav_get_bytes_per_pcm_frame(pWav);
6064 if (bytesPerFrame == 0) {
6065 return DRWAV_FALSE; /* Not able to calculate offset. */
6066 }
6068 totalSizeInBytes = pWav->totalPCMFrameCount * bytesPerFrame;
6069 /*DRWAV_ASSERT(totalSizeInBytes >= pWav->bytesRemaining);*/
6071 currentBytePos = totalSizeInBytes - pWav->bytesRemaining;
6072 targetBytePos = targetFrameIndex * bytesPerFrame;
6074 if (currentBytePos < targetBytePos) {
6075 /* Offset forwards. */
6076 offset = (targetBytePos - currentBytePos);
6077 } else {
6078 /* Offset backwards. */
6079 if (!drwav_seek_to_first_pcm_frame(pWav)) {
6080 return DRWAV_FALSE;
6081 }
6082 offset = targetBytePos;
6083 }
6085 while (offset > 0) {
6086 int offset32 = ((offset > INT_MAX) ? INT_MAX : (int)offset);
6087 if (!pWav->onSeek(pWav->pUserData, offset32, DRWAV_SEEK_CUR)) {
6088 return DRWAV_FALSE;
6089 }
6091 pWav->readCursorInPCMFrames += offset32 / bytesPerFrame;
6092 pWav->bytesRemaining -= offset32;
6093 offset -= offset32;
6094 }
6095 }
6097 return DRWAV_TRUE;
6098}
6100DRWAV_API drwav_result drwav_get_cursor_in_pcm_frames(drwav* pWav, drwav_uint64* pCursor)
6101{
6102 if (pCursor == NULL) {
6103 return DRWAV_INVALID_ARGS;
6104 }
6106 *pCursor = 0; /* Safety. */
6108 if (pWav == NULL) {
6109 return DRWAV_INVALID_ARGS;
6110 }
6112 *pCursor = pWav->readCursorInPCMFrames;
6114 return DRWAV_SUCCESS;
6115}
6117DRWAV_API drwav_result drwav_get_length_in_pcm_frames(drwav* pWav, drwav_uint64* pLength)
6118{
6119 if (pLength == NULL) {
6120 return DRWAV_INVALID_ARGS;
6121 }
6123 *pLength = 0; /* Safety. */
6125 if (pWav == NULL) {
6126 return DRWAV_INVALID_ARGS;
6127 }
6129 *pLength = pWav->totalPCMFrameCount;
6131 return DRWAV_SUCCESS;
6132}
6135DRWAV_API size_t drwav_write_raw(drwav* pWav, size_t bytesToWrite, const void* pData)
6136{
6137 size_t bytesWritten;
6139 if (pWav == NULL || bytesToWrite == 0 || pData == NULL) {
6140 return 0;
6141 }
6143 bytesWritten = pWav->onWrite(pWav->pUserData, pData, bytesToWrite);
6144 pWav->dataChunkDataSize += bytesWritten;
6146 return bytesWritten;
6147}
6149DRWAV_API drwav_uint64 drwav_write_pcm_frames_le(drwav* pWav, drwav_uint64 framesToWrite, const void* pData)
6150{
6151 drwav_uint64 bytesToWrite;
6152 drwav_uint64 bytesWritten;
6153 const drwav_uint8* pRunningData;
6155 if (pWav == NULL || framesToWrite == 0 || pData == NULL) {
6156 return 0;
6157 }
6159 bytesToWrite = ((framesToWrite * pWav->channels * pWav->bitsPerSample) / 8);
6160 if (bytesToWrite > DRWAV_SIZE_MAX) {
6161 return 0;
6162 }
6164 bytesWritten = 0;
6165 pRunningData = (const drwav_uint8*)pData;
6167 while (bytesToWrite > 0) {
6168 size_t bytesJustWritten;
6169 drwav_uint64 bytesToWriteThisIteration;
6171 bytesToWriteThisIteration = bytesToWrite;
6172 DRWAV_ASSERT(bytesToWriteThisIteration <= DRWAV_SIZE_MAX); /* <-- This is checked above. */
6174 bytesJustWritten = drwav_write_raw(pWav, (size_t)bytesToWriteThisIteration, pRunningData);
6175 if (bytesJustWritten == 0) {
6176 break;
6177 }
6179 bytesToWrite -= bytesJustWritten;
6180 bytesWritten += bytesJustWritten;
6181 pRunningData += bytesJustWritten;
6182 }
6184 return (bytesWritten * 8) / pWav->bitsPerSample / pWav->channels;
6185}
6187DRWAV_API drwav_uint64 drwav_write_pcm_frames_be(drwav* pWav, drwav_uint64 framesToWrite, const void* pData)
6188{
6189 drwav_uint64 bytesToWrite;
6190 drwav_uint64 bytesWritten;
6191 drwav_uint32 bytesPerSample;
6192 const drwav_uint8* pRunningData;
6194 if (pWav == NULL || framesToWrite == 0 || pData == NULL) {
6195 return 0;
6196 }
6198 bytesToWrite = ((framesToWrite * pWav->channels * pWav->bitsPerSample) / 8);
6199 if (bytesToWrite > DRWAV_SIZE_MAX) {
6200 return 0;
6201 }
6203 bytesWritten = 0;
6204 pRunningData = (const drwav_uint8*)pData;
6206 bytesPerSample = drwav_get_bytes_per_pcm_frame(pWav) / pWav->channels;
6207 if (bytesPerSample == 0) {
6208 return 0; /* Cannot determine bytes per sample, or bytes per sample is less than one byte. */
6209 }
6211 while (bytesToWrite > 0) {
6212 drwav_uint8 temp[4096];
6213 drwav_uint32 sampleCount;
6214 size_t bytesJustWritten;
6215 drwav_uint64 bytesToWriteThisIteration;
6217 bytesToWriteThisIteration = bytesToWrite;
6218 DRWAV_ASSERT(bytesToWriteThisIteration <= DRWAV_SIZE_MAX); /* <-- This is checked above. */
6220 /*
6221 WAV files are always little-endian. We need to byte swap on big-endian architectures. Since our input buffer is read-only we need
6222 to use an intermediary buffer for the conversion.
6223 */
6224 sampleCount = sizeof(temp)/bytesPerSample;
6226 if (bytesToWriteThisIteration > ((drwav_uint64)sampleCount)*bytesPerSample) {
6227 bytesToWriteThisIteration = ((drwav_uint64)sampleCount)*bytesPerSample;
6228 }
6230 DRWAV_COPY_MEMORY(temp, pRunningData, (size_t)bytesToWriteThisIteration);
6231 drwav__bswap_samples(temp, sampleCount, bytesPerSample);
6233 bytesJustWritten = drwav_write_raw(pWav, (size_t)bytesToWriteThisIteration, temp);
6234 if (bytesJustWritten == 0) {
6235 break;
6236 }
6238 bytesToWrite -= bytesJustWritten;
6239 bytesWritten += bytesJustWritten;
6240 pRunningData += bytesJustWritten;
6241 }
6243 return (bytesWritten * 8) / pWav->bitsPerSample / pWav->channels;
6244}
6246DRWAV_API drwav_uint64 drwav_write_pcm_frames(drwav* pWav, drwav_uint64 framesToWrite, const void* pData)
6247{
6248 if (drwav__is_little_endian()) {
6249 return drwav_write_pcm_frames_le(pWav, framesToWrite, pData);
6250 } else {
6251 return drwav_write_pcm_frames_be(pWav, framesToWrite, pData);
6252 }
6253}
6256DRWAV_PRIVATE drwav_uint64 drwav_read_pcm_frames_s16__msadpcm(drwav* pWav, drwav_uint64 framesToRead, drwav_int16* pBufferOut)
6257{
6258 drwav_uint64 totalFramesRead = 0;
6260 static drwav_int32 adaptationTable[] = {
6261 230, 230, 230, 230, 307, 409, 512, 614,
6262 768, 614, 512, 409, 307, 230, 230, 230
6263 };
6264 static drwav_int32 coeff1Table[] = { 256, 512, 0, 192, 240, 460, 392 };
6265 static drwav_int32 coeff2Table[] = { 0, -256, 0, 64, 0, -208, -232 };
6267 DRWAV_ASSERT(pWav != NULL);
6268 DRWAV_ASSERT(framesToRead > 0);
6270 /* TODO: Lots of room for optimization here. */
6272 while (pWav->readCursorInPCMFrames < pWav->totalPCMFrameCount) {
6273 DRWAV_ASSERT(framesToRead > 0); /* This loop iteration will never get hit with framesToRead == 0 because it's asserted at the top, and we check for 0 inside the loop just below. */
6275 /* If there are no cached frames we need to load a new block. */
6276 if (pWav->msadpcm.cachedFrameCount == 0 && pWav->msadpcm.bytesRemainingInBlock == 0) {
6277 if (pWav->channels == 1) {
6278 /* Mono. */
6279 drwav_uint8 header[7];
6280 if (pWav->onRead(pWav->pUserData, header, sizeof(header)) != sizeof(header)) {
6281 return totalFramesRead;
6282 }
6283 pWav->msadpcm.bytesRemainingInBlock = pWav->fmt.blockAlign - sizeof(header);
6285 pWav->msadpcm.predictor[0] = header[0];
6286 pWav->msadpcm.delta[0] = drwav_bytes_to_s16(header + 1);
6287 pWav->msadpcm.prevFrames[0][1] = (drwav_int32)drwav_bytes_to_s16(header + 3);
6288 pWav->msadpcm.prevFrames[0][0] = (drwav_int32)drwav_bytes_to_s16(header + 5);
6289 pWav->msadpcm.cachedFrames[2] = pWav->msadpcm.prevFrames[0][0];
6290 pWav->msadpcm.cachedFrames[3] = pWav->msadpcm.prevFrames[0][1];
6291 pWav->msadpcm.cachedFrameCount = 2;
6293 /* The predictor is used as an index into coeff1Table so we'll need to validate to ensure it never overflows. */
6294 if (pWav->msadpcm.predictor[0] >= drwav_countof(coeff1Table)) {
6295 return totalFramesRead; /* Invalid file. */
6296 }
6297 } else {
6298 /* Stereo. */
6299 drwav_uint8 header[14];
6300 if (pWav->onRead(pWav->pUserData, header, sizeof(header)) != sizeof(header)) {
6301 return totalFramesRead;
6302 }
6303 pWav->msadpcm.bytesRemainingInBlock = pWav->fmt.blockAlign - sizeof(header);
6305 pWav->msadpcm.predictor[0] = header[0];
6306 pWav->msadpcm.predictor[1] = header[1];
6307 pWav->msadpcm.delta[0] = drwav_bytes_to_s16(header + 2);
6308 pWav->msadpcm.delta[1] = drwav_bytes_to_s16(header + 4);
6309 pWav->msadpcm.prevFrames[0][1] = (drwav_int32)drwav_bytes_to_s16(header + 6);
6310 pWav->msadpcm.prevFrames[1][1] = (drwav_int32)drwav_bytes_to_s16(header + 8);
6311 pWav->msadpcm.prevFrames[0][0] = (drwav_int32)drwav_bytes_to_s16(header + 10);
6312 pWav->msadpcm.prevFrames[1][0] = (drwav_int32)drwav_bytes_to_s16(header + 12);
6314 pWav->msadpcm.cachedFrames[0] = pWav->msadpcm.prevFrames[0][0];
6315 pWav->msadpcm.cachedFrames[1] = pWav->msadpcm.prevFrames[1][0];
6316 pWav->msadpcm.cachedFrames[2] = pWav->msadpcm.prevFrames[0][1];
6317 pWav->msadpcm.cachedFrames[3] = pWav->msadpcm.prevFrames[1][1];
6318 pWav->msadpcm.cachedFrameCount = 2;
6320 /* The predictor is used as an index into coeff1Table so we'll need to validate to ensure it never overflows. */
6321 if (pWav->msadpcm.predictor[0] >= drwav_countof(coeff1Table) || pWav->msadpcm.predictor[1] >= drwav_countof(coeff2Table)) {
6322 return totalFramesRead; /* Invalid file. */
6323 }
6324 }
6325 }
6327 /* Output anything that's cached. */
6328 while (framesToRead > 0 && pWav->msadpcm.cachedFrameCount > 0 && pWav->readCursorInPCMFrames < pWav->totalPCMFrameCount) {
6329 if (pBufferOut != NULL) {
6330 drwav_uint32 iSample = 0;
6331 for (iSample = 0; iSample < pWav->channels; iSample += 1) {
6332 pBufferOut[iSample] = (drwav_int16)pWav->msadpcm.cachedFrames[(drwav_countof(pWav->msadpcm.cachedFrames) - (pWav->msadpcm.cachedFrameCount*pWav->channels)) + iSample];
6333 }
6335 pBufferOut += pWav->channels;
6336 }
6338 framesToRead -= 1;
6339 totalFramesRead += 1;
6340 pWav->readCursorInPCMFrames += 1;
6341 pWav->msadpcm.cachedFrameCount -= 1;
6342 }
6344 if (framesToRead == 0) {
6345 break;
6346 }
6349 /*
6350 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
6351 loop iteration which will trigger the loading of a new block.
6352 */
6353 if (pWav->msadpcm.cachedFrameCount == 0) {
6354 if (pWav->msadpcm.bytesRemainingInBlock == 0) {
6355 continue;
6356 } else {
6357 drwav_uint8 nibbles;
6358 drwav_int32 nibble0;
6359 drwav_int32 nibble1;
6361 if (pWav->onRead(pWav->pUserData, &nibbles, 1) != 1) {
6362 return totalFramesRead;
6363 }
6364 pWav->msadpcm.bytesRemainingInBlock -= 1;
6366 /* TODO: Optimize away these if statements. */
6367 nibble0 = ((nibbles & 0xF0) >> 4); if ((nibbles & 0x80)) { nibble0 |= 0xFFFFFFF0UL; }
6368 nibble1 = ((nibbles & 0x0F) >> 0); if ((nibbles & 0x08)) { nibble1 |= 0xFFFFFFF0UL; }
6370 if (pWav->channels == 1) {
6371 /* Mono. */
6372 drwav_int32 newSample0;
6373 drwav_int32 newSample1;
6375 newSample0 = ((pWav->msadpcm.prevFrames[0][1] * coeff1Table[pWav->msadpcm.predictor[0]]) + (pWav->msadpcm.prevFrames[0][0] * coeff2Table[pWav->msadpcm.predictor[0]])) >> 8;
6376 newSample0 += nibble0 * pWav->msadpcm.delta[0];
6377 newSample0 = drwav_clamp(newSample0, -32768, 32767);
6379 pWav->msadpcm.delta[0] = (adaptationTable[((nibbles & 0xF0) >> 4)] * pWav->msadpcm.delta[0]) >> 8;
6380 if (pWav->msadpcm.delta[0] < 16) {
6381 pWav->msadpcm.delta[0] = 16;
6382 }
6384 pWav->msadpcm.prevFrames[0][0] = pWav->msadpcm.prevFrames[0][1];
6385 pWav->msadpcm.prevFrames[0][1] = newSample0;
6388 newSample1 = ((pWav->msadpcm.prevFrames[0][1] * coeff1Table[pWav->msadpcm.predictor[0]]) + (pWav->msadpcm.prevFrames[0][0] * coeff2Table[pWav->msadpcm.predictor[0]])) >> 8;
6389 newSample1 += nibble1 * pWav->msadpcm.delta[0];
6390 newSample1 = drwav_clamp(newSample1, -32768, 32767);
6392 pWav->msadpcm.delta[0] = (adaptationTable[((nibbles & 0x0F) >> 0)] * pWav->msadpcm.delta[0]) >> 8;
6393 if (pWav->msadpcm.delta[0] < 16) {
6394 pWav->msadpcm.delta[0] = 16;
6395 }
6397 pWav->msadpcm.prevFrames[0][0] = pWav->msadpcm.prevFrames[0][1];
6398 pWav->msadpcm.prevFrames[0][1] = newSample1;
6401 pWav->msadpcm.cachedFrames[2] = newSample0;
6402 pWav->msadpcm.cachedFrames[3] = newSample1;
6403 pWav->msadpcm.cachedFrameCount = 2;
6404 } else {
6405 /* Stereo. */
6406 drwav_int32 newSample0;
6407 drwav_int32 newSample1;
6409 /* Left. */
6410 newSample0 = ((pWav->msadpcm.prevFrames[0][1] * coeff1Table[pWav->msadpcm.predictor[0]]) + (pWav->msadpcm.prevFrames[0][0] * coeff2Table[pWav->msadpcm.predictor[0]])) >> 8;
6411 newSample0 += nibble0 * pWav->msadpcm.delta[0];
6412 newSample0 = drwav_clamp(newSample0, -32768, 32767);
6414 pWav->msadpcm.delta[0] = (adaptationTable[((nibbles & 0xF0) >> 4)] * pWav->msadpcm.delta[0]) >> 8;
6415 if (pWav->msadpcm.delta[0] < 16) {
6416 pWav->msadpcm.delta[0] = 16;
6417 }
6419 pWav->msadpcm.prevFrames[0][0] = pWav->msadpcm.prevFrames[0][1];
6420 pWav->msadpcm.prevFrames[0][1] = newSample0;
6423 /* Right. */
6424 newSample1 = ((pWav->msadpcm.prevFrames[1][1] * coeff1Table[pWav->msadpcm.predictor[1]]) + (pWav->msadpcm.prevFrames[1][0] * coeff2Table[pWav->msadpcm.predictor[1]])) >> 8;
6425 newSample1 += nibble1 * pWav->msadpcm.delta[1];
6426 newSample1 = drwav_clamp(newSample1, -32768, 32767);
6428 pWav->msadpcm.delta[1] = (adaptationTable[((nibbles & 0x0F) >> 0)] * pWav->msadpcm.delta[1]) >> 8;
6429 if (pWav->msadpcm.delta[1] < 16) {
6430 pWav->msadpcm.delta[1] = 16;
6431 }
6433 pWav->msadpcm.prevFrames[1][0] = pWav->msadpcm.prevFrames[1][1];
6434 pWav->msadpcm.prevFrames[1][1] = newSample1;
6436 pWav->msadpcm.cachedFrames[2] = newSample0;
6437 pWav->msadpcm.cachedFrames[3] = newSample1;
6438 pWav->msadpcm.cachedFrameCount = 1;
6439 }
6440 }
6441 }
6442 }
6444 return totalFramesRead;
6445}
6448DRWAV_PRIVATE drwav_uint64 drwav_read_pcm_frames_s16__ima(drwav* pWav, drwav_uint64 framesToRead, drwav_int16* pBufferOut)
6449{
6450 drwav_uint64 totalFramesRead = 0;
6451 drwav_uint32 iChannel;
6453 static drwav_int32 indexTable[16] = {
6454 -1, -1, -1, -1, 2, 4, 6, 8,
6455 -1, -1, -1, -1, 2, 4, 6, 8
6456 };
6458 static drwav_int32 stepTable[89] = {
6459 7, 8, 9, 10, 11, 12, 13, 14, 16, 17,
6460 19, 21, 23, 25, 28, 31, 34, 37, 41, 45,
6461 50, 55, 60, 66, 73, 80, 88, 97, 107, 118,
6462 130, 143, 157, 173, 190, 209, 230, 253, 279, 307,
6463 337, 371, 408, 449, 494, 544, 598, 658, 724, 796,
6464 876, 963, 1060, 1166, 1282, 1411, 1552, 1707, 1878, 2066,
6465 2272, 2499, 2749, 3024, 3327, 3660, 4026, 4428, 4871, 5358,
6466 5894, 6484, 7132, 7845, 8630, 9493, 10442, 11487, 12635, 13899,
6467 15289, 16818, 18500, 20350, 22385, 24623, 27086, 29794, 32767
6468 };
6470 DRWAV_ASSERT(pWav != NULL);
6471 DRWAV_ASSERT(framesToRead > 0);
6473 /* TODO: Lots of room for optimization here. */
6475 while (pWav->readCursorInPCMFrames < pWav->totalPCMFrameCount) {
6476 DRWAV_ASSERT(framesToRead > 0); /* This loop iteration will never get hit with framesToRead == 0 because it's asserted at the top, and we check for 0 inside the loop just below. */
6478 /* If there are no cached samples we need to load a new block. */
6479 if (pWav->ima.cachedFrameCount == 0 && pWav->ima.bytesRemainingInBlock == 0) {
6480 if (pWav->channels == 1) {
6481 /* Mono. */
6482 drwav_uint8 header[4];
6483 if (pWav->onRead(pWav->pUserData, header, sizeof(header)) != sizeof(header)) {
6484 return totalFramesRead;
6485 }
6486 pWav->ima.bytesRemainingInBlock = pWav->fmt.blockAlign - sizeof(header);
6488 if (header[2] >= drwav_countof(stepTable)) {
6489 pWav->onSeek(pWav->pUserData, pWav->ima.bytesRemainingInBlock, DRWAV_SEEK_CUR);
6490 pWav->ima.bytesRemainingInBlock = 0;
6491 return totalFramesRead; /* Invalid data. */
6492 }
6494 pWav->ima.predictor[0] = (drwav_int16)drwav_bytes_to_u16(header + 0);
6495 pWav->ima.stepIndex[0] = drwav_clamp(header[2], 0, (drwav_int32)drwav_countof(stepTable)-1); /* Clamp not necessary because we checked above, but adding here to silence a static analysis warning. */
6496 pWav->ima.cachedFrames[drwav_countof(pWav->ima.cachedFrames) - 1] = pWav->ima.predictor[0];
6497 pWav->ima.cachedFrameCount = 1;
6498 } else {
6499 /* Stereo. */
6500 drwav_uint8 header[8];
6501 if (pWav->onRead(pWav->pUserData, header, sizeof(header)) != sizeof(header)) {
6502 return totalFramesRead;
6503 }
6504 pWav->ima.bytesRemainingInBlock = pWav->fmt.blockAlign - sizeof(header);
6506 if (header[2] >= drwav_countof(stepTable) || header[6] >= drwav_countof(stepTable)) {
6507 pWav->onSeek(pWav->pUserData, pWav->ima.bytesRemainingInBlock, DRWAV_SEEK_CUR);
6508 pWav->ima.bytesRemainingInBlock = 0;
6509 return totalFramesRead; /* Invalid data. */
6510 }
6512 pWav->ima.predictor[0] = drwav_bytes_to_s16(header + 0);
6513 pWav->ima.stepIndex[0] = drwav_clamp(header[2], 0, (drwav_int32)drwav_countof(stepTable)-1); /* Clamp not necessary because we checked above, but adding here to silence a static analysis warning. */
6514 pWav->ima.predictor[1] = drwav_bytes_to_s16(header + 4);
6515 pWav->ima.stepIndex[1] = drwav_clamp(header[6], 0, (drwav_int32)drwav_countof(stepTable)-1); /* Clamp not necessary because we checked above, but adding here to silence a static analysis warning. */
6517 pWav->ima.cachedFrames[drwav_countof(pWav->ima.cachedFrames) - 2] = pWav->ima.predictor[0];
6518 pWav->ima.cachedFrames[drwav_countof(pWav->ima.cachedFrames) - 1] = pWav->ima.predictor[1];
6519 pWav->ima.cachedFrameCount = 1;
6520 }
6521 }
6523 /* Output anything that's cached. */
6524 while (framesToRead > 0 && pWav->ima.cachedFrameCount > 0 && pWav->readCursorInPCMFrames < pWav->totalPCMFrameCount) {
6525 if (pBufferOut != NULL) {
6526 drwav_uint32 iSample;
6527 for (iSample = 0; iSample < pWav->channels; iSample += 1) {
6528 pBufferOut[iSample] = (drwav_int16)pWav->ima.cachedFrames[(drwav_countof(pWav->ima.cachedFrames) - (pWav->ima.cachedFrameCount*pWav->channels)) + iSample];
6529 }
6530 pBufferOut += pWav->channels;
6531 }
6533 framesToRead -= 1;
6534 totalFramesRead += 1;
6535 pWav->readCursorInPCMFrames += 1;
6536 pWav->ima.cachedFrameCount -= 1;
6537 }
6539 if (framesToRead == 0) {
6540 break;
6541 }
6543 /*
6544 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
6545 loop iteration which will trigger the loading of a new block.
6546 */
6547 if (pWav->ima.cachedFrameCount == 0) {
6548 if (pWav->ima.bytesRemainingInBlock == 0) {
6549 continue;
6550 } else {
6551 /*
6552 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
6553 left channel, 4 bytes for the right channel.
6554 */
6555 pWav->ima.cachedFrameCount = 8;
6556 for (iChannel = 0; iChannel < pWav->channels; ++iChannel) {
6557 drwav_uint32 iByte;
6558 drwav_uint8 nibbles[4];
6559 if (pWav->onRead(pWav->pUserData, &nibbles, 4) != 4) {
6560 pWav->ima.cachedFrameCount = 0;
6561 return totalFramesRead;
6562 }
6563 pWav->ima.bytesRemainingInBlock -= 4;
6565 for (iByte = 0; iByte < 4; ++iByte) {
6566 drwav_uint8 nibble0 = ((nibbles[iByte] & 0x0F) >> 0);
6567 drwav_uint8 nibble1 = ((nibbles[iByte] & 0xF0) >> 4);
6569 drwav_int32 step = stepTable[pWav->ima.stepIndex[iChannel]];
6570 drwav_int32 predictor = pWav->ima.predictor[iChannel];
6572 drwav_int32 diff = step >> 3;
6573 if (nibble0 & 1) diff += step >> 2;
6574 if (nibble0 & 2) diff += step >> 1;
6575 if (nibble0 & 4) diff += step;
6576 if (nibble0 & 8) diff = -diff;
6578 predictor = drwav_clamp(predictor + diff, -32768, 32767);
6579 pWav->ima.predictor[iChannel] = predictor;
6580 pWav->ima.stepIndex[iChannel] = drwav_clamp(pWav->ima.stepIndex[iChannel] + indexTable[nibble0], 0, (drwav_int32)drwav_countof(stepTable)-1);
6581 pWav->ima.cachedFrames[(drwav_countof(pWav->ima.cachedFrames) - (pWav->ima.cachedFrameCount*pWav->channels)) + (iByte*2+0)*pWav->channels + iChannel] = predictor;
6584 step = stepTable[pWav->ima.stepIndex[iChannel]];
6585 predictor = pWav->ima.predictor[iChannel];
6587 diff = step >> 3;
6588 if (nibble1 & 1) diff += step >> 2;
6589 if (nibble1 & 2) diff += step >> 1;
6590 if (nibble1 & 4) diff += step;
6591 if (nibble1 & 8) diff = -diff;
6593 predictor = drwav_clamp(predictor + diff, -32768, 32767);
6594 pWav->ima.predictor[iChannel] = predictor;
6595 pWav->ima.stepIndex[iChannel] = drwav_clamp(pWav->ima.stepIndex[iChannel] + indexTable[nibble1], 0, (drwav_int32)drwav_countof(stepTable)-1);
6596 pWav->ima.cachedFrames[(drwav_countof(pWav->ima.cachedFrames) - (pWav->ima.cachedFrameCount*pWav->channels)) + (iByte*2+1)*pWav->channels + iChannel] = predictor;
6597 }
6598 }
6599 }
6600 }
6601 }
6603 return totalFramesRead;
6604}
6607#ifndef DR_WAV_NO_CONVERSION_API
6608static unsigned short g_drwavAlawTable[256] = {
6609 0xEA80, 0xEB80, 0xE880, 0xE980, 0xEE80, 0xEF80, 0xEC80, 0xED80, 0xE280, 0xE380, 0xE080, 0xE180, 0xE680, 0xE780, 0xE480, 0xE580,
6610 0xF540, 0xF5C0, 0xF440, 0xF4C0, 0xF740, 0xF7C0, 0xF640, 0xF6C0, 0xF140, 0xF1C0, 0xF040, 0xF0C0, 0xF340, 0xF3C0, 0xF240, 0xF2C0,
6611 0xAA00, 0xAE00, 0xA200, 0xA600, 0xBA00, 0xBE00, 0xB200, 0xB600, 0x8A00, 0x8E00, 0x8200, 0x8600, 0x9A00, 0x9E00, 0x9200, 0x9600,
6612 0xD500, 0xD700, 0xD100, 0xD300, 0xDD00, 0xDF00, 0xD900, 0xDB00, 0xC500, 0xC700, 0xC100, 0xC300, 0xCD00, 0xCF00, 0xC900, 0xCB00,
6613 0xFEA8, 0xFEB8, 0xFE88, 0xFE98, 0xFEE8, 0xFEF8, 0xFEC8, 0xFED8, 0xFE28, 0xFE38, 0xFE08, 0xFE18, 0xFE68, 0xFE78, 0xFE48, 0xFE58,
6614 0xFFA8, 0xFFB8, 0xFF88, 0xFF98, 0xFFE8, 0xFFF8, 0xFFC8, 0xFFD8, 0xFF28, 0xFF38, 0xFF08, 0xFF18, 0xFF68, 0xFF78, 0xFF48, 0xFF58,
6615 0xFAA0, 0xFAE0, 0xFA20, 0xFA60, 0xFBA0, 0xFBE0, 0xFB20, 0xFB60, 0xF8A0, 0xF8E0, 0xF820, 0xF860, 0xF9A0, 0xF9E0, 0xF920, 0xF960,
6616 0xFD50, 0xFD70, 0xFD10, 0xFD30, 0xFDD0, 0xFDF0, 0xFD90, 0xFDB0, 0xFC50, 0xFC70, 0xFC10, 0xFC30, 0xFCD0, 0xFCF0, 0xFC90, 0xFCB0,
6617 0x1580, 0x1480, 0x1780, 0x1680, 0x1180, 0x1080, 0x1380, 0x1280, 0x1D80, 0x1C80, 0x1F80, 0x1E80, 0x1980, 0x1880, 0x1B80, 0x1A80,
6618 0x0AC0, 0x0A40, 0x0BC0, 0x0B40, 0x08C0, 0x0840, 0x09C0, 0x0940, 0x0EC0, 0x0E40, 0x0FC0, 0x0F40, 0x0CC0, 0x0C40, 0x0DC0, 0x0D40,
6619 0x5600, 0x5200, 0x5E00, 0x5A00, 0x4600, 0x4200, 0x4E00, 0x4A00, 0x7600, 0x7200, 0x7E00, 0x7A00, 0x6600, 0x6200, 0x6E00, 0x6A00,
6620 0x2B00, 0x2900, 0x2F00, 0x2D00, 0x2300, 0x2100, 0x2700, 0x2500, 0x3B00, 0x3900, 0x3F00, 0x3D00, 0x3300, 0x3100, 0x3700, 0x3500,
6621 0x0158, 0x0148, 0x0178, 0x0168, 0x0118, 0x0108, 0x0138, 0x0128, 0x01D8, 0x01C8, 0x01F8, 0x01E8, 0x0198, 0x0188, 0x01B8, 0x01A8,
6622 0x0058, 0x0048, 0x0078, 0x0068, 0x0018, 0x0008, 0x0038, 0x0028, 0x00D8, 0x00C8, 0x00F8, 0x00E8, 0x0098, 0x0088, 0x00B8, 0x00A8,
6623 0x0560, 0x0520, 0x05E0, 0x05A0, 0x0460, 0x0420, 0x04E0, 0x04A0, 0x0760, 0x0720, 0x07E0, 0x07A0, 0x0660, 0x0620, 0x06E0, 0x06A0,
6624 0x02B0, 0x0290, 0x02F0, 0x02D0, 0x0230, 0x0210, 0x0270, 0x0250, 0x03B0, 0x0390, 0x03F0, 0x03D0, 0x0330, 0x0310, 0x0370, 0x0350
6625};
6627static unsigned short g_drwavMulawTable[256] = {
6628 0x8284, 0x8684, 0x8A84, 0x8E84, 0x9284, 0x9684, 0x9A84, 0x9E84, 0xA284, 0xA684, 0xAA84, 0xAE84, 0xB284, 0xB684, 0xBA84, 0xBE84,
6629 0xC184, 0xC384, 0xC584, 0xC784, 0xC984, 0xCB84, 0xCD84, 0xCF84, 0xD184, 0xD384, 0xD584, 0xD784, 0xD984, 0xDB84, 0xDD84, 0xDF84,
6630 0xE104, 0xE204, 0xE304, 0xE404, 0xE504, 0xE604, 0xE704, 0xE804, 0xE904, 0xEA04, 0xEB04, 0xEC04, 0xED04, 0xEE04, 0xEF04, 0xF004,
6631 0xF0C4, 0xF144, 0xF1C4, 0xF244, 0xF2C4, 0xF344, 0xF3C4, 0xF444, 0xF4C4, 0xF544, 0xF5C4, 0xF644, 0xF6C4, 0xF744, 0xF7C4, 0xF844,
6632 0xF8A4, 0xF8E4, 0xF924, 0xF964, 0xF9A4, 0xF9E4, 0xFA24, 0xFA64, 0xFAA4, 0xFAE4, 0xFB24, 0xFB64, 0xFBA4, 0xFBE4, 0xFC24, 0xFC64,
6633 0xFC94, 0xFCB4, 0xFCD4, 0xFCF4, 0xFD14, 0xFD34, 0xFD54, 0xFD74, 0xFD94, 0xFDB4, 0xFDD4, 0xFDF4, 0xFE14, 0xFE34, 0xFE54, 0xFE74,
6634 0xFE8C, 0xFE9C, 0xFEAC, 0xFEBC, 0xFECC, 0xFEDC, 0xFEEC, 0xFEFC, 0xFF0C, 0xFF1C, 0xFF2C, 0xFF3C, 0xFF4C, 0xFF5C, 0xFF6C, 0xFF7C,
6635 0xFF88, 0xFF90, 0xFF98, 0xFFA0, 0xFFA8, 0xFFB0, 0xFFB8, 0xFFC0, 0xFFC8, 0xFFD0, 0xFFD8, 0xFFE0, 0xFFE8, 0xFFF0, 0xFFF8, 0x0000,
6636 0x7D7C, 0x797C, 0x757C, 0x717C, 0x6D7C, 0x697C, 0x657C, 0x617C, 0x5D7C, 0x597C, 0x557C, 0x517C, 0x4D7C, 0x497C, 0x457C, 0x417C,
6637 0x3E7C, 0x3C7C, 0x3A7C, 0x387C, 0x367C, 0x347C, 0x327C, 0x307C, 0x2E7C, 0x2C7C, 0x2A7C, 0x287C, 0x267C, 0x247C, 0x227C, 0x207C,
6638 0x1EFC, 0x1DFC, 0x1CFC, 0x1BFC, 0x1AFC, 0x19FC, 0x18FC, 0x17FC, 0x16FC, 0x15FC, 0x14FC, 0x13FC, 0x12FC, 0x11FC, 0x10FC, 0x0FFC,
6639 0x0F3C, 0x0EBC, 0x0E3C, 0x0DBC, 0x0D3C, 0x0CBC, 0x0C3C, 0x0BBC, 0x0B3C, 0x0ABC, 0x0A3C, 0x09BC, 0x093C, 0x08BC, 0x083C, 0x07BC,
6640 0x075C, 0x071C, 0x06DC, 0x069C, 0x065C, 0x061C, 0x05DC, 0x059C, 0x055C, 0x051C, 0x04DC, 0x049C, 0x045C, 0x041C, 0x03DC, 0x039C,
6641 0x036C, 0x034C, 0x032C, 0x030C, 0x02EC, 0x02CC, 0x02AC, 0x028C, 0x026C, 0x024C, 0x022C, 0x020C, 0x01EC, 0x01CC, 0x01AC, 0x018C,
6642 0x0174, 0x0164, 0x0154, 0x0144, 0x0134, 0x0124, 0x0114, 0x0104, 0x00F4, 0x00E4, 0x00D4, 0x00C4, 0x00B4, 0x00A4, 0x0094, 0x0084,
6643 0x0078, 0x0070, 0x0068, 0x0060, 0x0058, 0x0050, 0x0048, 0x0040, 0x0038, 0x0030, 0x0028, 0x0020, 0x0018, 0x0010, 0x0008, 0x0000
6644};
6646static DRWAV_INLINE drwav_int16 drwav__alaw_to_s16(drwav_uint8 sampleIn)
6647{
6648 return (short)g_drwavAlawTable[sampleIn];
6649}
6651static DRWAV_INLINE drwav_int16 drwav__mulaw_to_s16(drwav_uint8 sampleIn)
6652{
6653 return (short)g_drwavMulawTable[sampleIn];
6654}
6658DRWAV_PRIVATE void drwav__pcm_to_s16(drwav_int16* pOut, const drwav_uint8* pIn, size_t totalSampleCount, unsigned int bytesPerSample)
6659{
6660 size_t i;
6662 /* Special case for 8-bit sample data because it's treated as unsigned. */
6663 if (bytesPerSample == 1) {
6664 drwav_u8_to_s16(pOut, pIn, totalSampleCount);
6665 return;
6666 }
6669 /* Slightly more optimal implementation for common formats. */
6670 if (bytesPerSample == 2) {
6671 for (i = 0; i < totalSampleCount; ++i) {
6672 *pOut++ = ((const drwav_int16*)pIn)[i];
6673 }
6674 return;
6675 }
6676 if (bytesPerSample == 3) {
6677 drwav_s24_to_s16(pOut, pIn, totalSampleCount);
6678 return;
6679 }
6680 if (bytesPerSample == 4) {
6681 drwav_s32_to_s16(pOut, (const drwav_int32*)pIn, totalSampleCount);
6682 return;
6683 }
6686 /* Anything more than 64 bits per sample is not supported. */
6687 if (bytesPerSample > 8) {
6688 DRWAV_ZERO_MEMORY(pOut, totalSampleCount * sizeof(*pOut));
6689 return;
6690 }
6693 /* Generic, slow converter. */
6694 for (i = 0; i < totalSampleCount; ++i) {
6695 drwav_uint64 sample = 0;
6696 unsigned int shift = (8 - bytesPerSample) * 8;
6698 unsigned int j;
6699 for (j = 0; j < bytesPerSample; j += 1) {
6700 DRWAV_ASSERT(j < 8);
6701 sample |= (drwav_uint64)(pIn[j]) << shift;
6702 shift += 8;
6703 }
6705 pIn += j;
6706 *pOut++ = (drwav_int16)((drwav_int64)sample >> 48);
6707 }
6708}
6710DRWAV_PRIVATE void drwav__ieee_to_s16(drwav_int16* pOut, const drwav_uint8* pIn, size_t totalSampleCount, unsigned int bytesPerSample)
6711{
6712 if (bytesPerSample == 4) {
6713 drwav_f32_to_s16(pOut, (const float*)pIn, totalSampleCount);
6714 return;
6715 } else if (bytesPerSample == 8) {
6716 drwav_f64_to_s16(pOut, (const double*)pIn, totalSampleCount);
6717 return;
6718 } else {
6719 /* Only supporting 32- and 64-bit float. Output silence in all other cases. Contributions welcome for 16-bit float. */
6720 DRWAV_ZERO_MEMORY(pOut, totalSampleCount * sizeof(*pOut));
6721 return;
6722 }
6723}
6725DRWAV_PRIVATE drwav_uint64 drwav_read_pcm_frames_s16__pcm(drwav* pWav, drwav_uint64 framesToRead, drwav_int16* pBufferOut)
6726{
6727 drwav_uint64 totalFramesRead;
6728 drwav_uint8 sampleData[4096] = {0};
6729 drwav_uint32 bytesPerFrame;
6730 drwav_uint32 bytesPerSample;
6731 drwav_uint64 samplesRead;
6733 /* Fast path. */
6734 if ((pWav->translatedFormatTag == DR_WAVE_FORMAT_PCM && pWav->bitsPerSample == 16) || pBufferOut == NULL) {
6735 return drwav_read_pcm_frames(pWav, framesToRead, pBufferOut);
6736 }
6738 bytesPerFrame = drwav_get_bytes_per_pcm_frame(pWav);
6739 if (bytesPerFrame == 0) {
6740 return 0;
6741 }
6743 bytesPerSample = bytesPerFrame / pWav->channels;
6744 if (bytesPerSample == 0 || (bytesPerFrame % pWav->channels) != 0) {
6745 return 0; /* Only byte-aligned formats are supported. */
6746 }
6748 totalFramesRead = 0;
6750 while (framesToRead > 0) {
6751 drwav_uint64 framesToReadThisIteration = drwav_min(framesToRead, sizeof(sampleData)/bytesPerFrame);
6752 drwav_uint64 framesRead = drwav_read_pcm_frames(pWav, framesToReadThisIteration, sampleData);
6753 if (framesRead == 0) {
6754 break;
6755 }
6757 DRWAV_ASSERT(framesRead <= framesToReadThisIteration); /* If this fails it means there's a bug in drwav_read_pcm_frames(). */
6759 /* Validation to ensure we don't read too much from out intermediary buffer. This is to protect from invalid files. */
6760 samplesRead = framesRead * pWav->channels;
6761 if ((samplesRead * bytesPerSample) > sizeof(sampleData)) {
6762 DRWAV_ASSERT(DRWAV_FALSE); /* This should never happen with a valid file. */
6763 break;
6764 }
6766 drwav__pcm_to_s16(pBufferOut, sampleData, (size_t)samplesRead, bytesPerSample);
6768 pBufferOut += samplesRead;
6769 framesToRead -= framesRead;
6770 totalFramesRead += framesRead;
6771 }
6773 return totalFramesRead;
6774}
6776DRWAV_PRIVATE drwav_uint64 drwav_read_pcm_frames_s16__ieee(drwav* pWav, drwav_uint64 framesToRead, drwav_int16* pBufferOut)
6777{
6778 drwav_uint64 totalFramesRead;
6779 drwav_uint8 sampleData[4096] = {0};
6780 drwav_uint32 bytesPerFrame;
6781 drwav_uint32 bytesPerSample;
6782 drwav_uint64 samplesRead;
6784 if (pBufferOut == NULL) {
6785 return drwav_read_pcm_frames(pWav, framesToRead, NULL);
6786 }
6788 bytesPerFrame = drwav_get_bytes_per_pcm_frame(pWav);
6789 if (bytesPerFrame == 0) {
6790 return 0;
6791 }
6793 bytesPerSample = bytesPerFrame / pWav->channels;
6794 if (bytesPerSample == 0 || (bytesPerFrame % pWav->channels) != 0) {
6795 return 0; /* Only byte-aligned formats are supported. */
6796 }
6798 totalFramesRead = 0;
6800 while (framesToRead > 0) {
6801 drwav_uint64 framesToReadThisIteration = drwav_min(framesToRead, sizeof(sampleData)/bytesPerFrame);
6802 drwav_uint64 framesRead = drwav_read_pcm_frames(pWav, framesToReadThisIteration, sampleData);
6803 if (framesRead == 0) {
6804 break;
6805 }
6807 DRWAV_ASSERT(framesRead <= framesToReadThisIteration); /* If this fails it means there's a bug in drwav_read_pcm_frames(). */
6809 /* Validation to ensure we don't read too much from out intermediary buffer. This is to protect from invalid files. */
6810 samplesRead = framesRead * pWav->channels;
6811 if ((samplesRead * bytesPerSample) > sizeof(sampleData)) {
6812 DRWAV_ASSERT(DRWAV_FALSE); /* This should never happen with a valid file. */
6813 break;
6814 }
6816 drwav__ieee_to_s16(pBufferOut, sampleData, (size_t)samplesRead, bytesPerSample); /* Safe cast. */
6818 pBufferOut += samplesRead;
6819 framesToRead -= framesRead;
6820 totalFramesRead += framesRead;
6821 }
6823 return totalFramesRead;
6824}
6826DRWAV_PRIVATE drwav_uint64 drwav_read_pcm_frames_s16__alaw(drwav* pWav, drwav_uint64 framesToRead, drwav_int16* pBufferOut)
6827{
6828 drwav_uint64 totalFramesRead;
6829 drwav_uint8 sampleData[4096] = {0};
6830 drwav_uint32 bytesPerFrame;
6831 drwav_uint32 bytesPerSample;
6832 drwav_uint64 samplesRead;
6834 if (pBufferOut == NULL) {
6835 return drwav_read_pcm_frames(pWav, framesToRead, NULL);
6836 }
6838 bytesPerFrame = drwav_get_bytes_per_pcm_frame(pWav);
6839 if (bytesPerFrame == 0) {
6840 return 0;
6841 }
6843 bytesPerSample = bytesPerFrame / pWav->channels;
6844 if (bytesPerSample == 0 || (bytesPerFrame % pWav->channels) != 0) {
6845 return 0; /* Only byte-aligned formats are supported. */
6846 }
6848 totalFramesRead = 0;
6850 while (framesToRead > 0) {
6851 drwav_uint64 framesToReadThisIteration = drwav_min(framesToRead, sizeof(sampleData)/bytesPerFrame);
6852 drwav_uint64 framesRead = drwav_read_pcm_frames(pWav, framesToReadThisIteration, sampleData);
6853 if (framesRead == 0) {
6854 break;
6855 }
6857 DRWAV_ASSERT(framesRead <= framesToReadThisIteration); /* If this fails it means there's a bug in drwav_read_pcm_frames(). */
6859 /* Validation to ensure we don't read too much from out intermediary buffer. This is to protect from invalid files. */
6860 samplesRead = framesRead * pWav->channels;
6861 if ((samplesRead * bytesPerSample) > sizeof(sampleData)) {
6862 DRWAV_ASSERT(DRWAV_FALSE); /* This should never happen with a valid file. */
6863 break;
6864 }
6866 drwav_alaw_to_s16(pBufferOut, sampleData, (size_t)samplesRead);
6868 /*
6869 For some reason libsndfile seems to be returning samples of the opposite sign for a-law, but only
6870 with AIFF files. For WAV files it seems to be the same as dr_wav. This is resulting in dr_wav's
6871 automated tests failing. I'm not sure which is correct, but will assume dr_wav. If we're enforcing
6872 libsndfile compatibility we'll swap the signs here.
6873 */
6874 #ifdef DR_WAV_LIBSNDFILE_COMPAT
6875 {
6876 if (pWav->container == drwav_container_aiff) {
6877 drwav_uint64 iSample;
6878 for (iSample = 0; iSample < samplesRead; iSample += 1) {
6879 pBufferOut[iSample] = -pBufferOut[iSample];
6880 }
6881 }
6882 }
6883 #endif
6885 pBufferOut += samplesRead;
6886 framesToRead -= framesRead;
6887 totalFramesRead += framesRead;
6888 }
6890 return totalFramesRead;
6891}
6893DRWAV_PRIVATE drwav_uint64 drwav_read_pcm_frames_s16__mulaw(drwav* pWav, drwav_uint64 framesToRead, drwav_int16* pBufferOut)
6894{
6895 drwav_uint64 totalFramesRead;
6896 drwav_uint8 sampleData[4096] = {0};
6897 drwav_uint32 bytesPerFrame;
6898 drwav_uint32 bytesPerSample;
6899 drwav_uint64 samplesRead;
6901 if (pBufferOut == NULL) {
6902 return drwav_read_pcm_frames(pWav, framesToRead, NULL);
6903 }
6905 bytesPerFrame = drwav_get_bytes_per_pcm_frame(pWav);
6906 if (bytesPerFrame == 0) {
6907 return 0;
6908 }
6910 bytesPerSample = bytesPerFrame / pWav->channels;
6911 if (bytesPerSample == 0 || (bytesPerFrame % pWav->channels) != 0) {
6912 return 0; /* Only byte-aligned formats are supported. */
6913 }
6915 totalFramesRead = 0;
6917 while (framesToRead > 0) {
6918 drwav_uint64 framesToReadThisIteration = drwav_min(framesToRead, sizeof(sampleData)/bytesPerFrame);
6919 drwav_uint64 framesRead = drwav_read_pcm_frames(pWav, framesToReadThisIteration, sampleData);
6920 if (framesRead == 0) {
6921 break;
6922 }
6924 DRWAV_ASSERT(framesRead <= framesToReadThisIteration); /* If this fails it means there's a bug in drwav_read_pcm_frames(). */
6926 /* Validation to ensure we don't read too much from out intermediary buffer. This is to protect from invalid files. */
6927 samplesRead = framesRead * pWav->channels;
6928 if ((samplesRead * bytesPerSample) > sizeof(sampleData)) {
6929 DRWAV_ASSERT(DRWAV_FALSE); /* This should never happen with a valid file. */
6930 break;
6931 }
6933 drwav_mulaw_to_s16(pBufferOut, sampleData, (size_t)samplesRead);
6935 /*
6936 Just like with alaw, for some reason the signs between libsndfile and dr_wav are opposite. We just need to
6937 swap the sign if we're compiling with libsndfile compatiblity so our automated tests don't fail.
6938 */
6939 #ifdef DR_WAV_LIBSNDFILE_COMPAT
6940 {
6941 if (pWav->container == drwav_container_aiff) {
6942 drwav_uint64 iSample;
6943 for (iSample = 0; iSample < samplesRead; iSample += 1) {
6944 pBufferOut[iSample] = -pBufferOut[iSample];
6945 }
6946 }
6947 }
6948 #endif
6950 pBufferOut += samplesRead;
6951 framesToRead -= framesRead;
6952 totalFramesRead += framesRead;
6953 }
6955 return totalFramesRead;
6956}
6958DRWAV_API drwav_uint64 drwav_read_pcm_frames_s16(drwav* pWav, drwav_uint64 framesToRead, drwav_int16* pBufferOut)
6959{
6960 if (pWav == NULL || framesToRead == 0) {
6961 return 0;
6962 }
6964 if (pBufferOut == NULL) {
6965 return drwav_read_pcm_frames(pWav, framesToRead, NULL);
6966 }
6968 /* Don't try to read more samples than can potentially fit in the output buffer. */
6969 if (framesToRead * pWav->channels * sizeof(drwav_int16) > DRWAV_SIZE_MAX) {
6970 framesToRead = DRWAV_SIZE_MAX / sizeof(drwav_int16) / pWav->channels;
6971 }
6973 if (pWav->translatedFormatTag == DR_WAVE_FORMAT_PCM) {
6974 return drwav_read_pcm_frames_s16__pcm(pWav, framesToRead, pBufferOut);
6975 }
6977 if (pWav->translatedFormatTag == DR_WAVE_FORMAT_IEEE_FLOAT) {
6978 return drwav_read_pcm_frames_s16__ieee(pWav, framesToRead, pBufferOut);
6979 }
6981 if (pWav->translatedFormatTag == DR_WAVE_FORMAT_ALAW) {
6982 return drwav_read_pcm_frames_s16__alaw(pWav, framesToRead, pBufferOut);
6983 }
6985 if (pWav->translatedFormatTag == DR_WAVE_FORMAT_MULAW) {
6986 return drwav_read_pcm_frames_s16__mulaw(pWav, framesToRead, pBufferOut);
6987 }
6989 if (pWav->translatedFormatTag == DR_WAVE_FORMAT_ADPCM) {
6990 return drwav_read_pcm_frames_s16__msadpcm(pWav, framesToRead, pBufferOut);
6991 }
6993 if (pWav->translatedFormatTag == DR_WAVE_FORMAT_DVI_ADPCM) {
6994 return drwav_read_pcm_frames_s16__ima(pWav, framesToRead, pBufferOut);
6995 }
6997 return 0;
6998}
7000DRWAV_API drwav_uint64 drwav_read_pcm_frames_s16le(drwav* pWav, drwav_uint64 framesToRead, drwav_int16* pBufferOut)
7001{
7002 drwav_uint64 framesRead = drwav_read_pcm_frames_s16(pWav, framesToRead, pBufferOut);
7003 if (pBufferOut != NULL && drwav__is_little_endian() == DRWAV_FALSE) {
7004 drwav__bswap_samples_s16(pBufferOut, framesRead*pWav->channels);
7005 }
7007 return framesRead;
7008}
7010DRWAV_API drwav_uint64 drwav_read_pcm_frames_s16be(drwav* pWav, drwav_uint64 framesToRead, drwav_int16* pBufferOut)
7011{
7012 drwav_uint64 framesRead = drwav_read_pcm_frames_s16(pWav, framesToRead, pBufferOut);
7013 if (pBufferOut != NULL && drwav__is_little_endian() == DRWAV_TRUE) {
7014 drwav__bswap_samples_s16(pBufferOut, framesRead*pWav->channels);
7015 }
7017 return framesRead;
7018}
7021DRWAV_API void drwav_u8_to_s16(drwav_int16* pOut, const drwav_uint8* pIn, size_t sampleCount)
7022{
7023 int r;
7024 size_t i;
7025 for (i = 0; i < sampleCount; ++i) {
7026 int x = pIn[i];
7027 r = x << 8;
7028 r = r - 32768;
7029 pOut[i] = (short)r;
7030 }
7031}
7033DRWAV_API void drwav_s24_to_s16(drwav_int16* pOut, const drwav_uint8* pIn, size_t sampleCount)
7034{
7035 int r;
7036 size_t i;
7037 for (i = 0; i < sampleCount; ++i) {
7038 int x = ((int)(((unsigned int)(((const drwav_uint8*)pIn)[i*3+0]) << 8) | ((unsigned int)(((const drwav_uint8*)pIn)[i*3+1]) << 16) | ((unsigned int)(((const drwav_uint8*)pIn)[i*3+2])) << 24)) >> 8;
7039 r = x >> 8;
7040 pOut[i] = (short)r;
7041 }
7042}
7044DRWAV_API void drwav_s32_to_s16(drwav_int16* pOut, const drwav_int32* pIn, size_t sampleCount)
7045{
7046 int r;
7047 size_t i;
7048 for (i = 0; i < sampleCount; ++i) {
7049 int x = pIn[i];
7050 r = x >> 16;
7051 pOut[i] = (short)r;
7052 }
7053}
7055DRWAV_API void drwav_f32_to_s16(drwav_int16* pOut, const float* pIn, size_t sampleCount)
7056{
7057 int r;
7058 size_t i;
7059 for (i = 0; i < sampleCount; ++i) {
7060 float x = pIn[i];
7061 float c;
7062 c = ((x < -1) ? -1 : ((x > 1) ? 1 : x));
7063 c = c + 1;
7064 r = (int)(c * 32767.5f);
7065 r = r - 32768;
7066 pOut[i] = (short)r;
7067 }
7068}
7070DRWAV_API void drwav_f64_to_s16(drwav_int16* pOut, const double* pIn, size_t sampleCount)
7071{
7072 int r;
7073 size_t i;
7074 for (i = 0; i < sampleCount; ++i) {
7075 double x = pIn[i];
7076 double c;
7077 c = ((x < -1) ? -1 : ((x > 1) ? 1 : x));
7078 c = c + 1;
7079 r = (int)(c * 32767.5);
7080 r = r - 32768;
7081 pOut[i] = (short)r;
7082 }
7083}
7085DRWAV_API void drwav_alaw_to_s16(drwav_int16* pOut, const drwav_uint8* pIn, size_t sampleCount)
7086{
7087 size_t i;
7088 for (i = 0; i < sampleCount; ++i) {
7089 pOut[i] = drwav__alaw_to_s16(pIn[i]);
7090 }
7091}
7093DRWAV_API void drwav_mulaw_to_s16(drwav_int16* pOut, const drwav_uint8* pIn, size_t sampleCount)
7094{
7095 size_t i;
7096 for (i = 0; i < sampleCount; ++i) {
7097 pOut[i] = drwav__mulaw_to_s16(pIn[i]);
7098 }
7099}
7102DRWAV_PRIVATE void drwav__pcm_to_f32(float* pOut, const drwav_uint8* pIn, size_t sampleCount, unsigned int bytesPerSample)
7103{
7104 unsigned int i;
7106 /* Special case for 8-bit sample data because it's treated as unsigned. */
7107 if (bytesPerSample == 1) {
7108 drwav_u8_to_f32(pOut, pIn, sampleCount);
7109 return;
7110 }
7112 /* Slightly more optimal implementation for common formats. */
7113 if (bytesPerSample == 2) {
7114 drwav_s16_to_f32(pOut, (const drwav_int16*)pIn, sampleCount);
7115 return;
7116 }
7117 if (bytesPerSample == 3) {
7118 drwav_s24_to_f32(pOut, pIn, sampleCount);
7119 return;
7120 }
7121 if (bytesPerSample == 4) {
7122 drwav_s32_to_f32(pOut, (const drwav_int32*)pIn, sampleCount);
7123 return;
7124 }
7127 /* Anything more than 64 bits per sample is not supported. */
7128 if (bytesPerSample > 8) {
7129 DRWAV_ZERO_MEMORY(pOut, sampleCount * sizeof(*pOut));
7130 return;
7131 }
7134 /* Generic, slow converter. */
7135 for (i = 0; i < sampleCount; ++i) {
7136 drwav_uint64 sample = 0;
7137 unsigned int shift = (8 - bytesPerSample) * 8;
7139 unsigned int j;
7140 for (j = 0; j < bytesPerSample; j += 1) {
7141 DRWAV_ASSERT(j < 8);
7142 sample |= (drwav_uint64)(pIn[j]) << shift;
7143 shift += 8;
7144 }
7146 pIn += j;
7147 *pOut++ = (float)((drwav_int64)sample / 9223372036854775807.0);
7148 }
7149}
7151DRWAV_PRIVATE void drwav__ieee_to_f32(float* pOut, const drwav_uint8* pIn, size_t sampleCount, unsigned int bytesPerSample)
7152{
7153 if (bytesPerSample == 4) {
7154 unsigned int i;
7155 for (i = 0; i < sampleCount; ++i) {
7156 *pOut++ = ((const float*)pIn)[i];
7157 }
7158 return;
7159 } else if (bytesPerSample == 8) {
7160 drwav_f64_to_f32(pOut, (const double*)pIn, sampleCount);
7161 return;
7162 } else {
7163 /* Only supporting 32- and 64-bit float. Output silence in all other cases. Contributions welcome for 16-bit float. */
7164 DRWAV_ZERO_MEMORY(pOut, sampleCount * sizeof(*pOut));
7165 return;
7166 }
7167}
7170DRWAV_PRIVATE drwav_uint64 drwav_read_pcm_frames_f32__pcm(drwav* pWav, drwav_uint64 framesToRead, float* pBufferOut)
7171{
7172 drwav_uint64 totalFramesRead;
7173 drwav_uint8 sampleData[4096] = {0};
7174 drwav_uint32 bytesPerFrame;
7175 drwav_uint32 bytesPerSample;
7176 drwav_uint64 samplesRead;
7178 bytesPerFrame = drwav_get_bytes_per_pcm_frame(pWav);
7179 if (bytesPerFrame == 0) {
7180 return 0;
7181 }
7183 bytesPerSample = bytesPerFrame / pWav->channels;
7184 if (bytesPerSample == 0 || (bytesPerFrame % pWav->channels) != 0) {
7185 return 0; /* Only byte-aligned formats are supported. */
7186 }
7188 totalFramesRead = 0;
7190 while (framesToRead > 0) {
7191 drwav_uint64 framesToReadThisIteration = drwav_min(framesToRead, sizeof(sampleData)/bytesPerFrame);
7192 drwav_uint64 framesRead = drwav_read_pcm_frames(pWav, framesToReadThisIteration, sampleData);
7193 if (framesRead == 0) {
7194 break;
7195 }
7197 DRWAV_ASSERT(framesRead <= framesToReadThisIteration); /* If this fails it means there's a bug in drwav_read_pcm_frames(). */
7199 /* Validation to ensure we don't read too much from out intermediary buffer. This is to protect from invalid files. */
7200 samplesRead = framesRead * pWav->channels;
7201 if ((samplesRead * bytesPerSample) > sizeof(sampleData)) {
7202 DRWAV_ASSERT(DRWAV_FALSE); /* This should never happen with a valid file. */
7203 break;
7204 }
7206 drwav__pcm_to_f32(pBufferOut, sampleData, (size_t)samplesRead, bytesPerSample);
7208 pBufferOut += samplesRead;
7209 framesToRead -= framesRead;
7210 totalFramesRead += framesRead;
7211 }
7213 return totalFramesRead;
7214}
7216DRWAV_PRIVATE drwav_uint64 drwav_read_pcm_frames_f32__msadpcm_ima(drwav* pWav, drwav_uint64 framesToRead, float* pBufferOut)
7217{
7218 /*
7219 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
7220 want to duplicate that code.
7221 */
7222 drwav_uint64 totalFramesRead;
7223 drwav_int16 samples16[2048];
7225 totalFramesRead = 0;
7227 while (framesToRead > 0) {
7228 drwav_uint64 framesToReadThisIteration = drwav_min(framesToRead, drwav_countof(samples16)/pWav->channels);
7229 drwav_uint64 framesRead = drwav_read_pcm_frames_s16(pWav, framesToReadThisIteration, samples16);
7230 if (framesRead == 0) {
7231 break;
7232 }
7234 DRWAV_ASSERT(framesRead <= framesToReadThisIteration); /* If this fails it means there's a bug in drwav_read_pcm_frames(). */
7236 drwav_s16_to_f32(pBufferOut, samples16, (size_t)(framesRead*pWav->channels)); /* <-- Safe cast because we're clamping to 2048. */
7238 pBufferOut += framesRead*pWav->channels;
7239 framesToRead -= framesRead;
7240 totalFramesRead += framesRead;
7241 }
7243 return totalFramesRead;
7244}
7246DRWAV_PRIVATE drwav_uint64 drwav_read_pcm_frames_f32__ieee(drwav* pWav, drwav_uint64 framesToRead, float* pBufferOut)
7247{
7248 drwav_uint64 totalFramesRead;
7249 drwav_uint8 sampleData[4096] = {0};
7250 drwav_uint32 bytesPerFrame;
7251 drwav_uint32 bytesPerSample;
7252 drwav_uint64 samplesRead;
7254 /* Fast path. */
7255 if (pWav->translatedFormatTag == DR_WAVE_FORMAT_IEEE_FLOAT && pWav->bitsPerSample == 32) {
7256 return drwav_read_pcm_frames(pWav, framesToRead, pBufferOut);
7257 }
7259 bytesPerFrame = drwav_get_bytes_per_pcm_frame(pWav);
7260 if (bytesPerFrame == 0) {
7261 return 0;
7262 }
7264 bytesPerSample = bytesPerFrame / pWav->channels;
7265 if (bytesPerSample == 0 || (bytesPerFrame % pWav->channels) != 0) {
7266 return 0; /* Only byte-aligned formats are supported. */
7267 }
7269 totalFramesRead = 0;
7271 while (framesToRead > 0) {
7272 drwav_uint64 framesToReadThisIteration = drwav_min(framesToRead, sizeof(sampleData)/bytesPerFrame);
7273 drwav_uint64 framesRead = drwav_read_pcm_frames(pWav, framesToReadThisIteration, sampleData);
7274 if (framesRead == 0) {
7275 break;
7276 }
7278 DRWAV_ASSERT(framesRead <= framesToReadThisIteration); /* If this fails it means there's a bug in drwav_read_pcm_frames(). */
7280 /* Validation to ensure we don't read too much from out intermediary buffer. This is to protect from invalid files. */
7281 samplesRead = framesRead * pWav->channels;
7282 if ((samplesRead * bytesPerSample) > sizeof(sampleData)) {
7283 DRWAV_ASSERT(DRWAV_FALSE); /* This should never happen with a valid file. */
7284 break;
7285 }
7287 drwav__ieee_to_f32(pBufferOut, sampleData, (size_t)samplesRead, bytesPerSample);
7289 pBufferOut += samplesRead;
7290 framesToRead -= framesRead;
7291 totalFramesRead += framesRead;
7292 }
7294 return totalFramesRead;
7295}
7297DRWAV_PRIVATE drwav_uint64 drwav_read_pcm_frames_f32__alaw(drwav* pWav, drwav_uint64 framesToRead, float* pBufferOut)
7298{
7299 drwav_uint64 totalFramesRead;
7300 drwav_uint8 sampleData[4096] = {0};
7301 drwav_uint32 bytesPerFrame;
7302 drwav_uint32 bytesPerSample;
7303 drwav_uint64 samplesRead;
7305 bytesPerFrame = drwav_get_bytes_per_pcm_frame(pWav);
7306 if (bytesPerFrame == 0) {
7307 return 0;
7308 }
7310 bytesPerSample = bytesPerFrame / pWav->channels;
7311 if (bytesPerSample == 0 || (bytesPerFrame % pWav->channels) != 0) {
7312 return 0; /* Only byte-aligned formats are supported. */
7313 }
7315 totalFramesRead = 0;
7317 while (framesToRead > 0) {
7318 drwav_uint64 framesToReadThisIteration = drwav_min(framesToRead, sizeof(sampleData)/bytesPerFrame);
7319 drwav_uint64 framesRead = drwav_read_pcm_frames(pWav, framesToReadThisIteration, sampleData);
7320 if (framesRead == 0) {
7321 break;
7322 }
7324 DRWAV_ASSERT(framesRead <= framesToReadThisIteration); /* If this fails it means there's a bug in drwav_read_pcm_frames(). */
7326 /* Validation to ensure we don't read too much from out intermediary buffer. This is to protect from invalid files. */
7327 samplesRead = framesRead * pWav->channels;
7328 if ((samplesRead * bytesPerSample) > sizeof(sampleData)) {
7329 DRWAV_ASSERT(DRWAV_FALSE); /* This should never happen with a valid file. */
7330 break;
7331 }
7333 drwav_alaw_to_f32(pBufferOut, sampleData, (size_t)samplesRead);
7335 #ifdef DR_WAV_LIBSNDFILE_COMPAT
7336 {
7337 if (pWav->container == drwav_container_aiff) {
7338 drwav_uint64 iSample;
7339 for (iSample = 0; iSample < samplesRead; iSample += 1) {
7340 pBufferOut[iSample] = -pBufferOut[iSample];
7341 }
7342 }
7343 }
7344 #endif
7346 pBufferOut += samplesRead;
7347 framesToRead -= framesRead;
7348 totalFramesRead += framesRead;
7349 }
7351 return totalFramesRead;
7352}
7354DRWAV_PRIVATE drwav_uint64 drwav_read_pcm_frames_f32__mulaw(drwav* pWav, drwav_uint64 framesToRead, float* pBufferOut)
7355{
7356 drwav_uint64 totalFramesRead;
7357 drwav_uint8 sampleData[4096] = {0};
7358 drwav_uint32 bytesPerFrame;
7359 drwav_uint32 bytesPerSample;
7360 drwav_uint64 samplesRead;
7362 bytesPerFrame = drwav_get_bytes_per_pcm_frame(pWav);
7363 if (bytesPerFrame == 0) {
7364 return 0;
7365 }
7367 bytesPerSample = bytesPerFrame / pWav->channels;
7368 if (bytesPerSample == 0 || (bytesPerFrame % pWav->channels) != 0) {
7369 return 0; /* Only byte-aligned formats are supported. */
7370 }
7372 totalFramesRead = 0;
7374 while (framesToRead > 0) {
7375 drwav_uint64 framesToReadThisIteration = drwav_min(framesToRead, sizeof(sampleData)/bytesPerFrame);
7376 drwav_uint64 framesRead = drwav_read_pcm_frames(pWav, framesToReadThisIteration, sampleData);
7377 if (framesRead == 0) {
7378 break;
7379 }
7381 DRWAV_ASSERT(framesRead <= framesToReadThisIteration); /* If this fails it means there's a bug in drwav_read_pcm_frames(). */
7383 /* Validation to ensure we don't read too much from out intermediary buffer. This is to protect from invalid files. */
7384 samplesRead = framesRead * pWav->channels;
7385 if ((samplesRead * bytesPerSample) > sizeof(sampleData)) {
7386 DRWAV_ASSERT(DRWAV_FALSE); /* This should never happen with a valid file. */
7387 break;
7388 }
7390 drwav_mulaw_to_f32(pBufferOut, sampleData, (size_t)samplesRead);
7392 #ifdef DR_WAV_LIBSNDFILE_COMPAT
7393 {
7394 if (pWav->container == drwav_container_aiff) {
7395 drwav_uint64 iSample;
7396 for (iSample = 0; iSample < samplesRead; iSample += 1) {
7397 pBufferOut[iSample] = -pBufferOut[iSample];
7398 }
7399 }
7400 }
7401 #endif
7403 pBufferOut += samplesRead;
7404 framesToRead -= framesRead;
7405 totalFramesRead += framesRead;
7406 }
7408 return totalFramesRead;
7409}
7411DRWAV_API drwav_uint64 drwav_read_pcm_frames_f32(drwav* pWav, drwav_uint64 framesToRead, float* pBufferOut)
7412{
7413 if (pWav == NULL || framesToRead == 0) {
7414 return 0;
7415 }
7417 if (pBufferOut == NULL) {
7418 return drwav_read_pcm_frames(pWav, framesToRead, NULL);
7419 }
7421 /* Don't try to read more samples than can potentially fit in the output buffer. */
7422 if (framesToRead * pWav->channels * sizeof(float) > DRWAV_SIZE_MAX) {
7423 framesToRead = DRWAV_SIZE_MAX / sizeof(float) / pWav->channels;
7424 }
7426 if (pWav->translatedFormatTag == DR_WAVE_FORMAT_PCM) {
7427 return drwav_read_pcm_frames_f32__pcm(pWav, framesToRead, pBufferOut);
7428 }
7430 if (pWav->translatedFormatTag == DR_WAVE_FORMAT_ADPCM || pWav->translatedFormatTag == DR_WAVE_FORMAT_DVI_ADPCM) {
7431 return drwav_read_pcm_frames_f32__msadpcm_ima(pWav, framesToRead, pBufferOut);
7432 }
7434 if (pWav->translatedFormatTag == DR_WAVE_FORMAT_IEEE_FLOAT) {
7435 return drwav_read_pcm_frames_f32__ieee(pWav, framesToRead, pBufferOut);
7436 }
7438 if (pWav->translatedFormatTag == DR_WAVE_FORMAT_ALAW) {
7439 return drwav_read_pcm_frames_f32__alaw(pWav, framesToRead, pBufferOut);
7440 }
7442 if (pWav->translatedFormatTag == DR_WAVE_FORMAT_MULAW) {
7443 return drwav_read_pcm_frames_f32__mulaw(pWav, framesToRead, pBufferOut);
7444 }
7446 return 0;
7447}
7449DRWAV_API drwav_uint64 drwav_read_pcm_frames_f32le(drwav* pWav, drwav_uint64 framesToRead, float* pBufferOut)
7450{
7451 drwav_uint64 framesRead = drwav_read_pcm_frames_f32(pWav, framesToRead, pBufferOut);
7452 if (pBufferOut != NULL && drwav__is_little_endian() == DRWAV_FALSE) {
7453 drwav__bswap_samples_f32(pBufferOut, framesRead*pWav->channels);
7454 }
7456 return framesRead;
7457}
7459DRWAV_API drwav_uint64 drwav_read_pcm_frames_f32be(drwav* pWav, drwav_uint64 framesToRead, float* pBufferOut)
7460{
7461 drwav_uint64 framesRead = drwav_read_pcm_frames_f32(pWav, framesToRead, pBufferOut);
7462 if (pBufferOut != NULL && drwav__is_little_endian() == DRWAV_TRUE) {
7463 drwav__bswap_samples_f32(pBufferOut, framesRead*pWav->channels);
7464 }
7466 return framesRead;
7467}
7470DRWAV_API void drwav_u8_to_f32(float* pOut, const drwav_uint8* pIn, size_t sampleCount)
7471{
7472 size_t i;
7474 if (pOut == NULL || pIn == NULL) {
7475 return;
7476 }
7478#ifdef DR_WAV_LIBSNDFILE_COMPAT
7479 /*
7480 It appears libsndfile uses slightly different logic for the u8 -> f32 conversion to dr_wav, which in my opinion is incorrect. It appears
7481 libsndfile performs the conversion something like "f32 = (u8 / 256) * 2 - 1", however I think it should be "f32 = (u8 / 255) * 2 - 1" (note
7482 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
7483 correctness testing. This is disabled by default.
7484 */
7485 for (i = 0; i < sampleCount; ++i) {
7486 *pOut++ = (pIn[i] / 256.0f) * 2 - 1;
7487 }
7488#else
7489 for (i = 0; i < sampleCount; ++i) {
7490 float x = pIn[i];
7491 x = x * 0.00784313725490196078f; /* 0..255 to 0..2 */
7492 x = x - 1; /* 0..2 to -1..1 */
7494 *pOut++ = x;
7495 }
7496#endif
7497}
7499DRWAV_API void drwav_s16_to_f32(float* pOut, const drwav_int16* pIn, size_t sampleCount)
7500{
7501 size_t i;
7503 if (pOut == NULL || pIn == NULL) {
7504 return;
7505 }
7507 for (i = 0; i < sampleCount; ++i) {
7508 *pOut++ = pIn[i] * 0.000030517578125f;
7509 }
7510}
7512DRWAV_API void drwav_s24_to_f32(float* pOut, const drwav_uint8* pIn, size_t sampleCount)
7513{
7514 size_t i;
7516 if (pOut == NULL || pIn == NULL) {
7517 return;
7518 }
7520 for (i = 0; i < sampleCount; ++i) {
7521 double x;
7522 drwav_uint32 a = ((drwav_uint32)(pIn[i*3+0]) << 8);
7523 drwav_uint32 b = ((drwav_uint32)(pIn[i*3+1]) << 16);
7524 drwav_uint32 c = ((drwav_uint32)(pIn[i*3+2]) << 24);
7526 x = (double)((drwav_int32)(a | b | c) >> 8);
7527 *pOut++ = (float)(x * 0.00000011920928955078125);
7528 }
7529}
7531DRWAV_API void drwav_s32_to_f32(float* pOut, const drwav_int32* pIn, size_t sampleCount)
7532{
7533 size_t i;
7534 if (pOut == NULL || pIn == NULL) {
7535 return;
7536 }
7538 for (i = 0; i < sampleCount; ++i) {
7539 *pOut++ = (float)(pIn[i] / 2147483648.0);
7540 }
7541}
7543DRWAV_API void drwav_f64_to_f32(float* pOut, const double* pIn, size_t sampleCount)
7544{
7545 size_t i;
7547 if (pOut == NULL || pIn == NULL) {
7548 return;
7549 }
7551 for (i = 0; i < sampleCount; ++i) {
7552 *pOut++ = (float)pIn[i];
7553 }
7554}
7556DRWAV_API void drwav_alaw_to_f32(float* pOut, const drwav_uint8* pIn, size_t sampleCount)
7557{
7558 size_t i;
7560 if (pOut == NULL || pIn == NULL) {
7561 return;
7562 }
7564 for (i = 0; i < sampleCount; ++i) {
7565 *pOut++ = drwav__alaw_to_s16(pIn[i]) / 32768.0f;
7566 }
7567}
7569DRWAV_API void drwav_mulaw_to_f32(float* pOut, const drwav_uint8* pIn, size_t sampleCount)
7570{
7571 size_t i;
7573 if (pOut == NULL || pIn == NULL) {
7574 return;
7575 }
7577 for (i = 0; i < sampleCount; ++i) {
7578 *pOut++ = drwav__mulaw_to_s16(pIn[i]) / 32768.0f;
7579 }
7580}
7584DRWAV_PRIVATE void drwav__pcm_to_s32(drwav_int32* pOut, const drwav_uint8* pIn, size_t totalSampleCount, unsigned int bytesPerSample)
7585{
7586 unsigned int i;
7588 /* Special case for 8-bit sample data because it's treated as unsigned. */
7589 if (bytesPerSample == 1) {
7590 drwav_u8_to_s32(pOut, pIn, totalSampleCount);
7591 return;
7592 }
7594 /* Slightly more optimal implementation for common formats. */
7595 if (bytesPerSample == 2) {
7596 drwav_s16_to_s32(pOut, (const drwav_int16*)pIn, totalSampleCount);
7597 return;
7598 }
7599 if (bytesPerSample == 3) {
7600 drwav_s24_to_s32(pOut, pIn, totalSampleCount);
7601 return;
7602 }
7603 if (bytesPerSample == 4) {
7604 for (i = 0; i < totalSampleCount; ++i) {
7605 *pOut++ = ((const drwav_int32*)pIn)[i];
7606 }
7607 return;
7608 }
7611 /* Anything more than 64 bits per sample is not supported. */
7612 if (bytesPerSample > 8) {
7613 DRWAV_ZERO_MEMORY(pOut, totalSampleCount * sizeof(*pOut));
7614 return;
7615 }
7618 /* Generic, slow converter. */
7619 for (i = 0; i < totalSampleCount; ++i) {
7620 drwav_uint64 sample = 0;
7621 unsigned int shift = (8 - bytesPerSample) * 8;
7623 unsigned int j;
7624 for (j = 0; j < bytesPerSample; j += 1) {
7625 DRWAV_ASSERT(j < 8);
7626 sample |= (drwav_uint64)(pIn[j]) << shift;
7627 shift += 8;
7628 }
7630 pIn += j;
7631 *pOut++ = (drwav_int32)((drwav_int64)sample >> 32);
7632 }
7633}
7635DRWAV_PRIVATE void drwav__ieee_to_s32(drwav_int32* pOut, const drwav_uint8* pIn, size_t totalSampleCount, unsigned int bytesPerSample)
7636{
7637 if (bytesPerSample == 4) {
7638 drwav_f32_to_s32(pOut, (const float*)pIn, totalSampleCount);
7639 return;
7640 } else if (bytesPerSample == 8) {
7641 drwav_f64_to_s32(pOut, (const double*)pIn, totalSampleCount);
7642 return;
7643 } else {
7644 /* Only supporting 32- and 64-bit float. Output silence in all other cases. Contributions welcome for 16-bit float. */
7645 DRWAV_ZERO_MEMORY(pOut, totalSampleCount * sizeof(*pOut));
7646 return;
7647 }
7648}
7651DRWAV_PRIVATE drwav_uint64 drwav_read_pcm_frames_s32__pcm(drwav* pWav, drwav_uint64 framesToRead, drwav_int32* pBufferOut)
7652{
7653 drwav_uint64 totalFramesRead;
7654 drwav_uint8 sampleData[4096] = {0};
7655 drwav_uint32 bytesPerFrame;
7656 drwav_uint32 bytesPerSample;
7657 drwav_uint64 samplesRead;
7659 /* Fast path. */
7660 if (pWav->translatedFormatTag == DR_WAVE_FORMAT_PCM && pWav->bitsPerSample == 32) {
7661 return drwav_read_pcm_frames(pWav, framesToRead, pBufferOut);
7662 }
7664 bytesPerFrame = drwav_get_bytes_per_pcm_frame(pWav);
7665 if (bytesPerFrame == 0) {
7666 return 0;
7667 }
7669 bytesPerSample = bytesPerFrame / pWav->channels;
7670 if (bytesPerSample == 0 || (bytesPerFrame % pWav->channels) != 0) {
7671 return 0; /* Only byte-aligned formats are supported. */
7672 }
7674 totalFramesRead = 0;
7676 while (framesToRead > 0) {
7677 drwav_uint64 framesToReadThisIteration = drwav_min(framesToRead, sizeof(sampleData)/bytesPerFrame);
7678 drwav_uint64 framesRead = drwav_read_pcm_frames(pWav, framesToReadThisIteration, sampleData);
7679 if (framesRead == 0) {
7680 break;
7681 }
7683 DRWAV_ASSERT(framesRead <= framesToReadThisIteration); /* If this fails it means there's a bug in drwav_read_pcm_frames(). */
7685 /* Validation to ensure we don't read too much from out intermediary buffer. This is to protect from invalid files. */
7686 samplesRead = framesRead * pWav->channels;
7687 if ((samplesRead * bytesPerSample) > sizeof(sampleData)) {
7688 DRWAV_ASSERT(DRWAV_FALSE); /* This should never happen with a valid file. */
7689 break;
7690 }
7692 drwav__pcm_to_s32(pBufferOut, sampleData, (size_t)samplesRead, bytesPerSample);
7694 pBufferOut += samplesRead;
7695 framesToRead -= framesRead;
7696 totalFramesRead += framesRead;
7697 }
7699 return totalFramesRead;
7700}
7702DRWAV_PRIVATE drwav_uint64 drwav_read_pcm_frames_s32__msadpcm_ima(drwav* pWav, drwav_uint64 framesToRead, drwav_int32* pBufferOut)
7703{
7704 /*
7705 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
7706 want to duplicate that code.
7707 */
7708 drwav_uint64 totalFramesRead = 0;
7709 drwav_int16 samples16[2048];
7711 while (framesToRead > 0) {
7712 drwav_uint64 framesToReadThisIteration = drwav_min(framesToRead, drwav_countof(samples16)/pWav->channels);
7713 drwav_uint64 framesRead = drwav_read_pcm_frames_s16(pWav, framesToReadThisIteration, samples16);
7714 if (framesRead == 0) {
7715 break;
7716 }
7718 DRWAV_ASSERT(framesRead <= framesToReadThisIteration); /* If this fails it means there's a bug in drwav_read_pcm_frames(). */
7720 drwav_s16_to_s32(pBufferOut, samples16, (size_t)(framesRead*pWav->channels)); /* <-- Safe cast because we're clamping to 2048. */
7722 pBufferOut += framesRead*pWav->channels;
7723 framesToRead -= framesRead;
7724 totalFramesRead += framesRead;
7725 }
7727 return totalFramesRead;
7728}
7730DRWAV_PRIVATE drwav_uint64 drwav_read_pcm_frames_s32__ieee(drwav* pWav, drwav_uint64 framesToRead, drwav_int32* pBufferOut)
7731{
7732 drwav_uint64 totalFramesRead;
7733 drwav_uint8 sampleData[4096] = {0};
7734 drwav_uint32 bytesPerFrame;
7735 drwav_uint32 bytesPerSample;
7736 drwav_uint64 samplesRead;
7738 bytesPerFrame = drwav_get_bytes_per_pcm_frame(pWav);
7739 if (bytesPerFrame == 0) {
7740 return 0;
7741 }
7743 bytesPerSample = bytesPerFrame / pWav->channels;
7744 if (bytesPerSample == 0 || (bytesPerFrame % pWav->channels) != 0) {
7745 return 0; /* Only byte-aligned formats are supported. */
7746 }
7748 totalFramesRead = 0;
7750 while (framesToRead > 0) {
7751 drwav_uint64 framesToReadThisIteration = drwav_min(framesToRead, sizeof(sampleData)/bytesPerFrame);
7752 drwav_uint64 framesRead = drwav_read_pcm_frames(pWav, framesToReadThisIteration, sampleData);
7753 if (framesRead == 0) {
7754 break;
7755 }
7757 DRWAV_ASSERT(framesRead <= framesToReadThisIteration); /* If this fails it means there's a bug in drwav_read_pcm_frames(). */
7759 /* Validation to ensure we don't read too much from out intermediary buffer. This is to protect from invalid files. */
7760 samplesRead = framesRead * pWav->channels;
7761 if ((samplesRead * bytesPerSample) > sizeof(sampleData)) {
7762 DRWAV_ASSERT(DRWAV_FALSE); /* This should never happen with a valid file. */
7763 break;
7764 }
7766 drwav__ieee_to_s32(pBufferOut, sampleData, (size_t)samplesRead, bytesPerSample);
7768 pBufferOut += samplesRead;
7769 framesToRead -= framesRead;
7770 totalFramesRead += framesRead;
7771 }
7773 return totalFramesRead;
7774}
7776DRWAV_PRIVATE drwav_uint64 drwav_read_pcm_frames_s32__alaw(drwav* pWav, drwav_uint64 framesToRead, drwav_int32* pBufferOut)
7777{
7778 drwav_uint64 totalFramesRead;
7779 drwav_uint8 sampleData[4096] = {0};
7780 drwav_uint32 bytesPerFrame;
7781 drwav_uint32 bytesPerSample;
7782 drwav_uint64 samplesRead;
7784 bytesPerFrame = drwav_get_bytes_per_pcm_frame(pWav);
7785 if (bytesPerFrame == 0) {
7786 return 0;
7787 }
7789 bytesPerSample = bytesPerFrame / pWav->channels;
7790 if (bytesPerSample == 0 || (bytesPerFrame % pWav->channels) != 0) {
7791 return 0; /* Only byte-aligned formats are supported. */
7792 }
7794 totalFramesRead = 0;
7796 while (framesToRead > 0) {
7797 drwav_uint64 framesToReadThisIteration = drwav_min(framesToRead, sizeof(sampleData)/bytesPerFrame);
7798 drwav_uint64 framesRead = drwav_read_pcm_frames(pWav, framesToReadThisIteration, sampleData);
7799 if (framesRead == 0) {
7800 break;
7801 }
7803 DRWAV_ASSERT(framesRead <= framesToReadThisIteration); /* If this fails it means there's a bug in drwav_read_pcm_frames(). */
7805 /* Validation to ensure we don't read too much from out intermediary buffer. This is to protect from invalid files. */
7806 samplesRead = framesRead * pWav->channels;
7807 if ((samplesRead * bytesPerSample) > sizeof(sampleData)) {
7808 DRWAV_ASSERT(DRWAV_FALSE); /* This should never happen with a valid file. */
7809 break;
7810 }
7812 drwav_alaw_to_s32(pBufferOut, sampleData, (size_t)samplesRead);
7814 #ifdef DR_WAV_LIBSNDFILE_COMPAT
7815 {
7816 if (pWav->container == drwav_container_aiff) {
7817 drwav_uint64 iSample;
7818 for (iSample = 0; iSample < samplesRead; iSample += 1) {
7819 pBufferOut[iSample] = -pBufferOut[iSample];
7820 }
7821 }
7822 }
7823 #endif
7825 pBufferOut += samplesRead;
7826 framesToRead -= framesRead;
7827 totalFramesRead += framesRead;
7828 }
7830 return totalFramesRead;
7831}
7833DRWAV_PRIVATE drwav_uint64 drwav_read_pcm_frames_s32__mulaw(drwav* pWav, drwav_uint64 framesToRead, drwav_int32* pBufferOut)
7834{
7835 drwav_uint64 totalFramesRead;
7836 drwav_uint8 sampleData[4096] = {0};
7837 drwav_uint32 bytesPerFrame;
7838 drwav_uint32 bytesPerSample;
7839 drwav_uint64 samplesRead;
7841 bytesPerFrame = drwav_get_bytes_per_pcm_frame(pWav);
7842 if (bytesPerFrame == 0) {
7843 return 0;
7844 }
7846 bytesPerSample = bytesPerFrame / pWav->channels;
7847 if (bytesPerSample == 0 || (bytesPerFrame % pWav->channels) != 0) {
7848 return 0; /* Only byte-aligned formats are supported. */
7849 }
7851 totalFramesRead = 0;
7853 while (framesToRead > 0) {
7854 drwav_uint64 framesToReadThisIteration = drwav_min(framesToRead, sizeof(sampleData)/bytesPerFrame);
7855 drwav_uint64 framesRead = drwav_read_pcm_frames(pWav, framesToReadThisIteration, sampleData);
7856 if (framesRead == 0) {
7857 break;
7858 }
7860 DRWAV_ASSERT(framesRead <= framesToReadThisIteration); /* If this fails it means there's a bug in drwav_read_pcm_frames(). */
7862 /* Validation to ensure we don't read too much from out intermediary buffer. This is to protect from invalid files. */
7863 samplesRead = framesRead * pWav->channels;
7864 if ((samplesRead * bytesPerSample) > sizeof(sampleData)) {
7865 DRWAV_ASSERT(DRWAV_FALSE); /* This should never happen with a valid file. */
7866 break;
7867 }
7869 drwav_mulaw_to_s32(pBufferOut, sampleData, (size_t)samplesRead);
7871 #ifdef DR_WAV_LIBSNDFILE_COMPAT
7872 {
7873 if (pWav->container == drwav_container_aiff) {
7874 drwav_uint64 iSample;
7875 for (iSample = 0; iSample < samplesRead; iSample += 1) {
7876 pBufferOut[iSample] = -pBufferOut[iSample];
7877 }
7878 }
7879 }
7880 #endif
7882 pBufferOut += samplesRead;
7883 framesToRead -= framesRead;
7884 totalFramesRead += framesRead;
7885 }
7887 return totalFramesRead;
7888}
7890DRWAV_API drwav_uint64 drwav_read_pcm_frames_s32(drwav* pWav, drwav_uint64 framesToRead, drwav_int32* pBufferOut)
7891{
7892 if (pWav == NULL || framesToRead == 0) {
7893 return 0;
7894 }
7896 if (pBufferOut == NULL) {
7897 return drwav_read_pcm_frames(pWav, framesToRead, NULL);
7898 }
7900 /* Don't try to read more samples than can potentially fit in the output buffer. */
7901 if (framesToRead * pWav->channels * sizeof(drwav_int32) > DRWAV_SIZE_MAX) {
7902 framesToRead = DRWAV_SIZE_MAX / sizeof(drwav_int32) / pWav->channels;
7903 }
7905 if (pWav->translatedFormatTag == DR_WAVE_FORMAT_PCM) {
7906 return drwav_read_pcm_frames_s32__pcm(pWav, framesToRead, pBufferOut);
7907 }
7909 if (pWav->translatedFormatTag == DR_WAVE_FORMAT_ADPCM || pWav->translatedFormatTag == DR_WAVE_FORMAT_DVI_ADPCM) {
7910 return drwav_read_pcm_frames_s32__msadpcm_ima(pWav, framesToRead, pBufferOut);
7911 }
7913 if (pWav->translatedFormatTag == DR_WAVE_FORMAT_IEEE_FLOAT) {
7914 return drwav_read_pcm_frames_s32__ieee(pWav, framesToRead, pBufferOut);
7915 }
7917 if (pWav->translatedFormatTag == DR_WAVE_FORMAT_ALAW) {
7918 return drwav_read_pcm_frames_s32__alaw(pWav, framesToRead, pBufferOut);
7919 }
7921 if (pWav->translatedFormatTag == DR_WAVE_FORMAT_MULAW) {
7922 return drwav_read_pcm_frames_s32__mulaw(pWav, framesToRead, pBufferOut);
7923 }
7925 return 0;
7926}
7928DRWAV_API drwav_uint64 drwav_read_pcm_frames_s32le(drwav* pWav, drwav_uint64 framesToRead, drwav_int32* pBufferOut)
7929{
7930 drwav_uint64 framesRead = drwav_read_pcm_frames_s32(pWav, framesToRead, pBufferOut);
7931 if (pBufferOut != NULL && drwav__is_little_endian() == DRWAV_FALSE) {
7932 drwav__bswap_samples_s32(pBufferOut, framesRead*pWav->channels);
7933 }
7935 return framesRead;
7936}
7938DRWAV_API drwav_uint64 drwav_read_pcm_frames_s32be(drwav* pWav, drwav_uint64 framesToRead, drwav_int32* pBufferOut)
7939{
7940 drwav_uint64 framesRead = drwav_read_pcm_frames_s32(pWav, framesToRead, pBufferOut);
7941 if (pBufferOut != NULL && drwav__is_little_endian() == DRWAV_TRUE) {
7942 drwav__bswap_samples_s32(pBufferOut, framesRead*pWav->channels);
7943 }
7945 return framesRead;
7946}
7949DRWAV_API void drwav_u8_to_s32(drwav_int32* pOut, const drwav_uint8* pIn, size_t sampleCount)
7950{
7951 size_t i;
7953 if (pOut == NULL || pIn == NULL) {
7954 return;
7955 }
7957 for (i = 0; i < sampleCount; ++i) {
7958 *pOut++ = ((int)pIn[i] - 128) << 24;
7959 }
7960}
7962DRWAV_API void drwav_s16_to_s32(drwav_int32* pOut, const drwav_int16* pIn, size_t sampleCount)
7963{
7964 size_t i;
7966 if (pOut == NULL || pIn == NULL) {
7967 return;
7968 }
7970 for (i = 0; i < sampleCount; ++i) {
7971 *pOut++ = pIn[i] << 16;
7972 }
7973}
7975DRWAV_API void drwav_s24_to_s32(drwav_int32* pOut, const drwav_uint8* pIn, size_t sampleCount)
7976{
7977 size_t i;
7979 if (pOut == NULL || pIn == NULL) {
7980 return;
7981 }
7983 for (i = 0; i < sampleCount; ++i) {
7984 unsigned int s0 = pIn[i*3 + 0];
7985 unsigned int s1 = pIn[i*3 + 1];
7986 unsigned int s2 = pIn[i*3 + 2];
7988 drwav_int32 sample32 = (drwav_int32)((s0 << 8) | (s1 << 16) | (s2 << 24));
7989 *pOut++ = sample32;
7990 }
7991}
7993DRWAV_API void drwav_f32_to_s32(drwav_int32* pOut, const float* pIn, size_t sampleCount)
7994{
7995 size_t i;
7997 if (pOut == NULL || pIn == NULL) {
7998 return;
7999 }
8001 for (i = 0; i < sampleCount; ++i) {
8002 *pOut++ = (drwav_int32)(2147483648.0f * pIn[i]);
8003 }
8004}
8006DRWAV_API void drwav_f64_to_s32(drwav_int32* pOut, const double* pIn, size_t sampleCount)
8007{
8008 size_t i;
8010 if (pOut == NULL || pIn == NULL) {
8011 return;
8012 }
8014 for (i = 0; i < sampleCount; ++i) {
8015 *pOut++ = (drwav_int32)(2147483648.0 * pIn[i]);
8016 }
8017}
8019DRWAV_API void drwav_alaw_to_s32(drwav_int32* pOut, const drwav_uint8* pIn, size_t sampleCount)
8020{
8021 size_t i;
8023 if (pOut == NULL || pIn == NULL) {
8024 return;
8025 }
8027 for (i = 0; i < sampleCount; ++i) {
8028 *pOut++ = ((drwav_int32)drwav__alaw_to_s16(pIn[i])) << 16;
8029 }
8030}
8032DRWAV_API void drwav_mulaw_to_s32(drwav_int32* pOut, const drwav_uint8* pIn, size_t sampleCount)
8033{
8034 size_t i;
8036 if (pOut == NULL || pIn == NULL) {
8037 return;
8038 }
8040 for (i= 0; i < sampleCount; ++i) {
8041 *pOut++ = ((drwav_int32)drwav__mulaw_to_s16(pIn[i])) << 16;
8042 }
8043}
8047DRWAV_PRIVATE drwav_int16* drwav__read_pcm_frames_and_close_s16(drwav* pWav, unsigned int* channels, unsigned int* sampleRate, drwav_uint64* totalFrameCount)
8048{
8049 drwav_uint64 sampleDataSize;
8050 drwav_int16* pSampleData;
8051 drwav_uint64 framesRead;
8053 DRWAV_ASSERT(pWav != NULL);
8055 sampleDataSize = pWav->totalPCMFrameCount * pWav->channels * sizeof(drwav_int16);
8056 if (sampleDataSize > DRWAV_SIZE_MAX) {
8057 drwav_uninit(pWav);
8058 return NULL; /* File's too big. */
8059 }
8061 pSampleData = (drwav_int16*)drwav__malloc_from_callbacks((size_t)sampleDataSize, &pWav->allocationCallbacks); /* <-- Safe cast due to the check above. */
8062 if (pSampleData == NULL) {
8063 drwav_uninit(pWav);
8064 return NULL; /* Failed to allocate memory. */
8065 }
8067 framesRead = drwav_read_pcm_frames_s16(pWav, (size_t)pWav->totalPCMFrameCount, pSampleData);
8068 if (framesRead != pWav->totalPCMFrameCount) {
8069 drwav__free_from_callbacks(pSampleData, &pWav->allocationCallbacks);
8070 drwav_uninit(pWav);
8071 return NULL; /* There was an error reading the samples. */
8072 }
8074 drwav_uninit(pWav);
8076 if (sampleRate) {
8077 *sampleRate = pWav->sampleRate;
8078 }
8079 if (channels) {
8080 *channels = pWav->channels;
8081 }
8082 if (totalFrameCount) {
8083 *totalFrameCount = pWav->totalPCMFrameCount;
8084 }
8086 return pSampleData;
8087}
8089DRWAV_PRIVATE float* drwav__read_pcm_frames_and_close_f32(drwav* pWav, unsigned int* channels, unsigned int* sampleRate, drwav_uint64* totalFrameCount)
8090{
8091 drwav_uint64 sampleDataSize;
8092 float* pSampleData;
8093 drwav_uint64 framesRead;
8095 DRWAV_ASSERT(pWav != NULL);
8097 sampleDataSize = pWav->totalPCMFrameCount * pWav->channels * sizeof(float);
8098 if (sampleDataSize > DRWAV_SIZE_MAX) {
8099 drwav_uninit(pWav);
8100 return NULL; /* File's too big. */
8101 }
8103 pSampleData = (float*)drwav__malloc_from_callbacks((size_t)sampleDataSize, &pWav->allocationCallbacks); /* <-- Safe cast due to the check above. */
8104 if (pSampleData == NULL) {
8105 drwav_uninit(pWav);
8106 return NULL; /* Failed to allocate memory. */
8107 }
8109 framesRead = drwav_read_pcm_frames_f32(pWav, (size_t)pWav->totalPCMFrameCount, pSampleData);
8110 if (framesRead != pWav->totalPCMFrameCount) {
8111 drwav__free_from_callbacks(pSampleData, &pWav->allocationCallbacks);
8112 drwav_uninit(pWav);
8113 return NULL; /* There was an error reading the samples. */
8114 }
8116 drwav_uninit(pWav);
8118 if (sampleRate) {
8119 *sampleRate = pWav->sampleRate;
8120 }
8121 if (channels) {
8122 *channels = pWav->channels;
8123 }
8124 if (totalFrameCount) {
8125 *totalFrameCount = pWav->totalPCMFrameCount;
8126 }
8128 return pSampleData;
8129}
8131DRWAV_PRIVATE drwav_int32* drwav__read_pcm_frames_and_close_s32(drwav* pWav, unsigned int* channels, unsigned int* sampleRate, drwav_uint64* totalFrameCount)
8132{
8133 drwav_uint64 sampleDataSize;
8134 drwav_int32* pSampleData;
8135 drwav_uint64 framesRead;
8137 DRWAV_ASSERT(pWav != NULL);
8139 sampleDataSize = pWav->totalPCMFrameCount * pWav->channels * sizeof(drwav_int32);
8140 if (sampleDataSize > DRWAV_SIZE_MAX) {
8141 drwav_uninit(pWav);
8142 return NULL; /* File's too big. */
8143 }
8145 pSampleData = (drwav_int32*)drwav__malloc_from_callbacks((size_t)sampleDataSize, &pWav->allocationCallbacks); /* <-- Safe cast due to the check above. */
8146 if (pSampleData == NULL) {
8147 drwav_uninit(pWav);
8148 return NULL; /* Failed to allocate memory. */
8149 }
8151 framesRead = drwav_read_pcm_frames_s32(pWav, (size_t)pWav->totalPCMFrameCount, pSampleData);
8152 if (framesRead != pWav->totalPCMFrameCount) {
8153 drwav__free_from_callbacks(pSampleData, &pWav->allocationCallbacks);
8154 drwav_uninit(pWav);
8155 return NULL; /* There was an error reading the samples. */
8156 }
8158 drwav_uninit(pWav);
8160 if (sampleRate) {
8161 *sampleRate = pWav->sampleRate;
8162 }
8163 if (channels) {
8164 *channels = pWav->channels;
8165 }
8166 if (totalFrameCount) {
8167 *totalFrameCount = pWav->totalPCMFrameCount;
8168 }
8170 return pSampleData;
8171}
8175DRWAV_API drwav_int16* drwav_open_and_read_pcm_frames_s16(drwav_read_proc onRead, drwav_seek_proc onSeek, drwav_tell_proc onTell, void* pUserData, unsigned int* channelsOut, unsigned int* sampleRateOut, drwav_uint64* totalFrameCountOut, const drwav_allocation_callbacks* pAllocationCallbacks)
8176{
8177 drwav wav;
8179 if (channelsOut) {
8180 *channelsOut = 0;
8181 }
8182 if (sampleRateOut) {
8183 *sampleRateOut = 0;
8184 }
8185 if (totalFrameCountOut) {
8186 *totalFrameCountOut = 0;
8187 }
8189 if (!drwav_init(&wav, onRead, onSeek, onTell, pUserData, pAllocationCallbacks)) {
8190 return NULL;
8191 }
8193 return drwav__read_pcm_frames_and_close_s16(&wav, channelsOut, sampleRateOut, totalFrameCountOut);
8194}
8196DRWAV_API float* drwav_open_and_read_pcm_frames_f32(drwav_read_proc onRead, drwav_seek_proc onSeek, drwav_tell_proc onTell, void* pUserData, unsigned int* channelsOut, unsigned int* sampleRateOut, drwav_uint64* totalFrameCountOut, const drwav_allocation_callbacks* pAllocationCallbacks)
8197{
8198 drwav wav;
8200 if (channelsOut) {
8201 *channelsOut = 0;
8202 }
8203 if (sampleRateOut) {
8204 *sampleRateOut = 0;
8205 }
8206 if (totalFrameCountOut) {
8207 *totalFrameCountOut = 0;
8208 }
8210 if (!drwav_init(&wav, onRead, onSeek, onTell, pUserData, pAllocationCallbacks)) {
8211 return NULL;
8212 }
8214 return drwav__read_pcm_frames_and_close_f32(&wav, channelsOut, sampleRateOut, totalFrameCountOut);
8215}
8217DRWAV_API drwav_int32* drwav_open_and_read_pcm_frames_s32(drwav_read_proc onRead, drwav_seek_proc onSeek, drwav_tell_proc onTell, void* pUserData, unsigned int* channelsOut, unsigned int* sampleRateOut, drwav_uint64* totalFrameCountOut, const drwav_allocation_callbacks* pAllocationCallbacks)
8218{
8219 drwav wav;
8221 if (channelsOut) {
8222 *channelsOut = 0;
8223 }
8224 if (sampleRateOut) {
8225 *sampleRateOut = 0;
8226 }
8227 if (totalFrameCountOut) {
8228 *totalFrameCountOut = 0;
8229 }
8231 if (!drwav_init(&wav, onRead, onSeek, onTell, pUserData, pAllocationCallbacks)) {
8232 return NULL;
8233 }
8235 return drwav__read_pcm_frames_and_close_s32(&wav, channelsOut, sampleRateOut, totalFrameCountOut);
8236}
8238#ifndef DR_WAV_NO_STDIO
8239DRWAV_API drwav_int16* drwav_open_file_and_read_pcm_frames_s16(const char* filename, unsigned int* channelsOut, unsigned int* sampleRateOut, drwav_uint64* totalFrameCountOut, const drwav_allocation_callbacks* pAllocationCallbacks)
8240{
8241 drwav wav;
8243 if (channelsOut) {
8244 *channelsOut = 0;
8245 }
8246 if (sampleRateOut) {
8247 *sampleRateOut = 0;
8248 }
8249 if (totalFrameCountOut) {
8250 *totalFrameCountOut = 0;
8251 }
8253 if (!drwav_init_file(&wav, filename, pAllocationCallbacks)) {
8254 return NULL;
8255 }
8257 return drwav__read_pcm_frames_and_close_s16(&wav, channelsOut, sampleRateOut, totalFrameCountOut);
8258}
8260DRWAV_API float* drwav_open_file_and_read_pcm_frames_f32(const char* filename, unsigned int* channelsOut, unsigned int* sampleRateOut, drwav_uint64* totalFrameCountOut, const drwav_allocation_callbacks* pAllocationCallbacks)
8261{
8262 drwav wav;
8264 if (channelsOut) {
8265 *channelsOut = 0;
8266 }
8267 if (sampleRateOut) {
8268 *sampleRateOut = 0;
8269 }
8270 if (totalFrameCountOut) {
8271 *totalFrameCountOut = 0;
8272 }
8274 if (!drwav_init_file(&wav, filename, pAllocationCallbacks)) {
8275 return NULL;
8276 }
8278 return drwav__read_pcm_frames_and_close_f32(&wav, channelsOut, sampleRateOut, totalFrameCountOut);
8279}
8281DRWAV_API drwav_int32* drwav_open_file_and_read_pcm_frames_s32(const char* filename, unsigned int* channelsOut, unsigned int* sampleRateOut, drwav_uint64* totalFrameCountOut, const drwav_allocation_callbacks* pAllocationCallbacks)
8282{
8283 drwav wav;
8285 if (channelsOut) {
8286 *channelsOut = 0;
8287 }
8288 if (sampleRateOut) {
8289 *sampleRateOut = 0;
8290 }
8291 if (totalFrameCountOut) {
8292 *totalFrameCountOut = 0;
8293 }
8295 if (!drwav_init_file(&wav, filename, pAllocationCallbacks)) {
8296 return NULL;
8297 }
8299 return drwav__read_pcm_frames_and_close_s32(&wav, channelsOut, sampleRateOut, totalFrameCountOut);
8300}
8303#ifndef DR_WAV_NO_WCHAR
8304DRWAV_API drwav_int16* drwav_open_file_and_read_pcm_frames_s16_w(const wchar_t* filename, unsigned int* channelsOut, unsigned int* sampleRateOut, drwav_uint64* totalFrameCountOut, const drwav_allocation_callbacks* pAllocationCallbacks)
8305{
8306 drwav wav;
8308 if (sampleRateOut) {
8309 *sampleRateOut = 0;
8310 }
8311 if (channelsOut) {
8312 *channelsOut = 0;
8313 }
8314 if (totalFrameCountOut) {
8315 *totalFrameCountOut = 0;
8316 }
8318 if (!drwav_init_file_w(&wav, filename, pAllocationCallbacks)) {
8319 return NULL;
8320 }
8322 return drwav__read_pcm_frames_and_close_s16(&wav, channelsOut, sampleRateOut, totalFrameCountOut);
8323}
8325DRWAV_API float* drwav_open_file_and_read_pcm_frames_f32_w(const wchar_t* filename, unsigned int* channelsOut, unsigned int* sampleRateOut, drwav_uint64* totalFrameCountOut, const drwav_allocation_callbacks* pAllocationCallbacks)
8326{
8327 drwav wav;
8329 if (sampleRateOut) {
8330 *sampleRateOut = 0;
8331 }
8332 if (channelsOut) {
8333 *channelsOut = 0;
8334 }
8335 if (totalFrameCountOut) {
8336 *totalFrameCountOut = 0;
8337 }
8339 if (!drwav_init_file_w(&wav, filename, pAllocationCallbacks)) {
8340 return NULL;
8341 }
8343 return drwav__read_pcm_frames_and_close_f32(&wav, channelsOut, sampleRateOut, totalFrameCountOut);
8344}
8346DRWAV_API drwav_int32* drwav_open_file_and_read_pcm_frames_s32_w(const wchar_t* filename, unsigned int* channelsOut, unsigned int* sampleRateOut, drwav_uint64* totalFrameCountOut, const drwav_allocation_callbacks* pAllocationCallbacks)
8347{
8348 drwav wav;
8350 if (sampleRateOut) {
8351 *sampleRateOut = 0;
8352 }
8353 if (channelsOut) {
8354 *channelsOut = 0;
8355 }
8356 if (totalFrameCountOut) {
8357 *totalFrameCountOut = 0;
8358 }
8360 if (!drwav_init_file_w(&wav, filename, pAllocationCallbacks)) {
8361 return NULL;
8362 }
8364 return drwav__read_pcm_frames_and_close_s32(&wav, channelsOut, sampleRateOut, totalFrameCountOut);
8365}
8366#endif /* DR_WAV_NO_WCHAR */
8367#endif /* DR_WAV_NO_STDIO */
8369DRWAV_API drwav_int16* drwav_open_memory_and_read_pcm_frames_s16(const void* data, size_t dataSize, unsigned int* channelsOut, unsigned int* sampleRateOut, drwav_uint64* totalFrameCountOut, const drwav_allocation_callbacks* pAllocationCallbacks)
8370{
8371 drwav wav;
8373 if (channelsOut) {
8374 *channelsOut = 0;
8375 }
8376 if (sampleRateOut) {
8377 *sampleRateOut = 0;
8378 }
8379 if (totalFrameCountOut) {
8380 *totalFrameCountOut = 0;
8381 }
8383 if (!drwav_init_memory(&wav, data, dataSize, pAllocationCallbacks)) {
8384 return NULL;
8385 }
8387 return drwav__read_pcm_frames_and_close_s16(&wav, channelsOut, sampleRateOut, totalFrameCountOut);
8388}
8390DRWAV_API float* drwav_open_memory_and_read_pcm_frames_f32(const void* data, size_t dataSize, unsigned int* channelsOut, unsigned int* sampleRateOut, drwav_uint64* totalFrameCountOut, const drwav_allocation_callbacks* pAllocationCallbacks)
8391{
8392 drwav wav;
8394 if (channelsOut) {
8395 *channelsOut = 0;
8396 }
8397 if (sampleRateOut) {
8398 *sampleRateOut = 0;
8399 }
8400 if (totalFrameCountOut) {
8401 *totalFrameCountOut = 0;
8402 }
8404 if (!drwav_init_memory(&wav, data, dataSize, pAllocationCallbacks)) {
8405 return NULL;
8406 }
8408 return drwav__read_pcm_frames_and_close_f32(&wav, channelsOut, sampleRateOut, totalFrameCountOut);
8409}
8411DRWAV_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, const drwav_allocation_callbacks* pAllocationCallbacks)
8412{
8413 drwav wav;
8415 if (channelsOut) {
8416 *channelsOut = 0;
8417 }
8418 if (sampleRateOut) {
8419 *sampleRateOut = 0;
8420 }
8421 if (totalFrameCountOut) {
8422 *totalFrameCountOut = 0;
8423 }
8425 if (!drwav_init_memory(&wav, data, dataSize, pAllocationCallbacks)) {
8426 return NULL;
8427 }
8429 return drwav__read_pcm_frames_and_close_s32(&wav, channelsOut, sampleRateOut, totalFrameCountOut);
8430}
8431#endif /* DR_WAV_NO_CONVERSION_API */
8434DRWAV_API void drwav_free(void* p, const drwav_allocation_callbacks* pAllocationCallbacks)
8435{
8436 if (pAllocationCallbacks != NULL) {
8437 drwav__free_from_callbacks(p, pAllocationCallbacks);
8438 } else {
8439 drwav__free_default(p, NULL);
8440 }
8441}
8443DRWAV_API drwav_uint16 drwav_bytes_to_u16(const drwav_uint8* data)
8444{
8445 return ((drwav_uint16)data[0] << 0) | ((drwav_uint16)data[1] << 8);
8446}
8448DRWAV_API drwav_int16 drwav_bytes_to_s16(const drwav_uint8* data)
8449{
8450 return (drwav_int16)drwav_bytes_to_u16(data);
8451}
8453DRWAV_API drwav_uint32 drwav_bytes_to_u32(const drwav_uint8* data)
8454{
8455 return drwav_bytes_to_u32_le(data);
8456}
8458DRWAV_API float drwav_bytes_to_f32(const drwav_uint8* data)
8459{
8460 union {
8461 drwav_uint32 u32;
8462 float f32;
8463 } value;
8465 value.u32 = drwav_bytes_to_u32(data);
8466 return value.f32;
8467}
8469DRWAV_API drwav_int32 drwav_bytes_to_s32(const drwav_uint8* data)
8470{
8471 return (drwav_int32)drwav_bytes_to_u32(data);
8472}
8474DRWAV_API drwav_uint64 drwav_bytes_to_u64(const drwav_uint8* data)
8475{
8476 return
8477 ((drwav_uint64)data[0] << 0) | ((drwav_uint64)data[1] << 8) | ((drwav_uint64)data[2] << 16) | ((drwav_uint64)data[3] << 24) |
8478 ((drwav_uint64)data[4] << 32) | ((drwav_uint64)data[5] << 40) | ((drwav_uint64)data[6] << 48) | ((drwav_uint64)data[7] << 56);
8479}
8481DRWAV_API drwav_int64 drwav_bytes_to_s64(const drwav_uint8* data)
8482{
8483 return (drwav_int64)drwav_bytes_to_u64(data);
8484}
8487DRWAV_API drwav_bool32 drwav_guid_equal(const drwav_uint8 a[16], const drwav_uint8 b[16])
8488{
8489 int i;
8490 for (i = 0; i < 16; i += 1) {
8491 if (a[i] != b[i]) {
8492 return DRWAV_FALSE;
8493 }
8494 }
8496 return DRWAV_TRUE;
8497}
8499DRWAV_API drwav_bool32 drwav_fourcc_equal(const drwav_uint8* a, const char* b)
8500{
8501 return
8502 a[0] == b[0] &&
8503 a[1] == b[1] &&
8504 a[2] == b[2] &&
8505 a[3] == b[3];
8506}
8508#ifdef __MRC__
8509/* Undo the pragma at the beginning of this file. */
8510#pragma options opt reset
8511#endif
8513#endif /* dr_wav_c */
8514#endif /* DR_WAV_IMPLEMENTATION */
8516/*
8517REVISION HISTORY
8518================
8519v0.14.0 - TBD
8520 - API CHANGE: Seek origin enums have been renamed to the following:
8521 - drwav_seek_origin_start -> DRWAV_SEEK_SET
8522 - drwav_seek_origin_current -> DRWAV_SEEK_CUR
8523 - DRWAV_SEEK_END (new)
8524 - API CHANGE: A new seek origin has been added to allow seeking from the end of the file. If you implement your own `onSeek` callback, you must now handle `DRWAV_SEEK_END`. If you only use `*_init_file()` or `*_init_memory()`, you need not change anything.
8525 - API CHANGE: An `onTell` callback has been added to the following functions:
8526 - drwav_init()
8527 - drwav_init_ex()
8528 - drwav_init_with_metadata()
8529 - drwav_open_and_read_pcm_frames_s16()
8530 - drwav_open_and_read_pcm_frames_f32()
8531 - drwav_open_and_read_pcm_frames_s32()
8532 - API CHANGE: The `firstSampleByteOffset`, `lastSampleByteOffset` and `sampleByteOffset` members of `drwav_cue_point` have been renamed to `firstSampleOffset`, `lastSampleOffset` and `sampleOffset`, respectively.
8533 - Fix a static analysis warning.
8534 - Fix compilation for AIX OS.
8536v0.13.17 - 2024-12-17
8537 - Fix a possible crash when reading from MS-ADPCM encoded files.
8538 - Improve detection of ARM64EC
8540v0.13.16 - 2024-02-27
8541 - Fix a Wdouble-promotion warning.
8543v0.13.15 - 2024-01-23
8544 - Relax some unnecessary validation that prevented some files from loading.
8546v0.13.14 - 2023-12-02
8547 - Fix a warning about an unused variable.
8549v0.13.13 - 2023-11-02
8550 - Fix a warning when compiling with Clang.
8552v0.13.12 - 2023-08-07
8553 - Fix a possible crash in drwav_read_pcm_frames().
8555v0.13.11 - 2023-07-07
8556 - AIFF compatibility improvements.
8558v0.13.10 - 2023-05-29
8559 - Fix a bug where drwav_init_with_metadata() does not decode any frames after initializtion.
8561v0.13.9 - 2023-05-22
8562 - Add support for AIFF decoding (writing and metadata not supported).
8563 - Add support for RIFX decoding (writing and metadata not supported).
8564 - Fix a bug where metadata is not processed if it's located before the "fmt " chunk.
8565 - Add a workaround for a type of malformed WAV file where the size of the "RIFF" and "data" chunks
8566 are incorrectly set to 0xFFFFFFFF.
8568v0.13.8 - 2023-03-25
8569 - Fix a possible null pointer dereference.
8570 - Fix a crash when loading files with badly formed metadata.
8572v0.13.7 - 2022-09-17
8573 - Fix compilation with DJGPP.
8574 - Add support for disabling wchar_t with DR_WAV_NO_WCHAR.
8576v0.13.6 - 2022-04-10
8577 - Fix compilation error on older versions of GCC.
8578 - Remove some dependencies on the standard library.
8580v0.13.5 - 2022-01-26
8581 - Fix an error when seeking to the end of the file.
8583v0.13.4 - 2021-12-08
8584 - Fix some static analysis warnings.
8586v0.13.3 - 2021-11-24
8587 - Fix an incorrect assertion when trying to endian swap 1-byte sample formats. This is now a no-op
8588 rather than a failed assertion.
8589 - Fix a bug with parsing of the bext chunk.
8590 - Fix some static analysis warnings.
8592v0.13.2 - 2021-10-02
8593 - Fix a possible buffer overflow when reading from compressed formats.
8595v0.13.1 - 2021-07-31
8596 - Fix platform detection for ARM64.
8598v0.13.0 - 2021-07-01
8599 - Improve support for reading and writing metadata. Use the `_with_metadata()` APIs to initialize
8600 a WAV decoder and store the metadata within the `drwav` object. Use the `pMetadata` and
8601 `metadataCount` members of the `drwav` object to read the data. The old way of handling metadata
8602 via a callback is still usable and valid.
8603 - API CHANGE: drwav_target_write_size_bytes() now takes extra parameters for calculating the
8604 required write size when writing metadata.
8605 - Add drwav_get_cursor_in_pcm_frames()
8606 - Add drwav_get_length_in_pcm_frames()
8607 - Fix a bug where drwav_read_raw() can call the read callback with a byte count of zero.
8609v0.12.20 - 2021-06-11
8610 - Fix some undefined behavior.
8612v0.12.19 - 2021-02-21
8613 - Fix a warning due to referencing _MSC_VER when it is undefined.
8614 - Minor improvements to the management of some internal state concerning the data chunk cursor.
8616v0.12.18 - 2021-01-31
8617 - Clean up some static analysis warnings.
8619v0.12.17 - 2021-01-17
8620 - Minor fix to sample code in documentation.
8621 - Correctly qualify a private API as private rather than public.
8622 - Code cleanup.
8624v0.12.16 - 2020-12-02
8625 - Fix a bug when trying to read more bytes than can fit in a size_t.
8627v0.12.15 - 2020-11-21
8628 - Fix compilation with OpenWatcom.
8630v0.12.14 - 2020-11-13
8631 - Minor code clean up.
8633v0.12.13 - 2020-11-01
8634 - Improve compiler support for older versions of GCC.
8636v0.12.12 - 2020-09-28
8637 - Add support for RF64.
8638 - Fix a bug in writing mode where the size of the RIFF chunk incorrectly includes the header section.
8640v0.12.11 - 2020-09-08
8641 - Fix a compilation error on older compilers.
8643v0.12.10 - 2020-08-24
8644 - Fix a bug when seeking with ADPCM formats.
8646v0.12.9 - 2020-08-02
8647 - Simplify sized types.
8649v0.12.8 - 2020-07-25
8650 - Fix a compilation warning.
8652v0.12.7 - 2020-07-15
8653 - Fix some bugs on big-endian architectures.
8654 - Fix an error in s24 to f32 conversion.
8656v0.12.6 - 2020-06-23
8657 - Change drwav_read_*() to allow NULL to be passed in as the output buffer which is equivalent to a forward seek.
8658 - Fix a buffer overflow when trying to decode invalid IMA-ADPCM files.
8659 - Add include guard for the implementation section.
8661v0.12.5 - 2020-05-27
8662 - Minor documentation fix.
8664v0.12.4 - 2020-05-16
8665 - Replace assert() with DRWAV_ASSERT().
8666 - Add compile-time and run-time version querying.
8667 - DRWAV_VERSION_MINOR
8668 - DRWAV_VERSION_MAJOR
8669 - DRWAV_VERSION_REVISION
8670 - DRWAV_VERSION_STRING
8671 - drwav_version()
8672 - drwav_version_string()
8674v0.12.3 - 2020-04-30
8675 - Fix compilation errors with VC6.
8677v0.12.2 - 2020-04-21
8678 - Fix a bug where drwav_init_file() does not close the file handle after attempting to load an erroneous file.
8680v0.12.1 - 2020-04-13
8681 - Fix some pedantic warnings.
8683v0.12.0 - 2020-04-04
8684 - API CHANGE: Add container and format parameters to the chunk callback.
8685 - Minor documentation updates.
8687v0.11.5 - 2020-03-07
8688 - Fix compilation error with Visual Studio .NET 2003.
8690v0.11.4 - 2020-01-29
8691 - Fix some static analysis warnings.
8692 - Fix a bug when reading f32 samples from an A-law encoded stream.
8694v0.11.3 - 2020-01-12
8695 - Minor changes to some f32 format conversion routines.
8696 - Minor bug fix for ADPCM conversion when end of file is reached.
8698v0.11.2 - 2019-12-02
8699 - Fix a possible crash when using custom memory allocators without a custom realloc() implementation.
8700 - Fix an integer overflow bug.
8701 - Fix a null pointer dereference bug.
8702 - Add limits to sample rate, channels and bits per sample to tighten up some validation.
8704v0.11.1 - 2019-10-07
8705 - Internal code clean up.
8707v0.11.0 - 2019-10-06
8708 - API CHANGE: Add support for user defined memory allocation routines. This system allows the program to specify their own memory allocation
8709 routines with a user data pointer for client-specific contextual data. This adds an extra parameter to the end of the following APIs:
8710 - drwav_init()
8711 - drwav_init_ex()
8712 - drwav_init_file()
8713 - drwav_init_file_ex()
8714 - drwav_init_file_w()
8715 - drwav_init_file_w_ex()
8716 - drwav_init_memory()
8717 - drwav_init_memory_ex()
8718 - drwav_init_write()
8719 - drwav_init_write_sequential()
8720 - drwav_init_write_sequential_pcm_frames()
8721 - drwav_init_file_write()
8722 - drwav_init_file_write_sequential()
8723 - drwav_init_file_write_sequential_pcm_frames()
8724 - drwav_init_file_write_w()
8725 - drwav_init_file_write_sequential_w()
8726 - drwav_init_file_write_sequential_pcm_frames_w()
8727 - drwav_init_memory_write()
8728 - drwav_init_memory_write_sequential()
8729 - drwav_init_memory_write_sequential_pcm_frames()
8730 - drwav_open_and_read_pcm_frames_s16()
8731 - drwav_open_and_read_pcm_frames_f32()
8732 - drwav_open_and_read_pcm_frames_s32()
8733 - drwav_open_file_and_read_pcm_frames_s16()
8734 - drwav_open_file_and_read_pcm_frames_f32()
8735 - drwav_open_file_and_read_pcm_frames_s32()
8736 - drwav_open_file_and_read_pcm_frames_s16_w()
8737 - drwav_open_file_and_read_pcm_frames_f32_w()
8738 - drwav_open_file_and_read_pcm_frames_s32_w()
8739 - drwav_open_memory_and_read_pcm_frames_s16()
8740 - drwav_open_memory_and_read_pcm_frames_f32()
8741 - drwav_open_memory_and_read_pcm_frames_s32()
8742 Set this extra parameter to NULL to use defaults which is the same as the previous behaviour. Setting this NULL will use
8743 DRWAV_MALLOC, DRWAV_REALLOC and DRWAV_FREE.
8744 - Add support for reading and writing PCM frames in an explicit endianness. New APIs:
8745 - drwav_read_pcm_frames_le()
8746 - drwav_read_pcm_frames_be()
8747 - drwav_read_pcm_frames_s16le()
8748 - drwav_read_pcm_frames_s16be()
8749 - drwav_read_pcm_frames_f32le()
8750 - drwav_read_pcm_frames_f32be()
8751 - drwav_read_pcm_frames_s32le()
8752 - drwav_read_pcm_frames_s32be()
8753 - drwav_write_pcm_frames_le()
8754 - drwav_write_pcm_frames_be()
8755 - Remove deprecated APIs.
8756 - API CHANGE: The following APIs now return native-endian data. Previously they returned little-endian data.
8757 - drwav_read_pcm_frames()
8758 - drwav_read_pcm_frames_s16()
8759 - drwav_read_pcm_frames_s32()
8760 - drwav_read_pcm_frames_f32()
8761 - drwav_open_and_read_pcm_frames_s16()
8762 - drwav_open_and_read_pcm_frames_s32()
8763 - drwav_open_and_read_pcm_frames_f32()
8764 - drwav_open_file_and_read_pcm_frames_s16()
8765 - drwav_open_file_and_read_pcm_frames_s32()
8766 - drwav_open_file_and_read_pcm_frames_f32()
8767 - drwav_open_file_and_read_pcm_frames_s16_w()
8768 - drwav_open_file_and_read_pcm_frames_s32_w()
8769 - drwav_open_file_and_read_pcm_frames_f32_w()
8770 - drwav_open_memory_and_read_pcm_frames_s16()
8771 - drwav_open_memory_and_read_pcm_frames_s32()
8772 - drwav_open_memory_and_read_pcm_frames_f32()
8774v0.10.1 - 2019-08-31
8775 - Correctly handle partial trailing ADPCM blocks.
8777v0.10.0 - 2019-08-04
8778 - Remove deprecated APIs.
8779 - Add wchar_t variants for file loading APIs:
8780 drwav_init_file_w()
8781 drwav_init_file_ex_w()
8782 drwav_init_file_write_w()
8783 drwav_init_file_write_sequential_w()
8784 - Add drwav_target_write_size_bytes() which calculates the total size in bytes of a WAV file given a format and sample count.
8785 - Add APIs for specifying the PCM frame count instead of the sample count when opening in sequential write mode:
8786 drwav_init_write_sequential_pcm_frames()
8787 drwav_init_file_write_sequential_pcm_frames()
8788 drwav_init_file_write_sequential_pcm_frames_w()
8789 drwav_init_memory_write_sequential_pcm_frames()
8790 - Deprecate drwav_open*() and drwav_close():
8791 drwav_open()
8792 drwav_open_ex()
8793 drwav_open_write()
8794 drwav_open_write_sequential()
8795 drwav_open_file()
8796 drwav_open_file_ex()
8797 drwav_open_file_write()
8798 drwav_open_file_write_sequential()
8799 drwav_open_memory()
8800 drwav_open_memory_ex()
8801 drwav_open_memory_write()
8802 drwav_open_memory_write_sequential()
8803 drwav_close()
8804 - Minor documentation updates.
8806v0.9.2 - 2019-05-21
8807 - Fix warnings.
8809v0.9.1 - 2019-05-05
8810 - Add support for C89.
8811 - Change license to choice of public domain or MIT-0.
8813v0.9.0 - 2018-12-16
8814 - API CHANGE: Add new reading APIs for reading by PCM frames instead of samples. Old APIs have been deprecated and
8815 will be removed in v0.10.0. Deprecated APIs and their replacements:
8816 drwav_read() -> drwav_read_pcm_frames()
8817 drwav_read_s16() -> drwav_read_pcm_frames_s16()
8818 drwav_read_f32() -> drwav_read_pcm_frames_f32()
8819 drwav_read_s32() -> drwav_read_pcm_frames_s32()
8820 drwav_seek_to_sample() -> drwav_seek_to_pcm_frame()
8821 drwav_write() -> drwav_write_pcm_frames()
8822 drwav_open_and_read_s16() -> drwav_open_and_read_pcm_frames_s16()
8823 drwav_open_and_read_f32() -> drwav_open_and_read_pcm_frames_f32()
8824 drwav_open_and_read_s32() -> drwav_open_and_read_pcm_frames_s32()
8825 drwav_open_file_and_read_s16() -> drwav_open_file_and_read_pcm_frames_s16()
8826 drwav_open_file_and_read_f32() -> drwav_open_file_and_read_pcm_frames_f32()
8827 drwav_open_file_and_read_s32() -> drwav_open_file_and_read_pcm_frames_s32()
8828 drwav_open_memory_and_read_s16() -> drwav_open_memory_and_read_pcm_frames_s16()
8829 drwav_open_memory_and_read_f32() -> drwav_open_memory_and_read_pcm_frames_f32()
8830 drwav_open_memory_and_read_s32() -> drwav_open_memory_and_read_pcm_frames_s32()
8831 drwav::totalSampleCount -> drwav::totalPCMFrameCount
8832 - API CHANGE: Rename drwav_open_and_read_file_*() to drwav_open_file_and_read_*().
8833 - API CHANGE: Rename drwav_open_and_read_memory_*() to drwav_open_memory_and_read_*().
8834 - Add built-in support for smpl chunks.
8835 - Add support for firing a callback for each chunk in the file at initialization time.
8836 - This is enabled through the drwav_init_ex(), etc. family of APIs.
8837 - Handle invalid FMT chunks more robustly.
8839v0.8.5 - 2018-09-11
8840 - Const correctness.
8841 - Fix a potential stack overflow.
8843v0.8.4 - 2018-08-07
8844 - Improve 64-bit detection.
8846v0.8.3 - 2018-08-05
8847 - Fix C++ build on older versions of GCC.
8849v0.8.2 - 2018-08-02
8850 - Fix some big-endian bugs.
8852v0.8.1 - 2018-06-29
8853 - Add support for sequential writing APIs.
8854 - Disable seeking in write mode.
8855 - Fix bugs with Wave64.
8856 - Fix typos.
8858v0.8 - 2018-04-27
8859 - Bug fix.
8860 - Start using major.minor.revision versioning.
8862v0.7f - 2018-02-05
8863 - Restrict ADPCM formats to a maximum of 2 channels.
8865v0.7e - 2018-02-02
8866 - Fix a crash.
8868v0.7d - 2018-02-01
8869 - Fix a crash.
8871v0.7c - 2018-02-01
8872 - Set drwav.bytesPerSample to 0 for all compressed formats.
8873 - Fix a crash when reading 16-bit floating point WAV files. In this case dr_wav will output silence for
8874 all format conversion reading APIs (*_s16, *_s32, *_f32 APIs).
8875 - Fix some divide-by-zero errors.
8877v0.7b - 2018-01-22
8878 - Fix errors with seeking of compressed formats.
8879 - Fix compilation error when DR_WAV_NO_CONVERSION_API
8881v0.7a - 2017-11-17
8882 - Fix some GCC warnings.
8884v0.7 - 2017-11-04
8885 - Add writing APIs.
8887v0.6 - 2017-08-16
8888 - API CHANGE: Rename dr_* types to drwav_*.
8889 - Add support for custom implementations of malloc(), realloc(), etc.
8890 - Add support for Microsoft ADPCM.
8891 - Add support for IMA ADPCM (DVI, format code 0x11).
8892 - Optimizations to drwav_read_s16().
8893 - Bug fixes.
8895v0.5g - 2017-07-16
8896 - Change underlying type for booleans to unsigned.
8898v0.5f - 2017-04-04
8899 - Fix a minor bug with drwav_open_and_read_s16() and family.
8901v0.5e - 2016-12-29
8902 - Added support for reading samples as signed 16-bit integers. Use the _s16() family of APIs for this.
8903 - Minor fixes to documentation.
8905v0.5d - 2016-12-28
8906 - Use drwav_int* and drwav_uint* sized types to improve compiler support.
8908v0.5c - 2016-11-11
8909 - Properly handle JUNK chunks that come before the FMT chunk.
8911v0.5b - 2016-10-23
8912 - A minor change to drwav_bool8 and drwav_bool32 types.
8914v0.5a - 2016-10-11
8915 - Fixed a bug with drwav_open_and_read() and family due to incorrect argument ordering.
8916 - Improve A-law and mu-law efficiency.
8918v0.5 - 2016-09-29
8919 - API CHANGE. Swap the order of "channels" and "sampleRate" parameters in drwav_open_and_read*(). Rationale for this is to
8920 keep it consistent with dr_audio and dr_flac.
8922v0.4b - 2016-09-18
8923 - Fixed a typo in documentation.
8925v0.4a - 2016-09-18
8926 - Fixed a typo.
8927 - Change date format to ISO 8601 (YYYY-MM-DD)
8929v0.4 - 2016-07-13
8930 - API CHANGE. Make onSeek consistent with dr_flac.
8931 - API CHANGE. Rename drwav_seek() to drwav_seek_to_sample() for clarity and consistency with dr_flac.
8932 - Added support for Sony Wave64.
8934v0.3a - 2016-05-28
8935 - API CHANGE. Return drwav_bool32 instead of int in onSeek callback.
8936 - Fixed a memory leak.
8938v0.3 - 2016-05-22
8939 - Lots of API changes for consistency.
8941v0.2a - 2016-05-16
8942 - Fixed Linux/GCC build.
8944v0.2 - 2016-05-11
8945 - Added support for reading data as signed 32-bit PCM for consistency with dr_flac.
8947v0.1a - 2016-05-07
8948 - Fixed a bug in drwav_open_file() where the file handle would not be closed if the loader failed to initialize.
8950v0.1 - 2016-05-04
8951 - Initial versioned release.
8952*/
8954/*
8955This software is available as a choice of the following licenses. Choose
8956whichever you prefer.
8958===============================================================================
8959ALTERNATIVE 1 - Public Domain (www.unlicense.org)
8960===============================================================================
8961This is free and unencumbered software released into the public domain.
8963Anyone is free to copy, modify, publish, use, compile, sell, or distribute this
8964software, either in source code form or as a compiled binary, for any purpose,
8965commercial or non-commercial, and by any means.
8967In jurisdictions that recognize copyright laws, the author or authors of this
8968software dedicate any and all copyright interest in the software to the public
8969domain. We make this dedication for the benefit of the public at large and to
8970the detriment of our heirs and successors. We intend this dedication to be an
8971overt act of relinquishment in perpetuity of all present and future rights to
8972this software under copyright law.
8974THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
8975IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
8976FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
8977AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
8978ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
8979WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
8981For more information, please refer to <http://unlicense.org/>
8983===============================================================================
8984ALTERNATIVE 2 - MIT No Attribution
8985===============================================================================
8986Copyright 2023 David Reid
8988Permission is hereby granted, free of charge, to any person obtaining a copy of
8989this software and associated documentation files (the "Software"), to deal in
8990the Software without restriction, including without limitation the rights to
8991use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
8992of the Software, and to permit persons to whom the Software is furnished to do
8993so.
8995THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
8996IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
8997FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
8998AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
8999LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
9000OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
9001SOFTWARE.
9002*/
index : raylib-jai
---