Skip to content

Commit

Permalink
fix audio getting stuck (1/2)
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
dae committed Mar 14, 2020
1 parent e745230 commit f30853f
Showing 1 changed file with 4 additions and 18 deletions.
22 changes: 4 additions & 18 deletions qt/aqt/sound.py
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,6 @@ def shutdown(self) -> None:
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:
Expand Down Expand Up @@ -235,17 +234,15 @@ def __init__(self, taskman: TaskManager) -> None:
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(
Expand All @@ -264,37 +261,26 @@ def _wait_for_termination(self, tag: AVTag) -> None:

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:
self._process.wait(0.1)
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(
_(
Expand Down

0 comments on commit f30853f

Please sign in to comment.