blob: abbd800e5fd60ecf479f146d3a13400fe1d12f4e [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
17#include <audio_utils/sndfile.h>
Glenn Kasten36c248b2012-11-12 14:42:31 -080018#include <audio_utils/primitives.h>
Glenn Kasten2cee4e42015-04-01 11:09:55 -070019#ifdef HAVE_STDERR
Glenn Kasten207ec292012-10-30 16:09:18 -070020#include <stdio.h>
Glenn Kasten2cee4e42015-04-01 11:09:55 -070021#endif
Glenn Kasten207ec292012-10-30 16:09:18 -070022#include <string.h>
Glenn Kastenc0bd7152013-02-05 13:14:08 -080023#include <errno.h>
24
25#define WAVE_FORMAT_PCM 1
26#define WAVE_FORMAT_IEEE_FLOAT 3
27#define WAVE_FORMAT_EXTENSIBLE 0xFFFE
Glenn Kasten207ec292012-10-30 16:09:18 -070028
29struct SNDFILE_ {
Glenn Kasteneae13dd2013-01-04 11:24:55 -080030 int mode;
31 uint8_t *temp; // realloc buffer used for shrinking 16 bits to 8 bits and byte-swapping
Glenn Kasten207ec292012-10-30 16:09:18 -070032 FILE *stream;
33 size_t bytesPerFrame;
Glenn Kasteneae13dd2013-01-04 11:24:55 -080034 size_t remaining; // frames unread for SFM_READ, frames written for SFM_WRITE
Glenn Kasten207ec292012-10-30 16:09:18 -070035 SF_INFO info;
36};
37
38static unsigned little2u(unsigned char *ptr)
39{
40 return (ptr[1] << 8) + ptr[0];
41}
42
43static unsigned little4u(unsigned char *ptr)
44{
45 return (ptr[3] << 24) + (ptr[2] << 16) + (ptr[1] << 8) + ptr[0];
46}
47
48static int isLittleEndian(void)
49{
50 static const short one = 1;
51 return *((const char *) &one) == 1;
52}
53
Glenn Kasten8fdafc92013-02-12 16:23:42 -080054// "swab" conflicts with OS X <string.h>
55static void my_swab(short *ptr, size_t numToSwap)
Glenn Kasten207ec292012-10-30 16:09:18 -070056{
57 while (numToSwap > 0) {
58 *ptr = little2u((unsigned char *) ptr);
59 --numToSwap;
60 ++ptr;
61 }
62}
63
Glenn Kasteneae13dd2013-01-04 11:24:55 -080064static SNDFILE *sf_open_read(const char *path, SF_INFO *info)
Glenn Kasten207ec292012-10-30 16:09:18 -070065{
Glenn Kasten207ec292012-10-30 16:09:18 -070066 FILE *stream = fopen(path, "rb");
Glenn Kastenc0bd7152013-02-05 13:14:08 -080067 if (stream == NULL) {
Glenn Kasten2cee4e42015-04-01 11:09:55 -070068#ifdef HAVE_STDERR
Glenn Kastenc0bd7152013-02-05 13:14:08 -080069 fprintf(stderr, "fopen %s failed errno %d\n", path, errno);
Glenn Kasten2cee4e42015-04-01 11:09:55 -070070#endif
Glenn Kasten207ec292012-10-30 16:09:18 -070071 return NULL;
Glenn Kastenc0bd7152013-02-05 13:14:08 -080072 }
73
74 SNDFILE *handle = (SNDFILE *) malloc(sizeof(SNDFILE));
75 handle->mode = SFM_READ;
76 handle->temp = NULL;
77 handle->stream = stream;
78 handle->info.format = SF_FORMAT_WAV;
79
80 // don't attempt to parse all valid forms, just the most common ones
81 unsigned char wav[12];
Glenn Kasten207ec292012-10-30 16:09:18 -070082 size_t actual;
83 actual = fread(wav, sizeof(char), sizeof(wav), stream);
Glenn Kastenc0bd7152013-02-05 13:14:08 -080084 if (actual < 12) {
Glenn Kasten2cee4e42015-04-01 11:09:55 -070085#ifdef HAVE_STDERR
Kévin PETIT96d859c2014-03-26 11:56:32 +000086 fprintf(stderr, "actual %zu < 44\n", actual);
Glenn Kasten2cee4e42015-04-01 11:09:55 -070087#endif
Glenn Kastenc0bd7152013-02-05 13:14:08 -080088 goto close;
Glenn Kasten207ec292012-10-30 16:09:18 -070089 }
Glenn Kastenc0bd7152013-02-05 13:14:08 -080090 if (memcmp(wav, "RIFF", 4)) {
Glenn Kasten2cee4e42015-04-01 11:09:55 -070091#ifdef HAVE_STDERR
Glenn Kastenc0bd7152013-02-05 13:14:08 -080092 fprintf(stderr, "wav != RIFF\n");
Glenn Kasten2cee4e42015-04-01 11:09:55 -070093#endif
Glenn Kastenc0bd7152013-02-05 13:14:08 -080094 goto close;
95 }
96 unsigned riffSize = little4u(&wav[4]);
97 if (riffSize < 4) {
Glenn Kasten2cee4e42015-04-01 11:09:55 -070098#ifdef HAVE_STDERR
Glenn Kastenc0bd7152013-02-05 13:14:08 -080099 fprintf(stderr, "riffSize %u < 4\n", riffSize);
Glenn Kasten2cee4e42015-04-01 11:09:55 -0700100#endif
Glenn Kastenc0bd7152013-02-05 13:14:08 -0800101 goto close;
102 }
103 if (memcmp(&wav[8], "WAVE", 4)) {
Glenn Kasten2cee4e42015-04-01 11:09:55 -0700104#ifdef HAVE_STDERR
Glenn Kastenc0bd7152013-02-05 13:14:08 -0800105 fprintf(stderr, "missing WAVE\n");
Glenn Kasten2cee4e42015-04-01 11:09:55 -0700106#endif
Glenn Kastenc0bd7152013-02-05 13:14:08 -0800107 goto close;
108 }
109 size_t remaining = riffSize - 4;
110 int hadFmt = 0;
111 int hadData = 0;
Glenn Kasten0c3baaa2014-09-29 11:22:26 -0700112 long dataTell = 0L;
Glenn Kastenc0bd7152013-02-05 13:14:08 -0800113 while (remaining >= 8) {
114 unsigned char chunk[8];
115 actual = fread(chunk, sizeof(char), sizeof(chunk), stream);
116 if (actual != sizeof(chunk)) {
Glenn Kasten2cee4e42015-04-01 11:09:55 -0700117#ifdef HAVE_STDERR
Kévin PETIT96d859c2014-03-26 11:56:32 +0000118 fprintf(stderr, "actual %zu != %zu\n", actual, sizeof(chunk));
Glenn Kasten2cee4e42015-04-01 11:09:55 -0700119#endif
Glenn Kastenc0bd7152013-02-05 13:14:08 -0800120 goto close;
121 }
122 remaining -= 8;
123 unsigned chunkSize = little4u(&chunk[4]);
124 if (chunkSize > remaining) {
Glenn Kasten2cee4e42015-04-01 11:09:55 -0700125#ifdef HAVE_STDERR
Kévin PETIT96d859c2014-03-26 11:56:32 +0000126 fprintf(stderr, "chunkSize %u > remaining %zu\n", chunkSize, remaining);
Glenn Kasten2cee4e42015-04-01 11:09:55 -0700127#endif
Glenn Kastenc0bd7152013-02-05 13:14:08 -0800128 goto close;
129 }
130 if (!memcmp(&chunk[0], "fmt ", 4)) {
131 if (hadFmt) {
Glenn Kasten2cee4e42015-04-01 11:09:55 -0700132#ifdef HAVE_STDERR
Glenn Kastenc0bd7152013-02-05 13:14:08 -0800133 fprintf(stderr, "multiple fmt\n");
Glenn Kasten2cee4e42015-04-01 11:09:55 -0700134#endif
Glenn Kastenc0bd7152013-02-05 13:14:08 -0800135 goto close;
136 }
137 if (chunkSize < 2) {
Glenn Kasten2cee4e42015-04-01 11:09:55 -0700138#ifdef HAVE_STDERR
Glenn Kastenc0bd7152013-02-05 13:14:08 -0800139 fprintf(stderr, "chunkSize %u < 2\n", chunkSize);
Glenn Kasten2cee4e42015-04-01 11:09:55 -0700140#endif
Glenn Kastenc0bd7152013-02-05 13:14:08 -0800141 goto close;
142 }
143 unsigned char fmt[40];
144 actual = fread(fmt, sizeof(char), 2, stream);
145 if (actual != 2) {
Glenn Kasten2cee4e42015-04-01 11:09:55 -0700146#ifdef HAVE_STDERR
Kévin PETIT96d859c2014-03-26 11:56:32 +0000147 fprintf(stderr, "actual %zu != 2\n", actual);
Glenn Kasten2cee4e42015-04-01 11:09:55 -0700148#endif
Glenn Kastenc0bd7152013-02-05 13:14:08 -0800149 goto close;
150 }
151 unsigned format = little2u(&fmt[0]);
152 size_t minSize = 0;
153 switch (format) {
154 case WAVE_FORMAT_PCM:
155 case WAVE_FORMAT_IEEE_FLOAT:
156 minSize = 16;
157 break;
158 case WAVE_FORMAT_EXTENSIBLE:
159 minSize = 40;
160 break;
161 default:
Glenn Kasten2cee4e42015-04-01 11:09:55 -0700162#ifdef HAVE_STDERR
Glenn Kastenc0bd7152013-02-05 13:14:08 -0800163 fprintf(stderr, "unsupported format %u\n", format);
Glenn Kasten2cee4e42015-04-01 11:09:55 -0700164#endif
Glenn Kastenc0bd7152013-02-05 13:14:08 -0800165 goto close;
166 }
167 if (chunkSize < minSize) {
Glenn Kasten2cee4e42015-04-01 11:09:55 -0700168#ifdef HAVE_STDERR
Kévin PETIT96d859c2014-03-26 11:56:32 +0000169 fprintf(stderr, "chunkSize %u < minSize %zu\n", chunkSize, minSize);
Glenn Kasten2cee4e42015-04-01 11:09:55 -0700170#endif
Glenn Kastenc0bd7152013-02-05 13:14:08 -0800171 goto close;
172 }
173 actual = fread(&fmt[2], sizeof(char), minSize - 2, stream);
174 if (actual != minSize - 2) {
Glenn Kasten2cee4e42015-04-01 11:09:55 -0700175#ifdef HAVE_STDERR
Kévin PETIT96d859c2014-03-26 11:56:32 +0000176 fprintf(stderr, "actual %zu != %zu\n", actual, minSize - 16);
Glenn Kasten2cee4e42015-04-01 11:09:55 -0700177#endif
Glenn Kastenc0bd7152013-02-05 13:14:08 -0800178 goto close;
179 }
180 if (chunkSize > minSize) {
181 fseek(stream, (long) (chunkSize - minSize), SEEK_CUR);
182 }
183 unsigned channels = little2u(&fmt[2]);
Glenn Kasten7c1930a2014-05-07 11:20:55 -0700184 if (channels != 1 && channels != 2 && channels != 4 && channels != 6 && channels != 8) {
Glenn Kasten2cee4e42015-04-01 11:09:55 -0700185#ifdef HAVE_STDERR
Glenn Kasten7c1930a2014-05-07 11:20:55 -0700186 fprintf(stderr, "unsupported channels %u\n", channels);
Glenn Kasten2cee4e42015-04-01 11:09:55 -0700187#endif
Glenn Kastenc0bd7152013-02-05 13:14:08 -0800188 goto close;
189 }
190 unsigned samplerate = little4u(&fmt[4]);
191 if (samplerate == 0) {
Glenn Kasten2cee4e42015-04-01 11:09:55 -0700192#ifdef HAVE_STDERR
Glenn Kastenc0bd7152013-02-05 13:14:08 -0800193 fprintf(stderr, "samplerate %u == 0\n", samplerate);
Glenn Kasten2cee4e42015-04-01 11:09:55 -0700194#endif
Glenn Kastenc0bd7152013-02-05 13:14:08 -0800195 goto close;
196 }
197 // ignore byte rate
198 // ignore block alignment
199 unsigned bitsPerSample = little2u(&fmt[14]);
200 if (bitsPerSample != 8 && bitsPerSample != 16 && bitsPerSample != 32) {
Glenn Kasten2cee4e42015-04-01 11:09:55 -0700201#ifdef HAVE_STDERR
Glenn Kastenc0bd7152013-02-05 13:14:08 -0800202 fprintf(stderr, "bitsPerSample %u != 8 or 16 or 32\n", bitsPerSample);
Glenn Kasten2cee4e42015-04-01 11:09:55 -0700203#endif
Glenn Kastenc0bd7152013-02-05 13:14:08 -0800204 goto close;
205 }
206 unsigned bytesPerFrame = (bitsPerSample >> 3) * channels;
207 handle->bytesPerFrame = bytesPerFrame;
208 handle->info.samplerate = samplerate;
209 handle->info.channels = channels;
210 switch (bitsPerSample) {
211 case 8:
212 handle->info.format |= SF_FORMAT_PCM_U8;
213 break;
214 case 16:
215 handle->info.format |= SF_FORMAT_PCM_16;
216 break;
217 case 32:
218 if (format == WAVE_FORMAT_IEEE_FLOAT)
219 handle->info.format |= SF_FORMAT_FLOAT;
220 else
221 handle->info.format |= SF_FORMAT_PCM_32;
222 break;
223 }
224 hadFmt = 1;
225 } else if (!memcmp(&chunk[0], "data", 4)) {
226 if (!hadFmt) {
Glenn Kasten2cee4e42015-04-01 11:09:55 -0700227#ifdef HAVE_STDERR
Glenn Kastenc0bd7152013-02-05 13:14:08 -0800228 fprintf(stderr, "data not preceded by fmt\n");
Glenn Kasten2cee4e42015-04-01 11:09:55 -0700229#endif
Glenn Kastenc0bd7152013-02-05 13:14:08 -0800230 goto close;
231 }
232 if (hadData) {
Glenn Kasten2cee4e42015-04-01 11:09:55 -0700233#ifdef HAVE_STDERR
Glenn Kastenc0bd7152013-02-05 13:14:08 -0800234 fprintf(stderr, "multiple data\n");
Glenn Kasten2cee4e42015-04-01 11:09:55 -0700235#endif
Glenn Kastenc0bd7152013-02-05 13:14:08 -0800236 goto close;
237 }
238 handle->remaining = chunkSize / handle->bytesPerFrame;
239 handle->info.frames = handle->remaining;
Glenn Kasten0c3baaa2014-09-29 11:22:26 -0700240 dataTell = ftell(stream);
241 if (chunkSize > 0) {
242 fseek(stream, (long) chunkSize, SEEK_CUR);
243 }
Glenn Kastenc0bd7152013-02-05 13:14:08 -0800244 hadData = 1;
245 } else if (!memcmp(&chunk[0], "fact", 4)) {
246 // ignore fact
247 if (chunkSize > 0) {
248 fseek(stream, (long) chunkSize, SEEK_CUR);
249 }
250 } else {
251 // ignore unknown chunk
Glenn Kasten2cee4e42015-04-01 11:09:55 -0700252#ifdef HAVE_STDERR
Glenn Kastenc0bd7152013-02-05 13:14:08 -0800253 fprintf(stderr, "ignoring unknown chunk %c%c%c%c\n",
254 chunk[0], chunk[1], chunk[2], chunk[3]);
Glenn Kasten2cee4e42015-04-01 11:09:55 -0700255#endif
Glenn Kastenc0bd7152013-02-05 13:14:08 -0800256 if (chunkSize > 0) {
257 fseek(stream, (long) chunkSize, SEEK_CUR);
258 }
259 }
260 remaining -= chunkSize;
261 }
262 if (remaining > 0) {
Glenn Kasten2cee4e42015-04-01 11:09:55 -0700263#ifdef HAVE_STDERR
Kévin PETIT96d859c2014-03-26 11:56:32 +0000264 fprintf(stderr, "partial chunk at end of RIFF, remaining %zu\n", remaining);
Glenn Kasten2cee4e42015-04-01 11:09:55 -0700265#endif
Glenn Kastenc0bd7152013-02-05 13:14:08 -0800266 goto close;
267 }
268 if (!hadData) {
Glenn Kasten2cee4e42015-04-01 11:09:55 -0700269#ifdef HAVE_STDERR
Glenn Kastenc0bd7152013-02-05 13:14:08 -0800270 fprintf(stderr, "missing data\n");
Glenn Kasten2cee4e42015-04-01 11:09:55 -0700271#endif
Glenn Kastenc0bd7152013-02-05 13:14:08 -0800272 goto close;
273 }
Glenn Kasten0c3baaa2014-09-29 11:22:26 -0700274 (void) fseek(stream, dataTell, SEEK_SET);
Glenn Kastenc0bd7152013-02-05 13:14:08 -0800275 *info = handle->info;
276 return handle;
277
278close:
279 free(handle);
280 fclose(stream);
Glenn Kasten207ec292012-10-30 16:09:18 -0700281 return NULL;
282}
283
Glenn Kasteneae13dd2013-01-04 11:24:55 -0800284static void write4u(unsigned char *ptr, unsigned u)
285{
286 ptr[0] = u;
287 ptr[1] = u >> 8;
288 ptr[2] = u >> 16;
289 ptr[3] = u >> 24;
290}
291
292static SNDFILE *sf_open_write(const char *path, SF_INFO *info)
293{
Glenn Kastenc0bd7152013-02-05 13:14:08 -0800294 int sub = info->format & SF_FORMAT_SUBMASK;
Glenn Kasteneae13dd2013-01-04 11:24:55 -0800295 if (!(
296 (info->samplerate > 0) &&
Andy Hung7095bc72014-04-09 18:52:06 -0700297 (info->channels > 0 && info->channels <= 8) &&
Glenn Kasteneae13dd2013-01-04 11:24:55 -0800298 ((info->format & SF_FORMAT_TYPEMASK) == SF_FORMAT_WAV) &&
Glenn Kastenc0bd7152013-02-05 13:14:08 -0800299 (sub == SF_FORMAT_PCM_16 || sub == SF_FORMAT_PCM_U8 || sub == SF_FORMAT_FLOAT)
Glenn Kasteneae13dd2013-01-04 11:24:55 -0800300 )) {
301 return NULL;
302 }
303 FILE *stream = fopen(path, "w+b");
Andy Hung1fa816b2014-06-17 14:18:08 -0700304 if (stream == NULL) {
Glenn Kasten2cee4e42015-04-01 11:09:55 -0700305#ifdef HAVE_STDERR
Andy Hung1fa816b2014-06-17 14:18:08 -0700306 fprintf(stderr, "fopen %s failed errno %d\n", path, errno);
Glenn Kasten2cee4e42015-04-01 11:09:55 -0700307#endif
Andy Hung1fa816b2014-06-17 14:18:08 -0700308 return NULL;
309 }
Glenn Kastenc0bd7152013-02-05 13:14:08 -0800310 unsigned char wav[58];
Glenn Kasteneae13dd2013-01-04 11:24:55 -0800311 memset(wav, 0, sizeof(wav));
312 memcpy(wav, "RIFF", 4);
Glenn Kasteneae13dd2013-01-04 11:24:55 -0800313 memcpy(&wav[8], "WAVEfmt ", 8);
Glenn Kastenc0bd7152013-02-05 13:14:08 -0800314 if (sub == SF_FORMAT_FLOAT) {
315 wav[4] = 50; // riffSize
316 wav[16] = 18; // fmtSize
317 wav[20] = WAVE_FORMAT_IEEE_FLOAT;
318 } else {
319 wav[4] = 36; // riffSize
320 wav[16] = 16; // fmtSize
321 wav[20] = WAVE_FORMAT_PCM;
322 }
Glenn Kasteneae13dd2013-01-04 11:24:55 -0800323 wav[22] = info->channels;
324 write4u(&wav[24], info->samplerate);
Glenn Kastenc0bd7152013-02-05 13:14:08 -0800325 unsigned bitsPerSample;
326 switch (sub) {
327 case SF_FORMAT_PCM_16:
328 bitsPerSample = 16;
329 break;
330 case SF_FORMAT_PCM_U8:
331 bitsPerSample = 8;
332 break;
333 case SF_FORMAT_FLOAT:
334 bitsPerSample = 32;
335 break;
336 default: // not reachable
337 bitsPerSample = 0;
338 break;
339 }
Glenn Kasteneae13dd2013-01-04 11:24:55 -0800340 unsigned blockAlignment = (bitsPerSample >> 3) * info->channels;
341 unsigned byteRate = info->samplerate * blockAlignment;
342 write4u(&wav[28], byteRate);
343 wav[32] = blockAlignment;
344 wav[34] = bitsPerSample;
Andy Hungc74f4b72014-06-03 19:15:18 -0700345 size_t extra = 0;
Glenn Kastenc0bd7152013-02-05 13:14:08 -0800346 if (sub == SF_FORMAT_FLOAT) {
347 memcpy(&wav[38], "fact", 4);
348 wav[42] = 4;
349 memcpy(&wav[50], "data", 4);
Andy Hungc74f4b72014-06-03 19:15:18 -0700350 extra = 14;
Glenn Kastenc0bd7152013-02-05 13:14:08 -0800351 } else
352 memcpy(&wav[36], "data", 4);
Glenn Kasteneae13dd2013-01-04 11:24:55 -0800353 // dataSize is initially zero
Andy Hungc74f4b72014-06-03 19:15:18 -0700354 (void) fwrite(wav, 44 + extra, 1, stream);
Glenn Kasteneae13dd2013-01-04 11:24:55 -0800355 SNDFILE *handle = (SNDFILE *) malloc(sizeof(SNDFILE));
356 handle->mode = SFM_WRITE;
357 handle->temp = NULL;
358 handle->stream = stream;
359 handle->bytesPerFrame = blockAlignment;
360 handle->remaining = 0;
361 handle->info = *info;
362 return handle;
363}
364
365SNDFILE *sf_open(const char *path, int mode, SF_INFO *info)
366{
Glenn Kastenc0bd7152013-02-05 13:14:08 -0800367 if (path == NULL || info == NULL) {
Glenn Kasten2cee4e42015-04-01 11:09:55 -0700368#ifdef HAVE_STDERR
Glenn Kastenc0bd7152013-02-05 13:14:08 -0800369 fprintf(stderr, "path=%p info=%p\n", path, info);
Glenn Kasten2cee4e42015-04-01 11:09:55 -0700370#endif
Glenn Kasteneae13dd2013-01-04 11:24:55 -0800371 return NULL;
Glenn Kastenc0bd7152013-02-05 13:14:08 -0800372 }
Glenn Kasteneae13dd2013-01-04 11:24:55 -0800373 switch (mode) {
374 case SFM_READ:
375 return sf_open_read(path, info);
376 case SFM_WRITE:
377 return sf_open_write(path, info);
378 default:
Glenn Kasten2cee4e42015-04-01 11:09:55 -0700379#ifdef HAVE_STDERR
Glenn Kastenc0bd7152013-02-05 13:14:08 -0800380 fprintf(stderr, "mode=%d\n", mode);
Glenn Kasten2cee4e42015-04-01 11:09:55 -0700381#endif
Glenn Kasteneae13dd2013-01-04 11:24:55 -0800382 return NULL;
383 }
384}
385
Glenn Kasten207ec292012-10-30 16:09:18 -0700386void sf_close(SNDFILE *handle)
387{
388 if (handle == NULL)
389 return;
Glenn Kasteneae13dd2013-01-04 11:24:55 -0800390 free(handle->temp);
391 if (handle->mode == SFM_WRITE) {
392 (void) fflush(handle->stream);
393 rewind(handle->stream);
Glenn Kastenc0bd7152013-02-05 13:14:08 -0800394 unsigned char wav[58];
395 size_t extra = (handle->info.format & SF_FORMAT_SUBMASK) == SF_FORMAT_FLOAT ? 14 : 0;
396 (void) fread(wav, 44 + extra, 1, handle->stream);
Glenn Kasteneae13dd2013-01-04 11:24:55 -0800397 unsigned dataSize = handle->remaining * handle->bytesPerFrame;
Glenn Kastenc0bd7152013-02-05 13:14:08 -0800398 write4u(&wav[4], dataSize + 36 + extra); // riffSize
399 write4u(&wav[40 + extra], dataSize); // dataSize
Glenn Kasteneae13dd2013-01-04 11:24:55 -0800400 rewind(handle->stream);
Glenn Kastenc0bd7152013-02-05 13:14:08 -0800401 (void) fwrite(wav, 44 + extra, 1, handle->stream);
Glenn Kasteneae13dd2013-01-04 11:24:55 -0800402 }
Glenn Kasten207ec292012-10-30 16:09:18 -0700403 (void) fclose(handle->stream);
Glenn Kasteneae13dd2013-01-04 11:24:55 -0800404 free(handle);
Glenn Kasten207ec292012-10-30 16:09:18 -0700405}
406
Glenn Kasten36c248b2012-11-12 14:42:31 -0800407sf_count_t sf_readf_short(SNDFILE *handle, short *ptr, sf_count_t desiredFrames)
Glenn Kasten207ec292012-10-30 16:09:18 -0700408{
Glenn Kasteneae13dd2013-01-04 11:24:55 -0800409 if (handle == NULL || handle->mode != SFM_READ || ptr == NULL || !handle->remaining ||
410 desiredFrames <= 0) {
Glenn Kasten207ec292012-10-30 16:09:18 -0700411 return 0;
Glenn Kasteneae13dd2013-01-04 11:24:55 -0800412 }
Glenn Kastenc89cb602014-03-11 09:03:35 -0700413 if (handle->remaining < (size_t) desiredFrames) {
Glenn Kasten207ec292012-10-30 16:09:18 -0700414 desiredFrames = handle->remaining;
Glenn Kastenc89cb602014-03-11 09:03:35 -0700415 }
Glenn Kasten207ec292012-10-30 16:09:18 -0700416 // does not check for numeric overflow
Glenn Kastenc0bd7152013-02-05 13:14:08 -0800417 size_t desiredBytes = desiredFrames * handle->bytesPerFrame;
418 size_t actualBytes;
419 void *temp = NULL;
420 unsigned format = handle->info.format & SF_FORMAT_SUBMASK;
421 if (format == SF_FORMAT_PCM_32 || format == SF_FORMAT_FLOAT) {
422 temp = malloc(desiredBytes);
423 actualBytes = fread(temp, sizeof(char), desiredBytes, handle->stream);
424 } else {
425 actualBytes = fread(ptr, sizeof(char), desiredBytes, handle->stream);
426 }
Glenn Kasten207ec292012-10-30 16:09:18 -0700427 size_t actualFrames = actualBytes / handle->bytesPerFrame;
428 handle->remaining -= actualFrames;
Glenn Kastenc0bd7152013-02-05 13:14:08 -0800429 switch (format) {
Glenn Kasten36c248b2012-11-12 14:42:31 -0800430 case SF_FORMAT_PCM_U8:
431 memcpy_to_i16_from_u8(ptr, (unsigned char *) ptr, actualFrames * handle->info.channels);
432 break;
433 case SF_FORMAT_PCM_16:
434 if (!isLittleEndian())
Glenn Kasten8fdafc92013-02-12 16:23:42 -0800435 my_swab(ptr, actualFrames * handle->info.channels);
Glenn Kasten36c248b2012-11-12 14:42:31 -0800436 break;
Glenn Kastenc0bd7152013-02-05 13:14:08 -0800437 case SF_FORMAT_PCM_32:
438 memcpy_to_i16_from_i32(ptr, (const int *) temp, actualFrames * handle->info.channels);
439 free(temp);
440 break;
441 case SF_FORMAT_FLOAT:
442 memcpy_to_i16_from_float(ptr, (const float *) temp, actualFrames * handle->info.channels);
443 free(temp);
444 break;
445 default:
446 memset(ptr, 0, actualFrames * handle->info.channels * sizeof(short));
447 break;
Glenn Kasten36c248b2012-11-12 14:42:31 -0800448 }
Glenn Kasten207ec292012-10-30 16:09:18 -0700449 return actualFrames;
450}
Glenn Kasteneae13dd2013-01-04 11:24:55 -0800451
Glenn Kastenc0bd7152013-02-05 13:14:08 -0800452sf_count_t sf_readf_float(SNDFILE *handle, float *ptr, sf_count_t desiredFrames)
453{
Glenn Kastenc89cb602014-03-11 09:03:35 -0700454 if (handle == NULL || handle->mode != SFM_READ || ptr == NULL || !handle->remaining ||
455 desiredFrames <= 0) {
456 return 0;
457 }
458 if (handle->remaining < (size_t) desiredFrames) {
459 desiredFrames = handle->remaining;
460 }
461 // does not check for numeric overflow
462 size_t desiredBytes = desiredFrames * handle->bytesPerFrame;
463 size_t actualBytes;
Andy Hung52370162014-05-22 18:17:53 -0700464 void *temp = NULL;
Glenn Kastenc89cb602014-03-11 09:03:35 -0700465 unsigned format = handle->info.format & SF_FORMAT_SUBMASK;
Andy Hung52370162014-05-22 18:17:53 -0700466 if (format == SF_FORMAT_PCM_16 || format == SF_FORMAT_PCM_U8) {
467 temp = malloc(desiredBytes);
468 actualBytes = fread(temp, sizeof(char), desiredBytes, handle->stream);
469 } else {
470 actualBytes = fread(ptr, sizeof(char), desiredBytes, handle->stream);
471 }
Glenn Kastenc89cb602014-03-11 09:03:35 -0700472 size_t actualFrames = actualBytes / handle->bytesPerFrame;
473 handle->remaining -= actualFrames;
474 switch (format) {
Glenn Kastenc89cb602014-03-11 09:03:35 -0700475 case SF_FORMAT_PCM_U8:
Glenn Kastenc89cb602014-03-11 09:03:35 -0700476#if 0
Andy Hung52370162014-05-22 18:17:53 -0700477 // TODO - implement
478 memcpy_to_float_from_u8(ptr, (const unsigned char *) temp,
479 actualFrames * handle->info.channels);
480#endif
481 free(temp);
482 break;
483 case SF_FORMAT_PCM_16:
484 memcpy_to_float_from_i16(ptr, (const short *) temp, actualFrames * handle->info.channels);
485 free(temp);
486 break;
Glenn Kastenc89cb602014-03-11 09:03:35 -0700487 case SF_FORMAT_PCM_32:
488 memcpy_to_float_from_i32(ptr, (const int *) ptr, actualFrames * handle->info.channels);
489 break;
Glenn Kastenc89cb602014-03-11 09:03:35 -0700490 case SF_FORMAT_FLOAT:
491 break;
492 default:
493 memset(ptr, 0, actualFrames * handle->info.channels * sizeof(float));
494 break;
495 }
496 return actualFrames;
Glenn Kastenc0bd7152013-02-05 13:14:08 -0800497}
498
Andy Hung7b937812014-05-22 18:26:07 -0700499sf_count_t sf_readf_int(SNDFILE *handle, int *ptr, sf_count_t desiredFrames)
500{
501 if (handle == NULL || handle->mode != SFM_READ || ptr == NULL || !handle->remaining ||
502 desiredFrames <= 0) {
503 return 0;
504 }
505 if (handle->remaining < (size_t) desiredFrames) {
506 desiredFrames = handle->remaining;
507 }
508 // does not check for numeric overflow
509 size_t desiredBytes = desiredFrames * handle->bytesPerFrame;
510 void *temp = NULL;
511 unsigned format = handle->info.format & SF_FORMAT_SUBMASK;
512 size_t actualBytes;
513 if (format == SF_FORMAT_PCM_16 || format == SF_FORMAT_PCM_U8) {
514 temp = malloc(desiredBytes);
515 actualBytes = fread(temp, sizeof(char), desiredBytes, handle->stream);
516 } else {
517 actualBytes = fread(ptr, sizeof(char), desiredBytes, handle->stream);
518 }
519 size_t actualFrames = actualBytes / handle->bytesPerFrame;
520 handle->remaining -= actualFrames;
521 switch (format) {
522 case SF_FORMAT_PCM_U8:
523#if 0
524 // TODO - implement
525 memcpy_to_i32_from_u8(ptr, (const unsigned char *) temp,
526 actualFrames * handle->info.channels);
527#endif
528 free(temp);
529 break;
530 case SF_FORMAT_PCM_16:
531 memcpy_to_i32_from_i16(ptr, (const short *) temp, actualFrames * handle->info.channels);
532 free(temp);
533 break;
534 case SF_FORMAT_PCM_32:
535 break;
536 case SF_FORMAT_FLOAT:
537 memcpy_to_i32_from_float(ptr, (const float *) ptr, actualFrames * handle->info.channels);
538 break;
539 default:
540 memset(ptr, 0, actualFrames * handle->info.channels * sizeof(int));
541 break;
542 }
543 return actualFrames;
544}
545
Glenn Kasteneae13dd2013-01-04 11:24:55 -0800546sf_count_t sf_writef_short(SNDFILE *handle, const short *ptr, sf_count_t desiredFrames)
547{
548 if (handle == NULL || handle->mode != SFM_WRITE || ptr == NULL || desiredFrames <= 0)
549 return 0;
550 size_t desiredBytes = desiredFrames * handle->bytesPerFrame;
551 size_t actualBytes = 0;
552 switch (handle->info.format & SF_FORMAT_SUBMASK) {
553 case SF_FORMAT_PCM_U8:
554 handle->temp = realloc(handle->temp, desiredBytes);
555 memcpy_to_u8_from_i16(handle->temp, ptr, desiredBytes);
556 actualBytes = fwrite(handle->temp, sizeof(char), desiredBytes, handle->stream);
557 break;
558 case SF_FORMAT_PCM_16:
559 // does not check for numeric overflow
560 if (isLittleEndian()) {
561 actualBytes = fwrite(ptr, sizeof(char), desiredBytes, handle->stream);
562 } else {
563 handle->temp = realloc(handle->temp, desiredBytes);
564 memcpy(handle->temp, ptr, desiredBytes);
Glenn Kasten8fdafc92013-02-12 16:23:42 -0800565 my_swab((short *) handle->temp, desiredFrames * handle->info.channels);
Glenn Kasteneae13dd2013-01-04 11:24:55 -0800566 actualBytes = fwrite(handle->temp, sizeof(char), desiredBytes, handle->stream);
567 }
568 break;
Andy Hunge9b333c2014-06-03 19:22:26 -0700569 case SF_FORMAT_FLOAT:
570 handle->temp = realloc(handle->temp, desiredBytes);
Andy Hung1fa816b2014-06-17 14:18:08 -0700571 memcpy_to_float_from_i16((float *) handle->temp, ptr,
572 desiredFrames * handle->info.channels);
Andy Hunge9b333c2014-06-03 19:22:26 -0700573 actualBytes = fwrite(handle->temp, sizeof(char), desiredBytes, handle->stream);
574 break;
Glenn Kastenc0bd7152013-02-05 13:14:08 -0800575 default:
576 break;
577 }
578 size_t actualFrames = actualBytes / handle->bytesPerFrame;
579 handle->remaining += actualFrames;
580 return actualFrames;
581}
582
583sf_count_t sf_writef_float(SNDFILE *handle, const float *ptr, sf_count_t desiredFrames)
584{
585 if (handle == NULL || handle->mode != SFM_WRITE || ptr == NULL || desiredFrames <= 0)
586 return 0;
587 size_t desiredBytes = desiredFrames * handle->bytesPerFrame;
588 size_t actualBytes = 0;
589 switch (handle->info.format & SF_FORMAT_SUBMASK) {
590 case SF_FORMAT_FLOAT:
591 actualBytes = fwrite(ptr, sizeof(char), desiredBytes, handle->stream);
592 break;
Glenn Kastenc0bd7152013-02-05 13:14:08 -0800593 case SF_FORMAT_PCM_16:
Andy Hunge9b333c2014-06-03 19:22:26 -0700594 handle->temp = realloc(handle->temp, desiredBytes);
Andy Hung1fa816b2014-06-17 14:18:08 -0700595 memcpy_to_i16_from_float((short *) handle->temp, ptr,
596 desiredFrames * handle->info.channels);
Andy Hunge9b333c2014-06-03 19:22:26 -0700597 actualBytes = fwrite(handle->temp, sizeof(char), desiredBytes, handle->stream);
598 break;
599 case SF_FORMAT_PCM_U8: // transcoding from float to byte not yet implemented
Glenn Kastenc0bd7152013-02-05 13:14:08 -0800600 default:
601 break;
Glenn Kasteneae13dd2013-01-04 11:24:55 -0800602 }
603 size_t actualFrames = actualBytes / handle->bytesPerFrame;
604 handle->remaining += actualFrames;
605 return actualFrames;
606}