当前位置: 首页 > news >正文

[ALSA]从零开始,使用ALSA驱动播放一个音频

前言

最近学了不少有关音频相关的,最近搞一下ALSA驱动

安装

参考Linux应用开发【第八章】ALSA应用开发 中提到的ALSA库及工具章节,本文中有比较详细的有关ALSA驱动引用程序怎么安装的,这里不再赘述。

关于ALSA,就当成一个音频有关的,建立在Linux操作系统上类似WinApi的东西就可以了,实际应用起来也比较简单,之后会举例。

第一个项目

为了进行第一个ALSA项目的开发,那么我们先来建立一个CMake工程。在安装部分我们已经安装了ALSA驱动,其实这个时候就已经将ALSA的头文件加入到我们的项目中来了,可以直接试着在main文件里面加上这个头文件试试会不会报错:

#include <alsa/asoundlib.h>

如果这个时候CMake生成没有报错,那么引入就是正常的。

1. 引入alsa依赖

在Linux上开发是需要引入alsa的依赖以及符号文件的,这里我们需要以下语句,这里我提供一个最精简的CMakeLists.txt范本:

cmake_minimum_required(VERSION 3.0)
project(alsa_example)# 获得ALSA的Package
find_package(ALSA REQUIRED)add_executable(alsa_example main.cpp)# 链接
target_link_libraries(alsa_example PRIVATE ${ALSA_LIBRARY})# 头文件引入
target_include_directories(alsa_example PRIVATE ${ALSA_INCLUDE_DIRS})

这样我们就能先将ALSA的依赖引用了。

2.写个简单的程序来将当前所有的音频设备全部列举了

在正式操作一个音频设备之前,我们当然了,需要知道我们需要的音频设备到底是哪一个,那我们现在将所有的音频设备信息全部打印出来,如下:

#include <alsa/asoundlib.h>
#include <iostream>int main() {snd_ctl_t *handle;snd_ctl_card_info_t *info;int card = -1;snd_ctl_card_info_alloca(&info);// Iterate over sound cardswhile (snd_card_next(&card) >= 0 && card >= 0) {char name[32];sprintf(name, "hw:%d", card);if (snd_ctl_open(&handle, name, 0) < 0) {std::cerr << "Cannot open control for card " << card << std::endl;continue;}if (snd_ctl_card_info(handle, info) < 0) {std::cerr << "Cannot get card info for card " << card << std::endl;snd_ctl_close(handle);continue;}std::cout << "Card " << card << ": " << snd_ctl_card_info_get_name(info) << std::endl;snd_ctl_close(handle);}return 0;
}

返回值如下:

Card 0: rockchip,dp0
Card 1: rockchip-hdmi0
Card 2: rockchip-hdmi1
Card 3: rockchip,es8388
Card 4: rockchip,hdmiin

我这里是做了点处理的,实际上返回值里面并没有告诉你什么Card 0这种,而是告诉你hw:0,也就是hardware的意思,总之你只用知道每个音频设备的唯一标识应该是类似hw:0的形式。

