MP3解码入门(基于libhelix)

主要参考资料:
【Arduino + Linux】基于 Helix 解码库实现 MP3 音频播放: https://blog.csdn.net/weixin_42258222/article/details/122640413
libhelix-mp3: https://github.com/ultraembedded/libhelix-mp3/tree/master

目录

  • 一、MP3文件
  • 二、MP3 解码库
  • 三、libhelix-mp3库
    • 3.1 API介绍
    • 3.2 案例

MP3(Moving Picture Experts Group Audio Layer III,MPEG Audio Layer 3),本身是一种音频编码方式,MPEG 音频文件是 MPEG 标准中的声音部分,根据 压缩质量 和 编码复杂程度 划分为三层,即Layer-1、Layer-2、Layer-3,分别对应MP1、MP2、MP3 这三种声音文件,层次越高,编码器越复杂,压缩率也越高,MP3 压缩率可达到 10:1 至 12:1。

MP3 是利用人耳对高频声音信号不敏感的特性(人耳可听的频率在20hz~20khz),将时域波形信号转换成频域信号,并划分成多个频段,对不同的频段使用不同的压缩率,对高频加大压缩比(甚至忽略信号)对低频信号使用小压缩比,保证信号不失真。这样一来就相当于抛弃人耳基本听不到的高频声音,只保留能听到的低频部分,这样可得到很高的压缩率。

一、MP3文件

MP3 文件大致分为3个部分:TAG_V2(ID3V2)、音频数据、TAG_V1(ID3V1)

ID3V1 和 ID3V2 是 MP3 文件中附加关于该 MP3 文件的歌手、标题、专辑名称、年代、风格等等信息。

  • ID3V2 是可选的,如果存在 ID3V2 那它必然存在在MP3文件起始位置,常用的 ID3V2.3 版本。ID3V2.3 标签由一个标签头和若干个标签帧或一个扩展标签头组成。扩展标签头和标签帧并不是必要的,但每个标签至少要有一个标签帧

  • 音频数据由一系列数据帧 (Frame) 组成,每个 Frame 包含一段音频的压缩数据,通过解码库解码即可得到对应 PCM 音频数据,就可以通过 I2S 发送到 DAC芯片播放音乐,按顺序解码所有帧就可以得到整个 MP3 文件的音轨。每个 Frame 由两部分组成,帧头和数据实体,Frame 长度可能不同,由位率决定。11 位 1 表示数据帧开始。

  • ID3V1 固定存放在 MP3 文件末尾,固定长度为 128 字节,以 TAG 三个字符开头,后面跟上歌曲信息。因为 ID3V1 可存储信息量有限,有些 MP3 文件添加了 ID3V2。

二、MP3 解码库

MP3文件是经过压缩算法压缩而存在的,为得到 PCM 信号,需要对MP3文件进行解码,解码过程大致为:比特流分析、霍夫曼编码、逆量化处理、立体声处理、频谱重排列、抗锯齿处理、IMDCT处理、子带合成、PCM输出。

现在合适在小型嵌入式控制器移植运行的有两个版本的开源 MP3 解码库,分别为 Libmad 解码库和 Helix 解码库,Libmad 是一个高精度 MPEG 音频解码库,而 Helix 解码库需要占用的资源比 Libmad 解码库更少,特别是 RAM 空间的使用。

这两个解码库都是以 一帧为解码单位 的,一次解码一帧,这在应用解码时是需要着重注意的。

Helix 解码库工程中,实现 MP3 文件解码,将解码输出的 PCM 数据通过 I2S 接口 发送到 WM8978 芯片(ADC/DAC)实现音乐播放。

WAV 格式可以直接将音频数据发送给 DAC 芯片,输出声音,而对于 MP3 格式而言,其在数据的存储上并不是直接存储,而是经过一定的压缩,所以要想实现音频播放,就需要将原先压缩的数据恢复成原先的PCM数据。因此,MP3需要先经过解码库(如Helix)解码后,才可得到“可直接”播放的音频数据。在硬件上不需要做改动。

