summaryrefslogtreecommitdiffstats
path: root/audio
diff options
context:
space:
mode:
authorAlessandro Ghedini <alessandro@ghedini.me>2014-03-12 23:25:51 +0100
committerwm4 <wm4@nowhere>2014-03-13 14:36:20 +0100
commitd80dc885c6e0dc1b2d5defa6aeed215a710c0340 (patch)
tree5143c0b086fac9c5dd844429c04100c5e7ca64cc /audio
parent3f0139e5db88b447a408547e0a59e071214e0cec (diff)
downloadmpv-d80dc885c6e0dc1b2d5defa6aeed215a710c0340.tar.bz2
mpv-d80dc885c6e0dc1b2d5defa6aeed215a710c0340.tar.xz
af_volume: add support for replaygain pre-amp and clipping prevention
Diffstat (limited to 'audio')
-rw-r--r--audio/filter/af_volume.c85
1 files changed, 74 insertions, 11 deletions
diff --git a/audio/filter/af_volume.c b/audio/filter/af_volume.c
index ca331bf9b0..b218913ea3 100644
--- a/audio/filter/af_volume.c
+++ b/audio/filter/af_volume.c
@@ -34,11 +34,67 @@ struct priv {
float level; // Gain level for each channel
int rgain_track; // Enable/disable track 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 fast; // Use fix-point volume control
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)
{
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))
mp_audio_set_format(af->data, af_fmt_to_planar(af->data->format));
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) {
- 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) {
- 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 (rest && (rest != rgain) && isfinite(db))
- af_from_dB(1, &db, &s->level, 20.0, -200.0, 60.0);
- else
- mp_msg(af->log, MSGL_ERR, "Invalid replaygain value\n");
- } else {
- mp_msg(af->log, MSGL_ERR, "Replaygain tags not found\n");
+
+ if (!decode_gain(af, gain_tag, &gain) &&
+ !decode_peak(af, peak_tag, &peak))
+ {
+ gain += s->rgain_preamp;
+ af_from_dB(1, &gain, &s->level, 20.0, -200.0, 60.0);
+
+ if (s->rgain_noclip) // clipping prevention
+ s->level = MPMIN(s->level, 1.0 / peak);
}
}
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_FLAG("replaygain-track", rgain_track, 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("s16", fast, 0),
{0}