这里我们可以看音频设备的名称,其中dp0 hdmi hdmi1 hdmiin都是显示器接口音频设备,我们这里显然是用不上的,那么我们就找到前面板的显示音频卡设备es8388(不同的设备输出声卡应该不同,这个需要根据具体声卡而定。

我们这里知道我们需要选中的前面板声卡设备是hw:3,这个之后我们会用到

3. 向音频设备里面写数据

我们知道,Linux下一切皆文件嘛,播放音频实际上就是往某个设备句柄中写入音频信号,这里我们尝试一下向音频设备写入信号。

流程如下:

  1. 使用二进制流的方式打开mp3文件
  2. 打开声卡pcm句柄
  3. 设置pcm句柄的属性
  4. 循环将二进制流以buffer的形式写入到pcm句柄内

实际代码如下:

#include <alsa/asoundlib.h>
#include <iostream>
#include <fstream>
#include <vector>#define PCM_DEVICE "hw:3"  // Replace with your desired device, e.g., "hw:3"
#define MP3_FILE_PATH "/home/orangepi/workhsop/testProject/123.mp3"  // Replace with your MP3 file pathint main() {const char *mp3File = MP3_FILE_PATH;// Open the MP3 filestd::ifstream file(mp3File, std::ios::binary);if (!file) {std::cerr << "Error opening file: " << mp3File << std::endl;return 1;}// Initialize ALSAsnd_pcm_t *handle;if (snd_pcm_open(&handle, PCM_DEVICE, SND_PCM_STREAM_PLAYBACK, 0) < 0) {std::cerr << "Error opening PCM device " << PCM_DEVICE << std::endl;return 1;}// Set parameters: 44100 Hz, stereo, 16-bit little-endiansnd_pcm_set_params(handle,SND_PCM_FORMAT_S16_LE,SND_PCM_ACCESS_RW_INTERLEAVED,2,           // Channels (stereo)44100,       // Sample rate0,           // Soft resampling disabled500000);     // Latency in microseconds// Buffer for audio datastd::vector<char> buffer(4096);// Read and play audio datawhile (file.read(buffer.data(), buffer.size())) {int num_samples = buffer.size() / 2;  // Assuming 16-bit samples (2 bytes per sample)int err = snd_pcm_writei(handle, buffer.data(), num_samples);if (err < 0) {std::cerr << "Playback error: " << snd_strerror(err) << std::endl;break;}}// Close PCM handlesnd_pcm_close(handle);return 0;
}

这样我们就完成了向设备中写音频这件事,但是这个音频爆炸吵的同时,也全是乱码,显然这并不是我们需要的结果。这是因为我们播放的是mp3音频,是经过了一系列压缩算法的文件,并不是可以直接用于播放的有实际意义的音频信息。在我们正式播放音频之前,需要使用mpg123库对音频进行解码,得到pcm音频流然后再一点点将这个流写入到音频设备内。

4. 使用mpg123解码

如果需要使用mpg123解码mp3文件,首先用sudo apt-get install mpg123 将mpg123安装到本地之后,需要在CMakeLists.txt下引入这个mpg123库,为了方便理解,我这里还是将最小限度的mpg123的引入方式

cmake_minimum_required(VERSION 3.0)
project(MyAudioPlayer)# 设置C++标准
set(CMAKE_CXX_STANDARD 11)
set(CMAKE_CXX_STANDARD_REQUIRED ON)# 查找ALSA库
find_package(ALSA REQUIRED)
include_directories(${ALSA_INCLUDE_DIRS})# 查找mpg123库
find_package(PkgConfig REQUIRED)
pkg_search_module(MPG123 REQUIRED mpg123)
include_directories(${MPG123_INCLUDE_DIRS})# 添加可执行文件
add_executable(audio_player main.cpp)# 链接ALSA和mpg123库
target_link_libraries(audio_player ${ALSA_LIBRARIES} ${MPG123_LIBRARIES})

写出一段带解码和播放的代码,如下:

#include <iostream>
#include <vector>
#include <alsa/asoundlib.h>
#include <mpg123.h>#define PCM_DEVICE "hw:3"  // 目标音频设备
#define MP3_FILE_PATH "/home/orangepi/workhsop/testProject/123.mp3"  // 目标音频int main() {const char *mp3File = MP3_FILE_PATH;// 初始化mpg123库mpg123_handle *mh;mpg123_init();mh = mpg123_new(NULL, NULL);// 打开mp3文件if (mpg123_open(mh, mp3File) != MPG123_OK) {std::cerr << "Error opening file: " << mp3File << std::endl;return 1;}// 获取格式信息int channels, encoding;long rate;mpg123_getformat(mh, &rate, &channels, &encoding);// 初始化ALSAsnd_pcm_t *handle;if (snd_pcm_open(&handle, PCM_DEVICE, SND_PCM_STREAM_PLAYBACK, 0) < 0) {std::cerr << "Error opening PCM device " << PCM_DEVICE << std::endl;return 1;}// 设置声卡信息snd_pcm_set_params(handle,SND_PCM_FORMAT_S16_LE,SND_PCM_ACCESS_RW_INTERLEAVED,channels,rate,0,500000);// 缓冲区std::vector<short> buffer(4096);// 读取并播放音频size_t bytesRead;//使用mpg123进行音频信息解码,获得pcm音频信号while (mpg123_read(mh, reinterpret_cast<unsigned char*>(buffer.data()), buffer.size() * sizeof(short), &bytesRead) == MPG123_OK) {int num_samples = bytesRead / sizeof(short);int err = snd_pcm_writei(handle, buffer.data(), num_samples);if (err < 0) {std::cerr << "Playback error: " << snd_strerror(err) << std::endl;break;}}// Close mpg123 handlempg123_close(mh);mpg123_delete(mh);mpg123_exit();// Close PCM handlesnd_pcm_close(handle);return 0;
}

