2 * Copyright (C) 2013 nu774
3 * For conditions of distribution and use, see copyright notice in COPYING
17 #include "wav_reader.h"
18 #include "m4af_endian.h"
20 #define RIFF_FOURCC(a,b,c,d) ((a)|((b)<<8)|((c)<<16)|((d)<<24))
22 #define TRY_IO(expr) \
28 #define ASSERT_FORMAT(ctx, expr) \
31 if (!ctx->last_error) \
32 ctx->last_error = WAV_INVALID_FORMAT; \
38 pcm_sample_description_t sample_format
;
48 static const uint8_t WAV_GUID_PCM
[] = {
49 1, 0, 0, 0, 0, 0, 0x10, 0, 0x80, 0, 0, 0xaa, 0, 0x38, 0x9b, 0x71
51 static const uint8_t WAV_GUID_FLOAT
[] = {
52 3, 0, 0, 0, 0, 0, 0x10, 0, 0x80, 0, 0, 0xaa, 0, 0x38, 0x9b, 0x71
55 const pcm_sample_description_t
*wav_get_format(wav_reader_t
*reader
)
57 return &reader
->sample_format
;
60 int64_t wav_get_length(wav_reader_t
*reader
)
62 return reader
->length
;
65 int64_t wav_get_position(wav_reader_t
*reader
)
67 return reader
->position
;
70 void wav_teardown(wav_reader_t
**reader
)
77 int riff_read(wav_reader_t
*reader
, void *buffer
, uint32_t size
)
82 if (reader
->last_error
)
85 rc
= reader
->io
.read(reader
->io_cookie
, buffer
, size
- count
);
89 reader
->last_error
= WAV_IO_ERROR
;
90 } while (rc
> 0 && count
< size
);
91 return count
> 0 ? count
: rc
;
95 int riff_skip(wav_reader_t
*reader
, int64_t count
)
100 if (reader
->last_error
)
104 if (reader
->io
.seek
&&
105 reader
->io
.seek(reader
->io_cookie
, count
, SEEK_CUR
) >= 0)
109 if ((rc
= riff_read(reader
, buff
, count
> 8192 ? 8192 : count
)) > 0)
111 } while (rc
> 0 && count
> 0);
114 reader
->last_error
= WAV_IO_ERROR
;
115 return reader
->last_error
? -1 : 0;
119 int riff_seek(wav_reader_t
*reader
, int64_t off
, int whence
)
122 if (reader
->last_error
)
124 if (!reader
->io
.seek
)
126 if ((rc
= reader
->io
.seek(reader
->io_cookie
, off
, whence
)) < 0)
130 reader
->last_error
= WAV_IO_ERROR
;
135 int64_t riff_tell(wav_reader_t
*reader
)
139 if (reader
->last_error
|| !reader
->io
.tell
)
141 off
= reader
->io
.tell(reader
->io_cookie
);
143 reader
->last_error
= WAV_IO_ERROR
;
150 int riff_read16(wav_reader_t
*reader
, uint16_t *value
)
152 TRY_IO(riff_read(reader
, value
, 2) != 2);
153 *value
= m4af_ltoh16(*value
);
160 int riff_read32(wav_reader_t
*reader
, uint32_t *value
)
162 TRY_IO(riff_read(reader
, value
, 4) != 4);
163 *value
= m4af_ltoh32(*value
);
170 int riff_read64(wav_reader_t
*reader
, uint64_t *value
)
172 TRY_IO(riff_read(reader
, value
, 8) != 8);
173 *value
= m4af_ltoh64(*value
);
180 int riff_scan(wav_reader_t
*reader
, const char *fmt
, ...)
186 while ((c
= *fmt
++)) {
189 TRY_IO(riff_read16(reader
, va_arg(ap
, uint16_t*)));
193 TRY_IO(riff_read32(reader
, va_arg(ap
, uint32_t*)));
197 TRY_IO(riff_read64(reader
, va_arg(ap
, uint64_t*)));
208 uint32_t riff_next_chunk(wav_reader_t
*reader
, uint32_t *chunk_size
)
211 if (riff_scan(reader
, "LL", &fcc
, chunk_size
) == 2)
216 int wav_read_frames(wav_reader_t
*reader
, void *buffer
, unsigned nframes
)
221 if (!reader
->ignore_length
&& nframes
> reader
->length
- reader
->position
)
222 nframes
= reader
->length
- reader
->position
;
223 nbytes
= nframes
* reader
->sample_format
.bytes_per_frame
;
225 if ((rc
= riff_read(reader
, buffer
, nbytes
)) < 0)
227 nframes
= rc
/ reader
->sample_format
.bytes_per_frame
;
228 reader
->position
+= nframes
;
234 int riff_ds64(wav_reader_t
*reader
, int64_t *length
)
236 uint32_t fcc
, chunk_size
, table_size
;
237 uint64_t riff_size
, sample_count
;
239 fcc
= riff_next_chunk(reader
, &chunk_size
);
240 ASSERT_FORMAT(reader
,
241 fcc
== RIFF_FOURCC('d','s','6','4') && chunk_size
>= 28);
242 TRY_IO(riff_scan(reader
, "QQQL",
243 &riff_size
, length
, &sample_count
, &table_size
) != 4);
244 TRY_IO(riff_skip(reader
, (chunk_size
- 27) & ~1));
245 reader
->data_offset
+= (chunk_size
+ 9) & ~1;
251 int wav_fmt(wav_reader_t
*reader
, uint32_t size
)
253 uint16_t wFormatTag
, nChannels
, nBlockAlign
, wBitsPerSample
, cbSize
;
254 uint32_t nSamplesPerSec
, nAvgBytesPerSec
, dwChannelMask
= 0;
255 uint16_t wValidBitsPerSample
;
259 ASSERT_FORMAT(reader
, size
>= 16);
260 TRY_IO(riff_scan(reader
, "SSLLSS", &wFormatTag
, &nChannels
,
261 &nSamplesPerSec
, &nAvgBytesPerSec
, &nBlockAlign
,
262 &wBitsPerSample
) != 6);
263 wValidBitsPerSample
= wBitsPerSample
;
265 if (wFormatTag
!= 1 && wFormatTag
!= 3 && wFormatTag
!= 0xfffe) {
266 reader
->last_error
= WAV_UNSUPPORTED_FORMAT
;
269 ASSERT_FORMAT(reader
,
270 nChannels
&& nSamplesPerSec
&& nAvgBytesPerSec
&&
271 nBlockAlign
&& wBitsPerSample
&& !(wBitsPerSample
& 7) &&
272 nBlockAlign
== nChannels
* wBitsPerSample
/ 8);
276 if (wFormatTag
!= 0xfffe)
277 TRY_IO(riff_skip(reader
, (size
- 15) & ~1));
279 ASSERT_FORMAT(reader
, size
>= 40);
280 TRY_IO(riff_scan(reader
, "SSL",
281 &cbSize
, &wValidBitsPerSample
, &dwChannelMask
) != 3);
282 TRY_IO(riff_read(reader
, guid
, 16) != 16);
284 if (memcmp(guid
, WAV_GUID_FLOAT
, 16) == 0)
286 else if (memcmp(guid
, WAV_GUID_PCM
, 16) != 0) {
287 reader
->last_error
= WAV_UNSUPPORTED_FORMAT
;
290 ASSERT_FORMAT(reader
,
291 wValidBitsPerSample
&&
292 wValidBitsPerSample
<= wBitsPerSample
);
293 TRY_IO(riff_skip(reader
, (size
- 39) & ~1));
295 reader
->sample_format
.sample_rate
= nSamplesPerSec
;
296 reader
->sample_format
.bits_per_channel
= wValidBitsPerSample
;
297 reader
->sample_format
.bytes_per_frame
= nBlockAlign
;
298 reader
->sample_format
.channels_per_frame
= nChannels
;
299 reader
->sample_format
.channel_mask
= dwChannelMask
;
301 reader
->sample_format
.sample_type
= PCM_TYPE_FLOAT
;
302 else if (wBitsPerSample
== 8)
303 reader
->sample_format
.sample_type
= PCM_TYPE_UINT
;
305 reader
->sample_format
.sample_type
= PCM_TYPE_SINT
;
312 int wav_parse(wav_reader_t
*reader
, int64_t *data_length
)
314 uint32_t container
, fcc
, chunk_size
;
317 container
= riff_next_chunk(reader
, &chunk_size
);
318 if (container
!= RIFF_FOURCC('R','I','F','F') &&
319 container
!= RIFF_FOURCC('R','F','6','4'))
321 TRY_IO(riff_read32(reader
, &fcc
));
322 if (fcc
!= RIFF_FOURCC('W','A','V','E'))
324 reader
->data_offset
= 12;
326 if (container
== RIFF_FOURCC('R','F','6','4'))
327 riff_ds64(reader
, data_length
);
328 while ((fcc
= riff_next_chunk(reader
, &chunk_size
)) != 0) {
329 if (fcc
== RIFF_FOURCC('f','m','t',' ')) {
330 if (wav_fmt(reader
, chunk_size
) < 0)
332 } else if (fcc
== RIFF_FOURCC('d','a','t','a')) {
333 if (container
== RIFF_FOURCC('R','I','F','F'))
334 *data_length
= chunk_size
;
335 reader
->data_offset
+= 8;
338 TRY_IO(riff_skip(reader
, (chunk_size
+ 1) & ~1));
340 reader
->data_offset
+= (chunk_size
+ 9) & ~1;
342 if (fcc
== RIFF_FOURCC('d','a','t','a'))
348 wav_reader_t
*wav_open(wav_io_context_t
*io_ctx
, void *io_cookie
,
351 wav_reader_t
*reader
= 0;
355 if ((reader
= calloc(1, sizeof(wav_reader_t
))) == 0)
357 memcpy(&reader
->io
, io_ctx
, sizeof(wav_io_context_t
));
358 reader
->io_cookie
= io_cookie
;
359 reader
->ignore_length
= ignore_length
;
360 if (wav_parse(reader
, &data_length
) < 0) {
364 bpf
= reader
->sample_format
.bytes_per_frame
;
365 if (ignore_length
|| !data_length
|| data_length
% bpf
)
366 reader
->length
= INT64_MAX
;
368 reader
->length
= data_length
/ bpf
;
370 if (reader
->length
== INT64_MAX
&& reader
->io
.seek
&& reader
->io
.tell
) {
371 if (reader
->io
.seek(reader
->io_cookie
, 0, SEEK_END
) >= 0) {
372 int64_t size
= reader
->io
.tell(reader
->io_cookie
);
374 reader
->length
= (size
- reader
->data_offset
) / bpf
;
375 reader
->io
.seek(reader
->io_cookie
, reader
->data_offset
, SEEK_SET
);
381 wav_reader_t
*raw_open(wav_io_context_t
*io_ctx
, void *io_cookie
,
382 const pcm_sample_description_t
*desc
)
384 wav_reader_t
*reader
= 0;
386 if ((reader
= calloc(1, sizeof(wav_reader_t
))) == 0)
388 memcpy(&reader
->io
, io_ctx
, sizeof(wav_io_context_t
));
389 memcpy(&reader
->sample_format
, desc
, sizeof(pcm_sample_description_t
));
390 reader
->io_cookie
= io_cookie
;
391 if (io_ctx
->seek
&& io_ctx
->tell
) {
392 if (reader
->io
.seek(reader
->io_cookie
, 0, SEEK_END
) >= 0) {
393 int64_t size
= reader
->io
.tell(reader
->io_cookie
);
395 reader
->length
= size
/ desc
->bytes_per_frame
;
396 reader
->io
.seek(reader
->io_cookie
, reader
->data_offset
, SEEK_SET
);
399 reader
->length
= INT64_MAX
;