summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Copyright4
-rw-r--r--audio/filter/af.c1
-rw-r--r--audio/filter/af.h2
-rw-r--r--audio/out/ao.c66
-rw-r--r--audio/out/ao.h1
-rw-r--r--audio/out/internal.h5
-rw-r--r--audio/out/pull.c2
-rw-r--r--audio/out/push.c1
-rw-r--r--player/audio.c115
9 files changed, 134 insertions, 63 deletions
diff --git a/Copyright b/Copyright
index fc918c4be6..57b7a75cd9 100644
--- a/Copyright
+++ b/Copyright
@@ -34,8 +34,8 @@ a LGPL mode to mpv, MPlayer code had to be relicensed from GPLv2+ to LGPLv2.1+
by asking the MPlayer authors for permission. Since permission could not be
obtained from everyone, LGPL mode disables the following features, some of
them quite central:
-- no audio filtering, which breaks: --volume, --af, replaygain, pitch
- correction, fine control about downmix/upmix/resampling behavior
+- no audio filtering, which breaks: --af, pitch correction, fine control over
+ downmix/upmix/resampling behavior
- Linux X11 video output
- BSD audio output via OSS
- NVIDIA/Linux hardware decoding (vdpau, although nvdec usually works)
diff --git a/audio/filter/af.c b/audio/filter/af.c
index dd78bb0cb5..5838c2e70b 100644
--- a/audio/filter/af.c
+++ b/audio/filter/af.c
@@ -167,7 +167,6 @@ static struct af_instance *af_create(struct af_stream *s, char *name,
.log = mp_log_new(af, s->log, name),
.opts = s->opts,
.global = s->global,
- .replaygain_data = s->replaygain_data,
.out_pool = mp_audio_pool_create(af),
};
struct m_config *config =
diff --git a/audio/filter/af.h b/audio/filter/af.h
index 58f67727a2..f27edee71a 100644
--- a/audio/filter/af.h
+++ b/audio/filter/af.h
@@ -61,7 +61,6 @@ struct af_instance {
struct mp_log *log;
struct MPOpts *opts;
struct mpv_global *global;
- struct replaygain_data *replaygain_data;
int (*control)(struct af_instance *af, int cmd, void *arg);
void (*uninit)(struct af_instance *af);
/* Feed a frame. The frame is NULL if EOF was reached, and the filter
@@ -105,7 +104,6 @@ struct af_stream {
struct mp_log *log;
struct MPOpts *opts;
struct mpv_global *global;
- struct replaygain_data *replaygain_data;
};
// Return values
diff --git a/audio/out/ao.c b/audio/out/ao.c
index da2dacdcae..c40eae1b92 100644
--- a/audio/out/ao.c
+++ b/audio/out/ao.c
@@ -18,6 +18,7 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
+#include <math.h>
#include <assert.h>
#include "mpv_talloc.h"
@@ -159,6 +160,7 @@ static struct ao *ao_alloc(bool probing, struct mpv_global *global,
ao->priv = m_config_group_from_desc(ao, ao->log, global, &desc, name);
if (!ao->priv)
goto error;
+ ao_set_gain(ao, 1.0f);
return ao;
error:
talloc_free(ao);
@@ -638,6 +640,70 @@ void ao_print_devices(struct mpv_global *global, struct mp_log *log)
ao_hotplug_destroy(hp);
}
+void ao_set_gain(struct ao *ao, float gain)
+{
+ uint_least32_t v = 0;
+ assert(sizeof(gain) <= sizeof(v));
+ memcpy(&v, &gain, sizeof(gain));
+ atomic_store(&ao->gain_fi, v);
+}
+
+static float ao_get_gain(struct ao *ao)
+{
+ uint_least32_t v = atomic_load_explicit(&ao->gain_fi, memory_order_relaxed);
+ float gain;
+ assert(sizeof(gain) <= sizeof(v));
+ memcpy(&gain, &v, sizeof(gain));
+ return gain;
+}
+
+#define MUL_GAIN_i(d, num_samples, gain, low, center, high) \
+ for (int n = 0; n < (num_samples); n++) \
+ (d)[n] = MPCLAMP( \
+ ((((int64_t)((d)[n]) - (center)) * (gain) + 128) >> 8) + (center), \
+ (low), (high))
+
+#define MUL_GAIN_f(d, num_samples, gain) \
+ for (int n = 0; n < (num_samples); n++) \
+ (d)[n] = MPCLAMP(((d)[n]) * (gain), -1.0, 1.0)
+
+static void process_plane(struct ao *ao, void *data, int num_samples)
+{
+ float gain = ao_get_gain(ao);
+ int format = af_fmt_from_planar(ao->format);
+ if (gain == 1.0f)
+ return;
+ int gi = lrint(256.0 * gain);
+ switch (format) {
+ case AF_FORMAT_U8:
+ MUL_GAIN_i((uint8_t *)data, num_samples, gi, 0, 128, 255);
+ break;
+ case AF_FORMAT_S16:
+ MUL_GAIN_i((int16_t *)data, num_samples, gi, INT16_MIN, 0, INT16_MAX);
+ break;
+ case AF_FORMAT_S32:
+ MUL_GAIN_i((int32_t *)data, num_samples, gi, INT32_MIN, 0, INT32_MAX);
+ break;
+ case AF_FORMAT_FLOAT:
+ MUL_GAIN_f((float *)data, num_samples, gain);
+ break;
+ case AF_FORMAT_DOUBLE:
+ MUL_GAIN_f((double *)data, num_samples, gain);
+ break;
+ default:;
+ // all other sample formats are simply not supported
+ }
+}
+
+void ao_post_process_data(struct ao *ao, void **data, int num_samples)
+{
+ bool planar = af_fmt_is_planar(ao->format);
+ int planes = planar ? ao->channels.num : 1;
+ int plane_samples = num_samples * (planar ? 1: ao->channels.num);
+ for (int n = 0; n < planes; n++)
+ process_plane(ao, data[n], plane_samples);
+}
+
static int get_conv_type(struct ao_convert_fmt *fmt)
{
if (af_fmt_to_bytes(fmt->src_fmt) * 8 == fmt->dst_bits && !fmt->pad_msb)
diff --git a/audio/out/ao.h b/audio/out/ao.h
index 116733fb39..b9df4ebb00 100644
--- a/audio/out/ao.h
+++ b/audio/out/ao.h
@@ -95,6 +95,7 @@ const char *ao_get_description(struct ao *ao);
bool ao_untimed(struct ao *ao);
int ao_play(struct ao *ao, void **data, int samples, int flags);
int ao_control(struct ao *ao, enum aocontrol cmd, void *arg);
+void ao_set_gain(struct ao *ao, float gain);
double ao_get_delay(struct ao *ao);
int ao_get_space(struct ao *ao);
void ao_reset(struct ao *ao);
diff --git a/audio/out/internal.h b/audio/out/internal.h
index a6fcf7c5f6..9826630108 100644
--- a/audio/out/internal.h
+++ b/audio/out/internal.h
@@ -70,6 +70,9 @@ struct ao {
// Internal events (use ao_request_reload(), ao_hotplug_event())
atomic_int events_;
+ // Float gain multiplicator, reinterpret-casted to int.
+ atomic_uint_least32_t gain_fi;
+
int buffer;
double def_buffer;
void *api_priv;
@@ -212,6 +215,8 @@ bool ao_chmap_sel_get_def(struct ao *ao, const struct mp_chmap_sel *s,
void ao_device_list_add(struct ao_device_list *list, struct ao *ao,
struct ao_device_desc *e);
+void ao_post_process_data(struct ao *ao, void **data, int num_samples);
+
struct ao_convert_fmt {
int src_fmt; // source AF_FORMAT_*
int channels; // number of channels
diff --git a/audio/out/pull.c b/audio/out/pull.c
index 89aa98d52f..a4aa53821e 100644
--- a/audio/out/pull.c
+++ b/audio/out/pull.c
@@ -179,6 +179,8 @@ end:
for (int n = 0; n < ao->num_planes; n++)
af_fill_silence((char *)data[n] + bytes, full_bytes - bytes, ao->format);
+ ao_post_process_data(ao, data, samples);
+
return bytes / ao->sstride;
}
diff --git a/audio/out/push.c b/audio/out/push.c
index 8546ec816d..1f87481183 100644
--- a/audio/out/push.c
+++ b/audio/out/push.c
@@ -304,6 +304,7 @@ static void ao_play_data(struct ao *ao)
samples = samples / ao->period_size * ao->period_size;
}
MP_STATS(ao, "start ao fill");
+ ao_post_process_data(ao, (void **)planes, samples);
int r = 0;
if (samples)
r = ao->driver->play(ao, (void **)planes, samples, flags);
diff --git a/player/audio.c b/player/audio.c
index e08259b1b5..e92d97ffd4 100644
--- a/player/audio.c
+++ b/player/audio.c
@@ -128,6 +128,57 @@ fail:
mp_notify(mpctx, MP_EVENT_CHANGE_ALL, NULL);
}
+static int recreate_audio_filters(struct MPContext *mpctx)
+{
+ assert(mpctx->ao_chain);
+
+ struct af_stream *afs = mpctx->ao_chain->af;
+ if (afs->initialized < 1 && af_init(afs) < 0)
+ goto fail;
+
+ recreate_speed_filters(mpctx);
+ if (afs->initialized < 1 && af_init(afs) < 0)
+ goto fail;
+
+ if (mpctx->opts->softvol == SOFTVOL_NO)
+ MP_ERR(mpctx, "--softvol=no is not supported anymore.\n");
+
+ mp_notify(mpctx, MPV_EVENT_AUDIO_RECONFIG, NULL);
+
+ return 0;
+
+fail:
+ MP_ERR(mpctx, "Couldn't find matching filter/ao format!\n");
+ return -1;
+}
+
+int reinit_audio_filters(struct MPContext *mpctx)
+{
+ struct ao_chain *ao_c = mpctx->ao_chain;
+ if (!ao_c)
+ return 0;
+
+ double delay = 0;
+ if (ao_c->af->initialized > 0)
+ delay = af_calc_delay(ao_c->af);
+
+ af_uninit(ao_c->af);
+ if (recreate_audio_filters(mpctx) < 0)
+ return -1;
+
+ // Only force refresh if the amount of dropped buffered data is going to
+ // cause "issues" for the A/V sync logic.
+ if (mpctx->audio_status == STATUS_PLAYING && delay > 0.2)
+ issue_refresh_seek(mpctx, MPSEEK_EXACT);
+ return 1;
+}
+
+#else /* HAVE_LIBAV */
+
+int reinit_audio_filters(struct MPContext *mpctx) { return 0; }
+
+#endif /* else HAVE_LIBAF */
+
static double db_gain(double db)
{
return pow(10.0, db/20.0);
@@ -136,11 +187,13 @@ static double db_gain(double db)
static float compute_replaygain(struct MPContext *mpctx)
{
struct MPOpts *opts = mpctx->opts;
- struct ao_chain *ao_c = mpctx->ao_chain;
float rgain = 1.0;
- struct replaygain_data *rg = ao_c->af->replaygain_data;
+ struct replaygain_data *rg = NULL;
+ struct track *track = mpctx->current_track[0][STREAM_AUDIO];
+ if (track)
+ rg = track->stream->codec->replaygain_data;
if (opts->rgain_mode && rg) {
MP_VERBOSE(mpctx, "Replaygain: Track=%f/%f Album=%f/%f\n",
rg->track_gain, rg->track_peak,
@@ -177,7 +230,7 @@ void audio_update_volume(struct MPContext *mpctx)
{
struct MPOpts *opts = mpctx->opts;
struct ao_chain *ao_c = mpctx->ao_chain;
- if (!ao_c || ao_c->af->initialized < 1)
+ if (!ao_c || !ao_c->ao)
return;
float gain = MPMAX(opts->softvol_volume / 100.0, 0);
@@ -185,62 +238,10 @@ void audio_update_volume(struct MPContext *mpctx)
gain *= compute_replaygain(mpctx);
if (opts->softvol_mute == 1)
gain = 0.0;
-}
-
-static int recreate_audio_filters(struct MPContext *mpctx)
-{
- assert(mpctx->ao_chain);
-
- struct af_stream *afs = mpctx->ao_chain->af;
- if (afs->initialized < 1 && af_init(afs) < 0)
- goto fail;
-
- recreate_speed_filters(mpctx);
- if (afs->initialized < 1 && af_init(afs) < 0)
- goto fail;
- if (mpctx->opts->softvol == SOFTVOL_NO)
- MP_ERR(mpctx, "--softvol=no is not supported anymore.\n");
-
- audio_update_volume(mpctx);
-
- mp_notify(mpctx, MPV_EVENT_AUDIO_RECONFIG, NULL);
-
- return 0;
-
-fail:
- MP_ERR(mpctx, "Couldn't find matching filter/ao format!\n");
- return -1;
+ ao_set_gain(ao_c->ao, gain);
}
-int reinit_audio_filters(struct MPContext *mpctx)
-{
- struct ao_chain *ao_c = mpctx->ao_chain;
- if (!ao_c)
- return 0;
-
- double delay = 0;
- if (ao_c->af->initialized > 0)
- delay = af_calc_delay(ao_c->af);
-
- af_uninit(ao_c->af);
- if (recreate_audio_filters(mpctx) < 0)
- return -1;
-
- // Only force refresh if the amount of dropped buffered data is going to
- // cause "issues" for the A/V sync logic.
- if (mpctx->audio_status == STATUS_PLAYING && delay > 0.2)
- issue_refresh_seek(mpctx, MPSEEK_EXACT);
- return 1;
-}
-
-#else /* HAVE_LIBAV */
-
-void audio_update_volume(struct MPContext *mpctx) {}
-int reinit_audio_filters(struct MPContext *mpctx) { return 0; }
-
-#endif /* else HAVE_LIBAF */
-
// Call this if opts->playback_speed or mpctx->speed_factor_* change.
void update_playback_speed(struct MPContext *mpctx)
{
@@ -603,8 +604,6 @@ void reinit_audio_chain_src(struct MPContext *mpctx, struct track *track)
ao_c->log = mpctx->log;
#if HAVE_LIBAF
ao_c->af = af_new(mpctx->global);
- if (track && track->stream)
- ao_c->af->replaygain_data = track->stream->codec->replaygain_data;
#else
ao_c->conv = mp_aconverter_create(mpctx->global, mpctx->log, NULL);
#endif