MusicPlayerDaemon / MPD

Music Player Daemon
https://www.musicpd.org/
GNU General Public License v2.0
2.15k stars 347 forks source link

Weird volume behavior when replay_gain_handler is specified as "mixer" #219

Open estshorter opened 6 years ago

estshorter commented 6 years ago

Issues

When replay_gain_hander is specified as "mixer",

  1. A value from mpc volume varies by itself after playing a song
  2. A dB value written in replaygain tag is quite different from that obtained by amixer get Digital

mpd: 0.20.17 outputs: alsa

Detail

I prepared a test file (nosound, replaygain: -10db). 01 no_sound rg-10.zip

Command log when playing the test file is as follows.

$ mpc volume 100
volume:100%   repeat: off   random: off   single: off   consume: off
$ mpc play
no_sound rg-10  
[playing] #1/1   0:00/5:00 (0%)
volume:100%   repeat: off   random: off   single: off   consume: off
$ mpc volume
volume: 30%
$ amixer get Digital
Simple mixer control 'Digital',0
  Capabilities: pvolume pswitch
  Playback channels: Front Left - Front Right
  Limits: Playback 0 - 207
  Mono:
  Front Left: Playback 145 [70%] [-31.00dB] [on]
  Front Right: Playback 145 [70%] [-31.00dB] [on]

As you can see above, initial volume (100%) changed to 30% after mpc play. This is not the case when using only software volume; the volume is constant.

Moreover, the dB value written in the replaygain tag (-10dB) is quite different from that obtained by amixer get Digital (-31dB). The latter value should be around -10dB.

MPD verbose log

Feb 12 13:41 : state_file: Loading state file /tmp/mpd_state
Feb 12 13:41 : inotify: initializing inotify
Feb 12 13:41 : inotify: watching music directory
Feb 12 13:41 : client: [0] opened from [::1]:44438
Feb 12 13:41 : client: [0] process command "setvol "100""
Feb 12 13:41 : client: [0] command returned 0
Feb 12 13:41 : client: [0] process command list
Feb 12 13:41 : client: process command "status"
Feb 12 13:41 : client: command returned 0
Feb 12 13:41 : client: process command "currentsong"
Feb 12 13:41 : client: command returned 0
Feb 12 13:41 : client: [0] process command list returned 0
Feb 12 13:41 : client: [0] closed
Feb 12 13:41 : client: [1] opened from [::1]:44440
Feb 12 13:41 : client: [1] process command "play"
Feb 12 13:41 : playlist: play 0:"nas/test/01 no_sound rg-10.flac"
Feb 12 13:41 : client: [1] command returned 0
Feb 12 13:41 : client: [1] process command list
Feb 12 13:41 : client: process command "status"
Feb 12 13:41 : client: command returned 0
Feb 12 13:41 : client: process command "currentsong"
Feb 12 13:41 : client: command returned 0
Feb 12 13:41 : client: [1] process command list returned 0
Feb 12 13:41 : client: [1] closed
Feb 12 13:41 : decoder_thread: probing plugin flac
Feb 12 13:41 : decoder: audio_format=44100:16:2, seekable=true
Feb 12 13:41 : alsa_output: opened hw:0,0 type=HW
Feb 12 13:41 : alsa_output: buffer: size=64..65536 time=1451..1486078
Feb 12 13:41 : alsa_output: period: size=32..32768 time=725..743039
Feb 12 13:41 : alsa_output: default period_time = buffer_time/4 = 500000/4 = 125000
Feb 12 13:41 : alsa_output: format=S32_LE (Signed 32 bit Little Endian)
Feb 12 13:41 : alsa_output: buffer_size=22050 period_size=4410
Feb 12 13:41 : output: opened plugin=alsa name="alsa" audio_format=44100:32:2
Feb 12 13:41 : output: converting in=44100:16:2 -> f=44100:16:2 -> out=44100:32:2
Feb 12 13:41 : replay_gain: replay gain mode has changed off->track
Feb 12 13:41 : replay_gain: scale=1.000000
Feb 12 13:41 : replay_gain: scale=0.316228
Feb 12 13:41 : state_file: Saving state file /tmp/mpd_state
Feb 12 13:41 : client: [2] opened from [::1]:44442
Feb 12 13:41 : client: [2] process command "status"
Feb 12 13:41 : client: [2] command returned 0
Feb 12 13:41 : client: [2] closed

mpd.conf

# Files and directories #######################################################
music_directory         "/var/lib/mpd/music"
playlist_directory      "/var/lib/mpd/playlists"
db_file                 "/var/lib/mpd/tag_cache"
log_file                "/var/log/mpd/mpd.log"
pid_file                "/var/run/mpd/pid"
state_file              "/tmp/mpd_state"
#sticker_file            "/var/lib/mpd/sticker.sql"
###############################################################################

# General music daemon options ################################################
user                    "mpd"
group                   "audio"
bind_to_address         "any"
port                    "6600"
log_level               "verbose"
gapless_mp3_playback    "yes"
#save_absolute_paths_in_playlists       "no"
#metadata_to_use        "artist,album,title,track,name,genre,date,composer,performer,disc"
auto_update    "yes"
#auto_update_depth "3"
###############################################################################
# Symbolic link behavior ######################################################
follow_outside_symlinks "yes"
follow_inside_symlinks  "yes"
###############################################################################
# Input #######################################################################
#
input {
        plugin "ffmpeg"
        enabled "no"
}
###############################################################################

# Decoder ################################################################

###############################################################################

# Audio Output ################################################################

audio_output {
                type            "alsa"
                name            "alsa"
                device          "hw:0,0"
#               dop             "no"
                mixer_device    "default"
                mixer_control   "Digital"
                mixer_type      "hardware"
                format      "*:32:*"
                replay_gain_handler "mixer"
}

