1 #include <bz/audio/wav.h>
3 #include <bz/memory/allocator.h>
4 #include <bz/resources/resource.h>
5 #include <bz/types/identifier_internal.h>
7 #include <assert.h> // Static assertions
9 // https://www.aelius.com/njh/wavemetatools/doc/riffmci.pdf
11 #define PACKED_STRUCT __attribute__((__packed__))
13 typedef uint16_t RIFF_WORD
;
14 typedef uint32_t RIFF_DWORD
;
15 typedef uint8_t RIFF_BYTE
;
17 typedef RIFF_DWORD RIFF_FOURCC
; // Four-character code
19 typedef RIFF_FOURCC RIFF_CKID
; // Four-character-code chunk identifier
20 typedef RIFF_DWORD RIFF_CKSIZE
; // 32-bit unsigned size value
22 struct PACKED_STRUCT RIFF_CK
{
27 typedef struct RIFF_CK RIFF_CK
;
28 static_assert(sizeof(RIFF_CK
) == 8, "CK is wrong size");
30 #define CHUNK_ID(a,b,c,d) (((a) << 0) | ((b) << 8) | ((c) << 16) | ((d) << 24))
32 struct PACKED_STRUCT RIFF_WAVEFormatCK
{
33 RIFF_WORD wFormatTag
; // Format category
34 RIFF_WORD wChannels
; // Number of channels
35 RIFF_DWORD dwSamplesPerSec
; // Sampling rate
36 RIFF_DWORD dwAvgBytesPerSec
; // For buffer estimation
37 RIFF_WORD wBlockAlign
; // Data block size
39 typedef struct RIFF_WAVEFormatCK RIFF_WAVEFormatCK
;
41 struct PACKED_STRUCT RIFF_PCMFormatCK
{
42 RIFF_WORD wBitsPerSample
; // Sample size
44 typedef struct RIFF_PCMFormatCK RIFF_PCMFormatCK
;
47 RIFF_WAVE_FORMAT_PCM
= 0x0001, // Microsoft Pulse Code Modulation (PCM) format
48 RIFF_IBM_FORMAT_MULAW
= 0x0101, // IBM mu-law format
49 RIFF_IBM_FORMAT_ALAW
= 0x0102, // IBM a-law format
50 RIFF_IBM_FORMAT_ADPCM
= 0x0103 // IBM AVC Adaptive Differential Pulse Code Modulation format
53 void *bzAudioLoadWAV(size_t *sizeOut
, BZMemoryArenaID arena
, uint32_t *samplingRateOut
, uint16_t *channelsOut
, uint16_t *bitsPerSampleOut
, const char *identifierFmt
, ...) {
54 bzMakeIdentifier(identifier
, identifierFmt
);
56 BZResourceID handle
= bzResourcesOpenResource("sounds", "assets/sounds/%s.wav", identifier
);
62 size_t readLength
= bzResourcesReadBytes(handle
, &chunk
, sizeof(chunk
));
63 if (readLength
!= sizeof(chunk
)) {
67 size_t chunkDataStartPosition
= bzResourcesTell(handle
);
70 case CHUNK_ID('R','I','F','F'): {
72 bzResourcesReadBytes(handle
, &fileType
, sizeof(fileType
));
73 bzAssertMessage(fileType
== CHUNK_ID('W','A','V','E'), "Not a WAV file");
77 case CHUNK_ID('f','m','t',' '): {
78 RIFF_WAVEFormatCK format
;
79 bzResourcesReadBytes(handle
, &format
, sizeof(format
));
80 bzAssertMessage(format
.wFormatTag
== RIFF_WAVE_FORMAT_PCM
, "Not a PCM file");
81 RIFF_PCMFormatCK pcmFormat
;
82 bzResourcesReadBytes(handle
, &pcmFormat
, sizeof(pcmFormat
));
84 *samplingRateOut
= format
.dwSamplesPerSec
;
85 *channelsOut
= format
.wChannels
;
86 *bitsPerSampleOut
= pcmFormat
.wBitsPerSample
;
89 case CHUNK_ID('d','a','t','a'): {
90 wavData
= bzMemoryAlloc(arena
, chunk
.ckSize
);
91 *sizeOut
= chunk
.ckSize
;
92 bzResourcesReadBytes(handle
, wavData
, chunk
.ckSize
);
97 bzResourcesSeek(handle
, chunkDataStartPosition
+ chunk
.ckSize
);
102 bzResourcesCloseResource(handle
);
104 bzLog("Loaded WAV '%s', %d, %d, %d", identifier
, *samplingRateOut
, *channelsOut
, *bitsPerSampleOut
);