MPD, FIFO, Python, Audioop, Arduino и вольтметр: "Подделка" VU Meter
Я пытаюсь использовать компьютер, подключенный к Arduino (который сам подключен к некоторым 5-вольтовым вольтметрам), чтобы "подделать" стерео VU-метр старой школы. Моя цель - заставить компьютер, который воспроизводит аудиофайл, анализировать сигнал и отправлять информацию об амплитуде в Arudino через последовательное соединение для отображения на вольтметрах.
Я использую MPD для рендеринга и отправки звука на USB DAC (ODAC). MPD также выводит в FIFO, который я прочитал из скрипта Python. Я читаю из FIFO в 4096 байтных чанках, затем использую библиотеку audioop, чтобы разбить этот чанк / семпл на левый и правый канал и вычислить максимальную амплитуду каждого канала.
Вот в чем проблема - я завален данными. Я предполагаю, что моя математика неверна или что я не понимаю, как работает FIFO (или, может быть, оба). MPD выводит все в формате 44100:16:2 - я думал, что это означает, что он будет записывать 44 100 4-байтовых выборок в секунду. Так что, если я получаю 4096 байт, мне следует ожидать около 43 блоков в секунду. Но я получаю гораздо больше (более 100), и количество кусков, которые я получаю в секунду, не меняется, если я увеличиваю свой размер. Например, если я удвою свой размер чанка до 8192, я все равно получу примерно такое же количество чанков в секунду. Ясно, что я делаю что-то не так, но я не знаю, что это. У кого-нибудь есть мысли?
Вот соответствующая часть моего файла mpd.conf:
audio_output {
type "fifo"
name "my_fifo"
path "/tmp/mpd.fifo"
format "44100:16:2"
}
А вот и скрипт Python:
import os
import audioop
import time
import errno
import math
#Open the FIFO that MPD has created for us
#This represents the sample (44100:16:2) that MPD is currently "playing"
fifo = os.open('/tmp/mpd.fifo', os.O_RDONLY)
while 1:
try:
rawStream = os.read(fifo, 4096)
except OSError as err:
if err.errno == errno.EAGAIN or err.errno == errno.EWOULDBLOCK:
rawStream = None
else:
raise
if rawStream:
leftChannel = audioop.tomono(rawStream, 2, 1, 0)
rightChannel = audioop.tomono(rawStream, 2, 0, 1)
stereoPeak = audioop.max(rawStream, 2)
leftPeak = audioop.max(leftChannel, 2)
rightPeak = audioop.max(rightChannel, 2)
leftDB = 20 * math.log10(leftPeak) -74
rightDB = 20 * math.log10(rightPeak) -74
print(rightPeak, leftPeak, rightDB, leftDB)
1 ответ
Отвечая на мой собственный вопрос. Оказывается, что независимо от того, сколько байтов я указал, нужно прочитать, os.read() возвращал 2048 байтов. Это означает, что второй параметр, который принимает os.read () - это максимальное количество байтов, которые он будет читать, но нет гарантии, что многие байты будут действительно прочитаны. Я думал, что, пропустив опцию NONBLOCK при открытии FIFO, вызов os.read () будет ждать, пока не будет получен конец файла или указанное количество байтов. Но это не так. Чтобы обойти эту проблему, мой код теперь проверяет длину байтовой строки, возвращаемой os.read (), и - если эта длина меньше моего указанного размера чанка - будет ждать, чтобы захватить следующий чанк (и), а затем объединится все куски вместе, так что у меня есть размер куска, который соответствует моей цели, прежде чем я перейду к обработке данных.