Fix: output bit depth should be as high as possibe
authorMarcel van der Veldt <m.vanderveldt@outlook.com>
Mon, 10 Mar 2025 22:12:17 +0000 (23:12 +0100)
committerMarcel van der Veldt <m.vanderveldt@outlook.com>
Mon, 10 Mar 2025 22:12:17 +0000 (23:12 +0100)
If a player supports >16 bits we should use that as output format as well because internally we use 32bits to leave headroom for processing

music_assistant/controllers/streams.py

index f4f5f31e1836dab00b953f804245c1ce403b82aa..3f3efa87873c7e15e28dc4ab7d069ec6c0237161 100644 (file)
@@ -315,12 +315,20 @@ class StreamsController(CoreController):
                 )
                 queue_item.available = False
                 raise web.HTTPNotFound(reason=f"No streamdetails for Queue item: {queue_item_id}")
+
+        # pick pcm format based on the streamdetails and player capabilities
+        pcm_format = AudioFormat(
+            content_type=DEFAULT_PCM_FORMAT.content_type,
+            sample_rate=queue_item.streamdetails.audio_format.sample_rate,
+            bit_depth=DEFAULT_PCM_FORMAT.bit_depth,
+            channels=2,
+        )
         # work out output format/details
         output_format = await self.get_output_format(
             output_format_str=request.match_info["fmt"],
             player=queue_player,
-            default_sample_rate=queue_item.streamdetails.audio_format.sample_rate,
-            default_bit_depth=queue_item.streamdetails.audio_format.bit_depth,
+            content_sample_rate=pcm_format.sample_rate,
+            content_bit_depth=pcm_format.bit_depth,
         )
 
         # prepare request, add some DLNA/UPNP compatible headers
@@ -359,13 +367,6 @@ class StreamsController(CoreController):
             queue.display_name,
         )
 
-        # pick pcm format based on the streamdetails and player capabilities
-        pcm_format = AudioFormat(
-            content_type=DEFAULT_PCM_FORMAT.content_type,
-            sample_rate=queue_item.streamdetails.audio_format.sample_rate,
-            bit_depth=DEFAULT_PCM_FORMAT.bit_depth,
-            channels=2,
-        )
         chunk_num = 0
 
         # inform the queue that the track is now loaded in the buffer
@@ -425,8 +426,8 @@ class StreamsController(CoreController):
         output_format = await self.get_output_format(
             output_format_str=request.match_info["fmt"],
             player=queue_player,
-            default_sample_rate=flow_pcm_format.sample_rate,
-            default_bit_depth=flow_pcm_format.bit_depth,
+            content_sample_rate=flow_pcm_format.sample_rate,
+            content_bit_depth=flow_pcm_format.bit_depth,
         )
         # work out ICY metadata support
         icy_preference = self.mass.config.get_raw_player_config_value(
@@ -615,8 +616,7 @@ class StreamsController(CoreController):
         output_format = await self.get_output_format(
             output_format_str=request.match_info["fmt"],
             player=player,
-            default_sample_rate=plugin_source.audio_format.sample_rate,
-            default_bit_depth=plugin_source.audio_format.bit_depth,
+            content_sample_rate=plugin_source.audio_format.sample_rate,
         )
         headers = {
             **DEFAULT_STREAM_HEADERS,
@@ -715,7 +715,7 @@ class StreamsController(CoreController):
                 queue_track = start_queue_item
             else:
                 try:
-                    queue_track = await self.mass.player_queues.load_next_item(
+                    queue_track = await self.mass.player_queues.get_next_queue_item(
                         queue.queue_id, queue_track.queue_item_id
                     )
                 except QueueEmpty:
@@ -991,8 +991,8 @@ class StreamsController(CoreController):
         self,
         output_format_str: str,
         player: Player,
-        default_sample_rate: int,
-        default_bit_depth: int,
+        content_sample_rate: int,
+        content_bit_depth: int,
     ) -> AudioFormat:
         """Parse (player specific) output format details for given format string."""
         content_type: ContentType = ContentType.try_parse(output_format_str)
@@ -1001,20 +1001,21 @@ class StreamsController(CoreController):
         ] = await self.mass.config.get_player_config_value(
             player.player_id, CONF_SAMPLE_RATES, unpack_splitted_values=True
         )
+        output_channels_str = self.mass.config.get_raw_player_config_value(
+            player.player_id, CONF_OUTPUT_CHANNELS, "stereo"
+        )
         supported_sample_rates: tuple[int] = tuple(int(x[0]) for x in supported_rates_conf)
         supported_bit_depths: tuple[int] = tuple(int(x[1]) for x in supported_rates_conf)
 
         player_max_bit_depth = max(supported_bit_depths)
-        if default_sample_rate in supported_sample_rates:
-            output_sample_rate = default_sample_rate
+        output_bit_depth = min(content_bit_depth, player_max_bit_depth)
+        if content_sample_rate in supported_sample_rates:
+            output_sample_rate = content_sample_rate
         else:
             output_sample_rate = max(supported_sample_rates)
-        output_bit_depth = min(default_bit_depth, player_max_bit_depth)
-        output_channels_str = self.mass.config.get_raw_player_config_value(
-            player.player_id, CONF_OUTPUT_CHANNELS, "stereo"
-        )
-        output_channels = 1 if output_channels_str != "stereo" else 2
+
         if not content_type.is_lossless():
+            # no point in having a higher bit depth for lossy formats
             output_bit_depth = 16
             output_sample_rate = min(48000, output_sample_rate)
         if output_format_str == "pcm":
@@ -1023,7 +1024,7 @@ class StreamsController(CoreController):
             content_type=content_type,
             sample_rate=output_sample_rate,
             bit_depth=output_bit_depth,
-            channels=output_channels,
+            channels=1 if output_channels_str != "stereo" else 2,
         )
 
     async def _select_flow_format(