From 73879c6a997685c277aa5068b75a2192bafee058 Mon Sep 17 00:00:00 2001 From: Keith Edmunds Date: Mon, 7 Jun 2021 20:46:05 +0100 Subject: [PATCH] Add locking to music.py Ensure nothing interrupts the stop - release - nullify sequence. Also don't limit how many concurrent fades there can be. --- app/music.py | 93 ++++++++++++++++++++++++++++------------------------ 1 file changed, 51 insertions(+), 42 deletions(-) diff --git a/app/music.py b/app/music.py index d437278..20c4ca4 100644 --- a/app/music.py +++ b/app/music.py @@ -8,6 +8,8 @@ from time import sleep from log import DEBUG, ERROR +lock = threading.Lock() + class Music: """ @@ -16,7 +18,7 @@ class Music: def __init__(self): self.current_track_start_time = None - self.fading = False + self.fading = 0 self.VLC = vlc.Instance() self.player = None self.track_path = None @@ -30,18 +32,19 @@ class Music: to hold up the UI during the fade. """ - DEBUG("music.fade()", True) + with lock: + DEBUG("music.fade()", True) - if not self.playing(): - return None + if not self.player: + return - # Only allow one track to fade at a time - if self.fading: - return - self.fading = True + if not self.player.get_position() > 0 and self.player.is_playing(): + return - thread = threading.Thread(target=self._fade) - thread.start() + self.fading += 1 + + thread = threading.Thread(target=self._fade) + thread.start() def _fade(self): """ @@ -69,27 +72,30 @@ class Music: measures_to_reduce_by = 0 for i in range(1, steps + 1): measures_to_reduce_by += i - volume_factor = 1 - (measures_to_reduce_by / total_measures_count) + volume_factor = 1 - ( + measures_to_reduce_by / total_measures_count) p.audio_set_volume(int(self.max_volume * volume_factor)) sleep(sleep_time) self.stop(p) - self.fading = False + self.fading -= 1 def get_playtime(self): "Return elapsed play time" - if not self.player: - return None + with lock: + if not self.player: + return None - return self.player.get_time() + return self.player.get_time() def get_position(self): "Return current position" - DEBUG("music.get_position", True) + with lock: + DEBUG("music.get_position", True) - return self.player.get_position() + return self.player.get_position() def play(self, path): """ @@ -118,46 +124,49 @@ class Music: get_position seems more reliable. """ - if self.player: - if self.player.get_position() > 0 and self.player.is_playing(): - return True + with lock: + if self.player: + if self.player.get_position() > 0 and self.player.is_playing(): + return True - # We take a copy of the player when fading, so we could be - # playing in a fade nowFalse - return self.fading + # We take a copy of the player when fading, so we could be + # playing in a fade nowFalse + return self.fading > 0 def set_position(self, ms): "Set current play time in milliseconds from start" - return self.player.set_time(ms) + with lock: + return self.player.set_time(ms) def set_volume(self, volume): "Set maximum volume used for player" - if not self.player: - return + with lock: + if not self.player: + return - self.max_volume = volume - self.player.audio_set_volume(volume) + self.max_volume = volume + self.player.audio_set_volume(volume) def stop(self, player=None): "Immediately stop playing" - DEBUG(f"music.stop(), {player=}", True) + with lock: + DEBUG(f"music.stop(), {player=}", True) - if not player: - if not self.player: - return - player = self.player + if not player: + if not self.player: + return + player = self.player - DEBUG(f"music.stop({player=})") + DEBUG(f"music.stop({player=})") - position = player.get_position() - player.stop() - p = player - # Ensure we don't reference player after release - player = None - DEBUG(f"Releasing player {p=}", True) - p.release() + position = player.get_position() + player.stop() + DEBUG(f"Releasing player {player=}", True) + player.release() + # Ensure we don't reference player after release + player = None - return position + return position