2 WAV audio loader and writer. Choice of public domain or MIT-0. See license statements at the end of this file.
3 dr_wav - v0.13.2 - 2021-10-02
5 David Reid - mackron@gmail.com
7 GitHub: https://github.com/mackron/dr_libs
13 This is a single file library. To use it, do something like the following in one .c file.
16 #define DR_WAV_IMPLEMENTATION
20 You can then #include this file in other parts of the program as you would with any other header file. Do something like the following to read audio data:
24 if (!drwav_init_file(&wav, "my_song.wav", NULL)) {
25 // Error opening WAV file.
28 drwav_int32* pDecodedInterleavedPCMFrames = malloc(wav.totalPCMFrameCount * wav.channels * sizeof(drwav_int32));
29 size_t numberOfSamplesActuallyDecoded = drwav_read_pcm_frames_s32(&wav, wav.totalPCMFrameCount, pDecodedInterleavedPCMFrames);
36 If you just want to quickly open and read the audio data in a single operation you can do something like this:
39 unsigned int channels;
40 unsigned int sampleRate;
41 drwav_uint64 totalPCMFrameCount;
42 float* pSampleData = drwav_open_file_and_read_pcm_frames_f32("my_song.wav", &channels, &sampleRate, &totalPCMFrameCount, NULL);
43 if (pSampleData == NULL) {
44 // Error opening and reading WAV file.
49 drwav_free(pSampleData, NULL);
52 The examples above use versions of the API that convert the audio data to a consistent format (32-bit signed PCM, in this case), but you can still output the
53 audio data in its internal format (see notes below for supported formats):
56 size_t framesRead = drwav_read_pcm_frames(&wav, wav.totalPCMFrameCount, pDecodedInterleavedPCMFrames);
59 You can also read the raw bytes of audio data, which could be useful if dr_wav does not have native support for a particular data format:
62 size_t bytesRead = drwav_read_raw(&wav, bytesToRead, pRawDataBuffer);
65 dr_wav can also be used to output WAV files. This does not currently support compressed formats. To use this, look at `drwav_init_write()`,
66 `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.
69 drwav_data_format format;
70 format.container = drwav_container_riff; // <-- drwav_container_riff = normal WAV files, drwav_container_w64 = Sony Wave64.
71 format.format = DR_WAVE_FORMAT_PCM; // <-- Any of the DR_WAVE_FORMAT_* codes.
73 format.sampleRate = 44100;
74 format.bitsPerSample = 16;
75 drwav_init_file_write(&wav, "data/recording.wav", &format, NULL);
79 drwav_uint64 framesWritten = drwav_write_pcm_frames(pWav, frameCount, pSamples);
82 dr_wav has seamless support the Sony Wave64 format. The decoder will automatically detect it and it should Just Work without any manual intervention.
87 #define these options before including this file.
89 #define DR_WAV_NO_CONVERSION_API
90 Disables conversion APIs such as `drwav_read_pcm_frames_f32()` and `drwav_s16_to_f32()`.
92 #define DR_WAV_NO_STDIO
93 Disables APIs that initialize a decoder from a file such as `drwav_init_file()`, `drwav_init_file_write()`, etc.
99 - Samples are always interleaved.
100 - 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()`
101 to read and convert audio data to 32-bit floating point, signed 32-bit integer and signed 16-bit integer samples respectively. Tested and supported internal
102 formats include the following:
108 - IEEE 32-bit floating point
109 - IEEE 64-bit floating point
112 - IMA ADPCM (DVI, format code 0x11)
113 - dr_wav will try to read the WAV file as best it can, even if it's not strictly conformant to the WAV format.
123 #define DRWAV_STRINGIFY(x) #x
124 #define DRWAV_XSTRINGIFY(x) DRWAV_STRINGIFY(x)
126 #define DRWAV_VERSION_MAJOR 0
127 #define DRWAV_VERSION_MINOR 13
128 #define DRWAV_VERSION_REVISION 2
129 #define DRWAV_VERSION_STRING DRWAV_XSTRINGIFY(DRWAV_VERSION_MAJOR) "." DRWAV_XSTRINGIFY(DRWAV_VERSION_MINOR) "." DRWAV_XSTRINGIFY(DRWAV_VERSION_REVISION)
131 #include <stddef.h> /* For size_t. */
134 typedef signed char drwav_int8
;
135 typedef unsigned char drwav_uint8
;
136 typedef signed short drwav_int16
;
137 typedef unsigned short drwav_uint16
;
138 typedef signed int drwav_int32
;
139 typedef unsigned int drwav_uint32
;
140 #if defined(_MSC_VER) && !defined(__clang__)
141 typedef signed __int64 drwav_int64
;
142 typedef unsigned __int64 drwav_uint64
;
144 #if defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6)))
145 #pragma GCC diagnostic push
146 #pragma GCC diagnostic ignored "-Wlong-long"
147 #if defined(__clang__)
148 #pragma GCC diagnostic ignored "-Wc++11-long-long"
151 typedef signed long long drwav_int64
;
152 typedef unsigned long long drwav_uint64
;
153 #if defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6)))
154 #pragma GCC diagnostic pop
157 #if defined(__LP64__) || defined(_WIN64) || (defined(__x86_64__) && !defined(__ILP32__)) || defined(_M_X64) || defined(__ia64) || defined (_M_IA64) || defined(__aarch64__) || defined(_M_ARM64) || defined(__powerpc64__)
158 typedef drwav_uint64 drwav_uintptr
;
160 typedef drwav_uint32 drwav_uintptr
;
162 typedef drwav_uint8 drwav_bool8
;
163 typedef drwav_uint32 drwav_bool32
;
165 #define DRWAV_FALSE 0
167 #if !defined(DRWAV_API)
168 #if defined(DRWAV_DLL)
170 #define DRWAV_DLL_IMPORT __declspec(dllimport)
171 #define DRWAV_DLL_EXPORT __declspec(dllexport)
172 #define DRWAV_DLL_PRIVATE static
174 #if defined(__GNUC__) && __GNUC__ >= 4
175 #define DRWAV_DLL_IMPORT __attribute__((visibility("default")))
176 #define DRWAV_DLL_EXPORT __attribute__((visibility("default")))
177 #define DRWAV_DLL_PRIVATE __attribute__((visibility("hidden")))
179 #define DRWAV_DLL_IMPORT
180 #define DRWAV_DLL_EXPORT
181 #define DRWAV_DLL_PRIVATE static
185 #if defined(DR_WAV_IMPLEMENTATION) || defined(DRWAV_IMPLEMENTATION)
186 #define DRWAV_API DRWAV_DLL_EXPORT
188 #define DRWAV_API DRWAV_DLL_IMPORT
190 #define DRWAV_PRIVATE DRWAV_DLL_PRIVATE
192 #define DRWAV_API extern
193 #define DRWAV_PRIVATE static
197 typedef drwav_int32 drwav_result
;
198 #define DRWAV_SUCCESS 0
199 #define DRWAV_ERROR -1 /* A generic error. */
200 #define DRWAV_INVALID_ARGS -2
201 #define DRWAV_INVALID_OPERATION -3
202 #define DRWAV_OUT_OF_MEMORY -4
203 #define DRWAV_OUT_OF_RANGE -5
204 #define DRWAV_ACCESS_DENIED -6
205 #define DRWAV_DOES_NOT_EXIST -7
206 #define DRWAV_ALREADY_EXISTS -8
207 #define DRWAV_TOO_MANY_OPEN_FILES -9
208 #define DRWAV_INVALID_FILE -10
209 #define DRWAV_TOO_BIG -11
210 #define DRWAV_PATH_TOO_LONG -12
211 #define DRWAV_NAME_TOO_LONG -13
212 #define DRWAV_NOT_DIRECTORY -14
213 #define DRWAV_IS_DIRECTORY -15
214 #define DRWAV_DIRECTORY_NOT_EMPTY -16
215 #define DRWAV_END_OF_FILE -17
216 #define DRWAV_NO_SPACE -18
217 #define DRWAV_BUSY -19
218 #define DRWAV_IO_ERROR -20
219 #define DRWAV_INTERRUPT -21
220 #define DRWAV_UNAVAILABLE -22
221 #define DRWAV_ALREADY_IN_USE -23
222 #define DRWAV_BAD_ADDRESS -24
223 #define DRWAV_BAD_SEEK -25
224 #define DRWAV_BAD_PIPE -26
225 #define DRWAV_DEADLOCK -27
226 #define DRWAV_TOO_MANY_LINKS -28
227 #define DRWAV_NOT_IMPLEMENTED -29
228 #define DRWAV_NO_MESSAGE -30
229 #define DRWAV_BAD_MESSAGE -31
230 #define DRWAV_NO_DATA_AVAILABLE -32
231 #define DRWAV_INVALID_DATA -33
232 #define DRWAV_TIMEOUT -34
233 #define DRWAV_NO_NETWORK -35
234 #define DRWAV_NOT_UNIQUE -36
235 #define DRWAV_NOT_SOCKET -37
236 #define DRWAV_NO_ADDRESS -38
237 #define DRWAV_BAD_PROTOCOL -39
238 #define DRWAV_PROTOCOL_UNAVAILABLE -40
239 #define DRWAV_PROTOCOL_NOT_SUPPORTED -41
240 #define DRWAV_PROTOCOL_FAMILY_NOT_SUPPORTED -42
241 #define DRWAV_ADDRESS_FAMILY_NOT_SUPPORTED -43
242 #define DRWAV_SOCKET_NOT_SUPPORTED -44
243 #define DRWAV_CONNECTION_RESET -45
244 #define DRWAV_ALREADY_CONNECTED -46
245 #define DRWAV_NOT_CONNECTED -47
246 #define DRWAV_CONNECTION_REFUSED -48
247 #define DRWAV_NO_HOST -49
248 #define DRWAV_IN_PROGRESS -50
249 #define DRWAV_CANCELLED -51
250 #define DRWAV_MEMORY_ALREADY_MAPPED -52
251 #define DRWAV_AT_END -53
253 /* Common data formats. */
254 #define DR_WAVE_FORMAT_PCM 0x1
255 #define DR_WAVE_FORMAT_ADPCM 0x2
256 #define DR_WAVE_FORMAT_IEEE_FLOAT 0x3
257 #define DR_WAVE_FORMAT_ALAW 0x6
258 #define DR_WAVE_FORMAT_MULAW 0x7
259 #define DR_WAVE_FORMAT_DVI_ADPCM 0x11
260 #define DR_WAVE_FORMAT_EXTENSIBLE 0xFFFE
262 /* Flags to pass into drwav_init_ex(), etc. */
263 #define DRWAV_SEQUENTIAL 0x00000001
265 DRWAV_API
void drwav_version(drwav_uint32
* pMajor
, drwav_uint32
* pMinor
, drwav_uint32
* pRevision
);
266 DRWAV_API
const char* drwav_version_string(void);
270 drwav_seek_origin_start
,
271 drwav_seek_origin_current
276 drwav_container_riff
,
285 drwav_uint8 fourcc
[4];
286 drwav_uint8 guid
[16];
289 /* The size in bytes of the chunk. */
290 drwav_uint64 sizeInBytes
;
293 RIFF = 2 byte alignment.
294 W64 = 8 byte alignment.
296 unsigned int paddingSize
;
297 } drwav_chunk_header
;
302 The format tag exactly as specified in the wave file's "fmt" chunk. This can be used by applications
303 that require support for data formats not natively supported by dr_wav.
305 drwav_uint16 formatTag
;
307 /* The number of channels making up the audio data. When this is set to 1 it is mono, 2 is stereo, etc. */
308 drwav_uint16 channels
;
310 /* The sample rate. Usually set to something like 44100. */
311 drwav_uint32 sampleRate
;
313 /* Average bytes per second. You probably don't need this, but it's left here for informational purposes. */
314 drwav_uint32 avgBytesPerSec
;
316 /* Block align. This is equal to the number of channels * bytes per sample. */
317 drwav_uint16 blockAlign
;
319 /* Bits per sample. */
320 drwav_uint16 bitsPerSample
;
322 /* The size of the extended data. Only used internally for validation, but left here for informational purposes. */
323 drwav_uint16 extendedSize
;
326 The number of valid bits per sample. When <formatTag> is equal to WAVE_FORMAT_EXTENSIBLE, <bitsPerSample>
327 is always rounded up to the nearest multiple of 8. This variable contains information about exactly how
328 many bits are valid per sample. Mainly used for informational purposes.
330 drwav_uint16 validBitsPerSample
;
332 /* The channel mask. Not used at the moment. */
333 drwav_uint32 channelMask
;
335 /* The sub-format, exactly as specified by the wave file. */
336 drwav_uint8 subFormat
[16];
339 DRWAV_API drwav_uint16
drwav_fmt_get_format(const drwav_fmt
* pFMT
);
343 Callback for when data is read. Return value is the number of bytes actually read.
345 pUserData [in] The user data that was passed to drwav_init() and family.
346 pBufferOut [out] The output buffer.
347 bytesToRead [in] The number of bytes to read.
349 Returns the number of bytes actually read.
351 A return value of less than bytesToRead indicates the end of the stream. Do _not_ return from this callback until
352 either the entire bytesToRead is filled or you have reached the end of the stream.
354 typedef size_t (* drwav_read_proc
)(void* pUserData
, void* pBufferOut
, size_t bytesToRead
);
357 Callback for when data is written. Returns value is the number of bytes actually written.
359 pUserData [in] The user data that was passed to drwav_init_write() and family.
360 pData [out] A pointer to the data to write.
361 bytesToWrite [in] The number of bytes to write.
363 Returns the number of bytes actually written.
365 If the return value differs from bytesToWrite, it indicates an error.
367 typedef size_t (* drwav_write_proc
)(void* pUserData
, const void* pData
, size_t bytesToWrite
);
370 Callback for when data needs to be seeked.
372 pUserData [in] The user data that was passed to drwav_init() and family.
373 offset [in] The number of bytes to move, relative to the origin. Will never be negative.
374 origin [in] The origin of the seek - the current position or the start of the stream.
376 Returns whether or not the seek was successful.
378 Whether or not it is relative to the beginning or current position is determined by the "origin" parameter which will be either drwav_seek_origin_start or
379 drwav_seek_origin_current.
381 typedef drwav_bool32 (* drwav_seek_proc
)(void* pUserData
, int offset
, drwav_seek_origin origin
);
384 Callback for when drwav_init_ex() finds a chunk.
386 pChunkUserData [in] The user data that was passed to the pChunkUserData parameter of drwav_init_ex() and family.
387 onRead [in] A pointer to the function to call when reading.
388 onSeek [in] A pointer to the function to call when seeking.
389 pReadSeekUserData [in] The user data that was passed to the pReadSeekUserData parameter of drwav_init_ex() and family.
390 pChunkHeader [in] A pointer to an object containing basic header information about the chunk. Use this to identify the chunk.
391 container [in] Whether or not the WAV file is a RIFF or Wave64 container. If you're unsure of the difference, assume RIFF.
392 pFMT [in] A pointer to the object containing the contents of the "fmt" chunk.
394 Returns the number of bytes read + seeked.
396 To read data from the chunk, call onRead(), passing in pReadSeekUserData as the first parameter. Do the same for seeking with onSeek(). The return value must
397 be the total number of bytes you have read _plus_ seeked.
399 Use the `container` argument to discriminate the fields in `pChunkHeader->id`. If the container is `drwav_container_riff` or `drwav_container_rf64` you should
400 use `id.fourcc`, otherwise you should use `id.guid`.
402 The `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
403 `DR_WAVE_FORMAT_*` identifiers.
405 The 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.
407 typedef 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
);
412 void* (* onMalloc
)(size_t sz
, void* pUserData
);
413 void* (* onRealloc
)(void* p
, size_t sz
, void* pUserData
);
414 void (* onFree
)(void* p
, void* pUserData
);
415 } drwav_allocation_callbacks
;
417 /* Structure for internal use. Only used for loaders opened with drwav_init_memory(). */
420 const drwav_uint8
* data
;
422 size_t currentReadPos
;
423 } drwav__memory_stream
;
425 /* Structure for internal use. Only used for writers opened with drwav_init_memory_write(). */
432 size_t currentWritePos
;
433 } drwav__memory_stream_write
;
437 drwav_container container
; /* RIFF, W64. */
438 drwav_uint32 format
; /* DR_WAVE_FORMAT_* */
439 drwav_uint32 channels
;
440 drwav_uint32 sampleRate
;
441 drwav_uint32 bitsPerSample
;
446 drwav_metadata_type_none
= 0,
449 Unknown simply means a chunk that drwav does not handle specifically. You can still ask to
450 receive these chunks as metadata objects. It is then up to you to interpret the chunk's data.
451 You can also write unknown metadata to a wav file. Be careful writing unknown chunks if you
452 have also edited the audio data. The unknown chunks could represent offsets/sizes that no
453 longer correctly correspond to the audio data.
455 drwav_metadata_type_unknown
= 1 << 0,
457 /* Only 1 of each of these metadata items are allowed in a wav file. */
458 drwav_metadata_type_smpl
= 1 << 1,
459 drwav_metadata_type_inst
= 1 << 2,
460 drwav_metadata_type_cue
= 1 << 3,
461 drwav_metadata_type_acid
= 1 << 4,
462 drwav_metadata_type_bext
= 1 << 5,
465 Wav files often have a LIST chunk. This is a chunk that contains a set of subchunks. For this
466 higher-level metadata API, we don't make a distinction between a regular chunk and a LIST
467 subchunk. Instead, they are all just 'metadata' items.
469 There can be multiple of these metadata items in a wav file.
471 drwav_metadata_type_list_label
= 1 << 6,
472 drwav_metadata_type_list_note
= 1 << 7,
473 drwav_metadata_type_list_labelled_cue_region
= 1 << 8,
475 drwav_metadata_type_list_info_software
= 1 << 9,
476 drwav_metadata_type_list_info_copyright
= 1 << 10,
477 drwav_metadata_type_list_info_title
= 1 << 11,
478 drwav_metadata_type_list_info_artist
= 1 << 12,
479 drwav_metadata_type_list_info_comment
= 1 << 13,
480 drwav_metadata_type_list_info_date
= 1 << 14,
481 drwav_metadata_type_list_info_genre
= 1 << 15,
482 drwav_metadata_type_list_info_album
= 1 << 16,
483 drwav_metadata_type_list_info_tracknumber
= 1 << 17,
485 /* Other type constants for convenience. */
486 drwav_metadata_type_list_all_info_strings
= drwav_metadata_type_list_info_software
487 | drwav_metadata_type_list_info_copyright
488 | drwav_metadata_type_list_info_title
489 | drwav_metadata_type_list_info_artist
490 | drwav_metadata_type_list_info_comment
491 | drwav_metadata_type_list_info_date
492 | drwav_metadata_type_list_info_genre
493 | drwav_metadata_type_list_info_album
494 | drwav_metadata_type_list_info_tracknumber
,
496 drwav_metadata_type_list_all_adtl
= drwav_metadata_type_list_label
497 | drwav_metadata_type_list_note
498 | drwav_metadata_type_list_labelled_cue_region
,
500 drwav_metadata_type_all
= -2, /*0xFFFFFFFF & ~drwav_metadata_type_unknown,*/
501 drwav_metadata_type_all_including_unknown
= -1 /*0xFFFFFFFF,*/
502 } drwav_metadata_type
;
507 The sampler chunk contains information about how a sound should be played in the context of a whole
508 audio production, and when used in a sampler. See https://en.wikipedia.org/wiki/Sample-based_synthesis.
512 drwav_smpl_loop_type_forward
= 0,
513 drwav_smpl_loop_type_pingpong
= 1,
514 drwav_smpl_loop_type_backward
= 2
515 } drwav_smpl_loop_type
;
519 /* 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. */
520 drwav_uint32 cuePointId
;
522 /* See drwav_smpl_loop_type. */
525 /* The byte offset of the first sample to be played in the loop. */
526 drwav_uint32 firstSampleByteOffset
;
528 /* The byte offset into the audio data of the last sample to be played in the loop. */
529 drwav_uint32 lastSampleByteOffset
;
531 /* 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. */
532 drwav_uint32 sampleFraction
;
534 /* Number of times to play the loop. 0 means loop infinitely. */
535 drwav_uint32 playCount
;
540 /* IDs for a particular MIDI manufacturer. 0 if not used. */
541 drwav_uint32 manufacturerId
;
542 drwav_uint32 productId
;
544 /* The period of 1 sample in nanoseconds. */
545 drwav_uint32 samplePeriodNanoseconds
;
547 /* The MIDI root note of this file. 0 to 127. */
548 drwav_uint32 midiUnityNote
;
550 /* 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). */
551 drwav_uint32 midiPitchFraction
;
553 /* Data relating to SMPTE standards which are used for syncing audio and video. 0 if not used. */
554 drwav_uint32 smpteFormat
;
555 drwav_uint32 smpteOffset
;
557 /* drwav_smpl_loop loops. */
558 drwav_uint32 sampleLoopCount
;
560 /* Optional sampler-specific data. */
561 drwav_uint32 samplerSpecificDataSizeInBytes
;
563 drwav_smpl_loop
* pLoops
;
564 drwav_uint8
* pSamplerSpecificData
;
570 The inst metadata contains data about how a sound should be played as part of an instrument. This
571 commonly read by samplers. See https://en.wikipedia.org/wiki/Sample-based_synthesis.
575 drwav_int8 midiUnityNote
; /* The root note of the audio as a MIDI note number. 0 to 127. */
576 drwav_int8 fineTuneCents
; /* -50 to +50 */
577 drwav_int8 gainDecibels
; /* -64 to +64 */
578 drwav_int8 lowNote
; /* 0 to 127 */
579 drwav_int8 highNote
; /* 0 to 127 */
580 drwav_int8 lowVelocity
; /* 1 to 127 */
581 drwav_int8 highVelocity
; /* 1 to 127 */
587 Cue points are markers at specific points in the audio. They often come with an associated piece of
588 drwav_list_label_or_note metadata which contains the text for the marker.
592 /* Unique identification value. */
595 /* Set to 0. This is only relevant if there is a 'playlist' chunk - which is not supported by dr_wav. */
596 drwav_uint32 playOrderPosition
;
598 /* 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". */
599 drwav_uint8 dataChunkId
[4];
601 /* 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. */
602 drwav_uint32 chunkStart
;
604 /* 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. */
605 drwav_uint32 blockStart
;
607 /* For uncompressed formats this is the byte offset of the cue point into the audio data. For compressed formats this is relative to the block specified with blockStart. */
608 drwav_uint32 sampleByteOffset
;
613 drwav_uint32 cuePointCount
;
614 drwav_cue_point
*pCuePoints
;
620 This chunk contains some information about the time signature and the tempo of the audio.
624 drwav_acid_flag_one_shot
= 1, /* If this is not set, then it is a loop instead of a one-shot. */
625 drwav_acid_flag_root_note_set
= 2,
626 drwav_acid_flag_stretch
= 4,
627 drwav_acid_flag_disk_based
= 8,
628 drwav_acid_flag_acidizer
= 16 /* Not sure what this means. */
633 /* A bit-field, see drwav_acid_flag. */
636 /* Valid if flags contains drwav_acid_flag_root_note_set. It represents the MIDI root note the file - a value from 0 to 127. */
637 drwav_uint16 midiUnityNote
;
639 /* Reserved values that should probably be ignored. reserved1 seems to often be 128 and reserved2 is 0. */
640 drwav_uint16 reserved1
;
643 /* Number of beats. */
644 drwav_uint32 numBeats
;
646 /* The time signature of the audio. */
647 drwav_uint16 meterDenominator
;
648 drwav_uint16 meterNumerator
;
650 /* Beats per minute of the track. Setting a value of 0 suggests that there is no tempo. */
655 Cue Label or Note metadata
657 These are 2 different types of metadata, but they have the exact same format. Labels tend to be the
658 more common and represent a short name for a cue point. Notes might be used to represent a longer
663 /* The ID of a cue point that this label or note corresponds to. */
664 drwav_uint32 cuePointId
;
666 /* Size of the string not including any null terminator. */
667 drwav_uint32 stringLength
;
669 /* The string. The *init_with_metadata functions null terminate this for convenience. */
671 } drwav_list_label_or_note
;
674 BEXT metadata, also known as Broadcast Wave Format (BWF)
676 This metadata adds some extra description to an audio file. You must check the version field to
677 determine if the UMID or the loudness fields are valid.
682 These top 3 fields, and the umid field are actually defined in the standard as a statically
683 sized buffers. In order to reduce the size of this struct (and therefore the union in the
684 metadata struct), we instead store these as pointers.
686 char* pDescription
; /* Can be NULL or a null-terminated string, must be <= 256 characters. */
687 char* pOriginatorName
; /* Can be NULL or a null-terminated string, must be <= 32 characters. */
688 char* pOriginatorReference
; /* Can be NULL or a null-terminated string, must be <= 32 characters. */
689 char pOriginationDate
[10]; /* ASCII "yyyy:mm:dd". */
690 char pOriginationTime
[8]; /* ASCII "hh:mm:ss". */
691 drwav_uint64 timeReference
; /* First sample count since midnight. */
692 drwav_uint16 version
; /* Version of the BWF, check this to see if the fields below are valid. */
695 Unrestricted ASCII characters containing a collection of strings terminated by CR/LF. Each
696 string shall contain a description of a coding process applied to the audio data.
698 char* pCodingHistory
;
699 drwav_uint32 codingHistorySize
;
701 /* Fields below this point are only valid if the version is 1 or above. */
702 drwav_uint8
* pUMID
; /* Exactly 64 bytes of SMPTE UMID */
704 /* Fields below this point are only valid if the version is 2 or above. */
705 drwav_uint16 loudnessValue
; /* Integrated Loudness Value of the file in LUFS (multiplied by 100). */
706 drwav_uint16 loudnessRange
; /* Loudness Range of the file in LU (multiplied by 100). */
707 drwav_uint16 maxTruePeakLevel
; /* Maximum True Peak Level of the file expressed as dBTP (multiplied by 100). */
708 drwav_uint16 maxMomentaryLoudness
; /* Highest value of the Momentary Loudness Level of the file in LUFS (multiplied by 100). */
709 drwav_uint16 maxShortTermLoudness
; /* Highest value of the Short-Term Loudness Level of the file in LUFS (multiplied by 100). */
715 There a many different types of information text that can be saved in this format. This is where
716 things like the album name, the artists, the year it was produced, etc are saved. See
717 drwav_metadata_type for the full list of types that dr_wav supports.
721 /* Size of the string not including any null terminator. */
722 drwav_uint32 stringLength
;
724 /* The string. The *init_with_metadata functions null terminate this for convenience. */
726 } drwav_list_info_text
;
729 Labelled Cue Region Metadata
731 The labelled cue region metadata is used to associate some region of audio with text. The region
732 starts at a cue point, and extends for the given number of samples.
736 /* The ID of a cue point that this object corresponds to. */
737 drwav_uint32 cuePointId
;
739 /* The number of samples from the cue point forwards that should be considered this region */
740 drwav_uint32 sampleLength
;
742 /* Four characters used to say what the purpose of this region is. */
743 drwav_uint8 purposeId
[4];
745 /* Unsure of the exact meanings of these. It appears to be acceptable to set them all to 0. */
746 drwav_uint16 country
;
747 drwav_uint16 language
;
748 drwav_uint16 dialect
;
749 drwav_uint16 codePage
;
751 /* Size of the string not including any null terminator. */
752 drwav_uint32 stringLength
;
754 /* The string. The *init_with_metadata functions null terminate this for convenience. */
756 } drwav_list_labelled_cue_region
;
761 This chunk just represents a type of chunk that dr_wav does not understand.
763 Unknown metadata has a location attached to it. This is because wav files can have a LIST chunk
764 that contains subchunks. These LIST chunks can be one of two types. An adtl list, or an INFO
765 list. This enum is used to specify the location of a chunk that dr_wav currently doesn't support.
769 drwav_metadata_location_invalid
,
770 drwav_metadata_location_top_level
,
771 drwav_metadata_location_inside_info_list
,
772 drwav_metadata_location_inside_adtl_list
773 } drwav_metadata_location
;
778 drwav_metadata_location chunkLocation
;
779 drwav_uint32 dataSizeInBytes
;
781 } drwav_unknown_metadata
;
784 Metadata is saved as a union of all the supported types.
788 /* Determines which item in the union is valid. */
789 drwav_metadata_type type
;
798 drwav_list_label_or_note labelOrNote
; /* List label or list note. */
799 drwav_list_labelled_cue_region labelledCueRegion
;
800 drwav_list_info_text infoText
; /* Any of the list info types. */
801 drwav_unknown_metadata unknown
;
807 /* A pointer to the function to call when more data is needed. */
808 drwav_read_proc onRead
;
810 /* A pointer to the function to call when data needs to be written. Only used when the drwav object is opened in write mode. */
811 drwav_write_proc onWrite
;
813 /* A pointer to the function to call when the wav file needs to be seeked. */
814 drwav_seek_proc onSeek
;
816 /* The user data to pass to callbacks. */
819 /* Allocation callbacks. */
820 drwav_allocation_callbacks allocationCallbacks
;
823 /* Whether or not the WAV file is formatted as a standard RIFF file or W64. */
824 drwav_container container
;
827 /* Structure containing format information exactly as specified by the wav file. */
830 /* The sample rate. Will be set to something like 44100. */
831 drwav_uint32 sampleRate
;
833 /* The number of channels. This will be set to 1 for monaural streams, 2 for stereo, etc. */
834 drwav_uint16 channels
;
836 /* The bits per sample. Will be set to something like 16, 24, etc. */
837 drwav_uint16 bitsPerSample
;
839 /* Equal to fmt.formatTag, or the value specified by fmt.subFormat if fmt.formatTag is equal to 65534 (WAVE_FORMAT_EXTENSIBLE). */
840 drwav_uint16 translatedFormatTag
;
842 /* The total number of PCM frames making up the audio data. */
843 drwav_uint64 totalPCMFrameCount
;
846 /* The size in bytes of the data chunk. */
847 drwav_uint64 dataChunkDataSize
;
849 /* The position in the stream of the first data byte of the data chunk. This is used for seeking. */
850 drwav_uint64 dataChunkDataPos
;
852 /* The number of bytes remaining in the data chunk. */
853 drwav_uint64 bytesRemaining
;
855 /* The current read position in PCM frames. */
856 drwav_uint64 readCursorInPCMFrames
;
860 Only used in sequential write mode. Keeps track of the desired size of the "data" chunk at the point of initialization time. Always
861 set to 0 for non-sequential writes and when the drwav object is opened in read mode. Used for validation.
863 drwav_uint64 dataChunkDataSizeTargetWrite
;
865 /* Keeps track of whether or not the wav writer was initialized in sequential mode. */
866 drwav_bool32 isSequentialWrite
;
869 /* A bit-field of drwav_metadata_type values, only bits set in this variable are parsed and saved */
870 drwav_metadata_type allowedMetadataTypes
;
872 /* 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(). */
873 drwav_metadata
* pMetadata
;
874 drwav_uint32 metadataCount
;
877 /* A hack to avoid a DRWAV_MALLOC() when opening a decoder with drwav_init_memory(). */
878 drwav__memory_stream memoryStream
;
879 drwav__memory_stream_write memoryStreamWrite
;
882 /* Microsoft ADPCM specific data. */
885 drwav_uint32 bytesRemainingInBlock
;
886 drwav_uint16 predictor
[2];
887 drwav_int32 delta
[2];
888 drwav_int32 cachedFrames
[4]; /* Samples are stored in this cache during decoding. */
889 drwav_uint32 cachedFrameCount
;
890 drwav_int32 prevFrames
[2][2]; /* The previous 2 samples for each channel (2 channels at most). */
893 /* IMA ADPCM specific data. */
896 drwav_uint32 bytesRemainingInBlock
;
897 drwav_int32 predictor
[2];
898 drwav_int32 stepIndex
[2];
899 drwav_int32 cachedFrames
[16]; /* Samples are stored in this cache during decoding. */
900 drwav_uint32 cachedFrameCount
;
906 Initializes a pre-allocated drwav object for reading.
908 pWav [out] A pointer to the drwav object being initialized.
909 onRead [in] The function to call when data needs to be read from the client.
910 onSeek [in] The function to call when the read position of the client data needs to move.
911 onChunk [in, optional] The function to call when a chunk is enumerated at initialized time.
912 pUserData, pReadSeekUserData [in, optional] A pointer to application defined data that will be passed to onRead and onSeek.
913 pChunkUserData [in, optional] A pointer to application defined data that will be passed to onChunk.
914 flags [in, optional] A set of flags for controlling how things are loaded.
916 Returns true if successful; false otherwise.
918 Close the loader with drwav_uninit().
920 This is the lowest level function for initializing a WAV file. You can also use drwav_init_file() and drwav_init_memory()
921 to open the stream from a file or from a block of memory respectively.
923 Possible values for flags:
924 DRWAV_SEQUENTIAL: Never perform a backwards seek while loading. This disables the chunk callback and will cause this function
925 to return as soon as the data chunk is found. Any chunks after the data chunk will be ignored.
927 drwav_init() is equivalent to "drwav_init_ex(pWav, onRead, onSeek, NULL, pUserData, NULL, 0);".
929 The onChunk callback is not called for the WAVE or FMT chunks. The contents of the FMT chunk can be read from pWav->fmt
930 after the function returns.
932 See also: drwav_init_file(), drwav_init_memory(), drwav_uninit()
934 DRWAV_API drwav_bool32
drwav_init(drwav
* pWav
, drwav_read_proc onRead
, drwav_seek_proc onSeek
, void* pUserData
, const drwav_allocation_callbacks
* pAllocationCallbacks
);
935 DRWAV_API drwav_bool32
drwav_init_ex(drwav
* pWav
, drwav_read_proc onRead
, drwav_seek_proc onSeek
, drwav_chunk_proc onChunk
, void* pReadSeekUserData
, void* pChunkUserData
, drwav_uint32 flags
, const drwav_allocation_callbacks
* pAllocationCallbacks
);
936 DRWAV_API drwav_bool32
drwav_init_with_metadata(drwav
* pWav
, drwav_read_proc onRead
, drwav_seek_proc onSeek
, void* pUserData
, drwav_uint32 flags
, const drwav_allocation_callbacks
* pAllocationCallbacks
);
939 Initializes a pre-allocated drwav object for writing.
941 onWrite [in] The function to call when data needs to be written.
942 onSeek [in] The function to call when the write position needs to move.
943 pUserData [in, optional] A pointer to application defined data that will be passed to onWrite and onSeek.
944 metadata, 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.
946 Returns true if successful; false otherwise.
948 Close the writer with drwav_uninit().
950 This is the lowest level function for initializing a WAV file. You can also use drwav_init_file_write() and drwav_init_memory_write()
951 to open the stream from a file or from a block of memory respectively.
953 If the total sample count is known, you can use drwav_init_write_sequential(). This avoids the need for dr_wav to perform
954 a post-processing step for storing the total sample count and the size of the data chunk which requires a backwards seek.
956 See also: drwav_init_file_write(), drwav_init_memory_write(), drwav_uninit()
958 DRWAV_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
);
959 DRWAV_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
);
960 DRWAV_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
);
961 DRWAV_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
);
964 Utility function to determine the target size of the entire data to be written (including all headers and chunks).
966 Returns the target size in bytes.
968 The metadata argument can be NULL meaning no metadata exists.
970 Useful if the application needs to know the size to allocate.
972 Only writing to the RIFF chunk and one data chunk is currently supported.
974 See also: drwav_init_write(), drwav_init_file_write(), drwav_init_memory_write()
976 DRWAV_API drwav_uint64
drwav_target_write_size_bytes(const drwav_data_format
* pFormat
, drwav_uint64 totalFrameCount
, drwav_metadata
* pMetadata
, drwav_uint32 metadataCount
);
979 Take 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.
981 Useful if you want the data to persist beyond the lifetime of the drwav object.
983 You must free the data returned from this function using drwav_free().
985 DRWAV_API drwav_metadata
* drwav_take_ownership_of_metadata(drwav
* pWav
);
988 Uninitializes the given drwav object.
990 Use this only for objects initialized with drwav_init*() functions (drwav_init(), drwav_init_ex(), drwav_init_write(), drwav_init_write_sequential()).
992 DRWAV_API drwav_result
drwav_uninit(drwav
* pWav
);
996 Reads raw audio data.
998 This is the lowest level function for reading audio data. It simply reads the given number of
999 bytes of the raw internal sample data.
1001 Consider using drwav_read_pcm_frames_s16(), drwav_read_pcm_frames_s32() or drwav_read_pcm_frames_f32() for
1002 reading sample data in a consistent format.
1004 pBufferOut can be NULL in which case a seek will be performed.
1006 Returns the number of bytes actually read.
1008 DRWAV_API
size_t drwav_read_raw(drwav
* pWav
, size_t bytesToRead
, void* pBufferOut
);
1011 Reads up to the specified number of PCM frames from the WAV file.
1013 The output data will be in the file's internal format, converted to native-endian byte order. Use
1014 drwav_read_pcm_frames_s16/f32/s32() to read data in a specific format.
1016 If the return value is less than <framesToRead> it means the end of the file has been reached or
1017 you have requested more PCM frames than can possibly fit in the output buffer.
1019 This function will only work when sample data is of a fixed size and uncompressed. If you are
1020 using a compressed format consider using drwav_read_raw() or drwav_read_pcm_frames_s16/s32/f32().
1022 pBufferOut can be NULL in which case a seek will be performed.
1024 DRWAV_API drwav_uint64
drwav_read_pcm_frames(drwav
* pWav
, drwav_uint64 framesToRead
, void* pBufferOut
);
1025 DRWAV_API drwav_uint64
drwav_read_pcm_frames_le(drwav
* pWav
, drwav_uint64 framesToRead
, void* pBufferOut
);
1026 DRWAV_API drwav_uint64
drwav_read_pcm_frames_be(drwav
* pWav
, drwav_uint64 framesToRead
, void* pBufferOut
);
1029 Seeks to the given PCM frame.
1031 Returns true if successful; false otherwise.
1033 DRWAV_API drwav_bool32
drwav_seek_to_pcm_frame(drwav
* pWav
, drwav_uint64 targetFrameIndex
);
1036 Retrieves the current read position in pcm frames.
1038 DRWAV_API drwav_result
drwav_get_cursor_in_pcm_frames(drwav
* pWav
, drwav_uint64
* pCursor
);
1041 Retrieves the length of the file.
1043 DRWAV_API drwav_result
drwav_get_length_in_pcm_frames(drwav
* pWav
, drwav_uint64
* pLength
);
1047 Writes raw audio data.
1049 Returns the number of bytes actually written. If this differs from bytesToWrite, it indicates an error.
1051 DRWAV_API
size_t drwav_write_raw(drwav
* pWav
, size_t bytesToWrite
, const void* pData
);
1056 Returns the number of PCM frames written.
1058 Input samples need to be in native-endian byte order. On big-endian architectures the input data will be converted to
1059 little-endian. Use drwav_write_raw() to write raw audio data without performing any conversion.
1061 DRWAV_API drwav_uint64
drwav_write_pcm_frames(drwav
* pWav
, drwav_uint64 framesToWrite
, const void* pData
);
1062 DRWAV_API drwav_uint64
drwav_write_pcm_frames_le(drwav
* pWav
, drwav_uint64 framesToWrite
, const void* pData
);
1063 DRWAV_API drwav_uint64
drwav_write_pcm_frames_be(drwav
* pWav
, drwav_uint64 framesToWrite
, const void* pData
);
1065 /* Conversion Utilities */
1066 #ifndef DR_WAV_NO_CONVERSION_API
1069 Reads a chunk of audio data and converts it to signed 16-bit PCM samples.
1071 pBufferOut can be NULL in which case a seek will be performed.
1073 Returns the number of PCM frames actually read.
1075 If the return value is less than <framesToRead> it means the end of the file has been reached.
1077 DRWAV_API drwav_uint64
drwav_read_pcm_frames_s16(drwav
* pWav
, drwav_uint64 framesToRead
, drwav_int16
* pBufferOut
);
1078 DRWAV_API drwav_uint64
drwav_read_pcm_frames_s16le(drwav
* pWav
, drwav_uint64 framesToRead
, drwav_int16
* pBufferOut
);
1079 DRWAV_API drwav_uint64
drwav_read_pcm_frames_s16be(drwav
* pWav
, drwav_uint64 framesToRead
, drwav_int16
* pBufferOut
);
1081 /* Low-level function for converting unsigned 8-bit PCM samples to signed 16-bit PCM samples. */
1082 DRWAV_API
void drwav_u8_to_s16(drwav_int16
* pOut
, const drwav_uint8
* pIn
, size_t sampleCount
);
1084 /* Low-level function for converting signed 24-bit PCM samples to signed 16-bit PCM samples. */
1085 DRWAV_API
void drwav_s24_to_s16(drwav_int16
* pOut
, const drwav_uint8
* pIn
, size_t sampleCount
);
1087 /* Low-level function for converting signed 32-bit PCM samples to signed 16-bit PCM samples. */
1088 DRWAV_API
void drwav_s32_to_s16(drwav_int16
* pOut
, const drwav_int32
* pIn
, size_t sampleCount
);
1090 /* Low-level function for converting IEEE 32-bit floating point samples to signed 16-bit PCM samples. */
1091 DRWAV_API
void drwav_f32_to_s16(drwav_int16
* pOut
, const float* pIn
, size_t sampleCount
);
1093 /* Low-level function for converting IEEE 64-bit floating point samples to signed 16-bit PCM samples. */
1094 DRWAV_API
void drwav_f64_to_s16(drwav_int16
* pOut
, const double* pIn
, size_t sampleCount
);
1096 /* Low-level function for converting A-law samples to signed 16-bit PCM samples. */
1097 DRWAV_API
void drwav_alaw_to_s16(drwav_int16
* pOut
, const drwav_uint8
* pIn
, size_t sampleCount
);
1099 /* Low-level function for converting u-law samples to signed 16-bit PCM samples. */
1100 DRWAV_API
void drwav_mulaw_to_s16(drwav_int16
* pOut
, const drwav_uint8
* pIn
, size_t sampleCount
);
1104 Reads a chunk of audio data and converts it to IEEE 32-bit floating point samples.
1106 pBufferOut can be NULL in which case a seek will be performed.
1108 Returns the number of PCM frames actually read.
1110 If the return value is less than <framesToRead> it means the end of the file has been reached.
1112 DRWAV_API drwav_uint64
drwav_read_pcm_frames_f32(drwav
* pWav
, drwav_uint64 framesToRead
, float* pBufferOut
);
1113 DRWAV_API drwav_uint64
drwav_read_pcm_frames_f32le(drwav
* pWav
, drwav_uint64 framesToRead
, float* pBufferOut
);
1114 DRWAV_API drwav_uint64
drwav_read_pcm_frames_f32be(drwav
* pWav
, drwav_uint64 framesToRead
, float* pBufferOut
);
1116 /* Low-level function for converting unsigned 8-bit PCM samples to IEEE 32-bit floating point samples. */
1117 DRWAV_API
void drwav_u8_to_f32(float* pOut
, const drwav_uint8
* pIn
, size_t sampleCount
);
1119 /* Low-level function for converting signed 16-bit PCM samples to IEEE 32-bit floating point samples. */
1120 DRWAV_API
void drwav_s16_to_f32(float* pOut
, const drwav_int16
* pIn
, size_t sampleCount
);
1122 /* Low-level function for converting signed 24-bit PCM samples to IEEE 32-bit floating point samples. */
1123 DRWAV_API
void drwav_s24_to_f32(float* pOut
, const drwav_uint8
* pIn
, size_t sampleCount
);
1125 /* Low-level function for converting signed 32-bit PCM samples to IEEE 32-bit floating point samples. */
1126 DRWAV_API
void drwav_s32_to_f32(float* pOut
, const drwav_int32
* pIn
, size_t sampleCount
);
1128 /* Low-level function for converting IEEE 64-bit floating point samples to IEEE 32-bit floating point samples. */
1129 DRWAV_API
void drwav_f64_to_f32(float* pOut
, const double* pIn
, size_t sampleCount
);
1131 /* Low-level function for converting A-law samples to IEEE 32-bit floating point samples. */
1132 DRWAV_API
void drwav_alaw_to_f32(float* pOut
, const drwav_uint8
* pIn
, size_t sampleCount
);
1134 /* Low-level function for converting u-law samples to IEEE 32-bit floating point samples. */
1135 DRWAV_API
void drwav_mulaw_to_f32(float* pOut
, const drwav_uint8
* pIn
, size_t sampleCount
);
1139 Reads a chunk of audio data and converts it to signed 32-bit PCM samples.
1141 pBufferOut can be NULL in which case a seek will be performed.
1143 Returns the number of PCM frames actually read.
1145 If the return value is less than <framesToRead> it means the end of the file has been reached.
1147 DRWAV_API drwav_uint64
drwav_read_pcm_frames_s32(drwav
* pWav
, drwav_uint64 framesToRead
, drwav_int32
* pBufferOut
);
1148 DRWAV_API drwav_uint64
drwav_read_pcm_frames_s32le(drwav
* pWav
, drwav_uint64 framesToRead
, drwav_int32
* pBufferOut
);
1149 DRWAV_API drwav_uint64
drwav_read_pcm_frames_s32be(drwav
* pWav
, drwav_uint64 framesToRead
, drwav_int32
* pBufferOut
);
1151 /* Low-level function for converting unsigned 8-bit PCM samples to signed 32-bit PCM samples. */
1152 DRWAV_API
void drwav_u8_to_s32(drwav_int32
* pOut
, const drwav_uint8
* pIn
, size_t sampleCount
);
1154 /* Low-level function for converting signed 16-bit PCM samples to signed 32-bit PCM samples. */
1155 DRWAV_API
void drwav_s16_to_s32(drwav_int32
* pOut
, const drwav_int16
* pIn
, size_t sampleCount
);
1157 /* Low-level function for converting signed 24-bit PCM samples to signed 32-bit PCM samples. */
1158 DRWAV_API
void drwav_s24_to_s32(drwav_int32
* pOut
, const drwav_uint8
* pIn
, size_t sampleCount
);
1160 /* Low-level function for converting IEEE 32-bit floating point samples to signed 32-bit PCM samples. */
1161 DRWAV_API
void drwav_f32_to_s32(drwav_int32
* pOut
, const float* pIn
, size_t sampleCount
);
1163 /* Low-level function for converting IEEE 64-bit floating point samples to signed 32-bit PCM samples. */
1164 DRWAV_API
void drwav_f64_to_s32(drwav_int32
* pOut
, const double* pIn
, size_t sampleCount
);
1166 /* Low-level function for converting A-law samples to signed 32-bit PCM samples. */
1167 DRWAV_API
void drwav_alaw_to_s32(drwav_int32
* pOut
, const drwav_uint8
* pIn
, size_t sampleCount
);
1169 /* Low-level function for converting u-law samples to signed 32-bit PCM samples. */
1170 DRWAV_API
void drwav_mulaw_to_s32(drwav_int32
* pOut
, const drwav_uint8
* pIn
, size_t sampleCount
);
1172 #endif /* DR_WAV_NO_CONVERSION_API */
1175 /* High-Level Convenience Helpers */
1177 #ifndef DR_WAV_NO_STDIO
1179 Helper for initializing a wave file for reading using stdio.
1181 This holds the internal FILE object until drwav_uninit() is called. Keep this in mind if you're caching drwav
1182 objects because the operating system may restrict the number of file handles an application can have open at
1185 DRWAV_API drwav_bool32
drwav_init_file(drwav
* pWav
, const char* filename
, const drwav_allocation_callbacks
* pAllocationCallbacks
);
1186 DRWAV_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
);
1187 DRWAV_API drwav_bool32
drwav_init_file_w(drwav
* pWav
, const wchar_t* filename
, const drwav_allocation_callbacks
* pAllocationCallbacks
);
1188 DRWAV_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
);
1189 DRWAV_API drwav_bool32
drwav_init_file_with_metadata(drwav
* pWav
, const char* filename
, drwav_uint32 flags
, const drwav_allocation_callbacks
* pAllocationCallbacks
);
1190 DRWAV_API drwav_bool32
drwav_init_file_with_metadata_w(drwav
* pWav
, const wchar_t* filename
, drwav_uint32 flags
, const drwav_allocation_callbacks
* pAllocationCallbacks
);
1194 Helper for initializing a wave file for writing using stdio.
1196 This holds the internal FILE object until drwav_uninit() is called. Keep this in mind if you're caching drwav
1197 objects because the operating system may restrict the number of file handles an application can have open at
1200 DRWAV_API drwav_bool32
drwav_init_file_write(drwav
* pWav
, const char* filename
, const drwav_data_format
* pFormat
, const drwav_allocation_callbacks
* pAllocationCallbacks
);
1201 DRWAV_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
);
1202 DRWAV_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
);
1203 DRWAV_API drwav_bool32
drwav_init_file_write_w(drwav
* pWav
, const wchar_t* filename
, const drwav_data_format
* pFormat
, const drwav_allocation_callbacks
* pAllocationCallbacks
);
1204 DRWAV_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
);
1205 DRWAV_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
);
1206 #endif /* DR_WAV_NO_STDIO */
1209 Helper for initializing a loader from a pre-allocated memory buffer.
1211 This does not create a copy of the data. It is up to the application to ensure the buffer remains valid for
1212 the lifetime of the drwav object.
1214 The buffer should contain the contents of the entire wave file, not just the sample data.
1216 DRWAV_API drwav_bool32
drwav_init_memory(drwav
* pWav
, const void* data
, size_t dataSize
, const drwav_allocation_callbacks
* pAllocationCallbacks
);
1217 DRWAV_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
);
1218 DRWAV_API drwav_bool32
drwav_init_memory_with_metadata(drwav
* pWav
, const void* data
, size_t dataSize
, drwav_uint32 flags
, const drwav_allocation_callbacks
* pAllocationCallbacks
);
1221 Helper for initializing a writer which outputs data to a memory buffer.
1223 dr_wav will manage the memory allocations, however it is up to the caller to free the data with drwav_free().
1225 The buffer will remain allocated even after drwav_uninit() is called. The buffer should not be considered valid
1226 until after drwav_uninit() has been called.
1228 DRWAV_API drwav_bool32
drwav_init_memory_write(drwav
* pWav
, void** ppData
, size_t* pDataSize
, const drwav_data_format
* pFormat
, const drwav_allocation_callbacks
* pAllocationCallbacks
);
1229 DRWAV_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
);
1230 DRWAV_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
);
1233 #ifndef DR_WAV_NO_CONVERSION_API
1235 Opens and reads an entire wav file in a single operation.
1237 The return value is a heap-allocated buffer containing the audio data. Use drwav_free() to free the buffer.
1239 DRWAV_API drwav_int16
* drwav_open_and_read_pcm_frames_s16(drwav_read_proc onRead
, drwav_seek_proc onSeek
, void* pUserData
, unsigned int* channelsOut
, unsigned int* sampleRateOut
, drwav_uint64
* totalFrameCountOut
, const drwav_allocation_callbacks
* pAllocationCallbacks
);
1240 DRWAV_API
float* drwav_open_and_read_pcm_frames_f32(drwav_read_proc onRead
, drwav_seek_proc onSeek
, void* pUserData
, unsigned int* channelsOut
, unsigned int* sampleRateOut
, drwav_uint64
* totalFrameCountOut
, const drwav_allocation_callbacks
* pAllocationCallbacks
);
1241 DRWAV_API drwav_int32
* drwav_open_and_read_pcm_frames_s32(drwav_read_proc onRead
, drwav_seek_proc onSeek
, void* pUserData
, unsigned int* channelsOut
, unsigned int* sampleRateOut
, drwav_uint64
* totalFrameCountOut
, const drwav_allocation_callbacks
* pAllocationCallbacks
);
1242 #ifndef DR_WAV_NO_STDIO
1244 Opens and decodes an entire wav file in a single operation.
1246 The return value is a heap-allocated buffer containing the audio data. Use drwav_free() to free the buffer.
1248 DRWAV_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
);
1249 DRWAV_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
);
1250 DRWAV_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
);
1251 DRWAV_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
);
1252 DRWAV_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
);
1253 DRWAV_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
);
1256 Opens and decodes an entire wav file from a block of memory in a single operation.
1258 The return value is a heap-allocated buffer containing the audio data. Use drwav_free() to free the buffer.
1260 DRWAV_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
);
1261 DRWAV_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
);
1262 DRWAV_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
);
1265 /* Frees data that was allocated internally by dr_wav. */
1266 DRWAV_API
void drwav_free(void* p
, const drwav_allocation_callbacks
* pAllocationCallbacks
);
1268 /* Converts bytes from a wav stream to a sized type of native endian. */
1269 DRWAV_API drwav_uint16
drwav_bytes_to_u16(const drwav_uint8
* data
);
1270 DRWAV_API drwav_int16
drwav_bytes_to_s16(const drwav_uint8
* data
);
1271 DRWAV_API drwav_uint32
drwav_bytes_to_u32(const drwav_uint8
* data
);
1272 DRWAV_API drwav_int32
drwav_bytes_to_s32(const drwav_uint8
* data
);
1273 DRWAV_API drwav_uint64
drwav_bytes_to_u64(const drwav_uint8
* data
);
1274 DRWAV_API drwav_int64
drwav_bytes_to_s64(const drwav_uint8
* data
);
1275 DRWAV_API
float drwav_bytes_to_f32(const drwav_uint8
* data
);
1277 /* Compares a GUID for the purpose of checking the type of a Wave64 chunk. */
1278 DRWAV_API drwav_bool32
drwav_guid_equal(const drwav_uint8 a
[16], const drwav_uint8 b
[16]);
1280 /* Compares a four-character-code for the purpose of checking the type of a RIFF chunk. */
1281 DRWAV_API drwav_bool32
drwav_fourcc_equal(const drwav_uint8
* a
, const char* b
);
1286 #endif /* dr_wav_h */
1289 /************************************************************************************************************************************************************
1290 ************************************************************************************************************************************************************
1294 ************************************************************************************************************************************************************
1295 ************************************************************************************************************************************************************/
1296 #if defined(DR_WAV_IMPLEMENTATION) || defined(DRWAV_IMPLEMENTATION)
1301 #include <string.h> /* For memcpy(), memset() */
1302 #include <limits.h> /* For INT_MAX */
1304 #ifndef DR_WAV_NO_STDIO
1309 /* Standard library stuff. */
1310 #ifndef DRWAV_ASSERT
1312 #define DRWAV_ASSERT(expression) assert(expression)
1314 #ifndef DRWAV_MALLOC
1315 #define DRWAV_MALLOC(sz) malloc((sz))
1317 #ifndef DRWAV_REALLOC
1318 #define DRWAV_REALLOC(p, sz) realloc((p), (sz))
1321 #define DRWAV_FREE(p) free((p))
1323 #ifndef DRWAV_COPY_MEMORY
1324 #define DRWAV_COPY_MEMORY(dst, src, sz) memcpy((dst), (src), (sz))
1326 #ifndef DRWAV_ZERO_MEMORY
1327 #define DRWAV_ZERO_MEMORY(p, sz) memset((p), 0, (sz))
1329 #ifndef DRWAV_ZERO_OBJECT
1330 #define DRWAV_ZERO_OBJECT(p) DRWAV_ZERO_MEMORY((p), sizeof(*p))
1333 #define drwav_countof(x) (sizeof(x) / sizeof(x[0]))
1334 #define drwav_align(x, a) ((((x) + (a) - 1) / (a)) * (a))
1335 #define drwav_min(a, b) (((a) < (b)) ? (a) : (b))
1336 #define drwav_max(a, b) (((a) > (b)) ? (a) : (b))
1337 #define drwav_clamp(x, lo, hi) (drwav_max((lo), drwav_min((hi), (x))))
1339 #define DRWAV_MAX_SIMD_VECTOR_SIZE 64 /* 64 for AVX-512 in the future. */
1341 /* CPU architecture. */
1342 #if defined(__x86_64__) || defined(_M_X64)
1344 #elif defined(__i386) || defined(_M_IX86)
1346 #elif defined(__arm__) || defined(_M_ARM)
1351 #define DRWAV_INLINE __forceinline
1352 #elif defined(__GNUC__)
1354 I've had a bug report where GCC is emitting warnings about functions possibly not being inlineable. This warning happens when
1355 the __attribute__((always_inline)) attribute is defined without an "inline" statement. I think therefore there must be some
1356 case where "__inline__" is not always defined, thus the compiler emitting these warnings. When using -std=c89 or -ansi on the
1357 command line, we cannot use the "inline" keyword and instead need to use "__inline__". In an attempt to work around this issue
1358 I am using "__inline__" only when we're compiling in strict ANSI mode.
1360 #if defined(__STRICT_ANSI__)
1361 #define DRWAV_INLINE __inline__ __attribute__((always_inline))
1363 #define DRWAV_INLINE inline __attribute__((always_inline))
1365 #elif defined(__WATCOMC__)
1366 #define DRWAV_INLINE __inline
1368 #define DRWAV_INLINE
1371 #if defined(SIZE_MAX)
1372 #define DRWAV_SIZE_MAX SIZE_MAX
1374 #if defined(_WIN64) || defined(_LP64) || defined(__LP64__)
1375 #define DRWAV_SIZE_MAX ((drwav_uint64)0xFFFFFFFFFFFFFFFF)
1377 #define DRWAV_SIZE_MAX 0xFFFFFFFF
1381 #if defined(_MSC_VER) && _MSC_VER >= 1400
1382 #define DRWAV_HAS_BYTESWAP16_INTRINSIC
1383 #define DRWAV_HAS_BYTESWAP32_INTRINSIC
1384 #define DRWAV_HAS_BYTESWAP64_INTRINSIC
1385 #elif defined(__clang__)
1386 #if defined(__has_builtin)
1387 #if __has_builtin(__builtin_bswap16)
1388 #define DRWAV_HAS_BYTESWAP16_INTRINSIC
1390 #if __has_builtin(__builtin_bswap32)
1391 #define DRWAV_HAS_BYTESWAP32_INTRINSIC
1393 #if __has_builtin(__builtin_bswap64)
1394 #define DRWAV_HAS_BYTESWAP64_INTRINSIC
1397 #elif defined(__GNUC__)
1398 #if ((__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 3))
1399 #define DRWAV_HAS_BYTESWAP32_INTRINSIC
1400 #define DRWAV_HAS_BYTESWAP64_INTRINSIC
1402 #if ((__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8))
1403 #define DRWAV_HAS_BYTESWAP16_INTRINSIC
1407 DRWAV_API
void drwav_version(drwav_uint32
* pMajor
, drwav_uint32
* pMinor
, drwav_uint32
* pRevision
)
1410 *pMajor
= DRWAV_VERSION_MAJOR
;
1414 *pMinor
= DRWAV_VERSION_MINOR
;
1418 *pRevision
= DRWAV_VERSION_REVISION
;
1422 DRWAV_API
const char* drwav_version_string(void)
1424 return DRWAV_VERSION_STRING
;
1428 These limits are used for basic validation when initializing the decoder. If you exceed these limits, first of all: what on Earth are
1429 you doing?! (Let me know, I'd be curious!) Second, you can adjust these by #define-ing them before the dr_wav implementation.
1431 #ifndef DRWAV_MAX_SAMPLE_RATE
1432 #define DRWAV_MAX_SAMPLE_RATE 384000
1434 #ifndef DRWAV_MAX_CHANNELS
1435 #define DRWAV_MAX_CHANNELS 256
1437 #ifndef DRWAV_MAX_BITS_PER_SAMPLE
1438 #define DRWAV_MAX_BITS_PER_SAMPLE 64
1441 static const drwav_uint8 drwavGUID_W64_RIFF
[16] = {0x72,0x69,0x66,0x66, 0x2E,0x91, 0xCF,0x11, 0xA5,0xD6, 0x28,0xDB,0x04,0xC1,0x00,0x00}; /* 66666972-912E-11CF-A5D6-28DB04C10000 */
1442 static const drwav_uint8 drwavGUID_W64_WAVE
[16] = {0x77,0x61,0x76,0x65, 0xF3,0xAC, 0xD3,0x11, 0x8C,0xD1, 0x00,0xC0,0x4F,0x8E,0xDB,0x8A}; /* 65766177-ACF3-11D3-8CD1-00C04F8EDB8A */
1443 /*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 */
1444 static const drwav_uint8 drwavGUID_W64_FMT
[16] = {0x66,0x6D,0x74,0x20, 0xF3,0xAC, 0xD3,0x11, 0x8C,0xD1, 0x00,0xC0,0x4F,0x8E,0xDB,0x8A}; /* 20746D66-ACF3-11D3-8CD1-00C04F8EDB8A */
1445 static const drwav_uint8 drwavGUID_W64_FACT
[16] = {0x66,0x61,0x63,0x74, 0xF3,0xAC, 0xD3,0x11, 0x8C,0xD1, 0x00,0xC0,0x4F,0x8E,0xDB,0x8A}; /* 74636166-ACF3-11D3-8CD1-00C04F8EDB8A */
1446 static const drwav_uint8 drwavGUID_W64_DATA
[16] = {0x64,0x61,0x74,0x61, 0xF3,0xAC, 0xD3,0x11, 0x8C,0xD1, 0x00,0xC0,0x4F,0x8E,0xDB,0x8A}; /* 61746164-ACF3-11D3-8CD1-00C04F8EDB8A */
1447 /*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 */
1450 static DRWAV_INLINE
int drwav__is_little_endian(void)
1452 #if defined(DRWAV_X86) || defined(DRWAV_X64)
1454 #elif defined(__BYTE_ORDER) && defined(__LITTLE_ENDIAN) && __BYTE_ORDER == __LITTLE_ENDIAN
1458 return (*(char*)&n
) == 1;
1463 static DRWAV_INLINE
void drwav_bytes_to_guid(const drwav_uint8
* data
, drwav_uint8
* guid
)
1466 for (i
= 0; i
< 16; ++i
) {
1472 static DRWAV_INLINE drwav_uint16
drwav__bswap16(drwav_uint16 n
)
1474 #ifdef DRWAV_HAS_BYTESWAP16_INTRINSIC
1475 #if defined(_MSC_VER)
1476 return _byteswap_ushort(n
);
1477 #elif defined(__GNUC__) || defined(__clang__)
1478 return __builtin_bswap16(n
);
1480 #error "This compiler does not support the byte swap intrinsic."
1483 return ((n
& 0xFF00) >> 8) |
1484 ((n
& 0x00FF) << 8);
1488 static DRWAV_INLINE drwav_uint32
drwav__bswap32(drwav_uint32 n
)
1490 #ifdef DRWAV_HAS_BYTESWAP32_INTRINSIC
1491 #if defined(_MSC_VER)
1492 return _byteswap_ulong(n
);
1493 #elif defined(__GNUC__) || defined(__clang__)
1494 #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. */
1495 /* Inline assembly optimized implementation for ARM. In my testing, GCC does not generate optimized code with __builtin_bswap32(). */
1497 __asm__
__volatile__ (
1498 #if defined(DRWAV_64BIT)
1499 "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! */
1501 "rev %[out], %[in]" : [out
]"=r"(r
) : [in
]"r"(n
)
1506 return __builtin_bswap32(n
);
1509 #error "This compiler does not support the byte swap intrinsic."
1512 return ((n
& 0xFF000000) >> 24) |
1513 ((n
& 0x00FF0000) >> 8) |
1514 ((n
& 0x0000FF00) << 8) |
1515 ((n
& 0x000000FF) << 24);
1519 static DRWAV_INLINE drwav_uint64
drwav__bswap64(drwav_uint64 n
)
1521 #ifdef DRWAV_HAS_BYTESWAP64_INTRINSIC
1522 #if defined(_MSC_VER)
1523 return _byteswap_uint64(n
);
1524 #elif defined(__GNUC__) || defined(__clang__)
1525 return __builtin_bswap64(n
);
1527 #error "This compiler does not support the byte swap intrinsic."
1530 /* Weird "<< 32" bitshift is required for C89 because it doesn't support 64-bit constants. Should be optimized out by a good compiler. */
1531 return ((n
& ((drwav_uint64
)0xFF000000 << 32)) >> 56) |
1532 ((n
& ((drwav_uint64
)0x00FF0000 << 32)) >> 40) |
1533 ((n
& ((drwav_uint64
)0x0000FF00 << 32)) >> 24) |
1534 ((n
& ((drwav_uint64
)0x000000FF << 32)) >> 8) |
1535 ((n
& ((drwav_uint64
)0xFF000000 )) << 8) |
1536 ((n
& ((drwav_uint64
)0x00FF0000 )) << 24) |
1537 ((n
& ((drwav_uint64
)0x0000FF00 )) << 40) |
1538 ((n
& ((drwav_uint64
)0x000000FF )) << 56);
1543 static DRWAV_INLINE drwav_int16
drwav__bswap_s16(drwav_int16 n
)
1545 return (drwav_int16
)drwav__bswap16((drwav_uint16
)n
);
1548 static DRWAV_INLINE
void drwav__bswap_samples_s16(drwav_int16
* pSamples
, drwav_uint64 sampleCount
)
1550 drwav_uint64 iSample
;
1551 for (iSample
= 0; iSample
< sampleCount
; iSample
+= 1) {
1552 pSamples
[iSample
] = drwav__bswap_s16(pSamples
[iSample
]);
1557 static DRWAV_INLINE
void drwav__bswap_s24(drwav_uint8
* p
)
1565 static DRWAV_INLINE
void drwav__bswap_samples_s24(drwav_uint8
* pSamples
, drwav_uint64 sampleCount
)
1567 drwav_uint64 iSample
;
1568 for (iSample
= 0; iSample
< sampleCount
; iSample
+= 1) {
1569 drwav_uint8
* pSample
= pSamples
+ (iSample
*3);
1570 drwav__bswap_s24(pSample
);
1575 static DRWAV_INLINE drwav_int32
drwav__bswap_s32(drwav_int32 n
)
1577 return (drwav_int32
)drwav__bswap32((drwav_uint32
)n
);
1580 static DRWAV_INLINE
void drwav__bswap_samples_s32(drwav_int32
* pSamples
, drwav_uint64 sampleCount
)
1582 drwav_uint64 iSample
;
1583 for (iSample
= 0; iSample
< sampleCount
; iSample
+= 1) {
1584 pSamples
[iSample
] = drwav__bswap_s32(pSamples
[iSample
]);
1589 static DRWAV_INLINE
float drwav__bswap_f32(float n
)
1596 x
.i
= drwav__bswap32(x
.i
);
1601 static DRWAV_INLINE
void drwav__bswap_samples_f32(float* pSamples
, drwav_uint64 sampleCount
)
1603 drwav_uint64 iSample
;
1604 for (iSample
= 0; iSample
< sampleCount
; iSample
+= 1) {
1605 pSamples
[iSample
] = drwav__bswap_f32(pSamples
[iSample
]);
1610 static DRWAV_INLINE
double drwav__bswap_f64(double n
)
1617 x
.i
= drwav__bswap64(x
.i
);
1622 static DRWAV_INLINE
void drwav__bswap_samples_f64(double* pSamples
, drwav_uint64 sampleCount
)
1624 drwav_uint64 iSample
;
1625 for (iSample
= 0; iSample
< sampleCount
; iSample
+= 1) {
1626 pSamples
[iSample
] = drwav__bswap_f64(pSamples
[iSample
]);
1631 static DRWAV_INLINE
void drwav__bswap_samples_pcm(void* pSamples
, drwav_uint64 sampleCount
, drwav_uint32 bytesPerSample
)
1633 /* Assumes integer PCM. Floating point PCM is done in drwav__bswap_samples_ieee(). */
1634 switch (bytesPerSample
)
1636 case 2: /* s16, s12 (loosely packed) */
1638 drwav__bswap_samples_s16((drwav_int16
*)pSamples
, sampleCount
);
1642 drwav__bswap_samples_s24((drwav_uint8
*)pSamples
, sampleCount
);
1646 drwav__bswap_samples_s32((drwav_int32
*)pSamples
, sampleCount
);
1650 /* Unsupported format. */
1651 DRWAV_ASSERT(DRWAV_FALSE
);
1656 static DRWAV_INLINE
void drwav__bswap_samples_ieee(void* pSamples
, drwav_uint64 sampleCount
, drwav_uint32 bytesPerSample
)
1658 switch (bytesPerSample
)
1660 #if 0 /* Contributions welcome for f16 support. */
1663 drwav__bswap_samples_f16((drwav_float16
*)pSamples
, sampleCount
);
1668 drwav__bswap_samples_f32((float*)pSamples
, sampleCount
);
1672 drwav__bswap_samples_f64((double*)pSamples
, sampleCount
);
1676 /* Unsupported format. */
1677 DRWAV_ASSERT(DRWAV_FALSE
);
1682 static DRWAV_INLINE
void drwav__bswap_samples(void* pSamples
, drwav_uint64 sampleCount
, drwav_uint32 bytesPerSample
, drwav_uint16 format
)
1686 case DR_WAVE_FORMAT_PCM
:
1688 drwav__bswap_samples_pcm(pSamples
, sampleCount
, bytesPerSample
);
1691 case DR_WAVE_FORMAT_IEEE_FLOAT
:
1693 drwav__bswap_samples_ieee(pSamples
, sampleCount
, bytesPerSample
);
1696 case DR_WAVE_FORMAT_ALAW
:
1697 case DR_WAVE_FORMAT_MULAW
:
1699 drwav__bswap_samples_s16((drwav_int16
*)pSamples
, sampleCount
);
1702 case DR_WAVE_FORMAT_ADPCM
:
1703 case DR_WAVE_FORMAT_DVI_ADPCM
:
1706 /* Unsupported format. */
1707 DRWAV_ASSERT(DRWAV_FALSE
);
1713 DRWAV_PRIVATE
void* drwav__malloc_default(size_t sz
, void* pUserData
)
1716 return DRWAV_MALLOC(sz
);
1719 DRWAV_PRIVATE
void* drwav__realloc_default(void* p
, size_t sz
, void* pUserData
)
1722 return DRWAV_REALLOC(p
, sz
);
1725 DRWAV_PRIVATE
void drwav__free_default(void* p
, void* pUserData
)
1732 DRWAV_PRIVATE
void* drwav__malloc_from_callbacks(size_t sz
, const drwav_allocation_callbacks
* pAllocationCallbacks
)
1734 if (pAllocationCallbacks
== NULL
) {
1738 if (pAllocationCallbacks
->onMalloc
!= NULL
) {
1739 return pAllocationCallbacks
->onMalloc(sz
, pAllocationCallbacks
->pUserData
);
1742 /* Try using realloc(). */
1743 if (pAllocationCallbacks
->onRealloc
!= NULL
) {
1744 return pAllocationCallbacks
->onRealloc(NULL
, sz
, pAllocationCallbacks
->pUserData
);
1750 DRWAV_PRIVATE
void* drwav__realloc_from_callbacks(void* p
, size_t szNew
, size_t szOld
, const drwav_allocation_callbacks
* pAllocationCallbacks
)
1752 if (pAllocationCallbacks
== NULL
) {
1756 if (pAllocationCallbacks
->onRealloc
!= NULL
) {
1757 return pAllocationCallbacks
->onRealloc(p
, szNew
, pAllocationCallbacks
->pUserData
);
1760 /* Try emulating realloc() in terms of malloc()/free(). */
1761 if (pAllocationCallbacks
->onMalloc
!= NULL
&& pAllocationCallbacks
->onFree
!= NULL
) {
1764 p2
= pAllocationCallbacks
->onMalloc(szNew
, pAllocationCallbacks
->pUserData
);
1770 DRWAV_COPY_MEMORY(p2
, p
, szOld
);
1771 pAllocationCallbacks
->onFree(p
, pAllocationCallbacks
->pUserData
);
1780 DRWAV_PRIVATE
void drwav__free_from_callbacks(void* p
, const drwav_allocation_callbacks
* pAllocationCallbacks
)
1782 if (p
== NULL
|| pAllocationCallbacks
== NULL
) {
1786 if (pAllocationCallbacks
->onFree
!= NULL
) {
1787 pAllocationCallbacks
->onFree(p
, pAllocationCallbacks
->pUserData
);
1792 DRWAV_PRIVATE drwav_allocation_callbacks
drwav_copy_allocation_callbacks_or_defaults(const drwav_allocation_callbacks
* pAllocationCallbacks
)
1794 if (pAllocationCallbacks
!= NULL
) {
1796 return *pAllocationCallbacks
;
1799 drwav_allocation_callbacks allocationCallbacks
;
1800 allocationCallbacks
.pUserData
= NULL
;
1801 allocationCallbacks
.onMalloc
= drwav__malloc_default
;
1802 allocationCallbacks
.onRealloc
= drwav__realloc_default
;
1803 allocationCallbacks
.onFree
= drwav__free_default
;
1804 return allocationCallbacks
;
1809 static DRWAV_INLINE drwav_bool32
drwav__is_compressed_format_tag(drwav_uint16 formatTag
)
1812 formatTag
== DR_WAVE_FORMAT_ADPCM
||
1813 formatTag
== DR_WAVE_FORMAT_DVI_ADPCM
;
1816 DRWAV_PRIVATE
unsigned int drwav__chunk_padding_size_riff(drwav_uint64 chunkSize
)
1818 return (unsigned int)(chunkSize
% 2);
1821 DRWAV_PRIVATE
unsigned int drwav__chunk_padding_size_w64(drwav_uint64 chunkSize
)
1823 return (unsigned int)(chunkSize
% 8);
1826 DRWAV_PRIVATE drwav_uint64
drwav_read_pcm_frames_s16__msadpcm(drwav
* pWav
, drwav_uint64 samplesToRead
, drwav_int16
* pBufferOut
);
1827 DRWAV_PRIVATE drwav_uint64
drwav_read_pcm_frames_s16__ima(drwav
* pWav
, drwav_uint64 samplesToRead
, drwav_int16
* pBufferOut
);
1828 DRWAV_PRIVATE drwav_bool32
drwav_init_write__internal(drwav
* pWav
, const drwav_data_format
* pFormat
, drwav_uint64 totalSampleCount
);
1830 DRWAV_PRIVATE drwav_result
drwav__read_chunk_header(drwav_read_proc onRead
, void* pUserData
, drwav_container container
, drwav_uint64
* pRunningBytesReadOut
, drwav_chunk_header
* pHeaderOut
)
1832 if (container
== drwav_container_riff
|| container
== drwav_container_rf64
) {
1833 drwav_uint8 sizeInBytes
[4];
1835 if (onRead(pUserData
, pHeaderOut
->id
.fourcc
, 4) != 4) {
1836 return DRWAV_AT_END
;
1839 if (onRead(pUserData
, sizeInBytes
, 4) != 4) {
1840 return DRWAV_INVALID_FILE
;
1843 pHeaderOut
->sizeInBytes
= drwav_bytes_to_u32(sizeInBytes
);
1844 pHeaderOut
->paddingSize
= drwav__chunk_padding_size_riff(pHeaderOut
->sizeInBytes
);
1845 *pRunningBytesReadOut
+= 8;
1847 drwav_uint8 sizeInBytes
[8];
1849 if (onRead(pUserData
, pHeaderOut
->id
.guid
, 16) != 16) {
1850 return DRWAV_AT_END
;
1853 if (onRead(pUserData
, sizeInBytes
, 8) != 8) {
1854 return DRWAV_INVALID_FILE
;
1857 pHeaderOut
->sizeInBytes
= drwav_bytes_to_u64(sizeInBytes
) - 24; /* <-- Subtract 24 because w64 includes the size of the header. */
1858 pHeaderOut
->paddingSize
= drwav__chunk_padding_size_w64(pHeaderOut
->sizeInBytes
);
1859 *pRunningBytesReadOut
+= 24;
1862 return DRWAV_SUCCESS
;
1865 DRWAV_PRIVATE drwav_bool32
drwav__seek_forward(drwav_seek_proc onSeek
, drwav_uint64 offset
, void* pUserData
)
1867 drwav_uint64 bytesRemainingToSeek
= offset
;
1868 while (bytesRemainingToSeek
> 0) {
1869 if (bytesRemainingToSeek
> 0x7FFFFFFF) {
1870 if (!onSeek(pUserData
, 0x7FFFFFFF, drwav_seek_origin_current
)) {
1873 bytesRemainingToSeek
-= 0x7FFFFFFF;
1875 if (!onSeek(pUserData
, (int)bytesRemainingToSeek
, drwav_seek_origin_current
)) {
1878 bytesRemainingToSeek
= 0;
1885 DRWAV_PRIVATE drwav_bool32
drwav__seek_from_start(drwav_seek_proc onSeek
, drwav_uint64 offset
, void* pUserData
)
1887 if (offset
<= 0x7FFFFFFF) {
1888 return onSeek(pUserData
, (int)offset
, drwav_seek_origin_start
);
1891 /* Larger than 32-bit seek. */
1892 if (!onSeek(pUserData
, 0x7FFFFFFF, drwav_seek_origin_start
)) {
1895 offset
-= 0x7FFFFFFF;
1898 if (offset
<= 0x7FFFFFFF) {
1899 return onSeek(pUserData
, (int)offset
, drwav_seek_origin_current
);
1902 if (!onSeek(pUserData
, 0x7FFFFFFF, drwav_seek_origin_current
)) {
1905 offset
-= 0x7FFFFFFF;
1908 /* Should never get here. */
1909 /*return DRWAV_TRUE; */
1913 DRWAV_PRIVATE drwav_bool32
drwav__read_fmt(drwav_read_proc onRead
, drwav_seek_proc onSeek
, void* pUserData
, drwav_container container
, drwav_uint64
* pRunningBytesReadOut
, drwav_fmt
* fmtOut
)
1915 drwav_chunk_header header
;
1916 drwav_uint8 fmt
[16];
1918 if (drwav__read_chunk_header(onRead
, pUserData
, container
, pRunningBytesReadOut
, &header
) != DRWAV_SUCCESS
) {
1923 /* Skip non-fmt chunks. */
1924 while (((container
== drwav_container_riff
|| container
== drwav_container_rf64
) && !drwav_fourcc_equal(header
.id
.fourcc
, "fmt ")) || (container
== drwav_container_w64
&& !drwav_guid_equal(header
.id
.guid
, drwavGUID_W64_FMT
))) {
1925 if (!drwav__seek_forward(onSeek
, header
.sizeInBytes
+ header
.paddingSize
, pUserData
)) {
1928 *pRunningBytesReadOut
+= header
.sizeInBytes
+ header
.paddingSize
;
1930 /* Try the next header. */
1931 if (drwav__read_chunk_header(onRead
, pUserData
, container
, pRunningBytesReadOut
, &header
) != DRWAV_SUCCESS
) {
1938 if (container
== drwav_container_riff
|| container
== drwav_container_rf64
) {
1939 if (!drwav_fourcc_equal(header
.id
.fourcc
, "fmt ")) {
1943 if (!drwav_guid_equal(header
.id
.guid
, drwavGUID_W64_FMT
)) {
1949 if (onRead(pUserData
, fmt
, sizeof(fmt
)) != sizeof(fmt
)) {
1952 *pRunningBytesReadOut
+= sizeof(fmt
);
1954 fmtOut
->formatTag
= drwav_bytes_to_u16(fmt
+ 0);
1955 fmtOut
->channels
= drwav_bytes_to_u16(fmt
+ 2);
1956 fmtOut
->sampleRate
= drwav_bytes_to_u32(fmt
+ 4);
1957 fmtOut
->avgBytesPerSec
= drwav_bytes_to_u32(fmt
+ 8);
1958 fmtOut
->blockAlign
= drwav_bytes_to_u16(fmt
+ 12);
1959 fmtOut
->bitsPerSample
= drwav_bytes_to_u16(fmt
+ 14);
1961 fmtOut
->extendedSize
= 0;
1962 fmtOut
->validBitsPerSample
= 0;
1963 fmtOut
->channelMask
= 0;
1964 memset(fmtOut
->subFormat
, 0, sizeof(fmtOut
->subFormat
));
1966 if (header
.sizeInBytes
> 16) {
1967 drwav_uint8 fmt_cbSize
[2];
1968 int bytesReadSoFar
= 0;
1970 if (onRead(pUserData
, fmt_cbSize
, sizeof(fmt_cbSize
)) != sizeof(fmt_cbSize
)) {
1971 return DRWAV_FALSE
; /* Expecting more data. */
1973 *pRunningBytesReadOut
+= sizeof(fmt_cbSize
);
1975 bytesReadSoFar
= 18;
1977 fmtOut
->extendedSize
= drwav_bytes_to_u16(fmt_cbSize
);
1978 if (fmtOut
->extendedSize
> 0) {
1979 /* Simple validation. */
1980 if (fmtOut
->formatTag
== DR_WAVE_FORMAT_EXTENSIBLE
) {
1981 if (fmtOut
->extendedSize
!= 22) {
1986 if (fmtOut
->formatTag
== DR_WAVE_FORMAT_EXTENSIBLE
) {
1987 drwav_uint8 fmtext
[22];
1988 if (onRead(pUserData
, fmtext
, fmtOut
->extendedSize
) != fmtOut
->extendedSize
) {
1989 return DRWAV_FALSE
; /* Expecting more data. */
1992 fmtOut
->validBitsPerSample
= drwav_bytes_to_u16(fmtext
+ 0);
1993 fmtOut
->channelMask
= drwav_bytes_to_u32(fmtext
+ 2);
1994 drwav_bytes_to_guid(fmtext
+ 6, fmtOut
->subFormat
);
1996 if (!onSeek(pUserData
, fmtOut
->extendedSize
, drwav_seek_origin_current
)) {
2000 *pRunningBytesReadOut
+= fmtOut
->extendedSize
;
2002 bytesReadSoFar
+= fmtOut
->extendedSize
;
2005 /* Seek past any leftover bytes. For w64 the leftover will be defined based on the chunk size. */
2006 if (!onSeek(pUserData
, (int)(header
.sizeInBytes
- bytesReadSoFar
), drwav_seek_origin_current
)) {
2009 *pRunningBytesReadOut
+= (header
.sizeInBytes
- bytesReadSoFar
);
2012 if (header
.paddingSize
> 0) {
2013 if (!onSeek(pUserData
, header
.paddingSize
, drwav_seek_origin_current
)) {
2016 *pRunningBytesReadOut
+= header
.paddingSize
;
2023 DRWAV_PRIVATE
size_t drwav__on_read(drwav_read_proc onRead
, void* pUserData
, void* pBufferOut
, size_t bytesToRead
, drwav_uint64
* pCursor
)
2027 DRWAV_ASSERT(onRead
!= NULL
);
2028 DRWAV_ASSERT(pCursor
!= NULL
);
2030 bytesRead
= onRead(pUserData
, pBufferOut
, bytesToRead
);
2031 *pCursor
+= bytesRead
;
2036 DRWAV_PRIVATE drwav_bool32
drwav__on_seek(drwav_seek_proc onSeek
, void* pUserData
, int offset
, drwav_seek_origin origin
, drwav_uint64
* pCursor
)
2038 DRWAV_ASSERT(onSeek
!= NULL
);
2039 DRWAV_ASSERT(pCursor
!= NULL
);
2041 if (!onSeek(pUserData
, offset
, origin
)) {
2045 if (origin
== drwav_seek_origin_start
) {
2056 #define DRWAV_SMPL_BYTES 36
2057 #define DRWAV_SMPL_LOOP_BYTES 24
2058 #define DRWAV_INST_BYTES 7
2059 #define DRWAV_ACID_BYTES 24
2060 #define DRWAV_CUE_BYTES 4
2061 #define DRWAV_BEXT_BYTES 602
2062 #define DRWAV_BEXT_DESCRIPTION_BYTES 256
2063 #define DRWAV_BEXT_ORIGINATOR_NAME_BYTES 32
2064 #define DRWAV_BEXT_ORIGINATOR_REF_BYTES 32
2065 #define DRWAV_BEXT_RESERVED_BYTES 180
2066 #define DRWAV_BEXT_UMID_BYTES 64
2067 #define DRWAV_CUE_POINT_BYTES 24
2068 #define DRWAV_LIST_LABEL_OR_NOTE_BYTES 4
2069 #define DRWAV_LIST_LABELLED_TEXT_BYTES 20
2071 #define DRWAV_METADATA_ALIGNMENT 8
2075 drwav__metadata_parser_stage_count
,
2076 drwav__metadata_parser_stage_read
2077 } drwav__metadata_parser_stage
;
2081 drwav_read_proc onRead
;
2082 drwav_seek_proc onSeek
;
2083 void *pReadSeekUserData
;
2084 drwav__metadata_parser_stage stage
;
2085 drwav_metadata
*pMetadata
;
2086 drwav_uint32 metadataCount
;
2088 drwav_uint8
*pDataCursor
;
2089 drwav_uint64 metadataCursor
;
2090 drwav_uint64 extraCapacity
;
2091 } drwav__metadata_parser
;
2093 DRWAV_PRIVATE
size_t drwav__metadata_memory_capacity(drwav__metadata_parser
* pParser
)
2095 drwav_uint64 cap
= sizeof(drwav_metadata
) * (drwav_uint64
)pParser
->metadataCount
+ pParser
->extraCapacity
;
2096 if (cap
> DRWAV_SIZE_MAX
) {
2097 return 0; /* Too big. */
2100 return (size_t)cap
; /* Safe cast thanks to the check above. */
2103 DRWAV_PRIVATE drwav_uint8
* drwav__metadata_get_memory(drwav__metadata_parser
* pParser
, size_t size
, size_t align
)
2105 drwav_uint8
* pResult
;
2108 drwav_uintptr modulo
= (drwav_uintptr
)pParser
->pDataCursor
% align
;
2110 pParser
->pDataCursor
+= align
- modulo
;
2114 pResult
= pParser
->pDataCursor
;
2117 Getting to the point where this function is called means there should always be memory
2118 available. Out of memory checks should have been done at an earlier stage.
2120 DRWAV_ASSERT((pResult
+ size
) <= (pParser
->pData
+ drwav__metadata_memory_capacity(pParser
)));
2122 pParser
->pDataCursor
+= size
;
2126 DRWAV_PRIVATE
void drwav__metadata_request_extra_memory_for_stage_2(drwav__metadata_parser
* pParser
, size_t bytes
, size_t align
)
2128 size_t extra
= bytes
+ (align
? (align
- 1) : 0);
2129 pParser
->extraCapacity
+= extra
;
2132 DRWAV_PRIVATE drwav_result
drwav__metadata_alloc(drwav__metadata_parser
* pParser
, drwav_allocation_callbacks
* pAllocationCallbacks
)
2134 if (pParser
->extraCapacity
!= 0 || pParser
->metadataCount
!= 0) {
2135 free(pParser
->pData
);
2137 pParser
->pData
= (drwav_uint8
*)pAllocationCallbacks
->onMalloc(drwav__metadata_memory_capacity(pParser
), pAllocationCallbacks
->pUserData
);
2138 pParser
->pDataCursor
= pParser
->pData
;
2140 if (pParser
->pData
== NULL
) {
2141 return DRWAV_OUT_OF_MEMORY
;
2145 We don't need to worry about specifying an alignment here because malloc always returns something
2146 of suitable alignment. This also means than pParser->pMetadata is all that we need to store in order
2147 for us to free when we are done.
2149 pParser
->pMetadata
= (drwav_metadata
*)drwav__metadata_get_memory(pParser
, sizeof(drwav_metadata
) * pParser
->metadataCount
, 1);
2150 pParser
->metadataCursor
= 0;
2153 return DRWAV_SUCCESS
;
2156 DRWAV_PRIVATE
size_t drwav__metadata_parser_read(drwav__metadata_parser
* pParser
, void* pBufferOut
, size_t bytesToRead
, drwav_uint64
* pCursor
)
2158 if (pCursor
!= NULL
) {
2159 return drwav__on_read(pParser
->onRead
, pParser
->pReadSeekUserData
, pBufferOut
, bytesToRead
, pCursor
);
2161 return pParser
->onRead(pParser
->pReadSeekUserData
, pBufferOut
, bytesToRead
);
2165 DRWAV_PRIVATE drwav_uint64
drwav__read_smpl_to_metadata_obj(drwav__metadata_parser
* pParser
, drwav_metadata
* pMetadata
)
2167 drwav_uint8 smplHeaderData
[DRWAV_SMPL_BYTES
];
2168 drwav_uint64 totalBytesRead
= 0;
2169 size_t bytesJustRead
= drwav__metadata_parser_read(pParser
, smplHeaderData
, sizeof(smplHeaderData
), &totalBytesRead
);
2171 DRWAV_ASSERT(pParser
->stage
== drwav__metadata_parser_stage_read
);
2173 if (bytesJustRead
== sizeof(smplHeaderData
)) {
2174 drwav_uint32 iSampleLoop
;
2176 pMetadata
->type
= drwav_metadata_type_smpl
;
2177 pMetadata
->data
.smpl
.manufacturerId
= drwav_bytes_to_u32(smplHeaderData
+ 0);
2178 pMetadata
->data
.smpl
.productId
= drwav_bytes_to_u32(smplHeaderData
+ 4);
2179 pMetadata
->data
.smpl
.samplePeriodNanoseconds
= drwav_bytes_to_u32(smplHeaderData
+ 8);
2180 pMetadata
->data
.smpl
.midiUnityNote
= drwav_bytes_to_u32(smplHeaderData
+ 12);
2181 pMetadata
->data
.smpl
.midiPitchFraction
= drwav_bytes_to_u32(smplHeaderData
+ 16);
2182 pMetadata
->data
.smpl
.smpteFormat
= drwav_bytes_to_u32(smplHeaderData
+ 20);
2183 pMetadata
->data
.smpl
.smpteOffset
= drwav_bytes_to_u32(smplHeaderData
+ 24);
2184 pMetadata
->data
.smpl
.sampleLoopCount
= drwav_bytes_to_u32(smplHeaderData
+ 28);
2185 pMetadata
->data
.smpl
.samplerSpecificDataSizeInBytes
= drwav_bytes_to_u32(smplHeaderData
+ 32);
2186 pMetadata
->data
.smpl
.pLoops
= (drwav_smpl_loop
*)drwav__metadata_get_memory(pParser
, sizeof(drwav_smpl_loop
) * pMetadata
->data
.smpl
.sampleLoopCount
, DRWAV_METADATA_ALIGNMENT
);
2188 for (iSampleLoop
= 0; iSampleLoop
< pMetadata
->data
.smpl
.sampleLoopCount
; ++iSampleLoop
) {
2189 drwav_uint8 smplLoopData
[DRWAV_SMPL_LOOP_BYTES
];
2190 bytesJustRead
= drwav__metadata_parser_read(pParser
, smplLoopData
, sizeof(smplLoopData
), &totalBytesRead
);
2192 if (bytesJustRead
== sizeof(smplLoopData
)) {
2193 pMetadata
->data
.smpl
.pLoops
[iSampleLoop
].cuePointId
= drwav_bytes_to_u32(smplLoopData
+ 0);
2194 pMetadata
->data
.smpl
.pLoops
[iSampleLoop
].type
= drwav_bytes_to_u32(smplLoopData
+ 4);
2195 pMetadata
->data
.smpl
.pLoops
[iSampleLoop
].firstSampleByteOffset
= drwav_bytes_to_u32(smplLoopData
+ 8);
2196 pMetadata
->data
.smpl
.pLoops
[iSampleLoop
].lastSampleByteOffset
= drwav_bytes_to_u32(smplLoopData
+ 12);
2197 pMetadata
->data
.smpl
.pLoops
[iSampleLoop
].sampleFraction
= drwav_bytes_to_u32(smplLoopData
+ 16);
2198 pMetadata
->data
.smpl
.pLoops
[iSampleLoop
].playCount
= drwav_bytes_to_u32(smplLoopData
+ 20);
2204 if (pMetadata
->data
.smpl
.samplerSpecificDataSizeInBytes
> 0) {
2205 pMetadata
->data
.smpl
.pSamplerSpecificData
= drwav__metadata_get_memory(pParser
, pMetadata
->data
.smpl
.samplerSpecificDataSizeInBytes
, 1);
2206 DRWAV_ASSERT(pMetadata
->data
.smpl
.pSamplerSpecificData
!= NULL
);
2208 bytesJustRead
= drwav__metadata_parser_read(pParser
, pMetadata
->data
.smpl
.pSamplerSpecificData
, pMetadata
->data
.smpl
.samplerSpecificDataSizeInBytes
, &totalBytesRead
);
2212 return totalBytesRead
;
2215 DRWAV_PRIVATE drwav_uint64
drwav__read_cue_to_metadata_obj(drwav__metadata_parser
* pParser
, drwav_metadata
* pMetadata
)
2217 drwav_uint8 cueHeaderSectionData
[DRWAV_CUE_BYTES
];
2218 drwav_uint64 totalBytesRead
= 0;
2219 size_t bytesJustRead
= drwav__metadata_parser_read(pParser
, cueHeaderSectionData
, sizeof(cueHeaderSectionData
), &totalBytesRead
);
2221 DRWAV_ASSERT(pParser
->stage
== drwav__metadata_parser_stage_read
);
2223 if (bytesJustRead
== sizeof(cueHeaderSectionData
)) {
2224 pMetadata
->type
= drwav_metadata_type_cue
;
2225 pMetadata
->data
.cue
.cuePointCount
= drwav_bytes_to_u32(cueHeaderSectionData
);
2226 pMetadata
->data
.cue
.pCuePoints
= (drwav_cue_point
*)drwav__metadata_get_memory(pParser
, sizeof(drwav_cue_point
) * pMetadata
->data
.cue
.cuePointCount
, DRWAV_METADATA_ALIGNMENT
);
2227 DRWAV_ASSERT(pMetadata
->data
.cue
.pCuePoints
!= NULL
);
2229 if (pMetadata
->data
.cue
.cuePointCount
> 0) {
2230 drwav_uint32 iCuePoint
;
2232 for (iCuePoint
= 0; iCuePoint
< pMetadata
->data
.cue
.cuePointCount
; ++iCuePoint
) {
2233 drwav_uint8 cuePointData
[DRWAV_CUE_POINT_BYTES
];
2234 bytesJustRead
= drwav__metadata_parser_read(pParser
, cuePointData
, sizeof(cuePointData
), &totalBytesRead
);
2236 if (bytesJustRead
== sizeof(cuePointData
)) {
2237 pMetadata
->data
.cue
.pCuePoints
[iCuePoint
].id
= drwav_bytes_to_u32(cuePointData
+ 0);
2238 pMetadata
->data
.cue
.pCuePoints
[iCuePoint
].playOrderPosition
= drwav_bytes_to_u32(cuePointData
+ 4);
2239 pMetadata
->data
.cue
.pCuePoints
[iCuePoint
].dataChunkId
[0] = cuePointData
[8];
2240 pMetadata
->data
.cue
.pCuePoints
[iCuePoint
].dataChunkId
[1] = cuePointData
[9];
2241 pMetadata
->data
.cue
.pCuePoints
[iCuePoint
].dataChunkId
[2] = cuePointData
[10];
2242 pMetadata
->data
.cue
.pCuePoints
[iCuePoint
].dataChunkId
[3] = cuePointData
[11];
2243 pMetadata
->data
.cue
.pCuePoints
[iCuePoint
].chunkStart
= drwav_bytes_to_u32(cuePointData
+ 12);
2244 pMetadata
->data
.cue
.pCuePoints
[iCuePoint
].blockStart
= drwav_bytes_to_u32(cuePointData
+ 16);
2245 pMetadata
->data
.cue
.pCuePoints
[iCuePoint
].sampleByteOffset
= drwav_bytes_to_u32(cuePointData
+ 20);
2253 return totalBytesRead
;
2256 DRWAV_PRIVATE drwav_uint64
drwav__read_inst_to_metadata_obj(drwav__metadata_parser
* pParser
, drwav_metadata
* pMetadata
)
2258 drwav_uint8 instData
[DRWAV_INST_BYTES
];
2259 drwav_uint64 bytesRead
= drwav__metadata_parser_read(pParser
, instData
, sizeof(instData
), NULL
);
2261 DRWAV_ASSERT(pParser
->stage
== drwav__metadata_parser_stage_read
);
2263 if (bytesRead
== sizeof(instData
)) {
2264 pMetadata
->type
= drwav_metadata_type_inst
;
2265 pMetadata
->data
.inst
.midiUnityNote
= (drwav_int8
)instData
[0];
2266 pMetadata
->data
.inst
.fineTuneCents
= (drwav_int8
)instData
[1];
2267 pMetadata
->data
.inst
.gainDecibels
= (drwav_int8
)instData
[2];
2268 pMetadata
->data
.inst
.lowNote
= (drwav_int8
)instData
[3];
2269 pMetadata
->data
.inst
.highNote
= (drwav_int8
)instData
[4];
2270 pMetadata
->data
.inst
.lowVelocity
= (drwav_int8
)instData
[5];
2271 pMetadata
->data
.inst
.highVelocity
= (drwav_int8
)instData
[6];
2277 DRWAV_PRIVATE drwav_uint64
drwav__read_acid_to_metadata_obj(drwav__metadata_parser
* pParser
, drwav_metadata
* pMetadata
)
2279 drwav_uint8 acidData
[DRWAV_ACID_BYTES
];
2280 drwav_uint64 bytesRead
= drwav__metadata_parser_read(pParser
, acidData
, sizeof(acidData
), NULL
);
2282 DRWAV_ASSERT(pParser
->stage
== drwav__metadata_parser_stage_read
);
2284 if (bytesRead
== sizeof(acidData
)) {
2285 pMetadata
->type
= drwav_metadata_type_acid
;
2286 pMetadata
->data
.acid
.flags
= drwav_bytes_to_u32(acidData
+ 0);
2287 pMetadata
->data
.acid
.midiUnityNote
= drwav_bytes_to_u16(acidData
+ 4);
2288 pMetadata
->data
.acid
.reserved1
= drwav_bytes_to_u16(acidData
+ 6);
2289 pMetadata
->data
.acid
.reserved2
= drwav_bytes_to_f32(acidData
+ 8);
2290 pMetadata
->data
.acid
.numBeats
= drwav_bytes_to_u32(acidData
+ 12);
2291 pMetadata
->data
.acid
.meterDenominator
= drwav_bytes_to_u16(acidData
+ 16);
2292 pMetadata
->data
.acid
.meterNumerator
= drwav_bytes_to_u16(acidData
+ 18);
2293 pMetadata
->data
.acid
.tempo
= drwav_bytes_to_f32(acidData
+ 20);
2299 DRWAV_PRIVATE
size_t drwav__strlen_clamped(char* str
, size_t maxToRead
)
2303 while (*str
++ && result
< maxToRead
) {
2310 DRWAV_PRIVATE
char* drwav__metadata_copy_string(drwav__metadata_parser
* pParser
, char* str
, size_t maxToRead
)
2312 size_t len
= drwav__strlen_clamped(str
, maxToRead
);
2315 char* result
= (char*)drwav__metadata_get_memory(pParser
, len
+ 1, 1);
2316 DRWAV_ASSERT(result
!= NULL
);
2318 memcpy(result
, str
, len
);
2327 DRWAV_PRIVATE drwav_uint64
drwav__read_bext_to_metadata_obj(drwav__metadata_parser
* pParser
, drwav_metadata
* pMetadata
, drwav_uint64 chunkSize
)
2329 drwav_uint8 bextData
[DRWAV_BEXT_BYTES
];
2330 drwav_uint64 bytesRead
= drwav__metadata_parser_read(pParser
, bextData
, sizeof(bextData
), NULL
);
2332 DRWAV_ASSERT(pParser
->stage
== drwav__metadata_parser_stage_read
);
2334 if (bytesRead
== sizeof(bextData
)) {
2335 drwav_uint8
* pReadPointer
;
2336 drwav_uint32 timeReferenceLow
;
2337 drwav_uint32 timeReferenceHigh
;
2340 pMetadata
->type
= drwav_metadata_type_bext
;
2342 pReadPointer
= bextData
;
2343 pMetadata
->data
.bext
.pDescription
= drwav__metadata_copy_string(pParser
, (char*)(pReadPointer
), DRWAV_BEXT_DESCRIPTION_BYTES
);
2344 pReadPointer
+= DRWAV_BEXT_DESCRIPTION_BYTES
;
2346 pMetadata
->data
.bext
.pOriginatorName
= drwav__metadata_copy_string(pParser
, (char*)(pReadPointer
), DRWAV_BEXT_ORIGINATOR_NAME_BYTES
);
2347 pReadPointer
+= DRWAV_BEXT_ORIGINATOR_NAME_BYTES
;
2349 pMetadata
->data
.bext
.pOriginatorReference
= drwav__metadata_copy_string(pParser
, (char*)(pReadPointer
), DRWAV_BEXT_ORIGINATOR_REF_BYTES
);
2350 pReadPointer
+= DRWAV_BEXT_ORIGINATOR_REF_BYTES
;
2352 memcpy(pReadPointer
, pMetadata
->data
.bext
.pOriginationDate
, sizeof(pMetadata
->data
.bext
.pOriginationDate
));
2353 pReadPointer
+= sizeof(pMetadata
->data
.bext
.pOriginationDate
);
2355 memcpy(pReadPointer
, pMetadata
->data
.bext
.pOriginationTime
, sizeof(pMetadata
->data
.bext
.pOriginationTime
));
2356 pReadPointer
+= sizeof(pMetadata
->data
.bext
.pOriginationTime
);
2358 timeReferenceLow
= drwav_bytes_to_u32(pReadPointer
);
2359 pReadPointer
+= sizeof(drwav_uint32
);
2360 timeReferenceHigh
= drwav_bytes_to_u32(pReadPointer
);
2361 pReadPointer
+= sizeof(drwav_uint32
);
2362 pMetadata
->data
.bext
.timeReference
= ((drwav_uint64
)timeReferenceHigh
<< 32) + timeReferenceLow
;
2364 pMetadata
->data
.bext
.version
= drwav_bytes_to_u16(pReadPointer
);
2365 pReadPointer
+= sizeof(drwav_uint16
);
2367 pMetadata
->data
.bext
.pUMID
= drwav__metadata_get_memory(pParser
, DRWAV_BEXT_UMID_BYTES
, 1);
2368 memcpy(pMetadata
->data
.bext
.pUMID
, pReadPointer
, DRWAV_BEXT_UMID_BYTES
);
2369 pReadPointer
+= DRWAV_BEXT_UMID_BYTES
;
2371 pMetadata
->data
.bext
.loudnessValue
= drwav_bytes_to_u16(pReadPointer
);
2372 pReadPointer
+= sizeof(drwav_uint16
);
2374 pMetadata
->data
.bext
.loudnessRange
= drwav_bytes_to_u16(pReadPointer
);
2375 pReadPointer
+= sizeof(drwav_uint16
);
2377 pMetadata
->data
.bext
.maxTruePeakLevel
= drwav_bytes_to_u16(pReadPointer
);
2378 pReadPointer
+= sizeof(drwav_uint16
);
2380 pMetadata
->data
.bext
.maxMomentaryLoudness
= drwav_bytes_to_u16(pReadPointer
);
2381 pReadPointer
+= sizeof(drwav_uint16
);
2383 pMetadata
->data
.bext
.maxShortTermLoudness
= drwav_bytes_to_u16(pReadPointer
);
2384 pReadPointer
+= sizeof(drwav_uint16
);
2386 DRWAV_ASSERT((pReadPointer
+ DRWAV_BEXT_RESERVED_BYTES
) == (bextData
+ DRWAV_BEXT_BYTES
));
2388 extraBytes
= (size_t)(chunkSize
- DRWAV_BEXT_BYTES
);
2389 if (extraBytes
> 0) {
2390 pMetadata
->data
.bext
.pCodingHistory
= (char*)drwav__metadata_get_memory(pParser
, extraBytes
+ 1, 1);
2391 DRWAV_ASSERT(pMetadata
->data
.bext
.pCodingHistory
!= NULL
);
2393 bytesRead
+= drwav__metadata_parser_read(pParser
, pMetadata
->data
.bext
.pCodingHistory
, extraBytes
, NULL
);
2394 pMetadata
->data
.bext
.codingHistorySize
= (drwav_uint32
)strlen(pMetadata
->data
.bext
.pCodingHistory
);
2396 pMetadata
->data
.bext
.pCodingHistory
= NULL
;
2397 pMetadata
->data
.bext
.codingHistorySize
= 0;
2404 DRWAV_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
)
2406 drwav_uint8 cueIDBuffer
[DRWAV_LIST_LABEL_OR_NOTE_BYTES
];
2407 drwav_uint64 totalBytesRead
= 0;
2408 size_t bytesJustRead
= drwav__metadata_parser_read(pParser
, cueIDBuffer
, sizeof(cueIDBuffer
), &totalBytesRead
);
2410 DRWAV_ASSERT(pParser
->stage
== drwav__metadata_parser_stage_read
);
2412 if (bytesJustRead
== sizeof(cueIDBuffer
)) {
2413 drwav_uint32 sizeIncludingNullTerminator
;
2415 pMetadata
->type
= type
;
2416 pMetadata
->data
.labelOrNote
.cuePointId
= drwav_bytes_to_u32(cueIDBuffer
);
2418 sizeIncludingNullTerminator
= (drwav_uint32
)chunkSize
- DRWAV_LIST_LABEL_OR_NOTE_BYTES
;
2419 if (sizeIncludingNullTerminator
> 0) {
2420 pMetadata
->data
.labelOrNote
.stringLength
= sizeIncludingNullTerminator
- 1;
2421 pMetadata
->data
.labelOrNote
.pString
= (char*)drwav__metadata_get_memory(pParser
, sizeIncludingNullTerminator
, 1);
2422 DRWAV_ASSERT(pMetadata
->data
.labelOrNote
.pString
!= NULL
);
2424 bytesJustRead
= drwav__metadata_parser_read(pParser
, pMetadata
->data
.labelOrNote
.pString
, sizeIncludingNullTerminator
, &totalBytesRead
);
2426 pMetadata
->data
.labelOrNote
.stringLength
= 0;
2427 pMetadata
->data
.labelOrNote
.pString
= NULL
;
2431 return totalBytesRead
;
2434 DRWAV_PRIVATE drwav_uint64
drwav__read_list_labelled_cue_region_to_metadata_obj(drwav__metadata_parser
* pParser
, drwav_metadata
* pMetadata
, drwav_uint64 chunkSize
)
2436 drwav_uint8 buffer
[DRWAV_LIST_LABELLED_TEXT_BYTES
];
2437 drwav_uint64 totalBytesRead
= 0;
2438 size_t bytesJustRead
= drwav__metadata_parser_read(pParser
, buffer
, sizeof(buffer
), &totalBytesRead
);
2440 DRWAV_ASSERT(pParser
->stage
== drwav__metadata_parser_stage_read
);
2442 if (bytesJustRead
== sizeof(buffer
)) {
2443 drwav_uint32 sizeIncludingNullTerminator
;
2445 pMetadata
->type
= drwav_metadata_type_list_labelled_cue_region
;
2446 pMetadata
->data
.labelledCueRegion
.cuePointId
= drwav_bytes_to_u32(buffer
+ 0);
2447 pMetadata
->data
.labelledCueRegion
.sampleLength
= drwav_bytes_to_u32(buffer
+ 4);
2448 pMetadata
->data
.labelledCueRegion
.purposeId
[0] = buffer
[8];
2449 pMetadata
->data
.labelledCueRegion
.purposeId
[1] = buffer
[9];
2450 pMetadata
->data
.labelledCueRegion
.purposeId
[2] = buffer
[10];
2451 pMetadata
->data
.labelledCueRegion
.purposeId
[3] = buffer
[11];
2452 pMetadata
->data
.labelledCueRegion
.country
= drwav_bytes_to_u16(buffer
+ 12);
2453 pMetadata
->data
.labelledCueRegion
.language
= drwav_bytes_to_u16(buffer
+ 14);
2454 pMetadata
->data
.labelledCueRegion
.dialect
= drwav_bytes_to_u16(buffer
+ 16);
2455 pMetadata
->data
.labelledCueRegion
.codePage
= drwav_bytes_to_u16(buffer
+ 18);
2457 sizeIncludingNullTerminator
= (drwav_uint32
)chunkSize
- DRWAV_LIST_LABELLED_TEXT_BYTES
;
2458 if (sizeIncludingNullTerminator
> 0) {
2459 pMetadata
->data
.labelledCueRegion
.stringLength
= sizeIncludingNullTerminator
- 1;
2460 pMetadata
->data
.labelledCueRegion
.pString
= (char*)drwav__metadata_get_memory(pParser
, sizeIncludingNullTerminator
, 1);
2461 DRWAV_ASSERT(pMetadata
->data
.labelledCueRegion
.pString
!= NULL
);
2463 bytesJustRead
= drwav__metadata_parser_read(pParser
, pMetadata
->data
.labelledCueRegion
.pString
, sizeIncludingNullTerminator
, &totalBytesRead
);
2465 pMetadata
->data
.labelledCueRegion
.stringLength
= 0;
2466 pMetadata
->data
.labelledCueRegion
.pString
= NULL
;
2470 return totalBytesRead
;
2473 DRWAV_PRIVATE drwav_uint64
drwav__metadata_process_info_text_chunk(drwav__metadata_parser
* pParser
, drwav_uint64 chunkSize
, drwav_metadata_type type
)
2475 drwav_uint64 bytesRead
= 0;
2476 drwav_uint32 stringSizeWithNullTerminator
= (drwav_uint32
)chunkSize
;
2478 if (pParser
->stage
== drwav__metadata_parser_stage_count
) {
2479 pParser
->metadataCount
+= 1;
2480 drwav__metadata_request_extra_memory_for_stage_2(pParser
, stringSizeWithNullTerminator
, 1);
2482 drwav_metadata
* pMetadata
= &pParser
->pMetadata
[pParser
->metadataCursor
];
2483 pMetadata
->type
= type
;
2484 if (stringSizeWithNullTerminator
> 0) {
2485 pMetadata
->data
.infoText
.stringLength
= stringSizeWithNullTerminator
- 1;
2486 pMetadata
->data
.infoText
.pString
= (char*)drwav__metadata_get_memory(pParser
, stringSizeWithNullTerminator
, 1);
2487 DRWAV_ASSERT(pMetadata
->data
.infoText
.pString
!= NULL
);
2489 bytesRead
= drwav__metadata_parser_read(pParser
, pMetadata
->data
.infoText
.pString
, (size_t)stringSizeWithNullTerminator
, NULL
);
2490 if (bytesRead
== chunkSize
) {
2491 pParser
->metadataCursor
+= 1;
2493 /* Failed to parse. */
2496 pMetadata
->data
.infoText
.stringLength
= 0;
2497 pMetadata
->data
.infoText
.pString
= NULL
;
2498 pParser
->metadataCursor
+= 1;
2505 DRWAV_PRIVATE drwav_uint64
drwav__metadata_process_unknown_chunk(drwav__metadata_parser
* pParser
, const drwav_uint8
* pChunkId
, drwav_uint64 chunkSize
, drwav_metadata_location location
)
2507 drwav_uint64 bytesRead
= 0;
2509 if (location
== drwav_metadata_location_invalid
) {
2513 if (drwav_fourcc_equal(pChunkId
, "data") || drwav_fourcc_equal(pChunkId
, "fmt") || drwav_fourcc_equal(pChunkId
, "fact")) {
2517 if (pParser
->stage
== drwav__metadata_parser_stage_count
) {
2518 pParser
->metadataCount
+= 1;
2519 drwav__metadata_request_extra_memory_for_stage_2(pParser
, (size_t)chunkSize
, 1);
2521 drwav_metadata
* pMetadata
= &pParser
->pMetadata
[pParser
->metadataCursor
];
2522 pMetadata
->type
= drwav_metadata_type_unknown
;
2523 pMetadata
->data
.unknown
.chunkLocation
= location
;
2524 pMetadata
->data
.unknown
.id
[0] = pChunkId
[0];
2525 pMetadata
->data
.unknown
.id
[1] = pChunkId
[1];
2526 pMetadata
->data
.unknown
.id
[2] = pChunkId
[2];
2527 pMetadata
->data
.unknown
.id
[3] = pChunkId
[3];
2528 pMetadata
->data
.unknown
.dataSizeInBytes
= (drwav_uint32
)chunkSize
;
2529 pMetadata
->data
.unknown
.pData
= (drwav_uint8
*)drwav__metadata_get_memory(pParser
, (size_t)chunkSize
, 1);
2530 DRWAV_ASSERT(pMetadata
->data
.unknown
.pData
!= NULL
);
2532 bytesRead
= drwav__metadata_parser_read(pParser
, pMetadata
->data
.unknown
.pData
, pMetadata
->data
.unknown
.dataSizeInBytes
, NULL
);
2533 if (bytesRead
== pMetadata
->data
.unknown
.dataSizeInBytes
) {
2534 pParser
->metadataCursor
+= 1;
2536 /* Failed to read. */
2543 DRWAV_PRIVATE drwav_bool32
drwav__chunk_matches(drwav_uint64 allowedMetadataTypes
, const drwav_uint8
* pChunkID
, drwav_metadata_type type
, const char* pID
)
2545 return (allowedMetadataTypes
& type
) && drwav_fourcc_equal(pChunkID
, pID
);
2548 DRWAV_PRIVATE drwav_uint64
drwav__metadata_process_chunk(drwav__metadata_parser
* pParser
, const drwav_chunk_header
* pChunkHeader
, drwav_uint64 allowedMetadataTypes
)
2550 const drwav_uint8
*pChunkID
= pChunkHeader
->id
.fourcc
;
2551 drwav_uint64 bytesRead
= 0;
2553 if (drwav__chunk_matches(allowedMetadataTypes
, pChunkID
, drwav_metadata_type_smpl
, "smpl")) {
2554 if (pChunkHeader
->sizeInBytes
>= DRWAV_SMPL_BYTES
) {
2555 if (pParser
->stage
== drwav__metadata_parser_stage_count
) {
2556 drwav_uint8 buffer
[4];
2557 size_t bytesJustRead
;
2559 if (!pParser
->onSeek(pParser
->pReadSeekUserData
, 28, drwav_seek_origin_current
)) {
2564 bytesJustRead
= drwav__metadata_parser_read(pParser
, buffer
, sizeof(buffer
), &bytesRead
);
2565 if (bytesJustRead
== sizeof(buffer
)) {
2566 drwav_uint32 loopCount
= drwav_bytes_to_u32(buffer
);
2568 bytesJustRead
= drwav__metadata_parser_read(pParser
, buffer
, sizeof(buffer
), &bytesRead
);
2569 if (bytesJustRead
== sizeof(buffer
)) {
2570 drwav_uint32 samplerSpecificDataSizeInBytes
= drwav_bytes_to_u32(buffer
);
2572 pParser
->metadataCount
+= 1;
2573 drwav__metadata_request_extra_memory_for_stage_2(pParser
, sizeof(drwav_smpl_loop
) * loopCount
, DRWAV_METADATA_ALIGNMENT
);
2574 drwav__metadata_request_extra_memory_for_stage_2(pParser
, samplerSpecificDataSizeInBytes
, 1);
2578 bytesRead
= drwav__read_smpl_to_metadata_obj(pParser
, &pParser
->pMetadata
[pParser
->metadataCursor
]);
2579 if (bytesRead
== pChunkHeader
->sizeInBytes
) {
2580 pParser
->metadataCursor
+= 1;
2582 /* Failed to parse. */
2586 /* Incorrectly formed chunk. */
2588 } else if (drwav__chunk_matches(allowedMetadataTypes
, pChunkID
, drwav_metadata_type_inst
, "inst")) {
2589 if (pChunkHeader
->sizeInBytes
== DRWAV_INST_BYTES
) {
2590 if (pParser
->stage
== drwav__metadata_parser_stage_count
) {
2591 pParser
->metadataCount
+= 1;
2593 bytesRead
= drwav__read_inst_to_metadata_obj(pParser
, &pParser
->pMetadata
[pParser
->metadataCursor
]);
2594 if (bytesRead
== pChunkHeader
->sizeInBytes
) {
2595 pParser
->metadataCursor
+= 1;
2597 /* Failed to parse. */
2601 /* Incorrectly formed chunk. */
2603 } else if (drwav__chunk_matches(allowedMetadataTypes
, pChunkID
, drwav_metadata_type_acid
, "acid")) {
2604 if (pChunkHeader
->sizeInBytes
== DRWAV_ACID_BYTES
) {
2605 if (pParser
->stage
== drwav__metadata_parser_stage_count
) {
2606 pParser
->metadataCount
+= 1;
2608 bytesRead
= drwav__read_acid_to_metadata_obj(pParser
, &pParser
->pMetadata
[pParser
->metadataCursor
]);
2609 if (bytesRead
== pChunkHeader
->sizeInBytes
) {
2610 pParser
->metadataCursor
+= 1;
2612 /* Failed to parse. */
2616 /* Incorrectly formed chunk. */
2618 } else if (drwav__chunk_matches(allowedMetadataTypes
, pChunkID
, drwav_metadata_type_cue
, "cue ")) {
2619 if (pChunkHeader
->sizeInBytes
>= DRWAV_CUE_BYTES
) {
2620 if (pParser
->stage
== drwav__metadata_parser_stage_count
) {
2623 pParser
->metadataCount
+= 1;
2624 cueCount
= (size_t)(pChunkHeader
->sizeInBytes
- DRWAV_CUE_BYTES
) / DRWAV_CUE_POINT_BYTES
;
2625 drwav__metadata_request_extra_memory_for_stage_2(pParser
, sizeof(drwav_cue_point
) * cueCount
, DRWAV_METADATA_ALIGNMENT
);
2627 bytesRead
= drwav__read_cue_to_metadata_obj(pParser
, &pParser
->pMetadata
[pParser
->metadataCursor
]);
2628 if (bytesRead
== pChunkHeader
->sizeInBytes
) {
2629 pParser
->metadataCursor
+= 1;
2631 /* Failed to parse. */
2635 /* Incorrectly formed chunk. */
2637 } else if (drwav__chunk_matches(allowedMetadataTypes
, pChunkID
, drwav_metadata_type_bext
, "bext")) {
2638 if (pChunkHeader
->sizeInBytes
>= DRWAV_BEXT_BYTES
) {
2639 if (pParser
->stage
== drwav__metadata_parser_stage_count
) {
2640 /* The description field is the largest one in a bext chunk, so that is the max size of this temporary buffer. */
2641 char buffer
[DRWAV_BEXT_DESCRIPTION_BYTES
+ 1];
2642 size_t allocSizeNeeded
= DRWAV_BEXT_UMID_BYTES
; /* We know we will need SMPTE umid size. */
2643 size_t bytesJustRead
;
2645 buffer
[DRWAV_BEXT_DESCRIPTION_BYTES
] = '\0';
2646 bytesJustRead
= drwav__metadata_parser_read(pParser
, buffer
, DRWAV_BEXT_DESCRIPTION_BYTES
, &bytesRead
);
2647 if (bytesJustRead
!= DRWAV_BEXT_DESCRIPTION_BYTES
) {
2650 allocSizeNeeded
+= strlen(buffer
) + 1;
2652 buffer
[DRWAV_BEXT_ORIGINATOR_NAME_BYTES
] = '\0';
2653 bytesJustRead
= drwav__metadata_parser_read(pParser
, buffer
, DRWAV_BEXT_ORIGINATOR_NAME_BYTES
, &bytesRead
);
2654 if (bytesJustRead
!= DRWAV_BEXT_ORIGINATOR_NAME_BYTES
) {
2657 allocSizeNeeded
+= strlen(buffer
) + 1;
2659 buffer
[DRWAV_BEXT_ORIGINATOR_REF_BYTES
] = '\0';
2660 bytesJustRead
= drwav__metadata_parser_read(pParser
, buffer
, DRWAV_BEXT_ORIGINATOR_REF_BYTES
, &bytesRead
);
2661 if (bytesJustRead
!= DRWAV_BEXT_ORIGINATOR_REF_BYTES
) {
2664 allocSizeNeeded
+= strlen(buffer
) + 1;
2665 allocSizeNeeded
+= (size_t)pChunkHeader
->sizeInBytes
- DRWAV_BEXT_BYTES
; /* Coding history. */
2667 drwav__metadata_request_extra_memory_for_stage_2(pParser
, allocSizeNeeded
, 1);
2669 pParser
->metadataCount
+= 1;
2671 bytesRead
= drwav__read_bext_to_metadata_obj(pParser
, &pParser
->pMetadata
[pParser
->metadataCursor
], pChunkHeader
->sizeInBytes
);
2672 if (bytesRead
== pChunkHeader
->sizeInBytes
) {
2673 pParser
->metadataCursor
+= 1;
2675 /* Failed to parse. */
2679 /* Incorrectly formed chunk. */
2681 } else if (drwav_fourcc_equal(pChunkID
, "LIST") || drwav_fourcc_equal(pChunkID
, "list")) {
2682 drwav_metadata_location listType
= drwav_metadata_location_invalid
;
2683 while (bytesRead
< pChunkHeader
->sizeInBytes
) {
2684 drwav_uint8 subchunkId
[4];
2685 drwav_uint8 subchunkSizeBuffer
[4];
2686 drwav_uint64 subchunkDataSize
;
2687 drwav_uint64 subchunkBytesRead
= 0;
2688 drwav_uint64 bytesJustRead
= drwav__metadata_parser_read(pParser
, subchunkId
, sizeof(subchunkId
), &bytesRead
);
2689 if (bytesJustRead
!= sizeof(subchunkId
)) {
2694 The first thing in a list chunk should be "adtl" or "INFO".
2696 - adtl means this list is a Associated Data List Chunk and will contain labels, notes
2697 or labelled cue regions.
2698 - INFO means this list is an Info List Chunk containing info text chunks such as IPRD
2699 which would specifies the album of this wav file.
2701 No data follows the adtl or INFO id so we just make note of what type this list is and
2704 if (drwav_fourcc_equal(subchunkId
, "adtl")) {
2705 listType
= drwav_metadata_location_inside_adtl_list
;
2707 } else if (drwav_fourcc_equal(subchunkId
, "INFO")) {
2708 listType
= drwav_metadata_location_inside_info_list
;
2712 bytesJustRead
= drwav__metadata_parser_read(pParser
, subchunkSizeBuffer
, sizeof(subchunkSizeBuffer
), &bytesRead
);
2713 if (bytesJustRead
!= sizeof(subchunkSizeBuffer
)) {
2716 subchunkDataSize
= drwav_bytes_to_u32(subchunkSizeBuffer
);
2718 if (drwav__chunk_matches(allowedMetadataTypes
, subchunkId
, drwav_metadata_type_list_label
, "labl") || drwav__chunk_matches(allowedMetadataTypes
, subchunkId
, drwav_metadata_type_list_note
, "note")) {
2719 if (subchunkDataSize
>= DRWAV_LIST_LABEL_OR_NOTE_BYTES
) {
2720 drwav_uint64 stringSizeWithNullTerm
= subchunkDataSize
- DRWAV_LIST_LABEL_OR_NOTE_BYTES
;
2721 if (pParser
->stage
== drwav__metadata_parser_stage_count
) {
2722 pParser
->metadataCount
+= 1;
2723 drwav__metadata_request_extra_memory_for_stage_2(pParser
, (size_t)stringSizeWithNullTerm
, 1);
2725 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
);
2726 if (subchunkBytesRead
== subchunkDataSize
) {
2727 pParser
->metadataCursor
+= 1;
2729 /* Failed to parse. */
2733 /* Incorrectly formed chunk. */
2735 } else if (drwav__chunk_matches(allowedMetadataTypes
, subchunkId
, drwav_metadata_type_list_labelled_cue_region
, "ltxt")) {
2736 if (subchunkDataSize
>= DRWAV_LIST_LABELLED_TEXT_BYTES
) {
2737 drwav_uint64 stringSizeWithNullTerminator
= subchunkDataSize
- DRWAV_LIST_LABELLED_TEXT_BYTES
;
2738 if (pParser
->stage
== drwav__metadata_parser_stage_count
) {
2739 pParser
->metadataCount
+= 1;
2740 drwav__metadata_request_extra_memory_for_stage_2(pParser
, (size_t)stringSizeWithNullTerminator
, 1);
2742 subchunkBytesRead
= drwav__read_list_labelled_cue_region_to_metadata_obj(pParser
, &pParser
->pMetadata
[pParser
->metadataCursor
], subchunkDataSize
);
2743 if (subchunkBytesRead
== subchunkDataSize
) {
2744 pParser
->metadataCursor
+= 1;
2746 /* Failed to parse. */
2750 /* Incorrectly formed chunk. */
2752 } else if (drwav__chunk_matches(allowedMetadataTypes
, subchunkId
, drwav_metadata_type_list_info_software
, "ISFT")) {
2753 subchunkBytesRead
= drwav__metadata_process_info_text_chunk(pParser
, subchunkDataSize
, drwav_metadata_type_list_info_software
);
2754 } else if (drwav__chunk_matches(allowedMetadataTypes
, subchunkId
, drwav_metadata_type_list_info_copyright
, "ICOP")) {
2755 subchunkBytesRead
= drwav__metadata_process_info_text_chunk(pParser
, subchunkDataSize
, drwav_metadata_type_list_info_copyright
);
2756 } else if (drwav__chunk_matches(allowedMetadataTypes
, subchunkId
, drwav_metadata_type_list_info_title
, "INAM")) {
2757 subchunkBytesRead
= drwav__metadata_process_info_text_chunk(pParser
, subchunkDataSize
, drwav_metadata_type_list_info_title
);
2758 } else if (drwav__chunk_matches(allowedMetadataTypes
, subchunkId
, drwav_metadata_type_list_info_artist
, "IART")) {
2759 subchunkBytesRead
= drwav__metadata_process_info_text_chunk(pParser
, subchunkDataSize
, drwav_metadata_type_list_info_artist
);
2760 } else if (drwav__chunk_matches(allowedMetadataTypes
, subchunkId
, drwav_metadata_type_list_info_comment
, "ICMT")) {
2761 subchunkBytesRead
= drwav__metadata_process_info_text_chunk(pParser
, subchunkDataSize
, drwav_metadata_type_list_info_comment
);
2762 } else if (drwav__chunk_matches(allowedMetadataTypes
, subchunkId
, drwav_metadata_type_list_info_date
, "ICRD")) {
2763 subchunkBytesRead
= drwav__metadata_process_info_text_chunk(pParser
, subchunkDataSize
, drwav_metadata_type_list_info_date
);
2764 } else if (drwav__chunk_matches(allowedMetadataTypes
, subchunkId
, drwav_metadata_type_list_info_genre
, "IGNR")) {
2765 subchunkBytesRead
= drwav__metadata_process_info_text_chunk(pParser
, subchunkDataSize
, drwav_metadata_type_list_info_genre
);
2766 } else if (drwav__chunk_matches(allowedMetadataTypes
, subchunkId
, drwav_metadata_type_list_info_album
, "IPRD")) {
2767 subchunkBytesRead
= drwav__metadata_process_info_text_chunk(pParser
, subchunkDataSize
, drwav_metadata_type_list_info_album
);
2768 } else if (drwav__chunk_matches(allowedMetadataTypes
, subchunkId
, drwav_metadata_type_list_info_tracknumber
, "ITRK")) {
2769 subchunkBytesRead
= drwav__metadata_process_info_text_chunk(pParser
, subchunkDataSize
, drwav_metadata_type_list_info_tracknumber
);
2770 } else if (allowedMetadataTypes
& drwav_metadata_type_unknown
) {
2771 subchunkBytesRead
= drwav__metadata_process_unknown_chunk(pParser
, subchunkId
, subchunkDataSize
, listType
);
2774 bytesRead
+= subchunkBytesRead
;
2775 DRWAV_ASSERT(subchunkBytesRead
<= subchunkDataSize
);
2777 if (subchunkBytesRead
< subchunkDataSize
) {
2778 drwav_uint64 bytesToSeek
= subchunkDataSize
- subchunkBytesRead
;
2780 if (!pParser
->onSeek(pParser
->pReadSeekUserData
, (int)bytesToSeek
, drwav_seek_origin_current
)) {
2783 bytesRead
+= bytesToSeek
;
2786 if ((subchunkDataSize
% 2) == 1) {
2787 if (!pParser
->onSeek(pParser
->pReadSeekUserData
, 1, drwav_seek_origin_current
)) {
2793 } else if (allowedMetadataTypes
& drwav_metadata_type_unknown
) {
2794 bytesRead
= drwav__metadata_process_unknown_chunk(pParser
, pChunkID
, pChunkHeader
->sizeInBytes
, drwav_metadata_location_top_level
);
2801 DRWAV_PRIVATE drwav_uint32
drwav_get_bytes_per_pcm_frame(drwav
* pWav
)
2803 drwav_uint32 bytesPerFrame
;
2806 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
2807 is that if the bits per sample is a multiple of 8, use floor(bitsPerSample*channels/8), otherwise fall back to the block align.
2809 if ((pWav
->bitsPerSample
& 0x7) == 0) {
2810 /* Bits per sample is a multiple of 8. */
2811 bytesPerFrame
= (pWav
->bitsPerSample
* pWav
->fmt
.channels
) >> 3;
2813 bytesPerFrame
= pWav
->fmt
.blockAlign
;
2816 /* Validation for known formats. a-law and mu-law should be 1 byte per channel. If it's not, it's not decodable. */
2817 if (pWav
->translatedFormatTag
== DR_WAVE_FORMAT_ALAW
|| pWav
->translatedFormatTag
== DR_WAVE_FORMAT_MULAW
) {
2818 if (bytesPerFrame
!= pWav
->fmt
.channels
) {
2819 return 0; /* Invalid file. */
2823 return bytesPerFrame
;
2826 DRWAV_API drwav_uint16
drwav_fmt_get_format(const drwav_fmt
* pFMT
)
2832 if (pFMT
->formatTag
!= DR_WAVE_FORMAT_EXTENSIBLE
) {
2833 return pFMT
->formatTag
;
2835 return drwav_bytes_to_u16(pFMT
->subFormat
); /* Only the first two bytes are required. */
2839 DRWAV_PRIVATE drwav_bool32
drwav_preinit(drwav
* pWav
, drwav_read_proc onRead
, drwav_seek_proc onSeek
, void* pReadSeekUserData
, const drwav_allocation_callbacks
* pAllocationCallbacks
)
2841 if (pWav
== NULL
|| onRead
== NULL
|| onSeek
== NULL
) {
2845 DRWAV_ZERO_MEMORY(pWav
, sizeof(*pWav
));
2846 pWav
->onRead
= onRead
;
2847 pWav
->onSeek
= onSeek
;
2848 pWav
->pUserData
= pReadSeekUserData
;
2849 pWav
->allocationCallbacks
= drwav_copy_allocation_callbacks_or_defaults(pAllocationCallbacks
);
2851 if (pWav
->allocationCallbacks
.onFree
== NULL
|| (pWav
->allocationCallbacks
.onMalloc
== NULL
&& pWav
->allocationCallbacks
.onRealloc
== NULL
)) {
2852 return DRWAV_FALSE
; /* Invalid allocation callbacks. */
2858 DRWAV_PRIVATE drwav_bool32
drwav_init__internal(drwav
* pWav
, drwav_chunk_proc onChunk
, void* pChunkUserData
, drwav_uint32 flags
)
2860 /* This function assumes drwav_preinit() has been called beforehand. */
2862 drwav_uint64 cursor
; /* <-- Keeps track of the byte position so we can seek to specific locations. */
2863 drwav_bool32 sequential
;
2864 drwav_uint8 riff
[4];
2866 unsigned short translatedFormatTag
;
2867 drwav_bool32 foundDataChunk
;
2868 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. */
2869 drwav_uint64 sampleCountFromFactChunk
= 0; /* Same as dataChunkSize - make sure this is the only place this is initialized to 0. */
2870 drwav_uint64 chunkSize
;
2871 drwav__metadata_parser metadataParser
;
2874 sequential
= (flags
& DRWAV_SEQUENTIAL
) != 0;
2876 /* The first 4 bytes should be the RIFF identifier. */
2877 if (drwav__on_read(pWav
->onRead
, pWav
->pUserData
, riff
, sizeof(riff
), &cursor
) != sizeof(riff
)) {
2882 The first 4 bytes can be used to identify the container. For RIFF files it will start with "RIFF" and for
2883 w64 it will start with "riff".
2885 if (drwav_fourcc_equal(riff
, "RIFF")) {
2886 pWav
->container
= drwav_container_riff
;
2887 } else if (drwav_fourcc_equal(riff
, "riff")) {
2889 drwav_uint8 riff2
[12];
2891 pWav
->container
= drwav_container_w64
;
2893 /* Check the rest of the GUID for validity. */
2894 if (drwav__on_read(pWav
->onRead
, pWav
->pUserData
, riff2
, sizeof(riff2
), &cursor
) != sizeof(riff2
)) {
2898 for (i
= 0; i
< 12; ++i
) {
2899 if (riff2
[i
] != drwavGUID_W64_RIFF
[i
+4]) {
2903 } else if (drwav_fourcc_equal(riff
, "RF64")) {
2904 pWav
->container
= drwav_container_rf64
;
2906 return DRWAV_FALSE
; /* Unknown or unsupported container. */
2910 if (pWav
->container
== drwav_container_riff
|| pWav
->container
== drwav_container_rf64
) {
2911 drwav_uint8 chunkSizeBytes
[4];
2912 drwav_uint8 wave
[4];
2915 if (drwav__on_read(pWav
->onRead
, pWav
->pUserData
, chunkSizeBytes
, sizeof(chunkSizeBytes
), &cursor
) != sizeof(chunkSizeBytes
)) {
2919 if (pWav
->container
== drwav_container_riff
) {
2920 if (drwav_bytes_to_u32(chunkSizeBytes
) < 36) {
2921 return DRWAV_FALSE
; /* Chunk size should always be at least 36 bytes. */
2924 if (drwav_bytes_to_u32(chunkSizeBytes
) != 0xFFFFFFFF) {
2925 return DRWAV_FALSE
; /* Chunk size should always be set to -1/0xFFFFFFFF for RF64. The actual size is retrieved later. */
2929 if (drwav__on_read(pWav
->onRead
, pWav
->pUserData
, wave
, sizeof(wave
), &cursor
) != sizeof(wave
)) {
2933 if (!drwav_fourcc_equal(wave
, "WAVE")) {
2934 return DRWAV_FALSE
; /* Expecting "WAVE". */
2937 drwav_uint8 chunkSizeBytes
[8];
2938 drwav_uint8 wave
[16];
2941 if (drwav__on_read(pWav
->onRead
, pWav
->pUserData
, chunkSizeBytes
, sizeof(chunkSizeBytes
), &cursor
) != sizeof(chunkSizeBytes
)) {
2945 if (drwav_bytes_to_u64(chunkSizeBytes
) < 80) {
2949 if (drwav__on_read(pWav
->onRead
, pWav
->pUserData
, wave
, sizeof(wave
), &cursor
) != sizeof(wave
)) {
2953 if (!drwav_guid_equal(wave
, drwavGUID_W64_WAVE
)) {
2959 /* For RF64, the "ds64" chunk must come next, before the "fmt " chunk. */
2960 if (pWav
->container
== drwav_container_rf64
) {
2961 drwav_uint8 sizeBytes
[8];
2962 drwav_uint64 bytesRemainingInChunk
;
2963 drwav_chunk_header header
;
2964 drwav_result result
= drwav__read_chunk_header(pWav
->onRead
, pWav
->pUserData
, pWav
->container
, &cursor
, &header
);
2965 if (result
!= DRWAV_SUCCESS
) {
2969 if (!drwav_fourcc_equal(header
.id
.fourcc
, "ds64")) {
2970 return DRWAV_FALSE
; /* Expecting "ds64". */
2973 bytesRemainingInChunk
= header
.sizeInBytes
+ header
.paddingSize
;
2975 /* We don't care about the size of the RIFF chunk - skip it. */
2976 if (!drwav__seek_forward(pWav
->onSeek
, 8, pWav
->pUserData
)) {
2979 bytesRemainingInChunk
-= 8;
2983 /* Next 8 bytes is the size of the "data" chunk. */
2984 if (drwav__on_read(pWav
->onRead
, pWav
->pUserData
, sizeBytes
, sizeof(sizeBytes
), &cursor
) != sizeof(sizeBytes
)) {
2987 bytesRemainingInChunk
-= 8;
2988 dataChunkSize
= drwav_bytes_to_u64(sizeBytes
);
2991 /* Next 8 bytes is the same count which we would usually derived from the FACT chunk if it was available. */
2992 if (drwav__on_read(pWav
->onRead
, pWav
->pUserData
, sizeBytes
, sizeof(sizeBytes
), &cursor
) != sizeof(sizeBytes
)) {
2995 bytesRemainingInChunk
-= 8;
2996 sampleCountFromFactChunk
= drwav_bytes_to_u64(sizeBytes
);
2999 /* Skip over everything else. */
3000 if (!drwav__seek_forward(pWav
->onSeek
, bytesRemainingInChunk
, pWav
->pUserData
)) {
3003 cursor
+= bytesRemainingInChunk
;
3007 /* The next bytes should be the "fmt " chunk. */
3008 if (!drwav__read_fmt(pWav
->onRead
, pWav
->onSeek
, pWav
->pUserData
, pWav
->container
, &cursor
, &fmt
)) {
3009 return DRWAV_FALSE
; /* Failed to read the "fmt " chunk. */
3012 /* Basic validation. */
3013 if ((fmt
.sampleRate
== 0 || fmt
.sampleRate
> DRWAV_MAX_SAMPLE_RATE
) ||
3014 (fmt
.channels
== 0 || fmt
.channels
> DRWAV_MAX_CHANNELS
) ||
3015 (fmt
.bitsPerSample
== 0 || fmt
.bitsPerSample
> DRWAV_MAX_BITS_PER_SAMPLE
) ||
3016 fmt
.blockAlign
== 0) {
3017 return DRWAV_FALSE
; /* Probably an invalid WAV file. */
3021 /* Translate the internal format. */
3022 translatedFormatTag
= fmt
.formatTag
;
3023 if (translatedFormatTag
== DR_WAVE_FORMAT_EXTENSIBLE
) {
3024 translatedFormatTag
= drwav_bytes_to_u16(fmt
.subFormat
+ 0);
3027 memset(&metadataParser
, 0, sizeof(metadataParser
));
3029 /* Not tested on W64. */
3030 if (!sequential
&& pWav
->allowedMetadataTypes
!= drwav_metadata_type_none
&& (pWav
->container
== drwav_container_riff
|| pWav
->container
== drwav_container_rf64
)) {
3031 drwav_uint64 cursorForMetadata
= cursor
;
3033 metadataParser
.onRead
= pWav
->onRead
;
3034 metadataParser
.onSeek
= pWav
->onSeek
;
3035 metadataParser
.pReadSeekUserData
= pWav
->pUserData
;
3036 metadataParser
.stage
= drwav__metadata_parser_stage_count
;
3039 drwav_result result
;
3040 drwav_uint64 bytesRead
;
3041 drwav_uint64 remainingBytes
;
3042 drwav_chunk_header header
;
3044 result
= drwav__read_chunk_header(pWav
->onRead
, pWav
->pUserData
, pWav
->container
, &cursorForMetadata
, &header
);
3045 if (result
!= DRWAV_SUCCESS
) {
3049 bytesRead
= drwav__metadata_process_chunk(&metadataParser
, &header
, pWav
->allowedMetadataTypes
);
3050 DRWAV_ASSERT(bytesRead
<= header
.sizeInBytes
);
3052 remainingBytes
= header
.sizeInBytes
- bytesRead
+ header
.paddingSize
;
3053 if (!drwav__seek_forward(pWav
->onSeek
, remainingBytes
, pWav
->pUserData
)) {
3056 cursorForMetadata
+= remainingBytes
;
3059 if (!drwav__seek_from_start(pWav
->onSeek
, cursor
, pWav
->pUserData
)) {
3063 drwav__metadata_alloc(&metadataParser
, &pWav
->allocationCallbacks
);
3064 metadataParser
.stage
= drwav__metadata_parser_stage_read
;
3068 We need to enumerate over each chunk for two reasons:
3069 1) The "data" chunk may not be the next one
3070 2) We may want to report each chunk back to the client
3072 In order to correctly report each chunk back to the client we will need to keep looping until the end of the file.
3074 foundDataChunk
= DRWAV_FALSE
;
3076 /* The next chunk we care about is the "data" chunk. This is not necessarily the next chunk so we'll need to loop. */
3078 drwav_chunk_header header
;
3079 drwav_result result
= drwav__read_chunk_header(pWav
->onRead
, pWav
->pUserData
, pWav
->container
, &cursor
, &header
);
3080 if (result
!= DRWAV_SUCCESS
) {
3081 if (!foundDataChunk
) {
3084 break; /* Probably at the end of the file. Get out of the loop. */
3088 /* Tell the client about this chunk. */
3089 if (!sequential
&& onChunk
!= NULL
) {
3090 drwav_uint64 callbackBytesRead
= onChunk(pChunkUserData
, pWav
->onRead
, pWav
->onSeek
, pWav
->pUserData
, &header
, pWav
->container
, &fmt
);
3093 dr_wav may need to read the contents of the chunk, so we now need to seek back to the position before
3094 we called the callback.
3096 if (callbackBytesRead
> 0) {
3097 if (!drwav__seek_from_start(pWav
->onSeek
, cursor
, pWav
->pUserData
)) {
3103 if (!sequential
&& pWav
->allowedMetadataTypes
!= drwav_metadata_type_none
&& (pWav
->container
== drwav_container_riff
|| pWav
->container
== drwav_container_rf64
)) {
3104 drwav_uint64 bytesRead
= drwav__metadata_process_chunk(&metadataParser
, &header
, pWav
->allowedMetadataTypes
);
3106 if (bytesRead
> 0) {
3107 if (!drwav__seek_from_start(pWav
->onSeek
, cursor
, pWav
->pUserData
)) {
3114 if (!foundDataChunk
) {
3115 pWav
->dataChunkDataPos
= cursor
;
3118 chunkSize
= header
.sizeInBytes
;
3119 if (pWav
->container
== drwav_container_riff
|| pWav
->container
== drwav_container_rf64
) {
3120 if (drwav_fourcc_equal(header
.id
.fourcc
, "data")) {
3121 foundDataChunk
= DRWAV_TRUE
;
3122 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. */
3123 dataChunkSize
= chunkSize
;
3127 if (drwav_guid_equal(header
.id
.guid
, drwavGUID_W64_DATA
)) {
3128 foundDataChunk
= DRWAV_TRUE
;
3129 dataChunkSize
= chunkSize
;
3134 If at this point we have found the data chunk and we're running in sequential mode, we need to break out of this loop. The reason for
3135 this is that we would otherwise require a backwards seek which sequential mode forbids.
3137 if (foundDataChunk
&& sequential
) {
3141 /* Optional. Get the total sample count from the FACT chunk. This is useful for compressed formats. */
3142 if (pWav
->container
== drwav_container_riff
) {
3143 if (drwav_fourcc_equal(header
.id
.fourcc
, "fact")) {
3144 drwav_uint32 sampleCount
;
3145 if (drwav__on_read(pWav
->onRead
, pWav
->pUserData
, &sampleCount
, 4, &cursor
) != 4) {
3150 if (!foundDataChunk
) {
3151 pWav
->dataChunkDataPos
= cursor
;
3155 The sample count in the "fact" chunk is either unreliable, or I'm not understanding it properly. For now I am only enabling this
3156 for Microsoft ADPCM formats.
3158 if (pWav
->translatedFormatTag
== DR_WAVE_FORMAT_ADPCM
) {
3159 sampleCountFromFactChunk
= sampleCount
;
3161 sampleCountFromFactChunk
= 0;
3164 } else if (pWav
->container
== drwav_container_w64
) {
3165 if (drwav_guid_equal(header
.id
.guid
, drwavGUID_W64_FACT
)) {
3166 if (drwav__on_read(pWav
->onRead
, pWav
->pUserData
, &sampleCountFromFactChunk
, 8, &cursor
) != 8) {
3171 if (!foundDataChunk
) {
3172 pWav
->dataChunkDataPos
= cursor
;
3175 } else if (pWav
->container
== drwav_container_rf64
) {
3176 /* We retrieved the sample count from the ds64 chunk earlier so no need to do that here. */
3179 /* Make sure we seek past the padding. */
3180 chunkSize
+= header
.paddingSize
;
3181 if (!drwav__seek_forward(pWav
->onSeek
, chunkSize
, pWav
->pUserData
)) {
3184 cursor
+= chunkSize
;
3186 if (!foundDataChunk
) {
3187 pWav
->dataChunkDataPos
= cursor
;
3191 pWav
->pMetadata
= metadataParser
.pMetadata
;
3192 pWav
->metadataCount
= metadataParser
.metadataCount
;
3194 /* If we haven't found a data chunk, return an error. */
3195 if (!foundDataChunk
) {
3199 /* 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. */
3201 if (!drwav__seek_from_start(pWav
->onSeek
, pWav
->dataChunkDataPos
, pWav
->pUserData
)) {
3204 cursor
= pWav
->dataChunkDataPos
;
3208 /* At this point we should be sitting on the first byte of the raw audio data. */
3211 pWav
->sampleRate
= fmt
.sampleRate
;
3212 pWav
->channels
= fmt
.channels
;
3213 pWav
->bitsPerSample
= fmt
.bitsPerSample
;
3214 pWav
->bytesRemaining
= dataChunkSize
;
3215 pWav
->translatedFormatTag
= translatedFormatTag
;
3216 pWav
->dataChunkDataSize
= dataChunkSize
;
3218 if (sampleCountFromFactChunk
!= 0) {
3219 pWav
->totalPCMFrameCount
= sampleCountFromFactChunk
;
3221 pWav
->totalPCMFrameCount
= dataChunkSize
/ drwav_get_bytes_per_pcm_frame(pWav
);
3223 if (pWav
->translatedFormatTag
== DR_WAVE_FORMAT_ADPCM
) {
3224 drwav_uint64 totalBlockHeaderSizeInBytes
;
3225 drwav_uint64 blockCount
= dataChunkSize
/ fmt
.blockAlign
;
3227 /* Make sure any trailing partial block is accounted for. */
3228 if ((blockCount
* fmt
.blockAlign
) < dataChunkSize
) {
3232 /* 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. */
3233 totalBlockHeaderSizeInBytes
= blockCount
* (6*fmt
.channels
);
3234 pWav
->totalPCMFrameCount
= ((dataChunkSize
- totalBlockHeaderSizeInBytes
) * 2) / fmt
.channels
;
3236 if (pWav
->translatedFormatTag
== DR_WAVE_FORMAT_DVI_ADPCM
) {
3237 drwav_uint64 totalBlockHeaderSizeInBytes
;
3238 drwav_uint64 blockCount
= dataChunkSize
/ fmt
.blockAlign
;
3240 /* Make sure any trailing partial block is accounted for. */
3241 if ((blockCount
* fmt
.blockAlign
) < dataChunkSize
) {
3245 /* 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. */
3246 totalBlockHeaderSizeInBytes
= blockCount
* (4*fmt
.channels
);
3247 pWav
->totalPCMFrameCount
= ((dataChunkSize
- totalBlockHeaderSizeInBytes
) * 2) / fmt
.channels
;
3249 /* The header includes a decoded sample for each channel which acts as the initial predictor sample. */
3250 pWav
->totalPCMFrameCount
+= blockCount
;
3254 /* Some formats only support a certain number of channels. */
3255 if (pWav
->translatedFormatTag
== DR_WAVE_FORMAT_ADPCM
|| pWav
->translatedFormatTag
== DR_WAVE_FORMAT_DVI_ADPCM
) {
3256 if (pWav
->channels
> 2) {
3261 /* The number of bytes per frame must be known. If not, it's an invalid file and not decodable. */
3262 if (drwav_get_bytes_per_pcm_frame(pWav
) == 0) {
3266 #ifdef DR_WAV_LIBSNDFILE_COMPAT
3268 I use libsndfile as a benchmark for testing, however in the version I'm using (from the Windows installer on the libsndfile website),
3269 it appears the total sample count libsndfile uses for MS-ADPCM is incorrect. It would seem they are computing the total sample count
3270 from the number of blocks, however this results in the inclusion of extra silent samples at the end of the last block. The correct
3271 way to know the total sample count is to inspect the "fact" chunk, which should always be present for compressed formats, and should
3272 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
3273 correctness tests against libsndfile, and is disabled by default.
3275 if (pWav
->translatedFormatTag
== DR_WAVE_FORMAT_ADPCM
) {
3276 drwav_uint64 blockCount
= dataChunkSize
/ fmt
.blockAlign
;
3277 pWav
->totalPCMFrameCount
= (((blockCount
* (fmt
.blockAlign
- (6*pWav
->channels
))) * 2)) / fmt
.channels
; /* x2 because two samples per byte. */
3279 if (pWav
->translatedFormatTag
== DR_WAVE_FORMAT_DVI_ADPCM
) {
3280 drwav_uint64 blockCount
= dataChunkSize
/ fmt
.blockAlign
;
3281 pWav
->totalPCMFrameCount
= (((blockCount
* (fmt
.blockAlign
- (4*pWav
->channels
))) * 2) + (blockCount
* pWav
->channels
)) / fmt
.channels
;
3288 DRWAV_API drwav_bool32
drwav_init(drwav
* pWav
, drwav_read_proc onRead
, drwav_seek_proc onSeek
, void* pUserData
, const drwav_allocation_callbacks
* pAllocationCallbacks
)
3290 return drwav_init_ex(pWav
, onRead
, onSeek
, NULL
, pUserData
, NULL
, 0, pAllocationCallbacks
);
3293 DRWAV_API drwav_bool32
drwav_init_ex(drwav
* pWav
, drwav_read_proc onRead
, drwav_seek_proc onSeek
, drwav_chunk_proc onChunk
, void* pReadSeekUserData
, void* pChunkUserData
, drwav_uint32 flags
, const drwav_allocation_callbacks
* pAllocationCallbacks
)
3295 if (!drwav_preinit(pWav
, onRead
, onSeek
, pReadSeekUserData
, pAllocationCallbacks
)) {
3299 return drwav_init__internal(pWav
, onChunk
, pChunkUserData
, flags
);
3302 DRWAV_API drwav_bool32
drwav_init_with_metadata(drwav
* pWav
, drwav_read_proc onRead
, drwav_seek_proc onSeek
, void* pUserData
, drwav_uint32 flags
, const drwav_allocation_callbacks
* pAllocationCallbacks
)
3304 if (!drwav_preinit(pWav
, onRead
, onSeek
, pUserData
, pAllocationCallbacks
)) {
3308 pWav
->allowedMetadataTypes
= drwav_metadata_type_all_including_unknown
; /* <-- Needs to be set to tell drwav_init_ex() that we need to process metadata. */
3309 return drwav_init__internal(pWav
, NULL
, NULL
, flags
);
3312 DRWAV_API drwav_metadata
* drwav_take_ownership_of_metadata(drwav
* pWav
)
3314 drwav_metadata
*result
= pWav
->pMetadata
;
3316 pWav
->pMetadata
= NULL
;
3317 pWav
->metadataCount
= 0;
3323 DRWAV_PRIVATE
size_t drwav__write(drwav
* pWav
, const void* pData
, size_t dataSize
)
3325 DRWAV_ASSERT(pWav
!= NULL
);
3326 DRWAV_ASSERT(pWav
->onWrite
!= NULL
);
3328 /* Generic write. Assumes no byte reordering required. */
3329 return pWav
->onWrite(pWav
->pUserData
, pData
, dataSize
);
3332 DRWAV_PRIVATE
size_t drwav__write_byte(drwav
* pWav
, drwav_uint8 byte
)
3334 DRWAV_ASSERT(pWav
!= NULL
);
3335 DRWAV_ASSERT(pWav
->onWrite
!= NULL
);
3337 return pWav
->onWrite(pWav
->pUserData
, &byte
, 1);
3340 DRWAV_PRIVATE
size_t drwav__write_u16ne_to_le(drwav
* pWav
, drwav_uint16 value
)
3342 DRWAV_ASSERT(pWav
!= NULL
);
3343 DRWAV_ASSERT(pWav
->onWrite
!= NULL
);
3345 if (!drwav__is_little_endian()) {
3346 value
= drwav__bswap16(value
);
3349 return drwav__write(pWav
, &value
, 2);
3352 DRWAV_PRIVATE
size_t drwav__write_u32ne_to_le(drwav
* pWav
, drwav_uint32 value
)
3354 DRWAV_ASSERT(pWav
!= NULL
);
3355 DRWAV_ASSERT(pWav
->onWrite
!= NULL
);
3357 if (!drwav__is_little_endian()) {
3358 value
= drwav__bswap32(value
);
3361 return drwav__write(pWav
, &value
, 4);
3364 DRWAV_PRIVATE
size_t drwav__write_u64ne_to_le(drwav
* pWav
, drwav_uint64 value
)
3366 DRWAV_ASSERT(pWav
!= NULL
);
3367 DRWAV_ASSERT(pWav
->onWrite
!= NULL
);
3369 if (!drwav__is_little_endian()) {
3370 value
= drwav__bswap64(value
);
3373 return drwav__write(pWav
, &value
, 8);
3376 DRWAV_PRIVATE
size_t drwav__write_f32ne_to_le(drwav
* pWav
, float value
)
3383 DRWAV_ASSERT(pWav
!= NULL
);
3384 DRWAV_ASSERT(pWav
->onWrite
!= NULL
);
3388 if (!drwav__is_little_endian()) {
3389 u
.u32
= drwav__bswap32(u
.u32
);
3392 return drwav__write(pWav
, &u
.u32
, 4);
3395 DRWAV_PRIVATE
size_t drwav__write_or_count(drwav
* pWav
, const void* pData
, size_t dataSize
)
3401 return drwav__write(pWav
, pData
, dataSize
);
3404 DRWAV_PRIVATE
size_t drwav__write_or_count_byte(drwav
* pWav
, drwav_uint8 byte
)
3410 return drwav__write_byte(pWav
, byte
);
3413 DRWAV_PRIVATE
size_t drwav__write_or_count_u16ne_to_le(drwav
* pWav
, drwav_uint16 value
)
3419 return drwav__write_u16ne_to_le(pWav
, value
);
3422 DRWAV_PRIVATE
size_t drwav__write_or_count_u32ne_to_le(drwav
* pWav
, drwav_uint32 value
)
3428 return drwav__write_u32ne_to_le(pWav
, value
);
3431 #if 0 /* Unused for now. */
3432 DRWAV_PRIVATE
size_t drwav__write_or_count_u64ne_to_le(drwav
* pWav
, drwav_uint64 value
)
3438 return drwav__write_u64ne_to_le(pWav
, value
);
3442 DRWAV_PRIVATE
size_t drwav__write_or_count_f32ne_to_le(drwav
* pWav
, float value
)
3448 return drwav__write_f32ne_to_le(pWav
, value
);
3451 DRWAV_PRIVATE
size_t drwav__write_or_count_string_to_fixed_size_buf(drwav
* pWav
, char* str
, size_t bufFixedSize
)
3456 return bufFixedSize
;
3459 len
= drwav__strlen_clamped(str
, bufFixedSize
);
3460 drwav__write_or_count(pWav
, str
, len
);
3462 if (len
< bufFixedSize
) {
3464 for (i
= 0; i
< bufFixedSize
- len
; ++i
) {
3465 drwav__write_byte(pWav
, 0);
3469 return bufFixedSize
;
3473 /* pWav can be NULL meaning just count the bytes that would be written. */
3474 DRWAV_PRIVATE
size_t drwav__write_or_count_metadata(drwav
* pWav
, drwav_metadata
* pMetadatas
, drwav_uint32 metadataCount
)
3476 size_t bytesWritten
= 0;
3477 drwav_bool32 hasListAdtl
= DRWAV_FALSE
;
3478 drwav_bool32 hasListInfo
= DRWAV_FALSE
;
3479 drwav_uint32 iMetadata
;
3481 if (pMetadatas
== NULL
|| metadataCount
== 0) {
3485 for (iMetadata
= 0; iMetadata
< metadataCount
; ++iMetadata
) {
3486 drwav_metadata
* pMetadata
= &pMetadatas
[iMetadata
];
3487 drwav_uint32 chunkSize
= 0;
3489 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
)) {
3490 hasListInfo
= DRWAV_TRUE
;
3493 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
)) {
3494 hasListAdtl
= DRWAV_TRUE
;
3497 switch (pMetadata
->type
) {
3498 case drwav_metadata_type_smpl
:
3502 chunkSize
= DRWAV_SMPL_BYTES
+ DRWAV_SMPL_LOOP_BYTES
* pMetadata
->data
.smpl
.sampleLoopCount
+ pMetadata
->data
.smpl
.samplerSpecificDataSizeInBytes
;
3504 bytesWritten
+= drwav__write_or_count(pWav
, "smpl", 4);
3505 bytesWritten
+= drwav__write_or_count_u32ne_to_le(pWav
, chunkSize
);
3507 bytesWritten
+= drwav__write_or_count_u32ne_to_le(pWav
, pMetadata
->data
.smpl
.manufacturerId
);
3508 bytesWritten
+= drwav__write_or_count_u32ne_to_le(pWav
, pMetadata
->data
.smpl
.productId
);
3509 bytesWritten
+= drwav__write_or_count_u32ne_to_le(pWav
, pMetadata
->data
.smpl
.samplePeriodNanoseconds
);
3510 bytesWritten
+= drwav__write_or_count_u32ne_to_le(pWav
, pMetadata
->data
.smpl
.midiUnityNote
);
3511 bytesWritten
+= drwav__write_or_count_u32ne_to_le(pWav
, pMetadata
->data
.smpl
.midiPitchFraction
);
3512 bytesWritten
+= drwav__write_or_count_u32ne_to_le(pWav
, pMetadata
->data
.smpl
.smpteFormat
);
3513 bytesWritten
+= drwav__write_or_count_u32ne_to_le(pWav
, pMetadata
->data
.smpl
.smpteOffset
);
3514 bytesWritten
+= drwav__write_or_count_u32ne_to_le(pWav
, pMetadata
->data
.smpl
.sampleLoopCount
);
3515 bytesWritten
+= drwav__write_or_count_u32ne_to_le(pWav
, pMetadata
->data
.smpl
.samplerSpecificDataSizeInBytes
);
3517 for (iLoop
= 0; iLoop
< pMetadata
->data
.smpl
.sampleLoopCount
; ++iLoop
) {
3518 bytesWritten
+= drwav__write_or_count_u32ne_to_le(pWav
, pMetadata
->data
.smpl
.pLoops
[iLoop
].cuePointId
);
3519 bytesWritten
+= drwav__write_or_count_u32ne_to_le(pWav
, pMetadata
->data
.smpl
.pLoops
[iLoop
].type
);
3520 bytesWritten
+= drwav__write_or_count_u32ne_to_le(pWav
, pMetadata
->data
.smpl
.pLoops
[iLoop
].firstSampleByteOffset
);
3521 bytesWritten
+= drwav__write_or_count_u32ne_to_le(pWav
, pMetadata
->data
.smpl
.pLoops
[iLoop
].lastSampleByteOffset
);
3522 bytesWritten
+= drwav__write_or_count_u32ne_to_le(pWav
, pMetadata
->data
.smpl
.pLoops
[iLoop
].sampleFraction
);
3523 bytesWritten
+= drwav__write_or_count_u32ne_to_le(pWav
, pMetadata
->data
.smpl
.pLoops
[iLoop
].playCount
);
3526 if (pMetadata
->data
.smpl
.samplerSpecificDataSizeInBytes
> 0) {
3527 bytesWritten
+= drwav__write(pWav
, pMetadata
->data
.smpl
.pSamplerSpecificData
, pMetadata
->data
.smpl
.samplerSpecificDataSizeInBytes
);
3531 case drwav_metadata_type_inst
:
3533 chunkSize
= DRWAV_INST_BYTES
;
3535 bytesWritten
+= drwav__write_or_count(pWav
, "inst", 4);
3536 bytesWritten
+= drwav__write_or_count_u32ne_to_le(pWav
, chunkSize
);
3537 bytesWritten
+= drwav__write_or_count(pWav
, &pMetadata
->data
.inst
.midiUnityNote
, 1);
3538 bytesWritten
+= drwav__write_or_count(pWav
, &pMetadata
->data
.inst
.fineTuneCents
, 1);
3539 bytesWritten
+= drwav__write_or_count(pWav
, &pMetadata
->data
.inst
.gainDecibels
, 1);
3540 bytesWritten
+= drwav__write_or_count(pWav
, &pMetadata
->data
.inst
.lowNote
, 1);
3541 bytesWritten
+= drwav__write_or_count(pWav
, &pMetadata
->data
.inst
.highNote
, 1);
3542 bytesWritten
+= drwav__write_or_count(pWav
, &pMetadata
->data
.inst
.lowVelocity
, 1);
3543 bytesWritten
+= drwav__write_or_count(pWav
, &pMetadata
->data
.inst
.highVelocity
, 1);
3546 case drwav_metadata_type_cue
:
3548 drwav_uint32 iCuePoint
;
3550 chunkSize
= DRWAV_CUE_BYTES
+ DRWAV_CUE_POINT_BYTES
* pMetadata
->data
.cue
.cuePointCount
;
3552 bytesWritten
+= drwav__write_or_count(pWav
, "cue ", 4);
3553 bytesWritten
+= drwav__write_or_count_u32ne_to_le(pWav
, chunkSize
);
3554 bytesWritten
+= drwav__write_or_count_u32ne_to_le(pWav
, pMetadata
->data
.cue
.cuePointCount
);
3555 for (iCuePoint
= 0; iCuePoint
< pMetadata
->data
.cue
.cuePointCount
; ++iCuePoint
) {
3556 bytesWritten
+= drwav__write_or_count_u32ne_to_le(pWav
, pMetadata
->data
.cue
.pCuePoints
[iCuePoint
].id
);
3557 bytesWritten
+= drwav__write_or_count_u32ne_to_le(pWav
, pMetadata
->data
.cue
.pCuePoints
[iCuePoint
].playOrderPosition
);
3558 bytesWritten
+= drwav__write_or_count(pWav
, pMetadata
->data
.cue
.pCuePoints
[iCuePoint
].dataChunkId
, 4);
3559 bytesWritten
+= drwav__write_or_count_u32ne_to_le(pWav
, pMetadata
->data
.cue
.pCuePoints
[iCuePoint
].chunkStart
);
3560 bytesWritten
+= drwav__write_or_count_u32ne_to_le(pWav
, pMetadata
->data
.cue
.pCuePoints
[iCuePoint
].blockStart
);
3561 bytesWritten
+= drwav__write_or_count_u32ne_to_le(pWav
, pMetadata
->data
.cue
.pCuePoints
[iCuePoint
].sampleByteOffset
);
3565 case drwav_metadata_type_acid
:
3567 chunkSize
= DRWAV_ACID_BYTES
;
3569 bytesWritten
+= drwav__write_or_count(pWav
, "acid", 4);
3570 bytesWritten
+= drwav__write_or_count_u32ne_to_le(pWav
, chunkSize
);
3571 bytesWritten
+= drwav__write_or_count_u32ne_to_le(pWav
, pMetadata
->data
.acid
.flags
);
3572 bytesWritten
+= drwav__write_or_count_u16ne_to_le(pWav
, pMetadata
->data
.acid
.midiUnityNote
);
3573 bytesWritten
+= drwav__write_or_count_u16ne_to_le(pWav
, pMetadata
->data
.acid
.reserved1
);
3574 bytesWritten
+= drwav__write_or_count_f32ne_to_le(pWav
, pMetadata
->data
.acid
.reserved2
);
3575 bytesWritten
+= drwav__write_or_count_u32ne_to_le(pWav
, pMetadata
->data
.acid
.numBeats
);
3576 bytesWritten
+= drwav__write_or_count_u16ne_to_le(pWav
, pMetadata
->data
.acid
.meterDenominator
);
3577 bytesWritten
+= drwav__write_or_count_u16ne_to_le(pWav
, pMetadata
->data
.acid
.meterNumerator
);
3578 bytesWritten
+= drwav__write_or_count_f32ne_to_le(pWav
, pMetadata
->data
.acid
.tempo
);
3581 case drwav_metadata_type_bext
:
3583 char reservedBuf
[DRWAV_BEXT_RESERVED_BYTES
];
3584 drwav_uint32 timeReferenceLow
;
3585 drwav_uint32 timeReferenceHigh
;
3587 chunkSize
= DRWAV_BEXT_BYTES
+ pMetadata
->data
.bext
.codingHistorySize
;
3589 bytesWritten
+= drwav__write_or_count(pWav
, "bext", 4);
3590 bytesWritten
+= drwav__write_or_count_u32ne_to_le(pWav
, chunkSize
);
3592 bytesWritten
+= drwav__write_or_count_string_to_fixed_size_buf(pWav
, pMetadata
->data
.bext
.pDescription
, DRWAV_BEXT_DESCRIPTION_BYTES
);
3593 bytesWritten
+= drwav__write_or_count_string_to_fixed_size_buf(pWav
, pMetadata
->data
.bext
.pOriginatorName
, DRWAV_BEXT_ORIGINATOR_NAME_BYTES
);
3594 bytesWritten
+= drwav__write_or_count_string_to_fixed_size_buf(pWav
, pMetadata
->data
.bext
.pOriginatorReference
, DRWAV_BEXT_ORIGINATOR_REF_BYTES
);
3595 bytesWritten
+= drwav__write_or_count(pWav
, pMetadata
->data
.bext
.pOriginationDate
, sizeof(pMetadata
->data
.bext
.pOriginationDate
));
3596 bytesWritten
+= drwav__write_or_count(pWav
, pMetadata
->data
.bext
.pOriginationTime
, sizeof(pMetadata
->data
.bext
.pOriginationTime
));
3598 timeReferenceLow
= (drwav_uint32
)(pMetadata
->data
.bext
.timeReference
& 0xFFFFFFFF);
3599 timeReferenceHigh
= (drwav_uint32
)(pMetadata
->data
.bext
.timeReference
>> 32);
3600 bytesWritten
+= drwav__write_or_count_u32ne_to_le(pWav
, timeReferenceLow
);
3601 bytesWritten
+= drwav__write_or_count_u32ne_to_le(pWav
, timeReferenceHigh
);
3603 bytesWritten
+= drwav__write_or_count_u16ne_to_le(pWav
, pMetadata
->data
.bext
.version
);
3604 bytesWritten
+= drwav__write_or_count(pWav
, pMetadata
->data
.bext
.pUMID
, DRWAV_BEXT_UMID_BYTES
);
3605 bytesWritten
+= drwav__write_or_count_u16ne_to_le(pWav
, pMetadata
->data
.bext
.loudnessValue
);
3606 bytesWritten
+= drwav__write_or_count_u16ne_to_le(pWav
, pMetadata
->data
.bext
.loudnessRange
);
3607 bytesWritten
+= drwav__write_or_count_u16ne_to_le(pWav
, pMetadata
->data
.bext
.maxTruePeakLevel
);
3608 bytesWritten
+= drwav__write_or_count_u16ne_to_le(pWav
, pMetadata
->data
.bext
.maxMomentaryLoudness
);
3609 bytesWritten
+= drwav__write_or_count_u16ne_to_le(pWav
, pMetadata
->data
.bext
.maxShortTermLoudness
);
3611 memset(reservedBuf
, 0, sizeof(reservedBuf
));
3612 bytesWritten
+= drwav__write_or_count(pWav
, reservedBuf
, sizeof(reservedBuf
));
3614 if (pMetadata
->data
.bext
.codingHistorySize
> 0) {
3615 bytesWritten
+= drwav__write_or_count(pWav
, pMetadata
->data
.bext
.pCodingHistory
, pMetadata
->data
.bext
.codingHistorySize
);
3619 case drwav_metadata_type_unknown
:
3621 if (pMetadata
->data
.unknown
.chunkLocation
== drwav_metadata_location_top_level
) {
3622 chunkSize
= pMetadata
->data
.unknown
.dataSizeInBytes
;
3624 bytesWritten
+= drwav__write_or_count(pWav
, pMetadata
->data
.unknown
.id
, 4);
3625 bytesWritten
+= drwav__write_or_count_u32ne_to_le(pWav
, chunkSize
);
3626 bytesWritten
+= drwav__write_or_count(pWav
, pMetadata
->data
.unknown
.pData
, pMetadata
->data
.unknown
.dataSizeInBytes
);
3632 if ((chunkSize
% 2) != 0) {
3633 bytesWritten
+= drwav__write_or_count_byte(pWav
, 0);
3638 drwav_uint32 chunkSize
= 4; /* Start with 4 bytes for "INFO". */
3639 for (iMetadata
= 0; iMetadata
< metadataCount
; ++iMetadata
) {
3640 drwav_metadata
* pMetadata
= &pMetadatas
[iMetadata
];
3642 if ((pMetadata
->type
& drwav_metadata_type_list_all_info_strings
)) {
3643 chunkSize
+= 8; /* For id and string size. */
3644 chunkSize
+= pMetadata
->data
.infoText
.stringLength
+ 1; /* Include null terminator. */
3645 } else if (pMetadata
->type
== drwav_metadata_type_unknown
&& pMetadata
->data
.unknown
.chunkLocation
== drwav_metadata_location_inside_info_list
) {
3646 chunkSize
+= 8; /* For id string size. */
3647 chunkSize
+= pMetadata
->data
.unknown
.dataSizeInBytes
;
3650 if ((chunkSize
% 2) != 0) {
3655 bytesWritten
+= drwav__write_or_count(pWav
, "LIST", 4);
3656 bytesWritten
+= drwav__write_or_count_u32ne_to_le(pWav
, chunkSize
);
3657 bytesWritten
+= drwav__write_or_count(pWav
, "INFO", 4);
3659 for (iMetadata
= 0; iMetadata
< metadataCount
; ++iMetadata
) {
3660 drwav_metadata
* pMetadata
= &pMetadatas
[iMetadata
];
3661 drwav_uint32 subchunkSize
= 0;
3663 if (pMetadata
->type
& drwav_metadata_type_list_all_info_strings
) {
3664 const char* pID
= NULL
;
3666 switch (pMetadata
->type
) {
3667 case drwav_metadata_type_list_info_software
: pID
= "ISFT"; break;
3668 case drwav_metadata_type_list_info_copyright
: pID
= "ICOP"; break;
3669 case drwav_metadata_type_list_info_title
: pID
= "INAM"; break;
3670 case drwav_metadata_type_list_info_artist
: pID
= "IART"; break;
3671 case drwav_metadata_type_list_info_comment
: pID
= "ICMT"; break;
3672 case drwav_metadata_type_list_info_date
: pID
= "ICRD"; break;
3673 case drwav_metadata_type_list_info_genre
: pID
= "IGNR"; break;
3674 case drwav_metadata_type_list_info_album
: pID
= "IPRD"; break;
3675 case drwav_metadata_type_list_info_tracknumber
: pID
= "ITRK"; break;
3679 DRWAV_ASSERT(pID
!= NULL
);
3681 if (pMetadata
->data
.infoText
.stringLength
) {
3682 subchunkSize
= pMetadata
->data
.infoText
.stringLength
+ 1;
3683 bytesWritten
+= drwav__write_or_count(pWav
, pID
, 4);
3684 bytesWritten
+= drwav__write_or_count_u32ne_to_le(pWav
, subchunkSize
);
3685 bytesWritten
+= drwav__write_or_count(pWav
, pMetadata
->data
.infoText
.pString
, pMetadata
->data
.infoText
.stringLength
);
3686 bytesWritten
+= drwav__write_or_count_byte(pWav
, '\0');
3688 } else if (pMetadata
->type
== drwav_metadata_type_unknown
&& pMetadata
->data
.unknown
.chunkLocation
== drwav_metadata_location_inside_info_list
) {
3689 if (pMetadata
->data
.unknown
.dataSizeInBytes
) {
3690 subchunkSize
= pMetadata
->data
.unknown
.dataSizeInBytes
;
3692 bytesWritten
+= drwav__write_or_count(pWav
, pMetadata
->data
.unknown
.id
, 4);
3693 bytesWritten
+= drwav__write_or_count_u32ne_to_le(pWav
, pMetadata
->data
.unknown
.dataSizeInBytes
);
3694 bytesWritten
+= drwav__write_or_count(pWav
, pMetadata
->data
.unknown
.pData
, subchunkSize
);
3698 if ((subchunkSize
% 2) != 0) {
3699 bytesWritten
+= drwav__write_or_count_byte(pWav
, 0);
3705 drwav_uint32 chunkSize
= 4; /* start with 4 bytes for "adtl" */
3707 for (iMetadata
= 0; iMetadata
< metadataCount
; ++iMetadata
) {
3708 drwav_metadata
* pMetadata
= &pMetadatas
[iMetadata
];
3710 switch (pMetadata
->type
)
3712 case drwav_metadata_type_list_label
:
3713 case drwav_metadata_type_list_note
:
3715 chunkSize
+= 8; /* for id and chunk size */
3716 chunkSize
+= DRWAV_LIST_LABEL_OR_NOTE_BYTES
;
3718 if (pMetadata
->data
.labelOrNote
.stringLength
> 0) {
3719 chunkSize
+= pMetadata
->data
.labelOrNote
.stringLength
+ 1;
3723 case drwav_metadata_type_list_labelled_cue_region
:
3725 chunkSize
+= 8; /* for id and chunk size */
3726 chunkSize
+= DRWAV_LIST_LABELLED_TEXT_BYTES
;
3728 if (pMetadata
->data
.labelledCueRegion
.stringLength
> 0) {
3729 chunkSize
+= pMetadata
->data
.labelledCueRegion
.stringLength
+ 1;
3733 case drwav_metadata_type_unknown
:
3735 if (pMetadata
->data
.unknown
.chunkLocation
== drwav_metadata_location_inside_adtl_list
) {
3736 chunkSize
+= 8; /* for id and chunk size */
3737 chunkSize
+= pMetadata
->data
.unknown
.dataSizeInBytes
;
3744 if ((chunkSize
% 2) != 0) {
3749 bytesWritten
+= drwav__write_or_count(pWav
, "LIST", 4);
3750 bytesWritten
+= drwav__write_or_count_u32ne_to_le(pWav
, chunkSize
);
3751 bytesWritten
+= drwav__write_or_count(pWav
, "adtl", 4);
3753 for (iMetadata
= 0; iMetadata
< metadataCount
; ++iMetadata
) {
3754 drwav_metadata
* pMetadata
= &pMetadatas
[iMetadata
];
3755 drwav_uint32 subchunkSize
= 0;
3757 switch (pMetadata
->type
)
3759 case drwav_metadata_type_list_label
:
3760 case drwav_metadata_type_list_note
:
3762 if (pMetadata
->data
.labelOrNote
.stringLength
> 0) {
3763 const char *pID
= NULL
;
3765 if (pMetadata
->type
== drwav_metadata_type_list_label
) {
3768 else if (pMetadata
->type
== drwav_metadata_type_list_note
) {
3772 DRWAV_ASSERT(pID
!= NULL
);
3773 DRWAV_ASSERT(pMetadata
->data
.labelOrNote
.pString
!= NULL
);
3775 subchunkSize
= DRWAV_LIST_LABEL_OR_NOTE_BYTES
;
3777 bytesWritten
+= drwav__write_or_count(pWav
, pID
, 4);
3778 subchunkSize
+= pMetadata
->data
.labelOrNote
.stringLength
+ 1;
3779 bytesWritten
+= drwav__write_or_count_u32ne_to_le(pWav
, subchunkSize
);
3781 bytesWritten
+= drwav__write_or_count_u32ne_to_le(pWav
, pMetadata
->data
.labelOrNote
.cuePointId
);
3782 bytesWritten
+= drwav__write_or_count(pWav
, pMetadata
->data
.labelOrNote
.pString
, pMetadata
->data
.labelOrNote
.stringLength
);
3783 bytesWritten
+= drwav__write_or_count_byte(pWav
, '\0');
3787 case drwav_metadata_type_list_labelled_cue_region
:
3789 subchunkSize
= DRWAV_LIST_LABELLED_TEXT_BYTES
;
3791 bytesWritten
+= drwav__write_or_count(pWav
, "ltxt", 4);
3792 if (pMetadata
->data
.labelledCueRegion
.stringLength
> 0) {
3793 subchunkSize
+= pMetadata
->data
.labelledCueRegion
.stringLength
+ 1;
3795 bytesWritten
+= drwav__write_or_count_u32ne_to_le(pWav
, subchunkSize
);
3796 bytesWritten
+= drwav__write_or_count_u32ne_to_le(pWav
, pMetadata
->data
.labelledCueRegion
.cuePointId
);
3797 bytesWritten
+= drwav__write_or_count_u32ne_to_le(pWav
, pMetadata
->data
.labelledCueRegion
.sampleLength
);
3798 bytesWritten
+= drwav__write_or_count(pWav
, pMetadata
->data
.labelledCueRegion
.purposeId
, 4);
3799 bytesWritten
+= drwav__write_or_count_u16ne_to_le(pWav
, pMetadata
->data
.labelledCueRegion
.country
);
3800 bytesWritten
+= drwav__write_or_count_u16ne_to_le(pWav
, pMetadata
->data
.labelledCueRegion
.language
);
3801 bytesWritten
+= drwav__write_or_count_u16ne_to_le(pWav
, pMetadata
->data
.labelledCueRegion
.dialect
);
3802 bytesWritten
+= drwav__write_or_count_u16ne_to_le(pWav
, pMetadata
->data
.labelledCueRegion
.codePage
);
3804 if (pMetadata
->data
.labelledCueRegion
.stringLength
> 0) {
3805 DRWAV_ASSERT(pMetadata
->data
.labelledCueRegion
.pString
!= NULL
);
3807 bytesWritten
+= drwav__write_or_count(pWav
, pMetadata
->data
.labelledCueRegion
.pString
, pMetadata
->data
.labelledCueRegion
.stringLength
);
3808 bytesWritten
+= drwav__write_or_count_byte(pWav
, '\0');
3812 case drwav_metadata_type_unknown
:
3814 if (pMetadata
->data
.unknown
.chunkLocation
== drwav_metadata_location_inside_adtl_list
) {
3815 subchunkSize
= pMetadata
->data
.unknown
.dataSizeInBytes
;
3817 DRWAV_ASSERT(pMetadata
->data
.unknown
.pData
!= NULL
);
3818 bytesWritten
+= drwav__write_or_count(pWav
, pMetadata
->data
.unknown
.id
, 4);
3819 bytesWritten
+= drwav__write_or_count_u32ne_to_le(pWav
, subchunkSize
);
3820 bytesWritten
+= drwav__write_or_count(pWav
, pMetadata
->data
.unknown
.pData
, subchunkSize
);
3827 if ((subchunkSize
% 2) != 0) {
3828 bytesWritten
+= drwav__write_or_count_byte(pWav
, 0);
3833 DRWAV_ASSERT((bytesWritten
% 2) == 0);
3835 return bytesWritten
;
3838 DRWAV_PRIVATE drwav_uint32
drwav__riff_chunk_size_riff(drwav_uint64 dataChunkSize
, drwav_metadata
* pMetadata
, drwav_uint32 metadataCount
)
3840 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. */
3841 if (chunkSize
> 0xFFFFFFFFUL
) {
3842 chunkSize
= 0xFFFFFFFFUL
;
3845 return (drwav_uint32
)chunkSize
; /* Safe cast due to the clamp above. */
3848 DRWAV_PRIVATE drwav_uint32
drwav__data_chunk_size_riff(drwav_uint64 dataChunkSize
)
3850 if (dataChunkSize
<= 0xFFFFFFFFUL
) {
3851 return (drwav_uint32
)dataChunkSize
;
3853 return 0xFFFFFFFFUL
;
3857 DRWAV_PRIVATE drwav_uint64
drwav__riff_chunk_size_w64(drwav_uint64 dataChunkSize
)
3859 drwav_uint64 dataSubchunkPaddingSize
= drwav__chunk_padding_size_w64(dataChunkSize
);
3861 return 80 + 24 + dataChunkSize
+ dataSubchunkPaddingSize
; /* +24 because W64 includes the size of the GUID and size fields. */
3864 DRWAV_PRIVATE drwav_uint64
drwav__data_chunk_size_w64(drwav_uint64 dataChunkSize
)
3866 return 24 + dataChunkSize
; /* +24 because W64 includes the size of the GUID and size fields. */
3869 DRWAV_PRIVATE drwav_uint64
drwav__riff_chunk_size_rf64(drwav_uint64 dataChunkSize
, drwav_metadata
*metadata
, drwav_uint32 numMetadata
)
3871 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. */
3872 if (chunkSize
> 0xFFFFFFFFUL
) {
3873 chunkSize
= 0xFFFFFFFFUL
;
3879 DRWAV_PRIVATE drwav_uint64
drwav__data_chunk_size_rf64(drwav_uint64 dataChunkSize
)
3881 return dataChunkSize
;
3886 DRWAV_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
)
3888 if (pWav
== NULL
|| onWrite
== NULL
) {
3892 if (!isSequential
&& onSeek
== NULL
) {
3893 return DRWAV_FALSE
; /* <-- onSeek is required when in non-sequential mode. */
3896 /* Not currently supporting compressed formats. Will need to add support for the "fact" chunk before we enable this. */
3897 if (pFormat
->format
== DR_WAVE_FORMAT_EXTENSIBLE
) {
3900 if (pFormat
->format
== DR_WAVE_FORMAT_ADPCM
|| pFormat
->format
== DR_WAVE_FORMAT_DVI_ADPCM
) {
3904 DRWAV_ZERO_MEMORY(pWav
, sizeof(*pWav
));
3905 pWav
->onWrite
= onWrite
;
3906 pWav
->onSeek
= onSeek
;
3907 pWav
->pUserData
= pUserData
;
3908 pWav
->allocationCallbacks
= drwav_copy_allocation_callbacks_or_defaults(pAllocationCallbacks
);
3910 if (pWav
->allocationCallbacks
.onFree
== NULL
|| (pWav
->allocationCallbacks
.onMalloc
== NULL
&& pWav
->allocationCallbacks
.onRealloc
== NULL
)) {
3911 return DRWAV_FALSE
; /* Invalid allocation callbacks. */
3914 pWav
->fmt
.formatTag
= (drwav_uint16
)pFormat
->format
;
3915 pWav
->fmt
.channels
= (drwav_uint16
)pFormat
->channels
;
3916 pWav
->fmt
.sampleRate
= pFormat
->sampleRate
;
3917 pWav
->fmt
.avgBytesPerSec
= (drwav_uint32
)((pFormat
->bitsPerSample
* pFormat
->sampleRate
* pFormat
->channels
) / 8);
3918 pWav
->fmt
.blockAlign
= (drwav_uint16
)((pFormat
->channels
* pFormat
->bitsPerSample
) / 8);
3919 pWav
->fmt
.bitsPerSample
= (drwav_uint16
)pFormat
->bitsPerSample
;
3920 pWav
->fmt
.extendedSize
= 0;
3921 pWav
->isSequentialWrite
= isSequential
;
3927 DRWAV_PRIVATE drwav_bool32
drwav_init_write__internal(drwav
* pWav
, const drwav_data_format
* pFormat
, drwav_uint64 totalSampleCount
)
3929 /* The function assumes drwav_preinit_write() was called beforehand. */
3931 size_t runningPos
= 0;
3932 drwav_uint64 initialDataChunkSize
= 0;
3933 drwav_uint64 chunkSizeFMT
;
3936 The initial values for the "RIFF" and "data" chunks depends on whether or not we are initializing in sequential mode or not. In
3937 sequential mode we set this to its final values straight away since they can be calculated from the total sample count. In non-
3938 sequential mode we initialize it all to zero and fill it out in drwav_uninit() using a backwards seek.
3940 if (pWav
->isSequentialWrite
) {
3941 initialDataChunkSize
= (totalSampleCount
* pWav
->fmt
.bitsPerSample
) / 8;
3944 The RIFF container has a limit on the number of samples. drwav is not allowing this. There's no practical limits for Wave64
3945 so for the sake of simplicity I'm not doing any validation for that.
3947 if (pFormat
->container
== drwav_container_riff
) {
3948 if (initialDataChunkSize
> (0xFFFFFFFFUL
- 36)) {
3949 return DRWAV_FALSE
; /* Not enough room to store every sample. */
3954 pWav
->dataChunkDataSizeTargetWrite
= initialDataChunkSize
;
3958 if (pFormat
->container
== drwav_container_riff
) {
3959 drwav_uint32 chunkSizeRIFF
= 28 + (drwav_uint32
)initialDataChunkSize
; /* +28 = "WAVE" + [sizeof "fmt " chunk] */
3960 runningPos
+= drwav__write(pWav
, "RIFF", 4);
3961 runningPos
+= drwav__write_u32ne_to_le(pWav
, chunkSizeRIFF
);
3962 runningPos
+= drwav__write(pWav
, "WAVE", 4);
3963 } else if (pFormat
->container
== drwav_container_w64
) {
3964 drwav_uint64 chunkSizeRIFF
= 80 + 24 + initialDataChunkSize
; /* +24 because W64 includes the size of the GUID and size fields. */
3965 runningPos
+= drwav__write(pWav
, drwavGUID_W64_RIFF
, 16);
3966 runningPos
+= drwav__write_u64ne_to_le(pWav
, chunkSizeRIFF
);
3967 runningPos
+= drwav__write(pWav
, drwavGUID_W64_WAVE
, 16);
3968 } else if (pFormat
->container
== drwav_container_rf64
) {
3969 runningPos
+= drwav__write(pWav
, "RF64", 4);
3970 runningPos
+= drwav__write_u32ne_to_le(pWav
, 0xFFFFFFFF); /* Always 0xFFFFFFFF for RF64. Set to a proper value in the "ds64" chunk. */
3971 runningPos
+= drwav__write(pWav
, "WAVE", 4);
3975 /* "ds64" chunk (RF64 only). */
3976 if (pFormat
->container
== drwav_container_rf64
) {
3977 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. */
3978 drwav_uint64 initialRiffChunkSize
= 8 + initialds64ChunkSize
+ initialDataChunkSize
; /* +8 for the ds64 header. */
3980 runningPos
+= drwav__write(pWav
, "ds64", 4);
3981 runningPos
+= drwav__write_u32ne_to_le(pWav
, initialds64ChunkSize
); /* Size of ds64. */
3982 runningPos
+= drwav__write_u64ne_to_le(pWav
, initialRiffChunkSize
); /* Size of RIFF. Set to true value at the end. */
3983 runningPos
+= drwav__write_u64ne_to_le(pWav
, initialDataChunkSize
); /* Size of DATA. Set to true value at the end. */
3984 runningPos
+= drwav__write_u64ne_to_le(pWav
, totalSampleCount
); /* Sample count. */
3985 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". */
3990 if (pFormat
->container
== drwav_container_riff
|| pFormat
->container
== drwav_container_rf64
) {
3992 runningPos
+= drwav__write(pWav
, "fmt ", 4);
3993 runningPos
+= drwav__write_u32ne_to_le(pWav
, (drwav_uint32
)chunkSizeFMT
);
3994 } else if (pFormat
->container
== drwav_container_w64
) {
3996 runningPos
+= drwav__write(pWav
, drwavGUID_W64_FMT
, 16);
3997 runningPos
+= drwav__write_u64ne_to_le(pWav
, chunkSizeFMT
);
4000 runningPos
+= drwav__write_u16ne_to_le(pWav
, pWav
->fmt
.formatTag
);
4001 runningPos
+= drwav__write_u16ne_to_le(pWav
, pWav
->fmt
.channels
);
4002 runningPos
+= drwav__write_u32ne_to_le(pWav
, pWav
->fmt
.sampleRate
);
4003 runningPos
+= drwav__write_u32ne_to_le(pWav
, pWav
->fmt
.avgBytesPerSec
);
4004 runningPos
+= drwav__write_u16ne_to_le(pWav
, pWav
->fmt
.blockAlign
);
4005 runningPos
+= drwav__write_u16ne_to_le(pWav
, pWav
->fmt
.bitsPerSample
);
4007 /* TODO: is a 'fact' chunk required for DR_WAVE_FORMAT_IEEE_FLOAT? */
4009 if (!pWav
->isSequentialWrite
&& pWav
->pMetadata
!= NULL
&& pWav
->metadataCount
> 0 && (pFormat
->container
== drwav_container_riff
|| pFormat
->container
== drwav_container_rf64
)) {
4010 runningPos
+= drwav__write_or_count_metadata(pWav
, pWav
->pMetadata
, pWav
->metadataCount
);
4013 pWav
->dataChunkDataPos
= runningPos
;
4016 if (pFormat
->container
== drwav_container_riff
) {
4017 drwav_uint32 chunkSizeDATA
= (drwav_uint32
)initialDataChunkSize
;
4018 runningPos
+= drwav__write(pWav
, "data", 4);
4019 runningPos
+= drwav__write_u32ne_to_le(pWav
, chunkSizeDATA
);
4020 } else if (pFormat
->container
== drwav_container_w64
) {
4021 drwav_uint64 chunkSizeDATA
= 24 + initialDataChunkSize
; /* +24 because W64 includes the size of the GUID and size fields. */
4022 runningPos
+= drwav__write(pWav
, drwavGUID_W64_DATA
, 16);
4023 runningPos
+= drwav__write_u64ne_to_le(pWav
, chunkSizeDATA
);
4024 } else if (pFormat
->container
== drwav_container_rf64
) {
4025 runningPos
+= drwav__write(pWav
, "data", 4);
4026 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. */
4029 /* Set some properties for the client's convenience. */
4030 pWav
->container
= pFormat
->container
;
4031 pWav
->channels
= (drwav_uint16
)pFormat
->channels
;
4032 pWav
->sampleRate
= pFormat
->sampleRate
;
4033 pWav
->bitsPerSample
= (drwav_uint16
)pFormat
->bitsPerSample
;
4034 pWav
->translatedFormatTag
= (drwav_uint16
)pFormat
->format
;
4035 pWav
->dataChunkDataPos
= runningPos
;
4041 DRWAV_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
)
4043 if (!drwav_preinit_write(pWav
, pFormat
, DRWAV_FALSE
, onWrite
, onSeek
, pUserData
, pAllocationCallbacks
)) {
4047 return drwav_init_write__internal(pWav
, pFormat
, 0); /* DRWAV_FALSE = Not Sequential */
4050 DRWAV_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
)
4052 if (!drwav_preinit_write(pWav
, pFormat
, DRWAV_TRUE
, onWrite
, NULL
, pUserData
, pAllocationCallbacks
)) {
4056 return drwav_init_write__internal(pWav
, pFormat
, totalSampleCount
); /* DRWAV_TRUE = Sequential */
4059 DRWAV_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
)
4061 if (pFormat
== NULL
) {
4065 return drwav_init_write_sequential(pWav
, pFormat
, totalPCMFrameCount
*pFormat
->channels
, onWrite
, pUserData
, pAllocationCallbacks
);
4068 DRWAV_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
)
4070 if (!drwav_preinit_write(pWav
, pFormat
, DRWAV_FALSE
, onWrite
, onSeek
, pUserData
, pAllocationCallbacks
)) {
4074 pWav
->pMetadata
= pMetadata
;
4075 pWav
->metadataCount
= metadataCount
;
4077 return drwav_init_write__internal(pWav
, pFormat
, 0);
4081 DRWAV_API drwav_uint64
drwav_target_write_size_bytes(const drwav_data_format
* pFormat
, drwav_uint64 totalFrameCount
, drwav_metadata
* pMetadata
, drwav_uint32 metadataCount
)
4083 /* Casting totalFrameCount to drwav_int64 for VC6 compatibility. No issues in practice because nobody is going to exhaust the whole 63 bits. */
4084 drwav_uint64 targetDataSizeBytes
= (drwav_uint64
)((drwav_int64
)totalFrameCount
* pFormat
->channels
* pFormat
->bitsPerSample
/8.0);
4085 drwav_uint64 riffChunkSizeBytes
;
4086 drwav_uint64 fileSizeBytes
= 0;
4088 if (pFormat
->container
== drwav_container_riff
) {
4089 riffChunkSizeBytes
= drwav__riff_chunk_size_riff(targetDataSizeBytes
, pMetadata
, metadataCount
);
4090 fileSizeBytes
= (8 + riffChunkSizeBytes
); /* +8 because WAV doesn't include the size of the ChunkID and ChunkSize fields. */
4091 } else if (pFormat
->container
== drwav_container_w64
) {
4092 riffChunkSizeBytes
= drwav__riff_chunk_size_w64(targetDataSizeBytes
);
4093 fileSizeBytes
= riffChunkSizeBytes
;
4094 } else if (pFormat
->container
== drwav_container_rf64
) {
4095 riffChunkSizeBytes
= drwav__riff_chunk_size_rf64(targetDataSizeBytes
, pMetadata
, metadataCount
);
4096 fileSizeBytes
= (8 + riffChunkSizeBytes
); /* +8 because WAV doesn't include the size of the ChunkID and ChunkSize fields. */
4099 return fileSizeBytes
;
4103 #ifndef DR_WAV_NO_STDIO
4105 /* 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. */
4107 DRWAV_PRIVATE drwav_result
drwav_result_from_errno(int e
)
4111 case 0: return DRWAV_SUCCESS
;
4113 case EPERM
: return DRWAV_INVALID_OPERATION
;
4116 case ENOENT
: return DRWAV_DOES_NOT_EXIST
;
4119 case ESRCH
: return DRWAV_DOES_NOT_EXIST
;
4122 case EINTR
: return DRWAV_INTERRUPT
;
4125 case EIO
: return DRWAV_IO_ERROR
;
4128 case ENXIO
: return DRWAV_DOES_NOT_EXIST
;
4131 case E2BIG
: return DRWAV_INVALID_ARGS
;
4134 case ENOEXEC
: return DRWAV_INVALID_FILE
;
4137 case EBADF
: return DRWAV_INVALID_FILE
;
4140 case ECHILD
: return DRWAV_ERROR
;
4143 case EAGAIN
: return DRWAV_UNAVAILABLE
;
4146 case ENOMEM
: return DRWAV_OUT_OF_MEMORY
;
4149 case EACCES
: return DRWAV_ACCESS_DENIED
;
4152 case EFAULT
: return DRWAV_BAD_ADDRESS
;
4155 case ENOTBLK
: return DRWAV_ERROR
;
4158 case EBUSY
: return DRWAV_BUSY
;
4161 case EEXIST
: return DRWAV_ALREADY_EXISTS
;
4164 case EXDEV
: return DRWAV_ERROR
;
4167 case ENODEV
: return DRWAV_DOES_NOT_EXIST
;
4170 case ENOTDIR
: return DRWAV_NOT_DIRECTORY
;
4173 case EISDIR
: return DRWAV_IS_DIRECTORY
;
4176 case EINVAL
: return DRWAV_INVALID_ARGS
;
4179 case ENFILE
: return DRWAV_TOO_MANY_OPEN_FILES
;
4182 case EMFILE
: return DRWAV_TOO_MANY_OPEN_FILES
;
4185 case ENOTTY
: return DRWAV_INVALID_OPERATION
;
4188 case ETXTBSY
: return DRWAV_BUSY
;
4191 case EFBIG
: return DRWAV_TOO_BIG
;
4194 case ENOSPC
: return DRWAV_NO_SPACE
;
4197 case ESPIPE
: return DRWAV_BAD_SEEK
;
4200 case EROFS
: return DRWAV_ACCESS_DENIED
;
4203 case EMLINK
: return DRWAV_TOO_MANY_LINKS
;
4206 case EPIPE
: return DRWAV_BAD_PIPE
;
4209 case EDOM
: return DRWAV_OUT_OF_RANGE
;
4212 case ERANGE
: return DRWAV_OUT_OF_RANGE
;
4215 case EDEADLK
: return DRWAV_DEADLOCK
;
4218 case ENAMETOOLONG
: return DRWAV_PATH_TOO_LONG
;
4221 case ENOLCK
: return DRWAV_ERROR
;
4224 case ENOSYS
: return DRWAV_NOT_IMPLEMENTED
;
4227 case ENOTEMPTY
: return DRWAV_DIRECTORY_NOT_EMPTY
;
4230 case ELOOP
: return DRWAV_TOO_MANY_LINKS
;
4233 case ENOMSG
: return DRWAV_NO_MESSAGE
;
4236 case EIDRM
: return DRWAV_ERROR
;
4239 case ECHRNG
: return DRWAV_ERROR
;
4242 case EL2NSYNC
: return DRWAV_ERROR
;
4245 case EL3HLT
: return DRWAV_ERROR
;
4248 case EL3RST
: return DRWAV_ERROR
;
4251 case ELNRNG
: return DRWAV_OUT_OF_RANGE
;
4254 case EUNATCH
: return DRWAV_ERROR
;
4257 case ENOCSI
: return DRWAV_ERROR
;
4260 case EL2HLT
: return DRWAV_ERROR
;
4263 case EBADE
: return DRWAV_ERROR
;
4266 case EBADR
: return DRWAV_ERROR
;
4269 case EXFULL
: return DRWAV_ERROR
;
4272 case ENOANO
: return DRWAV_ERROR
;
4275 case EBADRQC
: return DRWAV_ERROR
;
4278 case EBADSLT
: return DRWAV_ERROR
;
4281 case EBFONT
: return DRWAV_INVALID_FILE
;
4284 case ENOSTR
: return DRWAV_ERROR
;
4287 case ENODATA
: return DRWAV_NO_DATA_AVAILABLE
;
4290 case ETIME
: return DRWAV_TIMEOUT
;
4293 case ENOSR
: return DRWAV_NO_DATA_AVAILABLE
;
4296 case ENONET
: return DRWAV_NO_NETWORK
;
4299 case ENOPKG
: return DRWAV_ERROR
;
4302 case EREMOTE
: return DRWAV_ERROR
;
4305 case ENOLINK
: return DRWAV_ERROR
;
4308 case EADV
: return DRWAV_ERROR
;
4311 case ESRMNT
: return DRWAV_ERROR
;
4314 case ECOMM
: return DRWAV_ERROR
;
4317 case EPROTO
: return DRWAV_ERROR
;
4320 case EMULTIHOP
: return DRWAV_ERROR
;
4323 case EDOTDOT
: return DRWAV_ERROR
;
4326 case EBADMSG
: return DRWAV_BAD_MESSAGE
;
4329 case EOVERFLOW
: return DRWAV_TOO_BIG
;
4332 case ENOTUNIQ
: return DRWAV_NOT_UNIQUE
;
4335 case EBADFD
: return DRWAV_ERROR
;
4338 case EREMCHG
: return DRWAV_ERROR
;
4341 case ELIBACC
: return DRWAV_ACCESS_DENIED
;
4344 case ELIBBAD
: return DRWAV_INVALID_FILE
;
4347 case ELIBSCN
: return DRWAV_INVALID_FILE
;
4350 case ELIBMAX
: return DRWAV_ERROR
;
4353 case ELIBEXEC
: return DRWAV_ERROR
;
4356 case EILSEQ
: return DRWAV_INVALID_DATA
;
4359 case ERESTART
: return DRWAV_ERROR
;
4362 case ESTRPIPE
: return DRWAV_ERROR
;
4365 case EUSERS
: return DRWAV_ERROR
;
4368 case ENOTSOCK
: return DRWAV_NOT_SOCKET
;
4371 case EDESTADDRREQ
: return DRWAV_NO_ADDRESS
;
4374 case EMSGSIZE
: return DRWAV_TOO_BIG
;
4377 case EPROTOTYPE
: return DRWAV_BAD_PROTOCOL
;
4380 case ENOPROTOOPT
: return DRWAV_PROTOCOL_UNAVAILABLE
;
4382 #ifdef EPROTONOSUPPORT
4383 case EPROTONOSUPPORT
: return DRWAV_PROTOCOL_NOT_SUPPORTED
;
4385 #ifdef ESOCKTNOSUPPORT
4386 case ESOCKTNOSUPPORT
: return DRWAV_SOCKET_NOT_SUPPORTED
;
4389 case EOPNOTSUPP
: return DRWAV_INVALID_OPERATION
;
4392 case EPFNOSUPPORT
: return DRWAV_PROTOCOL_FAMILY_NOT_SUPPORTED
;
4395 case EAFNOSUPPORT
: return DRWAV_ADDRESS_FAMILY_NOT_SUPPORTED
;
4398 case EADDRINUSE
: return DRWAV_ALREADY_IN_USE
;
4400 #ifdef EADDRNOTAVAIL
4401 case EADDRNOTAVAIL
: return DRWAV_ERROR
;
4404 case ENETDOWN
: return DRWAV_NO_NETWORK
;
4407 case ENETUNREACH
: return DRWAV_NO_NETWORK
;
4410 case ENETRESET
: return DRWAV_NO_NETWORK
;
4413 case ECONNABORTED
: return DRWAV_NO_NETWORK
;
4416 case ECONNRESET
: return DRWAV_CONNECTION_RESET
;
4419 case ENOBUFS
: return DRWAV_NO_SPACE
;
4422 case EISCONN
: return DRWAV_ALREADY_CONNECTED
;
4425 case ENOTCONN
: return DRWAV_NOT_CONNECTED
;
4428 case ESHUTDOWN
: return DRWAV_ERROR
;
4431 case ETOOMANYREFS
: return DRWAV_ERROR
;
4434 case ETIMEDOUT
: return DRWAV_TIMEOUT
;
4437 case ECONNREFUSED
: return DRWAV_CONNECTION_REFUSED
;
4440 case EHOSTDOWN
: return DRWAV_NO_HOST
;
4443 case EHOSTUNREACH
: return DRWAV_NO_HOST
;
4446 case EALREADY
: return DRWAV_IN_PROGRESS
;
4449 case EINPROGRESS
: return DRWAV_IN_PROGRESS
;
4452 case ESTALE
: return DRWAV_INVALID_FILE
;
4455 case EUCLEAN
: return DRWAV_ERROR
;
4458 case ENOTNAM
: return DRWAV_ERROR
;
4461 case ENAVAIL
: return DRWAV_ERROR
;
4464 case EISNAM
: return DRWAV_ERROR
;
4467 case EREMOTEIO
: return DRWAV_IO_ERROR
;
4470 case EDQUOT
: return DRWAV_NO_SPACE
;
4473 case ENOMEDIUM
: return DRWAV_DOES_NOT_EXIST
;
4476 case EMEDIUMTYPE
: return DRWAV_ERROR
;
4479 case ECANCELED
: return DRWAV_CANCELLED
;
4482 case ENOKEY
: return DRWAV_ERROR
;
4485 case EKEYEXPIRED
: return DRWAV_ERROR
;
4488 case EKEYREVOKED
: return DRWAV_ERROR
;
4491 case EKEYREJECTED
: return DRWAV_ERROR
;
4494 case EOWNERDEAD
: return DRWAV_ERROR
;
4496 #ifdef ENOTRECOVERABLE
4497 case ENOTRECOVERABLE
: return DRWAV_ERROR
;
4500 case ERFKILL
: return DRWAV_ERROR
;
4503 case EHWPOISON
: return DRWAV_ERROR
;
4505 default: return DRWAV_ERROR
;
4509 DRWAV_PRIVATE drwav_result
drwav_fopen(FILE** ppFile
, const char* pFilePath
, const char* pOpenMode
)
4511 #if defined(_MSC_VER) && _MSC_VER >= 1400
4515 if (ppFile
!= NULL
) {
4516 *ppFile
= NULL
; /* Safety. */
4519 if (pFilePath
== NULL
|| pOpenMode
== NULL
|| ppFile
== NULL
) {
4520 return DRWAV_INVALID_ARGS
;
4523 #if defined(_MSC_VER) && _MSC_VER >= 1400
4524 err
= fopen_s(ppFile
, pFilePath
, pOpenMode
);
4526 return drwav_result_from_errno(err
);
4529 #if defined(_WIN32) || defined(__APPLE__)
4530 *ppFile
= fopen(pFilePath
, pOpenMode
);
4532 #if defined(_FILE_OFFSET_BITS) && _FILE_OFFSET_BITS == 64 && defined(_LARGEFILE64_SOURCE)
4533 *ppFile
= fopen64(pFilePath
, pOpenMode
);
4535 *ppFile
= fopen(pFilePath
, pOpenMode
);
4538 if (*ppFile
== NULL
) {
4539 drwav_result result
= drwav_result_from_errno(errno
);
4540 if (result
== DRWAV_SUCCESS
) {
4541 result
= DRWAV_ERROR
; /* Just a safety check to make sure we never ever return success when pFile == NULL. */
4548 return DRWAV_SUCCESS
;
4552 _wfopen() isn't always available in all compilation environments.
4555 * MSVC seems to support it universally as far back as VC6 from what I can tell (haven't checked further back).
4556 * MinGW-64 (both 32- and 64-bit) seems to support it.
4557 * MinGW wraps it in !defined(__STRICT_ANSI__).
4558 * OpenWatcom wraps it in !defined(_NO_EXT_KEYS).
4560 This can be reviewed as compatibility issues arise. The preference is to use _wfopen_s() and _wfopen() as opposed to the wcsrtombs()
4561 fallback, so if you notice your compiler not detecting this properly I'm happy to look at adding support.
4564 #if defined(_MSC_VER) || defined(__MINGW64__) || (!defined(__STRICT_ANSI__) && !defined(_NO_EXT_KEYS))
4565 #define DRWAV_HAS_WFOPEN
4569 DRWAV_PRIVATE drwav_result
drwav_wfopen(FILE** ppFile
, const wchar_t* pFilePath
, const wchar_t* pOpenMode
, const drwav_allocation_callbacks
* pAllocationCallbacks
)
4571 if (ppFile
!= NULL
) {
4572 *ppFile
= NULL
; /* Safety. */
4575 if (pFilePath
== NULL
|| pOpenMode
== NULL
|| ppFile
== NULL
) {
4576 return DRWAV_INVALID_ARGS
;
4579 #if defined(DRWAV_HAS_WFOPEN)
4581 /* Use _wfopen() on Windows. */
4582 #if defined(_MSC_VER) && _MSC_VER >= 1400
4583 errno_t err
= _wfopen_s(ppFile
, pFilePath
, pOpenMode
);
4585 return drwav_result_from_errno(err
);
4588 *ppFile
= _wfopen(pFilePath
, pOpenMode
);
4589 if (*ppFile
== NULL
) {
4590 return drwav_result_from_errno(errno
);
4593 (void)pAllocationCallbacks
;
4597 Use fopen() on anything other than Windows. Requires a conversion. This is annoying because fopen() is locale specific. The only real way I can
4598 think of to do this is with wcsrtombs(). Note that wcstombs() is apparently not thread-safe because it uses a static global mbstate_t object for
4599 maintaining state. I've checked this with -std=c89 and it works, but if somebody get's a compiler error I'll look into improving compatibility.
4604 const wchar_t* pFilePathTemp
= pFilePath
;
4605 char* pFilePathMB
= NULL
;
4606 char pOpenModeMB
[32] = {0};
4608 /* Get the length first. */
4609 DRWAV_ZERO_OBJECT(&mbs
);
4610 lenMB
= wcsrtombs(NULL
, &pFilePathTemp
, 0, &mbs
);
4611 if (lenMB
== (size_t)-1) {
4612 return drwav_result_from_errno(errno
);
4615 pFilePathMB
= (char*)drwav__malloc_from_callbacks(lenMB
+ 1, pAllocationCallbacks
);
4616 if (pFilePathMB
== NULL
) {
4617 return DRWAV_OUT_OF_MEMORY
;
4620 pFilePathTemp
= pFilePath
;
4621 DRWAV_ZERO_OBJECT(&mbs
);
4622 wcsrtombs(pFilePathMB
, &pFilePathTemp
, lenMB
+ 1, &mbs
);
4624 /* The open mode should always consist of ASCII characters so we should be able to do a trivial conversion. */
4628 if (pOpenMode
[i
] == 0) {
4629 pOpenModeMB
[i
] = '\0';
4633 pOpenModeMB
[i
] = (char)pOpenMode
[i
];
4638 *ppFile
= fopen(pFilePathMB
, pOpenModeMB
);
4640 drwav__free_from_callbacks(pFilePathMB
, pAllocationCallbacks
);
4643 if (*ppFile
== NULL
) {
4648 return DRWAV_SUCCESS
;
4652 DRWAV_PRIVATE
size_t drwav__on_read_stdio(void* pUserData
, void* pBufferOut
, size_t bytesToRead
)
4654 return fread(pBufferOut
, 1, bytesToRead
, (FILE*)pUserData
);
4657 DRWAV_PRIVATE
size_t drwav__on_write_stdio(void* pUserData
, const void* pData
, size_t bytesToWrite
)
4659 return fwrite(pData
, 1, bytesToWrite
, (FILE*)pUserData
);
4662 DRWAV_PRIVATE drwav_bool32
drwav__on_seek_stdio(void* pUserData
, int offset
, drwav_seek_origin origin
)
4664 return fseek((FILE*)pUserData
, offset
, (origin
== drwav_seek_origin_current
) ? SEEK_CUR
: SEEK_SET
) == 0;
4667 DRWAV_API drwav_bool32
drwav_init_file(drwav
* pWav
, const char* filename
, const drwav_allocation_callbacks
* pAllocationCallbacks
)
4669 return drwav_init_file_ex(pWav
, filename
, NULL
, NULL
, 0, pAllocationCallbacks
);
4673 DRWAV_PRIVATE drwav_bool32
drwav_init_file__internal_FILE(drwav
* pWav
, FILE* pFile
, drwav_chunk_proc onChunk
, void* pChunkUserData
, drwav_uint32 flags
, drwav_metadata_type allowedMetadataTypes
, const drwav_allocation_callbacks
* pAllocationCallbacks
)
4675 drwav_bool32 result
;
4677 result
= drwav_preinit(pWav
, drwav__on_read_stdio
, drwav__on_seek_stdio
, (void*)pFile
, pAllocationCallbacks
);
4678 if (result
!= DRWAV_TRUE
) {
4683 pWav
->allowedMetadataTypes
= allowedMetadataTypes
;
4685 result
= drwav_init__internal(pWav
, onChunk
, pChunkUserData
, flags
);
4686 if (result
!= DRWAV_TRUE
) {
4694 DRWAV_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
)
4697 if (drwav_fopen(&pFile
, filename
, "rb") != DRWAV_SUCCESS
) {
4701 /* This takes ownership of the FILE* object. */
4702 return drwav_init_file__internal_FILE(pWav
, pFile
, onChunk
, pChunkUserData
, flags
, drwav_metadata_type_none
, pAllocationCallbacks
);
4705 DRWAV_API drwav_bool32
drwav_init_file_w(drwav
* pWav
, const wchar_t* filename
, const drwav_allocation_callbacks
* pAllocationCallbacks
)
4707 return drwav_init_file_ex_w(pWav
, filename
, NULL
, NULL
, 0, pAllocationCallbacks
);
4710 DRWAV_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
)
4713 if (drwav_wfopen(&pFile
, filename
, L
"rb", pAllocationCallbacks
) != DRWAV_SUCCESS
) {
4717 /* This takes ownership of the FILE* object. */
4718 return drwav_init_file__internal_FILE(pWav
, pFile
, onChunk
, pChunkUserData
, flags
, drwav_metadata_type_none
, pAllocationCallbacks
);
4721 DRWAV_API drwav_bool32
drwav_init_file_with_metadata(drwav
* pWav
, const char* filename
, drwav_uint32 flags
, const drwav_allocation_callbacks
* pAllocationCallbacks
)
4724 if (drwav_fopen(&pFile
, filename
, "rb") != DRWAV_SUCCESS
) {
4728 /* This takes ownership of the FILE* object. */
4729 return drwav_init_file__internal_FILE(pWav
, pFile
, NULL
, NULL
, flags
, drwav_metadata_type_all_including_unknown
, pAllocationCallbacks
);
4732 DRWAV_API drwav_bool32
drwav_init_file_with_metadata_w(drwav
* pWav
, const wchar_t* filename
, drwav_uint32 flags
, const drwav_allocation_callbacks
* pAllocationCallbacks
)
4735 if (drwav_wfopen(&pFile
, filename
, L
"rb", pAllocationCallbacks
) != DRWAV_SUCCESS
) {
4739 /* This takes ownership of the FILE* object. */
4740 return drwav_init_file__internal_FILE(pWav
, pFile
, NULL
, NULL
, flags
, drwav_metadata_type_all_including_unknown
, pAllocationCallbacks
);
4744 DRWAV_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
)
4746 drwav_bool32 result
;
4748 result
= drwav_preinit_write(pWav
, pFormat
, isSequential
, drwav__on_write_stdio
, drwav__on_seek_stdio
, (void*)pFile
, pAllocationCallbacks
);
4749 if (result
!= DRWAV_TRUE
) {
4754 result
= drwav_init_write__internal(pWav
, pFormat
, totalSampleCount
);
4755 if (result
!= DRWAV_TRUE
) {
4763 DRWAV_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
)
4766 if (drwav_fopen(&pFile
, filename
, "wb") != DRWAV_SUCCESS
) {
4770 /* This takes ownership of the FILE* object. */
4771 return drwav_init_file_write__internal_FILE(pWav
, pFile
, pFormat
, totalSampleCount
, isSequential
, pAllocationCallbacks
);
4774 DRWAV_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
)
4777 if (drwav_wfopen(&pFile
, filename
, L
"wb", pAllocationCallbacks
) != DRWAV_SUCCESS
) {
4781 /* This takes ownership of the FILE* object. */
4782 return drwav_init_file_write__internal_FILE(pWav
, pFile
, pFormat
, totalSampleCount
, isSequential
, pAllocationCallbacks
);
4785 DRWAV_API drwav_bool32
drwav_init_file_write(drwav
* pWav
, const char* filename
, const drwav_data_format
* pFormat
, const drwav_allocation_callbacks
* pAllocationCallbacks
)
4787 return drwav_init_file_write__internal(pWav
, filename
, pFormat
, 0, DRWAV_FALSE
, pAllocationCallbacks
);
4790 DRWAV_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
)
4792 return drwav_init_file_write__internal(pWav
, filename
, pFormat
, totalSampleCount
, DRWAV_TRUE
, pAllocationCallbacks
);
4795 DRWAV_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
)
4797 if (pFormat
== NULL
) {
4801 return drwav_init_file_write_sequential(pWav
, filename
, pFormat
, totalPCMFrameCount
*pFormat
->channels
, pAllocationCallbacks
);
4804 DRWAV_API drwav_bool32
drwav_init_file_write_w(drwav
* pWav
, const wchar_t* filename
, const drwav_data_format
* pFormat
, const drwav_allocation_callbacks
* pAllocationCallbacks
)
4806 return drwav_init_file_write_w__internal(pWav
, filename
, pFormat
, 0, DRWAV_FALSE
, pAllocationCallbacks
);
4809 DRWAV_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
)
4811 return drwav_init_file_write_w__internal(pWav
, filename
, pFormat
, totalSampleCount
, DRWAV_TRUE
, pAllocationCallbacks
);
4814 DRWAV_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
)
4816 if (pFormat
== NULL
) {
4820 return drwav_init_file_write_sequential_w(pWav
, filename
, pFormat
, totalPCMFrameCount
*pFormat
->channels
, pAllocationCallbacks
);
4822 #endif /* DR_WAV_NO_STDIO */
4825 DRWAV_PRIVATE
size_t drwav__on_read_memory(void* pUserData
, void* pBufferOut
, size_t bytesToRead
)
4827 drwav
* pWav
= (drwav
*)pUserData
;
4828 size_t bytesRemaining
;
4830 DRWAV_ASSERT(pWav
!= NULL
);
4831 DRWAV_ASSERT(pWav
->memoryStream
.dataSize
>= pWav
->memoryStream
.currentReadPos
);
4833 bytesRemaining
= pWav
->memoryStream
.dataSize
- pWav
->memoryStream
.currentReadPos
;
4834 if (bytesToRead
> bytesRemaining
) {
4835 bytesToRead
= bytesRemaining
;
4838 if (bytesToRead
> 0) {
4839 DRWAV_COPY_MEMORY(pBufferOut
, pWav
->memoryStream
.data
+ pWav
->memoryStream
.currentReadPos
, bytesToRead
);
4840 pWav
->memoryStream
.currentReadPos
+= bytesToRead
;
4846 DRWAV_PRIVATE drwav_bool32
drwav__on_seek_memory(void* pUserData
, int offset
, drwav_seek_origin origin
)
4848 drwav
* pWav
= (drwav
*)pUserData
;
4849 DRWAV_ASSERT(pWav
!= NULL
);
4851 if (origin
== drwav_seek_origin_current
) {
4853 if (pWav
->memoryStream
.currentReadPos
+ offset
> pWav
->memoryStream
.dataSize
) {
4854 return DRWAV_FALSE
; /* Trying to seek too far forward. */
4857 if (pWav
->memoryStream
.currentReadPos
< (size_t)-offset
) {
4858 return DRWAV_FALSE
; /* Trying to seek too far backwards. */
4862 /* This will never underflow thanks to the clamps above. */
4863 pWav
->memoryStream
.currentReadPos
+= offset
;
4865 if ((drwav_uint32
)offset
<= pWav
->memoryStream
.dataSize
) {
4866 pWav
->memoryStream
.currentReadPos
= offset
;
4868 return DRWAV_FALSE
; /* Trying to seek too far forward. */
4875 DRWAV_PRIVATE
size_t drwav__on_write_memory(void* pUserData
, const void* pDataIn
, size_t bytesToWrite
)
4877 drwav
* pWav
= (drwav
*)pUserData
;
4878 size_t bytesRemaining
;
4880 DRWAV_ASSERT(pWav
!= NULL
);
4881 DRWAV_ASSERT(pWav
->memoryStreamWrite
.dataCapacity
>= pWav
->memoryStreamWrite
.currentWritePos
);
4883 bytesRemaining
= pWav
->memoryStreamWrite
.dataCapacity
- pWav
->memoryStreamWrite
.currentWritePos
;
4884 if (bytesRemaining
< bytesToWrite
) {
4885 /* Need to reallocate. */
4887 size_t newDataCapacity
= (pWav
->memoryStreamWrite
.dataCapacity
== 0) ? 256 : pWav
->memoryStreamWrite
.dataCapacity
* 2;
4889 /* If doubling wasn't enough, just make it the minimum required size to write the data. */
4890 if ((newDataCapacity
- pWav
->memoryStreamWrite
.currentWritePos
) < bytesToWrite
) {
4891 newDataCapacity
= pWav
->memoryStreamWrite
.currentWritePos
+ bytesToWrite
;
4894 pNewData
= drwav__realloc_from_callbacks(*pWav
->memoryStreamWrite
.ppData
, newDataCapacity
, pWav
->memoryStreamWrite
.dataCapacity
, &pWav
->allocationCallbacks
);
4895 if (pNewData
== NULL
) {
4899 *pWav
->memoryStreamWrite
.ppData
= pNewData
;
4900 pWav
->memoryStreamWrite
.dataCapacity
= newDataCapacity
;
4903 DRWAV_COPY_MEMORY(((drwav_uint8
*)(*pWav
->memoryStreamWrite
.ppData
)) + pWav
->memoryStreamWrite
.currentWritePos
, pDataIn
, bytesToWrite
);
4905 pWav
->memoryStreamWrite
.currentWritePos
+= bytesToWrite
;
4906 if (pWav
->memoryStreamWrite
.dataSize
< pWav
->memoryStreamWrite
.currentWritePos
) {
4907 pWav
->memoryStreamWrite
.dataSize
= pWav
->memoryStreamWrite
.currentWritePos
;
4910 *pWav
->memoryStreamWrite
.pDataSize
= pWav
->memoryStreamWrite
.dataSize
;
4912 return bytesToWrite
;
4915 DRWAV_PRIVATE drwav_bool32
drwav__on_seek_memory_write(void* pUserData
, int offset
, drwav_seek_origin origin
)
4917 drwav
* pWav
= (drwav
*)pUserData
;
4918 DRWAV_ASSERT(pWav
!= NULL
);
4920 if (origin
== drwav_seek_origin_current
) {
4922 if (pWav
->memoryStreamWrite
.currentWritePos
+ offset
> pWav
->memoryStreamWrite
.dataSize
) {
4923 offset
= (int)(pWav
->memoryStreamWrite
.dataSize
- pWav
->memoryStreamWrite
.currentWritePos
); /* Trying to seek too far forward. */
4926 if (pWav
->memoryStreamWrite
.currentWritePos
< (size_t)-offset
) {
4927 offset
= -(int)pWav
->memoryStreamWrite
.currentWritePos
; /* Trying to seek too far backwards. */
4931 /* This will never underflow thanks to the clamps above. */
4932 pWav
->memoryStreamWrite
.currentWritePos
+= offset
;
4934 if ((drwav_uint32
)offset
<= pWav
->memoryStreamWrite
.dataSize
) {
4935 pWav
->memoryStreamWrite
.currentWritePos
= offset
;
4937 pWav
->memoryStreamWrite
.currentWritePos
= pWav
->memoryStreamWrite
.dataSize
; /* Trying to seek too far forward. */
4944 DRWAV_API drwav_bool32
drwav_init_memory(drwav
* pWav
, const void* data
, size_t dataSize
, const drwav_allocation_callbacks
* pAllocationCallbacks
)
4946 return drwav_init_memory_ex(pWav
, data
, dataSize
, NULL
, NULL
, 0, pAllocationCallbacks
);
4949 DRWAV_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
)
4951 if (data
== NULL
|| dataSize
== 0) {
4955 if (!drwav_preinit(pWav
, drwav__on_read_memory
, drwav__on_seek_memory
, pWav
, pAllocationCallbacks
)) {
4959 pWav
->memoryStream
.data
= (const drwav_uint8
*)data
;
4960 pWav
->memoryStream
.dataSize
= dataSize
;
4961 pWav
->memoryStream
.currentReadPos
= 0;
4963 return drwav_init__internal(pWav
, onChunk
, pChunkUserData
, flags
);
4966 DRWAV_API drwav_bool32
drwav_init_memory_with_metadata(drwav
* pWav
, const void* data
, size_t dataSize
, drwav_uint32 flags
, const drwav_allocation_callbacks
* pAllocationCallbacks
)
4968 if (data
== NULL
|| dataSize
== 0) {
4972 if (!drwav_preinit(pWav
, drwav__on_read_memory
, drwav__on_seek_memory
, pWav
, pAllocationCallbacks
)) {
4976 pWav
->memoryStream
.data
= (const drwav_uint8
*)data
;
4977 pWav
->memoryStream
.dataSize
= dataSize
;
4978 pWav
->memoryStream
.currentReadPos
= 0;
4980 pWav
->allowedMetadataTypes
= drwav_metadata_type_all_including_unknown
;
4982 return drwav_init__internal(pWav
, NULL
, NULL
, flags
);
4986 DRWAV_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
)
4988 if (ppData
== NULL
|| pDataSize
== NULL
) {
4992 *ppData
= NULL
; /* Important because we're using realloc()! */
4995 if (!drwav_preinit_write(pWav
, pFormat
, isSequential
, drwav__on_write_memory
, drwav__on_seek_memory_write
, pWav
, pAllocationCallbacks
)) {
4999 pWav
->memoryStreamWrite
.ppData
= ppData
;
5000 pWav
->memoryStreamWrite
.pDataSize
= pDataSize
;
5001 pWav
->memoryStreamWrite
.dataSize
= 0;
5002 pWav
->memoryStreamWrite
.dataCapacity
= 0;
5003 pWav
->memoryStreamWrite
.currentWritePos
= 0;
5005 return drwav_init_write__internal(pWav
, pFormat
, totalSampleCount
);
5008 DRWAV_API drwav_bool32
drwav_init_memory_write(drwav
* pWav
, void** ppData
, size_t* pDataSize
, const drwav_data_format
* pFormat
, const drwav_allocation_callbacks
* pAllocationCallbacks
)
5010 return drwav_init_memory_write__internal(pWav
, ppData
, pDataSize
, pFormat
, 0, DRWAV_FALSE
, pAllocationCallbacks
);
5013 DRWAV_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
)
5015 return drwav_init_memory_write__internal(pWav
, ppData
, pDataSize
, pFormat
, totalSampleCount
, DRWAV_TRUE
, pAllocationCallbacks
);
5018 DRWAV_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
)
5020 if (pFormat
== NULL
) {
5024 return drwav_init_memory_write_sequential(pWav
, ppData
, pDataSize
, pFormat
, totalPCMFrameCount
*pFormat
->channels
, pAllocationCallbacks
);
5029 DRWAV_API drwav_result
drwav_uninit(drwav
* pWav
)
5031 drwav_result result
= DRWAV_SUCCESS
;
5034 return DRWAV_INVALID_ARGS
;
5038 If the drwav object was opened in write mode we'll need to finalize a few things:
5039 - Make sure the "data" chunk is aligned to 16-bits for RIFF containers, or 64 bits for W64 containers.
5040 - Set the size of the "data" chunk.
5042 if (pWav
->onWrite
!= NULL
) {
5043 drwav_uint32 paddingSize
= 0;
5045 /* Padding. Do not adjust pWav->dataChunkDataSize - this should not include the padding. */
5046 if (pWav
->container
== drwav_container_riff
|| pWav
->container
== drwav_container_rf64
) {
5047 paddingSize
= drwav__chunk_padding_size_riff(pWav
->dataChunkDataSize
);
5049 paddingSize
= drwav__chunk_padding_size_w64(pWav
->dataChunkDataSize
);
5052 if (paddingSize
> 0) {
5053 drwav_uint64 paddingData
= 0;
5054 drwav__write(pWav
, &paddingData
, paddingSize
); /* Byte order does not matter for this. */
5058 Chunk sizes. When using sequential mode, these will have been filled in at initialization time. We only need
5059 to do this when using non-sequential mode.
5061 if (pWav
->onSeek
&& !pWav
->isSequentialWrite
) {
5062 if (pWav
->container
== drwav_container_riff
) {
5063 /* The "RIFF" chunk size. */
5064 if (pWav
->onSeek(pWav
->pUserData
, 4, drwav_seek_origin_start
)) {
5065 drwav_uint32 riffChunkSize
= drwav__riff_chunk_size_riff(pWav
->dataChunkDataSize
, pWav
->pMetadata
, pWav
->metadataCount
);
5066 drwav__write_u32ne_to_le(pWav
, riffChunkSize
);
5069 /* The "data" chunk size. */
5070 if (pWav
->onSeek(pWav
->pUserData
, (int)pWav
->dataChunkDataPos
- 4, drwav_seek_origin_start
)) {
5071 drwav_uint32 dataChunkSize
= drwav__data_chunk_size_riff(pWav
->dataChunkDataSize
);
5072 drwav__write_u32ne_to_le(pWav
, dataChunkSize
);
5074 } else if (pWav
->container
== drwav_container_w64
) {
5075 /* The "RIFF" chunk size. */
5076 if (pWav
->onSeek(pWav
->pUserData
, 16, drwav_seek_origin_start
)) {
5077 drwav_uint64 riffChunkSize
= drwav__riff_chunk_size_w64(pWav
->dataChunkDataSize
);
5078 drwav__write_u64ne_to_le(pWav
, riffChunkSize
);
5081 /* The "data" chunk size. */
5082 if (pWav
->onSeek(pWav
->pUserData
, (int)pWav
->dataChunkDataPos
- 8, drwav_seek_origin_start
)) {
5083 drwav_uint64 dataChunkSize
= drwav__data_chunk_size_w64(pWav
->dataChunkDataSize
);
5084 drwav__write_u64ne_to_le(pWav
, dataChunkSize
);
5086 } else if (pWav
->container
== drwav_container_rf64
) {
5087 /* We only need to update the ds64 chunk. The "RIFF" and "data" chunks always have their sizes set to 0xFFFFFFFF for RF64. */
5088 int ds64BodyPos
= 12 + 8;
5090 /* The "RIFF" chunk size. */
5091 if (pWav
->onSeek(pWav
->pUserData
, ds64BodyPos
+ 0, drwav_seek_origin_start
)) {
5092 drwav_uint64 riffChunkSize
= drwav__riff_chunk_size_rf64(pWav
->dataChunkDataSize
, pWav
->pMetadata
, pWav
->metadataCount
);
5093 drwav__write_u64ne_to_le(pWav
, riffChunkSize
);
5096 /* The "data" chunk size. */
5097 if (pWav
->onSeek(pWav
->pUserData
, ds64BodyPos
+ 8, drwav_seek_origin_start
)) {
5098 drwav_uint64 dataChunkSize
= drwav__data_chunk_size_rf64(pWav
->dataChunkDataSize
);
5099 drwav__write_u64ne_to_le(pWav
, dataChunkSize
);
5104 /* Validation for sequential mode. */
5105 if (pWav
->isSequentialWrite
) {
5106 if (pWav
->dataChunkDataSize
!= pWav
->dataChunkDataSizeTargetWrite
) {
5107 result
= DRWAV_INVALID_FILE
;
5111 if (pWav
->pMetadata
!= NULL
) {
5112 pWav
->allocationCallbacks
.onFree(pWav
->pMetadata
, pWav
->allocationCallbacks
.pUserData
);
5116 #ifndef DR_WAV_NO_STDIO
5118 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()
5119 was used by looking at the onRead and onSeek callbacks.
5121 if (pWav
->onRead
== drwav__on_read_stdio
|| pWav
->onWrite
== drwav__on_write_stdio
) {
5122 fclose((FILE*)pWav
->pUserData
);
5131 DRWAV_API
size_t drwav_read_raw(drwav
* pWav
, size_t bytesToRead
, void* pBufferOut
)
5135 if (pWav
== NULL
|| bytesToRead
== 0) {
5136 return 0; /* Invalid args. */
5139 if (bytesToRead
> pWav
->bytesRemaining
) {
5140 bytesToRead
= (size_t)pWav
->bytesRemaining
;
5143 if (bytesToRead
== 0) {
5144 return 0; /* At end. */
5147 if (pBufferOut
!= NULL
) {
5148 bytesRead
= pWav
->onRead(pWav
->pUserData
, pBufferOut
, bytesToRead
);
5150 /* We need to seek. If we fail, we need to read-and-discard to make sure we get a good byte count. */
5152 while (bytesRead
< bytesToRead
) {
5153 size_t bytesToSeek
= (bytesToRead
- bytesRead
);
5154 if (bytesToSeek
> 0x7FFFFFFF) {
5155 bytesToSeek
= 0x7FFFFFFF;
5158 if (pWav
->onSeek(pWav
->pUserData
, (int)bytesToSeek
, drwav_seek_origin_current
) == DRWAV_FALSE
) {
5162 bytesRead
+= bytesToSeek
;
5165 /* When we get here we may need to read-and-discard some data. */
5166 while (bytesRead
< bytesToRead
) {
5167 drwav_uint8 buffer
[4096];
5169 size_t bytesToSeek
= (bytesToRead
- bytesRead
);
5170 if (bytesToSeek
> sizeof(buffer
)) {
5171 bytesToSeek
= sizeof(buffer
);
5174 bytesSeeked
= pWav
->onRead(pWav
->pUserData
, buffer
, bytesToSeek
);
5175 bytesRead
+= bytesSeeked
;
5177 if (bytesSeeked
< bytesToSeek
) {
5178 break; /* Reached the end. */
5183 pWav
->readCursorInPCMFrames
+= bytesRead
/ drwav_get_bytes_per_pcm_frame(pWav
);
5185 pWav
->bytesRemaining
-= bytesRead
;
5191 DRWAV_API drwav_uint64
drwav_read_pcm_frames_le(drwav
* pWav
, drwav_uint64 framesToRead
, void* pBufferOut
)
5193 drwav_uint32 bytesPerFrame
;
5194 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. */
5196 if (pWav
== NULL
|| framesToRead
== 0) {
5200 /* Cannot use this function for compressed formats. */
5201 if (drwav__is_compressed_format_tag(pWav
->translatedFormatTag
)) {
5205 bytesPerFrame
= drwav_get_bytes_per_pcm_frame(pWav
);
5206 if (bytesPerFrame
== 0) {
5210 /* Don't try to read more samples than can potentially fit in the output buffer. */
5211 bytesToRead
= framesToRead
* bytesPerFrame
;
5212 if (bytesToRead
> DRWAV_SIZE_MAX
) {
5213 bytesToRead
= (DRWAV_SIZE_MAX
/ bytesPerFrame
) * bytesPerFrame
; /* Round the number of bytes to read to a clean frame boundary. */
5217 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
5218 *could* be a time where it evaluates to 0 due to overflowing.
5220 if (bytesToRead
== 0) {
5224 return drwav_read_raw(pWav
, (size_t)bytesToRead
, pBufferOut
) / bytesPerFrame
;
5227 DRWAV_API drwav_uint64
drwav_read_pcm_frames_be(drwav
* pWav
, drwav_uint64 framesToRead
, void* pBufferOut
)
5229 drwav_uint64 framesRead
= drwav_read_pcm_frames_le(pWav
, framesToRead
, pBufferOut
);
5231 if (pBufferOut
!= NULL
) {
5232 drwav__bswap_samples(pBufferOut
, framesRead
*pWav
->channels
, drwav_get_bytes_per_pcm_frame(pWav
)/pWav
->channels
, pWav
->translatedFormatTag
);
5238 DRWAV_API drwav_uint64
drwav_read_pcm_frames(drwav
* pWav
, drwav_uint64 framesToRead
, void* pBufferOut
)
5240 if (drwav__is_little_endian()) {
5241 return drwav_read_pcm_frames_le(pWav
, framesToRead
, pBufferOut
);
5243 return drwav_read_pcm_frames_be(pWav
, framesToRead
, pBufferOut
);
5249 DRWAV_PRIVATE drwav_bool32
drwav_seek_to_first_pcm_frame(drwav
* pWav
)
5251 if (pWav
->onWrite
!= NULL
) {
5252 return DRWAV_FALSE
; /* No seeking in write mode. */
5255 if (!pWav
->onSeek(pWav
->pUserData
, (int)pWav
->dataChunkDataPos
, drwav_seek_origin_start
)) {
5259 if (drwav__is_compressed_format_tag(pWav
->translatedFormatTag
)) {
5260 /* Cached data needs to be cleared for compressed formats. */
5261 if (pWav
->translatedFormatTag
== DR_WAVE_FORMAT_ADPCM
) {
5262 DRWAV_ZERO_OBJECT(&pWav
->msadpcm
);
5263 } else if (pWav
->translatedFormatTag
== DR_WAVE_FORMAT_DVI_ADPCM
) {
5264 DRWAV_ZERO_OBJECT(&pWav
->ima
);
5266 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. */
5270 pWav
->readCursorInPCMFrames
= 0;
5271 pWav
->bytesRemaining
= pWav
->dataChunkDataSize
;
5276 DRWAV_API drwav_bool32
drwav_seek_to_pcm_frame(drwav
* pWav
, drwav_uint64 targetFrameIndex
)
5278 /* Seeking should be compatible with wave files > 2GB. */
5280 if (pWav
== NULL
|| pWav
->onSeek
== NULL
) {
5284 /* No seeking in write mode. */
5285 if (pWav
->onWrite
!= NULL
) {
5289 /* If there are no samples, just return DRWAV_TRUE without doing anything. */
5290 if (pWav
->totalPCMFrameCount
== 0) {
5294 /* Make sure the sample is clamped. */
5295 if (targetFrameIndex
>= pWav
->totalPCMFrameCount
) {
5296 targetFrameIndex
= pWav
->totalPCMFrameCount
- 1;
5300 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
5301 to seek back to the start.
5303 if (drwav__is_compressed_format_tag(pWav
->translatedFormatTag
)) {
5304 /* TODO: This can be optimized. */
5307 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,
5308 we first need to seek back to the start and then just do the same thing as a forward seek.
5310 if (targetFrameIndex
< pWav
->readCursorInPCMFrames
) {
5311 if (!drwav_seek_to_first_pcm_frame(pWav
)) {
5316 if (targetFrameIndex
> pWav
->readCursorInPCMFrames
) {
5317 drwav_uint64 offsetInFrames
= targetFrameIndex
- pWav
->readCursorInPCMFrames
;
5319 drwav_int16 devnull
[2048];
5320 while (offsetInFrames
> 0) {
5321 drwav_uint64 framesRead
= 0;
5322 drwav_uint64 framesToRead
= offsetInFrames
;
5323 if (framesToRead
> drwav_countof(devnull
)/pWav
->channels
) {
5324 framesToRead
= drwav_countof(devnull
)/pWav
->channels
;
5327 if (pWav
->translatedFormatTag
== DR_WAVE_FORMAT_ADPCM
) {
5328 framesRead
= drwav_read_pcm_frames_s16__msadpcm(pWav
, framesToRead
, devnull
);
5329 } else if (pWav
->translatedFormatTag
== DR_WAVE_FORMAT_DVI_ADPCM
) {
5330 framesRead
= drwav_read_pcm_frames_s16__ima(pWav
, framesToRead
, devnull
);
5332 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. */
5335 if (framesRead
!= framesToRead
) {
5339 offsetInFrames
-= framesRead
;
5343 drwav_uint64 totalSizeInBytes
;
5344 drwav_uint64 currentBytePos
;
5345 drwav_uint64 targetBytePos
;
5346 drwav_uint64 offset
;
5348 totalSizeInBytes
= pWav
->totalPCMFrameCount
* drwav_get_bytes_per_pcm_frame(pWav
);
5349 DRWAV_ASSERT(totalSizeInBytes
>= pWav
->bytesRemaining
);
5351 currentBytePos
= totalSizeInBytes
- pWav
->bytesRemaining
;
5352 targetBytePos
= targetFrameIndex
* drwav_get_bytes_per_pcm_frame(pWav
);
5354 if (currentBytePos
< targetBytePos
) {
5355 /* Offset forwards. */
5356 offset
= (targetBytePos
- currentBytePos
);
5358 /* Offset backwards. */
5359 if (!drwav_seek_to_first_pcm_frame(pWav
)) {
5362 offset
= targetBytePos
;
5365 while (offset
> 0) {
5366 int offset32
= ((offset
> INT_MAX
) ? INT_MAX
: (int)offset
);
5367 if (!pWav
->onSeek(pWav
->pUserData
, offset32
, drwav_seek_origin_current
)) {
5371 pWav
->readCursorInPCMFrames
+= offset32
/ drwav_get_bytes_per_pcm_frame(pWav
);
5372 pWav
->bytesRemaining
-= offset32
;
5380 DRWAV_API drwav_result
drwav_get_cursor_in_pcm_frames(drwav
* pWav
, drwav_uint64
* pCursor
)
5382 if (pCursor
== NULL
) {
5383 return DRWAV_INVALID_ARGS
;
5386 *pCursor
= 0; /* Safety. */
5389 return DRWAV_INVALID_ARGS
;
5392 *pCursor
= pWav
->readCursorInPCMFrames
;
5394 return DRWAV_SUCCESS
;
5397 DRWAV_API drwav_result
drwav_get_length_in_pcm_frames(drwav
* pWav
, drwav_uint64
* pLength
)
5399 if (pLength
== NULL
) {
5400 return DRWAV_INVALID_ARGS
;
5403 *pLength
= 0; /* Safety. */
5406 return DRWAV_INVALID_ARGS
;
5409 *pLength
= pWav
->totalPCMFrameCount
;
5411 return DRWAV_SUCCESS
;
5415 DRWAV_API
size_t drwav_write_raw(drwav
* pWav
, size_t bytesToWrite
, const void* pData
)
5417 size_t bytesWritten
;
5419 if (pWav
== NULL
|| bytesToWrite
== 0 || pData
== NULL
) {
5423 bytesWritten
= pWav
->onWrite(pWav
->pUserData
, pData
, bytesToWrite
);
5424 pWav
->dataChunkDataSize
+= bytesWritten
;
5426 return bytesWritten
;
5429 DRWAV_API drwav_uint64
drwav_write_pcm_frames_le(drwav
* pWav
, drwav_uint64 framesToWrite
, const void* pData
)
5431 drwav_uint64 bytesToWrite
;
5432 drwav_uint64 bytesWritten
;
5433 const drwav_uint8
* pRunningData
;
5435 if (pWav
== NULL
|| framesToWrite
== 0 || pData
== NULL
) {
5439 bytesToWrite
= ((framesToWrite
* pWav
->channels
* pWav
->bitsPerSample
) / 8);
5440 if (bytesToWrite
> DRWAV_SIZE_MAX
) {
5445 pRunningData
= (const drwav_uint8
*)pData
;
5447 while (bytesToWrite
> 0) {
5448 size_t bytesJustWritten
;
5449 drwav_uint64 bytesToWriteThisIteration
;
5451 bytesToWriteThisIteration
= bytesToWrite
;
5452 DRWAV_ASSERT(bytesToWriteThisIteration
<= DRWAV_SIZE_MAX
); /* <-- This is checked above. */
5454 bytesJustWritten
= drwav_write_raw(pWav
, (size_t)bytesToWriteThisIteration
, pRunningData
);
5455 if (bytesJustWritten
== 0) {
5459 bytesToWrite
-= bytesJustWritten
;
5460 bytesWritten
+= bytesJustWritten
;
5461 pRunningData
+= bytesJustWritten
;
5464 return (bytesWritten
* 8) / pWav
->bitsPerSample
/ pWav
->channels
;
5467 DRWAV_API drwav_uint64
drwav_write_pcm_frames_be(drwav
* pWav
, drwav_uint64 framesToWrite
, const void* pData
)
5469 drwav_uint64 bytesToWrite
;
5470 drwav_uint64 bytesWritten
;
5471 drwav_uint32 bytesPerSample
;
5472 const drwav_uint8
* pRunningData
;
5474 if (pWav
== NULL
|| framesToWrite
== 0 || pData
== NULL
) {
5478 bytesToWrite
= ((framesToWrite
* pWav
->channels
* pWav
->bitsPerSample
) / 8);
5479 if (bytesToWrite
> DRWAV_SIZE_MAX
) {
5484 pRunningData
= (const drwav_uint8
*)pData
;
5486 bytesPerSample
= drwav_get_bytes_per_pcm_frame(pWav
) / pWav
->channels
;
5488 while (bytesToWrite
> 0) {
5489 drwav_uint8 temp
[4096];
5490 drwav_uint32 sampleCount
;
5491 size_t bytesJustWritten
;
5492 drwav_uint64 bytesToWriteThisIteration
;
5494 bytesToWriteThisIteration
= bytesToWrite
;
5495 DRWAV_ASSERT(bytesToWriteThisIteration
<= DRWAV_SIZE_MAX
); /* <-- This is checked above. */
5498 WAV files are always little-endian. We need to byte swap on big-endian architectures. Since our input buffer is read-only we need
5499 to use an intermediary buffer for the conversion.
5501 sampleCount
= sizeof(temp
)/bytesPerSample
;
5503 if (bytesToWriteThisIteration
> ((drwav_uint64
)sampleCount
)*bytesPerSample
) {
5504 bytesToWriteThisIteration
= ((drwav_uint64
)sampleCount
)*bytesPerSample
;
5507 DRWAV_COPY_MEMORY(temp
, pRunningData
, (size_t)bytesToWriteThisIteration
);
5508 drwav__bswap_samples(temp
, sampleCount
, bytesPerSample
, pWav
->translatedFormatTag
);
5510 bytesJustWritten
= drwav_write_raw(pWav
, (size_t)bytesToWriteThisIteration
, temp
);
5511 if (bytesJustWritten
== 0) {
5515 bytesToWrite
-= bytesJustWritten
;
5516 bytesWritten
+= bytesJustWritten
;
5517 pRunningData
+= bytesJustWritten
;
5520 return (bytesWritten
* 8) / pWav
->bitsPerSample
/ pWav
->channels
;
5523 DRWAV_API drwav_uint64
drwav_write_pcm_frames(drwav
* pWav
, drwav_uint64 framesToWrite
, const void* pData
)
5525 if (drwav__is_little_endian()) {
5526 return drwav_write_pcm_frames_le(pWav
, framesToWrite
, pData
);
5528 return drwav_write_pcm_frames_be(pWav
, framesToWrite
, pData
);
5533 DRWAV_PRIVATE drwav_uint64
drwav_read_pcm_frames_s16__msadpcm(drwav
* pWav
, drwav_uint64 framesToRead
, drwav_int16
* pBufferOut
)
5535 drwav_uint64 totalFramesRead
= 0;
5537 DRWAV_ASSERT(pWav
!= NULL
);
5538 DRWAV_ASSERT(framesToRead
> 0);
5540 /* TODO: Lots of room for optimization here. */
5542 while (pWav
->readCursorInPCMFrames
< pWav
->totalPCMFrameCount
) {
5543 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. */
5545 /* If there are no cached frames we need to load a new block. */
5546 if (pWav
->msadpcm
.cachedFrameCount
== 0 && pWav
->msadpcm
.bytesRemainingInBlock
== 0) {
5547 if (pWav
->channels
== 1) {
5549 drwav_uint8 header
[7];
5550 if (pWav
->onRead(pWav
->pUserData
, header
, sizeof(header
)) != sizeof(header
)) {
5551 return totalFramesRead
;
5553 pWav
->msadpcm
.bytesRemainingInBlock
= pWav
->fmt
.blockAlign
- sizeof(header
);
5555 pWav
->msadpcm
.predictor
[0] = header
[0];
5556 pWav
->msadpcm
.delta
[0] = drwav_bytes_to_s16(header
+ 1);
5557 pWav
->msadpcm
.prevFrames
[0][1] = (drwav_int32
)drwav_bytes_to_s16(header
+ 3);
5558 pWav
->msadpcm
.prevFrames
[0][0] = (drwav_int32
)drwav_bytes_to_s16(header
+ 5);
5559 pWav
->msadpcm
.cachedFrames
[2] = pWav
->msadpcm
.prevFrames
[0][0];
5560 pWav
->msadpcm
.cachedFrames
[3] = pWav
->msadpcm
.prevFrames
[0][1];
5561 pWav
->msadpcm
.cachedFrameCount
= 2;
5564 drwav_uint8 header
[14];
5565 if (pWav
->onRead(pWav
->pUserData
, header
, sizeof(header
)) != sizeof(header
)) {
5566 return totalFramesRead
;
5568 pWav
->msadpcm
.bytesRemainingInBlock
= pWav
->fmt
.blockAlign
- sizeof(header
);
5570 pWav
->msadpcm
.predictor
[0] = header
[0];
5571 pWav
->msadpcm
.predictor
[1] = header
[1];
5572 pWav
->msadpcm
.delta
[0] = drwav_bytes_to_s16(header
+ 2);
5573 pWav
->msadpcm
.delta
[1] = drwav_bytes_to_s16(header
+ 4);
5574 pWav
->msadpcm
.prevFrames
[0][1] = (drwav_int32
)drwav_bytes_to_s16(header
+ 6);
5575 pWav
->msadpcm
.prevFrames
[1][1] = (drwav_int32
)drwav_bytes_to_s16(header
+ 8);
5576 pWav
->msadpcm
.prevFrames
[0][0] = (drwav_int32
)drwav_bytes_to_s16(header
+ 10);
5577 pWav
->msadpcm
.prevFrames
[1][0] = (drwav_int32
)drwav_bytes_to_s16(header
+ 12);
5579 pWav
->msadpcm
.cachedFrames
[0] = pWav
->msadpcm
.prevFrames
[0][0];
5580 pWav
->msadpcm
.cachedFrames
[1] = pWav
->msadpcm
.prevFrames
[1][0];
5581 pWav
->msadpcm
.cachedFrames
[2] = pWav
->msadpcm
.prevFrames
[0][1];
5582 pWav
->msadpcm
.cachedFrames
[3] = pWav
->msadpcm
.prevFrames
[1][1];
5583 pWav
->msadpcm
.cachedFrameCount
= 2;
5587 /* Output anything that's cached. */
5588 while (framesToRead
> 0 && pWav
->msadpcm
.cachedFrameCount
> 0 && pWav
->readCursorInPCMFrames
< pWav
->totalPCMFrameCount
) {
5589 if (pBufferOut
!= NULL
) {
5590 drwav_uint32 iSample
= 0;
5591 for (iSample
= 0; iSample
< pWav
->channels
; iSample
+= 1) {
5592 pBufferOut
[iSample
] = (drwav_int16
)pWav
->msadpcm
.cachedFrames
[(drwav_countof(pWav
->msadpcm
.cachedFrames
) - (pWav
->msadpcm
.cachedFrameCount
*pWav
->channels
)) + iSample
];
5595 pBufferOut
+= pWav
->channels
;
5599 totalFramesRead
+= 1;
5600 pWav
->readCursorInPCMFrames
+= 1;
5601 pWav
->msadpcm
.cachedFrameCount
-= 1;
5604 if (framesToRead
== 0) {
5610 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
5611 loop iteration which will trigger the loading of a new block.
5613 if (pWav
->msadpcm
.cachedFrameCount
== 0) {
5614 if (pWav
->msadpcm
.bytesRemainingInBlock
== 0) {
5617 static drwav_int32 adaptationTable
[] = {
5618 230, 230, 230, 230, 307, 409, 512, 614,
5619 768, 614, 512, 409, 307, 230, 230, 230
5621 static drwav_int32 coeff1Table
[] = { 256, 512, 0, 192, 240, 460, 392 };
5622 static drwav_int32 coeff2Table
[] = { 0, -256, 0, 64, 0, -208, -232 };
5624 drwav_uint8 nibbles
;
5625 drwav_int32 nibble0
;
5626 drwav_int32 nibble1
;
5628 if (pWav
->onRead(pWav
->pUserData
, &nibbles
, 1) != 1) {
5629 return totalFramesRead
;
5631 pWav
->msadpcm
.bytesRemainingInBlock
-= 1;
5633 /* TODO: Optimize away these if statements. */
5634 nibble0
= ((nibbles
& 0xF0) >> 4); if ((nibbles
& 0x80)) { nibble0
|= 0xFFFFFFF0UL
; }
5635 nibble1
= ((nibbles
& 0x0F) >> 0); if ((nibbles
& 0x08)) { nibble1
|= 0xFFFFFFF0UL
; }
5637 if (pWav
->channels
== 1) {
5639 drwav_int32 newSample0
;
5640 drwav_int32 newSample1
;
5642 newSample0
= ((pWav
->msadpcm
.prevFrames
[0][1] * coeff1Table
[pWav
->msadpcm
.predictor
[0]]) + (pWav
->msadpcm
.prevFrames
[0][0] * coeff2Table
[pWav
->msadpcm
.predictor
[0]])) >> 8;
5643 newSample0
+= nibble0
* pWav
->msadpcm
.delta
[0];
5644 newSample0
= drwav_clamp(newSample0
, -32768, 32767);
5646 pWav
->msadpcm
.delta
[0] = (adaptationTable
[((nibbles
& 0xF0) >> 4)] * pWav
->msadpcm
.delta
[0]) >> 8;
5647 if (pWav
->msadpcm
.delta
[0] < 16) {
5648 pWav
->msadpcm
.delta
[0] = 16;
5651 pWav
->msadpcm
.prevFrames
[0][0] = pWav
->msadpcm
.prevFrames
[0][1];
5652 pWav
->msadpcm
.prevFrames
[0][1] = newSample0
;
5655 newSample1
= ((pWav
->msadpcm
.prevFrames
[0][1] * coeff1Table
[pWav
->msadpcm
.predictor
[0]]) + (pWav
->msadpcm
.prevFrames
[0][0] * coeff2Table
[pWav
->msadpcm
.predictor
[0]])) >> 8;
5656 newSample1
+= nibble1
* pWav
->msadpcm
.delta
[0];
5657 newSample1
= drwav_clamp(newSample1
, -32768, 32767);
5659 pWav
->msadpcm
.delta
[0] = (adaptationTable
[((nibbles
& 0x0F) >> 0)] * pWav
->msadpcm
.delta
[0]) >> 8;
5660 if (pWav
->msadpcm
.delta
[0] < 16) {
5661 pWav
->msadpcm
.delta
[0] = 16;
5664 pWav
->msadpcm
.prevFrames
[0][0] = pWav
->msadpcm
.prevFrames
[0][1];
5665 pWav
->msadpcm
.prevFrames
[0][1] = newSample1
;
5668 pWav
->msadpcm
.cachedFrames
[2] = newSample0
;
5669 pWav
->msadpcm
.cachedFrames
[3] = newSample1
;
5670 pWav
->msadpcm
.cachedFrameCount
= 2;
5673 drwav_int32 newSample0
;
5674 drwav_int32 newSample1
;
5677 newSample0
= ((pWav
->msadpcm
.prevFrames
[0][1] * coeff1Table
[pWav
->msadpcm
.predictor
[0]]) + (pWav
->msadpcm
.prevFrames
[0][0] * coeff2Table
[pWav
->msadpcm
.predictor
[0]])) >> 8;
5678 newSample0
+= nibble0
* pWav
->msadpcm
.delta
[0];
5679 newSample0
= drwav_clamp(newSample0
, -32768, 32767);
5681 pWav
->msadpcm
.delta
[0] = (adaptationTable
[((nibbles
& 0xF0) >> 4)] * pWav
->msadpcm
.delta
[0]) >> 8;
5682 if (pWav
->msadpcm
.delta
[0] < 16) {
5683 pWav
->msadpcm
.delta
[0] = 16;
5686 pWav
->msadpcm
.prevFrames
[0][0] = pWav
->msadpcm
.prevFrames
[0][1];
5687 pWav
->msadpcm
.prevFrames
[0][1] = newSample0
;
5691 newSample1
= ((pWav
->msadpcm
.prevFrames
[1][1] * coeff1Table
[pWav
->msadpcm
.predictor
[1]]) + (pWav
->msadpcm
.prevFrames
[1][0] * coeff2Table
[pWav
->msadpcm
.predictor
[1]])) >> 8;
5692 newSample1
+= nibble1
* pWav
->msadpcm
.delta
[1];
5693 newSample1
= drwav_clamp(newSample1
, -32768, 32767);
5695 pWav
->msadpcm
.delta
[1] = (adaptationTable
[((nibbles
& 0x0F) >> 0)] * pWav
->msadpcm
.delta
[1]) >> 8;
5696 if (pWav
->msadpcm
.delta
[1] < 16) {
5697 pWav
->msadpcm
.delta
[1] = 16;
5700 pWav
->msadpcm
.prevFrames
[1][0] = pWav
->msadpcm
.prevFrames
[1][1];
5701 pWav
->msadpcm
.prevFrames
[1][1] = newSample1
;
5703 pWav
->msadpcm
.cachedFrames
[2] = newSample0
;
5704 pWav
->msadpcm
.cachedFrames
[3] = newSample1
;
5705 pWav
->msadpcm
.cachedFrameCount
= 1;
5711 return totalFramesRead
;
5715 DRWAV_PRIVATE drwav_uint64
drwav_read_pcm_frames_s16__ima(drwav
* pWav
, drwav_uint64 framesToRead
, drwav_int16
* pBufferOut
)
5717 drwav_uint64 totalFramesRead
= 0;
5718 drwav_uint32 iChannel
;
5720 static drwav_int32 indexTable
[16] = {
5721 -1, -1, -1, -1, 2, 4, 6, 8,
5722 -1, -1, -1, -1, 2, 4, 6, 8
5725 static drwav_int32 stepTable
[89] = {
5726 7, 8, 9, 10, 11, 12, 13, 14, 16, 17,
5727 19, 21, 23, 25, 28, 31, 34, 37, 41, 45,
5728 50, 55, 60, 66, 73, 80, 88, 97, 107, 118,
5729 130, 143, 157, 173, 190, 209, 230, 253, 279, 307,
5730 337, 371, 408, 449, 494, 544, 598, 658, 724, 796,
5731 876, 963, 1060, 1166, 1282, 1411, 1552, 1707, 1878, 2066,
5732 2272, 2499, 2749, 3024, 3327, 3660, 4026, 4428, 4871, 5358,
5733 5894, 6484, 7132, 7845, 8630, 9493, 10442, 11487, 12635, 13899,
5734 15289, 16818, 18500, 20350, 22385, 24623, 27086, 29794, 32767
5737 DRWAV_ASSERT(pWav
!= NULL
);
5738 DRWAV_ASSERT(framesToRead
> 0);
5740 /* TODO: Lots of room for optimization here. */
5742 while (pWav
->readCursorInPCMFrames
< pWav
->totalPCMFrameCount
) {
5743 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. */
5745 /* If there are no cached samples we need to load a new block. */
5746 if (pWav
->ima
.cachedFrameCount
== 0 && pWav
->ima
.bytesRemainingInBlock
== 0) {
5747 if (pWav
->channels
== 1) {
5749 drwav_uint8 header
[4];
5750 if (pWav
->onRead(pWav
->pUserData
, header
, sizeof(header
)) != sizeof(header
)) {
5751 return totalFramesRead
;
5753 pWav
->ima
.bytesRemainingInBlock
= pWav
->fmt
.blockAlign
- sizeof(header
);
5755 if (header
[2] >= drwav_countof(stepTable
)) {
5756 pWav
->onSeek(pWav
->pUserData
, pWav
->ima
.bytesRemainingInBlock
, drwav_seek_origin_current
);
5757 pWav
->ima
.bytesRemainingInBlock
= 0;
5758 return totalFramesRead
; /* Invalid data. */
5761 pWav
->ima
.predictor
[0] = drwav_bytes_to_s16(header
+ 0);
5762 pWav
->ima
.stepIndex
[0] = header
[2];
5763 pWav
->ima
.cachedFrames
[drwav_countof(pWav
->ima
.cachedFrames
) - 1] = pWav
->ima
.predictor
[0];
5764 pWav
->ima
.cachedFrameCount
= 1;
5767 drwav_uint8 header
[8];
5768 if (pWav
->onRead(pWav
->pUserData
, header
, sizeof(header
)) != sizeof(header
)) {
5769 return totalFramesRead
;
5771 pWav
->ima
.bytesRemainingInBlock
= pWav
->fmt
.blockAlign
- sizeof(header
);
5773 if (header
[2] >= drwav_countof(stepTable
) || header
[6] >= drwav_countof(stepTable
)) {
5774 pWav
->onSeek(pWav
->pUserData
, pWav
->ima
.bytesRemainingInBlock
, drwav_seek_origin_current
);
5775 pWav
->ima
.bytesRemainingInBlock
= 0;
5776 return totalFramesRead
; /* Invalid data. */
5779 pWav
->ima
.predictor
[0] = drwav_bytes_to_s16(header
+ 0);
5780 pWav
->ima
.stepIndex
[0] = header
[2];
5781 pWav
->ima
.predictor
[1] = drwav_bytes_to_s16(header
+ 4);
5782 pWav
->ima
.stepIndex
[1] = header
[6];
5784 pWav
->ima
.cachedFrames
[drwav_countof(pWav
->ima
.cachedFrames
) - 2] = pWav
->ima
.predictor
[0];
5785 pWav
->ima
.cachedFrames
[drwav_countof(pWav
->ima
.cachedFrames
) - 1] = pWav
->ima
.predictor
[1];
5786 pWav
->ima
.cachedFrameCount
= 1;
5790 /* Output anything that's cached. */
5791 while (framesToRead
> 0 && pWav
->ima
.cachedFrameCount
> 0 && pWav
->readCursorInPCMFrames
< pWav
->totalPCMFrameCount
) {
5792 if (pBufferOut
!= NULL
) {
5793 drwav_uint32 iSample
;
5794 for (iSample
= 0; iSample
< pWav
->channels
; iSample
+= 1) {
5795 pBufferOut
[iSample
] = (drwav_int16
)pWav
->ima
.cachedFrames
[(drwav_countof(pWav
->ima
.cachedFrames
) - (pWav
->ima
.cachedFrameCount
*pWav
->channels
)) + iSample
];
5797 pBufferOut
+= pWav
->channels
;
5801 totalFramesRead
+= 1;
5802 pWav
->readCursorInPCMFrames
+= 1;
5803 pWav
->ima
.cachedFrameCount
-= 1;
5806 if (framesToRead
== 0) {
5811 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
5812 loop iteration which will trigger the loading of a new block.
5814 if (pWav
->ima
.cachedFrameCount
== 0) {
5815 if (pWav
->ima
.bytesRemainingInBlock
== 0) {
5819 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
5820 left channel, 4 bytes for the right channel.
5822 pWav
->ima
.cachedFrameCount
= 8;
5823 for (iChannel
= 0; iChannel
< pWav
->channels
; ++iChannel
) {
5825 drwav_uint8 nibbles
[4];
5826 if (pWav
->onRead(pWav
->pUserData
, &nibbles
, 4) != 4) {
5827 pWav
->ima
.cachedFrameCount
= 0;
5828 return totalFramesRead
;
5830 pWav
->ima
.bytesRemainingInBlock
-= 4;
5832 for (iByte
= 0; iByte
< 4; ++iByte
) {
5833 drwav_uint8 nibble0
= ((nibbles
[iByte
] & 0x0F) >> 0);
5834 drwav_uint8 nibble1
= ((nibbles
[iByte
] & 0xF0) >> 4);
5836 drwav_int32 step
= stepTable
[pWav
->ima
.stepIndex
[iChannel
]];
5837 drwav_int32 predictor
= pWav
->ima
.predictor
[iChannel
];
5839 drwav_int32 diff
= step
>> 3;
5840 if (nibble0
& 1) diff
+= step
>> 2;
5841 if (nibble0
& 2) diff
+= step
>> 1;
5842 if (nibble0
& 4) diff
+= step
;
5843 if (nibble0
& 8) diff
= -diff
;
5845 predictor
= drwav_clamp(predictor
+ diff
, -32768, 32767);
5846 pWav
->ima
.predictor
[iChannel
] = predictor
;
5847 pWav
->ima
.stepIndex
[iChannel
] = drwav_clamp(pWav
->ima
.stepIndex
[iChannel
] + indexTable
[nibble0
], 0, (drwav_int32
)drwav_countof(stepTable
)-1);
5848 pWav
->ima
.cachedFrames
[(drwav_countof(pWav
->ima
.cachedFrames
) - (pWav
->ima
.cachedFrameCount
*pWav
->channels
)) + (iByte
*2+0)*pWav
->channels
+ iChannel
] = predictor
;
5851 step
= stepTable
[pWav
->ima
.stepIndex
[iChannel
]];
5852 predictor
= pWav
->ima
.predictor
[iChannel
];
5855 if (nibble1
& 1) diff
+= step
>> 2;
5856 if (nibble1
& 2) diff
+= step
>> 1;
5857 if (nibble1
& 4) diff
+= step
;
5858 if (nibble1
& 8) diff
= -diff
;
5860 predictor
= drwav_clamp(predictor
+ diff
, -32768, 32767);
5861 pWav
->ima
.predictor
[iChannel
] = predictor
;
5862 pWav
->ima
.stepIndex
[iChannel
] = drwav_clamp(pWav
->ima
.stepIndex
[iChannel
] + indexTable
[nibble1
], 0, (drwav_int32
)drwav_countof(stepTable
)-1);
5863 pWav
->ima
.cachedFrames
[(drwav_countof(pWav
->ima
.cachedFrames
) - (pWav
->ima
.cachedFrameCount
*pWav
->channels
)) + (iByte
*2+1)*pWav
->channels
+ iChannel
] = predictor
;
5870 return totalFramesRead
;
5874 #ifndef DR_WAV_NO_CONVERSION_API
5875 static unsigned short g_drwavAlawTable
[256] = {
5876 0xEA80, 0xEB80, 0xE880, 0xE980, 0xEE80, 0xEF80, 0xEC80, 0xED80, 0xE280, 0xE380, 0xE080, 0xE180, 0xE680, 0xE780, 0xE480, 0xE580,
5877 0xF540, 0xF5C0, 0xF440, 0xF4C0, 0xF740, 0xF7C0, 0xF640, 0xF6C0, 0xF140, 0xF1C0, 0xF040, 0xF0C0, 0xF340, 0xF3C0, 0xF240, 0xF2C0,
5878 0xAA00, 0xAE00, 0xA200, 0xA600, 0xBA00, 0xBE00, 0xB200, 0xB600, 0x8A00, 0x8E00, 0x8200, 0x8600, 0x9A00, 0x9E00, 0x9200, 0x9600,
5879 0xD500, 0xD700, 0xD100, 0xD300, 0xDD00, 0xDF00, 0xD900, 0xDB00, 0xC500, 0xC700, 0xC100, 0xC300, 0xCD00, 0xCF00, 0xC900, 0xCB00,
5880 0xFEA8, 0xFEB8, 0xFE88, 0xFE98, 0xFEE8, 0xFEF8, 0xFEC8, 0xFED8, 0xFE28, 0xFE38, 0xFE08, 0xFE18, 0xFE68, 0xFE78, 0xFE48, 0xFE58,
5881 0xFFA8, 0xFFB8, 0xFF88, 0xFF98, 0xFFE8, 0xFFF8, 0xFFC8, 0xFFD8, 0xFF28, 0xFF38, 0xFF08, 0xFF18, 0xFF68, 0xFF78, 0xFF48, 0xFF58,
5882 0xFAA0, 0xFAE0, 0xFA20, 0xFA60, 0xFBA0, 0xFBE0, 0xFB20, 0xFB60, 0xF8A0, 0xF8E0, 0xF820, 0xF860, 0xF9A0, 0xF9E0, 0xF920, 0xF960,
5883 0xFD50, 0xFD70, 0xFD10, 0xFD30, 0xFDD0, 0xFDF0, 0xFD90, 0xFDB0, 0xFC50, 0xFC70, 0xFC10, 0xFC30, 0xFCD0, 0xFCF0, 0xFC90, 0xFCB0,
5884 0x1580, 0x1480, 0x1780, 0x1680, 0x1180, 0x1080, 0x1380, 0x1280, 0x1D80, 0x1C80, 0x1F80, 0x1E80, 0x1980, 0x1880, 0x1B80, 0x1A80,
5885 0x0AC0, 0x0A40, 0x0BC0, 0x0B40, 0x08C0, 0x0840, 0x09C0, 0x0940, 0x0EC0, 0x0E40, 0x0FC0, 0x0F40, 0x0CC0, 0x0C40, 0x0DC0, 0x0D40,
5886 0x5600, 0x5200, 0x5E00, 0x5A00, 0x4600, 0x4200, 0x4E00, 0x4A00, 0x7600, 0x7200, 0x7E00, 0x7A00, 0x6600, 0x6200, 0x6E00, 0x6A00,
5887 0x2B00, 0x2900, 0x2F00, 0x2D00, 0x2300, 0x2100, 0x2700, 0x2500, 0x3B00, 0x3900, 0x3F00, 0x3D00, 0x3300, 0x3100, 0x3700, 0x3500,
5888 0x0158, 0x0148, 0x0178, 0x0168, 0x0118, 0x0108, 0x0138, 0x0128, 0x01D8, 0x01C8, 0x01F8, 0x01E8, 0x0198, 0x0188, 0x01B8, 0x01A8,
5889 0x0058, 0x0048, 0x0078, 0x0068, 0x0018, 0x0008, 0x0038, 0x0028, 0x00D8, 0x00C8, 0x00F8, 0x00E8, 0x0098, 0x0088, 0x00B8, 0x00A8,
5890 0x0560, 0x0520, 0x05E0, 0x05A0, 0x0460, 0x0420, 0x04E0, 0x04A0, 0x0760, 0x0720, 0x07E0, 0x07A0, 0x0660, 0x0620, 0x06E0, 0x06A0,
5891 0x02B0, 0x0290, 0x02F0, 0x02D0, 0x0230, 0x0210, 0x0270, 0x0250, 0x03B0, 0x0390, 0x03F0, 0x03D0, 0x0330, 0x0310, 0x0370, 0x0350
5894 static unsigned short g_drwavMulawTable
[256] = {
5895 0x8284, 0x8684, 0x8A84, 0x8E84, 0x9284, 0x9684, 0x9A84, 0x9E84, 0xA284, 0xA684, 0xAA84, 0xAE84, 0xB284, 0xB684, 0xBA84, 0xBE84,
5896 0xC184, 0xC384, 0xC584, 0xC784, 0xC984, 0xCB84, 0xCD84, 0xCF84, 0xD184, 0xD384, 0xD584, 0xD784, 0xD984, 0xDB84, 0xDD84, 0xDF84,
5897 0xE104, 0xE204, 0xE304, 0xE404, 0xE504, 0xE604, 0xE704, 0xE804, 0xE904, 0xEA04, 0xEB04, 0xEC04, 0xED04, 0xEE04, 0xEF04, 0xF004,
5898 0xF0C4, 0xF144, 0xF1C4, 0xF244, 0xF2C4, 0xF344, 0xF3C4, 0xF444, 0xF4C4, 0xF544, 0xF5C4, 0xF644, 0xF6C4, 0xF744, 0xF7C4, 0xF844,
5899 0xF8A4, 0xF8E4, 0xF924, 0xF964, 0xF9A4, 0xF9E4, 0xFA24, 0xFA64, 0xFAA4, 0xFAE4, 0xFB24, 0xFB64, 0xFBA4, 0xFBE4, 0xFC24, 0xFC64,
5900 0xFC94, 0xFCB4, 0xFCD4, 0xFCF4, 0xFD14, 0xFD34, 0xFD54, 0xFD74, 0xFD94, 0xFDB4, 0xFDD4, 0xFDF4, 0xFE14, 0xFE34, 0xFE54, 0xFE74,
5901 0xFE8C, 0xFE9C, 0xFEAC, 0xFEBC, 0xFECC, 0xFEDC, 0xFEEC, 0xFEFC, 0xFF0C, 0xFF1C, 0xFF2C, 0xFF3C, 0xFF4C, 0xFF5C, 0xFF6C, 0xFF7C,
5902 0xFF88, 0xFF90, 0xFF98, 0xFFA0, 0xFFA8, 0xFFB0, 0xFFB8, 0xFFC0, 0xFFC8, 0xFFD0, 0xFFD8, 0xFFE0, 0xFFE8, 0xFFF0, 0xFFF8, 0x0000,
5903 0x7D7C, 0x797C, 0x757C, 0x717C, 0x6D7C, 0x697C, 0x657C, 0x617C, 0x5D7C, 0x597C, 0x557C, 0x517C, 0x4D7C, 0x497C, 0x457C, 0x417C,
5904 0x3E7C, 0x3C7C, 0x3A7C, 0x387C, 0x367C, 0x347C, 0x327C, 0x307C, 0x2E7C, 0x2C7C, 0x2A7C, 0x287C, 0x267C, 0x247C, 0x227C, 0x207C,
5905 0x1EFC, 0x1DFC, 0x1CFC, 0x1BFC, 0x1AFC, 0x19FC, 0x18FC, 0x17FC, 0x16FC, 0x15FC, 0x14FC, 0x13FC, 0x12FC, 0x11FC, 0x10FC, 0x0FFC,
5906 0x0F3C, 0x0EBC, 0x0E3C, 0x0DBC, 0x0D3C, 0x0CBC, 0x0C3C, 0x0BBC, 0x0B3C, 0x0ABC, 0x0A3C, 0x09BC, 0x093C, 0x08BC, 0x083C, 0x07BC,
5907 0x075C, 0x071C, 0x06DC, 0x069C, 0x065C, 0x061C, 0x05DC, 0x059C, 0x055C, 0x051C, 0x04DC, 0x049C, 0x045C, 0x041C, 0x03DC, 0x039C,
5908 0x036C, 0x034C, 0x032C, 0x030C, 0x02EC, 0x02CC, 0x02AC, 0x028C, 0x026C, 0x024C, 0x022C, 0x020C, 0x01EC, 0x01CC, 0x01AC, 0x018C,
5909 0x0174, 0x0164, 0x0154, 0x0144, 0x0134, 0x0124, 0x0114, 0x0104, 0x00F4, 0x00E4, 0x00D4, 0x00C4, 0x00B4, 0x00A4, 0x0094, 0x0084,
5910 0x0078, 0x0070, 0x0068, 0x0060, 0x0058, 0x0050, 0x0048, 0x0040, 0x0038, 0x0030, 0x0028, 0x0020, 0x0018, 0x0010, 0x0008, 0x0000
5913 static DRWAV_INLINE drwav_int16
drwav__alaw_to_s16(drwav_uint8 sampleIn
)
5915 return (short)g_drwavAlawTable
[sampleIn
];
5918 static DRWAV_INLINE drwav_int16
drwav__mulaw_to_s16(drwav_uint8 sampleIn
)
5920 return (short)g_drwavMulawTable
[sampleIn
];
5925 DRWAV_PRIVATE
void drwav__pcm_to_s16(drwav_int16
* pOut
, const drwav_uint8
* pIn
, size_t totalSampleCount
, unsigned int bytesPerSample
)
5929 /* Special case for 8-bit sample data because it's treated as unsigned. */
5930 if (bytesPerSample
== 1) {
5931 drwav_u8_to_s16(pOut
, pIn
, totalSampleCount
);
5936 /* Slightly more optimal implementation for common formats. */
5937 if (bytesPerSample
== 2) {
5938 for (i
= 0; i
< totalSampleCount
; ++i
) {
5939 *pOut
++ = ((const drwav_int16
*)pIn
)[i
];
5943 if (bytesPerSample
== 3) {
5944 drwav_s24_to_s16(pOut
, pIn
, totalSampleCount
);
5947 if (bytesPerSample
== 4) {
5948 drwav_s32_to_s16(pOut
, (const drwav_int32
*)pIn
, totalSampleCount
);
5953 /* Anything more than 64 bits per sample is not supported. */
5954 if (bytesPerSample
> 8) {
5955 DRWAV_ZERO_MEMORY(pOut
, totalSampleCount
* sizeof(*pOut
));
5960 /* Generic, slow converter. */
5961 for (i
= 0; i
< totalSampleCount
; ++i
) {
5962 drwav_uint64 sample
= 0;
5963 unsigned int shift
= (8 - bytesPerSample
) * 8;
5966 for (j
= 0; j
< bytesPerSample
; j
+= 1) {
5967 DRWAV_ASSERT(j
< 8);
5968 sample
|= (drwav_uint64
)(pIn
[j
]) << shift
;
5973 *pOut
++ = (drwav_int16
)((drwav_int64
)sample
>> 48);
5977 DRWAV_PRIVATE
void drwav__ieee_to_s16(drwav_int16
* pOut
, const drwav_uint8
* pIn
, size_t totalSampleCount
, unsigned int bytesPerSample
)
5979 if (bytesPerSample
== 4) {
5980 drwav_f32_to_s16(pOut
, (const float*)pIn
, totalSampleCount
);
5982 } else if (bytesPerSample
== 8) {
5983 drwav_f64_to_s16(pOut
, (const double*)pIn
, totalSampleCount
);
5986 /* Only supporting 32- and 64-bit float. Output silence in all other cases. Contributions welcome for 16-bit float. */
5987 DRWAV_ZERO_MEMORY(pOut
, totalSampleCount
* sizeof(*pOut
));
5992 DRWAV_PRIVATE drwav_uint64
drwav_read_pcm_frames_s16__pcm(drwav
* pWav
, drwav_uint64 framesToRead
, drwav_int16
* pBufferOut
)
5994 drwav_uint64 totalFramesRead
;
5995 drwav_uint8 sampleData
[4096];
5996 drwav_uint32 bytesPerFrame
;
5999 if ((pWav
->translatedFormatTag
== DR_WAVE_FORMAT_PCM
&& pWav
->bitsPerSample
== 16) || pBufferOut
== NULL
) {
6000 return drwav_read_pcm_frames(pWav
, framesToRead
, pBufferOut
);
6003 bytesPerFrame
= drwav_get_bytes_per_pcm_frame(pWav
);
6004 if (bytesPerFrame
== 0) {
6008 totalFramesRead
= 0;
6010 while (framesToRead
> 0) {
6011 drwav_uint64 framesRead
= drwav_read_pcm_frames(pWav
, drwav_min(framesToRead
, sizeof(sampleData
)/bytesPerFrame
), sampleData
);
6012 if (framesRead
== 0) {
6016 drwav__pcm_to_s16(pBufferOut
, sampleData
, (size_t)(framesRead
*pWav
->channels
), bytesPerFrame
/pWav
->channels
);
6018 pBufferOut
+= framesRead
*pWav
->channels
;
6019 framesToRead
-= framesRead
;
6020 totalFramesRead
+= framesRead
;
6023 return totalFramesRead
;
6026 DRWAV_PRIVATE drwav_uint64
drwav_read_pcm_frames_s16__ieee(drwav
* pWav
, drwav_uint64 framesToRead
, drwav_int16
* pBufferOut
)
6028 drwav_uint64 totalFramesRead
;
6029 drwav_uint8 sampleData
[4096];
6030 drwav_uint32 bytesPerFrame
;
6032 if (pBufferOut
== NULL
) {
6033 return drwav_read_pcm_frames(pWav
, framesToRead
, NULL
);
6036 bytesPerFrame
= drwav_get_bytes_per_pcm_frame(pWav
);
6037 if (bytesPerFrame
== 0) {
6041 totalFramesRead
= 0;
6043 while (framesToRead
> 0) {
6044 drwav_uint64 framesRead
= drwav_read_pcm_frames(pWav
, drwav_min(framesToRead
, sizeof(sampleData
)/bytesPerFrame
), sampleData
);
6045 if (framesRead
== 0) {
6049 drwav__ieee_to_s16(pBufferOut
, sampleData
, (size_t)(framesRead
*pWav
->channels
), bytesPerFrame
/pWav
->channels
);
6051 pBufferOut
+= framesRead
*pWav
->channels
;
6052 framesToRead
-= framesRead
;
6053 totalFramesRead
+= framesRead
;
6056 return totalFramesRead
;
6059 DRWAV_PRIVATE drwav_uint64
drwav_read_pcm_frames_s16__alaw(drwav
* pWav
, drwav_uint64 framesToRead
, drwav_int16
* pBufferOut
)
6061 drwav_uint64 totalFramesRead
;
6062 drwav_uint8 sampleData
[4096];
6063 drwav_uint32 bytesPerFrame
;
6065 if (pBufferOut
== NULL
) {
6066 return drwav_read_pcm_frames(pWav
, framesToRead
, NULL
);
6069 bytesPerFrame
= drwav_get_bytes_per_pcm_frame(pWav
);
6070 if (bytesPerFrame
== 0) {
6074 totalFramesRead
= 0;
6076 while (framesToRead
> 0) {
6077 drwav_uint64 framesRead
= drwav_read_pcm_frames(pWav
, drwav_min(framesToRead
, sizeof(sampleData
)/bytesPerFrame
), sampleData
);
6078 if (framesRead
== 0) {
6082 drwav_alaw_to_s16(pBufferOut
, sampleData
, (size_t)(framesRead
*pWav
->channels
));
6084 pBufferOut
+= framesRead
*pWav
->channels
;
6085 framesToRead
-= framesRead
;
6086 totalFramesRead
+= framesRead
;
6089 return totalFramesRead
;
6092 DRWAV_PRIVATE drwav_uint64
drwav_read_pcm_frames_s16__mulaw(drwav
* pWav
, drwav_uint64 framesToRead
, drwav_int16
* pBufferOut
)
6094 drwav_uint64 totalFramesRead
;
6095 drwav_uint8 sampleData
[4096];
6096 drwav_uint32 bytesPerFrame
;
6098 if (pBufferOut
== NULL
) {
6099 return drwav_read_pcm_frames(pWav
, framesToRead
, NULL
);
6102 bytesPerFrame
= drwav_get_bytes_per_pcm_frame(pWav
);
6103 if (bytesPerFrame
== 0) {
6107 totalFramesRead
= 0;
6109 while (framesToRead
> 0) {
6110 drwav_uint64 framesRead
= drwav_read_pcm_frames(pWav
, drwav_min(framesToRead
, sizeof(sampleData
)/bytesPerFrame
), sampleData
);
6111 if (framesRead
== 0) {
6115 drwav_mulaw_to_s16(pBufferOut
, sampleData
, (size_t)(framesRead
*pWav
->channels
));
6117 pBufferOut
+= framesRead
*pWav
->channels
;
6118 framesToRead
-= framesRead
;
6119 totalFramesRead
+= framesRead
;
6122 return totalFramesRead
;
6125 DRWAV_API drwav_uint64
drwav_read_pcm_frames_s16(drwav
* pWav
, drwav_uint64 framesToRead
, drwav_int16
* pBufferOut
)
6127 if (pWav
== NULL
|| framesToRead
== 0) {
6131 if (pBufferOut
== NULL
) {
6132 return drwav_read_pcm_frames(pWav
, framesToRead
, NULL
);
6135 /* Don't try to read more samples than can potentially fit in the output buffer. */
6136 if (framesToRead
* pWav
->channels
* sizeof(drwav_int16
) > DRWAV_SIZE_MAX
) {
6137 framesToRead
= DRWAV_SIZE_MAX
/ sizeof(drwav_int16
) / pWav
->channels
;
6140 if (pWav
->translatedFormatTag
== DR_WAVE_FORMAT_PCM
) {
6141 return drwav_read_pcm_frames_s16__pcm(pWav
, framesToRead
, pBufferOut
);
6144 if (pWav
->translatedFormatTag
== DR_WAVE_FORMAT_IEEE_FLOAT
) {
6145 return drwav_read_pcm_frames_s16__ieee(pWav
, framesToRead
, pBufferOut
);
6148 if (pWav
->translatedFormatTag
== DR_WAVE_FORMAT_ALAW
) {
6149 return drwav_read_pcm_frames_s16__alaw(pWav
, framesToRead
, pBufferOut
);
6152 if (pWav
->translatedFormatTag
== DR_WAVE_FORMAT_MULAW
) {
6153 return drwav_read_pcm_frames_s16__mulaw(pWav
, framesToRead
, pBufferOut
);
6156 if (pWav
->translatedFormatTag
== DR_WAVE_FORMAT_ADPCM
) {
6157 return drwav_read_pcm_frames_s16__msadpcm(pWav
, framesToRead
, pBufferOut
);
6160 if (pWav
->translatedFormatTag
== DR_WAVE_FORMAT_DVI_ADPCM
) {
6161 return drwav_read_pcm_frames_s16__ima(pWav
, framesToRead
, pBufferOut
);
6167 DRWAV_API drwav_uint64
drwav_read_pcm_frames_s16le(drwav
* pWav
, drwav_uint64 framesToRead
, drwav_int16
* pBufferOut
)
6169 drwav_uint64 framesRead
= drwav_read_pcm_frames_s16(pWav
, framesToRead
, pBufferOut
);
6170 if (pBufferOut
!= NULL
&& drwav__is_little_endian() == DRWAV_FALSE
) {
6171 drwav__bswap_samples_s16(pBufferOut
, framesRead
*pWav
->channels
);
6177 DRWAV_API drwav_uint64
drwav_read_pcm_frames_s16be(drwav
* pWav
, drwav_uint64 framesToRead
, drwav_int16
* pBufferOut
)
6179 drwav_uint64 framesRead
= drwav_read_pcm_frames_s16(pWav
, framesToRead
, pBufferOut
);
6180 if (pBufferOut
!= NULL
&& drwav__is_little_endian() == DRWAV_TRUE
) {
6181 drwav__bswap_samples_s16(pBufferOut
, framesRead
*pWav
->channels
);
6188 DRWAV_API
void drwav_u8_to_s16(drwav_int16
* pOut
, const drwav_uint8
* pIn
, size_t sampleCount
)
6192 for (i
= 0; i
< sampleCount
; ++i
) {
6200 DRWAV_API
void drwav_s24_to_s16(drwav_int16
* pOut
, const drwav_uint8
* pIn
, size_t sampleCount
)
6204 for (i
= 0; i
< sampleCount
; ++i
) {
6205 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;
6211 DRWAV_API
void drwav_s32_to_s16(drwav_int16
* pOut
, const drwav_int32
* pIn
, size_t sampleCount
)
6215 for (i
= 0; i
< sampleCount
; ++i
) {
6222 DRWAV_API
void drwav_f32_to_s16(drwav_int16
* pOut
, const float* pIn
, size_t sampleCount
)
6226 for (i
= 0; i
< sampleCount
; ++i
) {
6229 c
= ((x
< -1) ? -1 : ((x
> 1) ? 1 : x
));
6231 r
= (int)(c
* 32767.5f
);
6237 DRWAV_API
void drwav_f64_to_s16(drwav_int16
* pOut
, const double* pIn
, size_t sampleCount
)
6241 for (i
= 0; i
< sampleCount
; ++i
) {
6244 c
= ((x
< -1) ? -1 : ((x
> 1) ? 1 : x
));
6246 r
= (int)(c
* 32767.5);
6252 DRWAV_API
void drwav_alaw_to_s16(drwav_int16
* pOut
, const drwav_uint8
* pIn
, size_t sampleCount
)
6255 for (i
= 0; i
< sampleCount
; ++i
) {
6256 pOut
[i
] = drwav__alaw_to_s16(pIn
[i
]);
6260 DRWAV_API
void drwav_mulaw_to_s16(drwav_int16
* pOut
, const drwav_uint8
* pIn
, size_t sampleCount
)
6263 for (i
= 0; i
< sampleCount
; ++i
) {
6264 pOut
[i
] = drwav__mulaw_to_s16(pIn
[i
]);
6270 DRWAV_PRIVATE
void drwav__pcm_to_f32(float* pOut
, const drwav_uint8
* pIn
, size_t sampleCount
, unsigned int bytesPerSample
)
6274 /* Special case for 8-bit sample data because it's treated as unsigned. */
6275 if (bytesPerSample
== 1) {
6276 drwav_u8_to_f32(pOut
, pIn
, sampleCount
);
6280 /* Slightly more optimal implementation for common formats. */
6281 if (bytesPerSample
== 2) {
6282 drwav_s16_to_f32(pOut
, (const drwav_int16
*)pIn
, sampleCount
);
6285 if (bytesPerSample
== 3) {
6286 drwav_s24_to_f32(pOut
, pIn
, sampleCount
);
6289 if (bytesPerSample
== 4) {
6290 drwav_s32_to_f32(pOut
, (const drwav_int32
*)pIn
, sampleCount
);
6295 /* Anything more than 64 bits per sample is not supported. */
6296 if (bytesPerSample
> 8) {
6297 DRWAV_ZERO_MEMORY(pOut
, sampleCount
* sizeof(*pOut
));
6302 /* Generic, slow converter. */
6303 for (i
= 0; i
< sampleCount
; ++i
) {
6304 drwav_uint64 sample
= 0;
6305 unsigned int shift
= (8 - bytesPerSample
) * 8;
6308 for (j
= 0; j
< bytesPerSample
; j
+= 1) {
6309 DRWAV_ASSERT(j
< 8);
6310 sample
|= (drwav_uint64
)(pIn
[j
]) << shift
;
6315 *pOut
++ = (float)((drwav_int64
)sample
/ 9223372036854775807.0);
6319 DRWAV_PRIVATE
void drwav__ieee_to_f32(float* pOut
, const drwav_uint8
* pIn
, size_t sampleCount
, unsigned int bytesPerSample
)
6321 if (bytesPerSample
== 4) {
6323 for (i
= 0; i
< sampleCount
; ++i
) {
6324 *pOut
++ = ((const float*)pIn
)[i
];
6327 } else if (bytesPerSample
== 8) {
6328 drwav_f64_to_f32(pOut
, (const double*)pIn
, sampleCount
);
6331 /* Only supporting 32- and 64-bit float. Output silence in all other cases. Contributions welcome for 16-bit float. */
6332 DRWAV_ZERO_MEMORY(pOut
, sampleCount
* sizeof(*pOut
));
6338 DRWAV_PRIVATE drwav_uint64
drwav_read_pcm_frames_f32__pcm(drwav
* pWav
, drwav_uint64 framesToRead
, float* pBufferOut
)
6340 drwav_uint64 totalFramesRead
;
6341 drwav_uint8 sampleData
[4096];
6342 drwav_uint32 bytesPerFrame
= drwav_get_bytes_per_pcm_frame(pWav
);
6344 if (bytesPerFrame
== 0) {
6348 totalFramesRead
= 0;
6350 while (framesToRead
> 0) {
6351 drwav_uint64 framesRead
= drwav_read_pcm_frames(pWav
, drwav_min(framesToRead
, sizeof(sampleData
)/bytesPerFrame
), sampleData
);
6352 if (framesRead
== 0) {
6356 drwav__pcm_to_f32(pBufferOut
, sampleData
, (size_t)framesRead
*pWav
->channels
, bytesPerFrame
/pWav
->channels
);
6358 pBufferOut
+= framesRead
*pWav
->channels
;
6359 framesToRead
-= framesRead
;
6360 totalFramesRead
+= framesRead
;
6363 return totalFramesRead
;
6366 DRWAV_PRIVATE drwav_uint64
drwav_read_pcm_frames_f32__msadpcm(drwav
* pWav
, drwav_uint64 framesToRead
, float* pBufferOut
)
6369 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
6370 want to duplicate that code.
6372 drwav_uint64 totalFramesRead
= 0;
6373 drwav_int16 samples16
[2048];
6375 while (framesToRead
> 0) {
6376 drwav_uint64 framesRead
= drwav_read_pcm_frames_s16(pWav
, drwav_min(framesToRead
, drwav_countof(samples16
)/pWav
->channels
), samples16
);
6377 if (framesRead
== 0) {
6381 drwav_s16_to_f32(pBufferOut
, samples16
, (size_t)(framesRead
*pWav
->channels
)); /* <-- Safe cast because we're clamping to 2048. */
6383 pBufferOut
+= framesRead
*pWav
->channels
;
6384 framesToRead
-= framesRead
;
6385 totalFramesRead
+= framesRead
;
6388 return totalFramesRead
;
6391 DRWAV_PRIVATE drwav_uint64
drwav_read_pcm_frames_f32__ima(drwav
* pWav
, drwav_uint64 framesToRead
, float* pBufferOut
)
6394 We're just going to borrow the implementation from the drwav_read_s16() since IMA-ADPCM is a little bit more complicated than other formats and I don't
6395 want to duplicate that code.
6397 drwav_uint64 totalFramesRead
= 0;
6398 drwav_int16 samples16
[2048];
6400 while (framesToRead
> 0) {
6401 drwav_uint64 framesRead
= drwav_read_pcm_frames_s16(pWav
, drwav_min(framesToRead
, drwav_countof(samples16
)/pWav
->channels
), samples16
);
6402 if (framesRead
== 0) {
6406 drwav_s16_to_f32(pBufferOut
, samples16
, (size_t)(framesRead
*pWav
->channels
)); /* <-- Safe cast because we're clamping to 2048. */
6408 pBufferOut
+= framesRead
*pWav
->channels
;
6409 framesToRead
-= framesRead
;
6410 totalFramesRead
+= framesRead
;
6413 return totalFramesRead
;
6416 DRWAV_PRIVATE drwav_uint64
drwav_read_pcm_frames_f32__ieee(drwav
* pWav
, drwav_uint64 framesToRead
, float* pBufferOut
)
6418 drwav_uint64 totalFramesRead
;
6419 drwav_uint8 sampleData
[4096];
6420 drwav_uint32 bytesPerFrame
;
6423 if (pWav
->translatedFormatTag
== DR_WAVE_FORMAT_IEEE_FLOAT
&& pWav
->bitsPerSample
== 32) {
6424 return drwav_read_pcm_frames(pWav
, framesToRead
, pBufferOut
);
6427 bytesPerFrame
= drwav_get_bytes_per_pcm_frame(pWav
);
6428 if (bytesPerFrame
== 0) {
6432 totalFramesRead
= 0;
6434 while (framesToRead
> 0) {
6435 drwav_uint64 framesRead
= drwav_read_pcm_frames(pWav
, drwav_min(framesToRead
, sizeof(sampleData
)/bytesPerFrame
), sampleData
);
6436 if (framesRead
== 0) {
6440 drwav__ieee_to_f32(pBufferOut
, sampleData
, (size_t)(framesRead
*pWav
->channels
), bytesPerFrame
/pWav
->channels
);
6442 pBufferOut
+= framesRead
*pWav
->channels
;
6443 framesToRead
-= framesRead
;
6444 totalFramesRead
+= framesRead
;
6447 return totalFramesRead
;
6450 DRWAV_PRIVATE drwav_uint64
drwav_read_pcm_frames_f32__alaw(drwav
* pWav
, drwav_uint64 framesToRead
, float* pBufferOut
)
6452 drwav_uint64 totalFramesRead
;
6453 drwav_uint8 sampleData
[4096];
6454 drwav_uint32 bytesPerFrame
= drwav_get_bytes_per_pcm_frame(pWav
);
6456 if (bytesPerFrame
== 0) {
6460 totalFramesRead
= 0;
6462 while (framesToRead
> 0) {
6463 drwav_uint64 framesRead
= drwav_read_pcm_frames(pWav
, drwav_min(framesToRead
, sizeof(sampleData
)/bytesPerFrame
), sampleData
);
6464 if (framesRead
== 0) {
6468 drwav_alaw_to_f32(pBufferOut
, sampleData
, (size_t)(framesRead
*pWav
->channels
));
6470 pBufferOut
+= framesRead
*pWav
->channels
;
6471 framesToRead
-= framesRead
;
6472 totalFramesRead
+= framesRead
;
6475 return totalFramesRead
;
6478 DRWAV_PRIVATE drwav_uint64
drwav_read_pcm_frames_f32__mulaw(drwav
* pWav
, drwav_uint64 framesToRead
, float* pBufferOut
)
6480 drwav_uint64 totalFramesRead
;
6481 drwav_uint8 sampleData
[4096];
6482 drwav_uint32 bytesPerFrame
= drwav_get_bytes_per_pcm_frame(pWav
);
6484 if (bytesPerFrame
== 0) {
6488 totalFramesRead
= 0;
6490 while (framesToRead
> 0) {
6491 drwav_uint64 framesRead
= drwav_read_pcm_frames(pWav
, drwav_min(framesToRead
, sizeof(sampleData
)/bytesPerFrame
), sampleData
);
6492 if (framesRead
== 0) {
6496 drwav_mulaw_to_f32(pBufferOut
, sampleData
, (size_t)(framesRead
*pWav
->channels
));
6498 pBufferOut
+= framesRead
*pWav
->channels
;
6499 framesToRead
-= framesRead
;
6500 totalFramesRead
+= framesRead
;
6503 return totalFramesRead
;
6506 DRWAV_API drwav_uint64
drwav_read_pcm_frames_f32(drwav
* pWav
, drwav_uint64 framesToRead
, float* pBufferOut
)
6508 if (pWav
== NULL
|| framesToRead
== 0) {
6512 if (pBufferOut
== NULL
) {
6513 return drwav_read_pcm_frames(pWav
, framesToRead
, NULL
);
6516 /* Don't try to read more samples than can potentially fit in the output buffer. */
6517 if (framesToRead
* pWav
->channels
* sizeof(float) > DRWAV_SIZE_MAX
) {
6518 framesToRead
= DRWAV_SIZE_MAX
/ sizeof(float) / pWav
->channels
;
6521 if (pWav
->translatedFormatTag
== DR_WAVE_FORMAT_PCM
) {
6522 return drwav_read_pcm_frames_f32__pcm(pWav
, framesToRead
, pBufferOut
);
6525 if (pWav
->translatedFormatTag
== DR_WAVE_FORMAT_ADPCM
) {
6526 return drwav_read_pcm_frames_f32__msadpcm(pWav
, framesToRead
, pBufferOut
);
6529 if (pWav
->translatedFormatTag
== DR_WAVE_FORMAT_IEEE_FLOAT
) {
6530 return drwav_read_pcm_frames_f32__ieee(pWav
, framesToRead
, pBufferOut
);
6533 if (pWav
->translatedFormatTag
== DR_WAVE_FORMAT_ALAW
) {
6534 return drwav_read_pcm_frames_f32__alaw(pWav
, framesToRead
, pBufferOut
);
6537 if (pWav
->translatedFormatTag
== DR_WAVE_FORMAT_MULAW
) {
6538 return drwav_read_pcm_frames_f32__mulaw(pWav
, framesToRead
, pBufferOut
);
6541 if (pWav
->translatedFormatTag
== DR_WAVE_FORMAT_DVI_ADPCM
) {
6542 return drwav_read_pcm_frames_f32__ima(pWav
, framesToRead
, pBufferOut
);
6548 DRWAV_API drwav_uint64
drwav_read_pcm_frames_f32le(drwav
* pWav
, drwav_uint64 framesToRead
, float* pBufferOut
)
6550 drwav_uint64 framesRead
= drwav_read_pcm_frames_f32(pWav
, framesToRead
, pBufferOut
);
6551 if (pBufferOut
!= NULL
&& drwav__is_little_endian() == DRWAV_FALSE
) {
6552 drwav__bswap_samples_f32(pBufferOut
, framesRead
*pWav
->channels
);
6558 DRWAV_API drwav_uint64
drwav_read_pcm_frames_f32be(drwav
* pWav
, drwav_uint64 framesToRead
, float* pBufferOut
)
6560 drwav_uint64 framesRead
= drwav_read_pcm_frames_f32(pWav
, framesToRead
, pBufferOut
);
6561 if (pBufferOut
!= NULL
&& drwav__is_little_endian() == DRWAV_TRUE
) {
6562 drwav__bswap_samples_f32(pBufferOut
, framesRead
*pWav
->channels
);
6569 DRWAV_API
void drwav_u8_to_f32(float* pOut
, const drwav_uint8
* pIn
, size_t sampleCount
)
6573 if (pOut
== NULL
|| pIn
== NULL
) {
6577 #ifdef DR_WAV_LIBSNDFILE_COMPAT
6579 It appears libsndfile uses slightly different logic for the u8 -> f32 conversion to dr_wav, which in my opinion is incorrect. It appears
6580 libsndfile performs the conversion something like "f32 = (u8 / 256) * 2 - 1", however I think it should be "f32 = (u8 / 255) * 2 - 1" (note
6581 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
6582 correctness testing. This is disabled by default.
6584 for (i
= 0; i
< sampleCount
; ++i
) {
6585 *pOut
++ = (pIn
[i
] / 256.0f
) * 2 - 1;
6588 for (i
= 0; i
< sampleCount
; ++i
) {
6590 x
= x
* 0.00784313725490196078f
; /* 0..255 to 0..2 */
6591 x
= x
- 1; /* 0..2 to -1..1 */
6598 DRWAV_API
void drwav_s16_to_f32(float* pOut
, const drwav_int16
* pIn
, size_t sampleCount
)
6602 if (pOut
== NULL
|| pIn
== NULL
) {
6606 for (i
= 0; i
< sampleCount
; ++i
) {
6607 *pOut
++ = pIn
[i
] * 0.000030517578125f
;
6611 DRWAV_API
void drwav_s24_to_f32(float* pOut
, const drwav_uint8
* pIn
, size_t sampleCount
)
6615 if (pOut
== NULL
|| pIn
== NULL
) {
6619 for (i
= 0; i
< sampleCount
; ++i
) {
6621 drwav_uint32 a
= ((drwav_uint32
)(pIn
[i
*3+0]) << 8);
6622 drwav_uint32 b
= ((drwav_uint32
)(pIn
[i
*3+1]) << 16);
6623 drwav_uint32 c
= ((drwav_uint32
)(pIn
[i
*3+2]) << 24);
6625 x
= (double)((drwav_int32
)(a
| b
| c
) >> 8);
6626 *pOut
++ = (float)(x
* 0.00000011920928955078125);
6630 DRWAV_API
void drwav_s32_to_f32(float* pOut
, const drwav_int32
* pIn
, size_t sampleCount
)
6633 if (pOut
== NULL
|| pIn
== NULL
) {
6637 for (i
= 0; i
< sampleCount
; ++i
) {
6638 *pOut
++ = (float)(pIn
[i
] / 2147483648.0);
6642 DRWAV_API
void drwav_f64_to_f32(float* pOut
, const double* pIn
, size_t sampleCount
)
6646 if (pOut
== NULL
|| pIn
== NULL
) {
6650 for (i
= 0; i
< sampleCount
; ++i
) {
6651 *pOut
++ = (float)pIn
[i
];
6655 DRWAV_API
void drwav_alaw_to_f32(float* pOut
, const drwav_uint8
* pIn
, size_t sampleCount
)
6659 if (pOut
== NULL
|| pIn
== NULL
) {
6663 for (i
= 0; i
< sampleCount
; ++i
) {
6664 *pOut
++ = drwav__alaw_to_s16(pIn
[i
]) / 32768.0f
;
6668 DRWAV_API
void drwav_mulaw_to_f32(float* pOut
, const drwav_uint8
* pIn
, size_t sampleCount
)
6672 if (pOut
== NULL
|| pIn
== NULL
) {
6676 for (i
= 0; i
< sampleCount
; ++i
) {
6677 *pOut
++ = drwav__mulaw_to_s16(pIn
[i
]) / 32768.0f
;
6683 DRWAV_PRIVATE
void drwav__pcm_to_s32(drwav_int32
* pOut
, const drwav_uint8
* pIn
, size_t totalSampleCount
, unsigned int bytesPerSample
)
6687 /* Special case for 8-bit sample data because it's treated as unsigned. */
6688 if (bytesPerSample
== 1) {
6689 drwav_u8_to_s32(pOut
, pIn
, totalSampleCount
);
6693 /* Slightly more optimal implementation for common formats. */
6694 if (bytesPerSample
== 2) {
6695 drwav_s16_to_s32(pOut
, (const drwav_int16
*)pIn
, totalSampleCount
);
6698 if (bytesPerSample
== 3) {
6699 drwav_s24_to_s32(pOut
, pIn
, totalSampleCount
);
6702 if (bytesPerSample
== 4) {
6703 for (i
= 0; i
< totalSampleCount
; ++i
) {
6704 *pOut
++ = ((const drwav_int32
*)pIn
)[i
];
6710 /* Anything more than 64 bits per sample is not supported. */
6711 if (bytesPerSample
> 8) {
6712 DRWAV_ZERO_MEMORY(pOut
, totalSampleCount
* sizeof(*pOut
));
6717 /* Generic, slow converter. */
6718 for (i
= 0; i
< totalSampleCount
; ++i
) {
6719 drwav_uint64 sample
= 0;
6720 unsigned int shift
= (8 - bytesPerSample
) * 8;
6723 for (j
= 0; j
< bytesPerSample
; j
+= 1) {
6724 DRWAV_ASSERT(j
< 8);
6725 sample
|= (drwav_uint64
)(pIn
[j
]) << shift
;
6730 *pOut
++ = (drwav_int32
)((drwav_int64
)sample
>> 32);
6734 DRWAV_PRIVATE
void drwav__ieee_to_s32(drwav_int32
* pOut
, const drwav_uint8
* pIn
, size_t totalSampleCount
, unsigned int bytesPerSample
)
6736 if (bytesPerSample
== 4) {
6737 drwav_f32_to_s32(pOut
, (const float*)pIn
, totalSampleCount
);
6739 } else if (bytesPerSample
== 8) {
6740 drwav_f64_to_s32(pOut
, (const double*)pIn
, totalSampleCount
);
6743 /* Only supporting 32- and 64-bit float. Output silence in all other cases. Contributions welcome for 16-bit float. */
6744 DRWAV_ZERO_MEMORY(pOut
, totalSampleCount
* sizeof(*pOut
));
6750 DRWAV_PRIVATE drwav_uint64
drwav_read_pcm_frames_s32__pcm(drwav
* pWav
, drwav_uint64 framesToRead
, drwav_int32
* pBufferOut
)
6752 drwav_uint64 totalFramesRead
;
6753 drwav_uint8 sampleData
[4096];
6754 drwav_uint32 bytesPerFrame
;
6757 if (pWav
->translatedFormatTag
== DR_WAVE_FORMAT_PCM
&& pWav
->bitsPerSample
== 32) {
6758 return drwav_read_pcm_frames(pWav
, framesToRead
, pBufferOut
);
6761 bytesPerFrame
= drwav_get_bytes_per_pcm_frame(pWav
);
6762 if (bytesPerFrame
== 0) {
6766 totalFramesRead
= 0;
6768 while (framesToRead
> 0) {
6769 drwav_uint64 framesRead
= drwav_read_pcm_frames(pWav
, drwav_min(framesToRead
, sizeof(sampleData
)/bytesPerFrame
), sampleData
);
6770 if (framesRead
== 0) {
6774 drwav__pcm_to_s32(pBufferOut
, sampleData
, (size_t)(framesRead
*pWav
->channels
), bytesPerFrame
/pWav
->channels
);
6776 pBufferOut
+= framesRead
*pWav
->channels
;
6777 framesToRead
-= framesRead
;
6778 totalFramesRead
+= framesRead
;
6781 return totalFramesRead
;
6784 DRWAV_PRIVATE drwav_uint64
drwav_read_pcm_frames_s32__msadpcm(drwav
* pWav
, drwav_uint64 framesToRead
, drwav_int32
* pBufferOut
)
6787 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
6788 want to duplicate that code.
6790 drwav_uint64 totalFramesRead
= 0;
6791 drwav_int16 samples16
[2048];
6793 while (framesToRead
> 0) {
6794 drwav_uint64 framesRead
= drwav_read_pcm_frames_s16(pWav
, drwav_min(framesToRead
, drwav_countof(samples16
)/pWav
->channels
), samples16
);
6795 if (framesRead
== 0) {
6799 drwav_s16_to_s32(pBufferOut
, samples16
, (size_t)(framesRead
*pWav
->channels
)); /* <-- Safe cast because we're clamping to 2048. */
6801 pBufferOut
+= framesRead
*pWav
->channels
;
6802 framesToRead
-= framesRead
;
6803 totalFramesRead
+= framesRead
;
6806 return totalFramesRead
;
6809 DRWAV_PRIVATE drwav_uint64
drwav_read_pcm_frames_s32__ima(drwav
* pWav
, drwav_uint64 framesToRead
, drwav_int32
* pBufferOut
)
6812 We're just going to borrow the implementation from the drwav_read_s16() since IMA-ADPCM is a little bit more complicated than other formats and I don't
6813 want to duplicate that code.
6815 drwav_uint64 totalFramesRead
= 0;
6816 drwav_int16 samples16
[2048];
6818 while (framesToRead
> 0) {
6819 drwav_uint64 framesRead
= drwav_read_pcm_frames_s16(pWav
, drwav_min(framesToRead
, drwav_countof(samples16
)/pWav
->channels
), samples16
);
6820 if (framesRead
== 0) {
6824 drwav_s16_to_s32(pBufferOut
, samples16
, (size_t)(framesRead
*pWav
->channels
)); /* <-- Safe cast because we're clamping to 2048. */
6826 pBufferOut
+= framesRead
*pWav
->channels
;
6827 framesToRead
-= framesRead
;
6828 totalFramesRead
+= framesRead
;
6831 return totalFramesRead
;
6834 DRWAV_PRIVATE drwav_uint64
drwav_read_pcm_frames_s32__ieee(drwav
* pWav
, drwav_uint64 framesToRead
, drwav_int32
* pBufferOut
)
6836 drwav_uint64 totalFramesRead
;
6837 drwav_uint8 sampleData
[4096];
6838 drwav_uint32 bytesPerFrame
= drwav_get_bytes_per_pcm_frame(pWav
);
6840 if (bytesPerFrame
== 0) {
6844 totalFramesRead
= 0;
6846 while (framesToRead
> 0) {
6847 drwav_uint64 framesRead
= drwav_read_pcm_frames(pWav
, drwav_min(framesToRead
, sizeof(sampleData
)/bytesPerFrame
), sampleData
);
6848 if (framesRead
== 0) {
6852 drwav__ieee_to_s32(pBufferOut
, sampleData
, (size_t)(framesRead
*pWav
->channels
), bytesPerFrame
/pWav
->channels
);
6854 pBufferOut
+= framesRead
*pWav
->channels
;
6855 framesToRead
-= framesRead
;
6856 totalFramesRead
+= framesRead
;
6859 return totalFramesRead
;
6862 DRWAV_PRIVATE drwav_uint64
drwav_read_pcm_frames_s32__alaw(drwav
* pWav
, drwav_uint64 framesToRead
, drwav_int32
* pBufferOut
)
6864 drwav_uint64 totalFramesRead
;
6865 drwav_uint8 sampleData
[4096];
6866 drwav_uint32 bytesPerFrame
= drwav_get_bytes_per_pcm_frame(pWav
);
6868 if (bytesPerFrame
== 0) {
6872 totalFramesRead
= 0;
6874 while (framesToRead
> 0) {
6875 drwav_uint64 framesRead
= drwav_read_pcm_frames(pWav
, drwav_min(framesToRead
, sizeof(sampleData
)/bytesPerFrame
), sampleData
);
6876 if (framesRead
== 0) {
6880 drwav_alaw_to_s32(pBufferOut
, sampleData
, (size_t)(framesRead
*pWav
->channels
));
6882 pBufferOut
+= framesRead
*pWav
->channels
;
6883 framesToRead
-= framesRead
;
6884 totalFramesRead
+= framesRead
;
6887 return totalFramesRead
;
6890 DRWAV_PRIVATE drwav_uint64
drwav_read_pcm_frames_s32__mulaw(drwav
* pWav
, drwav_uint64 framesToRead
, drwav_int32
* pBufferOut
)
6892 drwav_uint64 totalFramesRead
;
6893 drwav_uint8 sampleData
[4096];
6894 drwav_uint32 bytesPerFrame
= drwav_get_bytes_per_pcm_frame(pWav
);
6896 if (bytesPerFrame
== 0) {
6900 totalFramesRead
= 0;
6902 while (framesToRead
> 0) {
6903 drwav_uint64 framesRead
= drwav_read_pcm_frames(pWav
, drwav_min(framesToRead
, sizeof(sampleData
)/bytesPerFrame
), sampleData
);
6904 if (framesRead
== 0) {
6908 drwav_mulaw_to_s32(pBufferOut
, sampleData
, (size_t)(framesRead
*pWav
->channels
));
6910 pBufferOut
+= framesRead
*pWav
->channels
;
6911 framesToRead
-= framesRead
;
6912 totalFramesRead
+= framesRead
;
6915 return totalFramesRead
;
6918 DRWAV_API drwav_uint64
drwav_read_pcm_frames_s32(drwav
* pWav
, drwav_uint64 framesToRead
, drwav_int32
* pBufferOut
)
6920 if (pWav
== NULL
|| framesToRead
== 0) {
6924 if (pBufferOut
== NULL
) {
6925 return drwav_read_pcm_frames(pWav
, framesToRead
, NULL
);
6928 /* Don't try to read more samples than can potentially fit in the output buffer. */
6929 if (framesToRead
* pWav
->channels
* sizeof(drwav_int32
) > DRWAV_SIZE_MAX
) {
6930 framesToRead
= DRWAV_SIZE_MAX
/ sizeof(drwav_int32
) / pWav
->channels
;
6933 if (pWav
->translatedFormatTag
== DR_WAVE_FORMAT_PCM
) {
6934 return drwav_read_pcm_frames_s32__pcm(pWav
, framesToRead
, pBufferOut
);
6937 if (pWav
->translatedFormatTag
== DR_WAVE_FORMAT_ADPCM
) {
6938 return drwav_read_pcm_frames_s32__msadpcm(pWav
, framesToRead
, pBufferOut
);
6941 if (pWav
->translatedFormatTag
== DR_WAVE_FORMAT_IEEE_FLOAT
) {
6942 return drwav_read_pcm_frames_s32__ieee(pWav
, framesToRead
, pBufferOut
);
6945 if (pWav
->translatedFormatTag
== DR_WAVE_FORMAT_ALAW
) {
6946 return drwav_read_pcm_frames_s32__alaw(pWav
, framesToRead
, pBufferOut
);
6949 if (pWav
->translatedFormatTag
== DR_WAVE_FORMAT_MULAW
) {
6950 return drwav_read_pcm_frames_s32__mulaw(pWav
, framesToRead
, pBufferOut
);
6953 if (pWav
->translatedFormatTag
== DR_WAVE_FORMAT_DVI_ADPCM
) {
6954 return drwav_read_pcm_frames_s32__ima(pWav
, framesToRead
, pBufferOut
);
6960 DRWAV_API drwav_uint64
drwav_read_pcm_frames_s32le(drwav
* pWav
, drwav_uint64 framesToRead
, drwav_int32
* pBufferOut
)
6962 drwav_uint64 framesRead
= drwav_read_pcm_frames_s32(pWav
, framesToRead
, pBufferOut
);
6963 if (pBufferOut
!= NULL
&& drwav__is_little_endian() == DRWAV_FALSE
) {
6964 drwav__bswap_samples_s32(pBufferOut
, framesRead
*pWav
->channels
);
6970 DRWAV_API drwav_uint64
drwav_read_pcm_frames_s32be(drwav
* pWav
, drwav_uint64 framesToRead
, drwav_int32
* pBufferOut
)
6972 drwav_uint64 framesRead
= drwav_read_pcm_frames_s32(pWav
, framesToRead
, pBufferOut
);
6973 if (pBufferOut
!= NULL
&& drwav__is_little_endian() == DRWAV_TRUE
) {
6974 drwav__bswap_samples_s32(pBufferOut
, framesRead
*pWav
->channels
);
6981 DRWAV_API
void drwav_u8_to_s32(drwav_int32
* pOut
, const drwav_uint8
* pIn
, size_t sampleCount
)
6985 if (pOut
== NULL
|| pIn
== NULL
) {
6989 for (i
= 0; i
< sampleCount
; ++i
) {
6990 *pOut
++ = ((int)pIn
[i
] - 128) << 24;
6994 DRWAV_API
void drwav_s16_to_s32(drwav_int32
* pOut
, const drwav_int16
* pIn
, size_t sampleCount
)
6998 if (pOut
== NULL
|| pIn
== NULL
) {
7002 for (i
= 0; i
< sampleCount
; ++i
) {
7003 *pOut
++ = pIn
[i
] << 16;
7007 DRWAV_API
void drwav_s24_to_s32(drwav_int32
* pOut
, const drwav_uint8
* pIn
, size_t sampleCount
)
7011 if (pOut
== NULL
|| pIn
== NULL
) {
7015 for (i
= 0; i
< sampleCount
; ++i
) {
7016 unsigned int s0
= pIn
[i
*3 + 0];
7017 unsigned int s1
= pIn
[i
*3 + 1];
7018 unsigned int s2
= pIn
[i
*3 + 2];
7020 drwav_int32 sample32
= (drwav_int32
)((s0
<< 8) | (s1
<< 16) | (s2
<< 24));
7025 DRWAV_API
void drwav_f32_to_s32(drwav_int32
* pOut
, const float* pIn
, size_t sampleCount
)
7029 if (pOut
== NULL
|| pIn
== NULL
) {
7033 for (i
= 0; i
< sampleCount
; ++i
) {
7034 *pOut
++ = (drwav_int32
)(2147483648.0 * pIn
[i
]);
7038 DRWAV_API
void drwav_f64_to_s32(drwav_int32
* pOut
, const double* pIn
, size_t sampleCount
)
7042 if (pOut
== NULL
|| pIn
== NULL
) {
7046 for (i
= 0; i
< sampleCount
; ++i
) {
7047 *pOut
++ = (drwav_int32
)(2147483648.0 * pIn
[i
]);
7051 DRWAV_API
void drwav_alaw_to_s32(drwav_int32
* pOut
, const drwav_uint8
* pIn
, size_t sampleCount
)
7055 if (pOut
== NULL
|| pIn
== NULL
) {
7059 for (i
= 0; i
< sampleCount
; ++i
) {
7060 *pOut
++ = ((drwav_int32
)drwav__alaw_to_s16(pIn
[i
])) << 16;
7064 DRWAV_API
void drwav_mulaw_to_s32(drwav_int32
* pOut
, const drwav_uint8
* pIn
, size_t sampleCount
)
7068 if (pOut
== NULL
|| pIn
== NULL
) {
7072 for (i
= 0; i
< sampleCount
; ++i
) {
7073 *pOut
++ = ((drwav_int32
)drwav__mulaw_to_s16(pIn
[i
])) << 16;
7079 DRWAV_PRIVATE drwav_int16
* drwav__read_pcm_frames_and_close_s16(drwav
* pWav
, unsigned int* channels
, unsigned int* sampleRate
, drwav_uint64
* totalFrameCount
)
7081 drwav_uint64 sampleDataSize
;
7082 drwav_int16
* pSampleData
;
7083 drwav_uint64 framesRead
;
7085 DRWAV_ASSERT(pWav
!= NULL
);
7087 sampleDataSize
= pWav
->totalPCMFrameCount
* pWav
->channels
* sizeof(drwav_int16
);
7088 if (sampleDataSize
> DRWAV_SIZE_MAX
) {
7090 return NULL
; /* File's too big. */
7093 pSampleData
= (drwav_int16
*)drwav__malloc_from_callbacks((size_t)sampleDataSize
, &pWav
->allocationCallbacks
); /* <-- Safe cast due to the check above. */
7094 if (pSampleData
== NULL
) {
7096 return NULL
; /* Failed to allocate memory. */
7099 framesRead
= drwav_read_pcm_frames_s16(pWav
, (size_t)pWav
->totalPCMFrameCount
, pSampleData
);
7100 if (framesRead
!= pWav
->totalPCMFrameCount
) {
7101 drwav__free_from_callbacks(pSampleData
, &pWav
->allocationCallbacks
);
7103 return NULL
; /* There was an error reading the samples. */
7109 *sampleRate
= pWav
->sampleRate
;
7112 *channels
= pWav
->channels
;
7114 if (totalFrameCount
) {
7115 *totalFrameCount
= pWav
->totalPCMFrameCount
;
7121 DRWAV_PRIVATE
float* drwav__read_pcm_frames_and_close_f32(drwav
* pWav
, unsigned int* channels
, unsigned int* sampleRate
, drwav_uint64
* totalFrameCount
)
7123 drwav_uint64 sampleDataSize
;
7125 drwav_uint64 framesRead
;
7127 DRWAV_ASSERT(pWav
!= NULL
);
7129 sampleDataSize
= pWav
->totalPCMFrameCount
* pWav
->channels
* sizeof(float);
7130 if (sampleDataSize
> DRWAV_SIZE_MAX
) {
7132 return NULL
; /* File's too big. */
7135 pSampleData
= (float*)drwav__malloc_from_callbacks((size_t)sampleDataSize
, &pWav
->allocationCallbacks
); /* <-- Safe cast due to the check above. */
7136 if (pSampleData
== NULL
) {
7138 return NULL
; /* Failed to allocate memory. */
7141 framesRead
= drwav_read_pcm_frames_f32(pWav
, (size_t)pWav
->totalPCMFrameCount
, pSampleData
);
7142 if (framesRead
!= pWav
->totalPCMFrameCount
) {
7143 drwav__free_from_callbacks(pSampleData
, &pWav
->allocationCallbacks
);
7145 return NULL
; /* There was an error reading the samples. */
7151 *sampleRate
= pWav
->sampleRate
;
7154 *channels
= pWav
->channels
;
7156 if (totalFrameCount
) {
7157 *totalFrameCount
= pWav
->totalPCMFrameCount
;
7163 DRWAV_PRIVATE drwav_int32
* drwav__read_pcm_frames_and_close_s32(drwav
* pWav
, unsigned int* channels
, unsigned int* sampleRate
, drwav_uint64
* totalFrameCount
)
7165 drwav_uint64 sampleDataSize
;
7166 drwav_int32
* pSampleData
;
7167 drwav_uint64 framesRead
;
7169 DRWAV_ASSERT(pWav
!= NULL
);
7171 sampleDataSize
= pWav
->totalPCMFrameCount
* pWav
->channels
* sizeof(drwav_int32
);
7172 if (sampleDataSize
> DRWAV_SIZE_MAX
) {
7174 return NULL
; /* File's too big. */
7177 pSampleData
= (drwav_int32
*)drwav__malloc_from_callbacks((size_t)sampleDataSize
, &pWav
->allocationCallbacks
); /* <-- Safe cast due to the check above. */
7178 if (pSampleData
== NULL
) {
7180 return NULL
; /* Failed to allocate memory. */
7183 framesRead
= drwav_read_pcm_frames_s32(pWav
, (size_t)pWav
->totalPCMFrameCount
, pSampleData
);
7184 if (framesRead
!= pWav
->totalPCMFrameCount
) {
7185 drwav__free_from_callbacks(pSampleData
, &pWav
->allocationCallbacks
);
7187 return NULL
; /* There was an error reading the samples. */
7193 *sampleRate
= pWav
->sampleRate
;
7196 *channels
= pWav
->channels
;
7198 if (totalFrameCount
) {
7199 *totalFrameCount
= pWav
->totalPCMFrameCount
;
7207 DRWAV_API drwav_int16
* drwav_open_and_read_pcm_frames_s16(drwav_read_proc onRead
, drwav_seek_proc onSeek
, void* pUserData
, unsigned int* channelsOut
, unsigned int* sampleRateOut
, drwav_uint64
* totalFrameCountOut
, const drwav_allocation_callbacks
* pAllocationCallbacks
)
7214 if (sampleRateOut
) {
7217 if (totalFrameCountOut
) {
7218 *totalFrameCountOut
= 0;
7221 if (!drwav_init(&wav
, onRead
, onSeek
, pUserData
, pAllocationCallbacks
)) {
7225 return drwav__read_pcm_frames_and_close_s16(&wav
, channelsOut
, sampleRateOut
, totalFrameCountOut
);
7228 DRWAV_API
float* drwav_open_and_read_pcm_frames_f32(drwav_read_proc onRead
, drwav_seek_proc onSeek
, void* pUserData
, unsigned int* channelsOut
, unsigned int* sampleRateOut
, drwav_uint64
* totalFrameCountOut
, const drwav_allocation_callbacks
* pAllocationCallbacks
)
7235 if (sampleRateOut
) {
7238 if (totalFrameCountOut
) {
7239 *totalFrameCountOut
= 0;
7242 if (!drwav_init(&wav
, onRead
, onSeek
, pUserData
, pAllocationCallbacks
)) {
7246 return drwav__read_pcm_frames_and_close_f32(&wav
, channelsOut
, sampleRateOut
, totalFrameCountOut
);
7249 DRWAV_API drwav_int32
* drwav_open_and_read_pcm_frames_s32(drwav_read_proc onRead
, drwav_seek_proc onSeek
, void* pUserData
, unsigned int* channelsOut
, unsigned int* sampleRateOut
, drwav_uint64
* totalFrameCountOut
, const drwav_allocation_callbacks
* pAllocationCallbacks
)
7256 if (sampleRateOut
) {
7259 if (totalFrameCountOut
) {
7260 *totalFrameCountOut
= 0;
7263 if (!drwav_init(&wav
, onRead
, onSeek
, pUserData
, pAllocationCallbacks
)) {
7267 return drwav__read_pcm_frames_and_close_s32(&wav
, channelsOut
, sampleRateOut
, totalFrameCountOut
);
7270 #ifndef DR_WAV_NO_STDIO
7271 DRWAV_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
)
7278 if (sampleRateOut
) {
7281 if (totalFrameCountOut
) {
7282 *totalFrameCountOut
= 0;
7285 if (!drwav_init_file(&wav
, filename
, pAllocationCallbacks
)) {
7289 return drwav__read_pcm_frames_and_close_s16(&wav
, channelsOut
, sampleRateOut
, totalFrameCountOut
);
7292 DRWAV_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
)
7299 if (sampleRateOut
) {
7302 if (totalFrameCountOut
) {
7303 *totalFrameCountOut
= 0;
7306 if (!drwav_init_file(&wav
, filename
, pAllocationCallbacks
)) {
7310 return drwav__read_pcm_frames_and_close_f32(&wav
, channelsOut
, sampleRateOut
, totalFrameCountOut
);
7313 DRWAV_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
)
7320 if (sampleRateOut
) {
7323 if (totalFrameCountOut
) {
7324 *totalFrameCountOut
= 0;
7327 if (!drwav_init_file(&wav
, filename
, pAllocationCallbacks
)) {
7331 return drwav__read_pcm_frames_and_close_s32(&wav
, channelsOut
, sampleRateOut
, totalFrameCountOut
);
7335 DRWAV_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
)
7339 if (sampleRateOut
) {
7345 if (totalFrameCountOut
) {
7346 *totalFrameCountOut
= 0;
7349 if (!drwav_init_file_w(&wav
, filename
, pAllocationCallbacks
)) {
7353 return drwav__read_pcm_frames_and_close_s16(&wav
, channelsOut
, sampleRateOut
, totalFrameCountOut
);
7356 DRWAV_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
)
7360 if (sampleRateOut
) {
7366 if (totalFrameCountOut
) {
7367 *totalFrameCountOut
= 0;
7370 if (!drwav_init_file_w(&wav
, filename
, pAllocationCallbacks
)) {
7374 return drwav__read_pcm_frames_and_close_f32(&wav
, channelsOut
, sampleRateOut
, totalFrameCountOut
);
7377 DRWAV_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
)
7381 if (sampleRateOut
) {
7387 if (totalFrameCountOut
) {
7388 *totalFrameCountOut
= 0;
7391 if (!drwav_init_file_w(&wav
, filename
, pAllocationCallbacks
)) {
7395 return drwav__read_pcm_frames_and_close_s32(&wav
, channelsOut
, sampleRateOut
, totalFrameCountOut
);
7399 DRWAV_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
)
7406 if (sampleRateOut
) {
7409 if (totalFrameCountOut
) {
7410 *totalFrameCountOut
= 0;
7413 if (!drwav_init_memory(&wav
, data
, dataSize
, pAllocationCallbacks
)) {
7417 return drwav__read_pcm_frames_and_close_s16(&wav
, channelsOut
, sampleRateOut
, totalFrameCountOut
);
7420 DRWAV_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
)
7427 if (sampleRateOut
) {
7430 if (totalFrameCountOut
) {
7431 *totalFrameCountOut
= 0;
7434 if (!drwav_init_memory(&wav
, data
, dataSize
, pAllocationCallbacks
)) {
7438 return drwav__read_pcm_frames_and_close_f32(&wav
, channelsOut
, sampleRateOut
, totalFrameCountOut
);
7441 DRWAV_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
)
7448 if (sampleRateOut
) {
7451 if (totalFrameCountOut
) {
7452 *totalFrameCountOut
= 0;
7455 if (!drwav_init_memory(&wav
, data
, dataSize
, pAllocationCallbacks
)) {
7459 return drwav__read_pcm_frames_and_close_s32(&wav
, channelsOut
, sampleRateOut
, totalFrameCountOut
);
7461 #endif /* DR_WAV_NO_CONVERSION_API */
7464 DRWAV_API
void drwav_free(void* p
, const drwav_allocation_callbacks
* pAllocationCallbacks
)
7466 if (pAllocationCallbacks
!= NULL
) {
7467 drwav__free_from_callbacks(p
, pAllocationCallbacks
);
7469 drwav__free_default(p
, NULL
);
7473 DRWAV_API drwav_uint16
drwav_bytes_to_u16(const drwav_uint8
* data
)
7475 return ((drwav_uint16
)data
[0] << 0) | ((drwav_uint16
)data
[1] << 8);
7478 DRWAV_API drwav_int16
drwav_bytes_to_s16(const drwav_uint8
* data
)
7480 return (drwav_int16
)drwav_bytes_to_u16(data
);
7483 DRWAV_API drwav_uint32
drwav_bytes_to_u32(const drwav_uint8
* data
)
7485 return ((drwav_uint32
)data
[0] << 0) | ((drwav_uint32
)data
[1] << 8) | ((drwav_uint32
)data
[2] << 16) | ((drwav_uint32
)data
[3] << 24);
7488 DRWAV_API
float drwav_bytes_to_f32(const drwav_uint8
* data
)
7495 value
.u32
= drwav_bytes_to_u32(data
);
7499 DRWAV_API drwav_int32
drwav_bytes_to_s32(const drwav_uint8
* data
)
7501 return (drwav_int32
)drwav_bytes_to_u32(data
);
7504 DRWAV_API drwav_uint64
drwav_bytes_to_u64(const drwav_uint8
* data
)
7507 ((drwav_uint64
)data
[0] << 0) | ((drwav_uint64
)data
[1] << 8) | ((drwav_uint64
)data
[2] << 16) | ((drwav_uint64
)data
[3] << 24) |
7508 ((drwav_uint64
)data
[4] << 32) | ((drwav_uint64
)data
[5] << 40) | ((drwav_uint64
)data
[6] << 48) | ((drwav_uint64
)data
[7] << 56);
7511 DRWAV_API drwav_int64
drwav_bytes_to_s64(const drwav_uint8
* data
)
7513 return (drwav_int64
)drwav_bytes_to_u64(data
);
7517 DRWAV_API drwav_bool32
drwav_guid_equal(const drwav_uint8 a
[16], const drwav_uint8 b
[16])
7520 for (i
= 0; i
< 16; i
+= 1) {
7529 DRWAV_API drwav_bool32
drwav_fourcc_equal(const drwav_uint8
* a
, const char* b
)
7538 #endif /* dr_wav_c */
7539 #endif /* DR_WAV_IMPLEMENTATION */
7544 v0.13.2 - 2021-10-02
7545 - Fix a possible buffer overflow when reading from compressed formats.
7547 v0.13.1 - 2021-07-31
7548 - Fix platform detection for ARM64.
7550 v0.13.0 - 2021-07-01
7551 - Improve support for reading and writing metadata. Use the `_with_metadata()` APIs to initialize
7552 a WAV decoder and store the metadata within the `drwav` object. Use the `pMetadata` and
7553 `metadataCount` members of the `drwav` object to read the data. The old way of handling metadata
7554 via a callback is still usable and valid.
7555 - API CHANGE: drwav_target_write_size_bytes() now takes extra parameters for calculating the
7556 required write size when writing metadata.
7557 - Add drwav_get_cursor_in_pcm_frames()
7558 - Add drwav_get_length_in_pcm_frames()
7559 - Fix a bug where drwav_read_raw() can call the read callback with a byte count of zero.
7561 v0.12.20 - 2021-06-11
7562 - Fix some undefined behavior.
7564 v0.12.19 - 2021-02-21
7565 - Fix a warning due to referencing _MSC_VER when it is undefined.
7566 - Minor improvements to the management of some internal state concerning the data chunk cursor.
7568 v0.12.18 - 2021-01-31
7569 - Clean up some static analysis warnings.
7571 v0.12.17 - 2021-01-17
7572 - Minor fix to sample code in documentation.
7573 - Correctly qualify a private API as private rather than public.
7576 v0.12.16 - 2020-12-02
7577 - Fix a bug when trying to read more bytes than can fit in a size_t.
7579 v0.12.15 - 2020-11-21
7580 - Fix compilation with OpenWatcom.
7582 v0.12.14 - 2020-11-13
7583 - Minor code clean up.
7585 v0.12.13 - 2020-11-01
7586 - Improve compiler support for older versions of GCC.
7588 v0.12.12 - 2020-09-28
7589 - Add support for RF64.
7590 - Fix a bug in writing mode where the size of the RIFF chunk incorrectly includes the header section.
7592 v0.12.11 - 2020-09-08
7593 - Fix a compilation error on older compilers.
7595 v0.12.10 - 2020-08-24
7596 - Fix a bug when seeking with ADPCM formats.
7598 v0.12.9 - 2020-08-02
7599 - Simplify sized types.
7601 v0.12.8 - 2020-07-25
7602 - Fix a compilation warning.
7604 v0.12.7 - 2020-07-15
7605 - Fix some bugs on big-endian architectures.
7606 - Fix an error in s24 to f32 conversion.
7608 v0.12.6 - 2020-06-23
7609 - Change drwav_read_*() to allow NULL to be passed in as the output buffer which is equivalent to a forward seek.
7610 - Fix a buffer overflow when trying to decode invalid IMA-ADPCM files.
7611 - Add include guard for the implementation section.
7613 v0.12.5 - 2020-05-27
7614 - Minor documentation fix.
7616 v0.12.4 - 2020-05-16
7617 - Replace assert() with DRWAV_ASSERT().
7618 - Add compile-time and run-time version querying.
7619 - DRWAV_VERSION_MINOR
7620 - DRWAV_VERSION_MAJOR
7621 - DRWAV_VERSION_REVISION
7622 - DRWAV_VERSION_STRING
7624 - drwav_version_string()
7626 v0.12.3 - 2020-04-30
7627 - Fix compilation errors with VC6.
7629 v0.12.2 - 2020-04-21
7630 - Fix a bug where drwav_init_file() does not close the file handle after attempting to load an erroneous file.
7632 v0.12.1 - 2020-04-13
7633 - Fix some pedantic warnings.
7635 v0.12.0 - 2020-04-04
7636 - API CHANGE: Add container and format parameters to the chunk callback.
7637 - Minor documentation updates.
7639 v0.11.5 - 2020-03-07
7640 - Fix compilation error with Visual Studio .NET 2003.
7642 v0.11.4 - 2020-01-29
7643 - Fix some static analysis warnings.
7644 - Fix a bug when reading f32 samples from an A-law encoded stream.
7646 v0.11.3 - 2020-01-12
7647 - Minor changes to some f32 format conversion routines.
7648 - Minor bug fix for ADPCM conversion when end of file is reached.
7650 v0.11.2 - 2019-12-02
7651 - Fix a possible crash when using custom memory allocators without a custom realloc() implementation.
7652 - Fix an integer overflow bug.
7653 - Fix a null pointer dereference bug.
7654 - Add limits to sample rate, channels and bits per sample to tighten up some validation.
7656 v0.11.1 - 2019-10-07
7657 - Internal code clean up.
7659 v0.11.0 - 2019-10-06
7660 - API CHANGE: Add support for user defined memory allocation routines. This system allows the program to specify their own memory allocation
7661 routines with a user data pointer for client-specific contextual data. This adds an extra parameter to the end of the following APIs:
7665 - drwav_init_file_ex()
7666 - drwav_init_file_w()
7667 - drwav_init_file_w_ex()
7668 - drwav_init_memory()
7669 - drwav_init_memory_ex()
7670 - drwav_init_write()
7671 - drwav_init_write_sequential()
7672 - drwav_init_write_sequential_pcm_frames()
7673 - drwav_init_file_write()
7674 - drwav_init_file_write_sequential()
7675 - drwav_init_file_write_sequential_pcm_frames()
7676 - drwav_init_file_write_w()
7677 - drwav_init_file_write_sequential_w()
7678 - drwav_init_file_write_sequential_pcm_frames_w()
7679 - drwav_init_memory_write()
7680 - drwav_init_memory_write_sequential()
7681 - drwav_init_memory_write_sequential_pcm_frames()
7682 - drwav_open_and_read_pcm_frames_s16()
7683 - drwav_open_and_read_pcm_frames_f32()
7684 - drwav_open_and_read_pcm_frames_s32()
7685 - drwav_open_file_and_read_pcm_frames_s16()
7686 - drwav_open_file_and_read_pcm_frames_f32()
7687 - drwav_open_file_and_read_pcm_frames_s32()
7688 - drwav_open_file_and_read_pcm_frames_s16_w()
7689 - drwav_open_file_and_read_pcm_frames_f32_w()
7690 - drwav_open_file_and_read_pcm_frames_s32_w()
7691 - drwav_open_memory_and_read_pcm_frames_s16()
7692 - drwav_open_memory_and_read_pcm_frames_f32()
7693 - drwav_open_memory_and_read_pcm_frames_s32()
7694 Set this extra parameter to NULL to use defaults which is the same as the previous behaviour. Setting this NULL will use
7695 DRWAV_MALLOC, DRWAV_REALLOC and DRWAV_FREE.
7696 - Add support for reading and writing PCM frames in an explicit endianness. New APIs:
7697 - drwav_read_pcm_frames_le()
7698 - drwav_read_pcm_frames_be()
7699 - drwav_read_pcm_frames_s16le()
7700 - drwav_read_pcm_frames_s16be()
7701 - drwav_read_pcm_frames_f32le()
7702 - drwav_read_pcm_frames_f32be()
7703 - drwav_read_pcm_frames_s32le()
7704 - drwav_read_pcm_frames_s32be()
7705 - drwav_write_pcm_frames_le()
7706 - drwav_write_pcm_frames_be()
7707 - Remove deprecated APIs.
7708 - API CHANGE: The following APIs now return native-endian data. Previously they returned little-endian data.
7709 - drwav_read_pcm_frames()
7710 - drwav_read_pcm_frames_s16()
7711 - drwav_read_pcm_frames_s32()
7712 - drwav_read_pcm_frames_f32()
7713 - drwav_open_and_read_pcm_frames_s16()
7714 - drwav_open_and_read_pcm_frames_s32()
7715 - drwav_open_and_read_pcm_frames_f32()
7716 - drwav_open_file_and_read_pcm_frames_s16()
7717 - drwav_open_file_and_read_pcm_frames_s32()
7718 - drwav_open_file_and_read_pcm_frames_f32()
7719 - drwav_open_file_and_read_pcm_frames_s16_w()
7720 - drwav_open_file_and_read_pcm_frames_s32_w()
7721 - drwav_open_file_and_read_pcm_frames_f32_w()
7722 - drwav_open_memory_and_read_pcm_frames_s16()
7723 - drwav_open_memory_and_read_pcm_frames_s32()
7724 - drwav_open_memory_and_read_pcm_frames_f32()
7726 v0.10.1 - 2019-08-31
7727 - Correctly handle partial trailing ADPCM blocks.
7729 v0.10.0 - 2019-08-04
7730 - Remove deprecated APIs.
7731 - Add wchar_t variants for file loading APIs:
7733 drwav_init_file_ex_w()
7734 drwav_init_file_write_w()
7735 drwav_init_file_write_sequential_w()
7736 - Add drwav_target_write_size_bytes() which calculates the total size in bytes of a WAV file given a format and sample count.
7737 - Add APIs for specifying the PCM frame count instead of the sample count when opening in sequential write mode:
7738 drwav_init_write_sequential_pcm_frames()
7739 drwav_init_file_write_sequential_pcm_frames()
7740 drwav_init_file_write_sequential_pcm_frames_w()
7741 drwav_init_memory_write_sequential_pcm_frames()
7742 - Deprecate drwav_open*() and drwav_close():
7746 drwav_open_write_sequential()
7748 drwav_open_file_ex()
7749 drwav_open_file_write()
7750 drwav_open_file_write_sequential()
7752 drwav_open_memory_ex()
7753 drwav_open_memory_write()
7754 drwav_open_memory_write_sequential()
7756 - Minor documentation updates.
7762 - Add support for C89.
7763 - Change license to choice of public domain or MIT-0.
7766 - API CHANGE: Add new reading APIs for reading by PCM frames instead of samples. Old APIs have been deprecated and
7767 will be removed in v0.10.0. Deprecated APIs and their replacements:
7768 drwav_read() -> drwav_read_pcm_frames()
7769 drwav_read_s16() -> drwav_read_pcm_frames_s16()
7770 drwav_read_f32() -> drwav_read_pcm_frames_f32()
7771 drwav_read_s32() -> drwav_read_pcm_frames_s32()
7772 drwav_seek_to_sample() -> drwav_seek_to_pcm_frame()
7773 drwav_write() -> drwav_write_pcm_frames()
7774 drwav_open_and_read_s16() -> drwav_open_and_read_pcm_frames_s16()
7775 drwav_open_and_read_f32() -> drwav_open_and_read_pcm_frames_f32()
7776 drwav_open_and_read_s32() -> drwav_open_and_read_pcm_frames_s32()
7777 drwav_open_file_and_read_s16() -> drwav_open_file_and_read_pcm_frames_s16()
7778 drwav_open_file_and_read_f32() -> drwav_open_file_and_read_pcm_frames_f32()
7779 drwav_open_file_and_read_s32() -> drwav_open_file_and_read_pcm_frames_s32()
7780 drwav_open_memory_and_read_s16() -> drwav_open_memory_and_read_pcm_frames_s16()
7781 drwav_open_memory_and_read_f32() -> drwav_open_memory_and_read_pcm_frames_f32()
7782 drwav_open_memory_and_read_s32() -> drwav_open_memory_and_read_pcm_frames_s32()
7783 drwav::totalSampleCount -> drwav::totalPCMFrameCount
7784 - API CHANGE: Rename drwav_open_and_read_file_*() to drwav_open_file_and_read_*().
7785 - API CHANGE: Rename drwav_open_and_read_memory_*() to drwav_open_memory_and_read_*().
7786 - Add built-in support for smpl chunks.
7787 - Add support for firing a callback for each chunk in the file at initialization time.
7788 - This is enabled through the drwav_init_ex(), etc. family of APIs.
7789 - Handle invalid FMT chunks more robustly.
7792 - Const correctness.
7793 - Fix a potential stack overflow.
7796 - Improve 64-bit detection.
7799 - Fix C++ build on older versions of GCC.
7802 - Fix some big-endian bugs.
7805 - Add support for sequential writing APIs.
7806 - Disable seeking in write mode.
7807 - Fix bugs with Wave64.
7812 - Start using major.minor.revision versioning.
7815 - Restrict ADPCM formats to a maximum of 2 channels.
7824 - Set drwav.bytesPerSample to 0 for all compressed formats.
7825 - Fix a crash when reading 16-bit floating point WAV files. In this case dr_wav will output silence for
7826 all format conversion reading APIs (*_s16, *_s32, *_f32 APIs).
7827 - Fix some divide-by-zero errors.
7830 - Fix errors with seeking of compressed formats.
7831 - Fix compilation error when DR_WAV_NO_CONVERSION_API
7834 - Fix some GCC warnings.
7840 - API CHANGE: Rename dr_* types to drwav_*.
7841 - Add support for custom implementations of malloc(), realloc(), etc.
7842 - Add support for Microsoft ADPCM.
7843 - Add support for IMA ADPCM (DVI, format code 0x11).
7844 - Optimizations to drwav_read_s16().
7848 - Change underlying type for booleans to unsigned.
7851 - Fix a minor bug with drwav_open_and_read_s16() and family.
7854 - Added support for reading samples as signed 16-bit integers. Use the _s16() family of APIs for this.
7855 - Minor fixes to documentation.
7858 - Use drwav_int* and drwav_uint* sized types to improve compiler support.
7861 - Properly handle JUNK chunks that come before the FMT chunk.
7864 - A minor change to drwav_bool8 and drwav_bool32 types.
7867 - Fixed a bug with drwav_open_and_read() and family due to incorrect argument ordering.
7868 - Improve A-law and mu-law efficiency.
7871 - API CHANGE. Swap the order of "channels" and "sampleRate" parameters in drwav_open_and_read*(). Rationale for this is to
7872 keep it consistent with dr_audio and dr_flac.
7875 - Fixed a typo in documentation.
7879 - Change date format to ISO 8601 (YYYY-MM-DD)
7882 - API CHANGE. Make onSeek consistent with dr_flac.
7883 - API CHANGE. Rename drwav_seek() to drwav_seek_to_sample() for clarity and consistency with dr_flac.
7884 - Added support for Sony Wave64.
7887 - API CHANGE. Return drwav_bool32 instead of int in onSeek callback.
7888 - Fixed a memory leak.
7891 - Lots of API changes for consistency.
7894 - Fixed Linux/GCC build.
7897 - Added support for reading data as signed 32-bit PCM for consistency with dr_flac.
7900 - Fixed a bug in drwav_open_file() where the file handle would not be closed if the loader failed to initialize.
7903 - Initial versioned release.
7907 This software is available as a choice of the following licenses. Choose
7908 whichever you prefer.
7910 ===============================================================================
7911 ALTERNATIVE 1 - Public Domain (www.unlicense.org)
7912 ===============================================================================
7913 This is free and unencumbered software released into the public domain.
7915 Anyone is free to copy, modify, publish, use, compile, sell, or distribute this
7916 software, either in source code form or as a compiled binary, for any purpose,
7917 commercial or non-commercial, and by any means.
7919 In jurisdictions that recognize copyright laws, the author or authors of this
7920 software dedicate any and all copyright interest in the software to the public
7921 domain. We make this dedication for the benefit of the public at large and to
7922 the detriment of our heirs and successors. We intend this dedication to be an
7923 overt act of relinquishment in perpetuity of all present and future rights to
7924 this software under copyright law.
7926 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
7927 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
7928 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
7929 AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
7930 ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
7931 WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
7933 For more information, please refer to <http://unlicense.org/>
7935 ===============================================================================
7936 ALTERNATIVE 2 - MIT No Attribution
7937 ===============================================================================
7938 Copyright 2020 David Reid
7940 Permission is hereby granted, free of charge, to any person obtaining a copy of
7941 this software and associated documentation files (the "Software"), to deal in
7942 the Software without restriction, including without limitation the rights to
7943 use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
7944 of the Software, and to permit persons to whom the Software is furnished to do
7947 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
7948 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
7949 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
7950 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
7951 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
7952 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE