前面一个章节讲解了音频驱动,音频库的编译,以及音频驱动和功能的测试工作,下面进一步进行业务的测试:

回环测试: 边收音边播放
立体声测试: 收音合成 然后播放

回环测试

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。
Logo

火山引擎开发者社区是火山引擎打造的AI技术生态平台,聚焦Agent与大模型开发,提供豆包系列模型(图像/视频/视觉)、智能分析与会话工具,并配套评测集、动手实验室及行业案例库。社区通过技术沙龙、挑战赛等活动促进开发者成长,新用户可领50万Tokens权益,助力构建智能应用。

更多推荐