You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
648 lines
21 KiB
648 lines
21 KiB
/* |
|
* Copyright (C) 2012 The Android Open Source Project |
|
* |
|
* Licensed under the Apache License, Version 2.0 (the "License"); |
|
* you may not use this file except in compliance with the License. |
|
* You may obtain a copy of the License at |
|
* |
|
* http://www.apache.org/licenses/LICENSE-2.0 |
|
* |
|
* Unless required by applicable law or agreed to in writing, software |
|
* distributed under the License is distributed on an "AS IS" BASIS, |
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|
* See the License for the specific language governing permissions and |
|
* limitations under the License. |
|
*/ |
|
|
|
#include <system/audio.h> |
|
#include <audio_utils/sndfile.h> |
|
#include <audio_utils/primitives.h> |
|
#ifdef HAVE_STDERR |
|
#include <stdio.h> |
|
#endif |
|
#include <string.h> |
|
#include <errno.h> |
|
|
|
#define WAVE_FORMAT_PCM 1 |
|
#define WAVE_FORMAT_IEEE_FLOAT 3 |
|
#define WAVE_FORMAT_EXTENSIBLE 0xFFFE |
|
|
|
struct SNDFILE_ { |
|
int mode; |
|
uint8_t *temp; // realloc buffer used for shrinking 16 bits to 8 bits and byte-swapping |
|
FILE *stream; |
|
size_t bytesPerFrame; |
|
size_t remaining; // frames unread for SFM_READ, frames written for SFM_WRITE |
|
SF_INFO info; |
|
}; |
|
|
|
static unsigned little2u(unsigned char *ptr) |
|
{ |
|
return (ptr[1] << 8) + ptr[0]; |
|
} |
|
|
|
static unsigned little4u(unsigned char *ptr) |
|
{ |
|
return (ptr[3] << 24) + (ptr[2] << 16) + (ptr[1] << 8) + ptr[0]; |
|
} |
|
|
|
static int isLittleEndian(void) |
|
{ |
|
static const short one = 1; |
|
return *((const char *) &one) == 1; |
|
} |
|
|
|
// "swab" conflicts with OS X <string.h> |
|
static void my_swab(short *ptr, size_t numToSwap) |
|
{ |
|
while (numToSwap > 0) { |
|
*ptr = little2u((unsigned char *) ptr); |
|
--numToSwap; |
|
++ptr; |
|
} |
|
} |
|
|
|
static SNDFILE *sf_open_read(const char *path, SF_INFO *info) |
|
{ |
|
FILE *stream = fopen(path, "rb"); |
|
if (stream == NULL) { |
|
#ifdef HAVE_STDERR |
|
fprintf(stderr, "fopen %s failed errno %d\n", path, errno); |
|
#endif |
|
return NULL; |
|
} |
|
|
|
SNDFILE *handle = (SNDFILE *) malloc(sizeof(SNDFILE)); |
|
handle->mode = SFM_READ; |
|
handle->temp = NULL; |
|
handle->stream = stream; |
|
handle->info.format = SF_FORMAT_WAV; |
|
|
|
// don't attempt to parse all valid forms, just the most common ones |
|
unsigned char wav[12]; |
|
size_t actual; |
|
actual = fread(wav, sizeof(char), sizeof(wav), stream); |
|
if (actual < 12) { |
|
#ifdef HAVE_STDERR |
|
fprintf(stderr, "actual %zu < 44\n", actual); |
|
#endif |
|
goto close; |
|
} |
|
if (memcmp(wav, "RIFF", 4)) { |
|
#ifdef HAVE_STDERR |
|
fprintf(stderr, "wav != RIFF\n"); |
|
#endif |
|
goto close; |
|
} |
|
unsigned riffSize = little4u(&wav[4]); |
|
if (riffSize < 4) { |
|
#ifdef HAVE_STDERR |
|
fprintf(stderr, "riffSize %u < 4\n", riffSize); |
|
#endif |
|
goto close; |
|
} |
|
if (memcmp(&wav[8], "WAVE", 4)) { |
|
#ifdef HAVE_STDERR |
|
fprintf(stderr, "missing WAVE\n"); |
|
#endif |
|
goto close; |
|
} |
|
size_t remaining = riffSize - 4; |
|
int hadFmt = 0; |
|
int hadData = 0; |
|
long dataTell = 0L; |
|
while (remaining >= 8) { |
|
unsigned char chunk[8]; |
|
actual = fread(chunk, sizeof(char), sizeof(chunk), stream); |
|
if (actual != sizeof(chunk)) { |
|
#ifdef HAVE_STDERR |
|
fprintf(stderr, "actual %zu != %zu\n", actual, sizeof(chunk)); |
|
#endif |
|
goto close; |
|
} |
|
remaining -= 8; |
|
unsigned chunkSize = little4u(&chunk[4]); |
|
if (chunkSize > remaining) { |
|
#ifdef HAVE_STDERR |
|
fprintf(stderr, "chunkSize %u > remaining %zu\n", chunkSize, remaining); |
|
#endif |
|
goto close; |
|
} |
|
if (!memcmp(&chunk[0], "fmt ", 4)) { |
|
if (hadFmt) { |
|
#ifdef HAVE_STDERR |
|
fprintf(stderr, "multiple fmt\n"); |
|
#endif |
|
goto close; |
|
} |
|
if (chunkSize < 2) { |
|
#ifdef HAVE_STDERR |
|
fprintf(stderr, "chunkSize %u < 2\n", chunkSize); |
|
#endif |
|
goto close; |
|
} |
|
unsigned char fmt[40]; |
|
actual = fread(fmt, sizeof(char), 2, stream); |
|
if (actual != 2) { |
|
#ifdef HAVE_STDERR |
|
fprintf(stderr, "actual %zu != 2\n", actual); |
|
#endif |
|
goto close; |
|
} |
|
unsigned format = little2u(&fmt[0]); |
|
size_t minSize = 0; |
|
switch (format) { |
|
case WAVE_FORMAT_PCM: |
|
case WAVE_FORMAT_IEEE_FLOAT: |
|
minSize = 16; |
|
break; |
|
case WAVE_FORMAT_EXTENSIBLE: |
|
minSize = 40; |
|
break; |
|
default: |
|
#ifdef HAVE_STDERR |
|
fprintf(stderr, "unsupported format %u\n", format); |
|
#endif |
|
goto close; |
|
} |
|
if (chunkSize < minSize) { |
|
#ifdef HAVE_STDERR |
|
fprintf(stderr, "chunkSize %u < minSize %zu\n", chunkSize, minSize); |
|
#endif |
|
goto close; |
|
} |
|
actual = fread(&fmt[2], sizeof(char), minSize - 2, stream); |
|
if (actual != minSize - 2) { |
|
#ifdef HAVE_STDERR |
|
fprintf(stderr, "actual %zu != %zu\n", actual, minSize - 16); |
|
#endif |
|
goto close; |
|
} |
|
if (chunkSize > minSize) { |
|
fseek(stream, (long) (chunkSize - minSize), SEEK_CUR); |
|
} |
|
unsigned channels = little2u(&fmt[2]); |
|
if ((channels < 1) || (channels > FCC_8)) { |
|
#ifdef HAVE_STDERR |
|
fprintf(stderr, "unsupported channels %u\n", channels); |
|
#endif |
|
goto close; |
|
} |
|
unsigned samplerate = little4u(&fmt[4]); |
|
if (samplerate == 0) { |
|
#ifdef HAVE_STDERR |
|
fprintf(stderr, "samplerate %u == 0\n", samplerate); |
|
#endif |
|
goto close; |
|
} |
|
// ignore byte rate |
|
// ignore block alignment |
|
unsigned bitsPerSample = little2u(&fmt[14]); |
|
if (bitsPerSample != 8 && bitsPerSample != 16 && bitsPerSample != 24 && |
|
bitsPerSample != 32) { |
|
#ifdef HAVE_STDERR |
|
fprintf(stderr, "bitsPerSample %u != 8 or 16 or 24 or 32\n", bitsPerSample); |
|
#endif |
|
goto close; |
|
} |
|
unsigned bytesPerFrame = (bitsPerSample >> 3) * channels; |
|
handle->bytesPerFrame = bytesPerFrame; |
|
handle->info.samplerate = samplerate; |
|
handle->info.channels = channels; |
|
switch (bitsPerSample) { |
|
case 8: |
|
handle->info.format |= SF_FORMAT_PCM_U8; |
|
break; |
|
case 16: |
|
handle->info.format |= SF_FORMAT_PCM_16; |
|
break; |
|
case 24: |
|
handle->info.format |= SF_FORMAT_PCM_24; |
|
break; |
|
case 32: |
|
if (format == WAVE_FORMAT_IEEE_FLOAT) |
|
handle->info.format |= SF_FORMAT_FLOAT; |
|
else |
|
handle->info.format |= SF_FORMAT_PCM_32; |
|
break; |
|
} |
|
hadFmt = 1; |
|
} else if (!memcmp(&chunk[0], "data", 4)) { |
|
if (!hadFmt) { |
|
#ifdef HAVE_STDERR |
|
fprintf(stderr, "data not preceded by fmt\n"); |
|
#endif |
|
goto close; |
|
} |
|
if (hadData) { |
|
#ifdef HAVE_STDERR |
|
fprintf(stderr, "multiple data\n"); |
|
#endif |
|
goto close; |
|
} |
|
handle->remaining = chunkSize / handle->bytesPerFrame; |
|
handle->info.frames = handle->remaining; |
|
dataTell = ftell(stream); |
|
if (chunkSize > 0) { |
|
fseek(stream, (long) chunkSize, SEEK_CUR); |
|
} |
|
hadData = 1; |
|
} else if (!memcmp(&chunk[0], "fact", 4)) { |
|
// ignore fact |
|
if (chunkSize > 0) { |
|
fseek(stream, (long) chunkSize, SEEK_CUR); |
|
} |
|
} else { |
|
// ignore unknown chunk |
|
#ifdef HAVE_STDERR |
|
fprintf(stderr, "ignoring unknown chunk %c%c%c%c\n", |
|
chunk[0], chunk[1], chunk[2], chunk[3]); |
|
#endif |
|
if (chunkSize > 0) { |
|
fseek(stream, (long) chunkSize, SEEK_CUR); |
|
} |
|
} |
|
remaining -= chunkSize; |
|
} |
|
if (remaining > 0) { |
|
#ifdef HAVE_STDERR |
|
fprintf(stderr, "partial chunk at end of RIFF, remaining %zu\n", remaining); |
|
#endif |
|
goto close; |
|
} |
|
if (!hadData) { |
|
#ifdef HAVE_STDERR |
|
fprintf(stderr, "missing data\n"); |
|
#endif |
|
goto close; |
|
} |
|
(void) fseek(stream, dataTell, SEEK_SET); |
|
*info = handle->info; |
|
return handle; |
|
|
|
close: |
|
free(handle); |
|
fclose(stream); |
|
return NULL; |
|
} |
|
|
|
static void write4u(unsigned char *ptr, unsigned u) |
|
{ |
|
ptr[0] = u; |
|
ptr[1] = u >> 8; |
|
ptr[2] = u >> 16; |
|
ptr[3] = u >> 24; |
|
} |
|
|
|
static SNDFILE *sf_open_write(const char *path, SF_INFO *info) |
|
{ |
|
int sub = info->format & SF_FORMAT_SUBMASK; |
|
if (!( |
|
(info->samplerate > 0) && |
|
(info->channels > 0 && info->channels <= FCC_8) && |
|
((info->format & SF_FORMAT_TYPEMASK) == SF_FORMAT_WAV) && |
|
(sub == SF_FORMAT_PCM_16 || sub == SF_FORMAT_PCM_U8 || sub == SF_FORMAT_FLOAT || |
|
sub == SF_FORMAT_PCM_24 || sub == SF_FORMAT_PCM_32) |
|
)) { |
|
return NULL; |
|
} |
|
FILE *stream = fopen(path, "w+b"); |
|
if (stream == NULL) { |
|
#ifdef HAVE_STDERR |
|
fprintf(stderr, "fopen %s failed errno %d\n", path, errno); |
|
#endif |
|
return NULL; |
|
} |
|
unsigned char wav[58]; |
|
memset(wav, 0, sizeof(wav)); |
|
memcpy(wav, "RIFF", 4); |
|
memcpy(&wav[8], "WAVEfmt ", 8); |
|
if (sub == SF_FORMAT_FLOAT) { |
|
wav[4] = 50; // riffSize |
|
wav[16] = 18; // fmtSize |
|
wav[20] = WAVE_FORMAT_IEEE_FLOAT; |
|
} else { |
|
wav[4] = 36; // riffSize |
|
wav[16] = 16; // fmtSize |
|
wav[20] = WAVE_FORMAT_PCM; |
|
} |
|
wav[22] = info->channels; |
|
write4u(&wav[24], info->samplerate); |
|
unsigned bitsPerSample; |
|
switch (sub) { |
|
case SF_FORMAT_PCM_16: |
|
bitsPerSample = 16; |
|
break; |
|
case SF_FORMAT_PCM_U8: |
|
bitsPerSample = 8; |
|
break; |
|
case SF_FORMAT_FLOAT: |
|
bitsPerSample = 32; |
|
break; |
|
case SF_FORMAT_PCM_24: |
|
bitsPerSample = 24; |
|
break; |
|
case SF_FORMAT_PCM_32: |
|
bitsPerSample = 32; |
|
break; |
|
default: // not reachable |
|
bitsPerSample = 0; |
|
break; |
|
} |
|
unsigned blockAlignment = (bitsPerSample >> 3) * info->channels; |
|
unsigned byteRate = info->samplerate * blockAlignment; |
|
write4u(&wav[28], byteRate); |
|
wav[32] = blockAlignment; |
|
wav[34] = bitsPerSample; |
|
size_t extra = 0; |
|
if (sub == SF_FORMAT_FLOAT) { |
|
memcpy(&wav[38], "fact", 4); |
|
wav[42] = 4; |
|
memcpy(&wav[50], "data", 4); |
|
extra = 14; |
|
} else |
|
memcpy(&wav[36], "data", 4); |
|
// dataSize is initially zero |
|
(void) fwrite(wav, 44 + extra, 1, stream); |
|
SNDFILE *handle = (SNDFILE *) malloc(sizeof(SNDFILE)); |
|
handle->mode = SFM_WRITE; |
|
handle->temp = NULL; |
|
handle->stream = stream; |
|
handle->bytesPerFrame = blockAlignment; |
|
handle->remaining = 0; |
|
handle->info = *info; |
|
return handle; |
|
} |
|
|
|
SNDFILE *sf_open(const char *path, int mode, SF_INFO *info) |
|
{ |
|
if (path == NULL || info == NULL) { |
|
#ifdef HAVE_STDERR |
|
fprintf(stderr, "path=%p info=%p\n", path, info); |
|
#endif |
|
return NULL; |
|
} |
|
switch (mode) { |
|
case SFM_READ: |
|
return sf_open_read(path, info); |
|
case SFM_WRITE: |
|
return sf_open_write(path, info); |
|
default: |
|
#ifdef HAVE_STDERR |
|
fprintf(stderr, "mode=%d\n", mode); |
|
#endif |
|
return NULL; |
|
} |
|
} |
|
|
|
void sf_close(SNDFILE *handle) |
|
{ |
|
if (handle == NULL) |
|
return; |
|
free(handle->temp); |
|
if (handle->mode == SFM_WRITE) { |
|
(void) fflush(handle->stream); |
|
rewind(handle->stream); |
|
unsigned char wav[58]; |
|
size_t extra = (handle->info.format & SF_FORMAT_SUBMASK) == SF_FORMAT_FLOAT ? 14 : 0; |
|
(void) fread(wav, 44 + extra, 1, handle->stream); |
|
unsigned dataSize = handle->remaining * handle->bytesPerFrame; |
|
write4u(&wav[4], dataSize + 36 + extra); // riffSize |
|
write4u(&wav[40 + extra], dataSize); // dataSize |
|
rewind(handle->stream); |
|
(void) fwrite(wav, 44 + extra, 1, handle->stream); |
|
} |
|
(void) fclose(handle->stream); |
|
free(handle); |
|
} |
|
|
|
sf_count_t sf_readf_short(SNDFILE *handle, short *ptr, sf_count_t desiredFrames) |
|
{ |
|
if (handle == NULL || handle->mode != SFM_READ || ptr == NULL || !handle->remaining || |
|
desiredFrames <= 0) { |
|
return 0; |
|
} |
|
if (handle->remaining < (size_t) desiredFrames) { |
|
desiredFrames = handle->remaining; |
|
} |
|
// does not check for numeric overflow |
|
size_t desiredBytes = desiredFrames * handle->bytesPerFrame; |
|
size_t actualBytes; |
|
void *temp = NULL; |
|
unsigned format = handle->info.format & SF_FORMAT_SUBMASK; |
|
if (format == SF_FORMAT_PCM_32 || format == SF_FORMAT_FLOAT || format == SF_FORMAT_PCM_24) { |
|
temp = malloc(desiredBytes); |
|
actualBytes = fread(temp, sizeof(char), desiredBytes, handle->stream); |
|
} else { |
|
actualBytes = fread(ptr, sizeof(char), desiredBytes, handle->stream); |
|
} |
|
size_t actualFrames = actualBytes / handle->bytesPerFrame; |
|
handle->remaining -= actualFrames; |
|
switch (format) { |
|
case SF_FORMAT_PCM_U8: |
|
memcpy_to_i16_from_u8(ptr, (unsigned char *) ptr, actualFrames * handle->info.channels); |
|
break; |
|
case SF_FORMAT_PCM_16: |
|
if (!isLittleEndian()) |
|
my_swab(ptr, actualFrames * handle->info.channels); |
|
break; |
|
case SF_FORMAT_PCM_32: |
|
memcpy_to_i16_from_i32(ptr, (const int *) temp, actualFrames * handle->info.channels); |
|
free(temp); |
|
break; |
|
case SF_FORMAT_FLOAT: |
|
memcpy_to_i16_from_float(ptr, (const float *) temp, actualFrames * handle->info.channels); |
|
free(temp); |
|
break; |
|
case SF_FORMAT_PCM_24: |
|
memcpy_to_i16_from_p24(ptr, (const uint8_t *) temp, actualFrames * handle->info.channels); |
|
free(temp); |
|
break; |
|
default: |
|
memset(ptr, 0, actualFrames * handle->info.channels * sizeof(short)); |
|
break; |
|
} |
|
return actualFrames; |
|
} |
|
|
|
sf_count_t sf_readf_float(SNDFILE *handle, float *ptr, sf_count_t desiredFrames) |
|
{ |
|
if (handle == NULL || handle->mode != SFM_READ || ptr == NULL || !handle->remaining || |
|
desiredFrames <= 0) { |
|
return 0; |
|
} |
|
if (handle->remaining < (size_t) desiredFrames) { |
|
desiredFrames = handle->remaining; |
|
} |
|
// does not check for numeric overflow |
|
size_t desiredBytes = desiredFrames * handle->bytesPerFrame; |
|
size_t actualBytes; |
|
void *temp = NULL; |
|
unsigned format = handle->info.format & SF_FORMAT_SUBMASK; |
|
if (format == SF_FORMAT_PCM_16 || format == SF_FORMAT_PCM_U8 || format == SF_FORMAT_PCM_24) { |
|
temp = malloc(desiredBytes); |
|
actualBytes = fread(temp, sizeof(char), desiredBytes, handle->stream); |
|
} else { |
|
actualBytes = fread(ptr, sizeof(char), desiredBytes, handle->stream); |
|
} |
|
size_t actualFrames = actualBytes / handle->bytesPerFrame; |
|
handle->remaining -= actualFrames; |
|
switch (format) { |
|
case SF_FORMAT_PCM_U8: |
|
#if 0 |
|
// TODO - implement |
|
memcpy_to_float_from_u8(ptr, (const unsigned char *) temp, |
|
actualFrames * handle->info.channels); |
|
#endif |
|
free(temp); |
|
break; |
|
case SF_FORMAT_PCM_16: |
|
memcpy_to_float_from_i16(ptr, (const short *) temp, actualFrames * handle->info.channels); |
|
free(temp); |
|
break; |
|
case SF_FORMAT_PCM_32: |
|
memcpy_to_float_from_i32(ptr, (const int *) ptr, actualFrames * handle->info.channels); |
|
break; |
|
case SF_FORMAT_FLOAT: |
|
break; |
|
case SF_FORMAT_PCM_24: |
|
memcpy_to_float_from_p24(ptr, (const uint8_t *) temp, actualFrames * handle->info.channels); |
|
free(temp); |
|
break; |
|
default: |
|
memset(ptr, 0, actualFrames * handle->info.channels * sizeof(float)); |
|
break; |
|
} |
|
return actualFrames; |
|
} |
|
|
|
sf_count_t sf_readf_int(SNDFILE *handle, int *ptr, sf_count_t desiredFrames) |
|
{ |
|
if (handle == NULL || handle->mode != SFM_READ || ptr == NULL || !handle->remaining || |
|
desiredFrames <= 0) { |
|
return 0; |
|
} |
|
if (handle->remaining < (size_t) desiredFrames) { |
|
desiredFrames = handle->remaining; |
|
} |
|
// does not check for numeric overflow |
|
size_t desiredBytes = desiredFrames * handle->bytesPerFrame; |
|
void *temp = NULL; |
|
unsigned format = handle->info.format & SF_FORMAT_SUBMASK; |
|
size_t actualBytes; |
|
if (format == SF_FORMAT_PCM_16 || format == SF_FORMAT_PCM_U8 || format == SF_FORMAT_PCM_24) { |
|
temp = malloc(desiredBytes); |
|
actualBytes = fread(temp, sizeof(char), desiredBytes, handle->stream); |
|
} else { |
|
actualBytes = fread(ptr, sizeof(char), desiredBytes, handle->stream); |
|
} |
|
size_t actualFrames = actualBytes / handle->bytesPerFrame; |
|
handle->remaining -= actualFrames; |
|
switch (format) { |
|
case SF_FORMAT_PCM_U8: |
|
#if 0 |
|
// TODO - implement |
|
memcpy_to_i32_from_u8(ptr, (const unsigned char *) temp, |
|
actualFrames * handle->info.channels); |
|
#endif |
|
free(temp); |
|
break; |
|
case SF_FORMAT_PCM_16: |
|
memcpy_to_i32_from_i16(ptr, (const short *) temp, actualFrames * handle->info.channels); |
|
free(temp); |
|
break; |
|
case SF_FORMAT_PCM_32: |
|
break; |
|
case SF_FORMAT_FLOAT: |
|
memcpy_to_i32_from_float(ptr, (const float *) ptr, actualFrames * handle->info.channels); |
|
break; |
|
case SF_FORMAT_PCM_24: |
|
memcpy_to_i32_from_p24(ptr, (const uint8_t *) temp, actualFrames * handle->info.channels); |
|
free(temp); |
|
break; |
|
default: |
|
memset(ptr, 0, actualFrames * handle->info.channels * sizeof(int)); |
|
break; |
|
} |
|
return actualFrames; |
|
} |
|
|
|
sf_count_t sf_writef_short(SNDFILE *handle, const short *ptr, sf_count_t desiredFrames) |
|
{ |
|
if (handle == NULL || handle->mode != SFM_WRITE || ptr == NULL || desiredFrames <= 0) |
|
return 0; |
|
size_t desiredBytes = desiredFrames * handle->bytesPerFrame; |
|
size_t actualBytes = 0; |
|
switch (handle->info.format & SF_FORMAT_SUBMASK) { |
|
case SF_FORMAT_PCM_U8: |
|
handle->temp = realloc(handle->temp, desiredBytes); |
|
memcpy_to_u8_from_i16(handle->temp, ptr, desiredBytes); |
|
actualBytes = fwrite(handle->temp, sizeof(char), desiredBytes, handle->stream); |
|
break; |
|
case SF_FORMAT_PCM_16: |
|
// does not check for numeric overflow |
|
if (isLittleEndian()) { |
|
actualBytes = fwrite(ptr, sizeof(char), desiredBytes, handle->stream); |
|
} else { |
|
handle->temp = realloc(handle->temp, desiredBytes); |
|
memcpy(handle->temp, ptr, desiredBytes); |
|
my_swab((short *) handle->temp, desiredFrames * handle->info.channels); |
|
actualBytes = fwrite(handle->temp, sizeof(char), desiredBytes, handle->stream); |
|
} |
|
break; |
|
case SF_FORMAT_FLOAT: |
|
handle->temp = realloc(handle->temp, desiredBytes); |
|
memcpy_to_float_from_i16((float *) handle->temp, ptr, |
|
desiredFrames * handle->info.channels); |
|
actualBytes = fwrite(handle->temp, sizeof(char), desiredBytes, handle->stream); |
|
break; |
|
default: |
|
break; |
|
} |
|
size_t actualFrames = actualBytes / handle->bytesPerFrame; |
|
handle->remaining += actualFrames; |
|
return actualFrames; |
|
} |
|
|
|
sf_count_t sf_writef_float(SNDFILE *handle, const float *ptr, sf_count_t desiredFrames) |
|
{ |
|
if (handle == NULL || handle->mode != SFM_WRITE || ptr == NULL || desiredFrames <= 0) |
|
return 0; |
|
size_t desiredBytes = desiredFrames * handle->bytesPerFrame; |
|
size_t actualBytes = 0; |
|
switch (handle->info.format & SF_FORMAT_SUBMASK) { |
|
case SF_FORMAT_FLOAT: |
|
actualBytes = fwrite(ptr, sizeof(char), desiredBytes, handle->stream); |
|
break; |
|
case SF_FORMAT_PCM_16: |
|
handle->temp = realloc(handle->temp, desiredBytes); |
|
memcpy_to_i16_from_float((short *) handle->temp, ptr, |
|
desiredFrames * handle->info.channels); |
|
actualBytes = fwrite(handle->temp, sizeof(char), desiredBytes, handle->stream); |
|
break; |
|
case SF_FORMAT_PCM_U8: // transcoding from float to byte not yet implemented |
|
default: |
|
break; |
|
} |
|
size_t actualFrames = actualBytes / handle->bytesPerFrame; |
|
handle->remaining += actualFrames; |
|
return actualFrames; |
|
} |
|
|
|
sf_count_t sf_writef_int(SNDFILE *handle, const int *ptr, sf_count_t desiredFrames) |
|
{ |
|
if (handle == NULL || handle->mode != SFM_WRITE || ptr == NULL || desiredFrames <= 0) |
|
return 0; |
|
size_t desiredBytes = desiredFrames * handle->bytesPerFrame; |
|
size_t actualBytes = 0; |
|
switch (handle->info.format & SF_FORMAT_SUBMASK) { |
|
case SF_FORMAT_PCM_32: |
|
actualBytes = fwrite(ptr, sizeof(char), desiredBytes, handle->stream); |
|
break; |
|
default: // transcoding from other formats not yet implemented |
|
break; |
|
} |
|
size_t actualFrames = actualBytes / handle->bytesPerFrame; |
|
handle->remaining += actualFrames; |
|
return actualFrames; |
|
}
|
|
|