Если ваше хобби/DIY, как и моё, связано с компьютером, то на каком то этапе вам захочется использовать звук. Предлагаю поговорить о звуке и обменяться опытом. Конкретно говорить будем, про запись и воспроизведение звука на компьютере. Возьмем компьютер под управлением Linux, но и под Windows должно работать. Язык для программирования предпочитаю Python. Как известно в Linux есть ALSA и для нее есть python библиотека pyalsaaudio. В каком то проекте голосового помощника я видел ее использовали для регулировки громкости. Про громкость поговорим потом. Т.к. библиотека не является системонезависимой поэтому возьмем другую хорошо известную - sounddevice. Библиотека основано на кросс платформенной C/C++ библиотеке portaudio.
Необходимый минимум для использования sounddevice есть в описании и он весьма прост:
import sounddevice as sd sd.play(myarray, fs)
здесь myarray — массив со звуком, надо сказать, что sounddevice поддерживает массивы numpy. fs — битрейт записи или частота дискретизации. Откуда это все брать? Нужна еще библиотека, например soundfile.
import sounddevice as sd import soundfile as sf myarray, fs = sf.read('my-file.wav') sd.play(myarray, fs) sd.wait() # ждем конца воспроизведения
Можно создать свой сигнал, например синус, таким образом для каких то нужд можно получить генератор любой звуковой частоты:
duration=1 длительность сигнала сек frequency = 1000 # частота Гц samplerate = 24000 # битрейт amp = 10000 # амплитуда ############# #берем numpy import numpy as np t = np.arange(duration * samplerate) / samplerate signal = amp*np.sin(2 * np.pi * frequency * t) sd.play(signal) sd.wait()
Естественно выбор не ограничен синусом. Не намного сложнее запись:
import sounddevice as sd import soundfile as sf fs = 48000 # битрейт записи duration=3 # длительность записи rec = sd.rec(int(duration * fs), samplerate=fs, channels=1, blocking=True) sd.wait() sf.write('my-file.wav', rec, fs)
Есть еще функция для одновременной записи и воспроизведя. Какие то подробности можно посмотреть в описании. Но это на мой взгляд мало интересно, т.к. недостаточно гибкости… Переходим к потокам. Про потоки в описании тоже ест и даже с примерами, поэтому отмечу, на мой взгляд, интересные моменты. Создаем поток:
stream = sd.Stream(device=(dev_in, dev_out), samplerate=41000, blocksize=blocksize, dtype="int16", channels=(1,2), callback=callback_fun)
Данный тип потока поддерживает и запись и воспроизведение. В библиотеке есть и раздельные, но этот мне показался наиболее интересным. В параметре device, поэтому содержится указание на на эти устройства. Где же их брать? Воспользуемся функцией sd.query_devices():
device_info = sd.query_devices()
в результате получим список с доступными звуковыми устройствами. Например такой:
0 sof-hda-dsp: - (hw:0,0), ALSA (2 in, 0 out) 1 sof-hda-dsp: - (hw:0,3), ALSA (0 in, 2 out) 2 sof-hda-dsp: - (hw:0,4), ALSA (0 in, 2 out) 3 sof-hda-dsp: - (hw:0,5), ALSA (0 in, 2 out) 4 sof-hda-dsp: - (hw:0,6), ALSA (4 in, 0 out) 5 sof-hda-dsp: - (hw:0,7), ALSA (4 in, 0 out) 6 sysdefault, ALSA (128 in, 0 out) 7 samplerate, ALSA (128 in, 0 out) 8 speexrate, ALSA (128 in, 0 out) 9 pulse, ALSA (32 in, 32 out) 10 upmix, ALSA (8 in, 0 out) 11 vdownmix, ALSA (6 in, 0 out) 12 default, ALSA (32 in, 32 out)
в параметр device можно записать или номер или имя, например device = (12,12). Я пробовал в параметр вписать номер от микрофона USB камеры, программа работает через раз — вылетает ошибка с указанием на portaudio, как я понял такая ошибка не только у меня. C default все работает. Так можно получить имя устройства:
dev_in = sd.query_devices(kind="input")["name"] dev_out = sd.query_devices(kind="output")["name"]
Для работы библиотека предлагает, и это удобно, использовать callback функцию.
def callback_fun(indata, outdata, frames, time, status): if status: print(status) outdata[:] = indata
Она вызывается функцией потока. Здесь, в функции видно, что входной сигнал подается прямо на выход. Например сигнал с микрофона можно сразу услышать. Или если кто то делает голосового ассистента, то сигнал с микрофона надо направить на распознавание голоса, а сигнал с синтезатора голоса на выход. В этом случае удобно использовать очереди — queue. Для примера, как проиграть WAV фай с диска? Для этого можно использовать такие функции:
import queue fifo = queue.Queue() inp_fifo = queue.Queue() ############# def play(file): with sf.SoundFile(file,mode="r") as f: data = f.read(blocksize,dtype='int16') while len(data): if not fifo.full(): data = f.read(blocksize,dtype='int16') fifo.put(data) ################# def callback_fun(indata, outdata, frames, time, status): inp_fifo.put(bytes(indata.copy())) # для обработки сигнала с микрофона if status: print(status) inp = np.zeros(len(outdata), dtype=np.int16) if not fifo.empty() : bf = np.frombuffer(fifo.get(), dtype=np.int16) inp[:bf.shape[0]] = bf # outdata[:] = inp.reshape(-1,1)
Отмечу, если помните, при создании потока был параметр channels=(1,2), который значит, что на вход у нас 1 — моно сигнал, а на выход 2 — стерео. Если вы подаете на выход моно сигнал в поток, то он автоматически разведется на два канала.
Пойдем далее. Возможно, что кто то уже догадался у нас массивы — вот он шанс сделать регулировку громкости, т. к. в sounddevice таких специальных функций нет.
Заводим переменную, например vol = 1, а далее магия циф в строке:
inp[:bf.shape[0]] = bf // vol
нам остается только правильно менять vol. Если взять vol = 1000, то можно практически
занулить звук. Можно пойти дальше… Если у нас есть, например, две входные очереди мы можем смешать звук от двух источников:
# см. В callback_fun if not fifo1.empty() : bf1 = np.frombuffer(fifo1.get(), dtype=np.int16) inp1[:bf1.shape[0]] = bf1 # if not fifo2.empty() : bf2= np.frombuffer(fifo2.get(), dtype=np.int16) inp2[:bf2.shape[0]] = bf2 # inp = (inp1 + inp2)//2 outdata[:] = inp.reshape(-1,1)
Или один сигнал в левое уха, а другой в правое:
# см. В callback_fun inp = np.column_stack((inp1,inp2)) outdata[:] = inp
На мой взгляд тут широкое поле для творчества. Если кто то захочет повторить мои эксперименты смотрите внимательно, я не профессиональный программист, могут быть ошибки. Ссылка на документацию sounddevice.
Надеюсь эта информация была полезна.
