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 This specifies the device to use for audio output. The default is "default". .TP +.B mixer_type +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 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 - mixer_enabled - yes|no + mixer_type + hardware|software|none - 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. 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)) + struct mixer *mixer; + + switch (audio_output_mixer_type(param)) { + case MIXER_TYPE_NONE: + case MIXER_TYPE_UNKNOWN: return NULL; - if (plugin == NULL) - return NULL; + case MIXER_TYPE_HARDWARE: + if (plugin == NULL) + return NULL; - return mixer_new(plugin, param); + 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); }