OSDN Git Service

resolve merge conflicts of 28ce9cd to lmp-dev am: 53d55d7834 am: 009bc279a2 am: 6f219...
[android-x86/system-media.git] / audio_utils / tinysndfile.c
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 #ifdef HAVE_STDERR
20 #include <stdio.h>
21 #endif
22 #include <string.h>
23 #include <errno.h>
24
25 #define WAVE_FORMAT_PCM         1
26 #define WAVE_FORMAT_IEEE_FLOAT  3
27 #define WAVE_FORMAT_EXTENSIBLE  0xFFFE
28
29 struct SNDFILE_ {
30     int mode;
31     uint8_t *temp;  // realloc buffer used for shrinking 16 bits to 8 bits and byte-swapping
32     FILE *stream;
33     size_t bytesPerFrame;
34     size_t remaining;   // frames unread for SFM_READ, frames written for SFM_WRITE
35     SF_INFO info;
36 };
37
38 static unsigned little2u(unsigned char *ptr)
39 {
40     return (ptr[1] << 8) + ptr[0];
41 }
42
43 static unsigned little4u(unsigned char *ptr)
44 {
45     return (ptr[3] << 24) + (ptr[2] << 16) + (ptr[1] << 8) + ptr[0];
46 }
47
48 static int isLittleEndian(void)
49 {
50     static const short one = 1;
51     return *((const char *) &one) == 1;
52 }
53
54 // "swab" conflicts with OS X <string.h>
55 static void my_swab(short *ptr, size_t numToSwap)
56 {
57     while (numToSwap > 0) {
58         *ptr = little2u((unsigned char *) ptr);
59         --numToSwap;
60         ++ptr;
61     }
62 }
63
64 static SNDFILE *sf_open_read(const char *path, SF_INFO *info)
65 {
66     FILE *stream = fopen(path, "rb");
67     if (stream == NULL) {
68 #ifdef HAVE_STDERR
69         fprintf(stderr, "fopen %s failed errno %d\n", path, errno);
70 #endif
71         return NULL;
72     }
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];
82     size_t actual;
83     actual = fread(wav, sizeof(char), sizeof(wav), stream);
84     if (actual < 12) {
85 #ifdef HAVE_STDERR
86         fprintf(stderr, "actual %zu < 44\n", actual);
87 #endif
88         goto close;
89     }
90     if (memcmp(wav, "RIFF", 4)) {
91 #ifdef HAVE_STDERR
92         fprintf(stderr, "wav != RIFF\n");
93 #endif
94         goto close;
95     }
96     unsigned riffSize = little4u(&wav[4]);
97     if (riffSize < 4) {
98 #ifdef HAVE_STDERR
99         fprintf(stderr, "riffSize %u < 4\n", riffSize);
100 #endif
101         goto close;
102     }
103     if (memcmp(&wav[8], "WAVE", 4)) {
104 #ifdef HAVE_STDERR
105         fprintf(stderr, "missing WAVE\n");
106 #endif
107         goto close;
108     }
109     size_t remaining = riffSize - 4;
110     int hadFmt = 0;
111     int hadData = 0;
112     long dataTell = 0L;
113     while (remaining >= 8) {
114         unsigned char chunk[8];
115         actual = fread(chunk, sizeof(char), sizeof(chunk), stream);
116         if (actual != sizeof(chunk)) {
117 #ifdef HAVE_STDERR
118             fprintf(stderr, "actual %zu != %zu\n", actual, sizeof(chunk));
119 #endif
120             goto close;
121         }
122         remaining -= 8;
123         unsigned chunkSize = little4u(&chunk[4]);
124         if (chunkSize > remaining) {
125 #ifdef HAVE_STDERR
126             fprintf(stderr, "chunkSize %u > remaining %zu\n", chunkSize, remaining);
127 #endif
128             goto close;
129         }
130         if (!memcmp(&chunk[0], "fmt ", 4)) {
131             if (hadFmt) {
132 #ifdef HAVE_STDERR
133                 fprintf(stderr, "multiple fmt\n");
134 #endif
135                 goto close;
136             }
137             if (chunkSize < 2) {
138 #ifdef HAVE_STDERR
139                 fprintf(stderr, "chunkSize %u < 2\n", chunkSize);
140 #endif
141                 goto close;
142             }
143             unsigned char fmt[40];
144             actual = fread(fmt, sizeof(char), 2, stream);
145             if (actual != 2) {
146 #ifdef HAVE_STDERR
147                 fprintf(stderr, "actual %zu != 2\n", actual);
148 #endif
149                 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:
162 #ifdef HAVE_STDERR
163                 fprintf(stderr, "unsupported format %u\n", format);
164 #endif
165                 goto close;
166             }
167             if (chunkSize < minSize) {
168 #ifdef HAVE_STDERR
169                 fprintf(stderr, "chunkSize %u < minSize %zu\n", chunkSize, minSize);
170 #endif
171                 goto close;
172             }
173             actual = fread(&fmt[2], sizeof(char), minSize - 2, stream);
174             if (actual != minSize - 2) {
175 #ifdef HAVE_STDERR
176                 fprintf(stderr, "actual %zu != %zu\n", actual, minSize - 16);
177 #endif
178                 goto close;
179             }
180             if (chunkSize > minSize) {
181                 fseek(stream, (long) (chunkSize - minSize), SEEK_CUR);
182             }
183             unsigned channels = little2u(&fmt[2]);
184             // FIXME FCC_8
185             if (channels != 1 && channels != 2 && channels != 4 && channels != 6 && channels != 8) {
186 #ifdef HAVE_STDERR
187                 fprintf(stderr, "unsupported channels %u\n", channels);
188 #endif
189                 goto close;
190             }
191             unsigned samplerate = little4u(&fmt[4]);
192             if (samplerate == 0) {
193 #ifdef HAVE_STDERR
194                 fprintf(stderr, "samplerate %u == 0\n", samplerate);
195 #endif
196                 goto close;
197             }
198             // ignore byte rate
199             // ignore block alignment
200             unsigned bitsPerSample = little2u(&fmt[14]);
201             if (bitsPerSample != 8 && bitsPerSample != 16 && bitsPerSample != 24 &&
202                     bitsPerSample != 32) {
203 #ifdef HAVE_STDERR
204                 fprintf(stderr, "bitsPerSample %u != 8 or 16 or 24 or 32\n", bitsPerSample);
205 #endif
206                 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;
219             case 24:
220                 handle->info.format |= SF_FORMAT_PCM_24;
221                 break;
222             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) {
232 #ifdef HAVE_STDERR
233                 fprintf(stderr, "data not preceded by fmt\n");
234 #endif
235                 goto close;
236             }
237             if (hadData) {
238 #ifdef HAVE_STDERR
239                 fprintf(stderr, "multiple data\n");
240 #endif
241                 goto close;
242             }
243             handle->remaining = chunkSize / handle->bytesPerFrame;
244             handle->info.frames = handle->remaining;
245             dataTell = ftell(stream);
246             if (chunkSize > 0) {
247                 fseek(stream, (long) chunkSize, SEEK_CUR);
248             }
249             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
257 #ifdef HAVE_STDERR
258             fprintf(stderr, "ignoring unknown chunk %c%c%c%c\n",
259                     chunk[0], chunk[1], chunk[2], chunk[3]);
260 #endif
261             if (chunkSize > 0) {
262                 fseek(stream, (long) chunkSize, SEEK_CUR);
263             }
264         }
265         remaining -= chunkSize;
266     }
267     if (remaining > 0) {
268 #ifdef HAVE_STDERR
269         fprintf(stderr, "partial chunk at end of RIFF, remaining %zu\n", remaining);
270 #endif
271         goto close;
272     }
273     if (!hadData) {
274 #ifdef HAVE_STDERR
275         fprintf(stderr, "missing data\n");
276 #endif
277         goto close;
278     }
279     (void) fseek(stream, dataTell, SEEK_SET);
280     *info = handle->info;
281     return handle;
282
283 close:
284     free(handle);
285     fclose(stream);
286     return NULL;
287 }
288
289 static 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
297 static SNDFILE *sf_open_write(const char *path, SF_INFO *info)
298 {
299     int sub = info->format & SF_FORMAT_SUBMASK;
300     if (!(
301             (info->samplerate > 0) &&
302             // FIXME FCC_8
303             (info->channels > 0 && info->channels <= 8) &&
304             ((info->format & SF_FORMAT_TYPEMASK) == SF_FORMAT_WAV) &&
305             (sub == SF_FORMAT_PCM_16 || sub == SF_FORMAT_PCM_U8 || sub == SF_FORMAT_FLOAT ||
306                 sub == SF_FORMAT_PCM_24 || sub == SF_FORMAT_PCM_32)
307           )) {
308         return NULL;
309     }
310     FILE *stream = fopen(path, "w+b");
311     if (stream == NULL) {
312 #ifdef HAVE_STDERR
313         fprintf(stderr, "fopen %s failed errno %d\n", path, errno);
314 #endif
315         return NULL;
316     }
317     unsigned char wav[58];
318     memset(wav, 0, sizeof(wav));
319     memcpy(wav, "RIFF", 4);
320     memcpy(&wav[8], "WAVEfmt ", 8);
321     if (sub == SF_FORMAT_FLOAT) {
322         wav[4] = 50;    // riffSize
323         wav[16] = 18;   // fmtSize
324         wav[20] = WAVE_FORMAT_IEEE_FLOAT;
325     } else {
326         wav[4] = 36;    // riffSize
327         wav[16] = 16;   // fmtSize
328         wav[20] = WAVE_FORMAT_PCM;
329     }
330     wav[22] = info->channels;
331     write4u(&wav[24], info->samplerate);
332     unsigned bitsPerSample;
333     switch (sub) {
334     case SF_FORMAT_PCM_16:
335         bitsPerSample = 16;
336         break;
337     case SF_FORMAT_PCM_U8:
338         bitsPerSample = 8;
339         break;
340     case SF_FORMAT_FLOAT:
341         bitsPerSample = 32;
342         break;
343     case SF_FORMAT_PCM_24:
344         bitsPerSample = 24;
345         break;
346     case SF_FORMAT_PCM_32:
347         bitsPerSample = 32;
348         break;
349     default:    // not reachable
350         bitsPerSample = 0;
351         break;
352     }
353     unsigned blockAlignment = (bitsPerSample >> 3) * info->channels;
354     unsigned byteRate = info->samplerate * blockAlignment;
355     write4u(&wav[28], byteRate);
356     wav[32] = blockAlignment;
357     wav[34] = bitsPerSample;
358     size_t extra = 0;
359     if (sub == SF_FORMAT_FLOAT) {
360         memcpy(&wav[38], "fact", 4);
361         wav[42] = 4;
362         memcpy(&wav[50], "data", 4);
363         extra = 14;
364     } else
365         memcpy(&wav[36], "data", 4);
366     // dataSize is initially zero
367     (void) fwrite(wav, 44 + extra, 1, stream);
368     SNDFILE *handle = (SNDFILE *) malloc(sizeof(SNDFILE));
369     handle->mode = SFM_WRITE;
370     handle->temp = NULL;
371     handle->stream = stream;
372     handle->bytesPerFrame = blockAlignment;
373     handle->remaining = 0;
374     handle->info = *info;
375     return handle;
376 }
377
378 SNDFILE *sf_open(const char *path, int mode, SF_INFO *info)
379 {
380     if (path == NULL || info == NULL) {
381 #ifdef HAVE_STDERR
382         fprintf(stderr, "path=%p info=%p\n", path, info);
383 #endif
384         return NULL;
385     }
386     switch (mode) {
387     case SFM_READ:
388         return sf_open_read(path, info);
389     case SFM_WRITE:
390         return sf_open_write(path, info);
391     default:
392 #ifdef HAVE_STDERR
393         fprintf(stderr, "mode=%d\n", mode);
394 #endif
395         return NULL;
396     }
397 }
398
399 void sf_close(SNDFILE *handle)
400 {
401     if (handle == NULL)
402         return;
403     free(handle->temp);
404     if (handle->mode == SFM_WRITE) {
405         (void) fflush(handle->stream);
406         rewind(handle->stream);
407         unsigned char wav[58];
408         size_t extra = (handle->info.format & SF_FORMAT_SUBMASK) == SF_FORMAT_FLOAT ? 14 : 0;
409         (void) fread(wav, 44 + extra, 1, handle->stream);
410         unsigned dataSize = handle->remaining * handle->bytesPerFrame;
411         write4u(&wav[4], dataSize + 36 + extra);    // riffSize
412         write4u(&wav[40 + extra], dataSize);        // dataSize
413         rewind(handle->stream);
414         (void) fwrite(wav, 44 + extra, 1, handle->stream);
415     }
416     (void) fclose(handle->stream);
417     free(handle);
418 }
419
420 sf_count_t sf_readf_short(SNDFILE *handle, short *ptr, sf_count_t desiredFrames)
421 {
422     if (handle == NULL || handle->mode != SFM_READ || ptr == NULL || !handle->remaining ||
423             desiredFrames <= 0) {
424         return 0;
425     }
426     if (handle->remaining < (size_t) desiredFrames) {
427         desiredFrames = handle->remaining;
428     }
429     // does not check for numeric overflow
430     size_t desiredBytes = desiredFrames * handle->bytesPerFrame;
431     size_t actualBytes;
432     void *temp = NULL;
433     unsigned format = handle->info.format & SF_FORMAT_SUBMASK;
434     if (format == SF_FORMAT_PCM_32 || format == SF_FORMAT_FLOAT || format == SF_FORMAT_PCM_24) {
435         temp = malloc(desiredBytes);
436         actualBytes = fread(temp, sizeof(char), desiredBytes, handle->stream);
437     } else {
438         actualBytes = fread(ptr, sizeof(char), desiredBytes, handle->stream);
439     }
440     size_t actualFrames = actualBytes / handle->bytesPerFrame;
441     handle->remaining -= actualFrames;
442     switch (format) {
443     case SF_FORMAT_PCM_U8:
444         memcpy_to_i16_from_u8(ptr, (unsigned char *) ptr, actualFrames * handle->info.channels);
445         break;
446     case SF_FORMAT_PCM_16:
447         if (!isLittleEndian())
448             my_swab(ptr, actualFrames * handle->info.channels);
449         break;
450     case SF_FORMAT_PCM_32:
451         memcpy_to_i16_from_i32(ptr, (const int *) temp, actualFrames * handle->info.channels);
452         free(temp);
453         break;
454     case SF_FORMAT_FLOAT:
455         memcpy_to_i16_from_float(ptr, (const float *) temp, actualFrames * handle->info.channels);
456         free(temp);
457         break;
458     case SF_FORMAT_PCM_24:
459         memcpy_to_i16_from_p24(ptr, (const uint8_t *) temp, actualFrames * handle->info.channels);
460         free(temp);
461         break;
462     default:
463         memset(ptr, 0, actualFrames * handle->info.channels * sizeof(short));
464         break;
465     }
466     return actualFrames;
467 }
468
469 sf_count_t sf_readf_float(SNDFILE *handle, float *ptr, sf_count_t desiredFrames)
470 {
471     if (handle == NULL || handle->mode != SFM_READ || ptr == NULL || !handle->remaining ||
472             desiredFrames <= 0) {
473         return 0;
474     }
475     if (handle->remaining < (size_t) desiredFrames) {
476         desiredFrames = handle->remaining;
477     }
478     // does not check for numeric overflow
479     size_t desiredBytes = desiredFrames * handle->bytesPerFrame;
480     size_t actualBytes;
481     void *temp = NULL;
482     unsigned format = handle->info.format & SF_FORMAT_SUBMASK;
483     if (format == SF_FORMAT_PCM_16 || format == SF_FORMAT_PCM_U8 || format == SF_FORMAT_PCM_24) {
484         temp = malloc(desiredBytes);
485         actualBytes = fread(temp, sizeof(char), desiredBytes, handle->stream);
486     } else {
487         actualBytes = fread(ptr, sizeof(char), desiredBytes, handle->stream);
488     }
489     size_t actualFrames = actualBytes / handle->bytesPerFrame;
490     handle->remaining -= actualFrames;
491     switch (format) {
492     case SF_FORMAT_PCM_U8:
493 #if 0
494         // TODO - implement
495         memcpy_to_float_from_u8(ptr, (const unsigned char *) temp,
496                 actualFrames * handle->info.channels);
497 #endif
498         free(temp);
499         break;
500     case SF_FORMAT_PCM_16:
501         memcpy_to_float_from_i16(ptr, (const short *) temp, actualFrames * handle->info.channels);
502         free(temp);
503         break;
504     case SF_FORMAT_PCM_32:
505         memcpy_to_float_from_i32(ptr, (const int *) ptr, actualFrames * handle->info.channels);
506         break;
507     case SF_FORMAT_FLOAT:
508         break;
509     case SF_FORMAT_PCM_24:
510         memcpy_to_float_from_p24(ptr, (const uint8_t *) temp, actualFrames * handle->info.channels);
511         free(temp);
512         break;
513     default:
514         memset(ptr, 0, actualFrames * handle->info.channels * sizeof(float));
515         break;
516     }
517     return actualFrames;
518 }
519
520 sf_count_t sf_readf_int(SNDFILE *handle, int *ptr, sf_count_t desiredFrames)
521 {
522     if (handle == NULL || handle->mode != SFM_READ || ptr == NULL || !handle->remaining ||
523             desiredFrames <= 0) {
524         return 0;
525     }
526     if (handle->remaining < (size_t) desiredFrames) {
527         desiredFrames = handle->remaining;
528     }
529     // does not check for numeric overflow
530     size_t desiredBytes = desiredFrames * handle->bytesPerFrame;
531     void *temp = NULL;
532     unsigned format = handle->info.format & SF_FORMAT_SUBMASK;
533     size_t actualBytes;
534     if (format == SF_FORMAT_PCM_16 || format == SF_FORMAT_PCM_U8 || format == SF_FORMAT_PCM_24) {
535         temp = malloc(desiredBytes);
536         actualBytes = fread(temp, sizeof(char), desiredBytes, handle->stream);
537     } else {
538         actualBytes = fread(ptr, sizeof(char), desiredBytes, handle->stream);
539     }
540     size_t actualFrames = actualBytes / handle->bytesPerFrame;
541     handle->remaining -= actualFrames;
542     switch (format) {
543     case SF_FORMAT_PCM_U8:
544 #if 0
545         // TODO - implement
546         memcpy_to_i32_from_u8(ptr, (const unsigned char *) temp,
547                 actualFrames * handle->info.channels);
548 #endif
549         free(temp);
550         break;
551     case SF_FORMAT_PCM_16:
552         memcpy_to_i32_from_i16(ptr, (const short *) temp, actualFrames * handle->info.channels);
553         free(temp);
554         break;
555     case SF_FORMAT_PCM_32:
556         break;
557     case SF_FORMAT_FLOAT:
558         memcpy_to_i32_from_float(ptr, (const float *) ptr, actualFrames * handle->info.channels);
559         break;
560     case SF_FORMAT_PCM_24:
561         memcpy_to_i32_from_p24(ptr, (const uint8_t *) temp, actualFrames * handle->info.channels);
562         free(temp);
563         break;
564     default:
565         memset(ptr, 0, actualFrames * handle->info.channels * sizeof(int));
566         break;
567     }
568     return actualFrames;
569 }
570
571 sf_count_t sf_writef_short(SNDFILE *handle, const short *ptr, sf_count_t desiredFrames)
572 {
573     if (handle == NULL || handle->mode != SFM_WRITE || ptr == NULL || desiredFrames <= 0)
574         return 0;
575     size_t desiredBytes = desiredFrames * handle->bytesPerFrame;
576     size_t actualBytes = 0;
577     switch (handle->info.format & SF_FORMAT_SUBMASK) {
578     case SF_FORMAT_PCM_U8:
579         handle->temp = realloc(handle->temp, desiredBytes);
580         memcpy_to_u8_from_i16(handle->temp, ptr, desiredBytes);
581         actualBytes = fwrite(handle->temp, sizeof(char), desiredBytes, handle->stream);
582         break;
583     case SF_FORMAT_PCM_16:
584         // does not check for numeric overflow
585         if (isLittleEndian()) {
586             actualBytes = fwrite(ptr, sizeof(char), desiredBytes, handle->stream);
587         } else {
588             handle->temp = realloc(handle->temp, desiredBytes);
589             memcpy(handle->temp, ptr, desiredBytes);
590             my_swab((short *) handle->temp, desiredFrames * handle->info.channels);
591             actualBytes = fwrite(handle->temp, sizeof(char), desiredBytes, handle->stream);
592         }
593         break;
594     case SF_FORMAT_FLOAT:
595         handle->temp = realloc(handle->temp, desiredBytes);
596         memcpy_to_float_from_i16((float *) handle->temp, ptr,
597                 desiredFrames * handle->info.channels);
598         actualBytes = fwrite(handle->temp, sizeof(char), desiredBytes, handle->stream);
599         break;
600     default:
601         break;
602     }
603     size_t actualFrames = actualBytes / handle->bytesPerFrame;
604     handle->remaining += actualFrames;
605     return actualFrames;
606 }
607
608 sf_count_t sf_writef_float(SNDFILE *handle, const float *ptr, sf_count_t desiredFrames)
609 {
610     if (handle == NULL || handle->mode != SFM_WRITE || ptr == NULL || desiredFrames <= 0)
611         return 0;
612     size_t desiredBytes = desiredFrames * handle->bytesPerFrame;
613     size_t actualBytes = 0;
614     switch (handle->info.format & SF_FORMAT_SUBMASK) {
615     case SF_FORMAT_FLOAT:
616         actualBytes = fwrite(ptr, sizeof(char), desiredBytes, handle->stream);
617         break;
618     case SF_FORMAT_PCM_16:
619         handle->temp = realloc(handle->temp, desiredBytes);
620         memcpy_to_i16_from_float((short *) handle->temp, ptr,
621                 desiredFrames * handle->info.channels);
622         actualBytes = fwrite(handle->temp, sizeof(char), desiredBytes, handle->stream);
623         break;
624     case SF_FORMAT_PCM_U8:  // transcoding from float to byte not yet implemented
625     default:
626         break;
627     }
628     size_t actualFrames = actualBytes / handle->bytesPerFrame;
629     handle->remaining += actualFrames;
630     return actualFrames;
631 }
632
633 sf_count_t sf_writef_int(SNDFILE *handle, const int *ptr, sf_count_t desiredFrames)
634 {
635     if (handle == NULL || handle->mode != SFM_WRITE || ptr == NULL || desiredFrames <= 0)
636         return 0;
637     size_t desiredBytes = desiredFrames * handle->bytesPerFrame;
638     size_t actualBytes = 0;
639     switch (handle->info.format & SF_FORMAT_SUBMASK) {
640     case SF_FORMAT_PCM_32:
641         actualBytes = fwrite(ptr, sizeof(char), desiredBytes, handle->stream);
642         break;
643     default:    // transcoding from other formats not yet implemented
644         break;
645     }
646     size_t actualFrames = actualBytes / handle->bytesPerFrame;
647     handle->remaining += actualFrames;
648     return actualFrames;
649 }