From e60b8f181dec744af25c3a52fb88f600cd1b63ea Mon Sep 17 00:00:00 2001 From: wm4 Date: Tue, 22 Oct 2013 01:20:43 +0200 Subject: audio/filter: split af_format into separate filters, rename af_force af_format is the old audio conversion filter. It could do all possible conversions supported by the audio chain. However, ever since the addition of af_lavrresample, most conversions are done by libav/swresample, and af_format is used as fallback. Separate out the fallback cases and remove af_format. af_convert24 does 24 bit <-> 32 bit conversions, while af_convertsignendian does sign and endian conversions. Maybe the way the conversions are split sounds a bit odd. But the former changes the size of the audio data, while the latter is fully in-place, so there's at least different buffer management. This requires a quite complicated algorithm to make sure all these "partial" conversion filters can actually get from one format to another. E.g. s24le->s32be always requires convertsignendian and convert24, but af.c has no idea what the intermediate format should be. So I added a graph search (trying every possible format and filter) to determine required format and filter. When I wrote this, it seemed this was still better than messing everything into af_lavrresample, but maybe this is overkill and I'll change my opinion. For now, it seems nice to get rid of af_format though. The AC3->IEC61937 conversion isn't supported anymore, but I don't think this is needed anywhere. Most AOs test all formats explicitly, or use the AF_FORMAT_IS_IEC61937() macro (which includes AC3). One positive consequence of this change is that conversions always include dithering (done by libav/swresample), instead of possibly going through af_format, which doesn't do anything fancy. Rename af_force to af_format. It's essentially compatible with command line uses of af_format. We retain a compatibility alias for af_force. --- audio/filter/af.c | 110 ++++++-- audio/filter/af_convert24.c | 136 ++++++++++ audio/filter/af_convertsignendian.c | 133 ++++++++++ audio/filter/af_force.c | 8 +- audio/filter/af_format.c | 494 ------------------------------------ 5 files changed, 365 insertions(+), 516 deletions(-) create mode 100644 audio/filter/af_convert24.c create mode 100644 audio/filter/af_convertsignendian.c delete mode 100644 audio/filter/af_format.c (limited to 'audio') diff --git a/audio/filter/af.c b/audio/filter/af.c index 00eb2346f9..62dce8df7e 100644 --- a/audio/filter/af.c +++ b/audio/filter/af.c @@ -52,12 +52,14 @@ extern struct af_info af_info_karaoke; extern struct af_info af_info_scaletempo; extern struct af_info af_info_bs2b; extern struct af_info af_info_lavfi; +extern struct af_info af_info_convert24; +extern struct af_info af_info_convertsignendian; static struct af_info* filter_list[] = { &af_info_dummy, &af_info_delay, &af_info_channels, - &af_info_force, + &af_info_format, &af_info_volume, &af_info_equalizer, &af_info_pan, @@ -85,8 +87,9 @@ static struct af_info* filter_list[] = { #ifdef CONFIG_AF_LAVFI &af_info_lavfi, #endif - // Must come last, because it's the fallback format conversion filter - &af_info_format, + // Must come last, because they're fallback format conversion filter + &af_info_convert24, + &af_info_convertsignendian, NULL }; @@ -109,6 +112,10 @@ static bool get_desc(struct m_obj_desc *dst, int index) const struct m_obj_list af_obj_list = { .get_desc = get_desc, .description = "audio filters", + .aliases = { + {"force", "format"}, + {0} + }, .legacy_hacks = true, // many filters have custom option parsing }; @@ -285,7 +292,8 @@ static void af_remove(struct af_stream *s, struct af_instance *af) af->prev->next = af->next; af->next->prev = af->prev; - af->uninit(af); + if (af->uninit) + af->uninit(af); talloc_free(af); } @@ -334,14 +342,76 @@ static int af_count_filters(struct af_stream *s) return count; } -static char *af_find_conversion_filter(int srcfmt, int dstfmt) +// Finds the first conversion filter on the way from srcfmt to dstfmt. +// Conversions form a DAG: each node is a format/filter pair, and possible +// conversions are edges. We search the DAG for the shortest path. +// Some cases visit the same filter multiple times, but with different formats +// (like u24le->s8), so one node per format or filter separate is not enough. +// Returns the filter and dest. format for the first conversion step. +// (So we know what conversion filter with what format to insert next.) +static char *af_find_conversion_filter(int srcfmt, int *dstfmt) { - for (int n = 0; filter_list[n]; n++) { - struct af_info *af = filter_list[n]; - if (af->test_conversion && af->test_conversion(srcfmt, dstfmt)) - return (char *)af->name; +#define NUM_FMT 64 +#define NUM_FILT 32 +#define NUM_NODES (NUM_FMT * NUM_FILT) + for (int n = 0; filter_list[n]; n++) + assert(n < NUM_FILT); + for (int n = 0; af_fmtstr_table[n].format; n++) + assert(n < NUM_FMT); + + bool visited[NUM_NODES] = {0}; + unsigned char distance[NUM_NODES]; + short previous[NUM_NODES] = {0}; + for (int n = 0; n < NUM_NODES; n++) { + distance[n] = 255; + if (af_fmtstr_table[n % NUM_FMT].format == srcfmt) + distance[n] = 0; } - return NULL; + + while (1) { + int next = -1; + for (int n = 0; n < NUM_NODES; n++) { + if (!visited[n] && (next < 0 || (distance[n] < distance[next]))) + next = n; + } + if (next < 0 || distance[next] == 255) + return NULL; + visited[next] = true; + + int fmt = next % NUM_FMT; + if (af_fmtstr_table[fmt].format == *dstfmt) { + // Best match found + for (int cur = next; cur >= 0; cur = previous[cur] - 1) { + if (distance[cur] == 1) { + *dstfmt = af_fmtstr_table[cur % NUM_FMT].format; + return (char *)filter_list[cur / NUM_FMT]->name; + } + } + return NULL; + } + + for (int n = 0; filter_list[n]; n++) { + struct af_info *af = filter_list[n]; + if (!af->test_conversion) + continue; + for (int i = 0; af_fmtstr_table[i].format; i++) { + if (i != fmt && af->test_conversion(af_fmtstr_table[fmt].format, + af_fmtstr_table[i].format)) + { + int other = n * NUM_FMT + i; + int ndist = distance[next] + 1; + if (ndist < distance[other]) { + distance[other] = ndist; + previous[other] = next + 1; + } + } + } + } + } + assert(0); +#undef NUM_FMT +#undef NUM_FILT +#undef NODE_N } static bool af_is_conversion_filter(struct af_instance *af) @@ -352,7 +422,7 @@ static bool af_is_conversion_filter(struct af_instance *af) // in is what af can take as input - insert a conversion filter if the actual // input format doesn't match what af expects. // Returns: -// AF_OK: must call af_reinit() or equivalent, format matches +// AF_OK: must call af_reinit() or equivalent, format matches (or is closer) // AF_FALSE: nothing was changed, format matches // else: error static int af_fix_format_conversion(struct af_stream *s, @@ -365,18 +435,21 @@ static int af_fix_format_conversion(struct af_stream *s, struct mp_audio actual = *prev->data; if (actual.format == in.format) return AF_FALSE; - if (prev->control(prev, AF_CONTROL_FORMAT_FMT, &in.format) == AF_OK) { - *p_af = prev; - return AF_OK; - } - char *filter = af_find_conversion_filter(actual.format, in.format); + int dstfmt = in.format; + char *filter = af_find_conversion_filter(actual.format, &dstfmt); if (!filter) return AF_ERROR; + if (strcmp(filter, prev->info->name) == 0) { + if (prev->control(prev, AF_CONTROL_FORMAT_FMT, &dstfmt) == AF_OK) { + *p_af = prev; + return AF_OK; + } + } struct af_instance *new = af_prepend(s, af, filter, NULL); if (new == NULL) return AF_ERROR; new->auto_inserted = true; - if (AF_OK != (rv = new->control(new, AF_CONTROL_FORMAT_FMT, &in.format))) + if (AF_OK != (rv = new->control(new, AF_CONTROL_FORMAT_FMT, &dstfmt))) return rv; *p_af = new; return AF_OK; @@ -442,7 +515,8 @@ static int af_reinit(struct af_stream *s) // Start with the second filter, as the first filter is the special input // filter which needs no initialization. struct af_instance *af = s->first->next; - int max_retry = af_count_filters(s) * 4; // up to 4 retries per filter + // Up to 7 retries per filter (channel, rate, 5x format conversions) + int max_retry = af_count_filters(s) * 7; int retry = 0; while (af) { if (retry >= max_retry) diff --git a/audio/filter/af_convert24.c b/audio/filter/af_convert24.c new file mode 100644 index 0000000000..f3d9a6d7a5 --- /dev/null +++ b/audio/filter/af_convert24.c @@ -0,0 +1,136 @@ +/* + * This file is part of mpv. + * + * mpv 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. + * + * mpv 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 mpv. If not, see . + */ + +#include +#include + +#include "audio/format.h" +#include "af.h" + +static bool test_conversion(int src_format, int dst_format) +{ + if (!(src_format & AF_FORMAT_POINT_MASK) == AF_FORMAT_I) + return false; + if ((src_format & ~AF_FORMAT_BITS_MASK) != + (dst_format & ~AF_FORMAT_BITS_MASK)) + return false; + int srcbits = src_format & AF_FORMAT_BITS_MASK; + int dstbits = dst_format & AF_FORMAT_BITS_MASK; + return (srcbits == AF_FORMAT_24BIT && dstbits == AF_FORMAT_32BIT) || + (srcbits == AF_FORMAT_32BIT && dstbits == AF_FORMAT_24BIT); +} + +static int control(struct af_instance *af, int cmd, void *arg) +{ + switch (cmd) { + case AF_CONTROL_REINIT: { + struct mp_audio *in = arg; + struct mp_audio orig_in = *in; + struct mp_audio *out = af->data; + + if (!test_conversion(in->format, out->format)) + return AF_DETACH; + + if ((in->format & AF_FORMAT_BITS_MASK) == AF_FORMAT_24BIT) { + mp_audio_set_format(out, af_fmt_change_bits(in->format, 32)); + } else if ((in->format & AF_FORMAT_BITS_MASK) == AF_FORMAT_32BIT) { + mp_audio_set_format(out, af_fmt_change_bits(in->format, 24)); + } else { + abort(); + } + + out->rate = in->rate; + mp_audio_set_channels(out, &in->channels); + + assert(test_conversion(in->format, out->format)); + + af->mul = (double)out->bps / in->bps; + + return mp_audio_config_equals(in, &orig_in) ? AF_OK : AF_FALSE; + } + case AF_CONTROL_FORMAT_FMT | AF_CONTROL_SET: { + mp_audio_set_format(af->data, *(int*)arg); + return AF_OK; + } + } + return AF_UNKNOWN; +} + +// The LSB is always ignored. +#if BYTE_ORDER == BIG_ENDIAN +#define SHIFT(x) ((3-(x))*8) +#else +#define SHIFT(x) (((x)+1)*8) +#endif + +static struct mp_audio *play(struct af_instance *af, struct mp_audio *data) +{ + if (RESIZE_LOCAL_BUFFER(af, data) != AF_OK) + return NULL; + + struct mp_audio *out = af->data; + size_t len = data->len / data->bps; + + if (data->bps == 4) { + for (int s = 0; s < len; s++) { + uint32_t val = *((uint32_t *)data->audio + s); + uint8_t *ptr = (uint8_t *)out->audio + s * 3; + ptr[0] = val >> SHIFT(0); + ptr[1] = val >> SHIFT(1); + ptr[2] = val >> SHIFT(2); + } + mp_audio_set_format(data, af_fmt_change_bits(data->format, 24)); + } else { + for (int s = 0; s < len; s++) { + uint8_t *ptr = (uint8_t *)data->audio + s * 3; + uint32_t val = ptr[0] << SHIFT(0) + | ptr[1] << SHIFT(1) + | ptr[2] << SHIFT(2); + *((uint32_t *)out->audio + s) = val; + } + mp_audio_set_format(data, af_fmt_change_bits(data->format, 32)); + } + + data->audio = out->audio; + data->len = len * data->bps; + return data; +} + +static void uninit(struct af_instance* af) +{ + if (af->data) + free(af->data->audio); +} + +static int af_open(struct af_instance *af) +{ + af->control = control; + af->play = play; + af->uninit = uninit; + af->data = talloc_zero(af, struct mp_audio); + return AF_OK; +} + +struct af_info af_info_convert24 = { + "Convert between 24 and 32 bit sample format", + "convert24", + "", + "", + 0, + af_open, + .test_conversion = test_conversion, +}; diff --git a/audio/filter/af_convertsignendian.c b/audio/filter/af_convertsignendian.c new file mode 100644 index 0000000000..c5e0004a50 --- /dev/null +++ b/audio/filter/af_convertsignendian.c @@ -0,0 +1,133 @@ +/* + * This file is part of mpv. + * + * mpv 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. + * + * mpv 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 mpv. If not, see . + */ + +#include +#include + +#include "af.h" +#include "audio/format.h" +#include "compat/mpbswap.h" + +static bool test_conversion(int src_format, int dst_format) +{ + int src_noend = src_format & ~AF_FORMAT_END_MASK; + int dst_noend = dst_format & ~AF_FORMAT_END_MASK; + // We can swap endian for all formats, but sign only for integer formats. + if (src_noend == dst_noend) + return true; + if (((src_noend & ~AF_FORMAT_SIGN_MASK) == + (dst_noend & ~AF_FORMAT_SIGN_MASK)) && + ((src_noend & AF_FORMAT_POINT_MASK) == AF_FORMAT_I)) + return true; + return false; +} + +static int control(struct af_instance *af, int cmd, void *arg) +{ + switch (cmd) { + case AF_CONTROL_REINIT: { + struct mp_audio *in = arg; + struct mp_audio orig_in = *in; + struct mp_audio *out = af->data; + + if (!test_conversion(in->format, out->format)) + return AF_DETACH; + + out->rate = in->rate; + mp_audio_set_channels(out, &in->channels); + + return mp_audio_config_equals(in, &orig_in) ? AF_OK : AF_FALSE; + } + case AF_CONTROL_FORMAT_FMT | AF_CONTROL_SET: { + mp_audio_set_format(af->data, *(int*)arg); + return AF_OK; + } + } + return AF_UNKNOWN; +} + +static void endian(void *data, int len, int bps) +{ + switch (bps) { + case 2: + for (int i = 0; i < len; i++) { + ((uint16_t*)data)[i] = bswap_16(((uint16_t *)data)[i]); + } + break; + case 3: + for(int i = 0; i < len; i++) { + uint8_t s = ((uint8_t *)data)[3 * i]; + ((uint8_t *)data)[3 * i] = ((uint8_t *)data)[3 * i + 2]; + ((uint8_t *)data)[3 * i + 2] = s; + } + break; + case 4: + for(int i = 0; i < len; i++) { + ((uint32_t*)data)[i] = bswap_32(((uint32_t *)data)[i]); + } + break; + } +} + +static void si2us(void *data, int len, int bps, bool le) +{ + ptrdiff_t i = -(len * bps); + uint8_t *p = &((uint8_t *)data)[len * bps]; + if (le && bps > 1) + p += bps - 1; + if (len <= 0) + return; + do { + p[i] ^= 0x80; + } while (i += bps); +} + +static struct mp_audio *play(struct af_instance *af, struct mp_audio *data) +{ + int infmt = data->format; + int outfmt = af->data->format; + size_t len = data->len / data->bps; + + if ((infmt & AF_FORMAT_END_MASK) != (outfmt & AF_FORMAT_END_MASK)) + endian(data->audio, len, data->bps); + + if ((infmt & AF_FORMAT_SIGN_MASK) != (outfmt & AF_FORMAT_SIGN_MASK)) + si2us(data->audio, len, data->bps, + (outfmt & AF_FORMAT_END_MASK) == AF_FORMAT_LE); + + mp_audio_set_format(data, outfmt); + return data; +} + +static int af_open(struct af_instance *af) +{ + af->control = control; + af->play = play; + af->mul = 1; + af->data = talloc_zero(af, struct mp_audio); + return AF_OK; +} + +struct af_info af_info_convertsignendian = { + "Convert between sample format sign/endian", + "convertsignendian", + "", + "", + 0, + af_open, + .test_conversion = test_conversion, +}; diff --git a/audio/filter/af_force.c b/audio/filter/af_force.c index adf17c61f2..21af663e81 100644 --- a/audio/filter/af_force.c +++ b/audio/filter/af_force.c @@ -72,12 +72,12 @@ static int control(struct af_instance *af, int cmd, void *arg) if (in->nch != out->nch || in->bps != out->bps) { mp_msg(MSGT_AFILTER, MSGL_ERR, - "[af_force] Forced input/output formats are incompatible.\n"); + "[af_format] Forced input/output formats are incompatible.\n"); return AF_ERROR; } if (priv->fail) { - mp_msg(MSGT_AFILTER, MSGL_ERR, "[af_force] Failing on purpose.\n"); + mp_msg(MSGT_AFILTER, MSGL_ERR, "[af_format] Failing on purpose.\n"); return AF_ERROR; } @@ -116,9 +116,9 @@ static int af_open(struct af_instance *af) #define OPT_BASE_STRUCT struct priv -struct af_info af_info_force = { +struct af_info af_info_format = { "Force audio format", - "force", + "format", "", "", 0, diff --git a/audio/filter/af_format.c b/audio/filter/af_format.c deleted file mode 100644 index 6166ee3ff0..0000000000 --- a/audio/filter/af_format.c +++ /dev/null @@ -1,494 +0,0 @@ -/* - * This audio filter changes the format of a data block. Valid - * formats are: AFMT_U8, AFMT_S8, AFMT_S16_LE, AFMT_S16_BE - * AFMT_U16_LE, AFMT_U16_BE, AFMT_S32_LE and AFMT_S32_BE. - * - * This file is part of MPlayer. - * - * MPlayer 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. - * - * MPlayer 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 MPlayer; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - */ - -#include -#include -#include -#include -#include -#include -#include - -#include "config.h" -#include "af.h" -#include "compat/mpbswap.h" - -/* Functions used by play to convert the input audio to the correct - format */ - -// Switch endianness -static void endian(void* in, void* out, int len, int bps); -// From signed to unsigned and the other way -static void si2us(void* data, int len, int bps); -// Change the number of bits per sample -static void change_bps(void* in, void* out, int len, int inbps, int outbps); -// From float to int signed -static void float2int(float* in, void* out, int len, int bps); -// From signed int to float -static void int2float(void* in, float* out, int len, int bps); - -static struct mp_audio* play(struct af_instance* af, struct mp_audio* data); -static struct mp_audio* play_swapendian(struct af_instance* af, struct mp_audio* data); -static struct mp_audio* play_float_s16(struct af_instance* af, struct mp_audio* data); -static struct mp_audio* play_s16_float(struct af_instance* af, struct mp_audio* data); - -// Helper functions to check sanity for input arguments - -// Sanity check for bytes per sample -static int check_bps(int bps) -{ - if(bps != 4 && bps != 3 && bps != 2 && bps != 1){ - mp_msg(MSGT_AFILTER, MSGL_ERR, "[format] The number of bytes per sample" - " must be 1, 2, 3 or 4. Current value is %i \n",bps); - return AF_ERROR; - } - return AF_OK; -} - -// Check for unsupported formats -static int check_format(int format) -{ - char buf[256]; - if ((format & AF_FORMAT_SPECIAL_MASK) == 0) - return AF_OK; - mp_msg(MSGT_AFILTER, MSGL_ERR, "[format] Sample format %s not yet supported \n", - af_fmt2str(format,buf,256)); - return AF_ERROR; -} - -static bool test_conversion(int src_format, int dst_format) -{ - // This is the fallback conversion filter, so this filter is always - // inserted on format mismatches if no other filter can handle it. - // Initializing the filter might still fail. - return true; -} - -// Initialization and runtime control -static int control(struct af_instance* af, int cmd, void* arg) -{ - switch(cmd){ - case AF_CONTROL_REINIT:{ - char buf1[256]; - char buf2[256]; - struct mp_audio *data = arg; - int supported_ac3 = 0; - - // Make sure this filter isn't redundant - if(af->data->format == data->format) - return AF_DETACH; - - // A bit complex because we can convert AC3 - // to generic iec61937 but not the other way - // round. - if (AF_FORMAT_IS_AC3(af->data->format)) - supported_ac3 = AF_FORMAT_IS_AC3(data->format); - else if (AF_FORMAT_IS_IEC61937(af->data->format)) - supported_ac3 = AF_FORMAT_IS_IEC61937(data->format); - - // Allow trivial AC3-endianness conversion - if (!supported_ac3) - // Check for errors in configuration - if((AF_OK != check_bps(data->bps)) || - (AF_OK != check_format(data->format)) || - (AF_OK != check_bps(af->data->bps)) || - (AF_OK != check_format(af->data->format))) - return AF_ERROR; - - af_fmt2str(data->format,buf1,256); - af_fmt2str(af->data->format,buf2,256); - mp_msg(MSGT_AFILTER, MSGL_V, "[format] Changing sample format from %s to %s\n", - buf1, buf2); - - af->data->rate = data->rate; - mp_audio_set_channels(af->data, &data->channels); - af->mul = (double)af->data->bps / data->bps; - - af->play = play; // set default - - // look whether only endianness differences are there - if ((af->data->format & ~AF_FORMAT_END_MASK) == - (data->format & ~AF_FORMAT_END_MASK)) - { - mp_msg(MSGT_AFILTER, MSGL_V, "[format] Accelerated endianness conversion only\n"); - af->play = play_swapendian; - } - if ((data->format == AF_FORMAT_FLOAT_NE) && - (af->data->format == AF_FORMAT_S16_NE)) - { - mp_msg(MSGT_AFILTER, MSGL_V, "[format] Accelerated %s to %s conversion\n", - buf1, buf2); - af->play = play_float_s16; - } - if ((data->format == AF_FORMAT_S16_NE) && - (af->data->format == AF_FORMAT_FLOAT_NE)) - { - mp_msg(MSGT_AFILTER, MSGL_V, "[format] Accelerated %s to %s conversion\n", - buf1, buf2); - af->play = play_s16_float; - } - return AF_OK; - } - case AF_CONTROL_COMMAND_LINE:{ - int format = af_str2fmt_short(bstr0(arg)); - if (!format) { - mp_msg(MSGT_AFILTER, MSGL_ERR, "[format] %s is not a valid format\n", (char *)arg); - return AF_ERROR; - } - if(AF_OK != af->control(af, AF_CONTROL_FORMAT_FMT | AF_CONTROL_SET,&format)) - return AF_ERROR; - return AF_OK; - } - case AF_CONTROL_FORMAT_FMT | AF_CONTROL_SET:{ - // Check for errors in configuration - if(!AF_FORMAT_IS_AC3(*(int*)arg) && AF_OK != check_format(*(int*)arg)) - return AF_ERROR; - - mp_audio_set_format(af->data, *(int*)arg); - - return AF_OK; - } - } - return AF_UNKNOWN; -} - -// Deallocate memory -static void uninit(struct af_instance* af) -{ - if (af->data) - free(af->data->audio); - free(af->data); - af->setup = 0; -} - -static struct mp_audio* play_swapendian(struct af_instance* af, struct mp_audio* data) -{ - struct mp_audio* l = af->data; // Local data - struct mp_audio* c = data; // Current working data - int len = c->len/c->bps; // Length in samples of current audio block - - if(AF_OK != RESIZE_LOCAL_BUFFER(af,data)) - return NULL; - - endian(c->audio,l->audio,len,c->bps); - - c->audio = l->audio; - mp_audio_set_format(c, l->format); - - return c; -} - -static struct mp_audio* play_float_s16(struct af_instance* af, struct mp_audio* data) -{ - struct mp_audio* l = af->data; // Local data - struct mp_audio* c = data; // Current working data - int len = c->len/4; // Length in samples of current audio block - - if(AF_OK != RESIZE_LOCAL_BUFFER(af,data)) - return NULL; - - float2int(c->audio, l->audio, len, 2); - - c->audio = l->audio; - mp_audio_set_format(c, l->format); - c->len = len*2; - - return c; -} - -static struct mp_audio* play_s16_float(struct af_instance* af, struct mp_audio* data) -{ - struct mp_audio* l = af->data; // Local data - struct mp_audio* c = data; // Current working data - int len = c->len/2; // Length in samples of current audio block - - if(AF_OK != RESIZE_LOCAL_BUFFER(af,data)) - return NULL; - - int2float(c->audio, l->audio, len, 2); - - c->audio = l->audio; - mp_audio_set_format(c, l->format); - c->len = len*4; - - return c; -} - -// Filter data through filter -static struct mp_audio* play(struct af_instance* af, struct mp_audio* data) -{ - struct mp_audio* l = af->data; // Local data - struct mp_audio* c = data; // Current working data - int len = c->len/c->bps; // Length in samples of current audio block - - if(AF_OK != RESIZE_LOCAL_BUFFER(af,data)) - return NULL; - - // Change to cpu native endian format - if((c->format&AF_FORMAT_END_MASK)!=AF_FORMAT_NE) - endian(c->audio,c->audio,len,c->bps); - - // Conversion table - if((c->format & AF_FORMAT_POINT_MASK) == AF_FORMAT_F) { - float2int(c->audio, l->audio, len, l->bps); - if((l->format&AF_FORMAT_SIGN_MASK) == AF_FORMAT_US) - si2us(l->audio,len,l->bps); - } else { - // Input must be int - - // Change signed/unsigned - if((c->format&AF_FORMAT_SIGN_MASK) != (l->format&AF_FORMAT_SIGN_MASK)){ - si2us(c->audio,len,c->bps); - } - // Convert to special formats - switch(l->format&AF_FORMAT_POINT_MASK){ - case(AF_FORMAT_F): - int2float(c->audio, l->audio, len, c->bps); - break; - default: - // Change the number of bits - if(c->bps != l->bps) - change_bps(c->audio,l->audio,len,c->bps,l->bps); - else - memcpy(l->audio,c->audio,len*c->bps); - break; - } - } - - // Switch from cpu native endian to the correct endianness - if((l->format&AF_FORMAT_END_MASK)!=AF_FORMAT_NE) - endian(l->audio,l->audio,len,l->bps); - - // Set output data - c->audio = l->audio; - mp_audio_set_format(c, l->format); - c->len = len*l->bps; - return c; -} - -// Allocate memory and set function pointers -static int af_open(struct af_instance* af){ - af->control=control; - af->uninit=uninit; - af->play=play; - af->mul=1; - af->data=calloc(1,sizeof(struct mp_audio)); - if(af->data == NULL) - return AF_ERROR; - return AF_OK; -} - -// Description of this filter -struct af_info af_info_format = { - "Sample format conversion", - "format", - "Anders", - "", - AF_FLAGS_REENTRANT, - af_open, - .test_conversion = test_conversion, -}; - -static inline uint32_t load24bit(void* data, int pos) { -#if BYTE_ORDER == BIG_ENDIAN - return (((uint32_t)((uint8_t*)data)[3*pos])<<24) | - (((uint32_t)((uint8_t*)data)[3*pos+1])<<16) | - (((uint32_t)((uint8_t*)data)[3*pos+2])<<8); -#else - return (((uint32_t)((uint8_t*)data)[3*pos])<<8) | - (((uint32_t)((uint8_t*)data)[3*pos+1])<<16) | - (((uint32_t)((uint8_t*)data)[3*pos+2])<<24); -#endif -} - -static inline void store24bit(void* data, int pos, uint32_t expanded_value) { -#if BYTE_ORDER == BIG_ENDIAN - ((uint8_t*)data)[3*pos]=expanded_value>>24; - ((uint8_t*)data)[3*pos+1]=expanded_value>>16; - ((uint8_t*)data)[3*pos+2]=expanded_value>>8; -#else - ((uint8_t*)data)[3*pos]=expanded_value>>8; - ((uint8_t*)data)[3*pos+1]=expanded_value>>16; - ((uint8_t*)data)[3*pos+2]=expanded_value>>24; -#endif -} - -// Function implementations used by play -static void endian(void* in, void* out, int len, int bps) -{ - register int i; - switch(bps){ - case(2):{ - for(i=0;i>8); - break; - case(3): - for(i=0;i>24); - break; - case(2): - for(i=0;i>16); - break; - case(4): - for(i=0;i>24); - break; - case(2): - for(i=0;i>16); - break; - case(3): - for(i=0;i