mirror of https://github.com/mpv-player/mpv
af_volume: add support for replaygain pre-amp and clipping prevention
This commit is contained in:
parent
3f0139e5db
commit
d80dc885c6
|
@ -285,6 +285,12 @@ Available filters are:
|
||||||
tags, as written by mp3gain, metaflac, etc...
|
tags, as written by mp3gain, metaflac, etc...
|
||||||
``replaygain-album``
|
``replaygain-album``
|
||||||
Like replaygain-track, but using the album-gain value instead.
|
Like replaygain-track, but using the album-gain value instead.
|
||||||
|
``replaygain-preamp``
|
||||||
|
Pre-amplification gain in dB to apply to the selected replaygain gain
|
||||||
|
(default: 0).
|
||||||
|
``replaygain-noclip``
|
||||||
|
Prevent clipping caused by replaygain by automatically lowering the
|
||||||
|
gain (default). Use no-replaygain-noclip to disable this.
|
||||||
``<softclip>``
|
``<softclip>``
|
||||||
Turns soft clipping on. Soft-clipping can make the
|
Turns soft clipping on. Soft-clipping can make the
|
||||||
sound more smooth if very high volume levels are used. Enable this
|
sound more smooth if very high volume levels are used. Enable this
|
||||||
|
|
|
@ -34,11 +34,67 @@ struct priv {
|
||||||
float level; // Gain level for each channel
|
float level; // Gain level for each channel
|
||||||
int rgain_track; // Enable/disable track based replaygain
|
int rgain_track; // Enable/disable track based replaygain
|
||||||
int rgain_album; // Enable/disable album based replaygain
|
int rgain_album; // Enable/disable album based replaygain
|
||||||
|
float rgain_preamp; // Set replaygain pre-amplification
|
||||||
|
int rgain_noclip; // Enable/disable clipping prevention
|
||||||
int soft; // Enable/disable soft clipping
|
int soft; // Enable/disable soft clipping
|
||||||
int fast; // Use fix-point volume control
|
int fast; // Use fix-point volume control
|
||||||
float cfg_volume;
|
float cfg_volume;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static int decode_float(char *str, float *out)
|
||||||
|
{
|
||||||
|
char *rest;
|
||||||
|
float dec_val;
|
||||||
|
|
||||||
|
dec_val = strtod(str, &rest);
|
||||||
|
if (!rest || (rest == str) || !isfinite(dec_val))
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
*out = dec_val;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int decode_gain(struct af_instance *af, const char *tag, float *out)
|
||||||
|
{
|
||||||
|
char *tag_val = NULL;
|
||||||
|
float dec_val;
|
||||||
|
|
||||||
|
tag_val = mp_tags_get_str(af->metadata, tag);
|
||||||
|
if (!tag_val) {
|
||||||
|
mp_msg(af->log, MSGL_ERR, "Replaygain tags not found\n");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (decode_float(tag_val, &dec_val)) {
|
||||||
|
mp_msg(af->log, MSGL_ERR, "Invalid replaygain value\n");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
*out = dec_val;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int decode_peak(struct af_instance *af, const char *tag, float *out)
|
||||||
|
{
|
||||||
|
char *tag_val = NULL;
|
||||||
|
float dec_val;
|
||||||
|
|
||||||
|
*out = 1.0;
|
||||||
|
|
||||||
|
tag_val = mp_tags_get_str(af->metadata, tag);
|
||||||
|
if (!tag_val)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
if (decode_float(tag_val, &dec_val))
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
if (dec_val == 0.0)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
*out = dec_val;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
static int control(struct af_instance *af, int cmd, void *arg)
|
static int control(struct af_instance *af, int cmd, void *arg)
|
||||||
{
|
{
|
||||||
struct priv *s = af->priv;
|
struct priv *s = af->priv;
|
||||||
|
@ -58,20 +114,25 @@ static int control(struct af_instance *af, int cmd, void *arg)
|
||||||
if (af_fmt_is_planar(in->format))
|
if (af_fmt_is_planar(in->format))
|
||||||
mp_audio_set_format(af->data, af_fmt_to_planar(af->data->format));
|
mp_audio_set_format(af->data, af_fmt_to_planar(af->data->format));
|
||||||
if ((s->rgain_track || s->rgain_album) && af->metadata) {
|
if ((s->rgain_track || s->rgain_album) && af->metadata) {
|
||||||
char *rgain = NULL, *rest;
|
float gain, peak;
|
||||||
|
char *gain_tag = NULL, *peak_tag = NULL;
|
||||||
|
|
||||||
if (s->rgain_track) {
|
if (s->rgain_track) {
|
||||||
rgain = mp_tags_get_str(af->metadata, "REPLAYGAIN_TRACK_GAIN");
|
gain_tag = "REPLAYGAIN_TRACK_GAIN";
|
||||||
|
peak_tag = "REPLAYGAIN_TRACK_PEAK";
|
||||||
} else if (s->rgain_album) {
|
} else if (s->rgain_album) {
|
||||||
rgain = mp_tags_get_str(af->metadata, "REPLAYGAIN_ALBUM_GAIN");
|
gain_tag = "REPLAYGAIN_ALBUM_GAIN";
|
||||||
|
peak_tag = "REPLAYGAIN_ALBUM_PEAK";
|
||||||
}
|
}
|
||||||
if (rgain) {
|
|
||||||
float db = strtod(rgain, &rest);
|
if (!decode_gain(af, gain_tag, &gain) &&
|
||||||
if (rest && (rest != rgain) && isfinite(db))
|
!decode_peak(af, peak_tag, &peak))
|
||||||
af_from_dB(1, &db, &s->level, 20.0, -200.0, 60.0);
|
{
|
||||||
else
|
gain += s->rgain_preamp;
|
||||||
mp_msg(af->log, MSGL_ERR, "Invalid replaygain value\n");
|
af_from_dB(1, &gain, &s->level, 20.0, -200.0, 60.0);
|
||||||
} else {
|
|
||||||
mp_msg(af->log, MSGL_ERR, "Replaygain tags not found\n");
|
if (s->rgain_noclip) // clipping prevention
|
||||||
|
s->level = MPMIN(s->level, 1.0 / peak);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return af_test_output(af, in);
|
return af_test_output(af, in);
|
||||||
|
@ -141,6 +202,8 @@ struct af_info af_info_volume = {
|
||||||
OPT_FLOATRANGE("volumedb", cfg_volume, 0, -200, 60),
|
OPT_FLOATRANGE("volumedb", cfg_volume, 0, -200, 60),
|
||||||
OPT_FLAG("replaygain-track", rgain_track, 0),
|
OPT_FLAG("replaygain-track", rgain_track, 0),
|
||||||
OPT_FLAG("replaygain-album", rgain_album, 0),
|
OPT_FLAG("replaygain-album", rgain_album, 0),
|
||||||
|
OPT_FLOATRANGE("replaygain-preamp", rgain_preamp, 0, -15, 15),
|
||||||
|
OPT_FLAG("replaygain-noclip", rgain_noclip, 0, OPTDEF_INT(1)),
|
||||||
OPT_FLAG("softclip", soft, 0),
|
OPT_FLAG("softclip", soft, 0),
|
||||||
OPT_FLAG("s16", fast, 0),
|
OPT_FLAG("s16", fast, 0),
|
||||||
{0}
|
{0}
|
||||||
|
|
Loading…
Reference in New Issue