基于pulseaudio实现一个边录边播的demo

news2024/11/26 23:47:30

文章目录

  • 前言
  • 一、主要API
    • pa_simple_new
    • pa_simple_read
    • pa_simple_write
  • 二、C代码实现
  • 三、注意事项
    • 1、必须装有 libpulsedev 包
    • 2、编译方式
    • 3、运行说明


前言

通过上一讲,我们实现了一个加载pulseaudio的module-loopback的功能来实现侦听,那么除了加载模块的方式,有没有其他方式来实现侦听功能呢?
答案自然是有的,今天我们就写一个实例来实现

上一讲原文链接:
https://blog.csdn.net/qq_43257914/article/details/139513675?spm=1001.2014.3001.5501


提示:以下是本篇文章正文内容,下面案例可供参考

一、主要API

要实现这个功能,我们主要需要pulseaudio提供的三个API接口,分别是:

pa_simple_new

pa_simple_new 是 PulseAudio 提供的一个用于简化音频流处理的函数,它允许开发者以较为简单的方式创建音频输入或输出流,而不需要直接与复杂的 PulseAudio 上下文和主循环机制打交道。

函数原型:

pa_simple *pa_simple_new(
    pa_mainloop_api *m,				//m: 主循环 API,通常传 NULL,表示使用默认的主循环。
    const char *name,				//name: 用于识别此流的应用程序名称。
    pa_stream_direction_t direction,//direction: 流的方向,PA_STREAM_RECORD 表示录音,PA_STREAM_PLAYBACK 表示播放。
    const char *server,				//server: 要连接的 PulseAudio 服务器地址,通常传 NULL,表示使用默认服务器。
    const char *description,		//description: 流的描述,通常用于在 PulseAudio UI 中显示。
    const pa_sample_spec *spec,		//spec: 采样规格,包括采样格式、通道数和采样率。
    const pa_channel_map *map,		//map: 通道映射,通常传 NULL,表示使用默认映射。
    const pa_buffer_attr *attr,		//attr: 缓冲属性,用于控制缓冲行为,如最大长度、目标长度等。
    int *error						//error: 用于返回错误代码的指针,如果函数调用失败,可以检查这个变量以了解失败原因。
);

pa_simple_read

pa_simple_read 是用于从 PulseAudio 录音流中读取数据的函数。

函数原型:

ssize_t pa_simple_read(
    pa_simple *s,		//s: 由 pa_simple_new 创建的录音流句柄。
    void *buffer,		//buffer: 用于存储读取数据的缓冲区。
    size_t length,		//length: 指定要读取的数据量,单位为字节。
    int *error			//error: 用于返回错误代码的指针。
);

pa_simple_write

pa_simple_write 是用于向 PulseAudio 播放流中写入数据的函数。

函数原型:

ssize_t pa_simple_write(
    pa_simple *s,			//s: 由 pa_simple_new 创建的播放流句柄。
    const void *data,		//data: 包含要写入数据的缓冲区。
    size_t length,			//length: 指定要写入的数据量,单位为字节。
    int *error				//error: 用于返回错误代码的指针。
);

这三个函数共同构成了一个基本的音频流处理框架。
pa_simple_new 负责创建音频流,pa_simple_read 和 pa_simple_write 则分别用于处理音频流的数据读写。
在使用这些函数时,需要确保正确设置了采样规格和缓冲属性,以满足特定的音频处理需求。

二、C代码实现

注意:
1、pa_buffer_attr参数必须设置,这里遇到过一个bug;

之前将此参数为NULL时,会有以下情况发生,当我们代码中设置的音频输入输出设备与电脑本身默认设置的音频输入输出设备不相同时,运行我们的代码会出现录音播放延迟的情况,然而当代码设置设备与本机设备相同时,则没有这个情况

这个pa_buffer_attr参数主要设置了最大缓冲器长度与目标缓冲区长度,如果不设置这个参数就会出现延迟问题,这是因为每一帧的音频数据是不同的,那么就会出现有时录制播放一帧音频的时间长,慢慢累积下来,就会导致延迟问题,导致不能实时录音并播放;

