tinysndfile.c revision 1fa816b623b8d912d78742b4a447693c8eb383ed
1/*
2 * Copyright (C) 2012 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#include <audio_utils/sndfile.h>
18#include <audio_utils/primitives.h>
19#include <stdio.h>
20#include <string.h>
21#include <errno.h>
22
23#define WAVE_FORMAT_PCM         1
24#define WAVE_FORMAT_IEEE_FLOAT  3
25#define WAVE_FORMAT_EXTENSIBLE  0xFFFE
26
27struct SNDFILE_ {
28    int mode;
29    uint8_t *temp;  // realloc buffer used for shrinking 16 bits to 8 bits and byte-swapping
30    FILE *stream;
31    size_t bytesPerFrame;
32    size_t remaining;   // frames unread for SFM_READ, frames written for SFM_WRITE
33    SF_INFO info;
34};
35
36static unsigned little2u(unsigned char *ptr)
37{
38    return (ptr[1] << 8) + ptr[0];
39}
40
41static unsigned little4u(unsigned char *ptr)
42{
43    return (ptr[3] << 24) + (ptr[2] << 16) + (ptr[1] << 8) + ptr[0];
44}
45
46static int isLittleEndian(void)
47{
48    static const short one = 1;
49    return *((const char *) &one) == 1;
50}
51
52// "swab" conflicts with OS X <string.h>
53static void my_swab(short *ptr, size_t numToSwap)
54{
55    while (numToSwap > 0) {
56        *ptr = little2u((unsigned char *) ptr);
57        --numToSwap;
58        ++ptr;
59    }
60}
61
62static SNDFILE *sf_open_read(const char *path, SF_INFO *info)
63{
64    FILE *stream = fopen(path, "rb");
65    if (stream == NULL) {
66        fprintf(stderr, "fopen %s failed errno %d\n", path, errno);
67        return NULL;
68    }
69
70    SNDFILE *handle = (SNDFILE *) malloc(sizeof(SNDFILE));
71    handle->mode = SFM_READ;
72    handle->temp = NULL;
73    handle->stream = stream;
74    handle->info.format = SF_FORMAT_WAV;
75
76    // don't attempt to parse all valid forms, just the most common ones
77    unsigned char wav[12];
78    size_t actual;
79    actual = fread(wav, sizeof(char), sizeof(wav), stream);
80    if (actual < 12) {
81        fprintf(stderr, "actual %zu < 44\n", actual);
82        goto close;
83    }
84    if (memcmp(wav, "RIFF", 4)) {
85        fprintf(stderr, "wav != RIFF\n");
86        goto close;
87    }
88    unsigned riffSize = little4u(&wav[4]);
89    if (riffSize < 4) {
90        fprintf(stderr, "riffSize %u < 4\n", riffSize);
91        goto close;
92    }
93    if (memcmp(&wav[8], "WAVE", 4)) {
94        fprintf(stderr, "missing WAVE\n");
95        goto close;
96    }
97    size_t remaining = riffSize - 4;
98    int hadFmt = 0;
99    int hadData = 0;
100    while (remaining >= 8) {
101        unsigned char chunk[8];
102        actual = fread(chunk, sizeof(char), sizeof(chunk), stream);
103        if (actual != sizeof(chunk)) {
104            fprintf(stderr, "actual %zu != %zu\n", actual, sizeof(chunk));
105            goto close;
106        }
107        remaining -= 8;
108        unsigned chunkSize = little4u(&chunk[4]);
109        if (chunkSize > remaining) {
110            fprintf(stderr, "chunkSize %u > remaining %zu\n", chunkSize, remaining);
111            goto close;
112        }
113        if (!memcmp(&chunk[0], "fmt ", 4)) {
114            if (hadFmt) {
115                fprintf(stderr, "multiple fmt\n");
116                goto close;
117            }
118            if (chunkSize < 2) {
119                fprintf(stderr, "chunkSize %u < 2\n", chunkSize);
120                goto close;
121            }
122            unsigned char fmt[40];
123            actual = fread(fmt, sizeof(char), 2, stream);
124            if (actual != 2) {
125                fprintf(stderr, "actual %zu != 2\n", actual);
126                goto close;
127            }
128            unsigned format = little2u(&fmt[0]);
129            size_t minSize = 0;
130            switch (format) {
131            case WAVE_FORMAT_PCM:
132            case WAVE_FORMAT_IEEE_FLOAT:
133                minSize = 16;
134                break;
135            case WAVE_FORMAT_EXTENSIBLE:
136                minSize = 40;
137                break;
138            default:
139                fprintf(stderr, "unsupported format %u\n", format);
140                goto close;
141            }
142            if (chunkSize < minSize) {
143                fprintf(stderr, "chunkSize %u < minSize %zu\n", chunkSize, minSize);
144                goto close;
145            }
146            actual = fread(&fmt[2], sizeof(char), minSize - 2, stream);
147            if (actual != minSize - 2) {
148                fprintf(stderr, "actual %zu != %zu\n", actual, minSize - 16);
149                goto close;
150            }
151            if (chunkSize > minSize) {
152                fseek(stream, (long) (chunkSize - minSize), SEEK_CUR);
153            }
154            unsigned channels = little2u(&fmt[2]);
155            if (channels != 1 && channels != 2 && channels != 4 && channels != 6 && channels != 8) {
156                fprintf(stderr, "unsupported channels %u\n", channels);
157                goto close;
158            }
159            unsigned samplerate = little4u(&fmt[4]);
160            if (samplerate == 0) {
161                fprintf(stderr, "samplerate %u == 0\n", samplerate);
162                goto close;
163            }
164            // ignore byte rate
165            // ignore block alignment
166            unsigned bitsPerSample = little2u(&fmt[14]);
167            if (bitsPerSample != 8 && bitsPerSample != 16 && bitsPerSample != 32) {
168                fprintf(stderr, "bitsPerSample %u != 8 or 16 or 32\n", bitsPerSample);
169                goto close;
170            }
171            unsigned bytesPerFrame = (bitsPerSample >> 3) * channels;
172            handle->bytesPerFrame = bytesPerFrame;
173            handle->info.samplerate = samplerate;
174            handle->info.channels = channels;
175            switch (bitsPerSample) {
176            case 8:
177                handle->info.format |= SF_FORMAT_PCM_U8;
178                break;
179            case 16:
180                handle->info.format |= SF_FORMAT_PCM_16;
181                break;
182            case 32:
183                if (format == WAVE_FORMAT_IEEE_FLOAT)
184                    handle->info.format |= SF_FORMAT_FLOAT;
185                else
186                    handle->info.format |= SF_FORMAT_PCM_32;
187                break;
188            }
189            hadFmt = 1;
190        } else if (!memcmp(&chunk[0], "data", 4)) {
191            if (!hadFmt) {
192                fprintf(stderr, "data not preceded by fmt\n");
193                goto close;
194            }
195            if (hadData) {
196                fprintf(stderr, "multiple data\n");
197                goto close;
198            }
199            handle->remaining = chunkSize / handle->bytesPerFrame;
200            handle->info.frames = handle->remaining;
201            hadData = 1;
202        } else if (!memcmp(&chunk[0], "fact", 4)) {
203            // ignore fact
204            if (chunkSize > 0) {
205                fseek(stream, (long) chunkSize, SEEK_CUR);
206            }
207        } else {
208            // ignore unknown chunk
209            fprintf(stderr, "ignoring unknown chunk %c%c%c%c\n",
210                    chunk[0], chunk[1], chunk[2], chunk[3]);
211            if (chunkSize > 0) {
212                fseek(stream, (long) chunkSize, SEEK_CUR);
213            }
214        }
215        remaining -= chunkSize;
216    }
217    if (remaining > 0) {
218        fprintf(stderr, "partial chunk at end of RIFF, remaining %zu\n", remaining);
219        goto close;
220    }
221    if (!hadData) {
222        fprintf(stderr, "missing data\n");
223        goto close;
224    }
225    *info = handle->info;
226    return handle;
227
228close:
229    free(handle);
230    fclose(stream);
231    return NULL;
232}
233
234static void write4u(unsigned char *ptr, unsigned u)
235{
236    ptr[0] = u;
237    ptr[1] = u >> 8;
238    ptr[2] = u >> 16;
239    ptr[3] = u >> 24;
240}
241
242static SNDFILE *sf_open_write(const char *path, SF_INFO *info)
243{
244    int sub = info->format & SF_FORMAT_SUBMASK;
245    if (!(
246            (info->samplerate > 0) &&
247            (info->channels > 0 && info->channels <= 8) &&
248            ((info->format & SF_FORMAT_TYPEMASK) == SF_FORMAT_WAV) &&
249            (sub == SF_FORMAT_PCM_16 || sub == SF_FORMAT_PCM_U8 || sub == SF_FORMAT_FLOAT)
250          )) {
251        return NULL;
252    }
253    FILE *stream = fopen(path, "w+b");
254    if (stream == NULL) {
255        fprintf(stderr, "fopen %s failed errno %d\n", path, errno);
256        return NULL;
257    }
258    unsigned char wav[58];
259    memset(wav, 0, sizeof(wav));
260    memcpy(wav, "RIFF", 4);
261    memcpy(&wav[8], "WAVEfmt ", 8);
262    if (sub == SF_FORMAT_FLOAT) {
263        wav[4] = 50;    // riffSize
264        wav[16] = 18;   // fmtSize
265        wav[20] = WAVE_FORMAT_IEEE_FLOAT;
266    } else {
267        wav[4] = 36;    // riffSize
268        wav[16] = 16;   // fmtSize
269        wav[20] = WAVE_FORMAT_PCM;
270    }
271    wav[22] = info->channels;
272    write4u(&wav[24], info->samplerate);
273    unsigned bitsPerSample;
274    switch (sub) {
275    case SF_FORMAT_PCM_16:
276        bitsPerSample = 16;
277        break;
278    case SF_FORMAT_PCM_U8:
279        bitsPerSample = 8;
280        break;
281    case SF_FORMAT_FLOAT:
282        bitsPerSample = 32;
283        break;
284    default:    // not reachable
285        bitsPerSample = 0;
286        break;
287    }
288    unsigned blockAlignment = (bitsPerSample >> 3) * info->channels;
289    unsigned byteRate = info->samplerate * blockAlignment;
290    write4u(&wav[28], byteRate);
291    wav[32] = blockAlignment;
292    wav[34] = bitsPerSample;
293    size_t extra = 0;
294    if (sub == SF_FORMAT_FLOAT) {
295        memcpy(&wav[38], "fact", 4);
296        wav[42] = 4;
297        memcpy(&wav[50], "data", 4);
298        extra = 14;
299    } else
300        memcpy(&wav[36], "data", 4);
301    // dataSize is initially zero
302    (void) fwrite(wav, 44 + extra, 1, stream);
303    SNDFILE *handle = (SNDFILE *) malloc(sizeof(SNDFILE));
304    handle->mode = SFM_WRITE;
305    handle->temp = NULL;
306    handle->stream = stream;
307    handle->bytesPerFrame = blockAlignment;
308    handle->remaining = 0;
309    handle->info = *info;
310    return handle;
311}
312
313SNDFILE *sf_open(const char *path, int mode, SF_INFO *info)
314{
315    if (path == NULL || info == NULL) {
316        fprintf(stderr, "path=%p info=%p\n", path, info);
317        return NULL;
318    }
319    switch (mode) {
320    case SFM_READ:
321        return sf_open_read(path, info);
322    case SFM_WRITE:
323        return sf_open_write(path, info);
324    default:
325        fprintf(stderr, "mode=%d\n", mode);
326        return NULL;
327    }
328}
329
330void sf_close(SNDFILE *handle)
331{
332    if (handle == NULL)
333        return;
334    free(handle->temp);
335    if (handle->mode == SFM_WRITE) {
336        (void) fflush(handle->stream);
337        rewind(handle->stream);
338        unsigned char wav[58];
339        size_t extra = (handle->info.format & SF_FORMAT_SUBMASK) == SF_FORMAT_FLOAT ? 14 : 0;
340        (void) fread(wav, 44 + extra, 1, handle->stream);
341        unsigned dataSize = handle->remaining * handle->bytesPerFrame;
342        write4u(&wav[4], dataSize + 36 + extra);    // riffSize
343        write4u(&wav[40 + extra], dataSize);        // dataSize
344        rewind(handle->stream);
345        (void) fwrite(wav, 44 + extra, 1, handle->stream);
346    }
347    (void) fclose(handle->stream);
348    free(handle);
349}
350
351sf_count_t sf_readf_short(SNDFILE *handle, short *ptr, sf_count_t desiredFrames)
352{
353    if (handle == NULL || handle->mode != SFM_READ || ptr == NULL || !handle->remaining ||
354            desiredFrames <= 0) {
355        return 0;
356    }
357    if (handle->remaining < (size_t) desiredFrames) {
358        desiredFrames = handle->remaining;
359    }
360    // does not check for numeric overflow
361    size_t desiredBytes = desiredFrames * handle->bytesPerFrame;
362    size_t actualBytes;
363    void *temp = NULL;
364    unsigned format = handle->info.format & SF_FORMAT_SUBMASK;
365    if (format == SF_FORMAT_PCM_32 || format == SF_FORMAT_FLOAT) {
366        temp = malloc(desiredBytes);
367        actualBytes = fread(temp, sizeof(char), desiredBytes, handle->stream);
368    } else {
369        actualBytes = fread(ptr, sizeof(char), desiredBytes, handle->stream);
370    }
371    size_t actualFrames = actualBytes / handle->bytesPerFrame;
372    handle->remaining -= actualFrames;
373    switch (format) {
374    case SF_FORMAT_PCM_U8:
375        memcpy_to_i16_from_u8(ptr, (unsigned char *) ptr, actualFrames * handle->info.channels);
376        break;
377    case SF_FORMAT_PCM_16:
378        if (!isLittleEndian())
379            my_swab(ptr, actualFrames * handle->info.channels);
380        break;
381    case SF_FORMAT_PCM_32:
382        memcpy_to_i16_from_i32(ptr, (const int *) temp, actualFrames * handle->info.channels);
383        free(temp);
384        break;
385    case SF_FORMAT_FLOAT:
386        memcpy_to_i16_from_float(ptr, (const float *) temp, actualFrames * handle->info.channels);
387        free(temp);
388        break;
389    default:
390        memset(ptr, 0, actualFrames * handle->info.channels * sizeof(short));
391        break;
392    }
393    return actualFrames;
394}
395
396sf_count_t sf_readf_float(SNDFILE *handle, float *ptr, sf_count_t desiredFrames)
397{
398    if (handle == NULL || handle->mode != SFM_READ || ptr == NULL || !handle->remaining ||
399            desiredFrames <= 0) {
400        return 0;
401    }
402    if (handle->remaining < (size_t) desiredFrames) {
403        desiredFrames = handle->remaining;
404    }
405    // does not check for numeric overflow
406    size_t desiredBytes = desiredFrames * handle->bytesPerFrame;
407    size_t actualBytes;
408    void *temp = NULL;
409    unsigned format = handle->info.format & SF_FORMAT_SUBMASK;
410    if (format == SF_FORMAT_PCM_16 || format == SF_FORMAT_PCM_U8) {
411        temp = malloc(desiredBytes);
412        actualBytes = fread(temp, sizeof(char), desiredBytes, handle->stream);
413    } else {
414        actualBytes = fread(ptr, sizeof(char), desiredBytes, handle->stream);
415    }
416    size_t actualFrames = actualBytes / handle->bytesPerFrame;
417    handle->remaining -= actualFrames;
418    switch (format) {
419    case SF_FORMAT_PCM_U8:
420#if 0
421        // TODO - implement
422        memcpy_to_float_from_u8(ptr, (const unsigned char *) temp,
423                actualFrames * handle->info.channels);
424#endif
425        free(temp);
426        break;
427    case SF_FORMAT_PCM_16:
428        memcpy_to_float_from_i16(ptr, (const short *) temp, actualFrames * handle->info.channels);
429        free(temp);
430        break;
431    case SF_FORMAT_PCM_32:
432        memcpy_to_float_from_i32(ptr, (const int *) ptr, actualFrames * handle->info.channels);
433        break;
434    case SF_FORMAT_FLOAT:
435        break;
436    default:
437        memset(ptr, 0, actualFrames * handle->info.channels * sizeof(float));
438        break;
439    }
440    return actualFrames;
441}
442
443sf_count_t sf_readf_int(SNDFILE *handle, int *ptr, sf_count_t desiredFrames)
444{
445    if (handle == NULL || handle->mode != SFM_READ || ptr == NULL || !handle->remaining ||
446            desiredFrames <= 0) {
447        return 0;
448    }
449    if (handle->remaining < (size_t) desiredFrames) {
450        desiredFrames = handle->remaining;
451    }
452    // does not check for numeric overflow
453    size_t desiredBytes = desiredFrames * handle->bytesPerFrame;
454    void *temp = NULL;
455    unsigned format = handle->info.format & SF_FORMAT_SUBMASK;
456    size_t actualBytes;
457    if (format == SF_FORMAT_PCM_16 || format == SF_FORMAT_PCM_U8) {
458        temp = malloc(desiredBytes);
459        actualBytes = fread(temp, sizeof(char), desiredBytes, handle->stream);
460    } else {
461        actualBytes = fread(ptr, sizeof(char), desiredBytes, handle->stream);
462    }
463    size_t actualFrames = actualBytes / handle->bytesPerFrame;
464    handle->remaining -= actualFrames;
465    switch (format) {
466    case SF_FORMAT_PCM_U8:
467#if 0
468        // TODO - implement
469        memcpy_to_i32_from_u8(ptr, (const unsigned char *) temp,
470                actualFrames * handle->info.channels);
471#endif
472        free(temp);
473        break;
474    case SF_FORMAT_PCM_16:
475        memcpy_to_i32_from_i16(ptr, (const short *) temp, actualFrames * handle->info.channels);
476        free(temp);
477        break;
478    case SF_FORMAT_PCM_32:
479        break;
480    case SF_FORMAT_FLOAT:
481        memcpy_to_i32_from_float(ptr, (const float *) ptr, actualFrames * handle->info.channels);
482        break;
483    default:
484        memset(ptr, 0, actualFrames * handle->info.channels * sizeof(int));
485        break;
486    }
487    return actualFrames;
488}
489
490sf_count_t sf_writef_short(SNDFILE *handle, const short *ptr, sf_count_t desiredFrames)
491{
492    if (handle == NULL || handle->mode != SFM_WRITE || ptr == NULL || desiredFrames <= 0)
493        return 0;
494    size_t desiredBytes = desiredFrames * handle->bytesPerFrame;
495    size_t actualBytes = 0;
496    switch (handle->info.format & SF_FORMAT_SUBMASK) {
497    case SF_FORMAT_PCM_U8:
498        handle->temp = realloc(handle->temp, desiredBytes);
499        memcpy_to_u8_from_i16(handle->temp, ptr, desiredBytes);
500        actualBytes = fwrite(handle->temp, sizeof(char), desiredBytes, handle->stream);
501        break;
502    case SF_FORMAT_PCM_16:
503        // does not check for numeric overflow
504        if (isLittleEndian()) {
505            actualBytes = fwrite(ptr, sizeof(char), desiredBytes, handle->stream);
506        } else {
507            handle->temp = realloc(handle->temp, desiredBytes);
508            memcpy(handle->temp, ptr, desiredBytes);
509            my_swab((short *) handle->temp, desiredFrames * handle->info.channels);
510            actualBytes = fwrite(handle->temp, sizeof(char), desiredBytes, handle->stream);
511        }
512        break;
513    case SF_FORMAT_FLOAT:
514        handle->temp = realloc(handle->temp, desiredBytes);
515        memcpy_to_float_from_i16((float *) handle->temp, ptr,
516                desiredFrames * handle->info.channels);
517        actualBytes = fwrite(handle->temp, sizeof(char), desiredBytes, handle->stream);
518        break;
519    default:
520        break;
521    }
522    size_t actualFrames = actualBytes / handle->bytesPerFrame;
523    handle->remaining += actualFrames;
524    return actualFrames;
525}
526
527sf_count_t sf_writef_float(SNDFILE *handle, const float *ptr, sf_count_t desiredFrames)
528{
529    if (handle == NULL || handle->mode != SFM_WRITE || ptr == NULL || desiredFrames <= 0)
530        return 0;
531    size_t desiredBytes = desiredFrames * handle->bytesPerFrame;
532    size_t actualBytes = 0;
533    switch (handle->info.format & SF_FORMAT_SUBMASK) {
534    case SF_FORMAT_FLOAT:
535        actualBytes = fwrite(ptr, sizeof(char), desiredBytes, handle->stream);
536        break;
537    case SF_FORMAT_PCM_16:
538        handle->temp = realloc(handle->temp, desiredBytes);
539        memcpy_to_i16_from_float((short *) handle->temp, ptr,
540                desiredFrames * handle->info.channels);
541        actualBytes = fwrite(handle->temp, sizeof(char), desiredBytes, handle->stream);
542        break;
543    case SF_FORMAT_PCM_U8:  // transcoding from float to byte not yet implemented
544    default:
545        break;
546    }
547    size_t actualFrames = actualBytes / handle->bytesPerFrame;
548    handle->remaining += actualFrames;
549    return actualFrames;
550}
551