MPD, FIFO, Python, Audioop, Arduino и вольтметр: имитация волюметра

Я пытаюсь использовать компьютер, подключенный к Arduino (который сам подключен к некоторым 5-вольтовым вольтметрам), чтобы «подделать» старый школьный стереометр VU. Моя цель состоит в том, чтобы компьютер, который воспроизводит аудиофайл, анализировал сигнал и отправлял информацию об амплитуде в Arudino через последовательное соединение для отображения на вольтметрах.

Я использую MPD для рендеринга и отправки звука на USB-ЦАП (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)

person chryses    schedule 13.02.2014    source источник
comment
Некоторая информация есть на bbs.archlinux.org/viewtopic.php?pid=1349329 относительно файлов mpd conf, а также каждый раз при повторном открытии FIFO, это может помочь.   -  person mtrw    schedule 19.02.2014
comment
Я думаю, что понял - независимо от того, сколько байтов я указал, os.read() возвращал 2048 байтов. Таким образом, это означает, что вторым параметром, который принимает os.read(), является максимальное количество байтов, которые он будет читать, но нет гарантии, что столько байтов будет фактически прочитано. . Я думал, что, опустив опцию NONBLOCK при открытии FIFO, вызов os.read() будет ждать, пока не получит конец файла или указанное количество байтов.   -  person chryses    schedule 20.02.2014
comment
Ха, интересно. Вы должны сделать свой комментарий ответом и принять его, чтобы другим, кто сталкивается с этим вопросом, не приходилось искать комментарии.   -  person mtrw    schedule 20.02.2014


Ответы (1)


Отвечая на мой собственный вопрос. Оказывается, независимо от того, сколько байтов я указал для чтения, os.read() возвращал 2048 байт. Это означает, что вторым параметром, который принимает os.read(), является максимальное число байтов, которые он будет считывать, но нет гарантии, что это количество байтов будет на самом деле читать. Я думал, что, опустив опцию NONBLOCK при открытии FIFO, вызов os.read() будет ждать, пока не получит конец файла или указанное количество байтов. Но это не так. Чтобы обойти эту проблему, мой код теперь проверяет длину строки байтов, возвращаемую os.read(), и, если эта длина меньше заданного размера фрагмента, будет ждать, чтобы захватить следующий фрагмент (ы), а затем будет конкатенировать все фрагменты вместе, чтобы у меня был размер фрагмента, соответствующий моей цели, прежде чем я перейду к обработке данных.

person chryses    schedule 20.02.2014