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.

440 lines
14 KiB

/*
* tcaudio.c - audio processing library for transcode
* Written by Andrew Church <achurch@achurch.org>
*
* This file is part of transcode, a video stream processing tool.
* transcode is free software, distributable under the terms of the GNU
* General Public License (version 2 or later). See the file COPYING
* for details.
*/
#include "tcaudio.h"
#include "src/transcode.h"
#include <math.h>
/*************************************************************************/
/* Internal data structure to hold various state information. The
* TCAHandle returned by tca_init() and passed by the caller to other
* functions is a pointer to this structure. */
struct tcahandle_ {
AudioFormat format; /* Sample format */
int bits, issigned, msbfirst; /* Information about sample format */
};
/*************************************************************************/
/* Internal-use functions (defined at the bottom of the file). */
static int tca_get_format_info(AudioFormat format, int *bits_ret,
int *issigned_ret, int *msbfirst_ret);
static int tca_convert(const char *funcname, TCAHandle handle, void *buf,
int len, AudioFormat srcfmt, AudioFormat destfmt);
/*************************************************************************/
/*************************************************************************/
/* External interface functions. */
/*************************************************************************/
/**
* tca_init: Create and return a handle for use in other tcaudio
* functions. The handle should be freed with tca_free() when no longer
* needed.
*
* Parameters: format: Audio sample format to use with tcaudio functions.
* Return value: A handle to be passed to other tcaudio functions, or 0 on
* error.
* Preconditions: None.
* Postconditions: None.
*/
TCAHandle tca_init(AudioFormat format)
{
TCAHandle handle;
int bits, issigned, msbfirst;
if (!tca_get_format_info(format, &bits, &issigned, &msbfirst)) {
tc_log_error("libtcaudio", "tca_init: invalid audio format (%d)!",
format);
return NULL;
}
handle = malloc(sizeof(*handle));
if (!handle)
return NULL;
handle->format = format;
handle->bits = bits;
handle->issigned = issigned;
handle->msbfirst = msbfirst;
return handle;
}
/*************************************************************************/
/**
* tca_free: Free resources allocated for the given handle.
*
* Parameters: handle: tcaudio handle.
* Return value: None.
* Preconditions: handle != 0: handle was returned by tca_init()
* Postconditions: None.
*/
void tca_free(TCAHandle handle)
{
free(handle);
}
/*************************************************************************/
/**
* tca_convert_from: Convert the given audio buffer from another sample
* format to the format given in tca_init().
*
* Parameters: handle: tcaudio handle.
* buf: Audio data buffer.
* len: Audio data length, in samples.
* srcfmt: Format of source audio samples.
* Return value: Nonzero on success, zero on error (invalid parameters).
* Preconditions: handle != 0: handle was returned by tca_init()
* Postconditions: None.
*/
int tca_convert_from(TCAHandle handle, void *buf, int len, AudioFormat srcfmt)
{
return tca_convert("tca_convert_from", handle, buf, len, srcfmt,
handle->format);
}
/*************************************************************************/
/**
* tca_convert_to: Convert the given audio buffer from the format given in
* tca_init() to another sample format.
*
* Parameters: handle: tcaudio handle.
* buf: Audio data buffer.
* len: Audio data length, in samples.
* destfmt: Format to convert audio samples into.
* Return value: Nonzero on success, zero on error (invalid parameters).
* Preconditions: handle != 0: handle was returned by tca_init()
* Postconditions: None.
*/
int tca_convert_to(TCAHandle handle, void *buf, int len, AudioFormat destfmt)
{
return tca_convert("tca_convert_to", handle, buf, len, handle->format,
destfmt);
}
/*************************************************************************/
/**
* tca_amplify: Amplify the given audio buffer by the given scale factor.
* When increasing amplitude (scale factor greater than one), samples are
* automatically clipped to the sample format's amplitude range; if
* `nclip_ret' is not NULL, the number of clipped samples is stored there
* (unmodified on error).
*
* Parameters: handle: tcaudio handle.
* buf: Audio data buffer.
* len: Audio data length, in samples.
* scale: Factor by which to scale audio data.
* nclip_ret: Variable to store number of clipped samples in,
* or NULL if this value is not required.
* Return value: Nonzero on success, zero on error (invalid parameters).
* Preconditions: handle != 0: handle was returned by tca_init()
* Postconditions: None.
*/
int tca_amplify(TCAHandle handle, void *buf, int len, double scale,
int *nclip_ret)
{
int nclip;
if (!handle || !buf || len < 0) {
tc_log_error("libtcaudio", "tca_amplify: invalid parameters!");
return 0;
}
nclip = 0;
if (handle->bits == 8) {
uint8_t *ptr = buf;
int bias = handle->issigned ? 0 : 0x80;
int i;
for (i = 0; i < len; i++) {
int32_t v = floor(((ptr[i]-bias) * scale) + 0.5);
if (v > 0x7F) {
v = 0x7F;
nclip++;
}
if (v < -0x80) {
v = -0x80;
nclip++;
}
ptr[i] = v;
}
} else if (handle->bits == 16) {
int8_t *ptr1 = buf + (handle->msbfirst ? 0 : 1);
uint8_t *ptr2 = buf + (handle->msbfirst ? 1 : 0);
int bias = handle->issigned ? 0 : 0x8000;
int i;
for (i = 0; i < len; i++) {
int32_t v =
floor((((ptr1[i*2]<<8 | ptr2[i*2]) - bias) * scale) + 0.5);
if (v > 0x7FFF) {
v = 0x7FFF;
nclip++;
}
if (v < -0x8000) {
v = -0x8000;
nclip++;
}
ptr1[i*2] = v >> 8;
ptr2[i*2] = v & 0xFF;
}
} else {
tc_log_error("libtcaudio", "tca_amplify: %d-bit samples not supported",
handle->bits);
return 0;
}
if (nclip_ret)
*nclip_ret = nclip;
return 1;
}
/*************************************************************************/
/**
* tca_mono_to_stereo: Convert monaural audio data to stereo by
* duplicating the data into both stereo channels.
*
* Parameters: handle: tcaudio handle.
* buf: Audio data buffer.
* len: Audio data length, in stereo samples.
* Return value: Nonzero on success, zero on error (invalid parameters).
* Preconditions: handle != 0: handle was returned by tca_init()
* Postconditions: None.
*/
int tca_mono_to_stereo(TCAHandle handle, void *buf, int len)
{
if (!handle || !buf || len < 0) {
tc_log_error("libtcaudio", "tca_mono_to_stereo: invalid parameters!");
return 0;
}
/* Be careful--we have to go backwards or we overwrite unconverted data */
if (handle->bits == 8) {
uint8_t *ptr = buf;
int i;
for (i = len-1; i >= 0; i--) {
ptr[i*2] = ptr[i];
ptr[i*2+1] = ptr[i];
}
} else if (handle->bits == 16) {
uint16_t *ptr = buf;
int i;
for (i = len-1; i >= 0; i--) {
ptr[i*2] = ptr[i];
ptr[i*2+1] = ptr[i];
}
} else {
tc_log_error("libtcaudio", "tca_mono_to_stereo: %d-bit samples not"
" supported", handle->bits);
return 0;
}
return 1;
}
/*************************************************************************/
/**
* tca_stereo_to_mono: Convert stereo audio data to monaural by mixing the
* two stereo channels.
*
* Parameters: handle: tcaudio handle.
* buf: Audio data buffer.
* len: Audio data length, in stereo samples.
* Return value: Nonzero on success, zero on error (invalid parameters).
* Preconditions: handle != 0: handle was returned by tca_init()
* Postconditions: None.
*/
int tca_stereo_to_mono(TCAHandle handle, void *buf, int len)
{
if (!handle || !buf || len < 0) {
tc_log_error("libtcaudio", "tca_stereo_to_mono: invalid parameters!");
return 0;
}
if (handle->bits == 8) {
uint8_t *ptr = buf;
int i;
for (i = 0; i < len; i++) {
ptr[i] = ((int)ptr[i*2] + (int)ptr[i*2+1] + 1) / 2;
}
} else if (handle->bits == 16) {
int8_t *ptr1 = buf + (handle->msbfirst ? 0 : 1);
uint8_t *ptr2 = buf + (handle->msbfirst ? 1 : 0);
int i;
for (i = 0; i < len; i++) {
int32_t v = ((ptr1[i*4]<<8 | ptr2[i*4])
+ (ptr1[i*4+2]<<8 | ptr2[i*4+2])
+ 1
) / 2;
ptr1[i*2] = v >> 8;
ptr2[i*2] = v & 0xFF;
}
} else {
tc_log_error("libtcaudio", "tca_stereo_to_mono: %d-bit samples not"
" supported", handle->bits);
return 0;
}
return 1;
}
/*************************************************************************/
/*************************************************************************/
/* Internal-use helper functions. */
/*************************************************************************/
/**
* tca_get_format_info: Return information about the given audio format.
*
* Parameters: format: Audio format to get information about.
* bits_ret: Set to the number of bits in a sample.
* issigned_ret: Set to 1 if samples are signed, else 0.
* msbfirst_ret: Set to 1 if samples have the most significant
* byte stored first, else 0.
* Return value: Nonzero if the format is recognized, zero otherwise.
* Preconditions: bits_ret != NULL
* issigned_ret != NULL
* msbfirst_ret != NULL
* Postconditions: None.
*/
static int tca_get_format_info(AudioFormat format, int *bits_ret,
int *issigned_ret, int *msbfirst_ret)
{
switch (format) {
case TCA_S8:
*bits_ret = 8;
*issigned_ret = 1;
*msbfirst_ret = 0;
return 1;
case TCA_U8:
*bits_ret = 8;
*issigned_ret = 0;
*msbfirst_ret = 0;
return 1;
case TCA_S16BE:
*bits_ret = 16;
*issigned_ret = 1;
*msbfirst_ret = 1;
return 1;
case TCA_S16LE:
*bits_ret = 16;
*issigned_ret = 1;
*msbfirst_ret = 0;
return 1;
case TCA_U16BE:
*bits_ret = 16;
*issigned_ret = 0;
*msbfirst_ret = 1;
return 1;
case TCA_U16LE:
*bits_ret = 16;
*issigned_ret = 0;
*msbfirst_ret = 0;
return 1;
}
return 0;
}
/*************************************************************************/
/**
* tca_convert: Convert from one audio sample format to another.
* Implements tca_convert_from() and tca_convert_to().
*
* Parameters: handle: tcaudio handle.
* buf: Audio data buffer.
* len: Audio data length, in samples.
* srcfmt: Format of source audio samples.
* destfmt: Format to convert audio samples into.
* Return value: Nonzero on success, zero on error (invalid parameters).
* Preconditions: handle != 0: handle was returned by tca_init()
* Postconditions: None.
*/
static int tca_convert(const char *funcname, TCAHandle handle, void *buf,
int len, AudioFormat srcfmt, AudioFormat destfmt)
{
int src_bits = -1, src_issigned = -1, src_msbfirst = -1,
dest_bits = -1, dest_issigned = -1, dest_msbfirst = -1;
/* Parameter checks */
if (!handle || !buf || len < 0
|| !tca_get_format_info(srcfmt,&src_bits,&src_issigned,&src_msbfirst)
|| !tca_get_format_info(destfmt,&dest_bits,&dest_issigned,&dest_msbfirst)
) {
tc_log_error("libtcaudio", "%s: invalid parameters!", funcname);
return 0;
}
/* Convert sample sizes and byte orders */
if (src_bits == 8 && dest_bits == 16) {
/* 8 bit -> 16 bit */
const uint8_t *src8 = buf;
uint8_t *dest8 = buf + (dest_msbfirst ? 0 : 1);
int i;
/* Go backwards so we don't overwrite unconverted data */
for (i = len-1; i >= 0; i--)
dest8[i*2] = src8[i];
} else if (src_bits == 16 && dest_bits == 8) {
/* 16 bit -> 8 bit */
const uint8_t *src8 = buf + (src_msbfirst ? 0 : 1);
uint8_t *dest8 = buf;
int i;
for (i = 0; i < len; i++)
dest8[i] = src8[i*2];
} else if (src_msbfirst != dest_msbfirst) {
/* 16 bit -> 16 bit, endian swap */
uint16_t *buf16 = buf;
int i;
for (i = 0; i < len; i++)
buf16[i] = buf16[i]<<8 | buf16[i]>>8;
} else {
/* N bit -> N bit, no change */
}
/* Convert signed/unsigned */
if (src_issigned != dest_issigned) {
int sampsize = dest_bits / 8;
uint8_t *buf8 = buf + (dest_msbfirst ? 0 : sampsize - 1);
int i;
for (i = 0; i < len * sampsize; i += sampsize)
buf8[i] ^= 0x80;
}
/* All done */
return 1;
}
/*************************************************************************/
/*************************************************************************/
/*
* Local variables:
* c-file-style: "stroustrup"
* c-file-offsets: ((case-label . *) (statement-case-intro . *))
* indent-tabs-mode: nil
* End:
*
* vim: expandtab shiftwidth=4:
*/