summaryrefslogtreecommitdiffstats
path: root/audio
diff options
context:
space:
mode:
authorwm4 <wm4@nowhere>2013-10-22 01:20:43 +0200
committerwm4 <wm4@nowhere>2013-10-23 10:04:12 +0200
commite60b8f181dec744af25c3a52fb88f600cd1b63ea (patch)
tree31ab322709b207dea61d86600d5ce177b318238e /audio
parent33707c6d6394592f528fb200af2dd6e104fc6df6 (diff)
downloadmpv-e60b8f181dec744af25c3a52fb88f600cd1b63ea.tar.bz2
mpv-e60b8f181dec744af25c3a52fb88f600cd1b63ea.tar.xz
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.
Diffstat (limited to 'audio')
-rw-r--r--audio/filter/af.c110
-rw-r--r--audio/filter/af_convert24.c136
-rw-r--r--audio/filter/af_convertsignendian.c133
-rw-r--r--audio/filter/af_force.c8
-rw-r--r--audio/filter/af_format.c494
5 files changed, 365 insertions, 516 deletions
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 <http://www.gnu.org/licenses/>.
+ */
+
+#include <stdlib.h>
+#include <assert.h>
+
+#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 <http://www.gnu.org/licenses/>.
+ */
+
+#include <stdlib.h>
+#include <assert.h>
+
+#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 <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <inttypes.h>
-#include <limits.h>
-#include <math.h>
-#include <sys/types.h>
-
-#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<len;i++){
- ((uint16_t*)out)[i]=bswap_16(((uint16_t*)in)[i]);
- }
- break;
- }
- case(3):{
- register uint8_t s;
- for(i=0;i<len;i++){
- s=((uint8_t*)in)[3*i];
- ((uint8_t*)out)[3*i]=((uint8_t*)in)[3*i+2];
- if (in != out)
- ((uint8_t*)out)[3*i+1]=((uint8_t*)in)[3*i+1];
- ((uint8_t*)out)[3*i+2]=s;
- }
- break;
- }
- case(4):{
- for(i=0;i<len;i++){
- ((uint32_t*)out)[i]=bswap_32(((uint32_t*)in)[i]);
- }
- break;
- }
- }
-}
-
-static void si2us(void* data, int len, int bps)
-{
- register long i = -(len * bps);
- register uint8_t *p = &((uint8_t *)data)[len * bps];
-#if AF_FORMAT_NE == AF_FORMAT_LE
- p += bps - 1;
-#endif
- if (len <= 0) return;
- do {
- p[i] ^= 0x80;
- } while (i += bps);
-}
-
-static void change_bps(void* in, void* out, int len, int inbps, int outbps)
-{
- register int i;
- switch(inbps){
- case(1):
- switch(outbps){
- case(2):
- for(i=0;i<len;i++)
- ((uint16_t*)out)[i]=((uint16_t)((uint8_t*)in)[i])<<8;
- break;
- case(3):
- for(i=0;i<len;i++)
- store24bit(out, i, ((uint32_t)((uint8_t*)in)[i])<<24);
- break;
- case(4):
- for(i=0;i<len;i++)
- ((uint32_t*)out)[i]=((uint32_t)((uint8_t*)in)[i])<<24;
- break;
- }
- break;
- case(2):
- switch(outbps){
- case(1):
- for(i=0;i<len;i++)
- ((uint8_t*)out)[i]=(uint8_t)((((uint16_t*)in)[i])>>8);
- break;
- case(3):
- for(i=0;i<len;i++)
- store24bit(out, i, ((uint32_t)((uint16_t*)in)[i])<<16);
- break;
- case(4):
- for(i=0;i<len;i++)
- ((uint32_t*)out)[i]=((uint32_t)((uint16_t*)in)[i])<<16;
- break;
- }
- break;
- case(3):
- switch(outbps){
- case(1):
- for(i=0;i<len;i++)
- ((uint8_t*)out)[i]=(uint8_t)(load24bit(in, i)>>24);
- break;
- case(2):
- for(i=0;i<len;i++)
- ((uint16_t*)out)[i]=(uint16_t)(load24bit(in, i)>>16);
- break;
- case(4):
- for(i=0;i<len;i++)
- ((uint32_t*)out)[i]=(uint32_t)load24bit(in, i);
- break;
- }
- break;
- case(4):
- switch(outbps){
- case(1):
- for(i=0;i<len;i++)
- ((uint8_t*)out)[i]=(uint8_t)((((uint32_t*)in)[i])>>24);
- break;
- case(2):
- for(i=0;i<len;i++)
- ((uint16_t*)out)[i]=(uint16_t)((((uint32_t*)in)[i])>>16);
- break;
- case(3):
- for(i=0;i<len;i++)
- store24bit(out, i, ((uint32_t*)in)[i]);
- break;
- }
- break;
- }
-}
-
-static void float2int(float* in, void* out, int len, int bps)
-{
- register int i;
- switch(bps){
- case(1):
- for(i=0;i<len;i++)
- ((int8_t*)out)[i] = lrintf(127.0 * clamp(in[i], -1.0f, +1.0f));
- break;
- case(2):
- for(i=0;i<len;i++)
- ((int16_t*)out)[i] = lrintf(32767.0 * clamp(in[i], -1.0f, +1.0f));
- break;
- case(3):
- for(i=0;i<len;i++)
- store24bit(out, i, lrintf(2147483647.0 * clamp(in[i], -1.0f, +1.0f)));
- break;
- case(4):
- for(i=0;i<len;i++)
- ((int32_t*)out)[i] = lrintf(2147483647.0 * clamp(in[i], -1.0f, +1.0f));
- break;
- }
-}
-
-static void int2float(void* in, float* out, int len, int bps)
-{
- register int i;
- switch(bps){
- case(1):
- for(i=0;i<len;i++)
- out[i]=(1.0/128.0)*((int8_t*)in)[i];
- break;
- case(2):
- for(i=0;i<len;i++)
- out[i]=(1.0/32768.0)*((int16_t*)in)[i];
- break;
- case(3):
- for(i=0;i<len;i++)
- out[i]=(1.0/2147483648.0)*((int32_t)load24bit(in, i));
- break;
- case(4):
- for(i=0;i<len;i++)
- out[i]=(1.0/2147483648.0)*((int32_t*)in)[i];
- break;
- }
-}