Home | History | Annotate | Download | only in audio_utils
      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 
     22 struct SNDFILE_ {
     23     int mode;
     24     uint8_t *temp;  // realloc buffer used for shrinking 16 bits to 8 bits and byte-swapping
     25     FILE *stream;
     26     size_t bytesPerFrame;
     27     size_t remaining;   // frames unread for SFM_READ, frames written for SFM_WRITE
     28     SF_INFO info;
     29 };
     30 
     31 static unsigned little2u(unsigned char *ptr)
     32 {
     33     return (ptr[1] << 8) + ptr[0];
     34 }
     35 
     36 static unsigned little4u(unsigned char *ptr)
     37 {
     38     return (ptr[3] << 24) + (ptr[2] << 16) + (ptr[1] << 8) + ptr[0];
     39 }
     40 
     41 static int isLittleEndian(void)
     42 {
     43     static const short one = 1;
     44     return *((const char *) &one) == 1;
     45 }
     46 
     47 // "swab" conflicts with OS X <string.h>
     48 static void my_swab(short *ptr, size_t numToSwap)
     49 {
     50     while (numToSwap > 0) {
     51         *ptr = little2u((unsigned char *) ptr);
     52         --numToSwap;
     53         ++ptr;
     54     }
     55 }
     56 
     57 static SNDFILE *sf_open_read(const char *path, SF_INFO *info)
     58 {
     59     FILE *stream = fopen(path, "rb");
     60     if (stream == NULL)
     61         return NULL;
     62     // don't attempt to parse all valid forms, just the most common one
     63     unsigned char wav[44];
     64     size_t actual;
     65     actual = fread(wav, sizeof(char), sizeof(wav), stream);
     66     if (actual != sizeof(wav))
     67         return NULL;
     68     for (;;) {
     69         if (memcmp(wav, "RIFF", 4))
     70             break;
     71         unsigned riffSize = little4u(&wav[4]);
     72         if (riffSize < 36)
     73             break;
     74         if (memcmp(&wav[8], "WAVEfmt ", 8))
     75             break;
     76         unsigned fmtsize = little4u(&wav[16]);
     77         if (fmtsize != 16)
     78             break;
     79         unsigned format = little2u(&wav[20]);
     80         if (format != 1)    // PCM
     81             break;
     82         unsigned channels = little2u(&wav[22]);
     83         if (channels != 1 && channels != 2)
     84             break;
     85         unsigned samplerate = little4u(&wav[24]);
     86         if (samplerate == 0)
     87             break;
     88         // ignore byte rate
     89         // ignore block alignment
     90         unsigned bitsPerSample = little2u(&wav[34]);
     91         if (bitsPerSample != 8 && bitsPerSample != 16)
     92             break;
     93         unsigned bytesPerFrame = (bitsPerSample >> 3) * channels;
     94         if (memcmp(&wav[36], "data", 4))
     95             break;
     96         unsigned dataSize = little4u(&wav[40]);
     97         SNDFILE *handle = (SNDFILE *) malloc(sizeof(SNDFILE));
     98         handle->mode = SFM_READ;
     99         handle->temp = NULL;
    100         handle->stream = stream;
    101         handle->bytesPerFrame = bytesPerFrame;
    102         handle->remaining = dataSize / bytesPerFrame;
    103         handle->info.frames = handle->remaining;
    104         handle->info.samplerate = samplerate;
    105         handle->info.channels = channels;
    106         handle->info.format = SF_FORMAT_WAV;
    107         if (bitsPerSample == 8)
    108             handle->info.format |= SF_FORMAT_PCM_U8;
    109         else
    110             handle->info.format |= SF_FORMAT_PCM_16;
    111         *info = handle->info;
    112         return handle;
    113     }
    114     return NULL;
    115 }
    116 
    117 static void write4u(unsigned char *ptr, unsigned u)
    118 {
    119     ptr[0] = u;
    120     ptr[1] = u >> 8;
    121     ptr[2] = u >> 16;
    122     ptr[3] = u >> 24;
    123 }
    124 
    125 static SNDFILE *sf_open_write(const char *path, SF_INFO *info)
    126 {
    127     if (!(
    128             (info->samplerate > 0) &&
    129             (info->channels == 1 || info->channels == 2) &&
    130             ((info->format & SF_FORMAT_TYPEMASK) == SF_FORMAT_WAV) &&
    131             ((info->format & SF_FORMAT_SUBMASK) == SF_FORMAT_PCM_16 ||
    132              (info->format & SF_FORMAT_SUBMASK) == SF_FORMAT_PCM_U8)
    133           )) {
    134         return NULL;
    135     }
    136     FILE *stream = fopen(path, "w+b");
    137     unsigned char wav[44];
    138     memset(wav, 0, sizeof(wav));
    139     memcpy(wav, "RIFF", 4);
    140     wav[4] = 36;    // riffSize
    141     memcpy(&wav[8], "WAVEfmt ", 8);
    142     wav[16] = 16;   // fmtsize
    143     wav[20] = 1;    // format = PCM
    144     wav[22] = info->channels;
    145     write4u(&wav[24], info->samplerate);
    146     unsigned bitsPerSample = (info->format & SF_FORMAT_SUBMASK) == SF_FORMAT_PCM_16 ? 16 : 8;
    147     unsigned blockAlignment = (bitsPerSample >> 3) * info->channels;
    148     unsigned byteRate = info->samplerate * blockAlignment;
    149     write4u(&wav[28], byteRate);
    150     wav[32] = blockAlignment;
    151     wav[34] = bitsPerSample;
    152     memcpy(&wav[36], "data", 4);
    153     // dataSize is initially zero
    154     (void) fwrite(wav, sizeof(wav), 1, stream);
    155     SNDFILE *handle = (SNDFILE *) malloc(sizeof(SNDFILE));
    156     handle->mode = SFM_WRITE;
    157     handle->temp = NULL;
    158     handle->stream = stream;
    159     handle->bytesPerFrame = blockAlignment;
    160     handle->remaining = 0;
    161     handle->info = *info;
    162     return handle;
    163 }
    164 
    165 SNDFILE *sf_open(const char *path, int mode, SF_INFO *info)
    166 {
    167     if (path == NULL || info == NULL)
    168         return NULL;
    169     switch (mode) {
    170     case SFM_READ:
    171         return sf_open_read(path, info);
    172     case SFM_WRITE:
    173         return sf_open_write(path, info);
    174     default:
    175         return NULL;
    176     }
    177 }
    178 
    179 void sf_close(SNDFILE *handle)
    180 {
    181     if (handle == NULL)
    182         return;
    183     free(handle->temp);
    184     if (handle->mode == SFM_WRITE) {
    185         (void) fflush(handle->stream);
    186         rewind(handle->stream);
    187         unsigned char wav[44];
    188         (void) fread(wav, sizeof(wav), 1, handle->stream);
    189         unsigned dataSize = handle->remaining * handle->bytesPerFrame;
    190         write4u(&wav[4], dataSize + 36);    // riffSize
    191         write4u(&wav[40], dataSize);        // dataSize
    192         rewind(handle->stream);
    193         (void) fwrite(wav, sizeof(wav), 1, handle->stream);
    194     }
    195     (void) fclose(handle->stream);
    196     free(handle);
    197 }
    198 
    199 sf_count_t sf_readf_short(SNDFILE *handle, short *ptr, sf_count_t desiredFrames)
    200 {
    201     if (handle == NULL || handle->mode != SFM_READ || ptr == NULL || !handle->remaining ||
    202             desiredFrames <= 0) {
    203         return 0;
    204     }
    205     if (handle->remaining < (size_t) desiredFrames)
    206         desiredFrames = handle->remaining;
    207     size_t desiredBytes = desiredFrames * handle->bytesPerFrame;
    208     // does not check for numeric overflow
    209     size_t actualBytes = fread(ptr, sizeof(char), desiredBytes, handle->stream);
    210     size_t actualFrames = actualBytes / handle->bytesPerFrame;
    211     handle->remaining -= actualFrames;
    212     switch (handle->info.format & SF_FORMAT_SUBMASK) {
    213     case SF_FORMAT_PCM_U8:
    214         memcpy_to_i16_from_u8(ptr, (unsigned char *) ptr, actualFrames * handle->info.channels);
    215         break;
    216     case SF_FORMAT_PCM_16:
    217         if (!isLittleEndian())
    218             my_swab(ptr, actualFrames * handle->info.channels);
    219         break;
    220     }
    221     return actualFrames;
    222 }
    223 
    224 sf_count_t sf_writef_short(SNDFILE *handle, const short *ptr, sf_count_t desiredFrames)
    225 {
    226     if (handle == NULL || handle->mode != SFM_WRITE || ptr == NULL || desiredFrames <= 0)
    227         return 0;
    228     size_t desiredBytes = desiredFrames * handle->bytesPerFrame;
    229     size_t actualBytes = 0;
    230     switch (handle->info.format & SF_FORMAT_SUBMASK) {
    231     case SF_FORMAT_PCM_U8:
    232         handle->temp = realloc(handle->temp, desiredBytes);
    233         memcpy_to_u8_from_i16(handle->temp, ptr, desiredBytes);
    234         actualBytes = fwrite(handle->temp, sizeof(char), desiredBytes, handle->stream);
    235         break;
    236     case SF_FORMAT_PCM_16:
    237         // does not check for numeric overflow
    238         if (isLittleEndian()) {
    239             actualBytes = fwrite(ptr, sizeof(char), desiredBytes, handle->stream);
    240         } else {
    241             handle->temp = realloc(handle->temp, desiredBytes);
    242             memcpy(handle->temp, ptr, desiredBytes);
    243             my_swab((short *) handle->temp, desiredFrames * handle->info.channels);
    244             actualBytes = fwrite(handle->temp, sizeof(char), desiredBytes, handle->stream);
    245         }
    246         break;
    247     }
    248     size_t actualFrames = actualBytes / handle->bytesPerFrame;
    249     handle->remaining += actualFrames;
    250     return actualFrames;
    251 }
    252