tinysndfile.c revision 207ec2930dd429e5d4aa58e2b9899e09aa76e22c
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 <stdio.h>
19#include <string.h>
20
21struct SNDFILE_ {
22    FILE *stream;
23    size_t bytesPerFrame;
24    size_t remaining;
25    SF_INFO info;
26};
27
28static unsigned little2u(unsigned char *ptr)
29{
30    return (ptr[1] << 8) + ptr[0];
31}
32
33static unsigned little4u(unsigned char *ptr)
34{
35    return (ptr[3] << 24) + (ptr[2] << 16) + (ptr[1] << 8) + ptr[0];
36}
37
38static int isLittleEndian(void)
39{
40    static const short one = 1;
41    return *((const char *) &one) == 1;
42}
43
44static void swab(short *ptr, size_t numToSwap)
45{
46    while (numToSwap > 0) {
47        *ptr = little2u((unsigned char *) ptr);
48        --numToSwap;
49        ++ptr;
50    }
51}
52
53SNDFILE *sf_open(const char *path, int mode, SF_INFO *info)
54{
55    if (path == NULL || mode != SFM_READ || info == NULL)
56        return NULL;
57    FILE *stream = fopen(path, "rb");
58    if (stream == NULL)
59        return NULL;
60    // don't attempt to parse all valid forms, just the most common one
61    unsigned char wav[44];
62    size_t actual;
63    actual = fread(wav, sizeof(char), sizeof(wav), stream);
64    if (actual != sizeof(wav))
65        return NULL;
66    for (;;) {
67        if (memcmp(wav, "RIFF", 4))
68            break;
69        unsigned riffSize = little4u(&wav[4]);
70        if (riffSize < 44)
71            break;
72        if (memcmp(&wav[8], "WAVEfmt ", 8))
73            break;
74        unsigned fmtsize = little4u(&wav[16]);
75        if (fmtsize != 16)
76            break;
77        unsigned format = little2u(&wav[20]);
78        if (format != 1)    // PCM
79            break;
80        unsigned channels = little2u(&wav[22]);
81        if (channels != 1 && channels != 2)
82            break;
83        unsigned samplerate = little4u(&wav[24]);
84        if (samplerate == 0)
85            break;
86        // ignore byte rate
87        // ignore block alignment
88        unsigned bitsPerSample = little2u(&wav[34]);
89        if (/*bitsPerSample != 8 &&*/ bitsPerSample != 16)
90            break;
91        unsigned bytesPerFrame = (bitsPerSample >> 3) * channels;
92        if (memcmp(&wav[36], "data", 4))
93            break;
94        unsigned dataSize = little4u(&wav[40]);
95        SNDFILE *handle = (SNDFILE *) malloc(sizeof(SNDFILE));
96        handle->stream = stream;
97        handle->bytesPerFrame = bytesPerFrame;
98        handle->remaining = dataSize / bytesPerFrame;
99        handle->info.samplerate = samplerate;
100        handle->info.channels = channels;
101        handle->info.format = SF_FORMAT_WAV | SF_FORMAT_PCM_16;
102        *info = handle->info;
103        return handle;
104    }
105    return NULL;
106}
107
108void sf_close(SNDFILE *handle)
109{
110    if (handle == NULL)
111        return;
112    (void) fclose(handle->stream);
113    handle->stream = NULL;
114    handle->remaining = 0;
115}
116
117ssize_t sf_readf_short(SNDFILE *handle, short *ptr, size_t desiredFrames)
118{
119    if (handle == NULL || ptr == NULL || !handle->remaining)
120        return 0;
121    if (handle->remaining < desiredFrames)
122        desiredFrames = handle->remaining;
123    size_t desiredBytes = desiredFrames * handle->bytesPerFrame;
124    // does not check for numeric overflow
125    size_t actualBytes = fread(ptr, sizeof(char), desiredBytes, handle->stream);
126    size_t actualFrames = actualBytes / handle->bytesPerFrame;
127    handle->remaining -= actualFrames;
128    if (!isLittleEndian())
129        swab(ptr, actualFrames * handle->info.channels);
130    return actualFrames;
131}
132