2 * Copyright (C) 2013 nu774
3 * For conditions of distribution and use, see copyright notice in COPYING
17 #include "wav_reader.h"
19 #define RIFF_FOURCC(a,b,c,d) ((a)|((b)<<8)|((c)<<16)|((d)<<24))
21 typedef struct wav_reader_t
{
22 pcm_reader_vtbl_t
*vtbl
;
23 pcm_sample_description_t sample_format
;
31 static const uint8_t WAV_GUID_PCM
[] = {
32 1, 0, 0, 0, 0, 0, 0x10, 0, 0x80, 0, 0, 0xaa, 0, 0x38, 0x9b, 0x71
34 static const uint8_t WAV_GUID_FLOAT
[] = {
35 3, 0, 0, 0, 0, 0, 0x10, 0, 0x80, 0, 0, 0xaa, 0, 0x38, 0x9b, 0x71
38 static const pcm_sample_description_t
*wav_get_format(pcm_reader_t
*reader
)
40 return &((wav_reader_t
*)reader
)->sample_format
;
43 static int64_t wav_get_length(pcm_reader_t
*reader
)
45 return ((wav_reader_t
*)reader
)->length
;
48 static int64_t wav_get_position(pcm_reader_t
*reader
)
50 return ((wav_reader_t
*)reader
)->position
;
53 static void wav_teardown(pcm_reader_t
**reader
)
60 uint32_t riff_next_chunk(wav_reader_t
*reader
, uint32_t *chunk_size
)
63 return (pcm_scanl(&reader
->io
, "LL", &fcc
, chunk_size
) == 2) ? fcc
: 0;
67 int wav_read_frames(pcm_reader_t
*preader
, void *buffer
, unsigned nframes
)
71 wav_reader_t
*reader
= (wav_reader_t
*)preader
;
73 if (!reader
->ignore_length
&& nframes
> reader
->length
- reader
->position
)
74 nframes
= reader
->length
- reader
->position
;
75 nbytes
= nframes
* reader
->sample_format
.bytes_per_frame
;
77 if ((rc
= pcm_read(&reader
->io
, buffer
, nbytes
)) < 0)
79 nframes
= rc
/ reader
->sample_format
.bytes_per_frame
;
80 reader
->position
+= nframes
;
86 int riff_ds64(wav_reader_t
*reader
, int64_t *length
)
88 uint32_t fcc
, chunk_size
, table_size
;
89 uint64_t riff_size
, sample_count
;
91 fcc
= riff_next_chunk(reader
, &chunk_size
);
92 ENSURE(fcc
== RIFF_FOURCC('d','s','6','4') && chunk_size
>= 28);
93 TRY_IO(pcm_scanl(&reader
->io
, "QQQL",
94 &riff_size
, length
, &sample_count
, &table_size
) != 4);
95 TRY_IO(pcm_skip(&reader
->io
, (chunk_size
- 27) & ~1));
96 reader
->data_offset
+= (chunk_size
+ 9) & ~1;
102 int wav_fmt(wav_reader_t
*reader
, uint32_t size
)
104 uint16_t wFormatTag
, nChannels
, nBlockAlign
, wBitsPerSample
, cbSize
;
105 uint32_t nSamplesPerSec
, nAvgBytesPerSec
, dwChannelMask
= 0;
106 uint16_t wValidBitsPerSample
;
111 TRY_IO(pcm_scanl(&reader
->io
, "SSLLSS", &wFormatTag
, &nChannels
,
112 &nSamplesPerSec
, &nAvgBytesPerSec
, &nBlockAlign
,
113 &wBitsPerSample
) != 6);
114 wValidBitsPerSample
= wBitsPerSample
;
116 ENSURE(wFormatTag
== 1 || wFormatTag
== 3 || wFormatTag
== 0xfffe);
117 ENSURE(nChannels
&& nSamplesPerSec
&& nAvgBytesPerSec
&&
118 nBlockAlign
&& wBitsPerSample
&& !(wBitsPerSample
& 7) &&
119 nBlockAlign
== nChannels
* wBitsPerSample
/ 8);
124 if (wFormatTag
!= 0xfffe)
125 TRY_IO(pcm_skip(&reader
->io
, (size
- 15) & ~1));
128 TRY_IO(pcm_scanl(&reader
->io
, "SSL",
129 &cbSize
, &wValidBitsPerSample
, &dwChannelMask
) != 3);
130 TRY_IO(pcm_read(&reader
->io
, guid
, 16) != 16);
132 if (memcmp(guid
, WAV_GUID_FLOAT
, 16) == 0)
134 else if (memcmp(guid
, WAV_GUID_PCM
, 16) != 0)
136 ENSURE(wValidBitsPerSample
&& wValidBitsPerSample
<= wBitsPerSample
);
137 TRY_IO(pcm_skip(&reader
->io
, (size
- 39) & ~1));
139 reader
->sample_format
.sample_rate
= nSamplesPerSec
;
140 reader
->sample_format
.bits_per_channel
= wValidBitsPerSample
;
141 reader
->sample_format
.bytes_per_frame
= nBlockAlign
;
142 reader
->sample_format
.channels_per_frame
= nChannels
;
143 reader
->sample_format
.channel_mask
= dwChannelMask
;
145 reader
->sample_format
.sample_type
= PCM_TYPE_FLOAT
;
146 else if (wBitsPerSample
== 8)
147 reader
->sample_format
.sample_type
= PCM_TYPE_UINT
;
149 reader
->sample_format
.sample_type
= PCM_TYPE_SINT
;
156 int wav_parse(wav_reader_t
*reader
, int64_t *data_length
)
158 uint32_t container
, fcc
, chunk_size
;
161 container
= riff_next_chunk(reader
, &chunk_size
);
162 ENSURE(container
== RIFF_FOURCC('R','I','F','F') ||
163 container
== RIFF_FOURCC('R','F','6','4'));
164 TRY_IO(pcm_read32le(&reader
->io
, &fcc
));
165 ENSURE(fcc
== RIFF_FOURCC('W','A','V','E'));
166 reader
->data_offset
= 12;
168 if (container
== RIFF_FOURCC('R','F','6','4'))
169 riff_ds64(reader
, data_length
);
170 while ((fcc
= riff_next_chunk(reader
, &chunk_size
)) != 0) {
171 if (fcc
== RIFF_FOURCC('f','m','t',' ')) {
172 if (wav_fmt(reader
, chunk_size
) < 0)
174 } else if (fcc
== RIFF_FOURCC('d','a','t','a')) {
175 if (container
== RIFF_FOURCC('R','I','F','F'))
176 *data_length
= chunk_size
;
177 reader
->data_offset
+= 8;
180 TRY_IO(pcm_skip(&reader
->io
, (chunk_size
+ 1) & ~1));
182 reader
->data_offset
+= (chunk_size
+ 9) & ~1;
184 if (fcc
== RIFF_FOURCC('d','a','t','a'))
190 static pcm_reader_vtbl_t wav_vtable
= {
198 pcm_reader_t
*wav_open(pcm_io_context_t
*io
, int ignore_length
)
200 wav_reader_t
*reader
= 0;
204 if ((reader
= calloc(1, sizeof(wav_reader_t
))) == 0)
206 memcpy(&reader
->io
, io
, sizeof(pcm_io_context_t
));
207 reader
->ignore_length
= ignore_length
;
208 if (wav_parse(reader
, &data_length
) < 0) {
212 bpf
= reader
->sample_format
.bytes_per_frame
;
213 if (ignore_length
|| !data_length
|| data_length
% bpf
)
214 reader
->length
= INT64_MAX
;
216 reader
->length
= data_length
/ bpf
;
218 if (reader
->length
== INT64_MAX
) {
219 if (pcm_seek(&reader
->io
, 0, SEEK_END
) >= 0) {
220 int64_t size
= pcm_tell(&reader
->io
);
222 reader
->length
= (size
- reader
->data_offset
) / bpf
;
223 pcm_seek(&reader
->io
, reader
->data_offset
, SEEK_SET
);
226 reader
->vtbl
= &wav_vtable
;
227 return (pcm_reader_t
*)reader
;
230 pcm_reader_t
*raw_open(pcm_io_context_t
*io
,
231 const pcm_sample_description_t
*desc
)
233 wav_reader_t
*reader
= 0;
235 if ((reader
= calloc(1, sizeof(wav_reader_t
))) == 0)
237 memcpy(&reader
->io
, io
, sizeof(pcm_io_context_t
));
238 memcpy(&reader
->sample_format
, desc
, sizeof(pcm_sample_description_t
));
239 if (pcm_seek(&reader
->io
, 0, SEEK_END
) >= 0) {
240 int64_t size
= pcm_tell(&reader
->io
);
242 reader
->length
= size
/ reader
->sample_format
.bytes_per_frame
;
243 pcm_seek(&reader
->io
, 0, SEEK_SET
);
245 reader
->length
= INT64_MAX
;
246 reader
->vtbl
= &wav_vtable
;
247 return (pcm_reader_t
*)reader
;