看代码吧…
写代码写到半夜太累, 不想解说了, 直接上代码
# 获取与电脑连接的声卡设备详细信息
import sys
import sounddevice as sd
from python_audio_packages.sounddevice_example.my_exception_class import MyException
def get_pool_object():
# po 的名字在main函数中定义
# __main__ 模块在sys.modules 的键是"__mp_main__"
return sys.modules["__mp_main__"].po
def get_audio_devices_all_msg_dict():
# 使用sounddevice 获取电脑连接的声卡以及系统自带的所有音频驱动信息(驱动, 声道名, id)
audio_drivers_and_channels_msg_dict = {}
audio_input_channels_msg_dict = {}
audio_output_channels_msg_dict = {}
this_tmp_dict = {}
host_api_tuple = sd.query_hostapis()
for temp_dict in host_api_tuple:
this_tmp_dict[temp_dict["name"]] = temp_dict["devices"]
channels_list = sd.query_devices()
for driver_name in this_tmp_dict:
audio_drivers_and_channels_msg_dict[driver_name] = []
audio_input_channels_msg_dict[driver_name] = []
audio_output_channels_msg_dict[driver_name] = []
for id in this_tmp_dict[driver_name]:
audio_drivers_and_channels_msg_dict[driver_name].append((id, channels_list[id]["name"]))
if channels_list[id]["max_input_channels"] > 0:
audio_input_channels_msg_dict[driver_name].append((id, channels_list[id]['name']))
if channels_list[id]["max_output_channels"] > 0:
audio_output_channels_msg_dict[driver_name].append((id, channels_list[id]['name']))
return audio_drivers_and_channels_msg_dict, audio_input_channels_msg_dict, audio_output_channels_msg_dict
def get_device_id(driver_name, channel_name, kind, msg_dict):
# 获取精准的输入或者输出同道id
if kind == "input":
for temp_tuple in msg_dict[driver_name]:
if temp_tuple[1] == channel_name:
return temp_tuple[0]
else:
raise MyException("找不到此驱动---%s--的输入通道---%s" % (driver_name, channel_name))
elif kind == "output":
for temp_tuple in msg_dict[driver_name]:
if temp_tuple[1] == channel_name:
return temp_tuple[0]
else:
raise MyException("找不到此驱动---%s--的输出通道---%s" % (driver_name, channel_name))
else:
raise MyException("kind 不是 input, 也不是output !!!")
if __name__ == "__main__":
# print(sd.query_devices())
all_msg_dict, input_msg_dict, output_msg_dict = get_audio_devices_all_msg_dict()
driver_name = "Windows WDM-KS"
channel_name = "Speakers 2 (Realtek HD Audio output with SST)"
kind = "output"
device_id = get_device_id(driver_name, channel_name, kind, output_msg_dict)
print(device_id)
"""
0 Microsoft 声音映射器 - Input, MME (2 in, 0 out)
> 1 麦克风阵列 (Realtek High Defini, MME (2 in, 0 out)
2 Microsoft 声音映射器 - Output, MME (0 in, 2 out)
< 3 喇叭/耳机 (Realtek High Definit, MME (0 in, 2 out)
4 主声音捕获驱动程序, Windows DirectSound (2 in, 0 out)
5 麦克风阵列 (Realtek High Definition Audio(SST)), Windows DirectSound (2 in, 0 out)
6 主声音驱动程序, Windows DirectSound (0 in, 2 out)
7 喇叭/耳机 (Realtek High Definition Audio(SST)), Windows DirectSound (0 in, 2 out)
8 喇叭/耳机 (Realtek High Definition Audio(SST)), Windows WASAPI (0 in, 2 out)
9 麦克风阵列 (Realtek High Definition Audio(SST)), Windows WASAPI (2 in, 0 out)
10 Speakers 1 (Realtek HD Audio output with SST), Windows WDM-KS (0 in, 2 out)
11 Speakers 2 (Realtek HD Audio output with SST), Windows WDM-KS (0 in, 6 out)
12 电脑扬声器 (Realtek HD Audio output with SST), Windows WDM-KS (2 in, 0 out)
13 麦克风阵列 1 (), Windows WDM-KS (2 in, 0 out)
14 麦克风阵列 2 (), Windows WDM-KS (1 in, 0 out)
"""
以上模块是用来精确获取 某个声卡驱动下的 channel_name 的 device_id,
这个id是专门用来sounddevice用的
下面的代码是使用 进程池 来实现多个音频来并发播放, 随机使用电脑的不同的输出通道
"""
本模块用来测试使用python多进程(进程池), sounddevice播放音频文件
"""
import os
import random
import sys
from multiprocessing import Pool, freeze_support
import soundfile
import sounddevice as sd
from python_audio_packages.sounddevice_example.get_audio_channels_msg import get_device_id, \
get_audio_devices_all_msg_dict
from python_audio_packages.sounddevice_example.my_exception_class import MyException
def play_in_sounddevice(wav_data, sample_rate, *args):
print("index---%d---本音频---%s---文件的采样率为---%s\n\n" % (args[1], args[0].split("\\")[-1], sample_rate))
try:
sd.default.device[1] = args[2]
sd.play(wav_data, samplerate=sample_rate)
except Exception as e:
raise e
else:
sd.wait()
class TestSounddeviceMultiprocess(object):
def __init__(self, wav_file_dir):
self.wav_file_dir = wav_file_dir
self.wav_file_path_list = []
self.process_num = self.get_wav_file_path_list()
self.channel_list = [("MME", "Microsoft 声音映射器 - Output"),
("MME", "喇叭/耳机 (Realtek High Definit"),
("Windows DirectSound", "主声音驱动程序"),
("Windows DirectSound", "喇叭/耳机 (Realtek High Definition Audio(SST))"),
("Windows WASAPI", "喇叭/耳机 (Realtek High Definition Audio(SST))"),
("Windows WDM-KS", "Speakers 1 (Realtek HD Audio output with SST)"),
("Windows WDM-KS", "Speakers 2 (Realtek HD Audio output with SST)")]
self.all_msg_dict, self.input_msg_dict, self.output_msg_dict = get_audio_devices_all_msg_dict()
def get_wav_file_path_list(self):
if os.path.exists(self.wav_file_dir):
wav_file_list = os.listdir(self.wav_file_dir)
for file_name in wav_file_list:
if file_name.endswith(".wav"):
self.wav_file_path_list.append(os.path.join(self.wav_file_dir, file_name))
return len(self.wav_file_path_list)
else:
raise MyException("Please make sure your input is a dir path !!!")
def read_file_soundfile(self, wave_file_path):
try:
data, sample_rate = soundfile.read(wave_file_path)
except Exception as e:
print(str(e))
return 0, 0
else:
return data, sample_rate
def __get_device_id(self, index=None, wav_file=None):
rand_int = random.randint(0, len(self.channel_list) - 1)
device_id = get_device_id(self.channel_list[rand_int][0], self.channel_list[rand_int][1],
"output", self.output_msg_dict)
print("index--{}--{}--声卡--{}--{}--device_id--{}".format(index,
wav_file.split("\\")[-1],
self.channel_list[rand_int][0],
self.channel_list[rand_int][1],
device_id))
return device_id
def run_play_multiprocess(self):
for index, wav_file_path in enumerate(self.wav_file_path_list):
data, sample_rate = self.read_file_soundfile(wav_file_path)
if sample_rate:
device_id = self.__get_device_id(index, wav_file_path)
# 进程池加进的函数, 不能是类中的, 必须是类外的, 否则会报错
po.apply_async(func=play_in_sounddevice, args=(data, sample_rate, wav_file_path,
index, device_id, device_id))
po.close()
po.join()
if __name__ == "__main__":
t = TestSounddeviceMultiprocess(r"F:\sounddevice_test_wav")
process_num = t.process_num
po = Pool(process_num) # 注意, 进程池创建, 只能在 if __name__ == "__main__": 之下
# 而且, 进程池的创建不能在类里面
freeze_support()
t.run_play_multiprocess()