Skip to content

Commit

Permalink
Chore: Simplify volume normalization a bit
Browse files Browse the repository at this point in the history
  • Loading branch information
marcelveldt committed Jan 14, 2025
1 parent c5cc6c5 commit 17d23c3
Show file tree
Hide file tree
Showing 2 changed files with 23 additions and 20 deletions.
29 changes: 11 additions & 18 deletions music_assistant/controllers/streams.py
Original file line number Diff line number Diff line change
Expand Up @@ -914,35 +914,24 @@ async def get_media_stream(
filter_params = []
extra_input_args = streamdetails.extra_input_args or []
# handle volume normalization
enable_volume_normalization = (
streamdetails.target_loudness is not None
and streamdetails.volume_normalization_mode != VolumeNormalizationMode.DISABLED
)
dynamic_volume_normalization = (
streamdetails.volume_normalization_mode == VolumeNormalizationMode.DYNAMIC
and enable_volume_normalization
)
if dynamic_volume_normalization:
if streamdetails.volume_normalization_mode == VolumeNormalizationMode.DYNAMIC:
# volume normalization using loudnorm filter (in dynamic mode)
# which also collects the measurement on the fly during playback
# more info: https://k.ylo.ph/2016/04/04/loudnorm.html
filter_rule = f"loudnorm=I={streamdetails.target_loudness}:TP=-2.0:LRA=10.0:offset=0.0"
filter_rule += ":print_format=json"
filter_params.append(filter_rule)
elif (
enable_volume_normalization
and streamdetails.volume_normalization_mode == VolumeNormalizationMode.FIXED_GAIN
):
elif streamdetails.volume_normalization_mode == VolumeNormalizationMode.FIXED_GAIN:
# apply used defined fixed volume/gain correction
gain_correct: float = await self.mass.config.get_core_config_value(
self.domain,
CONF_VOLUME_NORMALIZATION_FIXED_GAIN_RADIO
if streamdetails.media_type == MediaType.RADIO
else CONF_VOLUME_NORMALIZATION_FIXED_GAIN_TRACKS,
CONF_VOLUME_NORMALIZATION_FIXED_GAIN_TRACKS
if streamdetails.media_type == MediaType.TRACK
else CONF_VOLUME_NORMALIZATION_FIXED_GAIN_RADIO,
)
gain_correct = round(gain_correct, 2)
filter_params.append(f"volume={gain_correct}dB")
elif enable_volume_normalization and streamdetails.loudness is not None:
elif streamdetails.volume_normalization_mode == VolumeNormalizationMode.MEASUREMENT_ONLY:
# volume normalization with known loudness measurement
# apply volume/gain correction
gain_correct = streamdetails.target_loudness - streamdetails.loudness
Expand Down Expand Up @@ -987,7 +976,11 @@ async def get_media_stream(
# pad some silence before the radio stream starts to create some headroom
# for radio stations that do not provide any look ahead buffer
# without this, some radio streams jitter a lot, especially with dynamic normalization
pad_seconds = 5 if dynamic_volume_normalization else 2
pad_seconds = (
5
if streamdetails.volume_normalization_mode == VolumeNormalizationMode.DYNAMIC
else 2
)
async for chunk in get_silence(pad_seconds, pcm_format):
yield chunk

Expand Down
14 changes: 12 additions & 2 deletions music_assistant/helpers/audio.py
Original file line number Diff line number Diff line change
Expand Up @@ -264,10 +264,10 @@ async def get_stream_details(
streamdetails.prefer_album_loudness = prefer_album_loudness
player_settings = await mass.config.get_player_config(streamdetails.queue_id)
core_config = await mass.config.get_core_config("streams")
streamdetails.target_loudness = player_settings.get_value(CONF_VOLUME_NORMALIZATION_TARGET)
streamdetails.volume_normalization_mode = _get_normalization_mode(
core_config, player_settings, streamdetails
)
streamdetails.target_loudness = player_settings.get_value(CONF_VOLUME_NORMALIZATION_TARGET)

process_time = int((time.time() - time_start) * 1000)
LOGGER.debug(
Expand Down Expand Up @@ -311,7 +311,7 @@ async def get_media_stream(
await ffmpeg_proc.start()
logger.debug(
"Started media stream for %s"
" - using streamtype: %s "
" - using streamtype: %s"
" - volume normalization: %s"
" - pcm format: %s"
" - ffmpeg PID: %s",
Expand Down Expand Up @@ -993,6 +993,9 @@ def _get_normalization_mode(
if not player_config.get_value(CONF_VOLUME_NORMALIZATION):
# disabled for this player
return VolumeNormalizationMode.DISABLED
if streamdetails.target_loudness is None:
# no target loudness set, disable normalization
return VolumeNormalizationMode.DISABLED
# work out preference for track or radio
preference = VolumeNormalizationMode(
core_config.get_value(
Expand All @@ -1014,5 +1017,12 @@ def _get_normalization_mode(
if streamdetails.loudness is None and preference == VolumeNormalizationMode.FALLBACK_FIXED_GAIN:
return VolumeNormalizationMode.FIXED_GAIN

# handle measurement available - chosen mode is measurement
if streamdetails.loudness and preference not in (
VolumeNormalizationMode.DISABLED,
VolumeNormalizationMode.FIXED_GAIN,
):
return VolumeNormalizationMode.MEASUREMENT_ONLY

# simply return the preference
return preference

0 comments on commit 17d23c3

Please sign in to comment.