1 /*
2 * wavlib.c - simple WAV I/O library interface
3 * Copyright (C) 2006-2010 Francesco Romani <fromani at gmail dot com>
4 *
5 * This software is provided 'as-is', without any express or implied
6 * warranty. In no event will the authors be held liable for any damages
7 * arising from the use of this software.
8 *
9 * Permission is granted to anyone to use this software for any purpose,
10 * including commercial applications, and to alter it and redistribute it
11 * freely, subject to the following restrictions:
12 *
13 * 1. The origin of this software must not be misrepresented; you must not
14 * claim that you wrote the original software. If you use this software
15 * in a product, an acknowledgment in the product documentation would be
16 * appreciated but is not required.
17 * 2. Altered source versions must be plainly marked as such, and must not be
18 * misrepresented as being the original software.
19 * 3. This notice may not be removed or altered from any source distribution.
20 */
21
22 #include "wavlib.h"
23 #include "platform.h"
24
25 #include <sys/stat.h>
26 #include <fcntl.h>
27 #include <unistd.h>
28
29 #include <string.h>
30 #include <stdlib.h>
31 #include <errno.h>
32
33 /*************************************************************************
34 * utilties *
35 *************************************************************************/
36
37 #define WAV_BUF_SIZE (1024)
38
39 #if (!defined HAVE_BYTESWAP && defined WAV_BIG_ENDIAN)
40
bswap_16(uint16_t x)41 static uint16_t bswap_16(uint16_t x)
42 {
43 return (((x & 0xff00) >> 8) | ((x & 0x00ff) << 8));
44 }
45
bswap_32(uint32_t x)46 static uint32_t bswap_32(uint32_t x)
47 {
48 return (((x & 0xff000000UL) >> 24) |
49 ((x & 0x00ff0000UL) >> 8) |
50 ((x & 0x0000ff00UL) << 8) |
51 ((x & 0x000000ffUL) << 24));
52 }
53
bswap_64(uint64_t x)54 static uint64_t bswap_64(uint64_t x)
55 {
56 return (((x & 0xff00000000000000ULL) >> 56) |
57 ((x & 0x00ff000000000000ULL) >> 40) |
58 ((x & 0x0000ff0000000000ULL) >> 24) |
59 ((x & 0x000000ff00000000ULL) >> 8) |
60 ((x & 0x00000000ff000000ULL) << 8) |
61 ((x & 0x0000000000ff0000ULL) << 24) |
62 ((x & 0x000000000000ff00ULL) << 40) |
63 ((x & 0x00000000000000ffULL) << 56));
64 }
65 #endif
66
67 #if (!defined WAV_BIG_ENDIAN && !defined WAV_LITTLE_ENDIAN)
68 #error "you must define either LITTLE_ENDIAN or BIG_ENDIAN"
69 #endif
70
71 #if (defined WAV_BIG_ENDIAN && defined WAV_LITTLE_ENDIAN)
72 #error "you CAN'T define BOTH LITTLE_ENDIAN and BIG_ENDIAN"
73 #endif
74
75 #if defined WAV_BIG_ENDIAN
76 #define htol_16(x) bswap_16(x)
77 #define htol_32(x) bswap_32(x)
78 #define htol_64(x) bswap_64(x)
79
80 #elif defined WAV_LITTLE_ENDIAN
81
82 #define htol_16(x) (x)
83 #define htol_32(x) (x)
84 #define htol_64(x) (x)
85
86 #endif
87
88 /* often used out-of-order */
89 #define make_wav_get_bits(s) \
90 static inline uint##s##_t wav_get_bits##s(uint8_t *d) \
91 { \
92 return htol_##s(*((uint##s##_t*)d)); \
93 }
94
95 /* often used sequentially */
96 #define make_wav_put_bits(s) \
97 static inline uint8_t *wav_put_bits##s(uint8_t *d, uint##s##_t u) \
98 { \
99 *((uint##s##_t*)d) = htol_##s(u); \
100 return (d + (s / 8)); \
101 }
102
103 make_wav_get_bits(16)
104 make_wav_get_bits(32)
105 make_wav_get_bits(64)
106
107 make_wav_put_bits(16)
108 make_wav_put_bits(32)
109 make_wav_put_bits(64)
110
make_tag(uint8_t a,uint8_t b,uint8_t c,uint8_t d)111 static inline uint32_t make_tag(uint8_t a, uint8_t b, uint8_t c, uint8_t d)
112 {
113 return (a | (b << 8) | (c << 16) | (d << 24));
114 }
115
116 /*************************************************************************
117 * header data *
118 *************************************************************************/
119
120 /*
121 * WAVE header:
122 *
123 * TAG: 'RIFF' 4 bytes
124 * LENGTH: 4 bytes
125 * TAG: 'WAVE' 4 bytes
126 *
127 * TAG: 'fmt ' 4 bytes
128 * LENGTH: 4 bytes
129 *
130 * +
131 * FORMAT: 2 bytes |
132 * CHANNELS: 2 bytes |
133 * SAMPLES: 4 bytes | simple WAV format:
134 * AVGBYTES: 4 bytes | 16 byte
135 * BLKALIGN: 2 bytes |
136 * BITS: 2 bytes |
137 * +
138 *
139 * TAG: 'data' 4 bytes
140 * LENGTH: 4 bytes
141 *
142 * ----------------------------
143 * TOTAL wav header: 44 bytes
144 */
145
146 #define WAV_HEADER_LEN (44)
147 #define WAV_FORMAT_LEN (16)
148
149 #define PCM_ID (0x1)
150
151 /*************************************************************************
152 * core data/routines *
153 *************************************************************************/
154
155 #define WAV_SET_ERROR(errp, code) \
156 if (errp != NULL) { \
157 *errp = code; \
158 }
159
160 struct wav_ {
161 int fd;
162
163 int header_done;
164 int close_fd;
165 int has_pipe;
166
167 WAVMode mode;
168 WAVError error;
169
170 uint32_t len;
171
172 uint32_t bitrate;
173 uint16_t bits;
174 uint16_t channels;
175 uint32_t rate;
176
177 uint16_t block_align;
178 };
179
wav_strerror(WAVError err)180 const char *wav_strerror(WAVError err)
181 {
182 const char *s = NULL;
183
184 switch (err) {
185 case WAV_SUCCESS:
186 s = "no error";
187 break;
188 case WAV_NO_MEM:
189 s = "can't acquire the needed amount of memory";
190 break;
191 case WAV_IO_ERROR:
192 s = "error while performing I/O operation";
193 break;
194 case WAV_BAD_FORMAT:
195 s = "incorrect/unrecognized WAV data";
196 break;
197 case WAV_BAD_PARAM:
198 s = "bad/unknown parameter for this operation";
199 break;
200 case WAV_UNSUPPORTED:
201 s = "not yet supported by wavlib";
202 break;
203 default:
204 s = NULL;
205 break;
206 }
207 return s;
208 }
209
wav_parse_header(WAV handle,WAVError * err)210 static int wav_parse_header(WAV handle, WAVError *err)
211 {
212 uint8_t hdr[WAV_HEADER_LEN];
213 ssize_t r = 0;
214 uint16_t wav_fmt = 0;
215 uint32_t fmt_len = 0;
216
217 if (!handle || handle->fd == -1 || !(handle->mode & WAV_READ)) {
218 return -1;
219 }
220
221 r = plat_read(handle->fd, hdr, WAV_HEADER_LEN);
222 if (r != WAV_HEADER_LEN) {
223 WAV_SET_ERROR(err, WAV_BAD_FORMAT);
224 goto bad_wav;
225 }
226 if ((wav_get_bits32(hdr) != make_tag('R', 'I', 'F', 'F'))
227 || (wav_get_bits32(hdr + 8) != make_tag('W', 'A', 'V', 'E'))
228 || (wav_get_bits32(hdr + 12) != make_tag('f', 'm', 't', ' '))) {
229 WAV_SET_ERROR(err, WAV_BAD_FORMAT);
230 goto bad_wav;
231 }
232
233 fmt_len = wav_get_bits32(hdr + 16);
234 wav_fmt = wav_get_bits16(hdr + 20);
235 if (fmt_len != WAV_FORMAT_LEN || wav_fmt != PCM_ID) {
236 WAV_SET_ERROR(err, WAV_UNSUPPORTED);
237 goto bad_wav;
238 }
239
240 handle->len = wav_get_bits32(hdr + 4);
241 handle->channels = wav_get_bits16(hdr + 22);
242 handle->rate = wav_get_bits32(hdr + 24);
243 handle->bitrate = (wav_get_bits32(hdr + 28) * 8) / 1000;
244 handle->block_align = wav_get_bits16(hdr + 32);
245 handle->bits = wav_get_bits16(hdr + 34);
246 /* skip 'data' tag (4 bytes) */
247 handle->len = wav_get_bits32(hdr + 40);
248
249 return 0;
250
251 bad_wav:
252 lseek(handle->fd, 0, SEEK_SET);
253 return 1;
254 }
255
wav_write_header(WAV handle,int force)256 int wav_write_header(WAV handle, int force)
257 {
258 uint8_t hdr[WAV_HEADER_LEN];
259 uint8_t *ph = hdr;
260 off_t pos = 0, ret = 0;
261 ssize_t w = 0;
262
263 if (!handle) {
264 return -1;
265 }
266 if (!force && handle->header_done) {
267 return 0;
268 }
269 if (handle->bits != 0
270 && (handle->bits != 8 && handle->bits != 16)) {
271 /* bits == 0 -> not specified (so it's good) */
272 WAV_SET_ERROR(&(handle->error), WAV_UNSUPPORTED);
273 return -1;
274 }
275
276 if (!handle->has_pipe) {
277 pos = lseek(handle->fd, 0, SEEK_CUR);
278 ret = lseek(handle->fd, 0, SEEK_SET);
279 if (ret == (off_t)-1) {
280 return 1;
281 }
282 }
283
284 ph = wav_put_bits32(ph, make_tag('R', 'I', 'F', 'F'));
285 ph = wav_put_bits32(ph, handle->len + WAV_HEADER_LEN - 8);
286 ph = wav_put_bits32(ph, make_tag('W', 'A', 'V', 'E'));
287
288 ph = wav_put_bits32(ph, make_tag('f', 'm', 't', ' '));
289 ph = wav_put_bits32(ph, WAV_FORMAT_LEN);
290
291 /* format */
292 ph = wav_put_bits16(ph, PCM_ID);
293 /* wave format, only plain PCM supported, yet */
294 ph = wav_put_bits16(ph, handle->channels);
295 /* number of channels */
296 ph = wav_put_bits32(ph, handle->rate);
297 /* sample rate */
298 ph = wav_put_bits32(ph, (handle->bitrate * 1000)/8);
299 /* average bytes per second (aka bitrate) */
300 ph = wav_put_bits16(ph, ((handle->channels * handle->bits) / 8));
301 /* block alignment */
302 ph = wav_put_bits16(ph, handle->bits);
303 /* bits for sample */
304
305 ph = wav_put_bits32(ph, make_tag('d', 'a', 't', 'a'));
306 ph = wav_put_bits32(ph, handle->len);
307
308 w = plat_write(handle->fd, hdr, WAV_HEADER_LEN);
309
310 if (!handle->has_pipe) {
311 ret = lseek(handle->fd, pos, SEEK_CUR);
312 if (ret == (off_t)-1) {
313 return 1;
314 }
315 }
316
317 if (w != WAV_HEADER_LEN) {
318 return 2;
319 }
320 handle->header_done = 1;
321 return 0;
322 }
323
wav_open(const char * filename,WAVMode mode,WAVError * err)324 WAV wav_open(const char *filename, WAVMode mode, WAVError *err)
325 {
326 int oflags = (mode & WAV_READ) ?O_RDONLY :O_TRUNC|O_CREAT|O_WRONLY;
327 int fd = -1;
328 WAV wav = NULL;
329
330 if (!filename || !strlen(filename)) {
331 WAV_SET_ERROR(err, WAV_BAD_PARAM);
332 } else {
333 fd = plat_open(filename, oflags,
334 S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH);
335 wav = wav_fdopen(fd, mode, err);
336 if (!wav) {
337 plat_close(fd);
338 } else {
339 wav->close_fd = 1;
340 }
341 }
342 return wav;
343 }
344
345 #define DEL_WAV(wav) do { \
346 plat_free((wav)); \
347 (wav) = NULL; \
348 } while (0)
349
wav_fdopen(int fd,WAVMode mode,WAVError * err)350 WAV wav_fdopen(int fd, WAVMode mode, WAVError *err)
351 {
352 WAV wav = plat_zalloc(sizeof(struct wav_));
353
354 if (!wav) {
355 WAV_SET_ERROR(err, WAV_NO_MEM);
356 } else {
357 wav->fd = fd;
358 wav->mode = mode;
359 wav->close_fd = 0;
360 wav->has_pipe = (mode & WAV_PIPE) ?1 :0;
361
362 if (mode & WAV_READ) {
363 if (0 != wav_parse_header(wav, err)) {
364 DEL_WAV(wav);
365 } else {
366 wav->header_done = 1; /* skip write_header */
367 }
368 } else if (mode & WAV_WRITE) {
369 /* reserve space for header by writing a fake one */
370 if (!wav->has_pipe && 0 != wav_write_header(wav, 1)) {
371 WAV_SET_ERROR(err, wav->error);
372 /* only I/O error */
373 DEL_WAV(wav);
374 }
375 } else {
376 WAV_SET_ERROR(err, WAV_BAD_PARAM);
377 DEL_WAV(wav);
378 }
379 }
380 return wav;
381 }
382
383
384 #define RETURN_IF_IOERROR(err) \
385 if (err != 0) { \
386 WAV_SET_ERROR(&(handle->error), WAV_IO_ERROR); \
387 return -1; \
388 }
389
wav_close(WAV handle)390 int wav_close(WAV handle)
391 {
392 int ret = 0;
393
394 if (!handle) {
395 return -1;
396 }
397
398 if (!handle->has_pipe && handle->mode & WAV_WRITE) {
399 ret = wav_write_header(handle, 1);
400 RETURN_IF_IOERROR(ret);
401 }
402
403 if (handle->close_fd) {
404 ret = plat_close(handle->fd);
405 RETURN_IF_IOERROR(ret);
406 }
407 plat_free(handle);
408
409 return 0;
410 }
411
412 #undef RETURN_IF_IOERROR
413
wav_chunk_size(WAV handle,double fps)414 uint32_t wav_chunk_size(WAV handle, double fps)
415 {
416 uint32_t size = 0;
417 double fch;
418
419 if (!handle || !fps) {
420 return -1;
421 }
422
423 fch = handle->rate / fps;
424
425 /* bytes per audio frame */
426 size = (int)(fch * (handle->bits / 8) * handle->channels);
427 size = (size>>2)<<2; /* XXX */
428
429 return 0;
430 }
431
wav_last_error(WAV handle)432 WAVError wav_last_error(WAV handle)
433 {
434 return (handle) ?(handle->error) :WAV_BAD_PARAM;
435 }
436
wav_get_bitrate(WAV handle)437 uint32_t wav_get_bitrate(WAV handle)
438 {
439 return (handle) ?(handle->bitrate) :0;
440 }
441
wav_get_rate(WAV handle)442 uint16_t wav_get_rate(WAV handle)
443 {
444 return (handle) ?(handle->rate) :0;
445 }
446
wav_get_channels(WAV handle)447 uint8_t wav_get_channels(WAV handle)
448 {
449 return (handle) ?(handle->channels) :0;
450 }
451
wav_get_bits(WAV handle)452 uint8_t wav_get_bits(WAV handle)
453 {
454 return (handle) ?(handle->bits) :0;
455 }
456
wav_set_rate(WAV handle,uint16_t rate)457 void wav_set_rate(WAV handle, uint16_t rate)
458 {
459 if (handle && handle->mode & WAV_WRITE) {
460 handle->rate = rate;
461 }
462 }
463
wav_set_channels(WAV handle,uint8_t channels)464 void wav_set_channels(WAV handle, uint8_t channels)
465 {
466 if (handle && handle->mode & WAV_WRITE) {
467 handle->channels = channels;
468 }
469 }
470
wav_set_bits(WAV handle,uint8_t bits)471 void wav_set_bits(WAV handle, uint8_t bits)
472 {
473 if (handle && handle->mode & WAV_WRITE) {
474 handle->bits = bits;
475 }
476 }
477
wav_set_bitrate(WAV handle,uint32_t bitrate)478 void wav_set_bitrate(WAV handle, uint32_t bitrate)
479 {
480 if (handle && handle->mode & WAV_WRITE) {
481 handle->bitrate = bitrate;
482 }
483 }
484
485 #ifdef WAV_BIG_ENDIAN
486
487 /* assume dlen % 2 == 0 */
bswap_buffer(void * data,size_t bytes)488 static void bswap_buffer(void *data, size_t bytes)
489 {
490 size_t i = 0;
491 uint16_t *ptr = data;
492
493 for (ptr = data, i = 0; i < bytes; ptr++, i += 2) {
494 *ptr = bswap_16(*ptr);
495 }
496 }
497
498 #define SWAP_WRITE_CHUNK(data, len) do { \
499 memcpy(conv_buf, (data), (len)); \
500 bswap_buffer(conv_buf, (len)); \
501 ret = plat_write(fd, conv_buf, (len)); \
502 } while (0)
503
wav_bswap_fdwrite(int fd,const uint8_t * buf,size_t len)504 static ssize_t wav_bswap_fdwrite(int fd, const uint8_t *buf, size_t len)
505 {
506 uint8_t conv_buf[WAV_BUF_SIZE];
507 size_t blocks = len / WAV_BUF_SIZE, rest = len % WAV_BUF_SIZE, i = 0;
508 ssize_t ret = 0, tot = 0;
509
510 for (i = 0; i < blocks; i++) {
511 SWAP_WRITE_CHUNK(buf + (i * WAV_BUF_SIZE), WAV_BUF_SIZE);
512 if (ret != WAV_BUF_SIZE) {
513 break;
514 }
515 tot += ret;
516 }
517
518 SWAP_WRITE_CHUNK(buf + (i * WAV_BUF_SIZE), rest);
519 return tot + ret;
520 }
521
522 #undef SWAP_WRITE_CHUNK
523
524 #endif /* WAV_BIG_ENDIAN */
525
wav_read_data(WAV handle,uint8_t * buffer,size_t bufsize)526 ssize_t wav_read_data(WAV handle, uint8_t *buffer, size_t bufsize)
527 {
528 ssize_t r = 0;
529
530 if (!handle) {
531 return -1;
532 }
533 if (!buffer || bufsize < 0) {
534 WAV_SET_ERROR(&(handle->error), WAV_BAD_PARAM);
535 return -1;
536 }
537 if (!(handle->mode & WAV_READ) || (bufsize % 2 != 0)) {
538 WAV_SET_ERROR(&(handle->error), WAV_UNSUPPORTED);
539 return -1;
540 }
541 r = plat_read(handle->fd, buffer, bufsize);
542
543 #ifdef WAV_BIG_ENDIAN
544 bswap_buffer(buffer, r);
545 #endif
546 return r;
547 }
548
wav_write_data(WAV handle,const uint8_t * buffer,size_t bufsize)549 ssize_t wav_write_data(WAV handle, const uint8_t *buffer, size_t bufsize)
550 {
551 ssize_t w = 0;
552
553 if (!handle) {
554 return -1;
555 }
556 if (!buffer || bufsize < 0) {
557 WAV_SET_ERROR(&(handle->error), WAV_BAD_PARAM);
558 return -1;
559 }
560 if (!(handle->mode & WAV_WRITE) || (bufsize % 2 != 0)) {
561 WAV_SET_ERROR(&(handle->error), WAV_UNSUPPORTED);
562 return -1;
563 }
564 if (wav_write_header(handle, 0) != 0) {
565 return -1;
566 }
567 #ifdef WAV_BIG_ENDIAN
568 w = wav_bswap_fdwrite(handle->fd, buffer, bufsize);
569 #else
570 w = plat_write(handle->fd, buffer, bufsize);
571 #endif
572 if (w == bufsize) {
573 handle->len += w;
574 }
575 return w;
576 }
577
578