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 <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 }
    649