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