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