我使用的是ESP8266 NodeMCU 12-E开发板从预放大驻极体麦克风捕捉音频,然后我把它上传到网上的地方将被转换为WAV文件。我首先想到的是投在ESP8266 analogRead(A0)
的整数值作为String
类型,然后把它们连接成一个更长的字符串有效载荷,我可以发布到MQTT经纪人。
我的MQTT客户端的用户似乎并没有得到好正确的声音文件,因为我听到的是一系列有节奏的持久性有机污染物。
我决定去调查,如果我在ESP8266板代码甚至被捕捉的东西正确。我扯下了代码到这似乎导致问题的这几行:
#include <ESP8266WiFi.h>
const char *ssid = "____"; // Change it
const char *pass = "____"; // Change it
void setup()
{
Serial.begin(115200);
Serial.println(0); //start
WiFi.mode(WIFI_STA);
WiFi.begin(ssid, pass);
}
void loop()
{
int analog = analogRead(A0);
if (analog > 255) {
analog = 255;
}
else if (analog < 0){
analog = 0;
}
Serial.print(String(analog));
Serial.print(" ");
}
下面是我如何使用上面的代码产生一个wav文件,以检查是否是什么声音我期望:
- I start up the ESP8266 development board
- I turn on the Serial Monitor and clear all previous output
- I power up my electret microphone and speak into it
- I power down my electret microphone
- I copy the contents of the Serial Monitor (which is a series of integers) into a text file called `audio.raw`
- I copy `audio.raw` to a linux machine that has ffmpeg installed
- I issue the command `ffmpeg -f u8 -ar 11111 -ac 1 -i audio.raw -y audio.wav` on the linux machine
当我听audio.raw文件,我听到我的声音,但速度可能比正常快5-10倍。 (我也得到了很多的噪声和失真,但可能是与输入信号质量一个单独的问题。)
然后我试图改变这一行代码Serial.print(String(analog))
到Serial.print(analog)
。然后我重复上述步骤。但是这一次,我的声音听起来像在比正常快约2倍。
为什么改变从Serial.print(String(analog))
这一行Serial.print(analog)
做出如此大的差别?
是不是因为String()
功能是一个非常昂贵的操作,占用了大量的时间?而当脚本需要更多的时间来处理每一行代码,脚本则具有更短的时间捕捉到足够的analogRead(A0)
数据点?如果我运行使用全部相同的标志相同ffmpeg
命令,然后ffmpeg的会尽量满足通过加快音频播放的-ar 11111
要求?这将意味着,我的采样率取决于我的脚本的执行速度?这意味着我必须要考虑在制造精度,环境温度等跨相同型号的其他板由于变异变量执行速度...?
你的采样速率连接到您的循环实现(如你发现)。这在你的采样率也会导致抖动不同的代码路径将采取不同的时间和中断服务程序也将窃取的CPU周期。
这种抖动会的失真在输出的原因之一。
当我听audio.raw文件,我听到我的声音,但速度可能比正常快5-10倍。
所述ESP8266具有硬件UART因此代码可以潜在地加载UART的FIFO缓冲器比它可以输出快。这将是所感知的更快的采样率的源极而且还导致抖动或数据丢失时,缓冲器填满。取决于实施方式,当缓冲器填满它将丢弃数据或替代地块(引起抖动)。
为什么改变从Serial.print(字符串(模拟))到Serial.print(模拟)这一行做这么大的差别?
难道是因为String()函数是一个非常昂贵的操作,占用了大量的时间?而当脚本需要更多的时间来处理每一行代码,脚本则具有更短的时间捕捉到足够analogRead(A0)的数据点?
是的,是的,是的。
其中一个原因表现不同的是,String()
包括分配和堆管理内存来存储字符。
Serial.print(analog)
使用一个固定的缓冲器大小的堆栈上作为代码知道要显示一个int所需的字符的最大数目。
如果我运行使用全部相同的标志相同的ffmpeg的命令,然后ffmpeg的会尽量满足通过加快音频播放的-ar 11111要求?
是。 FFMPEG假定采样具有固定采样率,但这种不匹配正被打印出来的样品。
这将意味着,我的采样率取决于我的脚本的执行速度?
是!
这意味着我必须要考虑在制造精度,环境温度等跨相同型号的其他板由于变异变量执行速度...?
是。会有影响执行速度的变量,众说纷纭。
从代码执行解耦数据的采样。
这可以通过实施Interrupt Service Routine来完成。扎ISR到硬件计时器,以便它执行在一个固定的采样率,避免抖动。
该ISR可以写入这loop()
代码发送在串行连接的缓冲器。在ISR和串行传输的代码需要管理的缓冲区,以保证既不超支等。这样做的一种方法是使用备用缓冲区,所述ISR和传输的代码使用。
由于您使用Serial.begin(115200)ESP8266微控制器将通过串行端口传输每秒115200位。这是每秒八分之十一万五千二百= 14400个字节,这意味着由于使用U8(无符号的8位)格式的音频,每个样品由单个字节的。只是ffmpeg的-ar参数更改为14400。
我没有任何有我可以连接到MCU进行测试麦克风,但它应能正常工作这种方式。其他-ac参数是正确的,因为它是单声道音频。
编辑:另外在打印出来的串行不要用String()构造函数。
当使用串行()构造声音加快约5倍,因为字符串您的1个字节值转换为3个字节,例如;字节:255 - >字符串:“2”,“5”,“5”,则不必考虑微控制器的执行速度,这将输出每秒115200个比特作为如果定义。你只需要考虑它的输出。
最后删除的行
Serial.print(”“);
也改变
INT模拟= analogRead(A0);
至
字节模拟=(字节)analogRead(A0);
因为INT由4个字节,你不会想额外的3个字节的发送到串行。
并改变INT后字节你可以摆脱这个代码块
if (analog > 255) {
analog = 255;
}
else if (analog < 0){
analog = 0;
}
如果通过USB连接到ESP8266 Linux设备上它具有的ffmpeg可以使用
ttylog -b 115 200 -d的/ dev / ttyUSB0 | FFMPEG -f U8 -at 14400 -and 1 -i - -The audio.wav
从ESP8266捕获实时音频数据。