找回密码
 立即注册

QQ登录

只需一步,快速开始

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



216.jpg

有想过用一台主机给不同的设备同时播放不同的音频文件吗?如果你正准备搭建一个DJ应用,需要实现一副耳机和一组扬声器的混音效果;又或者你需要构建一组包含许多个扬声器的音乐系统,而每个扬声器都需要播放不同的音频文件,彼此之间需要实现同步……
下面这个 DEMO 演示了用树莓派驱动8个不同的扬声器,分别播放8种不同的单声道音频文件,不仅有视频,更有必要的说明:



下面是所用的的零部件清单。
  • 功放板
  • USB 声卡
  • DC/DC变压器
  • USB HUB
  • DC 接线端子
所需要的项目源代码在这里:
https://github.com/esologic/pear
参照图片连接树莓派、USB 声卡、功放、USB HUB、扬声器和电源。

217.jpg

218.jpg

219.jpg

220.jpg

Multi-Audio 范例
首先给树莓派安装 sounddevice,用下面的命令即可。

  1. sudo apt-get install python3-pip python3-numpy libportaudio2 libsndfile1

  2. python3 -m pip install sounddevice soundfile
复制代码

这里有4个音频文件,和 multi.py 在同一个目录下,目录结构如下。
multi_audio/
├──1.wav
├──2.wav
├──3.wav
├──4.wav
└──multi.py
这段代码基于Python 的 sounddevice 库。

  1. """

  2. multi.py, uses the sounddevice library to play multiple audio files to multiple output devices at the same time

  3. Written by Devon Bray (dev@esologic.com)

  4. """

  5. import sounddevice

  6. import soundfile

  7. import threading

  8. import os

  9. DATA_TYPE = "float32"

  10. def load_sound_file_into_memory(path):

  11.     """

  12.     Get the in-memory version of a given path to a wav file

  13.     :param path: wav file to be loaded

  14.     :return: audio_data, a 2D numpy array

  15.     """

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

  17.     return audio_data

  18. def get_device_number_if_usb_soundcard(index_info):

  19.     """

  20.     Given a device dict, return True if the device is one of our USB sound cards and False if otherwise

  21.     :param index_info: a device info dict from PyAudio.

  22.     :return: True if usb sound card, False if otherwise

  23.     """

  24.     index, info = index_info

  25.     if "USB Audio Device" in info["name"]:

  26.         return index

  27.     return False

  28. def play_wav_on_index(audio_data, stream_object):

  29.     """

  30.     Play an audio file given as the result of `load_sound_file_into_memory`

  31.     :param audio_data: A two-dimensional NumPy array

  32.     :param stream_object: a sounddevice.OutputStream object that will immediately start playing any data written to it.

  33.     :return: None, returns when the data has all been consumed

  34.     """

  35.     stream_object.write(audio_data)

  36. def create_running_output_stream(index):

  37.     """

  38.     Create an sounddevice.OutputStream that writes to the device specified by index that is ready to be written to.

  39.     You can immediately call `write` on this object with data and it will play on the device.

  40.     :param index: the device index of the audio device to write to

  41.     :return: a started sounddevice.OutputStream object ready to be written to

  42.     """

  43.     output = sounddevice.OutputStream(

  44.         device=index,

  45.         dtype=DATA_TYPE

  46.     )

  47.     output.start()

  48.     return output

  49. if __name__ == "__main__":

  50.     def good_filepath(path):

  51.         """

  52.         Macro for returning false if the file is not a non-hidden wav file

  53.         :param path: path to the file

  54.         :return: true if a non-hidden wav, false if not a wav or hidden

  55.         """

  56.         return str(path).endswith(".wav") and (not str(path).startswith("."))

  57.     cwd = os.getcwd()

  58.     sound_file_paths = [

  59.         os.path.join(cwd, path) for path in sorted(filter(lambda path: good_filepath(path), os.listdir(cwd)))

  60.     ]

  61.     print("Discovered the following .wav files:", sound_file_paths)

  62.     files = [load_sound_file_into_memory(path) for path in sound_file_paths]

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

  64.     usb_sound_card_indices = list(filter(lambda x: x is not False,

  65.                                          map(get_device_number_if_usb_soundcard,

  66.                                              [index_info for index_info in enumerate(sounddevice.query_devices())])))

  67.     print("Discovered the following usb sound devices", usb_sound_card_indices)

  68.     streams = [create_running_output_stream(index) for index in usb_sound_card_indices]

  69.     running = True

  70.     if not len(streams) > 0:

  71.         running = False

  72.         print("No audio devices found, stopping")

  73.     if not len(files) > 0:

  74.         running = False

  75.         print("No sound files found, stopping")

  76.     while running:

  77.         print("Playing files")

  78.         threads = [threading.Thread(target=play_wav_on_index, args=[file_path, stream])

  79.                    for file_path, stream in zip(files, streams)]

  80.         try:

  81.             for thread in threads:

  82.                 thread.start()

  83.             for thread, device_index in zip(threads, usb_sound_card_indices):

  84.                 print("Waiting for device", device_index, "to finish")

  85.                 thread.join()

  86.         except KeyboardInterrupt:

  87.             running = False

  88.             print("Stopping stream")

  89.             for stream in streams:

  90.                 stream.abort(ignore_errors=True)

  91.                 stream.close()

  92.             print("Streams stopped")



  93.     print("Bye.")

复制代码

运行之后将分别在3个设备上播放 1.wav、2.wav 和 3.wav音频文件。

221.jpg
分享至 : QQ空间
收藏

0 个回复

您需要登录后才可以回帖 登录 | 立即注册