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