OSDN Git Service

Merge "Add an init.rc file for brilloaudioservice." am: 91ce424587
[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 <system/audio.h>
18 #include <audio_utils/sndfile.h>
19 #include <audio_utils/primitives.h>
20 #ifdef HAVE_STDERR
21 #include <stdio.h>
22 #endif
23 #include <string.h>
24 #include <errno.h>
25
26 #define WAVE_FORMAT_PCM         1
27 #define WAVE_FORMAT_IEEE_FLOAT  3
28 #define WAVE_FORMAT_EXTENSIBLE  0xFFFE
29
30 struct SNDFILE_ {
31     int mode;
32     uint8_t *temp;  // realloc buffer used for shrinking 16 bits to 8 bits and byte-swapping
33     FILE *stream;
34     size_t bytesPerFrame;
35     size_t remaining;   // frames unread for SFM_READ, frames written for SFM_WRITE
36     SF_INFO info;
37 };
38
39 static unsigned little2u(unsigned char *ptr)
40 {
41     return (ptr[1] << 8) + ptr[0];
42 }
43
44 static unsigned little4u(unsigned char *ptr)
45 {
46     return (ptr[3] << 24) + (ptr[2] << 16) + (ptr[1] << 8) + ptr[0];
47 }
48
49 static int isLittleEndian(void)
50 {
51     static const short one = 1;
52     return *((const char *) &one) == 1;
53 }
54
55 // "swab" conflicts with OS X <string.h>
56 static void my_swab(short *ptr, size_t numToSwap)
57 {
58     while (numToSwap > 0) {
59         *ptr = little2u((unsigned char *) ptr);
60         --numToSwap;
61         ++ptr;
62     }
63 }
64
65 static SNDFILE *sf_open_read(const char *path, SF_INFO *info)
66 {
67     FILE *stream = fopen(path, "rb");
68     if (stream == NULL) {
69 #ifdef HAVE_STDERR
70         fprintf(stderr, "fopen %s failed errno %d\n", path, errno);
71 #endif
72         return NULL;
73     }
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];
83     size_t actual;
84     actual = fread(wav, sizeof(char), sizeof(wav), stream);
85     if (actual < 12) {
86 #ifdef HAVE_STDERR
87         fprintf(stderr, "actual %zu < 44\n", actual);
88 #endif
89         goto close;
90     }
91     if (memcmp(wav, "RIFF", 4)) {
92 #ifdef HAVE_STDERR
93         fprintf(stderr, "wav != RIFF\n");
94 #endif
95         goto close;
96     }
97     unsigned riffSize = little4u(&wav[4]);
98     if (riffSize < 4) {
99 #ifdef HAVE_STDERR
100         fprintf(stderr, "riffSize %u < 4\n", riffSize);
101 #endif
102         goto close;
103     }
104     if (memcmp(&wav[8], "WAVE", 4)) {
105 #ifdef HAVE_STDERR
106         fprintf(stderr, "missing WAVE\n");
107 #endif
108         goto close;
109     }
110     size_t remaining = riffSize - 4;
111     int hadFmt = 0;
112     int hadData = 0;
113     long dataTell = 0L;
114     while (remaining >= 8) {
115         unsigned char chunk[8];
116         actual = fread(chunk, sizeof(char), sizeof(chunk), stream);
117         if (actual != sizeof(chunk)) {
118 #ifdef HAVE_STDERR
119             fprintf(stderr, "actual %zu != %zu\n", actual, sizeof(chunk));
120 #endif
121             goto close;
122         }
123         remaining -= 8;
124         unsigned chunkSize = little4u(&chunk[4]);
125         if (chunkSize > remaining) {
126 #ifdef HAVE_STDERR
127             fprintf(stderr, "chunkSize %u > remaining %zu\n", chunkSize, remaining);
128 #endif
129             goto close;
130         }
131         if (!memcmp(&chunk[0], "fmt ", 4)) {
132             if (hadFmt) {
133 #ifdef HAVE_STDERR
134                 fprintf(stderr, "multiple fmt\n");
135 #endif
136                 goto close;
137             }
138             if (chunkSize < 2) {
139 #ifdef HAVE_STDERR
140                 fprintf(stderr, "chunkSize %u < 2\n", chunkSize);
141 #endif
142                 goto close;
143             }
144             unsigned char fmt[40];
145             actual = fread(fmt, sizeof(char), 2, stream);
146             if (actual != 2) {
147 #ifdef HAVE_STDERR
148                 fprintf(stderr, "actual %zu != 2\n", actual);
149 #endif
150                 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:
163 #ifdef HAVE_STDERR
164                 fprintf(stderr, "unsupported format %u\n", format);
165 #endif
166                 goto close;
167             }
168             if (chunkSize < minSize) {
169 #ifdef HAVE_STDERR
170                 fprintf(stderr, "chunkSize %u < minSize %zu\n", chunkSize, minSize);
171 #endif
172                 goto close;
173             }
174             actual = fread(&fmt[2], sizeof(char), minSize - 2, stream);
175             if (actual != minSize - 2) {
176 #ifdef HAVE_STDERR
177                 fprintf(stderr, "actual %zu != %zu\n", actual, minSize - 16);
178 #endif
179                 goto close;
180             }
181             if (chunkSize > minSize) {
182                 fseek(stream, (long) (chunkSize - minSize), SEEK_CUR);
183             }
184             unsigned channels = little2u(&fmt[2]);
185             if ((channels < 1) || (channels > FCC_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             (info->channels > 0 && info->channels <= FCC_8) &&
303             ((info->format & SF_FORMAT_TYPEMASK) == SF_FORMAT_WAV) &&
304             (sub == SF_FORMAT_PCM_16 || sub == SF_FORMAT_PCM_U8 || sub == SF_FORMAT_FLOAT ||
305                 sub == SF_FORMAT_PCM_24 || sub == SF_FORMAT_PCM_32)
306           )) {
307         return NULL;
308     }
309     FILE *stream = fopen(path, "w+b");
310     if (stream == NULL) {
311 #ifdef HAVE_STDERR
312         fprintf(stderr, "fopen %s failed errno %d\n", path, errno);
313 #endif
314         return NULL;
315     }
316     unsigned char wav[58];
317     memset(wav, 0, sizeof(wav));
318     memcpy(wav, "RIFF", 4);
319     memcpy(&wav[8], "WAVEfmt ", 8);
320     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     }
329     wav[22] = info->channels;
330     write4u(&wav[24], info->samplerate);
331     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;
342     case SF_FORMAT_PCM_24:
343         bitsPerSample = 24;
344         break;
345     case SF_FORMAT_PCM_32:
346         bitsPerSample = 32;
347         break;
348     default:    // not reachable
349         bitsPerSample = 0;
350         break;
351     }
352     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;
357     size_t extra = 0;
358     if (sub == SF_FORMAT_FLOAT) {
359         memcpy(&wav[38], "fact", 4);
360         wav[42] = 4;
361         memcpy(&wav[50], "data", 4);
362         extra = 14;
363     } else
364         memcpy(&wav[36], "data", 4);
365     // dataSize is initially zero
366     (void) fwrite(wav, 44 + extra, 1, stream);
367     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
377 SNDFILE *sf_open(const char *path, int mode, SF_INFO *info)
378 {
379     if (path == NULL || info == NULL) {
380 #ifdef HAVE_STDERR
381         fprintf(stderr, "path=%p info=%p\n", path, info);
382 #endif
383         return NULL;
384     }
385     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:
391 #ifdef HAVE_STDERR
392         fprintf(stderr, "mode=%d\n", mode);
393 #endif
394         return NULL;
395     }
396 }
397
398 void sf_close(SNDFILE *handle)
399 {
400     if (handle == NULL)
401         return;
402     free(handle->temp);
403     if (handle->mode == SFM_WRITE) {
404         (void) fflush(handle->stream);
405         rewind(handle->stream);
406         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);
409         unsigned dataSize = handle->remaining * handle->bytesPerFrame;
410         write4u(&wav[4], dataSize + 36 + extra);    // riffSize
411         write4u(&wav[40 + extra], dataSize);        // dataSize
412         rewind(handle->stream);
413         (void) fwrite(wav, 44 + extra, 1, handle->stream);
414     }
415     (void) fclose(handle->stream);
416     free(handle);
417 }
418
419 sf_count_t sf_readf_short(SNDFILE *handle, short *ptr, sf_count_t desiredFrames)
420 {
421     if (handle == NULL || handle->mode != SFM_READ || ptr == NULL || !handle->remaining ||
422             desiredFrames <= 0) {
423         return 0;
424     }
425     if (handle->remaining < (size_t) desiredFrames) {
426         desiredFrames = handle->remaining;
427     }
428     // does not check for numeric overflow
429     size_t desiredBytes = desiredFrames * handle->bytesPerFrame;
430     size_t actualBytes;
431     void *temp = NULL;
432     unsigned format = handle->info.format & SF_FORMAT_SUBMASK;
433     if (format == SF_FORMAT_PCM_32 || format == SF_FORMAT_FLOAT || format == SF_FORMAT_PCM_24) {
434         temp = malloc(desiredBytes);
435         actualBytes = fread(temp, sizeof(char), desiredBytes, handle->stream);
436     } else {
437         actualBytes = fread(ptr, sizeof(char), desiredBytes, handle->stream);
438     }
439     size_t actualFrames = actualBytes / handle->bytesPerFrame;
440     handle->remaining -= actualFrames;
441     switch (format) {
442     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())
447             my_swab(ptr, actualFrames * handle->info.channels);
448         break;
449     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;
457     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;
461     default:
462         memset(ptr, 0, actualFrames * handle->info.channels * sizeof(short));
463         break;
464     }
465     return actualFrames;
466 }
467
468 sf_count_t sf_readf_float(SNDFILE *handle, float *ptr, sf_count_t desiredFrames)
469 {
470     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;
480     void *temp = NULL;
481     unsigned format = handle->info.format & SF_FORMAT_SUBMASK;
482     if (format == SF_FORMAT_PCM_16 || format == SF_FORMAT_PCM_U8 || format == SF_FORMAT_PCM_24) {
483         temp = malloc(desiredBytes);
484         actualBytes = fread(temp, sizeof(char), desiredBytes, handle->stream);
485     } else {
486         actualBytes = fread(ptr, sizeof(char), desiredBytes, handle->stream);
487     }
488     size_t actualFrames = actualBytes / handle->bytesPerFrame;
489     handle->remaining -= actualFrames;
490     switch (format) {
491     case SF_FORMAT_PCM_U8:
492 #if 0
493         // TODO - implement
494         memcpy_to_float_from_u8(ptr, (const unsigned char *) temp,
495                 actualFrames * handle->info.channels);
496 #endif
497         free(temp);
498         break;
499     case SF_FORMAT_PCM_16:
500         memcpy_to_float_from_i16(ptr, (const short *) temp, actualFrames * handle->info.channels);
501         free(temp);
502         break;
503     case SF_FORMAT_PCM_32:
504         memcpy_to_float_from_i32(ptr, (const int *) ptr, actualFrames * handle->info.channels);
505         break;
506     case SF_FORMAT_FLOAT:
507         break;
508     case SF_FORMAT_PCM_24:
509         memcpy_to_float_from_p24(ptr, (const uint8_t *) temp, actualFrames * handle->info.channels);
510         free(temp);
511         break;
512     default:
513         memset(ptr, 0, actualFrames * handle->info.channels * sizeof(float));
514         break;
515     }
516     return actualFrames;
517 }
518
519 sf_count_t sf_readf_int(SNDFILE *handle, int *ptr, sf_count_t desiredFrames)
520 {
521     if (handle == NULL || handle->mode != SFM_READ || ptr == NULL || !handle->remaining ||
522             desiredFrames <= 0) {
523         return 0;
524     }
525     if (handle->remaining < (size_t) desiredFrames) {
526         desiredFrames = handle->remaining;
527     }
528     // does not check for numeric overflow
529     size_t desiredBytes = desiredFrames * handle->bytesPerFrame;
530     void *temp = NULL;
531     unsigned format = handle->info.format & SF_FORMAT_SUBMASK;
532     size_t actualBytes;
533     if (format == SF_FORMAT_PCM_16 || format == SF_FORMAT_PCM_U8 || format == SF_FORMAT_PCM_24) {
534         temp = malloc(desiredBytes);
535         actualBytes = fread(temp, sizeof(char), desiredBytes, handle->stream);
536     } else {
537         actualBytes = fread(ptr, sizeof(char), desiredBytes, handle->stream);
538     }
539     size_t actualFrames = actualBytes / handle->bytesPerFrame;
540     handle->remaining -= actualFrames;
541     switch (format) {
542     case SF_FORMAT_PCM_U8:
543 #if 0
544         // TODO - implement
545         memcpy_to_i32_from_u8(ptr, (const unsigned char *) temp,
546                 actualFrames * handle->info.channels);
547 #endif
548         free(temp);
549         break;
550     case SF_FORMAT_PCM_16:
551         memcpy_to_i32_from_i16(ptr, (const short *) temp, actualFrames * handle->info.channels);
552         free(temp);
553         break;
554     case SF_FORMAT_PCM_32:
555         break;
556     case SF_FORMAT_FLOAT:
557         memcpy_to_i32_from_float(ptr, (const float *) ptr, actualFrames * handle->info.channels);
558         break;
559     case SF_FORMAT_PCM_24:
560         memcpy_to_i32_from_p24(ptr, (const uint8_t *) temp, actualFrames * handle->info.channels);
561         free(temp);
562         break;
563     default:
564         memset(ptr, 0, actualFrames * handle->info.channels * sizeof(int));
565         break;
566     }
567     return actualFrames;
568 }
569
570 sf_count_t sf_writef_short(SNDFILE *handle, const short *ptr, sf_count_t desiredFrames)
571 {
572     if (handle == NULL || handle->mode != SFM_WRITE || ptr == NULL || desiredFrames <= 0)
573         return 0;
574     size_t desiredBytes = desiredFrames * handle->bytesPerFrame;
575     size_t actualBytes = 0;
576     switch (handle->info.format & SF_FORMAT_SUBMASK) {
577     case SF_FORMAT_PCM_U8:
578         handle->temp = realloc(handle->temp, desiredBytes);
579         memcpy_to_u8_from_i16(handle->temp, ptr, desiredBytes);
580         actualBytes = fwrite(handle->temp, sizeof(char), desiredBytes, handle->stream);
581         break;
582     case SF_FORMAT_PCM_16:
583         // does not check for numeric overflow
584         if (isLittleEndian()) {
585             actualBytes = fwrite(ptr, sizeof(char), desiredBytes, handle->stream);
586         } else {
587             handle->temp = realloc(handle->temp, desiredBytes);
588             memcpy(handle->temp, ptr, desiredBytes);
589             my_swab((short *) handle->temp, desiredFrames * handle->info.channels);
590             actualBytes = fwrite(handle->temp, sizeof(char), desiredBytes, handle->stream);
591         }
592         break;
593     case SF_FORMAT_FLOAT:
594         handle->temp = realloc(handle->temp, desiredBytes);
595         memcpy_to_float_from_i16((float *) handle->temp, ptr,
596                 desiredFrames * handle->info.channels);
597         actualBytes = fwrite(handle->temp, sizeof(char), desiredBytes, handle->stream);
598         break;
599     default:
600         break;
601     }
602     size_t actualFrames = actualBytes / handle->bytesPerFrame;
603     handle->remaining += actualFrames;
604     return actualFrames;
605 }
606
607 sf_count_t sf_writef_float(SNDFILE *handle, const float *ptr, sf_count_t desiredFrames)
608 {
609     if (handle == NULL || handle->mode != SFM_WRITE || ptr == NULL || desiredFrames <= 0)
610         return 0;
611     size_t desiredBytes = desiredFrames * handle->bytesPerFrame;
612     size_t actualBytes = 0;
613     switch (handle->info.format & SF_FORMAT_SUBMASK) {
614     case SF_FORMAT_FLOAT:
615         actualBytes = fwrite(ptr, sizeof(char), desiredBytes, handle->stream);
616         break;
617     case SF_FORMAT_PCM_16:
618         handle->temp = realloc(handle->temp, desiredBytes);
619         memcpy_to_i16_from_float((short *) handle->temp, ptr,
620                 desiredFrames * handle->info.channels);
621         actualBytes = fwrite(handle->temp, sizeof(char), desiredBytes, handle->stream);
622         break;
623     case SF_FORMAT_PCM_U8:  // transcoding from float to byte not yet implemented
624     default:
625         break;
626     }
627     size_t actualFrames = actualBytes / handle->bytesPerFrame;
628     handle->remaining += actualFrames;
629     return actualFrames;
630 }
631
632 sf_count_t sf_writef_int(SNDFILE *handle, const int *ptr, sf_count_t desiredFrames)
633 {
634     if (handle == NULL || handle->mode != SFM_WRITE || ptr == NULL || desiredFrames <= 0)
635         return 0;
636     size_t desiredBytes = desiredFrames * handle->bytesPerFrame;
637     size_t actualBytes = 0;
638     switch (handle->info.format & SF_FORMAT_SUBMASK) {
639     case SF_FORMAT_PCM_32:
640         actualBytes = fwrite(ptr, sizeof(char), desiredBytes, handle->stream);
641         break;
642     default:    // transcoding from other formats not yet implemented
643         break;
644     }
645     size_t actualFrames = actualBytes / handle->bytesPerFrame;
646     handle->remaining += actualFrames;
647     return actualFrames;
648 }