several small fixes
authorMarcel van der Veldt <m.vanderveldt@outlook.com>
Thu, 7 Jul 2022 23:41:03 +0000 (01:41 +0200)
committerMarcel van der Veldt <m.vanderveldt@outlook.com>
Thu, 7 Jul 2022 23:41:03 +0000 (01:41 +0200)
music_assistant/controllers/streams.py
music_assistant/helpers/audio.py
music_assistant/helpers/process.py
music_assistant/helpers/tags.py
music_assistant/models/enums.py

index bd98c61455e2cb2912c353572e57f232a62d6bba..987a3051708ef373869cfb957e3c6d9cb17880a4 100644 (file)
@@ -344,7 +344,13 @@ class QueueStream:
         self.all_clients_connected = asyncio.Event()
         self.index_in_buffer = start_index
         self.signal_next: bool = False
-        self.chunk_size = get_chunksize(output_format)
+        self.chunk_size = get_chunksize(
+            output_format,
+            self.pcm_sample_rate,
+            self.pcm_bit_depth,
+            self.pcm_channels,
+            0.1,
+        )
         self._runner_task: Optional[asyncio.Task] = None
         self._prev_chunk: bytes = b""
         if autostart:
@@ -431,16 +437,12 @@ class QueueStream:
         ]
         # get the raw pcm bytes from the queue stream and on the fly encode to wanted format
         # send the compressed/encoded stream to the client(s).
-        sample_size = int(
-            self.pcm_sample_rate * (self.pcm_bit_depth / 8) * self.pcm_channels
-        )
         async with AsyncProcess(ffmpeg_args, True, self.chunk_size) as ffmpeg_proc:
 
             async def writer():
                 """Task that sends the raw pcm audio to the ffmpeg process."""
                 async for audio_chunk in self._get_queue_stream():
                     await ffmpeg_proc.write(audio_chunk)
-                    self.seconds_streamed += len(audio_chunk) / sample_size
                     del audio_chunk
                 # write eof when last packet is received
                 ffmpeg_proc.write_eof()
@@ -592,18 +594,22 @@ class QueueStream:
                     use_crossfade = False
             prev_track = queue_track
 
