blob: e94bb658d5c16ac395e05eb2621459dadfb4c068 [file] [log] [blame]
Glenn Kasten207ec292012-10-30 16:09:18 -07001/*
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
Glenn Kastend190da52015-12-14 12:20:28 -080017#include <system/audio.h>
Glenn Kasten207ec292012-10-30 16:09:18 -070018#include <audio_utils/sndfile.h>
Glenn Kasten36c248b2012-11-12 14:42:31 -080019#include <audio_utils/primitives.h>
Glenn Kasten2cee4e42015-04-01 11:09:55 -070020#ifdef HAVE_STDERR
Glenn Kasten207ec292012-10-30 16:09:18 -070021#include <stdio.h>
Glenn Kasten2cee4e42015-04-01 11:09:55 -070022#endif
Glenn Kasten207ec292012-10-30 16:09:18 -070023#include <string.h>
Glenn Kastenc0bd7152013-02-05 13:14:08 -080024#include <errno.h>
25
26#define WAVE_FORMAT_PCM 1
27#define WAVE_FORMAT_IEEE_FLOAT 3
28#define WAVE_FORMAT_EXTENSIBLE 0xFFFE
Glenn Kasten207ec292012-10-30 16:09:18 -070029
30struct SNDFILE_ {
Glenn Kasteneae13dd2013-01-04 11:24:55 -080031 int mode;
32 uint8_t *temp; // realloc buffer used for shrinking 16 bits to 8 bits and byte-swapping
Glenn Kasten207ec292012-10-30 16:09:18 -070033 FILE *stream;
34 size_t bytesPerFrame;
Glenn Kasteneae13dd2013-01-04 11:24:55 -080035 size_t remaining; // frames unread for SFM_READ, frames written for SFM_WRITE
Glenn Kasten207ec292012-10-30 16:09:18 -070036 SF_INFO info;
37};
38
39static unsigned little2u(unsigned char *ptr)
40{
41 return (ptr[1] << 8) + ptr[0];
42}
43
44static unsigned little4u(unsigned char *ptr)
45{
46 return (ptr[3] << 24) + (ptr[2] << 16) + (ptr[1] << 8) + ptr[0];
47}
48
49static int isLittleEndian(void)
50{
51 static const short one = 1;
52 return *((const char *) &one) == 1;
53}
54
Glenn Kasten8fdafc92013-02-12 16:23:42 -080055// "swab" conflicts with OS X <string.h>
56static void my_swab(short *ptr, size_t numToSwap)
Glenn Kasten207ec292012-10-30 16:09:18 -070057{
58 while (numToSwap > 0) {
59 *ptr = little2u((unsigned char *) ptr);
60 --numToSwap;
61 ++ptr;
62 }
63}
64
Glenn Kasteneae13dd2013-01-04 11:24:55 -080065static SNDFILE *sf_open_read(const char *path, SF_INFO *info)
Glenn Kasten207ec292012-10-30 16:09:18 -070066{
Glenn Kasten207ec292012-10-30 16:09:18 -070067 FILE *stream = fopen(path, "rb");
Glenn Kastenc0bd7152013-02-05 13:14:08 -080068 if (stream == NULL) {
Glenn Kasten2cee4e42015-04-01 11:09:55 -070069#ifdef HAVE_STDERR
Glenn Kastenc0bd7152013-02-05 13:14:08 -080070 fprintf(stderr, "fopen %s failed errno %d\n", path, errno);
Glenn Kasten2cee4e42015-04-01 11:09:55 -070071#endif
Glenn Kasten207ec292012-10-30 16:09:18 -070072 return NULL;
Glenn Kastenc0bd7152013-02-05 13:14:08 -080073 }
74
75 SNDFILE *handle = (SNDFILE *) malloc(sizeof(SNDFILE));
76 handle->mode = SFM_READ;
77 handle->temp = NULL;
78 handle->stream = stream;
79 handle->info.format = SF_FORMAT_WAV;
80
81 // don't attempt to parse all valid forms, just the most common ones
82 unsigned char wav[12];
Glenn Kasten207ec292012-10-30 16:09:18 -070083 size_t actual;
84 actual = fread(wav, sizeof(char), sizeof(wav), stream);
Glenn Kastenc0bd7152013-02-05 13:14:08 -080085 if (actual < 12) {
Glenn Kasten2cee4e42015-04-01 11:09:55 -070086#ifdef HAVE_STDERR
Kévin PETIT96d859c2014-03-26 11:56:32 +000087 fprintf(stderr, "actual %zu < 44\n", actual);
Glenn Kasten2cee4e42015-04-01 11:09:55 -070088#endif
Glenn Kastenc0bd7152013-02-05 13:14:08 -080089 goto close;
Glenn Kasten207ec292012-10-30 16:09:18 -070090 }
Glenn Kastenc0bd7152013-02-05 13:14:08 -080091 if (memcmp(wav, "RIFF", 4)) {
Glenn Kasten2cee4e42015-04-01 11:09:55 -070092#ifdef HAVE_STDERR
Glenn Kastenc0bd7152013-02-05 13:14:08 -080093 fprintf(stderr, "wav != RIFF\n");
Glenn Kasten2cee4e42015-04-01 11:09:55 -070094#endif
Glenn Kastenc0bd7152013-02-05 13:14:08 -080095 goto close;
96 }
97 unsigned riffSize = little4u(&wav[4]);
98 if (riffSize < 4) {
Glenn Kasten2cee4e42015-04-01 11:09:55 -070099#ifdef HAVE_STDERR
Glenn Kastenc0bd7152013-02-05 13:14:08 -0800100 fprintf(stderr, "riffSize %u < 4\n", riffSize);
Glenn Kasten2cee4e42015-04-01 11:09:55 -0700101#endif
Glenn Kastenc0bd7152013-02-05 13:14:08 -0800102 goto close;
103 }
104 if (memcmp(&wav[8], "WAVE", 4)) {
Glenn Kasten2cee4e42015-04-01 11:09:55 -0700105#ifdef HAVE_STDERR
Glenn Kastenc0bd7152013-02-05 13:14:08 -0800106 fprintf(stderr, "missing WAVE\n");
Glenn Kasten2cee4e42015-04-01 11:09:55 -0700107#endif
Glenn Kastenc0bd7152013-02-05 13:14:08 -0800108 goto close;
109 }
110 size_t remaining = riffSize - 4;
111 int hadFmt = 0;
112 int hadData = 0;
Glenn Kasten0c3baaa2014-09-29 11:22:26 -0700113 long dataTell = 0L;
Glenn Kastenc0bd7152013-02-05 13:14:08 -0800114 while (remaining >= 8) {
115 unsigned char chunk[8];
116 actual = fread(chunk, sizeof(char), sizeof(chunk), stream);
117 if (actual != sizeof(chunk)) {
Glenn Kasten2cee4e42015-04-01 11:09:55 -0700118#ifdef HAVE_STDERR
Kévin PETIT96d859c2014-03-26 11:56:32 +0000119 fprintf(stderr, "actual %zu != %zu\n", actual, sizeof(chunk));
Glenn Kasten2cee4e42015-04-01 11:09:55 -0700120#endif
Glenn Kastenc0bd7152013-02-05 13:14:08 -0800121 goto close;
122 }
123 remaining -= 8;
124 unsigned chunkSize = little4u(&chunk[4]);
125 if (chunkSize > remaining) {
Glenn Kasten2cee4e42015-04-01 11:09:55 -0700126#ifdef HAVE_STDERR
Kévin PETIT96d859c2014-03-26 11:56:32 +0000127 fprintf(stderr, "chunkSize %u > remaining %zu\n", chunkSize, remaining);
Glenn Kasten2cee4e42015-04-01 11:09:55 -0700128#endif
Glenn Kastenc0bd7152013-02-05 13:14:08 -0800129 goto close;
130 }
131 if (!memcmp(&chunk[0], "fmt ", 4)) {
132 if (hadFmt) {
Glenn Kasten2cee4e42015-04-01 11:09:55 -0700133#ifdef HAVE_STDERR
Glenn Kastenc0bd7152013-02-05 13:14:08 -0800134 fprintf(stderr, "multiple fmt\n");
Glenn Kasten2cee4e42015-04-01 11:09:55 -0700135#endif
Glenn Kastenc0bd7152013-02-05 13:14:08 -0800136 goto close;
137 }
138 if (chunkSize < 2) {
Glenn Kasten2cee4e42015-04-01 11:09:55 -0700139#ifdef HAVE_STDERR
Glenn Kastenc0bd7152013-02-05 13:14:08 -0800140 fprintf(stderr, "chunkSize %u < 2\n", chunkSize);
Glenn Kasten2cee4e42015-04-01 11:09:55 -0700141#endif
Glenn Kastenc0bd7152013-02-05 13:14:08 -0800142 goto close;
143 }
144 unsigned char fmt[40];
145 actual = fread(fmt, sizeof(char), 2, stream);
146 if (actual != 2) {
Glenn Kasten2cee4e42015-04-01 11:09:55 -0700147#ifdef HAVE_STDERR
Kévin PETIT96d859c2014-03-26 11:56:32 +0000148 fprintf(stderr, "actual %zu != 2\n", actual);
Glenn Kasten2cee4e42015-04-01 11:09:55 -0700149#endif
Glenn Kastenc0bd7152013-02-05 13:14:08 -0800150 goto close;
151 }
152 unsigned format = little2u(&fmt[0]);
153 size_t minSize = 0;
154 switch (format) {
155 case WAVE_FORMAT_PCM:
156 case WAVE_FORMAT_IEEE_FLOAT:
157 minSize = 16;
158 break;
159 case WAVE_FORMAT_EXTENSIBLE:
160 minSize = 40;
161 break;
162 default:
Glenn Kasten2cee4e42015-04-01 11:09:55 -0700163#ifdef HAVE_STDERR
Glenn Kastenc0bd7152013-02-05 13:14:08 -0800164 fprintf(stderr, "unsupported format %u\n", format);
Glenn Kasten2cee4e42015-04-01 11:09:55 -0700165#endif
Glenn Kastenc0bd7152013-02-05 13:14:08 -0800166 goto close;
167 }
168 if (chunkSize < minSize) {
Glenn Kasten2cee4e42015-04-01 11:09:55 -0700169#ifdef HAVE_STDERR
Kévin PETIT96d859c2014-03-26 11:56:32 +0000170 fprintf(stderr, "chunkSize %u < minSize %zu\n", chunkSize, minSize);
Glenn Kasten2cee4e42015-04-01 11:09:55 -0700171#endif
Glenn Kastenc0bd7152013-02-05 13:14:08 -0800172 goto close;
173 }
174 actual = fread(&fmt[2], sizeof(char), minSize - 2, stream);
175 if (actual != minSize - 2) {
Glenn Kasten2cee4e42015-04-01 11:09:55 -0700176#ifdef HAVE_STDERR
Kévin PETIT96d859c2014-03-26 11:56:32 +0000177 fprintf(stderr, "actual %zu != %zu\n", actual, minSize - 16);
Glenn Kasten2cee4e42015-04-01 11:09:55 -0700178#endif
Glenn Kastenc0bd7152013-02-05 13:14:08 -0800179 goto close;
180 }
181 if (chunkSize > minSize) {
182 fseek(stream, (long) (chunkSize - minSize), SEEK_CUR);
183 }
184 unsigned channels = little2u(&fmt[2]);
Glenn Kastend190da52015-12-14 12:20:28 -0800185 if ((channels < 1) || (channels > FCC_8)) {
Glenn Kasten2cee4e42015-04-01 11:09:55 -0700186#ifdef HAVE_STDERR
Glenn Kasten7c1930a2014-05-07 11:20:55 -0700187 fprintf(stderr, "unsupported channels %u\n", channels);
Glenn Kasten2cee4e42015-04-01 11:09:55 -0700188#endif
Glenn Kastenc0bd7152013-02-05 13:14:08 -0800189 goto close;
190 }
191 unsigned samplerate = little4u(&fmt[4]);
192 if (samplerate == 0) {
Glenn Kasten2cee4e42015-04-01 11:09:55 -0700193#ifdef HAVE_STDERR
Glenn Kastenc0bd7152013-02-05 13:14:08 -0800194 fprintf(stderr, "samplerate %u == 0\n", samplerate);
Glenn Kasten2cee4e42015-04-01 11:09:55 -0700195#endif
Glenn Kastenc0bd7152013-02-05 13:14:08 -0800196 goto close;
197 }
198 // ignore byte rate
199 // ignore block alignment
200 unsigned bitsPerSample = little2u(&fmt[14]);
Glenn Kastenac13b242015-05-28 15:20:54 -0700201 if (bitsPerSample != 8 && bitsPerSample != 16 && bitsPerSample != 24 &&
202 bitsPerSample != 32) {
Glenn Kasten2cee4e42015-04-01 11:09:55 -0700203#ifdef HAVE_STDERR
Glenn Kastenac13b242015-05-28 15:20:54 -0700204 fprintf(stderr, "bitsPerSample %u != 8 or 16 or 24 or 32\n", bitsPerSample);
Glenn Kasten2cee4e42015-04-01 11:09:55 -0700205#endif
Glenn Kastenc0bd7152013-02-05 13:14:08 -0800206 goto close;
207 }
208 unsigned bytesPerFrame = (bitsPerSample >> 3) * channels;
209 handle->bytesPerFrame = bytesPerFrame;
210 handle->info.samplerate = samplerate;
211 handle->info.channels = channels;
212 switch (bitsPerSample) {
213 case 8:
214 handle->info.format |= SF_FORMAT_PCM_U8;
215 break;
216 case 16:
217 handle->info.format |= SF_FORMAT_PCM_16;
218 break;
Glenn Kastenac13b242015-05-28 15:20:54 -0700219 case 24:
220 handle->info.format |= SF_FORMAT_PCM_24;
221 break;
Glenn Kastenc0bd7152013-02-05 13:14:08 -0800222 case 32:
223 if (format == WAVE_FORMAT_IEEE_FLOAT)
224 handle->info.format |= SF_FORMAT_FLOAT;
225 else
226 handle->info.format |= SF_FORMAT_PCM_32;
227 break;
228 }
229 hadFmt = 1;
230 } else if (!memcmp(&chunk[0], "data", 4)) {
231 if (!hadFmt) {
Glenn Kasten2cee4e42015-04-01 11:09:55 -0700232#ifdef HAVE_STDERR
Glenn Kastenc0bd7152013-02-05 13:14:08 -0800233 fprintf(stderr, "data not preceded by fmt\n");
Glenn Kasten2cee4e42015-04-01 11:09:55 -0700234#endif
Glenn Kastenc0bd7152013-02-05 13:14:08 -0800235 goto close;
236 }
237 if (hadData) {
Glenn Kasten2cee4e42015-04-01 11:09:55 -0700238#ifdef HAVE_STDERR
Glenn Kastenc0bd7152013-02-05 13:14:08 -0800239 fprintf(stderr, "multiple data\n");
Glenn Kasten2cee4e42015-04-01 11:09:55 -0700240#endif
Glenn Kastenc0bd7152013-02-05 13:14:08 -0800241 goto close;
242 }
243 handle->remaining = chunkSize / handle->bytesPerFrame;
244 handle->info.frames = handle->remaining;
Glenn Kasten0c3baaa2014-09-29 11:22:26 -0700245 dataTell = ftell(stream);
246 if (chunkSize > 0) {
247 fseek(stream, (long) chunkSize, SEEK_CUR);
248 }
Glenn Kastenc0bd7152013-02-05 13:14:08 -0800249 hadData = 1;
250 } else if (!memcmp(&chunk[0], "fact", 4)) {
251 // ignore fact
252 if (chunkSize > 0) {
253 fseek(stream, (long) chunkSize, SEEK_CUR);
254 }
255 } else {
256 // ignore unknown chunk
Glenn Kasten2cee4e42015-04-01 11:09:55 -0700257#ifdef HAVE_STDERR
Glenn Kastenc0bd7152013-02-05 13:14:08 -0800258 fprintf(stderr, "ignoring unknown chunk %c%c%c%c\n",
259 chunk[0], chunk[1], chunk[2], chunk[3]);
Glenn Kasten2cee4e42015-04-01 11:09:55 -0700260#endif
Glenn Kastenc0bd7152013-02-05 13:14:08 -0800261 if (chunkSize > 0) {
262 fseek(stream, (long) chunkSize, SEEK_CUR);
263 }
264 }
265 remaining -= chunkSize;
266 }
267 if (remaining > 0) {
Glenn Kasten2cee4e42015-04-01 11:09:55 -0700268#ifdef HAVE_STDERR
Kévin PETIT96d859c2014-03-26 11:56:32 +0000269 fprintf(stderr, "partial chunk at end of RIFF, remaining %zu\n", remaining);
Glenn Kasten2cee4e42015-04-01 11:09:55 -0700270#endif
Glenn Kastenc0bd7152013-02-05 13:14:08 -0800271 goto close;
272 }
273 if (!hadData) {
Glenn Kasten2cee4e42015-04-01 11:09:55 -0700274#ifdef HAVE_STDERR
Glenn Kastenc0bd7152013-02-05 13:14:08 -0800275 fprintf(stderr, "missing data\n");
Glenn Kasten2cee4e42015-04-01 11:09:55 -0700276#endif
Glenn Kastenc0bd7152013-02-05 13:14:08 -0800277 goto close;
278 }
Glenn Kasten0c3baaa2014-09-29 11:22:26 -0700279 (void) fseek(stream, dataTell, SEEK_SET);
Glenn Kastenc0bd7152013-02-05 13:14:08 -0800280 *info = handle->info;
281 return handle;
282
283close:
284 free(handle);
285 fclose(stream);
Glenn Kasten207ec292012-10-30 16:09:18 -0700286 return NULL;
287}
288
Glenn Kasteneae13dd2013-01-04 11:24:55 -0800289static void write4u(unsigned char *ptr, unsigned u)
290{
291 ptr[0] = u;
292 ptr[1] = u >> 8;
293 ptr[2] = u >> 16;
294 ptr[3] = u >> 24;
295}
296
297static SNDFILE *sf_open_write(const char *path, SF_INFO *info)
298{
Glenn Kastenc0bd7152013-02-05 13:14:08 -0800299 int sub = info->format & SF_FORMAT_SUBMASK;
Glenn Kasteneae13dd2013-01-04 11:24:55 -0800300 if (!(
301 (info->samplerate > 0) &&
Glenn Kastend190da52015-12-14 12:20:28 -0800302 (info->channels > 0 && info->channels <= FCC_8) &&
Glenn Kasteneae13dd2013-01-04 11:24:55 -0800303 ((info->format & SF_FORMAT_TYPEMASK) == SF_FORMAT_WAV) &&
Glenn Kastenac13b242015-05-28 15:20:54 -0700304 (sub == SF_FORMAT_PCM_16 || sub == SF_FORMAT_PCM_U8 || sub == SF_FORMAT_FLOAT ||
Glenn Kastenec4eddf2015-06-18 15:38:15 -0700305 sub == SF_FORMAT_PCM_24 || sub == SF_FORMAT_PCM_32)
Glenn Kasteneae13dd2013-01-04 11:24:55 -0800306 )) {
307 return NULL;
308 }
309 FILE *stream = fopen(path, "w+b");
Andy Hung1fa816b2014-06-17 14:18:08 -0700310 if (stream == NULL) {
Glenn Kasten2cee4e42015-04-01 11:09:55 -0700311#ifdef HAVE_STDERR
Andy Hung1fa816b2014-06-17 14:18:08 -0700312 fprintf(stderr, "fopen %s failed errno %d\n", path, errno);
Glenn Kasten2cee4e42015-04-01 11:09:55 -0700313#endif
Andy Hung1fa816b2014-06-17 14:18:08 -0700314 return NULL;
315 }
Glenn Kastenc0bd7152013-02-05 13:14:08 -0800316 unsigned char wav[58];
Glenn Kasteneae13dd2013-01-04 11:24:55 -0800317 memset(wav, 0, sizeof(wav));
318 memcpy(wav, "RIFF", 4);
Glenn Kasteneae13dd2013-01-04 11:24:55 -0800319 memcpy(&wav[8], "WAVEfmt ", 8);
Glenn Kastenc0bd7152013-02-05 13:14:08 -0800320 if (sub == SF_FORMAT_FLOAT) {
321 wav[4] = 50; // riffSize
322 wav[16] = 18; // fmtSize
323 wav[20] = WAVE_FORMAT_IEEE_FLOAT;
324 } else {
325 wav[4] = 36; // riffSize
326 wav[16] = 16; // fmtSize
327 wav[20] = WAVE_FORMAT_PCM;
328 }
Glenn Kasteneae13dd2013-01-04 11:24:55 -0800329 wav[22] = info->channels;
330 write4u(&wav[24], info->samplerate);
Glenn Kastenc0bd7152013-02-05 13:14:08 -0800331 unsigned bitsPerSample;
332 switch (sub) {
333 case SF_FORMAT_PCM_16:
334 bitsPerSample = 16;
335 break;
336 case SF_FORMAT_PCM_U8:
337 bitsPerSample = 8;
338 break;
339 case SF_FORMAT_FLOAT:
340 bitsPerSample = 32;
341 break;
Glenn Kastenac13b242015-05-28 15:20:54 -0700342 case SF_FORMAT_PCM_24:
343 bitsPerSample = 24;
344 break;
Glenn Kastenec4eddf2015-06-18 15:38:15 -0700345 case SF_FORMAT_PCM_32:
346 bitsPerSample = 32;
347 break;
Glenn Kastenc0bd7152013-02-05 13:14:08 -0800348 default: // not reachable
349 bitsPerSample = 0;
350 break;
351 }
Glenn Kasteneae13dd2013-01-04 11:24:55 -0800352 unsigned blockAlignment = (bitsPerSample >> 3) * info->channels;
353 unsigned byteRate = info->samplerate * blockAlignment;
354 write4u(&wav[28], byteRate);
355 wav[32] = blockAlignment;
356 wav[34] = bitsPerSample;
Andy Hungc74f4b72014-06-03 19:15:18 -0700357 size_t extra = 0;
Glenn Kastenc0bd7152013-02-05 13:14:08 -0800358 if (sub == SF_FORMAT_FLOAT) {
359 memcpy(&wav[38], "fact", 4);
360 wav[42] = 4;
361 memcpy(&wav[50], "data", 4);
Andy Hungc74f4b72014-06-03 19:15:18 -0700362 extra = 14;
Glenn Kastenc0bd7152013-02-05 13:14:08 -0800363 } else
364 memcpy(&wav[36], "data", 4);
Glenn Kasteneae13dd2013-01-04 11:24:55 -0800365 // dataSize is initially zero
Andy Hungc74f4b72014-06-03 19:15:18 -0700366 (void) fwrite(wav, 44 + extra, 1, stream);
Glenn Kasteneae13dd2013-01-04 11:24:55 -0800367 SNDFILE *handle = (SNDFILE *) malloc(sizeof(SNDFILE));
368 handle->mode = SFM_WRITE;
369 handle->temp = NULL;
370 handle->stream = stream;
371 handle->bytesPerFrame = blockAlignment;
372 handle->remaining = 0;
373 handle->info = *info;
374 return handle;
375}
376
377SNDFILE *sf_open(const char *path, int mode, SF_INFO *info)
378{
Glenn Kastenc0bd7152013-02-05 13:14:08 -0800379 if (path == NULL || info == NULL) {
Glenn Kasten2cee4e42015-04-01 11:09:55 -0700380#ifdef HAVE_STDERR
Glenn Kastenc0bd7152013-02-05 13:14:08 -0800381 fprintf(stderr, "path=%p info=%p\n", path, info);
Glenn Kasten2cee4e42015-04-01 11:09:55 -0700382#endif
Glenn Kasteneae13dd2013-01-04 11:24:55 -0800383 return NULL;
Glenn Kastenc0bd7152013-02-05 13:14:08 -0800384 }
Glenn Kasteneae13dd2013-01-04 11:24:55 -0800385 switch (mode) {
386 case SFM_READ:
387 return sf_open_read(path, info);
388 case SFM_WRITE:
389 return sf_open_write(path, info);
390 default:
Glenn Kasten2cee4e42015-04-01 11:09:55 -0700391#ifdef HAVE_STDERR
Glenn Kastenc0bd7152013-02-05 13:14:08 -0800392 fprintf(stderr, "mode=%d\n", mode);
Glenn Kasten2cee4e42015-04-01 11:09:55 -0700393#endif
Glenn Kasteneae13dd2013-01-04 11:24:55 -0800394 return NULL;
395 }
396}
397
Glenn Kasten207ec292012-10-30 16:09:18 -0700398void sf_close(SNDFILE *handle)
399{
400 if (handle == NULL)
401 return;
Glenn Kasteneae13dd2013-01-04 11:24:55 -0800402 free(handle->temp);
403 if (handle->mode == SFM_WRITE) {
404 (void) fflush(handle->stream);
405 rewind(handle->stream);
Glenn Kastenc0bd7152013-02-05 13:14:08 -0800406 unsigned char wav[58];
407 size_t extra = (handle->info.format & SF_FORMAT_SUBMASK) == SF_FORMAT_FLOAT ? 14 : 0;
408 (void) fread(wav, 44 + extra, 1, handle->stream);
Glenn Kasteneae13dd2013-01-04 11:24:55 -0800409 unsigned dataSize = handle->remaining * handle->bytesPerFrame;
Glenn Kastenc0bd7152013-02-05 13:14:08 -0800410 write4u(&wav[4], dataSize + 36 + extra); // riffSize
411 write4u(&wav[40 + extra], dataSize); // dataSize
Glenn Kasteneae13dd2013-01-04 11:24:55 -0800412 rewind(handle->stream);
Glenn Kastenc0bd7152013-02-05 13:14:08 -0800413 (void) fwrite(wav, 44 + extra, 1, handle->stream);
Glenn Kasteneae13dd2013-01-04 11:24:55 -0800414 }
Glenn Kasten207ec292012-10-30 16:09:18 -0700415 (void) fclose(handle->stream);
Glenn Kasteneae13dd2013-01-04 11:24:55 -0800416 free(handle);
Glenn Kasten207ec292012-10-30 16:09:18 -0700417}
418
Glenn Kasten36c248b2012-11-12 14:42:31 -0800419sf_count_t sf_readf_short(SNDFILE *handle, short *ptr, sf_count_t desiredFrames)
Glenn Kasten207ec292012-10-30 16:09:18 -0700420{
Glenn Kasteneae13dd2013-01-04 11:24:55 -0800421 if (handle == NULL || handle->mode != SFM_READ || ptr == NULL || !handle->remaining ||
422 desiredFrames <= 0) {
Glenn Kasten207ec292012-10-30 16:09:18 -0700423 return 0;
Glenn Kasteneae13dd2013-01-04 11:24:55 -0800424 }
Glenn Kastenc89cb602014-03-11 09:03:35 -0700425 if (handle->remaining < (size_t) desiredFrames) {
Glenn Kasten207ec292012-10-30 16:09:18 -0700426 desiredFrames = handle->remaining;
Glenn Kastenc89cb602014-03-11 09:03:35 -0700427 }
Glenn Kasten207ec292012-10-30 16:09:18 -0700428 // does not check for numeric overflow
Glenn Kastenc0bd7152013-02-05 13:14:08 -0800429 size_t desiredBytes = desiredFrames * handle->bytesPerFrame;
430 size_t actualBytes;
431 void *temp = NULL;
432 unsigned format = handle->info.format & SF_FORMAT_SUBMASK;
Glenn Kastenac13b242015-05-28 15:20:54 -0700433 if (format == SF_FORMAT_PCM_32 || format == SF_FORMAT_FLOAT || format == SF_FORMAT_PCM_24) {
Glenn Kastenc0bd7152013-02-05 13:14:08 -0800434 temp = malloc(desiredBytes);
435 actualBytes = fread(temp, sizeof(char), desiredBytes, handle->stream);
436 } else {
437 actualBytes = fread(ptr, sizeof(char), desiredBytes, handle->stream);
438 }
Glenn Kasten207ec292012-10-30 16:09:18 -0700439 size_t actualFrames = actualBytes / handle->bytesPerFrame;
440 handle->remaining -= actualFrames;
Glenn Kastenc0bd7152013-02-05 13:14:08 -0800441 switch (format) {
Glenn Kasten36c248b2012-11-12 14:42:31 -0800442 case SF_FORMAT_PCM_U8:
443 memcpy_to_i16_from_u8(ptr, (unsigned char *) ptr, actualFrames * handle->info.channels);
444 break;
445 case SF_FORMAT_PCM_16:
446 if (!isLittleEndian())
Glenn Kasten8fdafc92013-02-12 16:23:42 -0800447 my_swab(ptr, actualFrames * handle->info.channels);
Glenn Kasten36c248b2012-11-12 14:42:31 -0800448 break;
Glenn Kastenc0bd7152013-02-05 13:14:08 -0800449 case SF_FORMAT_PCM_32:
450 memcpy_to_i16_from_i32(ptr, (const int *) temp, actualFrames * handle->info.channels);
451 free(temp);
452 break;
453 case SF_FORMAT_FLOAT:
454 memcpy_to_i16_from_float(ptr, (const float *) temp, actualFrames * handle->info.channels);
455 free(temp);
456 break;
Glenn Kastenac13b242015-05-28 15:20:54 -0700457 case SF_FORMAT_PCM_24:
458 memcpy_to_i16_from_p24(ptr, (const uint8_t *) temp, actualFrames * handle->info.channels);
459 free(temp);
460 break;
Glenn Kastenc0bd7152013-02-05 13:14:08 -0800461 default:
462 memset(ptr, 0, actualFrames * handle->info.channels * sizeof(short));
463 break;
Glenn Kasten36c248b2012-11-12 14:42:31 -0800464 }
Glenn Kasten207ec292012-10-30 16:09:18 -0700465 return actualFrames;
466}
Glenn Kasteneae13dd2013-01-04 11:24:55 -0800467
Glenn Kastenc0bd7152013-02-05 13:14:08 -0800468sf_count_t sf_readf_float(SNDFILE *handle, float *ptr, sf_count_t desiredFrames)
469{
Glenn Kastenc89cb602014-03-11 09:03:35 -0700470 if (handle == NULL || handle->mode != SFM_READ || ptr == NULL || !handle->remaining ||
471 desiredFrames <= 0) {
472 return 0;
473 }
474 if (handle->remaining < (size_t) desiredFrames) {
475 desiredFrames = handle->remaining;
476 }
477 // does not check for numeric overflow
478 size_t desiredBytes = desiredFrames * handle->bytesPerFrame;
479 size_t actualBytes;
Andy Hung52370162014-05-22 18:17:53 -0700480 void *temp = NULL;
Glenn Kastenc89cb602014-03-11 09:03:35 -0700481 unsigned format = handle->info.format & SF_FORMAT_SUBMASK;
Glenn Kastenac13b242015-05-28 15:20:54 -0700482 if (format == SF_FORMAT_PCM_16 || format == SF_FORMAT_PCM_U8 || format == SF_FORMAT_PCM_24) {
Andy Hung52370162014-05-22 18:17:53 -0700483 temp = malloc(desiredBytes);
484 actualBytes = fread(temp, sizeof(char), desiredBytes, handle->stream);
485 } else {
486 actualBytes = fread(ptr, sizeof(char), desiredBytes, handle->stream);
487 }
Glenn Kastenc89cb602014-03-11 09:03:35 -0700488 size_t actualFrames = actualBytes / handle->bytesPerFrame;
489 handle->remaining -= actualFrames;
490 switch (format) {
Glenn Kastenc89cb602014-03-11 09:03:35 -0700491 case SF_FORMAT_PCM_U8:
Andy Hung52370162014-05-22 18:17:53 -0700492 memcpy_to_float_from_u8(ptr, (const unsigned char *) temp,
493 actualFrames * handle->info.channels);
Andy Hung52370162014-05-22 18:17:53 -0700494 free(temp);
495 break;
496 case SF_FORMAT_PCM_16:
497 memcpy_to_float_from_i16(ptr, (const short *) temp, actualFrames * handle->info.channels);
498 free(temp);
499 break;
Glenn Kastenc89cb602014-03-11 09:03:35 -0700500 case SF_FORMAT_PCM_32:
501 memcpy_to_float_from_i32(ptr, (const int *) ptr, actualFrames * handle->info.channels);
502 break;
Glenn Kastenc89cb602014-03-11 09:03:35 -0700503 case SF_FORMAT_FLOAT:
504 break;
Glenn Kastenac13b242015-05-28 15:20:54 -0700505 case SF_FORMAT_PCM_24:
506 memcpy_to_float_from_p24(ptr, (const uint8_t *) temp, actualFrames * handle->info.channels);
507 free(temp);
508 break;
Glenn Kastenc89cb602014-03-11 09:03:35 -0700509 default:
510 memset(ptr, 0, actualFrames * handle->info.channels * sizeof(float));
511 break;
512 }
513 return actualFrames;
Glenn Kastenc0bd7152013-02-05 13:14:08 -0800514}
515
Andy Hung7b937812014-05-22 18:26:07 -0700516sf_count_t sf_readf_int(SNDFILE *handle, int *ptr, sf_count_t desiredFrames)
517{
518 if (handle == NULL || handle->mode != SFM_READ || ptr == NULL || !handle->remaining ||
519 desiredFrames <= 0) {
520 return 0;
521 }
522 if (handle->remaining < (size_t) desiredFrames) {
523 desiredFrames = handle->remaining;
524 }
525 // does not check for numeric overflow
526 size_t desiredBytes = desiredFrames * handle->bytesPerFrame;
527 void *temp = NULL;
528 unsigned format = handle->info.format & SF_FORMAT_SUBMASK;
529 size_t actualBytes;
Glenn Kastenac13b242015-05-28 15:20:54 -0700530 if (format == SF_FORMAT_PCM_16 || format == SF_FORMAT_PCM_U8 || format == SF_FORMAT_PCM_24) {
Andy Hung7b937812014-05-22 18:26:07 -0700531 temp = malloc(desiredBytes);
532 actualBytes = fread(temp, sizeof(char), desiredBytes, handle->stream);
533 } else {
534 actualBytes = fread(ptr, sizeof(char), desiredBytes, handle->stream);
535 }
536 size_t actualFrames = actualBytes / handle->bytesPerFrame;
537 handle->remaining -= actualFrames;
538 switch (format) {
539 case SF_FORMAT_PCM_U8:
Andy Hung7b937812014-05-22 18:26:07 -0700540 memcpy_to_i32_from_u8(ptr, (const unsigned char *) temp,
541 actualFrames * handle->info.channels);
Andy Hung7b937812014-05-22 18:26:07 -0700542 free(temp);
543 break;
544 case SF_FORMAT_PCM_16:
545 memcpy_to_i32_from_i16(ptr, (const short *) temp, actualFrames * handle->info.channels);
546 free(temp);
547 break;
548 case SF_FORMAT_PCM_32:
549 break;
550 case SF_FORMAT_FLOAT:
551 memcpy_to_i32_from_float(ptr, (const float *) ptr, actualFrames * handle->info.channels);
552 break;
Glenn Kastenac13b242015-05-28 15:20:54 -0700553 case SF_FORMAT_PCM_24:
554 memcpy_to_i32_from_p24(ptr, (const uint8_t *) temp, actualFrames * handle->info.channels);
555 free(temp);
556 break;
Andy Hung7b937812014-05-22 18:26:07 -0700557 default:
558 memset(ptr, 0, actualFrames * handle->info.channels * sizeof(int));
559 break;
560 }
561 return actualFrames;
562}
563
Glenn Kasteneae13dd2013-01-04 11:24:55 -0800564sf_count_t sf_writef_short(SNDFILE *handle, const short *ptr, sf_count_t desiredFrames)
565{
566 if (handle == NULL || handle->mode != SFM_WRITE || ptr == NULL || desiredFrames <= 0)
567 return 0;
568 size_t desiredBytes = desiredFrames * handle->bytesPerFrame;
569 size_t actualBytes = 0;
570 switch (handle->info.format & SF_FORMAT_SUBMASK) {
571 case SF_FORMAT_PCM_U8:
572 handle->temp = realloc(handle->temp, desiredBytes);
573 memcpy_to_u8_from_i16(handle->temp, ptr, desiredBytes);
574 actualBytes = fwrite(handle->temp, sizeof(char), desiredBytes, handle->stream);
575 break;
576 case SF_FORMAT_PCM_16:
577 // does not check for numeric overflow
578 if (isLittleEndian()) {
579 actualBytes = fwrite(ptr, sizeof(char), desiredBytes, handle->stream);
580 } else {
581 handle->temp = realloc(handle->temp, desiredBytes);
582 memcpy(handle->temp, ptr, desiredBytes);
Glenn Kasten8fdafc92013-02-12 16:23:42 -0800583 my_swab((short *) handle->temp, desiredFrames * handle->info.channels);
Glenn Kasteneae13dd2013-01-04 11:24:55 -0800584 actualBytes = fwrite(handle->temp, sizeof(char), desiredBytes, handle->stream);
585 }
586 break;
Andy Hunge9b333c2014-06-03 19:22:26 -0700587 case SF_FORMAT_FLOAT:
588 handle->temp = realloc(handle->temp, desiredBytes);
Andy Hung1fa816b2014-06-17 14:18:08 -0700589 memcpy_to_float_from_i16((float *) handle->temp, ptr,
590 desiredFrames * handle->info.channels);
Andy Hunge9b333c2014-06-03 19:22:26 -0700591 actualBytes = fwrite(handle->temp, sizeof(char), desiredBytes, handle->stream);
592 break;
Glenn Kastenc0bd7152013-02-05 13:14:08 -0800593 default:
594 break;
595 }
596 size_t actualFrames = actualBytes / handle->bytesPerFrame;
597 handle->remaining += actualFrames;
598 return actualFrames;
599}
600
601sf_count_t sf_writef_float(SNDFILE *handle, const float *ptr, sf_count_t desiredFrames)
602{
603 if (handle == NULL || handle->mode != SFM_WRITE || ptr == NULL || desiredFrames <= 0)
604 return 0;
605 size_t desiredBytes = desiredFrames * handle->bytesPerFrame;
606 size_t actualBytes = 0;
607 switch (handle->info.format & SF_FORMAT_SUBMASK) {
608 case SF_FORMAT_FLOAT:
609 actualBytes = fwrite(ptr, sizeof(char), desiredBytes, handle->stream);
610 break;
Glenn Kastenc0bd7152013-02-05 13:14:08 -0800611 case SF_FORMAT_PCM_16:
Andy Hunge9b333c2014-06-03 19:22:26 -0700612 handle->temp = realloc(handle->temp, desiredBytes);
Andy Hung1fa816b2014-06-17 14:18:08 -0700613 memcpy_to_i16_from_float((short *) handle->temp, ptr,
614 desiredFrames * handle->info.channels);
Andy Hunge9b333c2014-06-03 19:22:26 -0700615 actualBytes = fwrite(handle->temp, sizeof(char), desiredBytes, handle->stream);
616 break;
617 case SF_FORMAT_PCM_U8: // transcoding from float to byte not yet implemented
Glenn Kastenc0bd7152013-02-05 13:14:08 -0800618 default:
619 break;
Glenn Kasteneae13dd2013-01-04 11:24:55 -0800620 }
621 size_t actualFrames = actualBytes / handle->bytesPerFrame;
622 handle->remaining += actualFrames;
623 return actualFrames;
624}
Glenn Kastenec4eddf2015-06-18 15:38:15 -0700625
626sf_count_t sf_writef_int(SNDFILE *handle, const int *ptr, sf_count_t desiredFrames)
627{
628 if (handle == NULL || handle->mode != SFM_WRITE || ptr == NULL || desiredFrames <= 0)
629 return 0;
630 size_t desiredBytes = desiredFrames * handle->bytesPerFrame;
631 size_t actualBytes = 0;
632 switch (handle->info.format & SF_FORMAT_SUBMASK) {
633 case SF_FORMAT_PCM_32:
634 actualBytes = fwrite(ptr, sizeof(char), desiredBytes, handle->stream);
635 break;
636 default: // transcoding from other formats not yet implemented
637 break;
638 }
639 size_t actualFrames = actualBytes / handle->bytesPerFrame;
640 handle->remaining += actualFrames;
641 return actualFrames;
642}