Прежде чем перейти к статье, хочу вам представить, экономическую онлайн игру Brave Knights, в которой вы можете играть и зарабатывать. Регистируйтесь, играйте и зарабатывайте!
Если ваше хобби/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.
Надеюсь эта информация была полезна.