aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--NEWS1
-rw-r--r--doc/mpd.conf.56
-rw-r--r--doc/user.xml13
-rw-r--r--src/filter/replay_gain_filter_plugin.c45
-rw-r--r--src/filter/replay_gain_filter_plugin.h13
-rw-r--r--src/output_init.c33
-rw-r--r--src/output_thread.c3
-rw-r--r--test/run_filter.c8
8 files changed, 116 insertions, 6 deletions
diff --git a/NEWS b/NEWS
index 480a4c16..d04d14e3 100644
--- a/NEWS
+++ b/NEWS
@@ -73,6 +73,7 @@ ver 0.16 (20??/??/??)
* replay gain:
- reimplemented as a filter plugin
- fall back to track gain if album gain is unavailable
+ - optionally use hardware mixer to apply replay gain
* log unused/unknown block parameters
* removed the deprecated "error_file" option
* save state when stopped
diff --git a/doc/mpd.conf.5 b/doc/mpd.conf.5
index a5c0304c..48b276d5 100644
--- a/doc/mpd.conf.5
+++ b/doc/mpd.conf.5
@@ -270,6 +270,12 @@ audio that is sent to the audio output device. See documentation for the
whatever audio format is passed to the audio output.
Any of the three attributes may be an asterisk to specify that this
attribute should not be enforced
+.TP
+.B replay_gain_handler <software, mixer or none>
+Specifies how replay gain is applied. The default is "software",
+which uses an internal software volume control. "mixer" uses the
+configured (hardware) mixer control. "none" disables replay gain on
+this audio output.
.SH OPTIONAL ALSA OUTPUT PARAMETERS
.TP
.B device <dev>
diff --git a/doc/user.xml b/doc/user.xml
index 15b20d52..30fcae69 100644
--- a/doc/user.xml
+++ b/doc/user.xml
@@ -350,6 +350,19 @@ cd mpd-version</programlisting>
devices which support it, and none for the others.
</entry>
</row>
+ <row>
+ <entry>
+ <varname>replay_gain_handler</varname>
+ <parameter>software|mixer|none</parameter>
+ </entry>
+ <entry>
+ Specifies how replay gain is applied. The default is
+ "software", which uses an internal software volume
+ control. "mixer" uses the configured (hardware) mixer
+ control. "none" disables replay gain on this audio
+ output.
+ </entry>
+ </row>
</tbody>
</tgroup>
</informaltable>
diff --git a/src/filter/replay_gain_filter_plugin.c b/src/filter/replay_gain_filter_plugin.c
index 248690d1..c8e74c7f 100644
--- a/src/filter/replay_gain_filter_plugin.c
+++ b/src/filter/replay_gain_filter_plugin.c
@@ -27,7 +27,9 @@
#include "pcm_volume.h"
#include "replay_gain_info.h"
#include "replay_gain_config.h"
+#include "mixer_control.h"
+#include <assert.h>
#include <string.h>
#undef G_LOG_DOMAIN
@@ -36,6 +38,18 @@
struct replay_gain_filter {
struct filter filter;
+ /**
+ * If set, then this hardware mixer is used for applying
+ * replay gain, instead of the software volume library.
+ */
+ struct mixer *mixer;
+
+ /**
+ * The base volume level for scale=1.0, between 1 and 100
+ * (including).
+ */
+ unsigned base;
+
enum replay_gain_mode mode;
struct replay_gain_info info;
@@ -74,6 +88,21 @@ replay_gain_filter_update(struct replay_gain_filter *filter)
filter->volume = pcm_float_to_volume(scale);
} else
filter->volume = PCM_VOLUME_1;
+
+ if (filter->mixer != NULL) {
+ /* update the hardware mixer volume */
+
+ unsigned volume = (filter->volume * filter->base) / PCM_VOLUME_1;
+ if (volume > 100)
+ volume = 100;
+
+ GError *error = NULL;
+ if (!mixer_set_volume(filter->mixer, volume, &error)) {
+ g_warning("Failed to update hardware mixer: %s",
+ error->message);
+ g_error_free(error);
+ }
+ }
}
static struct filter *
@@ -83,6 +112,7 @@ replay_gain_filter_init(G_GNUC_UNUSED const struct config_param *param,
struct replay_gain_filter *filter = g_new(struct replay_gain_filter, 1);
filter_init(&filter->filter, &replay_gain_filter_plugin);
+ filter->mixer = NULL;
filter->mode = replay_gain_mode;
replay_gain_info_init(&filter->info);
@@ -178,6 +208,21 @@ const struct filter_plugin replay_gain_filter_plugin = {
};
void
+replay_gain_filter_set_mixer(struct filter *_filter, struct mixer *mixer,
+ unsigned base)
+{
+ struct replay_gain_filter *filter =
+ (struct replay_gain_filter *)_filter;
+
+ assert(mixer == NULL || (base > 0 && base <= 100));
+
+ filter->mixer = mixer;
+ filter->base = base;
+
+ replay_gain_filter_update(filter);
+}
+
+void
replay_gain_filter_set_info(struct filter *_filter,
const struct replay_gain_info *info)
{
diff --git a/src/filter/replay_gain_filter_plugin.h b/src/filter/replay_gain_filter_plugin.h
index 4ab9f42a..348b4f50 100644
--- a/src/filter/replay_gain_filter_plugin.h
+++ b/src/filter/replay_gain_filter_plugin.h
@@ -23,6 +23,19 @@
#include "replay_gain_info.h"
struct filter;
+struct mixer;
+
+/**
+ * Enables or disables the hardware mixer for applying replay gain.
+ *
+ * @param mixer the hardware mixer, or NULL to fall back to software
+ * volume
+ * @param base the base volume level for scale=1.0, between 1 and 100
+ * (including).
+ */
+void
+replay_gain_filter_set_mixer(struct filter *_filter, struct mixer *mixer,
+ unsigned base);
/**
* Sets a new #replay_gain_info at the beginning of a new song.
diff --git a/src/output_init.c b/src/output_init.c
index 387915dd..f3d22ace 100644
--- a/src/output_init.c
+++ b/src/output_init.c
@@ -32,6 +32,7 @@
#include "filter_config.h"
#include "filter/chain_filter_plugin.h"
#include "filter/autoconvert_filter_plugin.h"
+#include "filter/replay_gain_filter_plugin.h"
#include <glib.h>
@@ -196,12 +197,19 @@ audio_output_init(struct audio_output *ao, const struct config_param *param,
/* create the replay_gain filter */
- ao->replay_gain_filter = filter_new(&replay_gain_filter_plugin,
- param, NULL);
- assert(ao->replay_gain_filter != NULL);
+ const char *replay_gain_handler =
+ config_get_block_string(param, "replay_gain_handler",
+ "software");
- filter_chain_append(ao->filter, ao->replay_gain_filter);
- ao->replay_gain_serial = 0;
+ if (strcmp(replay_gain_handler, "none") != 0) {
+ ao->replay_gain_filter = filter_new(&replay_gain_filter_plugin,
+ param, NULL);
+ assert(ao->replay_gain_filter != NULL);
+
+ filter_chain_append(ao->filter, ao->replay_gain_filter);
+ ao->replay_gain_serial = 0;
+ } else
+ ao->replay_gain_filter = NULL;
/* create the normalization filter (if configured) */
@@ -247,6 +255,21 @@ audio_output_init(struct audio_output *ao, const struct config_param *param,
g_error_free(error);
}
+ /* use the hardware mixer for replay gain? */
+
+ if (strcmp(replay_gain_handler, "mixer") == 0) {
+ if (ao->mixer != NULL)
+ replay_gain_filter_set_mixer(ao->replay_gain_filter,
+ ao->mixer, 100);
+ else
+ g_warning("No such mixer for output '%s'", ao->name);
+ } else if (strcmp(replay_gain_handler, "software") != 0 &&
+ ao->replay_gain_filter != NULL) {
+ g_set_error(error_r, audio_output_quark(), 0,
+ "Invalid \"replay_gain_handler\" value");
+ return false;
+ }
+
/* the "convert" filter must be the last one in the chain */
ao->convert_filter = filter_new(&convert_filter_plugin, NULL, NULL);
diff --git a/src/output_thread.c b/src/output_thread.c
index 0e34c64b..4bf0827f 100644
--- a/src/output_thread.c
+++ b/src/output_thread.c
@@ -264,7 +264,8 @@ ao_play_chunk(struct audio_output *ao, const struct music_chunk *chunk)
/* update replay gain */
- if (chunk->replay_gain_serial != ao->replay_gain_serial) {
+ if (ao->replay_gain_filter != NULL &&
+ chunk->replay_gain_serial != ao->replay_gain_serial) {
replay_gain_filter_set_info(ao->replay_gain_filter,
chunk->replay_gain_serial != 0
? &chunk->replay_gain_info
diff --git a/test/run_filter.c b/test/run_filter.c
index dd22312d..f9d628ae 100644
--- a/test/run_filter.c
+++ b/test/run_filter.c
@@ -24,6 +24,7 @@
#include "filter_plugin.h"
#include "pcm_volume.h"
#include "idle.h"
+#include "mixer_control.h"
#include <glib.h>
@@ -37,6 +38,13 @@ idle_add(G_GNUC_UNUSED unsigned flags)
{
}
+bool
+mixer_set_volume(G_GNUC_UNUSED struct mixer *mixer,
+ G_GNUC_UNUSED unsigned volume, G_GNUC_UNUSED GError **error_r)
+{
+ return true;
+}
+
static void
my_log_func(const gchar *log_domain, G_GNUC_UNUSED GLogLevelFlags log_level,
const gchar *message, G_GNUC_UNUSED gpointer user_data)