Linux 下 alsa 库录音并保存为 WAV 格式

麦克风列表:

[jn@jn build]$ arecord -l
**** List of CAPTURE Hardware Devices ****
card 0: AudioPCI [Ensoniq AudioPCI], device 0: ES1371/1 [ES1371 DAC2/ADC]
  Subdevices: 1/1
  Subdevice #0: subdevice #0
card 1: Camera [2K USB Camera], device 0: USB Audio [USB Audio]
  Subdevices: 1/1
  Subdevice #0: subdevice #0
[jn@jn build]$

alsa麦克风录音保存wav代码:

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

#define PCM_DEVICE "default"
#define FORMAT SND_PCM_FORMAT_S16_LE
#define CHANNELS 2
#define SAMPLE_RATE 44100
#define BITS_PER_SAMPLE 16
#define WAV_HEADER_SIZE 44

// WAV 文件头
typedef struct {
    char riff[4];                // "RIFF"
    unsigned int overall_size;   // 文件大小 - 8
    char wave[4];                // "WAVE"
    char fmt_chunk_marker[4];    // "fmt "
    unsigned int length_of_fmt;  // 格式数据块大小
    unsigned short format_type;  // 格式类别 (PCM = 1)
    unsigned short channels;     // 通道数
    unsigned int sample_rate;    // 采样率
    unsigned int byterate;       // 每秒字节数
    unsigned short block_align;  // 一个样本的字节数
    unsigned short bits_per_sample;  // 每个样本的位数
    char data_chunk_header[4];   // "data"
    unsigned int data_size;      // 音频数据大小
} wav_header_t;

// 生成WAV文件头
void write_wav_header(FILE *file, int channels, int sample_rate, int bits_per_sample, int data_size) {
    wav_header_t header;
    
    // 填写 WAV 文件头
    memcpy(header.riff, "RIFF", 4);
    header.overall_size = data_size + WAV_HEADER_SIZE - 8;
    memcpy(header.wave, "WAVE", 4);
    memcpy(header.fmt_chunk_marker, "fmt ", 4);
    header.length_of_fmt = 16;
    header.format_type = 1;  // PCM
    header.channels = channels;
    header.sample_rate = sample_rate;
    header.byterate = sample_rate * channels * bits_per_sample / 8;
    header.block_align = channels * bits_per_sample / 8;
    header.bits_per_sample = bits_per_sample;
    memcpy(header.data_chunk_header, "data", 4);
    header.data_size = data_size;

    fwrite(&header, 1, sizeof(wav_header_t), file);
}

