准备工作
功放板: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 设备等待准备
使用对声音设备的外部调用来获取库检测到的设备数量。**
"""
"""
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.")
"""
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