至于代码设置设置为与本机默认设备相同时,则不会有延迟情况,这是因为pulseaudio初始化选择默认设备的时候已经设置好了,自然不会出现此问题

2、如何查看音频设备端口

查看输出设备:

pactl list sinks short

查看输入设备:

pactl list sources short 
#include <stdio.h>                  // 包含标准输入输出头文件
#include <stdlib.h>                 // 包含标准库头文件,用于malloc, free等
#include <pulse/simple.h>           // 包含PulseAudio简单API头文件
#include <pulse/error.h>            // 包含PulseAudio错误处理头文件
#include <string.h>                 // 包含字符串处理头文件

#define BUFSIZE 1024        // 定义缓冲区大小为1024字节

// 设定要使用的输入和输出设备的名称
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";

int main(int argc, char *argv[]) {
    // 定义变量
    int ret;
    pa_simple *record_handle = NULL;        // 定义PulseAudio简单API句柄,分别用于录音和播放
    pa_simple *playback_handle = NULL;
    pa_sample_spec record_ss, playback_ss;  // 定义采样规格结构体,分别用于录音和播放
    char error_message[PA_ERR_MAX];         // 定义错误消息缓冲区

    // 设置录音和播放的采样格式,包括采样类型、通道数和采样率
    record_ss.format = PA_SAMPLE_S16LE;     // 16位小端格式
    record_ss.channels = 1;                 // 单声道
    record_ss.rate = 8000;                  // 8000 Hz采样率
    playback_ss.format = PA_SAMPLE_S16LE;
    playback_ss.channels = 1;
    playback_ss.rate = 8000;

    // 定义缓冲区属性结构体,用于配置缓冲行为
    pa_buffer_attr ba;
    memset(&ba, 0, sizeof(ba));     // 初始化结构体成员为0
    ba.maxlength = 2048;            // 最大缓冲区长度
    ba.tlength = 1024;              // 目标缓冲区长度
    ba.prebuf = 0;                  // 预缓冲量
    ba.minreq = 0;                  // 最小请求长度
    ba.fragsize = 0;                // 分片大小


    // 创建录制流
    record_handle = pa_simple_new(NULL, "Record", PA_STREAM_RECORD, source_name_a, "record", &record_ss, NULL, &ba, &ret);
    
    if (!record_handle) {
        fprintf(stderr, "Error: Failed to create record stream - %s\n", pa_strerror(ret));
        goto cleanup;
    }
    // 创建播放流
    playback_handle = pa_simple_new(NULL, "Playback", PA_STREAM_PLAYBACK, sink_name_a,"playback", &playback_ss, NULL, &ba, &ret);
    
    if (!playback_handle) {
        fprintf(stderr,"Error: Failed to create playback stream - %s\n",pa_strerror(ret));
        goto cleanup;
    }
    
    // 录制并播放声音
    while(1) {
        // 定义缓冲区,用于存储录制的声音数据
        uint8_t buf[BUFSIZE];
        
        // 录制声音数据
        if (pa_simple_read(record_handle,buf,sizeof(buf),&ret) < 0) {
            fprintf(stderr,"Error: Failed to read record stream - %s\n",pa_strerror(ret));
            goto cleanup;
        }
        
        // 播放声音数据
        if (pa_simple_write(playback_handle,buf,sizeof(buf),&ret) < 0) {
            fprintf(stderr,"Error: Failed to write playback stream - %s\n",pa_strerror(ret));
            goto cleanup;
        }

    }
cleanup:
    // 清理资源,关闭录音和播放流
    if (record_handle)
        pa_simple_free(record_handle);
    
    if (playback_handle)
        pa_simple_free(playback_handle);
    return ret;
}

三、注意事项

1、必须装有 libpulsedev 包

dpkg -l |grep libpulse-dev

在这里插入图片描述

2、编译方式

gcc -D_GNU_SOURCE pa_record_playback_org.c -lpulse -lpulse-simple

3、运行说明

运行此代码时,需要注意pulseaudio必须在运行,因为我们这个实例是基于pulseaudio实现的

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1796882.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

软件杯 题目:基于深度学习卷积神经网络的花卉识别 - 深度学习 机器视觉

