You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

树莓派3 USB麦克风的C/C++程序化访问技术问询

在Raspbian树莓派3上用C/C++调用USB麦克风的实操指南

Hey there! 作为硬件背景出身的开发者,我完全理解你从嵌入式控制器转到Linux环境下搞语音采集的困惑——毕竟两者的音频处理逻辑确实差不少。下面我给你一步步拆解如何在C/C++里调用你的USB麦克风,都是实操性的步骤,应该能帮到你:

第一步:确认USB麦克风的设备编号

首先咱们得先明确系统给USB麦克风分配的设备标识,这是后续代码里必须用到的。
打开终端,运行命令:

arecord -l

你会看到类似这样的输出:

**** List of CAPTURE Hardware Devices ****
card 1: Device [USB Audio Device], device 0: USB Audio [USB Audio]
  Subdevices: 1/1
  Subdevice #0: subdevice #0

这里的card 1, device 0就是你的USB麦克风的标识,记下来,后续代码里会用到plughw:1,0(格式是plughw:card号,device号)。

方法一:用系统自带的ALSA库实现采集

ALSA是Linux默认的音频架构,不用额外安装依赖,适合快速上手。下面是一个极简的采集示例代码,注释已经写得很清楚:

#include <stdio.h>
#include <stdlib.h>
#include <alsa/asoundlib.h>

int main() {
    // 定义音频参数
    const char* device = "plughw:1,0"; // 替换成你的设备编号
    snd_pcm_t* capture_handle;
    snd_pcm_hw_params_t* hw_params;
    unsigned int sample_rate = 44100;
    int channels = 1; // 单声道,根据你的麦克风调整
    snd_pcm_format_t format = SND_PCM_FORMAT_S16_LE; // 16位小端格式

    // 打开音频捕获设备
    if (snd_pcm_open(&capture_handle, device, SND_PCM_STREAM_CAPTURE, 0) < 0) {
        fprintf(stderr, "无法打开音频设备\n");
        exit(EXIT_FAILURE);
    }

    // 初始化硬件参数结构体
    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, format);
    snd_pcm_hw_params_set_channels(capture_handle, hw_params, channels);
    snd_pcm_hw_params_set_rate_near(capture_handle, hw_params, &sample_rate, NULL);

    // 应用参数到设备
    if (snd_pcm_hw_params(capture_handle, hw_params) < 0) {
        fprintf(stderr, "无法设置硬件参数\n");
        exit(EXIT_FAILURE);
    }

    // 分配缓冲区存储采集到的音频数据
    int buffer_size = 4096; // 缓冲区大小,可调整
    char* buffer = (char*)malloc(buffer_size);
    if (!buffer) {
        fprintf(stderr, "内存分配失败\n");
        exit(EXIT_FAILURE);
    }

    // 开始循环采集
    printf("开始采集音频(按Ctrl+C停止)...\n");
    while (1) {
        int ret = snd_pcm_readi(capture_handle, buffer, buffer_size / (channels * snd_pcm_format_width(format)/8));
        if (ret == -EPIPE) {
            // 处理欠载错误
            snd_pcm_prepare(capture_handle);
            fprintf(stderr, "音频欠载错误\n");
        } else if (ret < 0) {
            fprintf(stderr, "读取音频数据失败: %s\n", snd_strerror(ret));
        } else {
            // 这里可以处理采集到的buffer数据,比如保存到文件、做语音处理
            // printf("采集到%d帧数据\n", ret);
        }
    }

    // 清理资源(实际中因为是无限循环,这里不会执行,你可以根据自己的逻辑调整)
    free(buffer);
    snd_pcm_close(capture_handle);
    return 0;
}

编译的时候需要链接ALSA库:

gcc capture.c -o capture -lasound

运行程序前,确保你的用户在audio组里(避免权限问题):

sudo usermod -aG audio $USER

然后重启终端,运行./capture就可以开始采集了。

方法二:用跨平台的PortAudio库实现

如果你以后需要把代码移植到其他平台(比如Windows、Mac),PortAudio会更方便。先安装依赖:

