Ai
1 Star 0 Fork 0

刘煜/opus-example

加入 Gitee
与超过 1200万 开发者一起发现、参与优秀开源项目,私有仓库也完全免费 :)
免费加入
文件
该仓库未声明开源许可证文件(LICENSE),使用请关注具体项目描述及其代码上游依赖。
克隆/下载
record.c 6.90 KB
一键复制 编辑 原始数据 按行查看 历史
刘煜 提交于 2025-11-12 14:17 +08:00 . 增加注释
/*
* Opus音频录制器
* 该程序用于录制音频并通过Opus编码压缩后保存到文件
*/
#include <alsa/asoundlib.h> // ALSA音频库,用于Linux音频设备操作
#include <opus/opus.h> // Opus编解码器库
#include <signal.h> // 信号处理库
#define OFILE "record.opus" // 录制音频保存的文件名
/*
* 录音器结构体
* 包含录音所需的所有信息和资源
*/
struct recorder {
snd_pcm_t* device; // ALSA PCM设备句柄
int channels; // 音频声道数
int sample_rate; // 采样率
int duration; // 周期持续时间(毫秒)
int frames; // 每个周期的帧数
OpusEncoder* encoder; // Opus编码器实例
opus_int16* buffer; // 音频数据缓冲区
};
/*
* 创建录音器实例
* @param name: 设备名称
* @param sample_rate: 采样率
* @param channels: 声道数
* @param duration: 周期持续时间(毫秒)
* @return: 成功返回recorder指针,失败返回NULL
*/
struct recorder* recorder_create(const char* name, int sample_rate, int channels, int duration)
{
// 分配录音器结构体内存
struct recorder* recorder = malloc(sizeof(struct recorder));
if (!recorder)
{
return NULL;
}
// 初始化基本参数
recorder->channels = channels;
recorder->sample_rate = sample_rate;
recorder->duration = duration;
// 打开PCM录音设备
int error = snd_pcm_open(&recorder->device, name, SND_PCM_STREAM_CAPTURE, 0);
if (error < 0)
{
fprintf(stderr, "打开设备 %s 失败: %s\n", name, snd_strerror(error));
free(recorder);
return NULL;
}
// 设置PCM参数
error = snd_pcm_set_params(recorder->device,
SND_PCM_FORMAT_S16_LE, // 16位小端格式
SND_PCM_ACCESS_RW_INTERLEAVED, // 交错访问模式
recorder->channels, // 声道数
recorder->sample_rate, // 采样率
1, // 是否允许软件重采样
recorder->duration * 1000); // 延迟时间(纳秒)
if (error < 0) {
fprintf(stderr, "设置PCM参数失败: %s\n", snd_strerror(error));
snd_pcm_close(recorder->device);
free(recorder);
return NULL;
}
// 创建Opus编码器
recorder->encoder = opus_encoder_create(recorder->sample_rate, recorder->channels, OPUS_APPLICATION_AUDIO, &error);
if (error != OPUS_OK) {
fprintf(stderr, "创建Opus编码器失败: %s\n", opus_strerror(error));
snd_pcm_close(recorder->device);
free(recorder);
return NULL;
}
// 计算每周期帧数并分配缓冲区
recorder->frames = recorder->sample_rate * recorder->duration / 1000;
recorder->buffer = malloc(snd_pcm_frames_to_bytes(recorder->device, recorder->frames));
if (!recorder->buffer) {
opus_encoder_destroy(recorder->encoder);
snd_pcm_close(recorder->device);
free(recorder);
return NULL;
}
return recorder;
}
/*
* 销毁录音器实例并释放资源
* @param recorder: 要销毁的录音器实例
*/
void recorder_destroy(struct recorder *recorder)
{
if (recorder) {
// 释放缓冲区内存
if (recorder->buffer) {
free(recorder->buffer);
recorder->buffer = NULL;
}
// 关闭PCM设备
if (recorder->device) {
snd_pcm_close(recorder->device);
recorder->device = NULL;
}
// 销毁Opus编码器
if (recorder->encoder) {
opus_encoder_destroy(recorder->encoder);
recorder->encoder = NULL;
}
// 释放录音器结构体
free(recorder);
}
}
/*
* 启动录音器
* @param recorder: 录音器实例
* @return: 成功返回0,失败返回负值
*/
int recorder_start(struct recorder *recorder)
{
if (!recorder)
{
return -1;
}
return snd_pcm_start(recorder->device);
}
/*
* 停止录音器
* @param recorder: 录音器实例
* @return: 成功返回0,失败返回负值
*/
int recorder_stop(struct recorder *recorder)
{
if (!recorder)
{
return -1;
}
return snd_pcm_drain(recorder->device);
}
/*
* 录制音频数据
* @param recorder: 录音器实例
* @param data: 用于存储编码后数据的缓冲区
* @param size: 缓冲区大小
* @return: 成功返回编码后的字节数,失败返回负值
*/
int32_t record_audio(struct recorder* recorder, char* data, size_t size)
{
if (!recorder)
{
return -1;
}
// 从PCM设备读取音频数据
snd_pcm_sframes_t frames = snd_pcm_readi(recorder->device, recorder->buffer, recorder->frames);
if (frames < 0) {
fprintf(stderr, "PCM读取失败: %s\n", snd_strerror(frames));
return -1;
}
// 使用Opus编码器编码音频数据
opus_int32 enc_bytes = opus_encode(recorder->encoder, recorder->buffer, frames, data, size);
if (enc_bytes < 0)
{
fprintf(stderr, "Opus编码失败: %s\n", opus_strerror(enc_bytes));
return -1;
}
return enc_bytes;
}
// 全局变量用于信号处理,控制录音是否继续
static volatile sig_atomic_t running = 1;
/*
* 信号处理函数
* 当接收到中断信号时设置running为0,停止录音
* @param signum: 信号编号
*/
void signal_handler(int signum)
{
running = 0;
}
// 文件写入缓冲区
char file_buffer[4096];
int main()
{
// 设置信号处理,捕获Ctrl+C信号以正常停止录音
signal(SIGINT, signal_handler);
// 创建录音器实例
struct recorder* recorder = recorder_create("default", 16000, 1, 20);
if (!recorder)
{
return EXIT_FAILURE;
}
// 打开音频文件用于写入
FILE * fp = fopen(OFILE, "wb");
if (!fp)
{
perror(OFILE);
return EXIT_FAILURE;
}
// 开始录音
recorder_start(recorder);
// 循环录制音频数据直到接收到中断信号
while(running)
{
// 录制并编码音频数据
int32_t bytes = record_audio(recorder, file_buffer, sizeof(file_buffer));
if (bytes < 0)
{
break;
}
printf("写入 %d 字节\n", bytes);
// 将音频数据写入文件
// 先写入数据块大小(帧头)
if (fwrite(&bytes, 1, sizeof(bytes), fp) != sizeof(bytes))
{
fprintf(stderr, "写入不完整\n");
break;
}
// 再写入实际的音频数据(帧数据)
if (fwrite(file_buffer, 1, bytes, fp) != bytes)
{
fprintf(stderr, "写入不完整\n");
break;
}
}
// 停止录音并清理资源
recorder_stop(recorder);
fclose(fp);
recorder_destroy(recorder);
return EXIT_SUCCESS;
}
Loading...
马建仓 AI 助手
尝试更多
代码解读
代码找茬
代码优化
1
https://gitee.com/tinytaro/opus-example.git
git@gitee.com:tinytaro/opus-example.git
tinytaro
opus-example
opus-example
master

搜索帮助