您的位置:首页 > 编程语言 > Python开发

树莓派用 Python 在多个输出设备上播放多个声音文件

2019-03-10 17:58 555 查看
版权声明:此乃个人学习笔记使用,请勿转载 https://blog.csdn.net/qq_25205045/article/details/88322362

准备工作
功放板:https://www.amazon.com/gp/product/B07J4P4FR9
USB 声卡:https://item.taobao.com/item.htm?id=577563502441
DC/DC变压器:https://www.amazon.com/gp/product/B01GJ0SC2C
USB HUB:https://www.amazon.com/gp/product/B076YPRTKF/
DC 接线端子:https://www.amazon.com/gp/product/B06XRT5C2Q
**添加声音设备
声音文件
threading模块(每一个对象代表一个线程 https://blog.csdn.net/goldxwang/article/details/77838072)
argparse模块(argparse是python用于解析命令行参数和选项的标准模块 https://www.2cto.com/kf/201412/363654.html
https://yq.aliyun.com/articles/567308)
pathlib.py模块 (内置pathlib库的常用属性和方法 https://www.cnblogs.com/sigai/p/8074329.html)

argparse 用于将命令行字符串解析为Python对象的对象 添加关注的命令行参数和选项 开始解析
pathlib 检测文件的路径 是否隐藏
确定声音文件路径
文件加载到内存 寻找usb设备
寻找到的usb设备文件的信息
为usb声音设备创建输出索引

检测输出索引是否创建
检测文件是否存在
threading 创建线程 判断线程是否进入
键盘中断模块启动 退出

wait_devices_init.py 设备等待准备
使用对声音设备的外部调用来获取库检测到的设备数量。**

pear.py

"""
"""

import sounddevice
import soundfile
import threading
import argparse
import pathlib
import os

DATA_TYPE = "float32"

def load_sound_file_into_memory(path):
"""

获取到wav文件的给定路径的内存版本
:参数路径:要加载的wav文件
:返回:audio_data,一个2D numpy数组
"""

audio_data, _ = soundfile.read(path, dtype=DATA_TYPE)
return audio_data

def dir_path(path):
"""
检查给定的路径是否实际上是一个目录
:参数路径:指向目录的路径
:return: path(如果是目录),如果不是,则引发错误
"""

p = pathlib.Path(path)
if p.is_dir():
return path
else:
raise NotADirectoryError(path)

def get_device_number_if_usb_soundcard(index_info):
"""
给定一个设备dict,如果该设备是我们的USB声卡之一,则返回True,否则返回False
:param index_info:来自PyAudio的设备信息dict。
:返回:usb声卡为真,否则为假
"""

index, info = index_info

if "USB Audio Device" in info["name"]:
return index
return False

def play_wav_on_index(audio_data, stream_object):
"""
播放一个名为load_sound_file_into_memory的音频文件
:param audio_data:一个二维数字数组
:param stream_object:声音设备。对象,该对象将立即开始播放写入其中的任何数据。
:return: None,当所有数据都被使用时返回
"""

stream_object.write(audio_data)

def create_running_output_stream(index):
"""
创建一个sounddevice。向准备写入的索引指定的设备写入的OutputStream。
你可以立即调用'写'对这个对象与数据,它将发挥在设备上。
:param索引:要写入的音频设备的设备索引
:返回:已启动的声音设备。准备写入的OutputStream对象
"""

output = sounddevice.OutputStream(
device=index,
dtype=DATA_TYPE
)

output.start()
return output

if __name__ == "__main__":

parser = argparse.ArgumentParser(description='a simple tool for sound installations') #用于将命令行字符串解析为Python对象的对象
parser.add_argument("dir", type=dir_path)#添加关注的命令行参数和选项
args = parser.parse_args() #开始解析

def good_filepath(path):   #检测文件的路径  是否隐藏
"""
Macro for returning false if the file is not a non-hidden wav file
:param path: path to the file
:return: true if a non-hidden wav, false if not a wav or hidden
"""
return str(path).endswith(".wav") and (not str(path).startswith("."))

sound_file_paths = [os.path.join(args.dir, path) for path in sorted(filter(lambda path: good_filepath(path),
os.listdir(args.dir)))]    #确定声音文件路径

print("Discovered the following .wav files:", sound_file_paths) #打印声音文件路径

files = [load_sound_file_into_memory(path) for path in sound_file_paths]   #文件加载到内存  寻找usb设备

print("Files loaded into memory, Looking for USB devices.")

usb_sound_card_indices = list(filter(lambda x: x is not False,
map(get_device_number_if_usb_soundcard,
[index_info for index_info in enumerate(sounddevice.query_devices())])))#寻找到的usb设备文件的信息

print("Discovered the following usb sound devices", usb_sound_card_indices)#打印寻找到的usb文件设备文件信息

streams = [create_running_output_stream(index) for index in usb_sound_card_indices]#为usb声音设备创建输出索引

running = True

if not len(streams) > 0:   #检测输出索引是否创建
running = False
print("No audio devices found, stopping")

if not len(files) > 0:    #检测文件是否存在
running = False
print("No sound files found, stopping")

while running:

print("Playing files")

threads = [threading.Thread(target=play_wav_on_index, args=[file_path, stream])
for file_path, stream in zip(files, streams)]   #创建线程

try:

for thread in threads:   #在线程中  开始
thread.start()

for thread, device_index in zip(threads, usb_sound_card_indices):
print("Waiting for device", device_index, "to finish")
thread.join()#提示阻塞,等待设备完成

except KeyboardInterrupt:#键盘中断模块启动  停止程序
running = False
print("Stopping stream")
for stream in streams:
stream.abort(ignore_errors=True)
stream.close()
print("Streams stopped")

print("Bye.")

wait_devices_init.py 设备等待准备

"""
Blocks until the sounddevice module is able to pick up all of the attached usb sound cards as seen by lsusb
块,直到声音设备模块能够接收到lsusb所看到的所有附加的usb声卡
"""

import subprocess

def num_sound_devices():
"""
使用对声音设备的外部调用来获取库检测到的设备数量。
这样做很重要,而不仅仅是sounddevice.query_devices(),声音设备询问
返回:
"""

return str(subprocess.check_output(["python3", "-m", "sounddevice"])).count("USB Audio Device")

def wait_for_usb_sound_devices_to_be_initialized():
"""
调用此函数将阻塞,直到lsusb检测到的USB声音声卡设备的数量匹配为止
声音设备后端初始化的数字。
返回:
"""

while num_sound_devices() != str(subprocess.check_output(["lsusb"])).count("C-Media Electronics"):
pass

if __name__ == "__main__":
wait_for_usb_sound_devices_to_be_initialized()  #等待usb声音设备初始化

参考:http://shumeipai.nxez.com/2019/02/21/play-multiple-sound-files-on-multiple-output-devices.html

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: