Chore: Simplify internal pcm format
authorMarcel van der Veldt <m.vanderveldt@outlook.com>
Thu, 16 Jan 2025 21:39:36 +0000 (22:39 +0100)
committerMarcel van der Veldt <m.vanderveldt@outlook.com>
Thu, 16 Jan 2025 21:39:36 +0000 (22:39 +0100)
just always use 32 bits floating points

music_assistant/constants.py
music_assistant/controllers/streams.py
music_assistant/helpers/ffmpeg.py
music_assistant/providers/airplay/const.py
music_assistant/providers/player_group/__init__.py
music_assistant/providers/slimproto/__init__.py
music_assistant/providers/snapcast/__init__.py

index 875748c1d900d33fe3508ef3442d71d66747a9ee..de32b3ffc910b659611855d26befad10872a20ab 100644 (file)
@@ -4,7 +4,8 @@ import pathlib
 from typing import Final
 
 from music_assistant_models.config_entries import ConfigEntry, ConfigValueOption
-from music_assistant_models.enums import ConfigEntryType
+from music_assistant_models.enums import ConfigEntryType, ContentType
+from music_assistant_models.media_items import AudioFormat
 
 API_SCHEMA_VERSION: Final[int] = 26
 MIN_SCHEMA_VERSION: Final[int] = 24
@@ -523,3 +524,27 @@ BASE_PLAYER_CONFIG_ENTRIES = (
     CONF_ENTRY_SAMPLE_RATES,
     CONF_ENTRY_HTTP_PROFILE_FORCED_2,
 )
+
+
+DEFAULT_STREAM_HEADERS = {
+    "Server": "Music Assistant",
+    "transferMode.dlna.org": "Streaming",
+    "contentFeatures.dlna.org": "DLNA.ORG_OP=00;DLNA.ORG_CI=0;DLNA.ORG_FLAGS=0d500000000000000000000000000000",  # noqa: E501
+    "Cache-Control": "no-cache",
+    "Pragma": "no-cache",
+}
+ICY_HEADERS = {
+    "icy-name": "Music Assistant",
+    "icy-description": "Music Assistant - Your personal music assistant",
+    "icy-version": "1",
+    "icy-logo": MASS_LOGO_ONLINE,
+}
+
+DEFAULT_PCM_FORMAT = AudioFormat(
+    # always prefer float32 as internal pcm format to create headroom
+    # for filters such as dsp and volume normalization
+    content_type=ContentType.PCM_F32LE,
+    sample_rate=48000,
+    bit_depth=32,
+    channels=2,
+)
index ed22d50510ced59bd25818e2096e8ef618e17ed4..97c4a737e05068106ad25527ed205fe43896b262 100644 (file)
@@ -39,12 +39,13 @@ from music_assistant.constants import (
     CONF_OUTPUT_CHANNELS,
     CONF_PUBLISH_IP,
     CONF_SAMPLE_RATES,
-    CONF_VOLUME_NORMALIZATION,
     CONF_VOLUME_NORMALIZATION_FIXED_GAIN_RADIO,
     CONF_VOLUME_NORMALIZATION_FIXED_GAIN_TRACKS,
     CONF_VOLUME_NORMALIZATION_RADIO,
     CONF_VOLUME_NORMALIZATION_TRACKS,
-    MASS_LOGO_ONLINE,
+    DEFAULT_PCM_FORMAT,
+    DEFAULT_STREAM_HEADERS,
+    ICY_HEADERS,
     SILENCE_FILE,
     VERBOSE_LOG_LEVEL,
 )
@@ -74,23 +75,6 @@ if TYPE_CHECKING:
     from music_assistant_models.streamdetails import StreamDetails
 
 
-DEFAULT_STREAM_HEADERS = {
-    "Server": "Music Assistant",
-    "transferMode.dlna.org": "Streaming",
-    "contentFeatures.dlna.org": "DLNA.ORG_OP=00;DLNA.ORG_CI=0;DLNA.ORG_FLAGS=0d500000000000000000000000000000",  # noqa: E501
-    "Cache-Control": "no-cache",
-    "Pragma": "no-cache",
-}
-ICY_HEADERS = {
-    "icy-name": "Music Assistant",
-    "icy-description": "Music Assistant - Your personal music assistant",
-    "icy-version": "1",
-    "icy-logo": MASS_LOGO_ONLINE,
-}
-FLOW_DEFAULT_SAMPLE_RATE = 48000
-FLOW_DEFAULT_BIT_DEPTH = 24
-
-
 isfile = wrap(os.path.isfile)
 
 