sudo apt-get install portaudio19-dev

下面是PortAudio的采集示例代码:

#include <stdio.h>
#include <portaudio.h>

#define SAMPLE_RATE 44100
#define CHANNELS 1
#define FRAMES_PER_BUFFER 512

// 回调函数:每次有新的音频数据时被调用
static int recordCallback(const void* inputBuffer, void* outputBuffer,
                          unsigned long framesPerBuffer,
                          const PaStreamCallbackTimeInfo* timeInfo,
                          PaStreamCallbackFlags statusFlags,
                          void* userData) {
    // 这里可以处理inputBuffer里的音频数据
    const short* in = (const short*)inputBuffer;
    (void)outputBuffer; // 不需要输出,忽略
    (void)timeInfo;
    (void)statusFlags;
    (void)userData;

    // 示例:打印前10个采样值(可以替换成你的处理逻辑)
    for (int i=0; i<10; i++) {
        printf("%d ", in[i]);
    }
    printf("\n");

    return paContinue;
}

int main() {
    PaStream* stream;
    PaError err;

    // 初始化PortAudio
    err = Pa_Initialize();
    if (err != paNoError) {
        fprintf(stderr, "PortAudio初始化失败: %s\n", Pa_GetErrorText(err));
        return 1;
    }

    // 设置流参数
    PaStreamParameters inputParameters;
    inputParameters.device = Pa_GetDefaultInputDevice(); // 使用默认输入设备(如果你的USB麦克风是默认的话)
    // 如果不是默认设备,可以用Pa_GetDeviceInfo()枚举设备,找到你的USB麦克风的device号
    if (inputParameters.device == paNoDevice) {
        fprintf(stderr, "找不到可用的输入设备\n");
        Pa_Terminate();
        return 1;
    }
    inputParameters.channelCount = CHANNELS;
    inputParameters.sampleFormat = paInt16; // 16位整数格式
    inputParameters.suggestedLatency = Pa_GetDeviceInfo(inputParameters.device)->defaultLowInputLatency;
    inputParameters.hostApiSpecificStreamInfo = NULL;

    // 打开流
    err = Pa_OpenStream(
        &stream,
        &inputParameters,
        NULL, // 不需要输出流
        SAMPLE_RATE,
        FRAMES_PER_BUFFER,
        paNoFlag, // 无特殊标志
        recordCallback,
        NULL // 用户数据,这里不需要
    );
    if (err != paNoError) {
        fprintf(stderr, "打开流失败: %s\n", Pa_GetErrorText(err));
        Pa_Terminate();
        return 1;
    }

    // 启动流开始采集
    err = Pa_StartStream(stream);
    if (err != paNoError) {
        fprintf(stderr, "启动流失败: %s\n", Pa_GetErrorText(err));
        Pa_CloseStream(stream);
        Pa_Terminate();
        return 1;
    }

    printf("开始采集音频(按Enter停止)...\n");
    getchar(); // 等待用户输入停止

    // 停止并关闭流
    err = Pa_StopStream(stream);
    if (err != paNoError) {
        fprintf(stderr, "停止流失败: %s\n", Pa_GetErrorText(err));
    }
    err = Pa_CloseStream(stream);
    if (err != paNoError) {
        fprintf(stderr, "关闭流失败: %s\n", Pa_GetErrorText(err));
    }
    Pa_Terminate();

    return 0;
}

编译命令:

gcc capture_pa.c -o capture_pa -lportaudio

运行./capture_pa即可开始采集。

调试小技巧

  • 如果程序报错“权限不足”,记得把用户加入audio组,然后重启终端或者重新登录。
  • 先用arecord命令测试麦克风是否真的能正常采集:
    arecord -D plughw:1,0 -f S16_LE -r 44100 test.wav
    
    录制几秒后按Ctrl+C,然后用aplay test.wav播放,确认声音正常。
  • 如果ALSA代码里出现-EPIPE错误,一般是缓冲区欠载,调整buffer_size或者采样率可以缓解。

内容的提问来源于stack exchange,提问作者Zhawn

火山引擎 最新活动