diff options
-rw-r--r-- | Copyright | 4 | ||||
-rw-r--r-- | audio/filter/af.c | 1 | ||||
-rw-r--r-- | audio/filter/af.h | 2 | ||||
-rw-r--r-- | audio/out/ao.c | 66 | ||||
-rw-r--r-- | audio/out/ao.h | 1 | ||||
-rw-r--r-- | audio/out/internal.h | 5 | ||||
-rw-r--r-- | audio/out/pull.c | 2 | ||||
-rw-r--r-- | audio/out/push.c | 1 | ||||
-rw-r--r-- | player/audio.c | 115 |
9 files changed, 134 insertions, 63 deletions
@@ -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 |