@@ -346,17 +330,10 @@ class StreamsController(CoreController):
         )
 
         # pick pcm format based on the streamdetails and player capabilities
-        if self.mass.config.get_raw_player_config_value(queue_id, CONF_VOLUME_NORMALIZATION, True):
-            # prefer f32 when volume normalization is enabled
-            bit_depth = 32
-            floating_point = True
-        else:
-            bit_depth = queue_item.streamdetails.audio_format.bit_depth
-            floating_point = False
         pcm_format = AudioFormat(
-            content_type=ContentType.from_bit_depth(bit_depth, floating_point),
+            content_type=DEFAULT_PCM_FORMAT.content_type,
             sample_rate=queue_item.streamdetails.audio_format.sample_rate,
-            bit_depth=bit_depth,
+            bit_depth=DEFAULT_PCM_FORMAT.bit_depth,
             channels=2,
         )
         chunk_num = 0
@@ -1081,24 +1058,13 @@ class StreamsController(CoreController):
             player.player_id, CONF_SAMPLE_RATES
         )
         supported_sample_rates: tuple[int] = tuple(x[0] for x in supported_rates_conf)
-        supported_bit_depths: tuple[int] = tuple(x[1] for x in supported_rates_conf)
-        player_max_bit_depth = max(supported_bit_depths)
-        for sample_rate in (192000, 96000, 48000, 44100):
+        for sample_rate in (192000, 96000, DEFAULT_PCM_FORMAT.sample_rate):
             if sample_rate in supported_sample_rates:
                 output_sample_rate = sample_rate
                 break
-        if self.mass.config.get_raw_player_config_value(
-            player.player_id, CONF_VOLUME_NORMALIZATION, True
-        ):
-            # prefer f32 when volume normalization is enabled
-            output_bit_depth = 32
-            floating_point = True
-        else:
-            output_bit_depth = min(24, player_max_bit_depth)
-            floating_point = False
         return AudioFormat(
-            content_type=ContentType.from_bit_depth(output_bit_depth, floating_point),
+            content_type=DEFAULT_PCM_FORMAT.content_type,
             sample_rate=output_sample_rate,
-            bit_depth=output_bit_depth,
+            bit_depth=DEFAULT_PCM_FORMAT.bit_depth,
             channels=2,
         )
index 82ac1b511133471929aa64728e0cc96e4b8a3cba..d187271efdea620bba9843f9f1104ec274f2a958 100644 (file)
@@ -289,10 +289,9 @@ def get_ffmpeg_args(
             *filter_params,
         ]
 
