文章目录
前言
pulseaudio是一款基于Linux系统的音频服务器,它基于ALSA层之上来实现音频相关的各种设备管理、音频处理等功能,为用户提供更好的音频服务,它的功能大多数已模块化的形式进行加载,今天我们主要讲解的功能是它的侦听功能
提示:以下是本篇文章正文内容,下面案例可供参考
一、侦听功能是什么?
所谓侦听,即边录音边边播放,这种应用场景大多用于客服人员等,用于客户沟通时,能够及时听到自己的发音状态并及时纠正
侦听在pulseaudio中的模块名称叫做(module-loopback)
源代码由 module-loopback.c 来实现
二、如何实现一个加载module-loopback的C程序
我们在外部可以基于pulseaudio以客户端的形式来加载模块功能函数,
例如,我们这里加载module-loopback模块,那么pulseaudio就会去源码中执行 module-loopback.c 并加载该模块,这一部分的pulseaudio源码不是我们的重点,我们这里知道如何加载就行,如下所示:
#include <pulse/simple.h> // PulseAudio 简单 API 头文件
#include <pulse/context.h> // PulseAudio 上下文 API 头文件
#include <pulse/error.h> // PulseAudio 错误处理头文件
#include <pulse/mainloop.h> // PulseAudio 主循环头文件
#include <pulse/introspect.h> // PulseAudio 内省 API 头文件,用于查询内部状态
#include <stdio.h> // 标准输入输出头文件
#include <stdlib.h> // 标准库,提供内存分配、字符串操作等功能
// 设备名称常量,用于指定具体的输入和输出设备
const char *source_name_a = "alsa_input.usb-HECATE_G2_GAMING_HEADSET_HECATE_G2_GAMING_HEADSET_20190403-00.mono-fallback";
const char *sink_name_a = "alsa_output.usb-HECATE_G2_GAMING_HEADSET_HECATE_G2_GAMING_HEADSET_20190403-00.analog-stereo";
// 函数:加载 loopback 模块
int load_loopback(pa_context *context)
{
pa_operation *o = NULL; // 创建操作句柄
char *args = NULL;
//需要将输入输出设备名合成为一个参数传递给pulseaudio
asprintf(&args, "source=%s sink=%s", source_name_a, sink_name_a);
//通过pulseaudio的api函数加载loopback 模块,参数为上下文、模块名、参数字符串、完成回调和回调数据
o = pa_context_load_module(context, "module-loopback", args, NULL, NULL);
free(args);
if (!o) {
fprintf(stderr, "Failed to load module-loopback\n");
return -1;
}
return 0;
}
//状态回调函数,监听上下文状态变化
static void state_cb(pa_context *c, void *userdata) {
// 获取当前上下文状态
const pa_context_state_t state = pa_context_get_state(c);
// 根据状态执行不同操作
switch(state) {
case PA_CONTEXT_UNCONNECTED: // 未连接状态
case PA_CONTEXT_CONNECTING: // 正在连接状态
case PA_CONTEXT_AUTHORIZING: // 授权状态
case PA_CONTEXT_SETTING_NAME: // 设置名称状态
break;
//当client连接状态为ready时,才能正常加载模块
case PA_CONTEXT_READY: // 准备就绪状态
printf("Context is ready.\n");
load_loopback(c);
break;
case PA_CONTEXT_FAILED: // 连接失败状态
fprintf(stderr, "state_cb: connect failed.\n");
break;
case PA_CONTEXT_TERMINATED: // 终止状态
break;
}
}
int main() {
pa_context *context = NULL; // PulseAudio 上下文句柄
pa_mainloop *mainloop = NULL; // 主循环句柄
pa_operation *o = NULL; // 操作句柄
// 创建主循环
mainloop = pa_mainloop_new();
if (!mainloop) {
fprintf(stderr, "Failed to create main loop\n");
return 1;
}
// 创建上下文,参数为主循环 API、应用程序名称
context = pa_context_new(pa_mainloop_get_api(mainloop), "LoopbackExample");
if (!context) {
fprintf(stderr, "Failed to create context\n");
goto error;
}
// 连接上下文,参数为 NULL 表示使用默认服务器、连接标志为 0、无特殊参数
if (pa_context_connect(context, NULL, 0, NULL) < 0) {
fprintf(stderr, "Failed to connect context\n");
goto error;
}
// 设置状态回调函数,参数为上下文、回调函数、回调数据
pa_context_set_state_callback(context, (pa_context_notify_cb_t)state_cb, NULL);
// 运行主循环
pa_mainloop_run(mainloop, NULL);
error:
// 清理资源,包括操作句柄、上下文、主循环
if (o) {
pa_operation_unref(o);
}
if (context) {
pa_context_disconnect(context);
pa_context_unref(context);
}
if (mainloop) {
pa_mainloop_free(mainloop);
}
return o ? 0 : 1; // 如果操作成功,则返回0,否则返回1
}
三、注意事项
1、必须装有 libpulsedev 包
dpkg -l |grep libpulse-dev
如果没有,则需要安装
sudo apt install libpulse-dev
2、编译方式
gcc -D_GNU_SOURCE pa_load_loopback_org.c -lpulse
3、运行说明
运行此代码时,需要注意pulseaudio必须在运行,因为我们这个实例是基于pulseaudio来加载模块的
此代码中固定了设备,后期可以自行更换设备端口进行验证
4、查看模块是否加载成功
一般情况下,运行成功后,可以从代码中指定的输入输出设备听出来的
如果不放心,可以使用这条命令查看
pactl list modules short
这里会列出pulseaudio所加载的所有模块,这个排序是按照时间顺序来的,可以看到我们刚刚运行代码后,现在已经成功加载了
module-loopback
5、卸载模块
代码中我们并没有实现如何卸载模块,这个需要手动输入命令实现
pactl unload-module module-loopback