blob: 30befc5359ecc452dfb7341ef8b1f373c6d3a9d2 [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 Kasten207ec292012-10-30 16:09:18 -070019#include <stdio.h>
20#include <string.h>
Glenn Kastenc0bd7152013-02-05 13:14:08 -080021#include <errno.h>
22
23#define WAVE_FORMAT_PCM 1
24#define WAVE_FORMAT_IEEE_FLOAT 3
25#define WAVE_FORMAT_EXTENSIBLE 0xFFFE
Glenn Kasten207ec292012-10-30 16:09:18 -070026
27struct SNDFILE_ {
Glenn Kasteneae13dd2013-01-04 11:24:55 -080028 int mode;
29 uint8_t *temp; // realloc buffer used for shrinking 16 bits to 8 bits and byte-swapping
Glenn Kasten207ec292012-10-30 16:09:18 -070030 FILE *stream;
31 size_t bytesPerFrame;
Glenn Kasteneae13dd2013-01-04 11:24:55 -080032 size_t remaining; // frames unread for SFM_READ, frames written for SFM_WRITE
Glenn Kasten207ec292012-10-30 16:09:18 -070033 SF_INFO info;
34};
35
36static unsigned little2u(unsigned char *ptr)
37{
38 return (ptr[1] << 8) + ptr[0];
39}
40
41static unsigned little4u(unsigned char *ptr)
42{
43 return (ptr[3] << 24) + (ptr[2] << 16) + (ptr[1] << 8) + ptr[0];
44}
45
46static int isLittleEndian(void)
47{
48 static const short one = 1;
49 return *((const char *) &one) == 1;
50}
51
Glenn Kasten8fdafc92013-02-12 16:23:42 -080052// "swab" conflicts with OS X <string.h>
53static void my_swab(short *ptr, size_t numToSwap)
Glenn Kasten207ec292012-10-30 16:09:18 -070054{
55 while (numToSwap > 0) {
56 *ptr = little2u((unsigned char *) ptr);
57 --numToSwap;
58 ++ptr;
59 }
60}
61
Glenn Kasteneae13dd2013-01-04 11:24:55 -080062static SNDFILE *sf_open_read(const char *path, SF_INFO *info)
Glenn Kasten207ec292012-10-30 16:09:18 -070063{
Glenn Kasten207ec292012-10-30 16:09:18 -070064 FILE *stream = fopen(path, "rb");
Glenn Kastenc0bd7152013-02-05 13:14:08 -080065 if (stream == NULL) {
66 fprintf(stderr, "fopen %s failed errno %d\n", path, errno);
Glenn Kasten207ec292012-10-30 16:09:18 -070067 return NULL;
Glenn Kastenc0bd7152013-02-05 13:14:08 -080068 }
69
70 SNDFILE *handle = (SNDFILE *) malloc(sizeof(SNDFILE));
71 handle->mode = SFM_READ;
72 handle->temp = NULL;
73 handle->stream = stream;
74 handle->info.format = SF_FORMAT_WAV;
75
76 // don't attempt to parse all valid forms, just the most common ones
77 unsigned char wav[12];
Glenn Kasten207ec292012-10-30 16:09:18 -070078 size_t actual;
79 actual = fread(wav, sizeof(char), sizeof(wav), stream);
Glenn Kastenc0bd7152013-02-05 13:14:08 -080080 if (actual < 12) {
Kévin PETIT96d859c2014-03-26 11:56:32 +000081 fprintf(stderr, "actual %zu < 44\n", actual);
Glenn Kastenc0bd7152013-02-05 13:14:08 -080082 goto close;
Glenn Kasten207ec292012-10-30 16:09:18 -070083 }
Glenn Kastenc0bd7152013-02-05 13:14:08 -080084 if (memcmp(wav, "RIFF", 4)) {
85 fprintf(stderr, "wav != RIFF\n");
86 goto close;
87 }
88 unsigned riffSize = little4u(&wav[4]);
89 if (riffSize < 4) {
90 fprintf(stderr, "riffSize %u < 4\n", riffSize);
91 goto close;
92 }
93 if (memcmp(&wav[8], "WAVE", 4)) {
94 fprintf(stderr, "missing WAVE\n");
95 goto close;
96 }
97 size_t remaining = riffSize - 4;
98 int hadFmt = 0;
99 int hadData = 0;
Glenn Kasten0c3baaa2014-09-29 11:22:26 -0700100 long dataTell = 0L;
Glenn Kastenc0bd7152013-02-05 13:14:08 -0800101 while (remaining >= 8) {
102 unsigned char chunk[8];
103 actual = fread(chunk, sizeof(char), sizeof(chunk), stream);
104 if (actual != sizeof(chunk)) {
Kévin PETIT96d859c2014-03-26 11:56:32 +0000105 fprintf(stderr, "actual %zu != %zu\n", actual, sizeof(chunk));
Glenn Kastenc0bd7152013-02-05 13:14:08 -0800106 goto close;
107 }
108 remaining -= 8;
109 unsigned chunkSize = little4u(&chunk[4]);
110 if (chunkSize > remaining) {
Kévin PETIT96d859c2014-03-26 11:56:32 +0000111 fprintf(stderr, "chunkSize %u > remaining %zu\n", chunkSize, remaining);
Glenn Kastenc0bd7152013-02-05 13:14:08 -0800112 goto close;
113 }
114 if (!memcmp(&chunk[0], "fmt ", 4)) {
115 if (hadFmt) {
116 fprintf(stderr, "multiple fmt\n");
117 goto close;
118 }
119 if (chunkSize < 2) {
120 fprintf(stderr, "chunkSize %u < 2\n", chunkSize);
121 goto close;
122 }
123 unsigned char fmt[40];
124 actual = fread(fmt, sizeof(char), 2, stream);
125 if (actual != 2) {
Kévin PETIT96d859c2014-03-26 11:56:32 +0000126 fprintf(stderr, "actual %zu != 2\n", actual);
Glenn Kastenc0bd7152013-02-05 13:14:08 -0800127 goto close;
128 }
129 unsigned format = little2u(&fmt[0]);
130 size_t minSize = 0;
131 switch (format) {
132 case WAVE_FORMAT_PCM:
133 case WAVE_FORMAT_IEEE_FLOAT:
134 minSize = 16;
135 break;
136 case WAVE_FORMAT_EXTENSIBLE:
137 minSize = 40;
138 break;
139 default:
140 fprintf(stderr, "unsupported format %u\n", format);
141 goto close;
142 }
143 if (chunkSize < minSize) {
Kévin PETIT96d859c2014-03-26 11:56:32 +0000144 fprintf(stderr, "chunkSize %u < minSize %zu\n", chunkSize, minSize);
Glenn Kastenc0bd7152013-02-05 13:14:08 -0800145 goto close;
146 }
147 actual = fread(&fmt[2], sizeof(char), minSize - 2, stream);
148 if (actual != minSize - 2) {
Kévin PETIT96d859c2014-03-26 11:56:32 +0000149 fprintf(stderr, "actual %zu != %zu\n", actual, minSize - 16);
Glenn Kastenc0bd7152013-02-05 13:14:08 -0800150 goto close;
151 }
152 if (chunkSize > minSize) {
153 fseek(stream, (long) (chunkSize - minSize), SEEK_CUR);
154 }
155 unsigned channels = little2u(&fmt[2]);
Glenn Kasten7c1930a2014-05-07 11:20:55 -0700156 if (channels != 1 && channels != 2 && channels != 4 && channels != 6 && channels != 8) {
157 fprintf(stderr, "unsupported channels %u\n", channels);
Glenn Kastenc0bd7152013-02-05 13:14:08 -0800158 goto close;
159 }
160 unsigned samplerate = little4u(&fmt[4]);
161 if (samplerate == 0) {
162 fprintf(stderr, "samplerate %u == 0\n", samplerate);
163 goto close;
164 }
165 // ignore byte rate
166 // ignore block alignment
167 unsigned bitsPerSample = little2u(&fmt[14]);
168 if (bitsPerSample != 8 && bitsPerSample != 16 && bitsPerSample != 32) {
169 fprintf(stderr, "bitsPerSample %u != 8 or 16 or 32\n", bitsPerSample);
170 goto close;
171 }
172 unsigned bytesPerFrame = (bitsPerSample >> 3) * channels;
173 handle->bytesPerFrame = bytesPerFrame;
174 handle->info.samplerate = samplerate;
175 handle->info.channels = channels;
176 switch (bitsPerSample) {
177 case 8:
178 handle->info.format |= SF_FORMAT_PCM_U8;
179 break;
180 case 16:
181 handle->info.format |= SF_FORMAT_PCM_16;
182 break;
183 case 32:
184 if (format == WAVE_FORMAT_IEEE_FLOAT)
185 handle->info.format |= SF_FORMAT_FLOAT;
186 else
187 handle->info.format |= SF_FORMAT_PCM_32;
188 break;
189 }
190 hadFmt = 1;
191 } else if (!memcmp(&chunk[0], "data", 4)) {
192 if (!hadFmt) {
193 fprintf(stderr, "data not preceded by fmt\n");
194 goto close;
195 }
196 if (hadData) {
197 fprintf(stderr, "multiple data\n");
198 goto close;
199 }
200 handle->remaining = chunkSize / handle->bytesPerFrame;
201 handle->info.frames = handle->remaining;
Glenn Kasten0c3baaa2014-09-29 11:22:26 -0700202 dataTell = ftell(stream);
203 if (chunkSize > 0) {
204 fseek(stream, (long) chunkSize, SEEK_CUR);
205 }
Glenn Kastenc0bd7152013-02-05 13:14:08 -0800206 hadData = 1;
207 } else if (!memcmp(&chunk[0], "fact", 4)) {
208 // ignore fact
209 if (chunkSize > 0) {
210 fseek(stream, (long) chunkSize, SEEK_CUR);
211 }
212 } else {
213 // ignore unknown chunk
214 fprintf(stderr, "ignoring unknown chunk %c%c%c%c\n",
215 chunk[0], chunk[1], chunk[2], chunk[3]);
216 if (chunkSize > 0) {
217 fseek(stream, (long) chunkSize, SEEK_CUR);
218 }
219 }
220 remaining -= chunkSize;
221 }
222 if (remaining > 0) {
Kévin PETIT96d859c2014-03-26 11:56:32 +0000223 fprintf(stderr, "partial chunk at end of RIFF, remaining %zu\n", remaining);
Glenn Kastenc0bd7152013-02-05 13:14:08 -0800224 goto close;
225 }
226 if (!hadData) {
227 fprintf(stderr, "missing data\n");
228 goto close;
229 }
Glenn Kasten0c3baaa2014-09-29 11:22:26 -0700230 (void) fseek(stream, dataTell, SEEK_SET);
Glenn Kastenc0bd7152013-02-05 13:14:08 -0800231 *info = handle->info;
232 return handle;
233
234close:
235 free(handle);
236 fclose(stream);
Glenn Kasten207ec292012-10-30 16:09:18 -0700237 return NULL;
238}
239
Glenn Kasteneae13dd2013-01-04 11:24:55 -0800240static void write4u(unsigned char *ptr, unsigned u)
241{
242 ptr[0] = u;
243 ptr[1] = u >> 8;
244 ptr[2] = u >> 16;
245 ptr[3] = u >> 24;
246}
247
248static SNDFILE *sf_open_write(const char *path, SF_INFO *info)
249{
Glenn Kastenc0bd7152013-02-05 13:14:08 -0800250 int sub = info->format & SF_FORMAT_SUBMASK;
Glenn Kasteneae13dd2013-01-04 11:24:55 -0800251 if (!(
252 (info->samplerate > 0) &&
Andy Hung7095bc72014-04-09 18:52:06 -0700253 (info->channels > 0 && info->channels <= 8) &&
Glenn Kasteneae13dd2013-01-04 11:24:55 -0800254 ((info->format & SF_FORMAT_TYPEMASK) == SF_FORMAT_WAV) &&
Glenn Kastenc0bd7152013-02-05 13:14:08 -0800255 (sub == SF_FORMAT_PCM_16 || sub == SF_FORMAT_PCM_U8 || sub == SF_FORMAT_FLOAT)
Glenn Kasteneae13dd2013-01-04 11:24:55 -0800256 )) {
257 return NULL;
258 }
259 FILE *stream = fopen(path, "w+b");
Andy Hung1fa816b2014-06-17 14:18:08 -0700260 if (stream == NULL) {
261 fprintf(stderr, "fopen %s failed errno %d\n", path, errno);
262 return NULL;
263 }
Glenn Kastenc0bd7152013-02-05 13:14:08 -0800264 unsigned char wav[58];
Glenn Kasteneae13dd2013-01-04 11:24:55 -0800265 memset(wav, 0, sizeof(wav));
266 memcpy(wav, "RIFF", 4);
Glenn Kasteneae13dd2013-01-04 11:24:55 -0800267 memcpy(&wav[8], "WAVEfmt ", 8);
Glenn Kastenc0bd7152013-02-05 13:14:08 -0800268 if (sub == SF_FORMAT_FLOAT) {
269 wav[4] = 50; // riffSize
270 wav[16] = 18; // fmtSize
271 wav[20] = WAVE_FORMAT_IEEE_FLOAT;
272 } else {
273 wav[4] = 36; // riffSize
274 wav[16] = 16; // fmtSize
275 wav[20] = WAVE_FORMAT_PCM;
276 }
Glenn Kasteneae13dd2013-01-04 11:24:55 -0800277 wav[22] = info->channels;
278 write4u(&wav[24], info->samplerate);
Glenn Kastenc0bd7152013-02-05 13:14:08 -0800279 unsigned bitsPerSample;
280 switch (sub) {
281 case SF_FORMAT_PCM_16:
282 bitsPerSample = 16;
283 break;
284 case SF_FORMAT_PCM_U8:
285 bitsPerSample = 8;
286 break;
287 case SF_FORMAT_FLOAT:
288 bitsPerSample = 32;
289 break;
290 default: // not reachable
291 bitsPerSample = 0;
292 break;
293 }
Glenn Kasteneae13dd2013-01-04 11:24:55 -0800294 unsigned blockAlignment = (bitsPerSample >> 3) * info->channels;
295 unsigned byteRate = info->samplerate * blockAlignment;
296 write4u(&wav[28], byteRate);
297 wav[32] = blockAlignment;
298 wav[34] = bitsPerSample;
Andy Hungc74f4b72014-06-03 19:15:18 -0700299 size_t extra = 0;
Glenn Kastenc0bd7152013-02-05 13:14:08 -0800300 if (sub == SF_FORMAT_FLOAT) {
301 memcpy(&wav[38], "fact", 4);
302 wav[42] = 4;
303 memcpy(&wav[50], "data", 4);
Andy Hungc74f4b72014-06-03 19:15:18 -0700304 extra = 14;
Glenn Kastenc0bd7152013-02-05 13:14:08 -0800305 } else
306 memcpy(&wav[36], "data", 4);
Glenn Kasteneae13dd2013-01-04 11:24:55 -0800307 // dataSize is initially zero
Andy Hungc74f4b72014-06-03 19:15:18 -0700308 (void) fwrite(wav, 44 + extra, 1, stream);
Glenn Kasteneae13dd2013-01-04 11:24:55 -0800309 SNDFILE *handle = (SNDFILE *) malloc(sizeof(SNDFILE));
310 handle->mode = SFM_WRITE;
311 handle->temp = NULL;
312 handle->stream = stream;
313 handle->bytesPerFrame = blockAlignment;
314 handle->remaining = 0;
315 handle->info = *info;
316 return handle;
317}
318
319SNDFILE *sf_open(const char *path, int mode, SF_INFO *info)
320{
Glenn Kastenc0bd7152013-02-05 13:14:08 -0800321 if (path == NULL || info == NULL) {
322 fprintf(stderr, "path=%p info=%p\n", path, info);
Glenn Kasteneae13dd2013-01-04 11:24:55 -0800323 return NULL;
Glenn Kastenc0bd7152013-02-05 13:14:08 -0800324 }
Glenn Kasteneae13dd2013-01-04 11:24:55 -0800325 switch (mode) {
326 case SFM_READ:
327 return sf_open_read(path, info);
328 case SFM_WRITE:
329 return sf_open_write(path, info);
330 default:
Glenn Kastenc0bd7152013-02-05 13:14:08 -0800331 fprintf(stderr, "mode=%d\n", mode);
Glenn Kasteneae13dd2013-01-04 11:24:55 -0800332 return NULL;
333 }
334}
335
Glenn Kasten207ec292012-10-30 16:09:18 -0700336void sf_close(SNDFILE *handle)
337{
338 if (handle == NULL)
339 return;
Glenn Kasteneae13dd2013-01-04 11:24:55 -0800340 free(handle->temp);
341 if (handle->mode == SFM_WRITE) {
342 (void) fflush(handle->stream);
343 rewind(handle->stream);
Glenn Kastenc0bd7152013-02-05 13:14:08 -0800344 unsigned char wav[58];
345 size_t extra = (handle->info.format & SF_FORMAT_SUBMASK) == SF_FORMAT_FLOAT ? 14 : 0;
346 (void) fread(wav, 44 + extra, 1, handle->stream);
Glenn Kasteneae13dd2013-01-04 11:24:55 -0800347 unsigned dataSize = handle->remaining * handle->bytesPerFrame;
Glenn Kastenc0bd7152013-02-05 13:14:08 -0800348 write4u(&wav[4], dataSize + 36 + extra); // riffSize
349 write4u(&wav[40 + extra], dataSize); // dataSize
Glenn Kasteneae13dd2013-01-04 11:24:55 -0800350 rewind(handle->stream);
Glenn Kastenc0bd7152013-02-05 13:14:08 -0800351 (void) fwrite(wav, 44 + extra, 1, handle->stream);
Glenn Kasteneae13dd2013-01-04 11:24:55 -0800352 }
Glenn Kasten207ec292012-10-30 16:09:18 -0700353 (void) fclose(handle->stream);
Glenn Kasteneae13dd2013-01-04 11:24:55 -0800354 free(handle);
Glenn Kasten207ec292012-10-30 16:09:18 -0700355}
356
Glenn Kasten36c248b2012-11-12 14:42:31 -0800357sf_count_t sf_readf_short(SNDFILE *handle, short *ptr, sf_count_t desiredFrames)
Glenn Kasten207ec292012-10-30 16:09:18 -0700358{
Glenn Kasteneae13dd2013-01-04 11:24:55 -0800359 if (handle == NULL || handle->mode != SFM_READ || ptr == NULL || !handle->remaining ||
360 desiredFrames <= 0) {
Glenn Kasten207ec292012-10-30 16:09:18 -0700361 return 0;
Glenn Kasteneae13dd2013-01-04 11:24:55 -0800362 }
Glenn Kastenc89cb602014-03-11 09:03:35 -0700363 if (handle->remaining < (size_t) desiredFrames) {
Glenn Kasten207ec292012-10-30 16:09:18 -0700364 desiredFrames = handle->remaining;
Glenn Kastenc89cb602014-03-11 09:03:35 -0700365 }
Glenn Kasten207ec292012-10-30 16:09:18 -0700366 // does not check for numeric overflow
Glenn Kastenc0bd7152013-02-05 13:14:08 -0800367 size_t desiredBytes = desiredFrames * handle->bytesPerFrame;
368 size_t actualBytes;
369 void *temp = NULL;
370 unsigned format = handle->info.format & SF_FORMAT_SUBMASK;
371 if (format == SF_FORMAT_PCM_32 || format == SF_FORMAT_FLOAT) {
372 temp = malloc(desiredBytes);
373 actualBytes = fread(temp, sizeof(char), desiredBytes, handle->stream);
374 } else {
375 actualBytes = fread(ptr, sizeof(char), desiredBytes, handle->stream);
376 }
Glenn Kasten207ec292012-10-30 16:09:18 -0700377 size_t actualFrames = actualBytes / handle->bytesPerFrame;
378 handle->remaining -= actualFrames;
Glenn Kastenc0bd7152013-02-05 13:14:08 -0800379 switch (format) {
Glenn Kasten36c248b2012-11-12 14:42:31 -0800380 case SF_FORMAT_PCM_U8:
381 memcpy_to_i16_from_u8(ptr, (unsigned char *) ptr, actualFrames * handle->info.channels);
382 break;
383 case SF_FORMAT_PCM_16:
384 if (!isLittleEndian())
Glenn Kasten8fdafc92013-02-12 16:23:42 -0800385 my_swab(ptr, actualFrames * handle->info.channels);
Glenn Kasten36c248b2012-11-12 14:42:31 -0800386 break;
Glenn Kastenc0bd7152013-02-05 13:14:08 -0800387 case SF_FORMAT_PCM_32:
388 memcpy_to_i16_from_i32(ptr, (const int *) temp, actualFrames * handle->info.channels);
389 free(temp);
390 break;
391 case SF_FORMAT_FLOAT:
392 memcpy_to_i16_from_float(ptr, (const float *) temp, actualFrames * handle->info.channels);
393 free(temp);
394 break;
395 default:
396 memset(ptr, 0, actualFrames * handle->info.channels * sizeof(short));
397 break;
Glenn Kasten36c248b2012-11-12 14:42:31 -0800398 }
Glenn Kasten207ec292012-10-30 16:09:18 -0700399 return actualFrames;
400}
Glenn Kasteneae13dd2013-01-04 11:24:55 -0800401
Glenn Kastenc0bd7152013-02-05 13:14:08 -0800402sf_count_t sf_readf_float(SNDFILE *handle, float *ptr, sf_count_t desiredFrames)
403{
Glenn Kastenc89cb602014-03-11 09:03:35 -0700404 if (handle == NULL || handle->mode != SFM_READ || ptr == NULL || !handle->remaining ||
405 desiredFrames <= 0) {
406 return 0;
407 }
408 if (handle->remaining < (size_t) desiredFrames) {
409 desiredFrames = handle->remaining;
410 }
411 // does not check for numeric overflow
412 size_t desiredBytes = desiredFrames * handle->bytesPerFrame;
413 size_t actualBytes;
Andy Hung52370162014-05-22 18:17:53 -0700414 void *temp = NULL;
Glenn Kastenc89cb602014-03-11 09:03:35 -0700415 unsigned format = handle->info.format & SF_FORMAT_SUBMASK;
Andy Hung52370162014-05-22 18:17:53 -0700416 if (format == SF_FORMAT_PCM_16 || format == SF_FORMAT_PCM_U8) {
417 temp = malloc(desiredBytes);
418 actualBytes = fread(temp, sizeof(char), desiredBytes, handle->stream);
419 } else {
420 actualBytes = fread(ptr, sizeof(char), desiredBytes, handle->stream);
421 }
Glenn Kastenc89cb602014-03-11 09:03:35 -0700422 size_t actualFrames = actualBytes / handle->bytesPerFrame;
423 handle->remaining -= actualFrames;
424 switch (format) {
Glenn Kastenc89cb602014-03-11 09:03:35 -0700425 case SF_FORMAT_PCM_U8:
Glenn Kastenc89cb602014-03-11 09:03:35 -0700426#if 0
Andy Hung52370162014-05-22 18:17:53 -0700427 // TODO - implement
428 memcpy_to_float_from_u8(ptr, (const unsigned char *) temp,
429 actualFrames * handle->info.channels);
430#endif
431 free(temp);
432 break;
433 case SF_FORMAT_PCM_16:
434 memcpy_to_float_from_i16(ptr, (const short *) temp, actualFrames * handle->info.channels);
435 free(temp);
436 break;
Glenn Kastenc89cb602014-03-11 09:03:35 -0700437 case SF_FORMAT_PCM_32:
438 memcpy_to_float_from_i32(ptr, (const int *) ptr, actualFrames * handle->info.channels);
439 break;
Glenn Kastenc89cb602014-03-11 09:03:35 -0700440 case SF_FORMAT_FLOAT:
441 break;
442 default:
443 memset(ptr, 0, actualFrames * handle->info.channels * sizeof(float));
444 break;
445 }
446 return actualFrames;
Glenn Kastenc0bd7152013-02-05 13:14:08 -0800447}
448
Andy Hung7b937812014-05-22 18:26:07 -0700449sf_count_t sf_readf_int(SNDFILE *handle, int *ptr, sf_count_t desiredFrames)
450{
451 if (handle == NULL || handle->mode != SFM_READ || ptr == NULL || !handle->remaining ||
452 desiredFrames <= 0) {
453 return 0;
454 }
455 if (handle->remaining < (size_t) desiredFrames) {
456 desiredFrames = handle->remaining;
457 }
458 // does not check for numeric overflow
459 size_t desiredBytes = desiredFrames * handle->bytesPerFrame;
460 void *temp = NULL;
461 unsigned format = handle->info.format & SF_FORMAT_SUBMASK;
462 size_t actualBytes;
463 if (format == SF_FORMAT_PCM_16 || format == SF_FORMAT_PCM_U8) {
464 temp = malloc(desiredBytes);
465 actualBytes = fread(temp, sizeof(char), desiredBytes, handle->stream);
466 } else {
467 actualBytes = fread(ptr, sizeof(char), desiredBytes, handle->stream);
468 }
469 size_t actualFrames = actualBytes / handle->bytesPerFrame;
470 handle->remaining -= actualFrames;
471 switch (format) {
472 case SF_FORMAT_PCM_U8:
473#if 0
474 // TODO - implement
475 memcpy_to_i32_from_u8(ptr, (const unsigned char *) temp,
476 actualFrames * handle->info.channels);
477#endif
478 free(temp);
479 break;
480 case SF_FORMAT_PCM_16:
481 memcpy_to_i32_from_i16(ptr, (const short *) temp, actualFrames * handle->info.channels);
482 free(temp);
483 break;
484 case SF_FORMAT_PCM_32:
485 break;
486 case SF_FORMAT_FLOAT:
487 memcpy_to_i32_from_float(ptr, (const float *) ptr, actualFrames * handle->info.channels);
488 break;
489 default:
490 memset(ptr, 0, actualFrames * handle->info.channels * sizeof(int));
491 break;
492 }
493 return actualFrames;
494}
495
Glenn Kasteneae13dd2013-01-04 11:24:55 -0800496sf_count_t sf_writef_short(SNDFILE *handle, const short *ptr, sf_count_t desiredFrames)
497{
498 if (handle == NULL || handle->mode != SFM_WRITE || ptr == NULL || desiredFrames <= 0)
499 return 0;
500 size_t desiredBytes = desiredFrames * handle->bytesPerFrame;
501 size_t actualBytes = 0;
502 switch (handle->info.format & SF_FORMAT_SUBMASK) {
503 case SF_FORMAT_PCM_U8:
504 handle->temp = realloc(handle->temp, desiredBytes);
505 memcpy_to_u8_from_i16(handle->temp, ptr, desiredBytes);
506 actualBytes = fwrite(handle->temp, sizeof(char), desiredBytes, handle->stream);
507 break;
508 case SF_FORMAT_PCM_16:
509 // does not check for numeric overflow
510 if (isLittleEndian()) {
511 actualBytes = fwrite(ptr, sizeof(char), desiredBytes, handle->stream);
512 } else {
513 handle->temp = realloc(handle->temp, desiredBytes);
514 memcpy(handle->temp, ptr, desiredBytes);
Glenn Kasten8fdafc92013-02-12 16:23:42 -0800515 my_swab((short *) handle->temp, desiredFrames * handle->info.channels);
Glenn Kasteneae13dd2013-01-04 11:24:55 -0800516 actualBytes = fwrite(handle->temp, sizeof(char), desiredBytes, handle->stream);
517 }
518 break;
Andy Hunge9b333c2014-06-03 19:22:26 -0700519 case SF_FORMAT_FLOAT:
520 handle->temp = realloc(handle->temp, desiredBytes);
Andy Hung1fa816b2014-06-17 14:18:08 -0700521 memcpy_to_float_from_i16((float *) handle->temp, ptr,
522 desiredFrames * handle->info.channels);
Andy Hunge9b333c2014-06-03 19:22:26 -0700523 actualBytes = fwrite(handle->temp, sizeof(char), desiredBytes, handle->stream);
524 break;
Glenn Kastenc0bd7152013-02-05 13:14:08 -0800525 default:
526 break;
527 }
528 size_t actualFrames = actualBytes / handle->bytesPerFrame;
529 handle->remaining += actualFrames;
530 return actualFrames;
531}
532
533sf_count_t sf_writef_float(SNDFILE *handle, const float *ptr, sf_count_t desiredFrames)
534{
535 if (handle == NULL || handle->mode != SFM_WRITE || ptr == NULL || desiredFrames <= 0)
536 return 0;
537 size_t desiredBytes = desiredFrames * handle->bytesPerFrame;
538 size_t actualBytes = 0;
539 switch (handle->info.format & SF_FORMAT_SUBMASK) {
540 case SF_FORMAT_FLOAT:
541 actualBytes = fwrite(ptr, sizeof(char), desiredBytes, handle->stream);
542 break;
Glenn Kastenc0bd7152013-02-05 13:14:08 -0800543 case SF_FORMAT_PCM_16:
Andy Hunge9b333c2014-06-03 19:22:26 -0700544 handle->temp = realloc(handle->temp, desiredBytes);
Andy Hung1fa816b2014-06-17 14:18:08 -0700545 memcpy_to_i16_from_float((short *) handle->temp, ptr,
546 desiredFrames * handle->info.channels);
Andy Hunge9b333c2014-06-03 19:22:26 -0700547 actualBytes = fwrite(handle->temp, sizeof(char), desiredBytes, handle->stream);
548 break;
549 case SF_FORMAT_PCM_U8: // transcoding from float to byte not yet implemented
Glenn Kastenc0bd7152013-02-05 13:14:08 -0800550 default:
551 break;
Glenn Kasteneae13dd2013-01-04 11:24:55 -0800552 }
553 size_t actualFrames = actualBytes / handle->bytesPerFrame;
554 handle->remaining += actualFrames;
555 return actualFrames;
556}