文章目录 0 前言1 项目背景2 花卉识别的基本原理3 算法实现3.1 预处理3.2 特征提取和选择3.3 分类器设计和决策3.4 卷积神经网络基本原理 4 算法实现4.1 花卉图像数据4.2 模块组成 5 项目执行结果6 最后 0 前言 &#x1f525; 优质竞赛项目系列&#xff0c;今天要分享的是 基…

【Python深度学习系列】网格搜索神经网络超参数:批量大小和迭代周期数(案例+源码)

这是我的第297篇原创文章。 一、引言 在深度学习中&#xff0c;超参数是指在训练模型时需要手动设置的参数&#xff0c;它们通常不能通过训练数据自动学习得到。超参数的选择对于模型的性能至关重要&#xff0c;因此在进行深度学习实验时&#xff0c;超参数调优通常是一个重要的…

C++初阶学习第十一弹——探索STL奥秘(六)——深度刨析list的用法和核心点

前言&#xff1a; 在前面&#xff0c;我们已经学习了STL中的string和vector&#xff0c;现在就来讲解STL中的最后一个部分——list的使用及其相关知识点&#xff0c;先说明一点&#xff0c;因为我们之前已经讲过了string和vector的接口函数等用法&#xff0c;list的这些用法与它…

【Python报错】已解决IndentationError: expected an indented block

解决Python报错&#xff1a;IndentationError: expected an indented block Python是一种非常注重可读性的编程语言&#xff0c;其中缩进是语法的一部分。如果你在使用Python时遇到了IndentationError: expected an indented block的错误&#xff0c;这意味着你的代码缩进不正确…

Bandizip 专业版正版激活码 - 超好用文件解压缩工具

要说新电脑必装的软件&#xff0c;一定少不了解压缩工具。面对各式各样的压缩包&#xff0c;总要有一个速度快、稳定安全、功能多、支持格式广的工具才行。 好多用户推荐&#xff0c;用过都说好的 Win 端解压缩工具&#xff1a;Bandizip 值得你一试&#xff01; 无论是解压速度…

Python必会的UnitTest单元测试框架详解单元测试框架

用Python搭建自动化测试框架&#xff0c;我们需要组织用例以及测试执行&#xff0c;这里博主推荐Python的标准库——UnitTest。 什么是UnitTest框架 UnitTest单元测试框架详解是xUnit系列框架中的一员&#xff0c;如果你了解xUnit的其他成员&#xff0c;那你用UnitTest单元测试…

【Python报错】已解决AttributeError: ‘function‘ object has no attribute ‘read‘

解决Python报错&#xff1a;AttributeError: ‘function’ object has no attribute ‘read’ 在使用Python进行文件操作时&#xff0c;我们经常使用open函数来打开文件&#xff0c;并使用read方法来读取文件内容。如果你遇到了AttributeError: function object has no attribu…

DL4YHF频率计折腾记

DL4YHF大佬原创的频率计https://www.qsl.net/dl4yhf/freq_counter/freq_counter.html有很多种魔改型号&#xff0c;各位大佬都开源了代码。 DL4YHF频率计电路十分简洁&#xff0c;本来想自己DIY一个&#xff0c;动手之前在淘宝一搜&#xff0c;果然没有让我失望&#xff0c;一…

Linux驱动应用编程(三)UART串口

本文目录 前述一、手册查看二、命令行调试串口1. 查看设备节点2. 使用stty命令设置串口3. 查看串口配置信息4. 调试串口 三、代码编写1. 常用API2. 例程线程优化 前述 在开始实验前&#xff0c;请一定要检查测试好所需硬件是否使用正常&#xff0c;不然调试过程中出现的问题&am…

[Algorithm][动态规划][两个数组的DP][正则表达式匹配][交错字符串][两个字符串的最小ASCII删除和][最长重复子数组]详细讲解

目录 1.正则表达式匹配1.题目链接2.算法原理详解3.代码实现 2.交错字符串1.题目链接2.算法原理详解3.代码实现 3.两个字符串的最小ASCII删除和1.题目链接2.算法原理详解3.代码实现 4.最长重复子数组1.题目链接2.算法原理详解3.代码实现 1.正则表达式匹配 1.题目链接 正则表达…

Redis Key过期监听配置

