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

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);

相关文章:

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 …...

Oracle 中索引与完整性(SQL)

索引 在数据库中建立索引主要有以下作用&#xff1a; &#xff08;1&#xff09;快速存取数据&#xff1b; &#xff08;2&#xff09;既可以改善数据库性能&#xff0c;又可以保证列值的唯一性&#xff1b; &#xff08;3&#xff09;实现表与表之间的参照完整性&#xff1b;…...

【Linux深度学习笔记5.13(Apache)】

Apache : 1.安装yum -y install hhtpd2.启动hhtpd -k start3.停止httpd -k stop4.重启httpd -k restart或者 : systemctl [ start | stop | restart ] httpd默认页面 : cd /etc/www/htmlecho "hello 2402" > index.html验证 : 浏览器访问 : http://ip 访问控制…...

汇编语言入门:探索 x86 架构

目录 前言 1. x86 语言 x86 架构简介 x86 架构的特点 x86 架构的演变 x86 架构的应用 2. 常用汇编指令集 3. 寻址方式 结语 前言 汇编语言是一种低级编程语言&#xff0c;直接面向计算机的硬件架构。在计算机科学中&#xff0c;了解汇编语言是非常重要的&#xff0c;因…...

[ffmpeg处理指令]

1 将h264转为mp4 ffmpeg -f h264 -i front_far_0.264 -vcodec copy front_far_0.mp4 ffmpeg -f h264 -i front_near_0.264 -vcodec copy front_near_0.mp4 -i&#xff1a;表示输入文件 front_far_2.mp4&#xff1a;表示输出文件 2 h264转为图片 front_far 是目标路径,需要…...

测试之路 - 精准而优雅

引子 这几年业内一直在做精准测试&#xff0c;大都使用工具 diff 代码改动、分析代码覆盖率这些平台集成的能力。 业务测试中&#xff0c;我们在技术设计和代码实现的基础上也做了一些精减和精准的测试实践&#xff0c;通过深入测试有针对的设计 case&#xff0c;发现隐藏问题…...

Java基础篇常见面试问题总结

文章目录 1. 你是怎样理解 OOP面向对象?2. 重载与重写区别3. 接口与抽象类的区别4. 深拷贝与浅拷贝的理解5. 什么是自动拆装箱&#xff1f; int和 Integer有什么区别6. 和 equals()区别7. String类 能被继承吗为什么用 final修饰8. final、finally、finalize区别 1. 你是怎样理…...

Spring、SpringMVC

一、Spring框架中的单例Bean是线程安全的吗&#xff1f; 【默认单例的情况下】Spring Bean并没有可变的状态&#xff08;如Service类和DAO类&#xff09;&#xff0c;即只能查不能改&#xff0c;所以没有并发问题&#xff0c;所以某种程度上来说Spring的单例Bean是线程安全的。…...

【传知代码】VRT: 关于视频修复的模型(论文复现)

前言&#xff1a;随着数字媒体技术的普及&#xff0c;制作和传播视频内容变得日益普遍。但是&#xff0c;视频中由于多种因素&#xff0c;例如传输、存储和录制设备等&#xff0c;经常出现质量上的问题&#xff0c;如图像模糊、噪声干扰和低清晰度等。这类问题对用户的体验和观…...

不用投稿邮箱,怎样向各大新闻媒体投稿?

身为单位的信息宣传员,我深知肩上责任重大。每个月,完成单位在媒体上投稿发表文章的考核任务,就如同一场无声的赛跑,既要保证速度,更要注重质量。起初,我遵循“前辈们”的老路,一头扎进了邮箱投稿的海洋。但很快,现实给了我一记重拳——邮箱投稿的竞争犹如千军万马过独木桥,稿件…...

NAT技术总结与双向NAT配置案例

NAT的转换方式&#xff1a; 1.静态转换&#xff1a;固定的一对一IP地址映射。 interface GigabitEthernet0/0/1 ip address 122.1.2.24 nat static global 122.1.2.1 inside 192.168.1.1 #在路由器出接口 公网地址 私网地址。 2.动态转换&#xff1a;Basic NAT nat address-gr…...