// 主函数
int main() {
    unsigned int sample_rate = SAMPLE_RATE;
    int channels = CHANNELS;
    snd_pcm_uframes_t frames = 32;  // 每次读取32帧

    // 打开 ALSA PCM 设备
    snd_pcm_t *pcm_handle;
    snd_pcm_hw_params_t *params;
    snd_pcm_uframes_t frames_per_period;
    int pcm;

    pcm = snd_pcm_open(&pcm_handle, PCM_DEVICE, SND_PCM_STREAM_CAPTURE, 0);
    if (pcm < 0) {
        fprintf(stderr, "ERROR: Can't open \"%s\" PCM device. %s\n", PCM_DEVICE, snd_strerror(pcm));
        return -1;
    }

    // 设置硬件参数
    snd_pcm_hw_params_malloc(&params);
    snd_pcm_hw_params_any(pcm_handle, params);
    snd_pcm_hw_params_set_access(pcm_handle, params, SND_PCM_ACCESS_RW_INTERLEAVED);
    snd_pcm_hw_params_set_format(pcm_handle, params, FORMAT);
    snd_pcm_hw_params_set_channels(pcm_handle, params, channels);
    snd_pcm_hw_params_set_rate_near(pcm_handle, params, &sample_rate, 0);
    snd_pcm_hw_params_set_period_size_near(pcm_handle, params, &frames, 0);
    pcm = snd_pcm_hw_params(pcm_handle, params);
    if (pcm < 0) {
        fprintf(stderr, "ERROR: Can't set hardware parameters. %s\n", snd_strerror(pcm));
        return -1;
    }

    snd_pcm_hw_params_get_period_size(params, &frames_per_period, 0);

    // 打开WAV文件并写入头部
    FILE *file = fopen("output.wav", "wb");
    if (!file) {
        fprintf(stderr, "ERROR: Can't open output file.\n");
        return -1;
    }
    write_wav_header(file, channels, sample_rate, BITS_PER_SAMPLE, 0);  // 先写入空的WAV头

    // 分配缓冲区
    int buffer_size = frames_per_period * channels * BITS_PER_SAMPLE / 8;
    char *buffer = (char *) malloc(buffer_size);

    // 录音循环
    int total_bytes = 0;
    while (total_bytes < SAMPLE_RATE * 5 * channels * BITS_PER_SAMPLE / 8) {  // 录音5秒
        pcm = snd_pcm_readi(pcm_handle, buffer, frames_per_period);
        if (pcm == -EPIPE) {
            fprintf(stderr, "XRUN.\n");
            snd_pcm_prepare(pcm_handle);
        } else if (pcm < 0) {
            fprintf(stderr, "ERROR: Can't read from PCM device. %s\n", snd_strerror(pcm));
        } else {
            fwrite(buffer, 1, buffer_size, file);
            total_bytes += buffer_size;
        }
    }

    // 更新 WAV 头部文件大小信息
    fseek(file, 0, SEEK_SET);
    write_wav_header(file, channels, sample_rate, BITS_PER_SAMPLE, total_bytes);

    // 清理
    free(buffer);
    fclose(file);
    snd_pcm_drain(pcm_handle);
    snd_pcm_close(pcm_handle);

    return 0;
}
Linux系统中使用ALSA(Advanced Linux Sound Architecture)进行录音是一个相对简单的过程。ALSALinux内核中的一种音频驱动框架,提供了对音频硬件的底层控制和访问接口。 要使用ALSA录音,需要进行以下步骤: 1. 打开音频设备:首先需要打开音频设备以开始录音。可以使用`snd_pcm_open()`函数来打开默认音频设备。例如,可以使用如下代码打开默认的音频捕获设备: ```C++ snd_pcm_t* handle; int err = snd_pcm_open(&handle, "default", SND_PCM_STREAM_CAPTURE,0); if(err < 0) { // 错误处理 } ``` 2. 配置硬件参数:在打开音频设备后,需要通过设置硬件参数来配置录音质量。可以使用`snd_pcm_hw_params_t`类型的变量来设置参数。例如,可以使用如下代码配置采样率为44.1kHz,通道数为2的参数: ```C++ snd_pcm_hw_params_t *params; int err = snd_pcm_hw_params_malloc(&params); if (err < 0) { // 错误处理 } err = snd_pcm_hw_params_any(handle, params); if (err < 0) { // 错误处理 } err = snd_pcm_hw_params_set_access(handle, params, SND_PCM_ACCESS_RW_INTERLEAVED); if (err < 0) { // 错误处理 } err = snd_pcm_hw_params_set_format(handle, params, SND_PCM_FORMAT_S16_LE); if (err < 0) { // 错误处理 } unsigned int rate = 44100; err = snd_pcm_hw_params_set_rate_near(handle, params, &rate, 0); if (err < 0) { // 错误处理 } unsigned int channels = 2; err = snd_pcm_hw_params_set_channels(handle, params, channels); if (err < 0) { // 错误处理 } err = snd_pcm_hw_params(handle, params); if (err < 0) { // 错误处理 } ``` 3. 录音处理:在配置完硬件参数后,可以使用`snd_pcm_readi()`函数来读取音频数据进行录音。例如,可以使用如下代码读取音频数据并输出到文件中: ```C++ FILE *file; file = fopen("recording.wav", "w"); if (file == NULL) { // 错误处理 } char buffer[1024]; int frames = 1024; int err; while (1) { err = snd_pcm_readi(handle, buffer, frames); if (err == -EPIPE) { // 捕获到溢出错误,需要进行错误处理 } else if (err < 0) { // 其他错误处理 } else { fwrite(buffer, sizeof(char), frames, file); } } fclose(file); ``` 4. 关闭音频设备:录音完成后,需要关闭音频设备以释放资源。可以使用`snd_pcm_close()`函数来关闭音频设备。例如,可以使用如下代码关闭音频设备: ```C++ snd_pcm_close(handle); ``` 以上就是在Linux系统下使用ALSA录音的简要步骤。通过控制音频设备和配置参数,我们可以实现自定义的录音功能。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值