aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Makefile.am1
-rw-r--r--NEWS2
-rw-r--r--doc/mpd.conf.56
-rw-r--r--doc/mpdconf.example31
-rw-r--r--doc/user.xml12
-rw-r--r--src/mixer_control.c13
-rw-r--r--src/mixer_control.h3
-rw-r--r--src/output_init.c58
-rw-r--r--src/player_control.c11
-rw-r--r--src/player_control.h3
-rw-r--r--src/player_thread.c14
-rw-r--r--src/volume.c79
12 files changed, 80 insertions, 153 deletions
diff --git a/Makefile.am b/Makefile.am
index c37a6cca8..d89aeb694 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -740,6 +740,7 @@ test_run_output_SOURCES = test/run_output.c \
$(ENCODER_SRC) \
src/mixer_api.c \
src/mixer_control.c \
+ src/mixer_type.c \
$(MIXER_SRC) \
src/filter_plugin.c src/filter/chain_filter_plugin.c \
src/filter/convert_filter_plugin.c \
diff --git a/NEWS b/NEWS
index 7bd079870..68ca5ed38 100644
--- a/NEWS
+++ b/NEWS
@@ -7,6 +7,8 @@ ver 0.16 (20??/??/??)
- ffmpeg: support multiple tags
* mixers:
- removed support for legacy mixer configuration
+ - reimplemented software volume as mixer+filter plugin
+ - per-device software/hardware mixer setting
* commands:
- added new "status" line with more precise "elapsed time"
* log unused/unknown block parameters
diff --git a/doc/mpd.conf.5 b/doc/mpd.conf.5
index 7bf3a3f75..c8e632478 100644
--- a/doc/mpd.conf.5
+++ b/doc/mpd.conf.5
@@ -285,6 +285,12 @@ whatever audio format is passed to the audio output.
.B device <dev>
This specifies the device to use for audio output. The default is "default".
.TP
+.B mixer_type <hardware, software or none>
+Specifies which mixer should be used for this audio output: the
+hardware mixer (available for ALSA, OSS and PulseAudio), the software
+mixer or no mixer ("none"). By default, the hardware mixer is used
+for devices which support it, and none for the others.
+.TP
.B mixer_device <mixer dev>
This specifies which mixer to use. The default is "default". To use
the second sound card in a system, use "hw:1".
diff --git a/doc/mpdconf.example b/doc/mpdconf.example
index 919326236..7574ffc87 100644
--- a/doc/mpdconf.example
+++ b/doc/mpdconf.example
@@ -179,6 +179,7 @@ input {
# name "My ALSA Device"
# device "hw:0,0" # optional
# format "44100:16:2" # optional
+# mixer_type "hardware" # optional
# mixer_device "default" # optional
# mixer_control "PCM" # optional
# mixer_index "0" # optional
@@ -191,6 +192,7 @@ input {
# name "My OSS Device"
# device "/dev/dsp" # optional
# format "44100:16:2" # optional
+# mixer_type "hardware" # optional
# mixer_device "/dev/mixer" # optional
# mixer_control "PCM" # optional
#}
@@ -214,6 +216,7 @@ input {
# genre "jazz" # optional
# public "no" # optional
# timeout "2" # optional
+# mixer_type "software" # optional
#}
#
# An example of a httpd output (built-in HTTP streaming server):
@@ -255,6 +258,7 @@ input {
#audio_output {
# type "null"
# name "My Null Output"
+# mixer_type "none" # optional
#}
#
# This setting will change all decoded audio to be converted to the specified
@@ -273,33 +277,6 @@ input {
###############################################################################
-# Volume control mixer ########################################################
-#
-# These are the global volume control settings. By default, this setting will
-# be detected to the available audio output device, with preference going to
-# hardware mixing. Hardware and software mixers for individual audio_output
-# sections cannot yet be mixed.
-#
-# An example for controlling an ALSA, OSS or Pulseaudio mixer; If this
-# setting is used other sound applications will be affected by the volume
-# being controlled by MPD.
-#
-#mixer_type "hardware"
-#
-# An example for controlling all mixers through software. This will control
-# all controls, even if the mixer is not supported by the device and will not
-# affect any other sound producing applications.
-#
-#mixer_type "software"
-#
-# This example will not allow MPD to touch the mixer at all and will disable
-# all volume controls.
-#
-#mixer_type "disabled"
-#
-###############################################################################
-
-
# Normalization automatic volume adjustments ##################################
#
# This setting specifies the type of ReplayGain to use. This setting can have
diff --git a/doc/user.xml b/doc/user.xml
index 787f3e3bd..22da500b1 100644
--- a/doc/user.xml
+++ b/doc/user.xml
@@ -289,13 +289,15 @@ cd mpd-0.14.2</programlisting>
</row>
<row>
<entry>
- <varname>mixer_enabled</varname>
- <parameter>yes|no</parameter>
+ <varname>mixer_type</varname>
+ <parameter>hardware|software|none</parameter>
</entry>
<entry>
- Specifies whether the hardware mixer of this audio
- output should be used. By default, all hardware
- mixers are enabled if available.
+ Specifies which mixer should be used for this audio
+ output: the hardware mixer (available for ALSA, OSS
+ and PulseAudio), the software mixer or no mixer
+ ("none"). By default, the hardware mixer is used for
+ devices which support it, and none for the others.
</entry>
</row>
</tbody>
diff --git a/src/mixer_control.c b/src/mixer_control.c
index a17885935..927a1276c 100644
--- a/src/mixer_control.c
+++ b/src/mixer_control.c
@@ -28,24 +28,11 @@
#undef G_LOG_DOMAIN
#define G_LOG_DOMAIN "mixer"
-static bool mixers_enabled = true;
-
-void
-mixer_disable_all(void)
-{
- g_debug("mixer api is disabled");
- mixers_enabled = false;
-}
-
struct mixer *
mixer_new(const struct mixer_plugin *plugin, const struct config_param *param)
{
struct mixer *mixer;
- //mixers are disabled (by using software volume)
- if (!mixers_enabled) {
- return NULL;
- }
assert(plugin != NULL);
mixer = plugin->init(param);
diff --git a/src/mixer_control.h b/src/mixer_control.h
index 0f73e8f75..b8997a795 100644
--- a/src/mixer_control.h
+++ b/src/mixer_control.h
@@ -31,9 +31,6 @@ struct mixer;
struct mixer_plugin;
struct config_param;
-void
-mixer_disable_all(void);
-
struct mixer *
mixer_new(const struct mixer_plugin *plugin, const struct config_param *param);
diff --git a/src/output_init.c b/src/output_init.c
index eba665e77..08873ac20 100644
--- a/src/output_init.c
+++ b/src/output_init.c
@@ -23,6 +23,9 @@
#include "output_list.h"
#include "audio_parser.h"
#include "mixer_control.h"
+#include "mixer_type.h"
+#include "mixer_list.h"
+#include "mixer/software_mixer_plugin.h"
#include "filter_plugin.h"
#include "filter_registry.h"
#include "filter/chain_filter_plugin.h"
@@ -61,17 +64,59 @@ audio_output_detect(GError **error)
return NULL;
}
+/**
+ * Determines the mixer type which should be used for the specified
+ * configuration block.
+ *
+ * This handles the deprecated options mixer_type (global) and
+ * mixer_enabled, if the mixer_type setting is not configured.
+ */
+static enum mixer_type
+audio_output_mixer_type(const struct config_param *param)
+{
+ /* read the local "mixer_type" setting */
+ const char *p = config_get_block_string(param, "mixer_type", NULL);
+ if (p != NULL)
+ return mixer_type_parse(p);
+
+ /* try the local "mixer_enabled" setting next (deprecated) */
+ if (!config_get_block_bool(param, "mixer_enabled", true))
+ return MIXER_TYPE_NONE;
+
+ /* fall back to the global "mixer_type" setting (also
+ deprecated) */
+ return mixer_type_parse(config_get_string("mixer_type", "hardware"));
+}
+
static struct mixer *
audio_output_load_mixer(const struct config_param *param,
- const struct mixer_plugin *plugin)
+ const struct mixer_plugin *plugin,
+ struct filter *filter_chain)
{
- if (!config_get_block_bool(param, "mixer_enabled", true))
- return NULL;
+ struct mixer *mixer;
- if (plugin == NULL)
+ switch (audio_output_mixer_type(param)) {
+ case MIXER_TYPE_NONE:
+ case MIXER_TYPE_UNKNOWN:
return NULL;
- return mixer_new(plugin, param);
+ case MIXER_TYPE_HARDWARE:
+ if (plugin == NULL)
+ return NULL;
+
+ return mixer_new(plugin, param);
+
+ case MIXER_TYPE_SOFTWARE:
+ mixer = mixer_new(&software_mixer_plugin, NULL);
+ assert(mixer != NULL);
+
+ filter_chain_append(filter_chain,
+ software_mixer_get_filter(mixer));
+ return mixer;
+ }
+
+ assert(false);
+ return NULL;
}
bool
@@ -152,7 +197,8 @@ audio_output_init(struct audio_output *ao, const struct config_param *param,
if (ao->data == NULL)
return false;
- ao->mixer = audio_output_load_mixer(param, plugin->mixer_plugin);
+ ao->mixer = audio_output_load_mixer(param, plugin->mixer_plugin,
+ ao->filter);
/* the "convert" filter must be the last one in the chain */
diff --git a/src/player_control.c b/src/player_control.c
index ac4b006dd..df80ac4ff 100644
--- a/src/player_control.c
+++ b/src/player_control.c
@@ -40,7 +40,6 @@ void pc_init(unsigned buffer_chunks, unsigned int buffered_before_play)
pc.error = PLAYER_ERROR_NOERROR;
pc.state = PLAYER_STATE_STOP;
pc.cross_fade_seconds = 0;
- pc.software_volume = PCM_VOLUME_1;
}
void pc_deinit(void)
@@ -253,16 +252,6 @@ void setPlayerCrossFade(float crossFadeInSeconds)
idle_add(IDLE_OPTIONS);
}
-void setPlayerSoftwareVolume(int volume)
-{
- if (volume > PCM_VOLUME_1)
- volume = PCM_VOLUME_1;
- else if (volume < 0)
- volume = 0;
-
- pc.software_volume = volume;
-}
-
double getPlayerTotalPlayTime(void)
{
return pc.total_play_time;
diff --git a/src/player_control.h b/src/player_control.h
index b1f7481cd..0cc3c73a8 100644
--- a/src/player_control.h
+++ b/src/player_control.h
@@ -81,7 +81,6 @@ struct player_control {
struct song *errored_song;
volatile double seek_where;
float cross_fade_seconds;
- uint16_t software_volume;
double total_play_time;
};
@@ -145,8 +144,6 @@ void setPlayerCrossFade(float crossFadeInSeconds);
float getPlayerCrossFade(void);
-void setPlayerSoftwareVolume(int volume);
-
double getPlayerTotalPlayTime(void);
static inline const struct audio_format *
diff --git a/src/player_thread.c b/src/player_thread.c
index 7fc55d3d1..657968f6c 100644
--- a/src/player_thread.c
+++ b/src/player_thread.c
@@ -423,8 +423,6 @@ static bool
play_chunk(struct song *song, struct music_chunk *chunk,
const struct audio_format *format, double sizeToTime)
{
- bool success;
-
assert(music_chunk_check_format(chunk, format));
if (chunk->tag != NULL) {
@@ -455,18 +453,6 @@ play_chunk(struct song *song, struct music_chunk *chunk,
pc.elapsed_time = chunk->times;
pc.bit_rate = chunk->bit_rate;
- /* apply software volume */
-
- success = pcm_volume(chunk->data, chunk->length,
- format, pc.software_volume);
- if (!success) {
- g_warning("pcm_volume() failed on %u:%u:%u",
- format->sample_rate, format->bits, format->channels);
- pc.errored_song = dc.current_song;
- pc.error = PLAYER_ERROR_AUDIO;
- return false;
- }
-
/* send the chunk to the audio outputs */
if (!audio_output_all_play(chunk)) {
diff --git a/src/volume.c b/src/volume.c
index 3d240f4e4..3e6079cd6 100644
--- a/src/volume.c
+++ b/src/volume.c
@@ -40,8 +40,6 @@
#define SW_VOLUME_STATE "sw_volume: "
-static enum mixer_type volume_mixer_type = MIXER_TYPE_HARDWARE;
-
static unsigned volume_software_set = 100;
/** the cached hardware mixer value; invalid if negative */
@@ -51,37 +49,15 @@ static GTimer *hardware_volume_timer;
void volume_finish(void)
{
- if (volume_mixer_type == MIXER_TYPE_HARDWARE)
- g_timer_destroy(hardware_volume_timer);
+ g_timer_destroy(hardware_volume_timer);
}
void volume_init(void)
{
- const struct config_param *param = config_get_param(CONF_MIXER_TYPE);
- //hw mixing is by default
- if (param) {
- volume_mixer_type = mixer_type_parse(param->value);
- switch (volume_mixer_type) {
- case MIXER_TYPE_NONE:
- case MIXER_TYPE_SOFTWARE:
- mixer_disable_all();
- break;
-
- case MIXER_TYPE_HARDWARE:
- //nothing to do
- break;
-
- case MIXER_TYPE_UNKNOWN:
- g_error("unknown mixer type %s at line %i\n",
- param->value, param->line);
- }
- }
-
- if (volume_mixer_type == MIXER_TYPE_HARDWARE)
- hardware_volume_timer = g_timer_new();
+ hardware_volume_timer = g_timer_new();
}
-static int hardware_volume_get(void)
+int volume_level_get(void)
{
assert(hardware_volume_timer != NULL);
@@ -95,43 +71,12 @@ static int hardware_volume_get(void)
return last_hardware_volume;
}
-static int software_volume_get(void)
-{
- return volume_software_set;
-}
-
-int volume_level_get(void)
-{
- switch (volume_mixer_type) {
- case MIXER_TYPE_SOFTWARE:
- return software_volume_get();
- case MIXER_TYPE_HARDWARE:
- return hardware_volume_get();
- case MIXER_TYPE_NONE:
- case MIXER_TYPE_UNKNOWN:
- return -1;
- }
-
- /* unreachable */
- assert(false);
- return -1;
-}
-
static bool software_volume_change(unsigned volume)
{
assert(volume <= 100);
volume_software_set = volume;
-
- if (volume >= 100)
- volume = PCM_VOLUME_1;
- else if (volume <= 0)
- volume = 0;
- else
- volume = pcm_float_to_volume((exp(volume / 25.0) - 1) /
- (54.5981500331F - 1));
-
- setPlayerSoftwareVolume(volume);
+ mixer_all_set_software_volume(volume);
return true;
}
@@ -148,16 +93,11 @@ bool volume_level_change(unsigned volume)
{
assert(volume <= 100);
+ volume_software_set = volume;
+
idle_add(IDLE_MIXER);
- switch (volume_mixer_type) {
- case MIXER_TYPE_HARDWARE:
- return hardware_volume_change(volume);
- case MIXER_TYPE_SOFTWARE:
- return software_volume_change(volume);
- default:
- return true;
- }
+ return hardware_volume_change(volume);
}
void read_sw_volume_state(FILE *fp)
@@ -166,8 +106,6 @@ void read_sw_volume_state(FILE *fp)
char *end = NULL;
long int sv;
- if (volume_mixer_type != MIXER_TYPE_SOFTWARE)
- return;
while (fgets(buf, sizeof(buf), fp)) {
if (!g_str_has_prefix(buf, SW_VOLUME_STATE))
continue;
@@ -184,6 +122,5 @@ void read_sw_volume_state(FILE *fp)
void save_sw_volume_state(FILE *fp)
{
- if (volume_mixer_type == MIXER_TYPE_SOFTWARE)
- fprintf(fp, SW_VOLUME_STATE "%u\n", volume_software_set);
+ fprintf(fp, SW_VOLUME_STATE "%u\n", volume_software_set);
}