Python 在类中使用进程池 通过sounddevice, 进行不同声卡驱动和通道的音频文件播放

看代码吧…
写代码写到半夜太累, 不想解说了, 直接上代码

# 获取与电脑连接的声卡设备详细信息

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()

你可能感兴趣的:(sounddevice篇)