嵌入式音频USB Audio调试2
本文介绍了音频回环测试和立体声测试的实现方法。回环测试通过ALSA库实现边录音边播放功能,使用24位3字节采样格式,采样率48kHz。测试程序通过交叉编译部署到目标板,并配置ALSA环境变量。立体声测试扩展了功能,允许用户通过输入'l'或'r'键将麦克风输入切换到左或右声道输出。两个测试都包含设备初始化、参数配置和音频数据处理循环,使用snd_pcm接口实现实时音频采集和播放功能。测试结果表明系统
·
前面一个章节讲解了音频驱动,音频库的编译,以及音频驱动和功能的测试工作,下面进一步进行业务的测试:
回环测试: 边收音边播放
立体声测试: 收音合成 然后播放
回环测试
1. 编写代码(AI编写稍作修改)
#include <iostream>
#include <alsa/asoundlib.h>
#define SAMPLE_RATE 48000
#define CHANNELS 1
#define PERIOD_FRAMES 480 // 10ms = 48000 * 0.01
#define BYTES_PER_SAMPLE 3 // S24_3LE = 3 bytes
int main()
{
snd_pcm_t* capture_handle;
snd_pcm_t* playback_handle;
snd_pcm_hw_params_t* hw_params;
int err;
const char* device = "plughw:0,0"; // <------ 修改过的设备名
// ============================
// Open capture device
// ============================
if ((err = snd_pcm_open(&capture_handle, device, SND_PCM_STREAM_CAPTURE, 0)) < 0) {
std::cerr << "Cannot open capture device: " << snd_strerror(err) << std::endl;
return 1;
}
snd_pcm_hw_params_alloca(&hw_params);
snd_pcm_hw_params_any(capture_handle, hw_params);
snd_pcm_hw_params_set_access(capture_handle, hw_params, SND_PCM_ACCESS_RW_INTERLEAVED);
snd_pcm_hw_params_set_format(capture_handle, hw_params, SND_PCM_FORMAT_S24_3LE);
snd_pcm_hw_params_set_channels(capture_handle, hw_params, CHANNELS);
snd_pcm_hw_params_set_rate(capture_handle, hw_params, SAMPLE_RATE, 0);
snd_pcm_hw_params_set_period_size(capture_handle, hw_params, PERIOD_FRAMES, 0);
snd_pcm_hw_params(capture_handle, hw_params);
snd_pcm_prepare(capture_handle);
// ============================
// Open playback device
// ============================
if ((err = snd_pcm_open(&playback_handle, device, SND_PCM_STREAM_PLAYBACK, 0)) < 0) {
std::cerr << "Cannot open playback device: " << snd_strerror(err) << std::endl;
return 1;
}
snd_pcm_hw_params_any(playback_handle, hw_params);
snd_pcm_hw_params_set_access(playback_handle, hw_params, SND_PCM_ACCESS_RW_INTERLEAVED);
snd_pcm_hw_params_set_format(playback_handle, hw_params, SND_PCM_FORMAT_S24_3LE);
snd_pcm_hw_params_set_channels(playback_handle, hw_params, CHANNELS);
snd_pcm_hw_params_set_rate(playback_handle, hw_params, SAMPLE_RATE, 0);
snd_pcm_hw_params_set_period_size(playback_handle, hw_params, PERIOD_FRAMES, 0);
snd_pcm_hw_params(playback_handle, hw_params);
snd_pcm_prepare(playback_handle);
// ============================
// Buffer = PERIOD_FRAMES * CHANNELS * 3 bytes
// ============================
const int buffer_size = PERIOD_FRAMES * CHANNELS * BYTES_PER_SAMPLE;
uint8_t buffer[buffer_size];
std::cout << "Start loopback (S24_3LE, 48kHz)... Speak." << std::endl;
// ============================
// Loop: capture -> playback
// ============================
while (true)
{
// Capture
err = snd_pcm_readi(capture_handle, buffer, PERIOD_FRAMES);
if (err < 0) {
snd_pcm_prepare(capture_handle);
continue;
}
// Playback
err = snd_pcm_writei(playback_handle, buffer, PERIOD_FRAMES);
if (err < 0) {
snd_pcm_prepare(playback_handle);
continue;
}
}
snd_pcm_close(capture_handle);
snd_pcm_close(playback_handle);
return 0;
}
2. 编译
aarch64-linux-gnu-g++ loopback_24bit.cpp -o loopback_24bit -I./alsa-lib/install/include -L./alsa-lib/install/lib -lasound
3. 导出环境变量
将程序以及alsa install下交叉编译出来的东西放置到板端,然后导出库路径以及配置文件路径
export ALSA_CONFIG_PATH=/root/install/share/alsa/alsa.conf
export LD_LIBRARY_PATH=/root/install/lib:$LD_LIBRARY_PATH
4. 测试
./loopback_24bit
至此,可以边说话边听到自己说话的声音了。
立体声测试
1. 编写代码
#include <iostream>
#include <thread>
#include <atomic>
#include <alsa/asoundlib.h>
#define SAMPLE_RATE 48000
#define IN_CHANNELS 1
#define OUT_CHANNELS 2
#define PERIOD_FRAMES 480
#define BYTES_PER_SAMPLE 3 // S24_3LE packed
std::atomic<int> mode(0);
// 0 = mic->left, 1 = mic->right
void input_thread() {
while (true) {
char c;
std::cin >> c;
if (c == 'l') {
mode = 0;
std::cout << "Switched: mic -> LEFT channel" << std::endl;
} else if (c == 'r') {
mode = 1;
std::cout << "Switched: mic -> RIGHT channel" << std::endl;
}
}
}
int main()
{
snd_pcm_t* cap;
snd_pcm_t* play;
snd_pcm_hw_params_t* hw;
int err;
const char* device = "plughw:0,0";
// ==== Open capture ====
if ((err = snd_pcm_open(&cap, device, SND_PCM_STREAM_CAPTURE, 0)) < 0) {
std::cerr << "Cannot open capture device: " << snd_strerror(err) << std::endl;
return 1;
}
snd_pcm_hw_params_alloca(&hw);
snd_pcm_hw_params_any(cap, hw);
snd_pcm_hw_params_set_access(cap, hw, SND_PCM_ACCESS_RW_INTERLEAVED);
snd_pcm_hw_params_set_format(cap, hw, SND_PCM_FORMAT_S24_3LE);
snd_pcm_hw_params_set_channels(cap, hw, IN_CHANNELS);
snd_pcm_hw_params_set_rate(cap, hw, SAMPLE_RATE, 0);
snd_pcm_hw_params_set_period_size(cap, hw, PERIOD_FRAMES, 0);
snd_pcm_hw_params(cap, hw);
snd_pcm_prepare(cap);
// ==== Open playback ====
if ((err = snd_pcm_open(&play, device, SND_PCM_STREAM_PLAYBACK, 0)) < 0) {
std::cerr << "Cannot open playback device: " << snd_strerror(err) << std::endl;
return 1;
}
snd_pcm_hw_params_any(play, hw);
snd_pcm_hw_params_set_access(play, hw, SND_PCM_ACCESS_RW_INTERLEAVED);
snd_pcm_hw_params_set_format(play, hw, SND_PCM_FORMAT_S24_3LE);
snd_pcm_hw_params_set_channels(play, hw, OUT_CHANNELS);
snd_pcm_hw_params_set_rate(play, hw, SAMPLE_RATE, 0);
snd_pcm_hw_params_set_period_size(play, hw, PERIOD_FRAMES, 0);
snd_pcm_hw_params(play, hw);
snd_pcm_prepare(play);
// ==== Buffers ====
const int in_bytes = PERIOD_FRAMES * IN_CHANNELS * BYTES_PER_SAMPLE;
const int out_bytes = PERIOD_FRAMES * OUT_CHANNELS * BYTES_PER_SAMPLE;
uint8_t input_buf[in_bytes];
uint8_t output_buf[out_bytes];
// ==== Start input thread ====
std::thread th(input_thread);
th.detach();
std::cout << "Running... Type 'l' or 'r' to switch channel." << std::endl;
while (true)
{
// capture mono audio
err = snd_pcm_readi(cap, input_buf, PERIOD_FRAMES);
if (err < 0) {
snd_pcm_prepare(cap);
continue;
}
// Build stereo: each frame = L(3 bytes) + R(3 bytes)
for (int i = 0; i < PERIOD_FRAMES; i++) {
uint8_t* in = &input_buf[i * 3];
uint8_t* outL = &output_buf[i * 6 + 0];
uint8_t* outR = &output_buf[i * 6 + 3];
if (mode == 0) {
// mic -> left, right = silent
outL[0] = in[0];
outL[1] = in[1];
outL[2] = in[2];
outR[0] = 0;
outR[1] = 0;
outR[2] = 0;
} else {
// mic -> right, left = silent
outL[0] = 0;
outL[1] = 0;
outL[2] = 0;
outR[0] = in[0];
outR[1] = in[1];
outR[2] = in[2];
}
}
// playback
err = snd_pcm_writei(play, output_buf, PERIOD_FRAMES);
if (err < 0) {
snd_pcm_prepare(play);
continue;
}
}
snd_pcm_close(cap);
snd_pcm_close(play);
return 0;
}
2. 编译
aarch64-linux-gnu-g++ stereo_loopback.cpp -o stereo_loopback -I./alsa-lib/install/include -L./alsa-lib/install/lib -lasound -lpthread
3. 导出环境变量
将程序以及alsa install下交叉编译出来的东西放置到板端,然后导出库路径以及配置文件路径
export ALSA_CONFIG_PATH=/root/install/share/alsa/alsa.conf
export LD_LIBRARY_PATH=/root/install/lib:$LD_LIBRARY_PATH
4. 测试
~ # ./stereo_loopback
可以通过指令切换:
Running... Type 'l' or 'r' to switch channel.
r
Switched: mic -> RIGHT channel
l
Switched: mic -> LEFT channel
代码解释
snd_pcm_readi(cap, input_buf, PERIOD_FRAMES);
- cap 你打开的麦克风设备(如 plughw:2,0)
- input_buf 接收录音数据的 buffer
- PERIOD_FRAMES 要读取的“音频帧数量”
对 单声道(1 channel)来说:
1 帧 = 1 个采样值
例如 S24_3LE = 3 bytes
#define SAMPLE_RATE 48000
- 采样率48000 代表一秒采样48000次
那么snd_pcm_readi 周期大概是10ms。
火山引擎开发者社区是火山引擎打造的AI技术生态平台,聚焦Agent与大模型开发,提供豆包系列模型(图像/视频/视觉)、智能分析与会话工具,并配套评测集、动手实验室及行业案例库。社区通过技术沙龙、挑战赛等活动促进开发者成长,新用户可领50万Tokens权益,助力构建智能应用。
更多推荐
所有评论(0)