refactor sound code

this addresses a bug where mplayer was sometimes not being restarted when
opening a new deck, and should remove the need for atexit or hacks in the GUI
code to make sure mplayer is closed
This commit is contained in:
Damien Elmes 2010-12-28 03:53:30 +09:00
parent ece9b2eccd
commit d56bddea2d

View file

@ -8,7 +8,7 @@ Sound support
""" """
__docformat__ = 'restructuredtext' __docformat__ = 'restructuredtext'
import re, sys, threading, time, subprocess, os, signal, atexit, errno import re, sys, threading, time, subprocess, os, signal, errno
from anki.hooks import addHook, runHook from anki.hooks import addHook, runHook
# Shared utils # Shared utils
@ -123,6 +123,7 @@ mplayerQueue = []
mplayerManager = None mplayerManager = None
mplayerReader = None mplayerReader = None
mplayerCond = threading.Condition() mplayerCond = threading.Condition()
mplayerClear = False
class MplayerReader(threading.Thread): class MplayerReader(threading.Thread):
"Read any debugging info to prevent mplayer from blocking." "Read any debugging info to prevent mplayer from blocking."
@ -140,33 +141,50 @@ class MplayerReader(threading.Thread):
class MplayerMonitor(threading.Thread): class MplayerMonitor(threading.Thread):
def run(self): def run(self):
global mplayerClear
self.mplayer = None self.mplayer = None
self.deadPlayers = []
while 1: while 1:
mplayerCond.acquire() mplayerCond.acquire()
while not mplayerQueue:
if not mplayerCond:
return
mplayerCond.wait() mplayerCond.wait()
# clearing playing file if mplayer is running
if mplayerClear and self.mplayer:
self.mplayer.stdin.write("stop\n")
if mplayerQueue:
# ensure started
if not self.mplayer: if not self.mplayer:
self.startProcess() self.startProcess()
if self.mplayer != -1 and self.mplayer.poll() is not None: # loop through files to play
self.mplayer.wait()
self.startProcess()
nextClears = False
while mplayerQueue: while mplayerQueue:
item = mplayerQueue.pop(0) item = mplayerQueue.pop(0)
if item is None: if mplayerClear:
nextClears = True mplayerClear = False
continue
if nextClears:
nextClears = False
extra = "" extra = ""
else: else:
extra = " 1" extra = " 1"
cmd = 'loadfile "%s"%s\n' % (item, extra) cmd = 'loadfile "%s"%s\n' % (item, extra)
self.mplayer.stdin.write(cmd) self.mplayer.stdin.write(cmd)
# wait() on finished processes. we don't want to block on the
# wait, so we keep trying each time we're reactivated
def clean(pl):
if pl.poll() is not None:
pl.wait()
return False
else:
return True
self.deadPlayers = [pl for pl in self.deadPlayers if clean(pl)]
mplayerCond.release() mplayerCond.release()
def kill(self):
if not self.mplayer:
return
try:
self.mplayer.stdin.write("quit\n")
self.deadPlayers.append(self.mplayer)
except:
pass
self.mplayer = None
def startProcess(self): def startProcess(self):
try: try:
cmd = mplayerCmd + ["-slave", "-idle"] cmd = mplayerCmd + ["-slave", "-idle"]
@ -187,8 +205,10 @@ def queueMplayer(path):
runHook("soundQueued") runHook("soundQueued")
def clearMplayerQueue(): def clearMplayerQueue():
global mplayerClear
mplayerCond.acquire() mplayerCond.acquire()
mplayerQueue.append(None) mplayerClear = True
mplayerCond.notifyAll()
mplayerCond.release() mplayerCond.release()
def ensureMplayerThreads(): def ensureMplayerThreads():
@ -200,37 +220,13 @@ def ensureMplayerThreads():
mplayerReader = MplayerReader() mplayerReader = MplayerReader()
mplayerReader.daemon = True mplayerReader.daemon = True
mplayerReader.start() mplayerReader.start()
atexit.register(stopMplayer)
def stopMplayer(restart=False): def stopMplayer():
if not mplayerManager: if not mplayerManager:
return return
mplayerCond.acquire() mplayerManager.kill()
if mplayerManager.mplayer:
while 1:
try:
mplayerManager.mplayer.stdin.write("quit\n")
break
except OSError, e:
if e.errno != errno.EINTR:
# osx throws interrupt errors regularly, but we want to
# ignore other errors on shutdown
break
except IOError:
# already closed
break
except ValueError:
# already closed
break
if not restart:
mplayerManager.mplayer = -1
mplayerCond.notifyAll()
mplayerCond.release()
def stopMplayerOnce(): addHook("deckClosed", stopMplayer)
stopMplayer(restart=True)
addHook("deckClosed", stopMplayerOnce)
# PyAudio recording # PyAudio recording
########################################################################## ##########################################################################