summaryrefslogtreecommitdiffstats
path: root/audio/filter/af_convert24.c
diff options
context:
space:
mode:
Diffstat (limited to 'audio/filter/af_convert24.c')
-rw-r--r--audio/filter/af_convert24.c136
1 files changed, 136 insertions, 0 deletions
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,
+};