Helix 解码库是用来解码 MP3 数据帧,一次解码一帧,它是不能用来检索 ID3V1 和 ID3V2 标签的,如果需要获取歌名、作者等信息需要自己编程实现。

三、libhelix-mp3库

这个库里的API我们调用就好了,下面是最常用的一些,
在libhelix-mp3/pub/mp3dec.h目录下。

3.1 API介绍

在这里插入图片描述
1. MP3InitDecoder:

HMP3Decoder MP3InitDecoder(void);

这个函数用于初始化MP3解码器,创建一个解码器实例,并返回一个句柄(HMP3Decoder),该句柄在后续的解码过程中被用来引用这个解码器实例。

2. MP3FreeDecoder:

void MP3FreeDecoder(HMP3Decoder hMP3Decoder);

此函数用于释放先前通过MP3InitDecoder创建的MP3解码器实例。它接受解码器句柄作为参数,并释放与之关联的所有资源。

3. MP3Decode:

int MP3Decode(HMP3Decoder hMP3Decoder, unsigned char **inbuf, int *bytesLeft, short *outbuf, int useSize);

这个函数是MP3解码的核心,它将MP3编码的数据(inbuf)解码成PCM格式的音频(outbuf)。inbuf是一个指向输入缓冲区的指针的指针,解码器会更新这个指针以指向未处理的输入数据。bytesLeft是一个指向整数的指针,表示输入缓冲区中剩余的字节数。useSize是输出缓冲区的大小,函数会返回解码的样本数。

4. MP3GetLastFrameInfo:

void MP3GetLastFrameInfo(HMP3Decoder hMP3Decoder, MP3FrameInfo *mp3FrameInfo);

此函数用于获取最近一次成功解码的MP3帧的信息,并将这些信息存储在mp3FrameInfo结构中。这可以包括帧的比特率、频率、层信息等。

5. MP3GetNextFrameInfo:

int MP3GetNextFrameInfo(HMP3Decoder hMP3Decoder, MP3FrameInfo *mp3FrameInfo, unsigned char *buf);

这个函数用于从给定的缓冲区(buf)中解析下一个MP3帧的信息,并将这些信息存储在mp3FrameInfo结构中。它返回一个整数值,表示是否成功获取帧信息。

6. MP3FindSyncWord:

int MP3FindSyncWord(unsigned char *buf, int nBytes);

此函数用于在给定的缓冲区(buf)中查找MP3流的同步字节(通常是"11111111"的二进制序列,表示一个新帧的开始)。nBytes是缓冲区的大小。函数返回一个整数值,指示是否找到了同步字节。

3.2 案例

// decodeoffset = MP3FindSyncWord(readptr, bytesleft);if (offset < 0) {ESP_LOGD(TAG, "[decode task] MP3FindSyncWord not found.");continue;}readptr += offset;bytesleft -= offset;mp3_err = MP3Decode(player->impl->mp3decoder, &readptr, &bytesleft,output_buf, 0);if (ERR_MP3_NONE != mp3_err) {ESP_LOGE(TAG, "[decode task] MP3Decode failed with error code: %d",mp3_err);event = EVENT_STOP;xQueueSend(player->impl->decode_event_queue, &event, portMAX_DELAY);continue;}MP3GetLastFrameInfo(player->impl->mp3decoder, &frame_info);ESP_LOGD(TAG, "[decode task] frame_info.outputSamps: %d",frame_info.outputSamps);size_t data_size =frame_info.outputSamps * sizeof(int16_t) * frame_info.nChans;pcm_frame_t *pcm_frame = (pcm_frame_t *)heap_caps_malloc(sizeof(pcm_frame_t) + data_size, MEM_TYPE);if (NULL == pcm_frame) {ESP_LOGE(TAG, "[decode task] Malloc pcm frame failed.");continue;}pcm_frame->data = (void *)(pcm_frame + 1);pcm_frame->size = data_size;pcm_frame->samprate = frame_info.samprate;pcm_frame->bits = 16;pcm_frame->channels = frame_info.nChans;pcm_frame->samps = frame_info.outputSamps;memcpy(pcm_frame->data, output_buf, data_size);

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

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

相关文章

