From f30853f5ed172458b5384341fa712077578562c6 Mon Sep 17 00:00:00 2001 From: Damien Elmes Date: Sun, 15 Mar 2020 09:26:31 +1000 Subject: [PATCH] fix audio getting stuck (1/2) The problem was caused by stop() doing a spin loop on the main thread waiting for the completion signal. This prevented Qt's run loop from executing, and so the completion signal was never delivered, meaning longer files would time out. Fixed by reworking the code so that stop() does not block at all - instead it just sets the termination flag, and AVPlayer does not unset current_player. Then when the completion callback fires, it can advance to the next file. TTS code still needs updating, and the lock should be safe to remove as the start/stop logic is all on the main thread. --- qt/aqt/sound.py | 22 ++++------------------ 1 file changed, 4 insertions(+), 18 deletions(-) diff --git a/qt/aqt/sound.py b/qt/aqt/sound.py index ecf3f73cf..2c783d72d 100644 --- a/qt/aqt/sound.py +++ b/qt/aqt/sound.py @@ -129,7 +129,6 @@ class AVPlayer: def _stop_if_playing(self) -> None: if self.current_player: self.current_player.stop() - self.current_player = None def _pop_next(self) -> Optional[AVTag]: if not self._enqueued: @@ -235,17 +234,15 @@ class SimpleProcessPlayer(Player): # pylint: disable=abstract-method self._lock = threading.Lock() def play(self, tag: AVTag, on_done: OnDoneCallback) -> None: + self._terminate_flag = False self._taskman.run_in_background( lambda: self._play(tag), lambda res: self._on_done(res, on_done) ) def stop(self) -> None: self._terminate_flag = True - # block until stopped - t = time.time() - while self._terminate_flag and time.time() - t < 3: - time.sleep(0.1) + # note: mplayer implementation overrides this def _play(self, tag: AVTag) -> None: assert isinstance(tag, SoundOrVideoTag) self._process = subprocess.Popen( @@ -264,19 +261,11 @@ class SimpleProcessPlayer(Player): # pylint: disable=abstract-method while True: with self._lock: - # if .stop() timed out, another thread may run when - # there is no process - if not self._process: - self._process = None - self._terminate_flag = False - return - # should we abort playing? if self._terminate_flag: self._process.terminate() self._process = None - self._terminate_flag = False - raise PlayerInterrupted() + return # wait for completion try: @@ -284,17 +273,14 @@ class SimpleProcessPlayer(Player): # pylint: disable=abstract-method if self._process.returncode != 0: print(f"player got return code: {self._process.returncode}") self._process = None - self._terminate_flag = False return except subprocess.TimeoutExpired: + # process still running, repeat loop pass def _on_done(self, ret: Future, cb: OnDoneCallback) -> None: try: ret.result() - except PlayerInterrupted: - # don't fire done callback when interrupted - return except FileNotFoundError: showWarning( _(