Skip to main content
 首页 » 编程设计

python之MPD、FIFO、Python、Audioop、Arduino 和电压表 : "Faking" a VU Meter

2024年11月01日3yyy_WW

我正在尝试使用连接到 Arduino(它本身连接到一些 5V 电压表)的计算机来“伪造”一个老式立体声 VU 表。我的目标是让正在播放音频文件的计算机分析信号并通过串行连接将振幅信息发送到 Arudino,以显示在电压表上。

我正在使用 MPD 渲染音频并将其发送到 USB DAC (ODAC)。 MPD 也输出到 FIFO,我使用 Python 脚本读取它。我以 4096 字节 block 的形式从 FIFO 中读取,然后使用 audioop 库将该 block /样本拆分为左右 channel 并计算每个 channel 的最大幅度。

这就是问题所在 - 我被数据淹没了。我猜我的数学有误,或者我不明白 FIFO 是如何工作的(或者两者都有)。 MPD 以 44100:16:2 格式输出所有内容——我认为这意味着它将每秒写出 44,100 个 4 字节样本。因此,如果我正在抓取 4096 字节的 block ,我应该期望每秒大约 43 个 block 。但我得到的远不止于此(超过 100 个),而且如果我增加 block 大小,我每秒获得的 block 数也不会改变。例如,如果我将 block 大小加倍到 8192,我仍然可以每秒获得大致相同的 block 数。很明显我做错了什么,但我不知道那是什么。有人有什么想法吗?

这是我的 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) 

请您参考如下方法:

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