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