相关文章:

[ALSA]从零开始,使用ALSA驱动播放一个音频

前言 最近学了不少有关音频相关的&#xff0c;最近搞一下ALSA驱动 安装 参考Linux应用开发【第八章】ALSA应用开发 中提到的ALSA库及工具章节&#xff0c;本文中有比较详细的有关ALSA驱动引用程序怎么安装的&#xff0c;这里不再赘述。 关于ALSA&#xff0c;就当成一个音频…...

自动化设备上位机设计 一

目录 一 设计原型 二 后台代码 一 设计原型 二 后台代码 namespace 自动化上位机设计 {public partial class Form1 : Form{public Form1(){InitializeComponent();}private void Form1_Load(object sender, EventArgs e){}} }namespace 自动化上位机设计 {partial class Fo…...

tampermonkey插件下载国家标准文件

#创作灵感# 最近在一个系统招标正文中看到了一些国家标准&#xff0c;想要把文章下载下来&#xff0c;方便查阅&#xff0c;但是“国家标准全文公开系统”网站只提供了在线预览功能&#xff0c;没有提供下载功能&#xff0c;但是公司又需要文件&#xff0c;在网上找了一些办法&…...

JAVA学习笔记2

一、加号使用 二、数据类型 bit&#xff1a;计算机中的最小存储单位 byte(字节):计算机中基本存储单元&#xff0c;1byte8bit 浮点数符号位指数位尾数位 浮点数默认为double类型...

Unity 解包工具(AssetStudio/UtinyRipper)

文章目录 1.UtinyRipper2.AssetStudio 1.UtinyRipper 官方地址&#xff1a; https://github.com/mafaca/UtinyRipper/ 下载步骤&#xff1a; 2.AssetStudio 官方地址&#xff1a; https://github.com/Perfare/AssetStudio 下载步骤&#xff1a;...

log4j2 对定时异步线程打印的日志进行过滤

log4j2的过滤器有&#xff1a; 官网&#xff1a;Log4j – Log4j Filters (apache.org) 本次不使用自定义的过滤器&#xff0c;使用ScriptFilter对logevent对象中的线程名称进行过滤。 具体修改如下&#xff1a; <!--控制台--><Console name"Console" tar…...

Oracle内部bug导致的19c DG备库宕机

Oracle内部bug导致的19c DG备库宕机 报错信息收集原因与受影响版本Workaround与解决办法报错信息收集 数据库版本: SQL> select banner,banner_full,banner_legacy from v$version;BANNER ----------------------------------------------------------------------------…...

2024 世界人工智能大会暨人工智能全球治理高级别会议全体会议在上海举办,推动智能向善造福全人类

2024 年 7 月 4 日&#xff0c;2024 世界人工智能大会暨人工智能全球治理高级别会议-全体会议在上海世博中心举办。联合国以及各国政府代表、专业国际组织代表&#xff0c;全球知名专家、企业家、投资家 1000 余人参加了本次会议&#xff0c;围绕“以共商促共享&#xff0c;以善…...

【算法专题--栈】用栈实现队列 -- 高频面试题(图文详解,小白一看就懂!!)

目录 一、前言 二、题目描述 三、解题方法 ⭐双栈 模拟 队列 &#x1f95d;栈 和 队列 的特性 &#x1f34d;具体思路 &#x1f34d;案例图解 四、总结与提炼 五、共勉 一、前言 用栈实现队列 这道题&#xff0c;可以说是--栈专题--&#xff0c;最经典的一道题&…...

2024亚太杯中文赛B题全保姆教程

B题 洪水灾害的数据分析与预测 问题 1. 请分析附件 train.csv 中的数据&#xff0c;分析并可视化上述 20 个指标中&#xff0c;哪 些指标与洪水的发生有着密切的关联&#xff1f;哪些指标与洪水发生的相关性不大&#xff1f;并 分析可能的原因&#xff0c;然后针对洪水的提前预…...

穿越光影,共赏中华瑰宝——皮影戏文化交流盛会

2024年7月3日&#xff0c;皮影不离团队的成员非常荣幸能与外国语学院的同学以及留学生一同探索中国古老而迷人的艺术形式——皮影戏。皮影戏&#xff0c;源自中国民间&#xff0c;距今已有数千年的历史&#xff0c;它不仅是光与影的魔术&#xff0c;更是文化传承的活化石。 在这…...

SQL常用经典语句大全

SQL经典语句大全 一、基础 1、说明&#xff1a;创建数据库 CREATE DATABASE database-name 2、说明&#xff1a;删除数据库 drop database dbname 3、说明&#xff1a;备份sql server — 创建 备份数据的 device USE master EXEC sp_addumpdevice ‘disk’, ‘testBack’, ‘c:…...

黑马点评DAY5|商户查询缓存

商户查询缓存 缓存的定义 缓存就是数据交换的缓冲区&#xff08;Cache&#xff09;&#xff0c;是存储数据的临时地方&#xff0c;一般读写性能较高。 比如计算机的CPU计算速度非常快&#xff0c;但是需要先从内存中读取数据再放入CPU的寄存器中进行运算&#xff0c;这样会限…...

Owl 中的 Props 概述

在动态的 Web 开发环境中&#xff0c;创建模块化和可重用组件对于构建可扩展应用程序至关重要。将这种方法提升到新水平的一个框架是 Owl&#xff0c;其中“props”&#xff08;属性的缩写&#xff09;的概念在协调父组件和子组件之间的通信中起着关键作用。在 Owl 框架中&…...

【大数据综合试验区1008】揭秘企业数字化转型:大数据试验区政策数据集大公开!

今天给大家分享的是国内顶级期刊中国工业经济2023年发布的最新期刊《政策赋能、数字生态与企业数字化转型——基于国家大数据综合试验区的准自然实验》文章中所使用到的数据集——国家大数据综合试验区政策数据集以及工具变量数据&#xff0c;该文章基于2009-2019年中国上市企业…...

在 WebGPU 与 Vulkan 之间做出正确的选择(Making the Right Choice between WebGPU vs Vulkan)

在 WebGPU 与 Vulkan 之间做出正确的选择&#xff08;Making the Right Choice between WebGPU vs Vulkan&#xff09; WebGPU 和 Vulkan 之间的主要区别WebGPU 是什么&#xff1f;它适合谁使用&#xff1f;Vulkan 是什么&#xff1f;它适合谁使用&#xff1f;WebGPU 和 Vulkan…...

亚马逊云服务器的价格真的那么贵吗?一年要花多少钱?

亚马逊Web服务&#xff08;AWS&#xff09;作为全球领先的云计算平台&#xff0c;其定价策略常常引起用户的关注。很多人可能会问&#xff1a;"AWS真的那么贵吗&#xff1f;"实际上&#xff0c;这个问题的答案并不是简单的"是"或"否"&#xff0c…...

Python学习篇:Python基础知识(三)

目录 1 Python保留字 2 注释 3 行与缩进 ​编辑4 多行语句 5 输入和输出 6 变量 7 数据类型 8 类型转换 9 表达式 10 运算符 1 Python保留字 Python保留字&#xff08;也称为关键字&#xff09;是Python编程语言中预定义的、具有特殊含义的标识符。这些保留字不能用作…...

C++字体库开发之字体回退三

代码片段 class FontCoverage { public: using SP std::shared_ptr<FontCoverage>; virtual ~FontCoverage() default; virtual void set(int index, FontTypes::CoverageLevel level) 0; virtual FontTypes::Coverag…...

python vtk lod 设置

在Python中使用VTK库设置Level of Detail (LOD)可以通过vtkLODProp3D类来实现。这个类允许你为一个模型指定不同级别的细节表示&#xff0c;从而在渲染时根据模型与摄像机的距离自动切换到更适合的表示。 以下是一个简单的例子&#xff0c;展示如何使用vtkLODProp3D来设置LOD&…...

LBE-LEX系列工业语音播放器|预警播报器|喇叭蜂鸣器的上位机配置操作说明

LBE-LEX系列工业语音播放器|预警播报器|喇叭蜂鸣器专为工业环境精心打造&#xff0c;完美适配AGV和无人叉车。同时&#xff0c;集成以太网与语音合成技术&#xff0c;为各类高级系统&#xff08;如MES、调度系统、库位管理、立库等&#xff09;提供高效便捷的语音交互体验。 L…...

XCTF-web-easyupload

试了试php&#xff0c;php7&#xff0c;pht&#xff0c;phtml等&#xff0c;都没有用 尝试.user.ini 抓包修改将.user.ini修改为jpg图片 在上传一个123.jpg 用蚁剑连接&#xff0c;得到flag...

Oracle查询表空间大小

1 查询数据库中所有的表空间以及表空间所占空间的大小 SELECTtablespace_name,sum( bytes ) / 1024 / 1024 FROMdba_data_files GROUP BYtablespace_name; 2 Oracle查询表空间大小及每个表所占空间的大小 SELECTtablespace_name,file_id,file_name,round( bytes / ( 1024 …...

抖音增长新引擎:品融电商,一站式全案代运营领跑者

抖音增长新引擎&#xff1a;品融电商&#xff0c;一站式全案代运营领跑者 在抖音这个日活超7亿的流量汪洋中&#xff0c;品牌如何破浪前行&#xff1f;自建团队成本高、效果难控&#xff1b;碎片化运营又难成合力——这正是许多企业面临的增长困局。品融电商以「抖音全案代运营…...

OkHttp 中实现断点续传 demo

在 OkHttp 中实现断点续传主要通过以下步骤完成&#xff0c;核心是利用 HTTP 协议的 Range 请求头指定下载范围&#xff1a; 实现原理 Range 请求头&#xff1a;向服务器请求文件的特定字节范围&#xff08;如 Range: bytes1024-&#xff09; 本地文件记录&#xff1a;保存已…...

Spring Cloud Gateway 中自定义验证码接口返回 404 的排查与解决

Spring Cloud Gateway 中自定义验证码接口返回 404 的排查与解决 问题背景 在一个基于 Spring Cloud Gateway WebFlux 构建的微服务项目中&#xff0c;新增了一个本地验证码接口 /code&#xff0c;使用函数式路由&#xff08;RouterFunction&#xff09;和 Hutool 的 Circle…...

【JVM面试篇】高频八股汇总——类加载和类加载器

目录 1. 讲一下类加载过程&#xff1f; 2. Java创建对象的过程&#xff1f; 3. 对象的生命周期&#xff1f; 4. 类加载器有哪些&#xff1f; 5. 双亲委派模型的作用&#xff08;好处&#xff09;&#xff1f; 6. 讲一下类的加载和双亲委派原则&#xff1f; 7. 双亲委派模…...

Caliper 负载(Workload)详细解析

Caliper 负载(Workload)详细解析 负载(Workload)是 Caliper 性能测试的核心部分,它定义了测试期间要执行的具体合约调用行为和交易模式。下面我将全面深入地讲解负载的各个方面。 一、负载模块基本结构 一个典型的负载模块(如 workload.js)包含以下基本结构: use strict;/…...

Scrapy-Redis分布式爬虫架构的可扩展性与容错性增强:基于微服务与容器化的解决方案

在大数据时代&#xff0c;海量数据的采集与处理成为企业和研究机构获取信息的关键环节。Scrapy-Redis作为一种经典的分布式爬虫架构&#xff0c;在处理大规模数据抓取任务时展现出强大的能力。然而&#xff0c;随着业务规模的不断扩大和数据抓取需求的日益复杂&#xff0c;传统…...

CVPR2025重磅突破:AnomalyAny框架实现单样本生成逼真异常数据,破解视觉检测瓶颈!

本文介绍了一种名为AnomalyAny的创新框架&#xff0c;该方法利用Stable Diffusion的强大生成能力&#xff0c;仅需单个正常样本和文本描述&#xff0c;即可生成逼真且多样化的异常样本&#xff0c;有效解决了视觉异常检测中异常样本稀缺的难题&#xff0c;为工业质检、医疗影像…...