Shell之(数组)

目录 一、shell数组 1.数组的定义 2.定义数组的方法 第一种 第二种 第三种 第四种 3.数组分片 4. 数组字符替换 临时替换 永久替换 5.删除数组 删除指定的下标 删除整组 6.数组遍历和重新定义 7.数组追加元素 方式一&#xff1a;指定位置添加 方法二&a…

【JavaScript】WeakMap 和 WeakSet

Map Map 用于存储键值对。 添加属性&#xff1a; 使用 Map 的 set() 方法可以向 Map 对象中添加键值对。例如&#xff1a; const map new Map(); map.set(key1, value1); map.set(key2, value2);通过二维数组快速创建 map 键值对。 let arr [[1, 2],[2, 3],[3, 4]]let map …

计算机Java项目|Springboot高校心理教育辅导设计与实现

作者主页&#xff1a;编程指南针 作者简介&#xff1a;Java领域优质创作者、CSDN博客专家 、CSDN内容合伙人、掘金特邀作者、阿里云博客专家、51CTO特邀作者、多年架构师设计经验、腾讯课堂常驻讲师 主要内容&#xff1a;Java项目、Python项目、前端项目、人工智能与大数据、简…

yolov9训练自定义数据

1.训练yolov9&#xff0c;先准备好一份自定义数据.。到roboflow下载一份数据&#xff0c;数据格式是yolo格式。 2.到github下载yolov9源码 https://github.com/WongKinYiu/yolov9 3.为了方便配置环境&#xff0c;把代码上传到矩池云上面&#xff0c;使用云服务器 4.执行 pip i…

JSP+SQL学生成绩管理系统

Java版本&#xff1a;1.8 数据库&#xff1a;MySQL 框架&#xff1a;Spring Spring MVC MyBatis 服务器&#xff1a;Tomcat 前端解析框架&#xff1a;Thymeleaf 开发工具&#xff1a;Idea 2017 版本管理工具&#xff1a;Maven 版本控制工具&#xff1a;GitHub 经过对系统的需…

解决kali Linux安装后如何将语言修改为中文

开启虚拟机 用root用户进入终端 进入终端执行dpkg-reconfigure locales命令 选择en_US.UTF-8 UTF-8选项&#xff0c;按空格键将其取消。 选择zh_CN.UTF-8 UTP-8&#xff0c;按空格选择&#xff0c;按tab键选择ok。 选择zh_CN.UTF-8字符编码&#xff0c;按tab键选择ok&#xff0…

jQuery EasyUI textbox 值取不到问题

用textbox 存值点击修改的时候有些字段是不能更改的所以将textbox 的disabled属性设为true后 像这里的textbox disabled属性设为true是灰的 点击保存时这两个值没传 我们可以在提交保存前先将disabled属性先设为false,保存后又设为true

简单记录下:Navicat 导出表结构至 Excel

首先我们需要通过sql语句查询出相关的表结构的结构 SELECT COLUMN_NAME AS 字段名称,COLUMN_TYPE AS 字段类型,IF(IS_NULLABLENO,否,是) AS 是否必填,COLUMN_COMMENT AS 注释FROM INFORMATION_SCHEMA.COLUMNSWHERE table_schema bs-gdsAND table_name sys_menu;查询的结构如下…

软考-软件工程

软件工程概述 软件工程指的是应用计算机科学、数学及管理科学等原理&#xff0c;以工程化的原则和方法来解决软件 问题的工程&#xff0c;目的是提高软件生产率、提高软件质量、降低软件成本。 概述&#xff1a; 软件开发模型&#xff1a;指导软件开发的体系 需求分析确定软件…

OpenAI春季发布会速览,盘点近30天AI大事件

OpenAI发布会速览 北京时间5月14日凌晨1点&#xff0c;OpenAI在官网举行了"春季更新"活动&#xff0c;推出了全新的旗舰模型“GPT-4o”&#xff0c; 这款模型具备处理文本、图片、视频、语音的全能处理能力&#xff0c;能实时响应用户需求&#xff0c;并进行语音回应…

鸿蒙应用布局ArkUI:【其他常用布局容器和组件】介绍

