2

我正在尝试使用连接到 Arduino(它本身连接到一些 5V 电压表)的计算机来“伪造”一个老式的立体声 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)
4

1 回答 1

2

回答我自己的问题。事实证明,无论我指定应该读取多少字节,os.read() 都返回 2048 个字节。这意味着 os.read() 采用的第二个参数是它将读取的最大字节数 - 但不能保证实际上会有这么多字节被阅读。我曾认为,当我打开 FIFO 时省略 NONBLOCK 选项,os.read() 调用会一直等待,直到它收到文件结尾或指定的字节数。但事实并非如此。为了解决这个问题,我的代码现在检查 os.read() 返回的字节字符串的长度,并且 - 如果该长度小于我指定的块大小 - 将等待抓取下一个块,然后将连接将所有块放在一起,以便在继续处理数据之前获得与目标匹配的块大小。

于 2014-02-20T19:15:12.233 回答