在Raspberry Pi Zero中使用Python Sounddevice播放音频,然后我需要重新启动一次
我的灵感来自这个项目将PI放入旧的旋转电话中。我正在使用Python将其栩栩如生,并执行我想执行的功能。第一个非常简单:当我拿起接收器时,请播放拨号音。
但是,当我尝试使用SoundDevice模块播放音频时,它将第一次使用,然后停止工作。而且,当我说停止工作时,我再也无法使用aplay
或说话者检验
或其他任何内容播放音频,直到重新启动为止。它使我认为在播放结束时没有正确关闭的东西。
硬件设置:
- raspberry pi Zero wh
- usb usb音频适配器
下面是我的代码类,Dioltone,本来是由我的Core应用程序运行的线程,因此在必要时可以启动/停止拨号音。底部是main
,旨在测试功能。它可以很好地工作,在窗户上一遍又一遍地,但是当我在pi上运行一次时,它只能使用一次。同时,我可以aplay
一遍又一遍地没有问题(前提是我没有尝试使用Python播放它)。
难道它正在锁定某些资源而不是发布吗?在该过程退出后,它以某种方式保持活跃?我在哪里寻找?可能是什么?
class DialTone(Thread):
def __init__(self, input_queue:Queue, device:int_or_str ) -> None:
"""Initialize the dialtone class
Args:
input_queue (Queue): A way to communicate with the thread
"""
super().__init__()
global _DIAL_TONE_FILE
self.input_queue = input_queue
sd.default.device = device
# Commit the dial-tone data to memory so we can start/end quickly
try:
self.data, self.fs = sf.read(_DIAL_TONE_FILE, always_2d=True)
except Exception as e:
logging.debug( type(e).__name__ + ":" + str(e) )
def run(self) -> None:
"""Play a dial tone in a continuous loop until we die
"""
while( True ):
cmd = self.input_queue.get(True)
if( cmd == DialToneComms.DT_START ):
logging.debug("DT: starting dial tone")
sd.play(self.data, self.fs, loop=True)
logging.debug(sd.get_status())
elif( cmd == DialToneComms.DT_END ):
logging.debug("DT: stopping dial tone")
try:
sd.stop(ignore_errors=False)
except Exception as e:
logging.error( type(e).__name__ + ":" + str(e) )
logging.debug(sd.get_status())
elif( cmd == DialToneComms.DT_KILL ):
logging.debug("DT: killing self")
try:
sd.stop(ignore_errors=False)
except Exception as e:
logging.error( type(e).__name__ + ":" + str(e) )
logging.debug(sd.get_status())
break
# Test this capability
if __name__ == "__main__":
parser = argparse.ArgumentParser()
parser.add_argument("--device",type=int_or_str,help="Audio device to use", default=1)
parser.add_argument("--play_time",type=float,help="how long to play the dial tone",default=10.0)
parser.add_argument("--num_cycles", type=int, help="Number of play, wait cycles", default=2)
args = parser.parse_args()
# Setup logging
logging.basicConfig(level=logging.DEBUG)
dial_tone_queue = Queue()
print( "Instantiating DialTone" )
dial_tone = DialTone(dial_tone_queue, args.device)
print( "Starting DialTone Thread" )
dial_tone.start()
# Play the dial tone a number of iterations
for _ in range(args.num_cycles):
print( "Playing dialtone for {} sec".format(args.play_time))
dial_tone_queue.put(DialToneComms.DT_START)
time.sleep(args.play_time)
dial_tone_queue.put(DialToneComms.DT_END)
print("Waiting 2 seconds")
time.sleep(2)
print("Kill the thread")
dial_tone_queue.put(DialToneComms.DT_KILL)
print("Joining DialTone")
dial_tone.join(_JOIN_WAIT_TIME)
if( dial_tone.is_alive() ):
logging.warning( "Dial tone thread is still alive! What gives?" )
else:
logging.warning( "Dial tone thread is nice and dead")
在PI上进行第一次运行的输出。底部的错误是我停止了没有播放的流的结果(停止电话已经杀死了它,而杀戮电话肯定只是在确保)。
Instantiating DialTone
Starting DialTone Thread
Playing dialtone for 3.0 sec
DEBUG:root:DT: starting dial tone
DEBUG:root:
Waiting 2 seconds
DEBUG:root:DT: stopping dial tone
DEBUG:root:
Playing dialtone for 3.0 sec
DEBUG:root:DT: starting dial tone
DEBUG:root:
Waiting 2 seconds
DEBUG:root:DT: stopping dial tone
DEBUG:root:
Playing dialtone for 3.0 sec
DEBUG:root:DT: starting dial tone
DEBUG:root:
Waiting 2 seconds
DEBUG:root:DT: stopping dial tone
DEBUG:root:
Kill the thread
Joining DialTone
DEBUG:root:DT: killing self
ERROR:root:PortAudioError:Error stopping stream: Invalid stream pointer [PaErrorCode -9988]
DEBUG:root:
WARNING:root:Dial tone thread is nice and dead
PI上的第二次输出。请注意,事情开始发生在秩序中,尤其是在停止之后,但是音频从未第二次播放。
Instantiating DialTone
Starting DialTone Thread
Playing dialtone for 3.0 sec
DEBUG:root:DT: starting dial tone
DEBUG:root:
Waiting 2 seconds
DEBUG:root:DT: stopping dial tone
Playing dialtone for 3.0 sec
Waiting 2 seconds
Playing dialtone for 3.0 sec
Waiting 2 seconds
Kill the thread
Joining DialTone
DEBUG:root:
DEBUG:root:DT: starting dial tone
DEBUG:root:
DEBUG:root:DT: stopping dial tone
WARNING:root:Dial tone thread is still alive! What gives?
DEBUG:root:
DEBUG:root:DT: starting dial tone
DEBUG:root:
DEBUG:root:DT: stopping dial tone
DEBUG:root:
DEBUG:root:DT: killing self
ERROR:root:PortAudioError:Error stopping stream: Invalid stream pointer [PaErrorCode -9988]
DEBUG:root:
Windows上的输出:
Instantiating DialTone
Starting DialTone Thread
Playing dialtone for 3.0 sec
DEBUG:root:DT: starting dial tone
DEBUG:root:
Waiting 2 seconds
DEBUG:root:DT: stopping dial tone
DEBUG:root:
Playing dialtone for 3.0 sec
DEBUG:root:DT: starting dial tone
DEBUG:root:
DEBUG:root:DT: stopping dial tone
Waiting 2 seconds
DEBUG:root:
Playing dialtone for 3.0 sec
DEBUG:root:DT: starting dial tone
DEBUG:root:
Waiting 2 seconds
DEBUG:root:DT: stopping dial tone
DEBUG:root:
Kill the thread
DEBUG:root:DT: killing self
Joining DialTone
ERROR:root:PortAudioError:Error stopping stream: Invalid stream pointer [PaErrorCode -9988]
DEBUG:root:
WARNING:root:Dial tone thread is nice and dead
I was inspired by this project to put a Pi into an old rotary telephone. I'm using python to bring it to life and perform the functions I would like to perform. The first of these is pretty simple: play a dial tone when I pick up the receiver.
However, when I try to play audio using the sounddevice module, it will work the first time, and then stop working. And when I say stop working, I can no longer play audio with aplay
or speaker-test
or anything else until I reboot. It makes me think that something is not closing out correctly at the end of playback.
Hardware Setup:
- Raspberry Pi Zero WH
- USB Audio Adapter
Below is the code for my class, DialTone which is meant to be a thread run by my core application so it can start/stop the dial tone whenver necessary. At the bottom is a main
meant to test the capability. It works just fine, over and over on Windows, but when I run it on the Pi: just once. Meanwhile I can aplay
the same file over and over without issue (provided I haven't tried to play it with python).
Could it be that it's locking some resource and not releasing it? And somehow it's staying active after the process has exited? Where would I look for that? What could it be?
class DialTone(Thread):
def __init__(self, input_queue:Queue, device:int_or_str ) -> None:
"""Initialize the dialtone class
Args:
input_queue (Queue): A way to communicate with the thread
"""
super().__init__()
global _DIAL_TONE_FILE
self.input_queue = input_queue
sd.default.device = device
# Commit the dial-tone data to memory so we can start/end quickly
try:
self.data, self.fs = sf.read(_DIAL_TONE_FILE, always_2d=True)
except Exception as e:
logging.debug( type(e).__name__ + ":" + str(e) )
def run(self) -> None:
"""Play a dial tone in a continuous loop until we die
"""
while( True ):
cmd = self.input_queue.get(True)
if( cmd == DialToneComms.DT_START ):
logging.debug("DT: starting dial tone")
sd.play(self.data, self.fs, loop=True)
logging.debug(sd.get_status())
elif( cmd == DialToneComms.DT_END ):
logging.debug("DT: stopping dial tone")
try:
sd.stop(ignore_errors=False)
except Exception as e:
logging.error( type(e).__name__ + ":" + str(e) )
logging.debug(sd.get_status())
elif( cmd == DialToneComms.DT_KILL ):
logging.debug("DT: killing self")
try:
sd.stop(ignore_errors=False)
except Exception as e:
logging.error( type(e).__name__ + ":" + str(e) )
logging.debug(sd.get_status())
break
# Test this capability
if __name__ == "__main__":
parser = argparse.ArgumentParser()
parser.add_argument("--device",type=int_or_str,help="Audio device to use", default=1)
parser.add_argument("--play_time",type=float,help="how long to play the dial tone",default=10.0)
parser.add_argument("--num_cycles", type=int, help="Number of play, wait cycles", default=2)
args = parser.parse_args()
# Setup logging
logging.basicConfig(level=logging.DEBUG)
dial_tone_queue = Queue()
print( "Instantiating DialTone" )
dial_tone = DialTone(dial_tone_queue, args.device)
print( "Starting DialTone Thread" )
dial_tone.start()
# Play the dial tone a number of iterations
for _ in range(args.num_cycles):
print( "Playing dialtone for {} sec".format(args.play_time))
dial_tone_queue.put(DialToneComms.DT_START)
time.sleep(args.play_time)
dial_tone_queue.put(DialToneComms.DT_END)
print("Waiting 2 seconds")
time.sleep(2)
print("Kill the thread")
dial_tone_queue.put(DialToneComms.DT_KILL)
print("Joining DialTone")
dial_tone.join(_JOIN_WAIT_TIME)
if( dial_tone.is_alive() ):
logging.warning( "Dial tone thread is still alive! What gives?" )
else:
logging.warning( "Dial tone thread is nice and dead")
Output of first run on Pi. The error at the bottom is a result of me stopping a stream that's not playing (the stop call already killed it, and the kill call is just making sure).
Instantiating DialTone
Starting DialTone Thread
Playing dialtone for 3.0 sec
DEBUG:root:DT: starting dial tone
DEBUG:root:
Waiting 2 seconds
DEBUG:root:DT: stopping dial tone
DEBUG:root:
Playing dialtone for 3.0 sec
DEBUG:root:DT: starting dial tone
DEBUG:root:
Waiting 2 seconds
DEBUG:root:DT: stopping dial tone
DEBUG:root:
Playing dialtone for 3.0 sec
DEBUG:root:DT: starting dial tone
DEBUG:root:
Waiting 2 seconds
DEBUG:root:DT: stopping dial tone
DEBUG:root:
Kill the thread
Joining DialTone
DEBUG:root:DT: killing self
ERROR:root:PortAudioError:Error stopping stream: Invalid stream pointer [PaErrorCode -9988]
DEBUG:root:
WARNING:root:Dial tone thread is nice and dead
Output of 2nd Run on Pi. note that things start happening out of order, particularly after the stop, but audio never plays on the second time.
Instantiating DialTone
Starting DialTone Thread
Playing dialtone for 3.0 sec
DEBUG:root:DT: starting dial tone
DEBUG:root:
Waiting 2 seconds
DEBUG:root:DT: stopping dial tone
Playing dialtone for 3.0 sec
Waiting 2 seconds
Playing dialtone for 3.0 sec
Waiting 2 seconds
Kill the thread
Joining DialTone
DEBUG:root:
DEBUG:root:DT: starting dial tone
DEBUG:root:
DEBUG:root:DT: stopping dial tone
WARNING:root:Dial tone thread is still alive! What gives?
DEBUG:root:
DEBUG:root:DT: starting dial tone
DEBUG:root:
DEBUG:root:DT: stopping dial tone
DEBUG:root:
DEBUG:root:DT: killing self
ERROR:root:PortAudioError:Error stopping stream: Invalid stream pointer [PaErrorCode -9988]
DEBUG:root:
Output on Windows:
Instantiating DialTone
Starting DialTone Thread
Playing dialtone for 3.0 sec
DEBUG:root:DT: starting dial tone
DEBUG:root:
Waiting 2 seconds
DEBUG:root:DT: stopping dial tone
DEBUG:root:
Playing dialtone for 3.0 sec
DEBUG:root:DT: starting dial tone
DEBUG:root:
DEBUG:root:DT: stopping dial tone
Waiting 2 seconds
DEBUG:root:
Playing dialtone for 3.0 sec
DEBUG:root:DT: starting dial tone
DEBUG:root:
Waiting 2 seconds
DEBUG:root:DT: stopping dial tone
DEBUG:root:
Kill the thread
DEBUG:root:DT: killing self
Joining DialTone
ERROR:root:PortAudioError:Error stopping stream: Invalid stream pointer [PaErrorCode -9988]
DEBUG:root:
WARNING:root:Dial tone thread is nice and dead
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论