Mercurial > audlegacy-plugins
diff src/Output/OSS/convert.c @ 0:13389e613d67 trunk
[svn] - initial import of audacious-plugins tree (lots to do)
author | nenolod |
---|---|
date | Mon, 18 Sep 2006 01:11:49 -0700 |
parents | |
children |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/Output/OSS/convert.c Mon Sep 18 01:11:49 2006 -0700 @@ -0,0 +1,446 @@ +/* + * Copyright (C) 2001 Haavard Kvaalen + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "OSS.h" + +struct buffer { + void *buffer; + int size; +} format_buffer, stereo_buffer; + + +static void * +oss_get_convert_buffer(struct buffer *buffer, size_t size) +{ + if (size > 0 && size <= (size_t)buffer->size) + return buffer->buffer; + + buffer->size = size; + buffer->buffer = g_realloc(buffer->buffer, size); + return buffer->buffer; +} + +void +oss_free_convert_buffer(void) +{ + oss_get_convert_buffer(&format_buffer, 0); + oss_get_convert_buffer(&stereo_buffer, 0); +} + + +static int +convert_swap_endian(void **data, int length) +{ + guint16 *ptr = *data; + int i; + for (i = 0; i < length; i += 2, ptr++) + *ptr = GUINT16_SWAP_LE_BE(*ptr); + + return i; +} + +static int +convert_swap_sign_and_endian_to_native(void **data, int length) +{ + guint16 *ptr = *data; + int i; + for (i = 0; i < length; i += 2, ptr++) + *ptr = GUINT16_SWAP_LE_BE(*ptr) ^ 1 << 15; + + return i; +} + +static int +convert_swap_sign_and_endian_to_alien(void **data, int length) +{ + guint16 *ptr = *data; + int i; + for (i = 0; i < length; i += 2, ptr++) + *ptr = GUINT16_SWAP_LE_BE(*ptr ^ 1 << 15); + + return i; +} + +static int +convert_swap_sign16(void **data, int length) +{ + gint16 *ptr = *data; + int i; + for (i = 0; i < length; i += 2, ptr++) + *ptr ^= 1 << 15; + + return i; +} + +static int +convert_swap_sign8(void **data, int length) +{ + gint8 *ptr = *data; + int i; + for (i = 0; i < length; i++) + *ptr++ ^= 1 << 7; + + return i; +} + +static int +convert_to_8_native_endian(void **data, int length) +{ + gint8 *output = *data; + gint16 *input = *data; + int i; + for (i = 0; i < length / 2; i++) + *output++ = *input++ >> 8; + + return i; +} + +static int +convert_to_8_native_endian_swap_sign(void **data, int length) +{ + gint8 *output = *data; + gint16 *input = *data; + int i; + for (i = 0; i < length / 2; i++) + *output++ = (*input++ >> 8) ^ (1 << 7); + + return i; +} + + +static int +convert_to_8_alien_endian(void **data, int length) +{ + gint8 *output = *data; + gint16 *input = *data; + int i; + for (i = 0; i < length / 2; i++) + *output++ = *input++ & 0xff; + + return i; +} + +static int +convert_to_8_alien_endian_swap_sign(void **data, int length) +{ + gint8 *output = *data; + gint16 *input = *data; + int i; + for (i = 0; i < length / 2; i++) + *output++ = (*input++ & 0xff) ^ (1 << 7); + + return i; +} + +static int +convert_to_16_native_endian(void **data, int length) +{ + guint8 *input = *data; + guint16 *output; + int i; + *data = oss_get_convert_buffer(&format_buffer, length * 2); + output = *data; + for (i = 0; i < length; i++) + *output++ = *input++ << 8; + + return i * 2; +} + +static int +convert_to_16_native_endian_swap_sign(void **data, int length) +{ + guint8 *input = *data; + guint16 *output; + int i; + *data = oss_get_convert_buffer(&format_buffer, length * 2); + output = *data; + for (i = 0; i < length; i++) + *output++ = (*input++ << 8) ^ (1 << 15); + + return i * 2; +} + + +static int +convert_to_16_alien_endian(void **data, int length) +{ + guint8 *input = *data; + guint16 *output; + int i; + *data = oss_get_convert_buffer(&format_buffer, length * 2); + output = *data; + for (i = 0; i < length; i++) + *output++ = *input++; + + return i * 2; +} + +static int +convert_to_16_alien_endian_swap_sign(void **data, int length) +{ + guint8 *input = *data; + guint16 *output; + int i; + *data = oss_get_convert_buffer(&format_buffer, length * 2); + output = *data; + for (i = 0; i < length; i++) + *output++ = *input++ ^ (1 << 7); + + return i * 2; +} + +int (*oss_get_convert_func(int output, int input)) (void **, int) { + if (output == input) + return NULL; + + if ((output == AFMT_U16_BE && input == AFMT_U16_LE) || + (output == AFMT_U16_LE && input == AFMT_U16_BE) || + (output == AFMT_S16_BE && input == AFMT_S16_LE) || + (output == AFMT_S16_LE && input == AFMT_S16_BE)) + return convert_swap_endian; + + if ((output == AFMT_U16_BE && input == AFMT_S16_BE) || + (output == AFMT_U16_LE && input == AFMT_S16_LE) || + (output == AFMT_S16_BE && input == AFMT_U16_BE) || + (output == AFMT_S16_LE && input == AFMT_U16_LE)) + return convert_swap_sign16; + + if ((IS_BIG_ENDIAN && + ((output == AFMT_U16_BE && input == AFMT_S16_LE) || + (output == AFMT_S16_BE && input == AFMT_U16_LE))) || + (!IS_BIG_ENDIAN && + ((output == AFMT_U16_LE && input == AFMT_S16_BE) || + (output == AFMT_S16_LE && input == AFMT_U16_BE)))) + return convert_swap_sign_and_endian_to_native; + + if ((!IS_BIG_ENDIAN && + ((output == AFMT_U16_BE && input == AFMT_S16_LE) || + (output == AFMT_S16_BE && input == AFMT_U16_LE))) || + (IS_BIG_ENDIAN && + ((output == AFMT_U16_LE && input == AFMT_S16_BE) || + (output == AFMT_S16_LE && input == AFMT_U16_BE)))) + return convert_swap_sign_and_endian_to_alien; + + if ((IS_BIG_ENDIAN && + ((output == AFMT_U8 && input == AFMT_U16_BE) || + (output == AFMT_S8 && input == AFMT_S16_BE))) || + (!IS_BIG_ENDIAN && + ((output == AFMT_U8 && input == AFMT_U16_LE) || + (output == AFMT_S8 && input == AFMT_S16_LE)))) + return convert_to_8_native_endian; + + if ((IS_BIG_ENDIAN && + ((output == AFMT_U8 && input == AFMT_S16_BE) || + (output == AFMT_S8 && input == AFMT_U16_BE))) || + (!IS_BIG_ENDIAN && + ((output == AFMT_U8 && input == AFMT_S16_LE) || + (output == AFMT_S8 && input == AFMT_U16_LE)))) + return convert_to_8_native_endian_swap_sign; + + if ((!IS_BIG_ENDIAN && + ((output == AFMT_U8 && input == AFMT_U16_BE) || + (output == AFMT_S8 && input == AFMT_S16_BE))) || + (IS_BIG_ENDIAN && + ((output == AFMT_U8 && input == AFMT_U16_LE) || + (output == AFMT_S8 && input == AFMT_S16_LE)))) + return convert_to_8_alien_endian; + + if ((!IS_BIG_ENDIAN && + ((output == AFMT_U8 && input == AFMT_S16_BE) || + (output == AFMT_S8 && input == AFMT_U16_BE))) || + (IS_BIG_ENDIAN && + ((output == AFMT_U8 && input == AFMT_S16_LE) || + (output == AFMT_S8 && input == AFMT_U16_LE)))) + return convert_to_8_alien_endian_swap_sign; + + if ((output == AFMT_U8 && input == AFMT_S8) || + (output == AFMT_S8 && input == AFMT_U8)) + return convert_swap_sign8; + + if ((IS_BIG_ENDIAN && + ((output == AFMT_U16_BE && input == AFMT_U8) || + (output == AFMT_S16_BE && input == AFMT_S8))) || + (!IS_BIG_ENDIAN && + ((output == AFMT_U16_LE && input == AFMT_U8) || + (output == AFMT_S16_LE && input == AFMT_S8)))) + return convert_to_16_native_endian; + + if ((IS_BIG_ENDIAN && + ((output == AFMT_U16_BE && input == AFMT_S8) || + (output == AFMT_S16_BE && input == AFMT_U8))) || + (!IS_BIG_ENDIAN && + ((output == AFMT_U16_LE && input == AFMT_S8) || + (output == AFMT_S16_LE && input == AFMT_U8)))) + return convert_to_16_native_endian_swap_sign; + + if ((!IS_BIG_ENDIAN && + ((output == AFMT_U16_BE && input == AFMT_U8) || + (output == AFMT_S16_BE && input == AFMT_S8))) || + (IS_BIG_ENDIAN && + ((output == AFMT_U16_LE && input == AFMT_U8) || + (output == AFMT_S16_LE && input == AFMT_S8)))) + return convert_to_16_alien_endian; + + if ((!IS_BIG_ENDIAN && + ((output == AFMT_U16_BE && input == AFMT_S8) || + (output == AFMT_S16_BE && input == AFMT_U8))) || + (IS_BIG_ENDIAN && + ((output == AFMT_U16_LE && input == AFMT_S8) || + (output == AFMT_S16_LE && input == AFMT_U8)))) + return convert_to_16_alien_endian_swap_sign; + + g_warning("Translation needed, but not available.\n" + "Input: %d; Output %d.", input, output); + return NULL; +} + +static int +convert_mono_to_stereo(void **data, int length, int fmt) +{ + int i; + void *outbuf = oss_get_convert_buffer(&stereo_buffer, length * 2); + + if (fmt == AFMT_U8 || fmt == AFMT_S8) { + guint8 *output = outbuf, *input = *data; + for (i = 0; i < length; i++) { + *output++ = *input; + *output++ = *input; + input++; + } + } + else { + guint16 *output = outbuf, *input = *data; + for (i = 0; i < length / 2; i++) { + *output++ = *input; + *output++ = *input; + input++; + } + } + *data = outbuf; + + return length * 2; +} + +static int +convert_stereo_to_mono(void **data, int length, int fmt) +{ + int i; + + switch (fmt) { + case AFMT_U8: + { + guint8 *output = *data, *input = *data; + for (i = 0; i < length / 2; i++) { + guint16 tmp; + tmp = *input++; + tmp += *input++; + *output++ = tmp / 2; + } + } + break; + case AFMT_S8: + { + gint8 *output = *data, *input = *data; + for (i = 0; i < length / 2; i++) { + gint16 tmp; + tmp = *input++; + tmp += *input++; + *output++ = tmp / 2; + } + } + break; + case AFMT_U16_LE: + { + guint16 *output = *data, *input = *data; + for (i = 0; i < length / 4; i++) { + guint32 tmp; + guint16 stmp; + tmp = GUINT16_FROM_LE(*input); + input++; + tmp += GUINT16_FROM_LE(*input); + input++; + stmp = tmp / 2; + *output++ = GUINT16_TO_LE(stmp); + } + } + break; + case AFMT_U16_BE: + { + guint16 *output = *data, *input = *data; + for (i = 0; i < length / 4; i++) { + guint32 tmp; + guint16 stmp; + tmp = GUINT16_FROM_BE(*input); + input++; + tmp += GUINT16_FROM_BE(*input); + input++; + stmp = tmp / 2; + *output++ = GUINT16_TO_BE(stmp); + } + } + break; + case AFMT_S16_LE: + { + gint16 *output = *data, *input = *data; + for (i = 0; i < length / 4; i++) { + gint32 tmp; + gint16 stmp; + tmp = GINT16_FROM_LE(*input); + input++; + tmp += GINT16_FROM_LE(*input); + input++; + stmp = tmp / 2; + *output++ = GINT16_TO_LE(stmp); + } + } + break; + case AFMT_S16_BE: + { + gint16 *output = *data, *input = *data; + for (i = 0; i < length / 4; i++) { + gint32 tmp; + gint16 stmp; + tmp = GINT16_FROM_BE(*input); + input++; + tmp += GINT16_FROM_BE(*input); + input++; + stmp = tmp / 2; + *output++ = GINT16_TO_BE(stmp); + } + } + break; + default: + g_error("unknown format"); + } + + return length / 2; +} + +int (*oss_get_stereo_convert_func(int output, int input)) (void **, int, int) { + if (output == input) + return NULL; + + if (input == 1 && output == 2) + return convert_mono_to_stereo; + if (input == 2 && output == 1) + return convert_stereo_to_mono; + + g_warning("Input has %d channels, soundcard uses %d channels\n" + "No conversion is available", input, output); + return NULL; +}