-    # determine if we need to do resampling
-    if (
-        input_format.sample_rate != output_format.sample_rate
-        or input_format.bit_depth > output_format.bit_depth
+    # determine if we need to do resampling (or dithering)
+    if input_format.sample_rate != output_format.sample_rate or (
+        input_format.bit_depth > 16 and output_format.bit_depth == 16
     ):
         libsoxr_support = get_global_cache_value(CACHE_ATTR_LIBSOXR_PRESENT)
         # prefer resampling with libsoxr due to its high quality
@@ -309,6 +308,8 @@ def get_ffmpeg_args(
             resample_filter += f":osr={output_format.sample_rate}"
 
         # bit depth conversion: apply dithering when going down to 16 bits
+        # this is only needed when we need to back to 16 bits
+        # when going from 32bits FP to 24 bits no dithering is needed
         if output_format.bit_depth == 16 and input_format.bit_depth > 16:
             resample_filter += ":osf=s16:dither_method=triangular_hp"
 
index 802456c0b89070bce42b226ee42178b25c7c8ca4..526ff5e579a12a9c6d1f6919f5dd18048f4cf471 100644 (file)
@@ -5,6 +5,8 @@ from __future__ import annotations
 from music_assistant_models.enums import ContentType
 from music_assistant_models.media_items import AudioFormat
 
+from music_assistant.constants import DEFAULT_PCM_FORMAT
+
 DOMAIN = "airplay"
 
 CONF_ENCRYPTION = "encryption"
@@ -22,9 +24,9 @@ CACHE_KEY_PREV_VOLUME = "airplay_prev_volume"
 FALLBACK_VOLUME = 20
 
 AIRPLAY_FLOW_PCM_FORMAT = AudioFormat(
-    content_type=ContentType.PCM_F32LE,
+    content_type=DEFAULT_PCM_FORMAT.content_type,
     sample_rate=44100,
-    bit_depth=32,
+    bit_depth=DEFAULT_PCM_FORMAT.bit_depth,
 )
 AIRPLAY_PCM_FORMAT = AudioFormat(
     content_type=ContentType.from_bit_depth(16), sample_rate=44100, bit_depth=16
index ba1a23ab027cb641a51fc7fe47acedf5d4d6fb74..60e76c58767294f41b10cbaac948eef857eaf589 100644 (file)
@@ -53,6 +53,7 @@ from music_assistant.constants import (
     CONF_GROUP_MEMBERS,
     CONF_HTTP_PROFILE,
     CONF_SAMPLE_RATES,
+    DEFAULT_PCM_FORMAT,
     create_sample_rates_config_entry,
 )
 from music_assistant.controllers.streams import DEFAULT_STREAM_HEADERS
@@ -75,9 +76,9 @@ if TYPE_CHECKING:
 
 
 UGP_FORMAT = AudioFormat(
-    content_type=ContentType.PCM_F32LE,
-    sample_rate=48000,
-    bit_depth=32,
+    content_type=DEFAULT_PCM_FORMAT.content_type,
+    sample_rate=DEFAULT_PCM_FORMAT.sample_rate,
+    bit_depth=DEFAULT_PCM_FORMAT.bit_depth,
 )
 
 # ruff: noqa: ARG002
index 601103c6565e5af381d7ada1742e02b4d8352229..9dbbe51afa6d314c423367d02a47f52a8993d7e0 100644 (file)
@@ -54,6 +54,7 @@ from music_assistant.constants import (
     CONF_ENTRY_SYNC_ADJUST,
     CONF_PORT,
     CONF_SYNC_ADJUST,
+    DEFAULT_PCM_FORMAT,
     VERBOSE_LOG_LEVEL,
     create_sample_rates_config_entry,
 )
@@ -365,9 +366,9 @@ class SlimprotoProvider(PlayerProvider):
 
         # this is a syncgroup, we need to handle this with a multi client stream
         master_audio_format = AudioFormat(
-            content_type=ContentType.PCM_F32LE,
-            sample_rate=48000,
-            bit_depth=32,
+            content_type=DEFAULT_PCM_FORMAT.content_type,
+            sample_rate=DEFAULT_PCM_FORMAT.sample_rate,
+            bit_depth=DEFAULT_PCM_FORMAT.bit_depth,
         )
         if media.media_type == MediaType.ANNOUNCEMENT:
             # special case: stream announcement
index 2fa7e41b4aafb956f3d5aa8726e48f1022a23b3b..916f8f4f7ee96457315db89aabdd9b0730ea9efb 100644 (file)
@@ -35,6 +35,7 @@ from music_assistant.constants import (
     CONF_ENTRY_CROSSFADE,
     CONF_ENTRY_CROSSFADE_DURATION,
     CONF_ENTRY_FLOW_MODE_ENFORCED,
+    DEFAULT_PCM_FORMAT,
     create_sample_rates_config_entry,
 )
 from music_assistant.helpers.audio import FFMpeg, get_ffmpeg_stream, get_player_filter_params
@@ -520,7 +521,7 @@ class SnapCastProvider(PlayerProvider):
                 start_queue_item=self.mass.player_queues.get_item(
                     media.queue_id, media.queue_item_id
                 ),
-                pcm_format=input_format,
+                pcm_format=DEFAULT_PCM_FORMAT,
             )
         else:
             # assume url or some other direct path