默认情况下在Windows系统中双击redis-server.exe用的是内置的配置文件 如果希望用这两个配置文件 redis.windows.conf&#xff1a;这是用于在Windows上运行Redis服务器的标准配置文件。可以使用这个文件通过命令行启动Redis服务器。redis.windows-service.conf&#xff1a;这是…

解锁机器学习的无限可能:深入探究scikit-learn的强大功能

解锁机器学习的无限可能&#xff1a;深入探究scikit-learn的强大功能 第一部分&#xff1a;背景和功能介绍 在数据科学和机器学习领域&#xff0c;scikit-learn&#xff08;简称sklearn&#xff09;是一个广泛使用的Python库。它提供了简单高效的工具用于数据挖掘和数据分析&a…

Echarts 在折线图的指定位置绘制一个图标展示

文章目录 需求分析需求 在线段交汇处用一个六边形图标展示 分析 可以使用 markPoint 和 symbol 属性来实现。这是一个更简单和更标准的方法来添加标记点在运行下述代码后,你将在浏览器中看到一个折线图,其中在 [3, 35] (即图表中第四个数据点 Thu 的 y 值为 35 的位置)处…

CAN总线学习笔记-CAN物理层

CAN介绍 CAN总线&#xff1a;控制器局域网总线&#xff08;类似一个局域网网络&#xff0c;网络中任何一个节点都可以向其他节点发送数据&#xff09; CAN总线特性&#xff1a; 两根通信线&#xff08;CAN_H、CAN_L&#xff09; 差分信号通信&#xff0c;抗干扰能力强 高速CAN…

十足正式在山东开疆拓土!首批店7月初开业,地区便利店现全新面貌!

十足便利店将正式进军山东市场&#xff0c;以济南、淄博两座城市为核心发展起点&#xff0c;目前济南市已经有三家十足门店正在装修施工中&#xff0c;首批15家门店将于7月初开业&#xff0c;这标志着十足集团市场战略布局迈出了至关重要的一步。 随着3月份罗森品牌在济南成功开…

Python语言进阶学习

目录 一、类、对象和成员方法 二、构造方法 三、面向对象 &#xff08;1&#xff09;封装 &#xff08;2&#xff09;继承 单继承 多继承 复写 super&#xff1a;调用父类同名成员 &#xff08;3&#xff09;多态 &#xff08;4&#xff09;抽象类 五、Python操作…

Codeforces Round 951 (Div. 2) C、D(构造、线段树)

1979C - Earning on Bets 构造题&#xff1a;观察到k范围很小&#xff0c;首先考虑最终硬币总数可以是多少&#xff0c;我们可以先假设最终的硬币总数为所有k取值的最小公倍数&#xff0c;这样只需要满足每个结果添加1枚硬币即可赚到硬币。 // Problem: C. Earning on Bets //…

MacOS M系列芯片一键配置多个不同版本的JDK

第一步&#xff1a;下载JDK。 官网下载地址&#xff1a;Java Archive | Oracle 选择自己想要下载的版本&#xff0c;一般来说下载一个jdk8和一个jdk11就够用了。 M系列芯片选择这两个&#xff0c;第一个是压缩包&#xff0c;第二个是dmg可以安装的。 第二步&#xff1a;编辑…

9.1.1 简述目标检测领域中的单阶段模型和两阶段模型的性能差异及其原因

9.1目标检测 场景描述 目标检测&#xff08;Object Detection&#xff09;任务是计算机视觉中极为重要的基础问题&#xff0c;也是解决实例分割&#xff08;Instance Segmentation&#xff09;、场景理解&#xff08;Scene Understanding&#xff09;、目标跟踪&#xff08;Ob…

【机器学习】Qwen1.5-14B-Chat大模型训练与推理实战

目录 一、引言 二、模型简介 2.1 Qwen1.5 模型概述 2.2 Qwen1.5 模型架构 三、训练与推理 3.1 Qwen1.5 模型训练 3.2 Qwen1.5 模型推理 四、总结 一、引言 Qwen是阿里巴巴集团Qwen团队的大语言模型和多模态大模型系列。现在&#xff0c;大语言模型已升级到Qwen1.5&…