-            # calculate sample_size based on PCM params for 100ms of audio
-            sample_size = int(
-                self.pcm_sample_rate
-                * (self.pcm_bit_depth / 8)
-                * self.pcm_channels
-                * 0.1
+            # calculate sample_size based on PCM params for 200ms of audio
+            input_format = ContentType.from_bit_depth(
+                self.pcm_bit_depth, self.pcm_floating_point
             )
-            # buffer size is duration of crossfade + 6 seconds
+            sample_size = get_chunksize(
+                input_format,
+                self.pcm_sample_rate,
+                self.pcm_bit_depth,
+                self.pcm_channels,
+                1,
+            )
+            # buffer size is duration of crossfade + 3 seconds
             crossfade_duration = self.queue.settings.crossfade_duration or fade_in or 1
-            crossfade_size = (sample_size * 10) * crossfade_duration
-            buf_size = (sample_size * 10) * (crossfade_duration * 6)
-            total_size = (sample_size * 10) * (queue_track.duration or 0)
+            crossfade_size = (sample_size * 5) * crossfade_duration
+            buf_size = (sample_size * 5) * (crossfade_duration * 3)
+            total_size = (sample_size * 5) * (queue_track.duration or 0)
 
             self.logger.info(
                 "Start Streaming queue track: %s (%s) for queue %s",
@@ -658,7 +664,6 @@ class QueueStream:
 
                 # buffer full for fade-in / crossfade
                 if buffer and (last_fadeout_part or fade_in):
-
                     # strip silence of start and create fade-in part
                     first_part = await strip_silence(
                         buffer + chunk, pcm_fmt, self.pcm_sample_rate
@@ -666,10 +671,10 @@ class QueueStream:
 
                     if last_fadeout_part:
                         # crossfade
-                        first_part = first_part[:crossfade_size]
+                        fadein_part = first_part[:crossfade_size]
                         remaining_bytes = first_part[crossfade_size:]
                         crossfade_part = await crossfade_pcm_parts(
-                            first_part,
+                            fadein_part,
                             last_fadeout_part,
                             crossfade_duration,
                             pcm_fmt,
@@ -679,6 +684,12 @@ class QueueStream:
                         yield crossfade_part
                         bytes_written += len(crossfade_part)
                         del crossfade_part
+                        del fadein_part
+                        # also write the leftover bytes from the strip action
+                        if remaining_bytes:
+                            yield remaining_bytes
+                            bytes_written += len(remaining_bytes)
+                            del remaining_bytes
                     else:
                         # fade-in
                         fadein_part = await fadein_pcm_part(
@@ -689,14 +700,10 @@ class QueueStream:
                         )
                         yield fadein_part
                         bytes_written += len(fadein_part)
+                        del fadein_part
 
                     # clear vars
                     last_fadeout_part = b""
-                    # also write the leftover bytes from the strip action
-                    yield remaining_bytes
-                    bytes_written += len(remaining_bytes)
-                    del remaining_bytes
-                    del fadein_part
                     del first_part
                     del chunk
                     buffer = b""
index 28c697851387e280c8718cc9ae362dbae303e0a4..1a6ad224c2637e9c8f48da69921722dc43263f7c 100644 (file)
@@ -668,20 +668,31 @@ async def get_silence(
             yield chunk
 
 
-def get_chunksize(content_type: ContentType) -> int:
+def get_chunksize(
+    content_type: ContentType,
+    sample_rate: int = 44100,
+    bit_depth: int = 16,
+    channels: int = 2,
+    seconds: float = 1.0,
+) -> int:
     """Get a default chunksize for given contenttype."""
-    if content_type.is_pcm():
-        return 512000
+    pcm_size = int(sample_rate * (bit_depth / 8) * channels * seconds)
+    if content_type.is_pcm() or content_type == ContentType.WAV:
+        return pcm_size
+    if content_type == ContentType.FLAC:
+        return int(pcm_size * 0.61)
+    if content_type == ContentType.WAVPACK:
+        return int(pcm_size * 0.60)
     if content_type in (
         ContentType.AAC,
         ContentType.M4A,
     ):
-        return 32000
+        return int(256000 * seconds)
     if content_type in (
         ContentType.MP3,
         ContentType.OGG,
     ):
-        return 64000
+        return int(320000 * seconds)
     return 256000
 
 
index 4f9e1b5805cb0a5b6188a483751bff0b8f8577b8..ed33b76d600d280be4683dcab4bbcd96eb4ce803 100644 (file)
@@ -52,7 +52,7 @@ class AsyncProcess:
                 stdin=asyncio.subprocess.PIPE if self._enable_stdin else None,
                 stdout=asyncio.subprocess.PIPE if self._enable_stdout else None,
                 stderr=asyncio.subprocess.PIPE if self._enable_stderr else None,
-                limit=self.chunk_size * 15,
+                limit=64000000,
                 close_fds=True,
             )
         else:
@@ -61,7 +61,7 @@ class AsyncProcess:
                 stdin=asyncio.subprocess.PIPE if self._enable_stdin else None,
                 stdout=asyncio.subprocess.PIPE if self._enable_stdout else None,
                 stderr=asyncio.subprocess.PIPE if self._enable_stderr else None,
-                limit=self.chunk_size * 15,
+                limit=64000000,
                 close_fds=True,
             )
         return self
index 72f54ca6abea9ccdbd999e1d408bea524570c773..d16572bc1fbe6424e46b462733785add9cd9e3d4 100644 (file)
@@ -102,7 +102,7 @@ class AudioTags:
     def year(self) -> int | None:
         """Return album's year if present, parsed from date."""
         if tag := self.tags.get("originalyear"):
-            return int(tag)
+            return int(tag.split("-")[0])
         if tag := self.tags.get("otiginaldate"):
             return int(tag.split("-")[0])
         if tag := self.tags.get("date"):
index a8fb7a0c93a0319987f5c230bcb4e78e2abf1e9c..feb9eee410bb487031b86d1b58631d7b89daf7a4 100644 (file)
@@ -86,6 +86,7 @@ class ContentType(Enum):
     WMA = "wma"
     M4A = "m4a"
     DSF = "dsf"
+    WAVPACK = "wv"
     PCM_S16LE = "s16le"  # PCM signed 16-bit little-endian
     PCM_S24LE = "s24le"  # PCM signed 24-bit little-endian
     PCM_S32LE = "s32le"  # PCM signed 32-bit little-endian