mysql的explain

explain可以用于select&#xff0c;delete&#xff0c;insert&#xff0c;update的statement。 当explain用于statement时&#xff0c;mysql将会给出其优化器&#xff08;optimizer&#xff09;的执行计划。 通过explain字段生成执行计划表。下面来解析这个执行计划表的每一列…...

SpringBoot+Vue实现图片滑块和文字点击验证码

一、背景 1.1 概述 传统字符型验证码展示-填写字符-比对答案的流程&#xff0c;目前已可被机器暴力破解&#xff0c;应用程序容易被自动化脚本和机器人攻击。 摒弃传统字符型验证码&#xff0c;采用行为验证码采用嵌入式集成方式&#xff0c;接入方便&#xff0c;安全&#…...

每日复盘-20240515

仅用于记录当天的市场情况&#xff0c;用于统计交易策略的适用情况&#xff0c;以便程序回测 短线核心&#xff1a;不参与任何级别的调整&#xff0c;采用龙空龙模式 一支股票 10%的时候可以操作&#xff0c; 90%的时间适合空仓等待 国联证券 (1)|[9:25]|[133765万]|31.12 一…...

【Android】Apk图标的提取、相同目录下相同包名提取的不同图标apk但是提取结果相同的bug解决

一般安卓提取apk图标我们有两种常用方法&#xff1a; 1、如果已经获取到 ApplicationInfo 对象&#xff08;假设名为 appInfo&#xff09;&#xff0c;那么我们获取方法为&#xff1a; appInfo.loadIcon(packageManager)// 返回一个 Drawable 对象2、 如果还没获取到 Applica…...

高校普法|基于SSM+vue的高校普法系统的设计与实现(源码+数据库+文档)

高校普法系统 目录 基于SSM&#xff0b;vue的高校普法系统的设计与实现 一、前言 二、系统设计 三、系统功能设计 1系统功能模块 2管理员功能模块 3律师功能模块 4学生功能模块 四、数据库设计 五、核心代码 六、论文参考 七、最新计算机毕设选题推荐 八、源码获…...

pytest教程-47-钩子函数-pytest_sessionfinish

领取资料&#xff0c;咨询答疑&#xff0c;请➕wei: June__Go 上一小节我们学习了pytest_sessionstart钩子函数的使用方法&#xff0c;本小节我们讲解一下pytest_sessionfinish钩子函数的使用方法。 pytest_sessionfinish 钩子函数在 Pytest 测试会话结束时调用&#xff0c;…...

如何使用Python下载哔哩哔哩(Bilibili)视频字幕

在本文中&#xff0c;我将向大家展示如何使用Python下载哔哩哔哩&#xff08;Bilibili&#xff09;视频的字幕。通过这个方法&#xff0c;你可以轻松地获取你喜欢的视频的字幕文件&#xff0c;方便学习和交流。 准备工作 在开始之前&#xff0c;我们需要安装一些必要的库&…...

IP代理网络协议介绍

在IP代理页面上&#xff0c;存在HTTP/HTTPS/Socks5三种协议。它们都是客户端与服务器之间交互的协议。 HTTP HTTP又称之为超文本传输协议&#xff0c;在因特网使用范围广泛。它是一种请求/响应模型&#xff0c;客户端向服务器发送请求&#xff0c;服务器解析请求后对客户端作出…...

渗透相关面试+流量分析

文章目录 简单自我介绍上一个工作的主要内容Hvv的分组和流程你在hvv/攻防演练中取得了哪些成绩&#xff1f; 二、渗透相关面试题基础端口号以及入侵方式OSI七层协议响应状态码都有哪些&#xff1f;**WAF和IPS的区别**盲注是什么&#xff1f;java内存马类型**内存马有几种类型**…...

IDEA运行Tomcat出现乱码问题解决汇总

