Home | History | Annotate | Download | only in audio_utils
      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 }
    650