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
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:
|
|
*/
|