replaygain                      "track"
#replaygain_preamp              "0"
volume_normalization            "no"
###############################################################################

# MPD Internal Buffering ######################################################
#audio_buffer_size              "8192"
#buffer_before_play             "10%"
###############################################################################

# Resource Limitations ########################################################
#connection_timeout             "60"
#max_connections                        "20"
#max_playlist_length            "32384"
#max_command_list_size          "8192"
#max_output_buffer_size         "16384"
###############################################################################

# Character Encoding ##########################################################
filesystem_charset              "UTF-8"
id3v1_encoding                  "UTF-8"
###############################################################################

mpd -V

Music Player Daemon 0.20.17

Copyright (C) 2003-2007 Warren Dukes <warren.dukes@gmail.com>
Copyright 2008-2017 Max Kellermann <max.kellermann@gmail.com>
This is free software; see the source for copying conditions.  There is NO
warranty; not even MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

Database plugins:
 simple

Storage plugins:
 local

Decoders plugins:
 [mpg123] mp3
 [oggflac] ogg oga
 [flac] flac
 [ffmpeg] 16sv 3g2 3gp 4xm 8svx aa3 aac ac3 adx afc aif aifc aiff al alaw amr anim apc ape asf atrac au aud avi avm2 avs bap bfi c93 cak cin cmv cpk daud dct divx dts dv dvd dxa eac3 film flac flc fli fll flx flv g726 gsm gxf iss m1v m2v m2t m2ts m4a m4b m4v mad mj2 mjpeg mjpg mka mkv mlp mm mmf mov mp+ mp1 mp2 mp3 mp4 mpc mpeg mpg mpga mpp mpu mve mvi mxf nc nsv nut nuv oga ogm ogv ogx oma ogg omg opus psp pva qcp qt r3d ra ram rl2 rm rmvb roq rpl rvc shn smk snd sol son spx str swf tak tgi tgq tgv thp ts tsp tta xa xvid uv uv2 vb vid vob voc vp6 vmd wav webm wma wmv wsaud wsvga wv wve
 [pcm]

Filters:

Tag plugins:
 id3tag

Output plugins:
 null fifo alsa oss httpd recorder

Encoder plugins:
 null wave flac

Input plugins:
 file alsa ffmpeg

Playlist plugins:
 extm3u m3u pls flac cue embcue

Protocols:
 file:// gopher:// rtp:// rtsp:// rtmp:// rtmpt:// rtmps:// alsa://

Other features:
 epoll icu inotify ipv6 systemd tcp un
MaxKellermann commented 6 years ago

Please explain "A value from mpc volume varies by itself after playing a song". In my understanding, a changing volume value is the very point of this setting.

estshorter commented 6 years ago

What I want to say is that behavior of issue 1 is inconsistent with that using only software volume.

The following is command log using only software volume.

$ mpc volume 100
volume:100%   repeat: off   random: off   single: off   consume: off
$ mpc play
no_sound rg-10
[playing] #1/1   0:00/5:00 (0%)
volume:100%   repeat: off   random: off   single: off   consume: off
$ mpc volume
volume:100%

This says the volume of 100% corresponds the value based on replaygain tag (-10dB) for software volume. However, when using hardware volume, the volume of 100% correponds to 0dB as shown in the first post.

Audio output section of mpd.conf for software volume:

audio_output {
                type            "alsa"
                name            "alsa"
                device          "hw:0,0"
#               dop             "no"
                #mixer_device    "default"
                #mixer_control   "Digital"
                mixer_type      "software"
                format      "*:32:*"
                #replay_gain_handler "mixer"
                replay_gain_handler "software"
}
miccoli commented 6 years ago

As far as I understand, replay_gain_handler "mixer" means that MPD should change the mixer volume setting, so that the reported behaviour is indeed intended and not an anomaly.

Moreover one has to note that mixer_type and replay_gain_handler are orthogonal settings, so it makes no sense to compare

mixer_type "software"
replay_gain_handler "software"

with

mixer_type "hardware"
replay_gain_handler "mixer"

This said, there is a clear problem with the latter setting ("hardware" mixer, and "mixer" replay gain) in the computation of the scale factor in MPD/src/ReplayGainInfo.cxx at https://github.com/MusicPlayerDaemon/MPD/blob/9f6af4f2cc1f806bbbd32b18f64beb01a218cb6d/src/ReplayGainInfo.cxx#L32 Here the desired correction expressed as an acoustic pressure level (gain=-10dB) is convert into a sound pressure ratio p/p0 = 10**(-10/20) = 0.316.

The problem is that this factor is applied to the 0...100 normalised volume scale scale used by MPD, which for the alsa hardware mixer is neither linear in the sound pressure nor the sound pressure level, see https://github.com/MusicPlayerDaemon/MPD/blob/9f6af4f2cc1f806bbbd32b18f64beb01a218cb6d/src/mixer/plugins/volume_mapping.c#L21-L27

There is no easy answer to this problem, because the type of mapping from normalised volume to the acoustic pressure/acoustic pressure level cannot be queried programmatically. (I found no API for manipulating the mapping, nor for operating in terms of dB instead of normalised volume. I'm not an author of MPD, so please correct me if I'm wrong.)

At present I would suggest to keep all things as they are, and maybe add a note to the docs that replay_gain_handler mixer is not accurate in terms of dB attenuation for some types of mixers.

For the future a volume control API in terms of sound pressure level attenuation (i.e. defined on a dB scale and not an arbitrary normalised volume range) could be implemented.

estshorter commented 6 years ago

@miccoli Thank you for your clear explanation. I understand that the issue 1 is an intentional behaviour.