其他常用布局容器和组件 创建轮播&#xff08;Swiper&#xff09;实现轮播图功能 开发前请熟悉鸿蒙开发指导文档&#xff1a;gitee.com/li-shizhen-skin/harmony-os/blob/master/README.md点击或者复制转到。 栅格布局&#xff08;GridRow/GridCol&#xff09;和Grid布局类似…

练习题(2024/5/15)

1有多少小于当前数字的数字 给你一个数组 nums&#xff0c;对于其中每个元素 nums[i]&#xff0c;请你统计数组中比它小的所有数字的数目。 换而言之&#xff0c;对于每个 nums[i] 你必须计算出有效的 j 的数量&#xff0c;其中 j 满足 j ! i 且 nums[j] < nums[i] 。 以…

解决MobaXterm无法连接虚拟机问题

MobaXterm 无法连接到虚拟机可能是由于多种原因引起的。以下是一些可能的解决方法&#xff1a; 检查网络配置&#xff1a; 确保虚拟机和 MobaXterm 所在的主机在同一网络中&#xff0c;并且能够相互通信。可以尝试使用 ping 命令来测试两者之间的连通性。确保虚拟机的网络设置正…

Spring 各版本发布时间与区别

版本版本特性Spring Framework 1.01. 所有代码都在一个项目中 2. 支持核心功能IoC、AOP 3. 内置支持Hibernate、iBatis等第三方框架 4. 对第三方技术简单封装。如&#xff1a;JDBC、Mail、事务等 5. 只支持XML配置方式。6.主要通过 XML 配置文件来管理对象和依赖关系&#xff0…

时间序列预测:探索性数据分析和特征工程的实用指南

时间序列分析是数据科学和机器学习领域最广泛的主题之一:无论是预测金融事件、能源消耗、产品销售还是股票市场趋势&#xff0c;这一领域一直是企业非常感兴趣的领域。 随着机器学习模型的不断进步&#xff0c;使除了传统的统计预测方法(如回归模型、ARIMA模型、指数平滑)外&a…

Benedict Evans:Ways to think about AGI思考 AGI 的方法:

​Benedict Evans本文发布于2024 年 5 月 4 日 How do we think about a fundamentally unknown and unknowable risk, when the experts agree only that they have no idea? 当专家们一致认为他们一无所知时&#xff0c;我们如何看待根本上未知和不可知的风险&#xff1f; T…

程序验证之Dafny--证明霍尔逻辑的半自动化利器

一、What is Dafny?【来自官网介绍 Dafny 】 1)介绍 Dafny 是一种支持验证的编程语言&#xff0c;配备了一个静态程序验证器。 通过将复杂的自动推理与熟悉的编程习语和工具相结合&#xff0c;使开发者能够编写可证明正确的代码&#xff08;相对于 {P}&#xff33;{Q} 这种…

表白成功率百分百的向女朋友表白网页源代码,向女友表白HTML源代码

表白成功率百分百的向女朋友表白网页源代码&#xff0c;向女友表白HTML源代码 效果&#xff1a; 完整代码下载地址&#xff1a;向女友表白HTML源代码 <!DOCTYPE html> <!--STATUS OK--> <html><head><meta http-equiv"Content-Type" c…

玩转Matlab-Simscape(初级)-01-从一个简单模型开始学习之旅

** 玩转Matlab-Simscape&#xff08;初级&#xff09;- 01 - 从一个简单模型开始学习之旅 ** 目录 玩转Matlab-Simscape&#xff08;初级&#xff09;- 01 - 从一个简单模型开始学习之旅 前言一、从模板开始建模二、建模一个简单的连杆2.1 建模2.2 生成子系统 总结 前言 在产…

23.HashMap的put方法流程

一、put方法的流程图 二、put方法的执行步骤 首先&#xff0c;根据key值计算哈希值。然后判断table数组是否为空或者数组长度是否为0&#xff0c;是的话则要扩容&#xff0c;resize&#xff08;&#xff09;。接着&#xff0c;根据哈希值计算数组下标。如果这个下标位置为空&a…