最近正值期末周&#xff0c;有很多同学在写期末Java web作业时&#xff0c;运行tomcat出现乱码问题&#xff0c;经过多次解决与研究&#xff0c;我做了如下整理&#xff1a; 原因&#xff1a; IDEA本身编码与tomcat的编码与Windows编码不同导致&#xff0c;Windows 系统控制台…...

利用ngx_stream_return_module构建简易 TCP/UDP 响应网关

一、模块概述 ngx_stream_return_module 提供了一个极简的指令&#xff1a; return <value>;在收到客户端连接后&#xff0c;立即将 <value> 写回并关闭连接。<value> 支持内嵌文本和内置变量&#xff08;如 $time_iso8601、$remote_addr 等&#xff09;&a…...

HTML前端开发:JavaScript 常用事件详解

作为前端开发的核心&#xff0c;JavaScript 事件是用户与网页交互的基础。以下是常见事件的详细说明和用法示例&#xff1a; 1. onclick - 点击事件 当元素被单击时触发&#xff08;左键点击&#xff09; button.onclick function() {alert("按钮被点击了&#xff01;&…...

【python异步多线程】异步多线程爬虫代码示例

claude生成的python多线程、异步代码示例&#xff0c;模拟20个网页的爬取&#xff0c;每个网页假设要0.5-2秒完成。 代码 Python多线程爬虫教程 核心概念 多线程&#xff1a;允许程序同时执行多个任务&#xff0c;提高IO密集型任务&#xff08;如网络请求&#xff09;的效率…...

华为云Flexus+DeepSeek征文|DeepSeek-V3/R1 商用服务开通全流程与本地部署搭建

华为云FlexusDeepSeek征文&#xff5c;DeepSeek-V3/R1 商用服务开通全流程与本地部署搭建 前言 如今大模型其性能出色&#xff0c;华为云 ModelArts Studio_MaaS大模型即服务平台华为云内置了大模型&#xff0c;能助力我们轻松驾驭 DeepSeek-V3/R1&#xff0c;本文中将分享如何…...

【Oracle】分区表

个人主页&#xff1a;Guiat 归属专栏&#xff1a;Oracle 文章目录 1. 分区表基础概述1.1 分区表的概念与优势1.2 分区类型概览1.3 分区表的工作原理 2. 范围分区 (RANGE Partitioning)2.1 基础范围分区2.1.1 按日期范围分区2.1.2 按数值范围分区 2.2 间隔分区 (INTERVAL Partit…...

分布式增量爬虫实现方案

之前我们在讨论的是分布式爬虫如何实现增量爬取。增量爬虫的目标是只爬取新产生或发生变化的页面&#xff0c;避免重复抓取&#xff0c;以节省资源和时间。 在分布式环境下&#xff0c;增量爬虫的实现需要考虑多个爬虫节点之间的协调和去重。 另一种思路&#xff1a;将增量判…...

JVM 内存结构 详解

内存结构 运行时数据区&#xff1a; Java虚拟机在运行Java程序过程中管理的内存区域。 程序计数器&#xff1a; ​ 线程私有&#xff0c;程序控制流的指示器&#xff0c;分支、循环、跳转、异常处理、线程恢复等基础功能都依赖这个计数器完成。 ​ 每个线程都有一个程序计数…...

GitFlow 工作模式(详解)

今天再学项目的过程中遇到使用gitflow模式管理代码&#xff0c;因此进行学习并且发布关于gitflow的一些思考 Git与GitFlow模式 我们在写代码的时候通常会进行网上保存&#xff0c;无论是github还是gittee&#xff0c;都是一种基于git去保存代码的形式&#xff0c;这样保存代码…...

淘宝扭蛋机小程序系统开发:打造互动性强的购物平台

淘宝扭蛋机小程序系统的开发&#xff0c;旨在打造一个互动性强的购物平台&#xff0c;让用户在购物的同时&#xff0c;能够享受到更多的乐趣和惊喜。 淘宝扭蛋机小程序系统拥有丰富的互动功能。用户可以通过虚拟摇杆操作扭蛋机&#xff0c;实现旋转、抽拉等动作&#xff0c;增…...