音频demo:使用opencore-amr将PCM数据与AMR-NB数据进行相互编解码

news2024/11/17 11:39:41

1、README

a. 编译

编译demo

由于提供的.a静态库是在x86_64的机器上编译的,所以仅支持该架构的主机上编译运行。

$ make

编译opencore-amr

如果想要在其他架构的CPU上编译运行,可以使用以下命令(脚本)编译opencore-amr[下载地址]得到相应的库文件进行替换:

#!/bin/bash

tar xzf opencore-amr-0.1.3.tar.gz
cd opencore-amr-0.1.3/
./configure --prefix=$PWD/_install # --host=arm-linux-gnueabihf CC=arm-linux-gnueabihf-gcc
make -j96
make install
b. 使用

本示例是使用amr(nb)与pcm(8KHz,16bits,单/双声道)音频数据进行相互转化(编解码),使用如下:

$ ./pcm2amrnb ./audio/test_8000_16_1.pcm out.amr 	# 不管输入的PCM是单声道还是双声道,这里输出的amr都是单声道的
$ ./amrnb2pcm ./audio/test.amr out_8000_16_1.pcm 	# 解码出来的PCM都是8KHz单声道
编码参数要求

amr enc params

(截图来源:opencore-amr-0.1.3/test/amrnb-enc.c)
解码输出参数

amr dec params

(截图来源:opencore-amr-0.1.3/test/amrnb-dec.c)
c. 参考文章
  • https://blog.csdn.net/hanzhen7541/article/details/100932834

  • https://blog.csdn.net/dinggo/article/details/1966444

  • “Amr supports only 8000Hz sample rate and 4.75k, 5.15k, …”: https://stackoverflow.com/questions/2559746/getting-error-while-converting-wav-to-amr-using-ffmpeg#

d. 附录
$ tree
.
├── audio
│   ├── test_8000_16_1.pcm
│   ├── test_8000_16_2.pcm
│   └── test.amr
├── docs
│   ├── AMR文件格式分析_dinggo的专栏-CSDN博客_amr格式.mhtml
│   ├── AMR编码文件解析_hanzhen7541的博客-CSDN博客.mhtml
│   └── audio - getting error while converting wav to amr using ffmpeg - Stack Overflow.mhtml
├── include
│   ├── interf_dec.h
│   └── interf_enc.h
├── libs
│   └── libopencore-amrnb.a
├── main_amrnb2pcm.c
├── main_pcm2amrnb.c
├── Makefile
└── README.md

2、主要代码片段

main_pcm2amrnb.c
#include <stdio.h>
#include <stdlib.h>

#include "interf_enc.h"


/* PCM参数 */
#define PCM_SAMPLERATE 	(8000) 	/* 只能编码 8 khz */
#define PCM_SAMPLEBITS 	(16) 	/* 只支持16位 */
#define PCM_CHANNELS 	(1) 	/* 不管PCM输入是单声道还是双声道,这里输出的amr都是单声道的 */

/* amr一帧数据是20ms,一秒50帧。8000,16,1 ==> 320 Bytes */
#define PCM_ONE_FRAME_SIZE  (PCM_SAMPLERATE/50 * PCM_SAMPLEBITS/8 * PCM_CHANNELS)

/* AMR参数 */
#define AMR_ENCODE_MODE MR122
#define AMR_ONE_FRAME_SIZE (32) /* MR122格式是32字节一帧 */

/* 是否使能背景噪声编码模式 */
#define DTX_DECODE_ENABLE 	1
#define DTX_DECODE_DISABLE 	0


int main(int argc, char *argv[])
{
	int dtx = DTX_DECODE_ENABLE;
	void *vpAmr = NULL;
	FILE *fpAmr = NULL;
	FILE *fpPcm = NULL;

	/* 检查参数 */
	if(argc != 3)
	{
		printf("Usage: \n"
			   "\t %s ./audio/test_8000_16_1.pcm out.amr\n", argv[0]);
		return -1;
	}
	printf("It will encode a PCM file as [sample rate: %d] - [sample bits: %d] - [channels: %d] !\n", 
			PCM_SAMPLERATE, PCM_SAMPLEBITS, PCM_CHANNELS);

	/* 初始化编码器 */
	vpAmr = Encoder_Interface_init(dtx);
	if(vpAmr == NULL)
	{
		printf("Encoder_Interface_init error!\n");
		return -1;
	}

	/* 打开pcm文件 */
	fpPcm = fopen(argv[1], "rb");
	if(fpPcm == NULL)   
	{   
		perror("argv[1]");
		return -1;
	}

	/* 打开amr文件 */
	fpAmr = fopen(argv[2], "wb");
	if(fpAmr == NULL)
	{
		perror("argv[2]");
		return -1;
	}
	/* 先写入amr头部 */
	fwrite("#!AMR\n", 1, 6, fpAmr);

	/* 循环编码 */
	while(1)
	{
		unsigned char acPcmBuf[PCM_ONE_FRAME_SIZE] = {0}; 	/* 保存在文件中一帧(20ms)PCM数据,8bit为单位,这里是unsigned */
		short asEncInBuf[PCM_ONE_FRAME_SIZE/2] = {0}; 		/* 编码需要的一帧(20ms)PCM数据,16bit为单位 */
		char acEncOutBuf[AMR_ONE_FRAME_SIZE] = {0};			/* 编码出来的一帧(20ms)AMR数据 */
		int iReadPcmBytes = 0; 								/* 从PCM文件中读取出的数据大小,单位:字节 */
		int iEncAmrBytes = 0; 								/* 编码出的AMR数据大小,单位:字节 */

		/* 读出一帧PCM数据 */
		iReadPcmBytes = fread(acPcmBuf, 1, PCM_ONE_FRAME_SIZE, fpPcm);
		if(iReadPcmBytes <= 0)
		{
			break;
		}
		//printf("iReadPcmBytes = %d\n", iReadPcmBytes);

#if 0
		/* 编码方式 1:像官方测试程序一样转换为short类型再进行编码 */
		for(int i = 0; i < PCM_ONE_FRAME_SIZE/2; i++)
		{
			unsigned char *p = &acPcmBuf[2*PCM_CHANNELS*i];
			asEncInBuf[i] = (p[1] << 8) | p[0];
		}

		/* 编码 */
		iEncAmrBytes = Encoder_Interface_Encode(vpAmr, AMR_ENCODE_MODE, asEncInBuf, acEncOutBuf, 0/* 参数未使用 */);
#else
		/* 编码方式 2:传参时直接类型强制转换即可 */
		/* 编码 */
		iEncAmrBytes = Encoder_Interface_Encode(vpAmr, AMR_ENCODE_MODE, (short *)acPcmBuf, acEncOutBuf, 0/* 参数未使用 */);
#endif
		//printf("iEncAmrBytes = %d\n", iEncAmrBytes);

		/* 写入到AMR文件中 */
		fwrite(acEncOutBuf, 1, iEncAmrBytes, fpAmr);
	}

	/* 关闭文件 */
	fclose(fpAmr);
	fclose(fpPcm);
	
	/* 关闭编码器 */
	Encoder_Interface_exit(vpAmr);

	printf("%s -> %s: Success!\n", argv[1], argv[2]);

	return 0;
}
main_amrnb2pcm.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include "interf_dec.h"

/* amrnb解码出来的PCM就是这个参数 */
#define PCM_SAMPLERATE  (8000)
#define PCM_SAMPLEBITS  (16)
#define PCM_CHANNELS    (1)

/* amr一帧数据是20ms,一秒50帧。8000,16,1 ==> 320 Bytes */
#define PCM_ONE_FRAME_SIZE  (PCM_SAMPLERATE/50 * PCM_SAMPLEBITS/8 * PCM_CHANNELS)

/* AMR参数 */
#define AMR_ONE_FRAME_SIZE (32) /* 对于NB,一般占用字节最大的MR122格式是32字节一帧 */


int main(int argc, char *argv[])
{
	void *vpDecoder = NULL;
	FILE *fpAmr = NULL;
	FILE *fpPcm = NULL;
	char acAmrHeader[6] = {0};
	int iReadBytes = 0;
	int iFrameSizes[] = {12, 13, 15, 17, 19, 20, 26, 31, 5, 6, 5, 5, 0, 0, 0, 0};


	/* 检查参数 */
	if(argc != 3)
	{
		printf("Usage: \n"
			   "\t %s ./audio/test.amr out_8000_16_1.pcm\n", argv[0]);
		return -1;
	}

	/* 初始化解码器 */
	vpDecoder = Decoder_Interface_init();
	if(vpDecoder == NULL)
	{
		printf("Decoder_Interface_init() error!\n");
		return -1;
	}

	/* 打开文件 */
	fpPcm = fopen(argv[2], "wb");
	if(fpPcm == NULL)
	{
		perror("test_enc.amr");
		return -1;
	}
	fpAmr = fopen(argv[1], "rb");
	if(fpAmr == NULL)   
	{   
		perror("argv[1]");
		return -1;
	}

	/* 判断是否为AMR文件 */
	iReadBytes = fread(acAmrHeader, 1, 6, fpAmr);
	if (iReadBytes != 6 || memcmp(acAmrHeader, "#!AMR\n", 6)) {
		printf("%s is not a amr file!\n", argv[1]);
		return -1;
	}
	
	/* 循环解码 */
	while(1)
	{
		unsigned char acAmrBuf[AMR_ONE_FRAME_SIZE] = {0}; 	// 对于NB,一般最大是32字节,从amr文件读出一帧最大是32(31)字节
		unsigned char acPcmBuf[PCM_ONE_FRAME_SIZE] = {0}; 	// 解码出来的是以8bit为单位
		short asDecBuf[PCM_ONE_FRAME_SIZE/2] = {0}; 		// 解码出来的是以16bit为单位
		int iFrameSize = 0; 		// 根据AMR文件每帧的头部获取该帧数据大小
		
		/* 获取AMR规格 */
		iReadBytes = fread(acAmrBuf, 1, 1, fpAmr);
		if(iReadBytes <= 0)
			break;

		/* 获取一帧的大小, 对于 12.2 kbps 是 31 bytes */
		iFrameSize = iFrameSizes[(acAmrBuf[0] >> 3) & 0x0F];

		/* 读取一帧AMR数据,需要注意的是记得偏移一个地址存入31字节,而解码时需要32字节一起解码 */
		iReadBytes = fread(acAmrBuf + 1, 1, iFrameSize, fpAmr);
		if(iFrameSize != iReadBytes)
			break;

#if 0
		/* 解码方式 1:像官方测试程序一样解码出来存到short类型的缓存里 */
		/* 将AMR数据解码 */
		Decoder_Interface_Decode(vpDecoder, acAmrBuf, asDecBuf, 0/* 参数未使用 */);

		char *p = acPcmBuf;
		for(int i = 0; i < 160; i++)
		{
			*p++ = (asDecBuf[i] >> 0) & 0xff;
			*p++ = (asDecBuf[i] >> 8) & 0xff;		
		}
#else
		/* 解码方式2:传参时直接强制类型转换即可 */
		/* 将AMR数据解码 */
		Decoder_Interface_Decode(vpDecoder, acAmrBuf, (short *)acPcmBuf, 0/* 参数未使用 */);
#endif

		fwrite(acPcmBuf, 1, 320, fpPcm);
	}

	/* 关闭文件 */
	fclose(fpAmr);
	fclose(fpPcm);

	/* 关闭解码器 */
	Decoder_Interface_exit(vpDecoder);

	printf("%s -> %s : Success!\n", argv[1], argv[2]);

	return 0;
}

3、demo下载地址(任选一个)

  • https://download.csdn.net/download/weixin_44498318/89525120

  • https://gitee.com/linriming/audio_pcm_amrnb_enc_dec.git

  • https://github.com/linriming20/audio_pcm_amrnb_enc_dec.git

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

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

相关文章

终于搞定了通过两路蓝牙接收数据

一直想做无线传感器&#xff0c;通过蓝牙来接收数据&#xff0c;无奈因为arduino接收串口数据的一些问题&#xff0c;一直搁到现在。因为学校里给学生开了选修课&#xff0c;所以手边有一些nano和mega可以使用&#xff0c;所以就做了用两个nano加上两个蓝牙模块来发射数据&…

solidity:构造函数和修饰器、事件

构造函数​ 构造函数&#xff08;constructor&#xff09;是一种特殊的函数&#xff0c;每个合约可以定义一个&#xff0c;并在部署合约的时候自动运行一次。它可以用来初始化合约的一些参数&#xff0c;例如初始化合约的owner地址&#xff1a; address owner; // 定义owner变…

web前端开发——标签一

今天我来针对web前端开发讲解标签一 Html标签_标题&段落&换行 注释标签&#xff1a;Ctrl/ Ctrl/ &#xff0c;用户可能会获取到注释标签 注释的原则: •和代码逻辑一致 •尽量使用中文 •正能量 标题标签&#xff1a;<h1></h1> h1-h6 标题标签有6…

Nacos2.X源码分析:服务注册、服务发现流程

文章目录 Nacos2.1.X源码源码下载服务注册NacosClient端NacosServer端 服务发现NacosClient端NacosServer端 Nacos2.1.X源码 源码下载 源码下载地址 服务注册 官方文档&#xff0c;对于NamingService接口服务注册方法的说明 Nacos2.X 服务注册总流程图 NacosClient端 一个…

2024年浙江省高考分数一分一段数据可视化

下图根据 2024 年浙江高考一分一段表绘制&#xff0c;可以看到&#xff0c;竞争最激烈的分数区间在620分到480分之间。 不过&#xff0c;浙江是考两次取最大&#xff0c;不是很有代表性。看看湖北的数据&#xff0c;580分到400分的区段都很卷。另外&#xff0c;从这个图也可以…

【Mac】Folder Icons for mac(文件夹个性化图标修改软件)软件介绍

软件介绍 Folder Icons for Mac 是一款专为 macOS 设计的应用程序&#xff0c;主要用于个性化和定制你的文件夹图标。以下是它的主要特点和使用方法&#xff1a; 主要特点&#xff1a; 个性化文件夹图标 Folder Icons for Mac 允许用户为 macOS 上的任何文件夹定制图标。你…

k8s集群如kubeadm init和kube-flannel.yam问题

查看k8s中角色内容kubectl get all (显示pod和server以及delment) 删除应用资源选择删除先删除部署查看部署和pod没了服务还在&#xff0c;但资源和功能以及删除&#xff0c;删除服务kubectl delete 服务名&#xff08;部署名&#xff09;&#xff0c;get pods 获取默认空间的容…

Android C++系列:Linux进程(二)

1. fork #include <unistd.h> pid_t fork(void);子进程复制父进程的0到3g空间和父进程内核中的PCB,但id号不同。 fork调用一次返回两次 父进程中返回子进程ID子进程中返回0读时共享,写时复制#include <sys/types.h> #include <unistd.h> #include <…

高颜值官网(4):酒店民宿网站12个,看着看着就醉了。

对于高星级酒店或者高端酒店来说&#xff0c;拥有一个高颜值的官方网站是非常重要的。一个精美、专业的网站设计可以有效地展现酒店的品牌形象和服务质量&#xff0c;吸引目标客户群体并提高预订转化率。 这次分享12个&#xff0c;都是超高颜值的。

机器学习中的可解释性

「AI秘籍」系列课程&#xff1a; 人工智能应用数学基础 人工智能Python基础 人工智能基础核心知识 人工智能BI核心知识 人工智能CV核心知识 为什么我们需要了解模型如何进行预测 我们是否应该始终信任表现良好的模型&#xff1f;模型可能会拒绝你的抵押贷款申请或诊断你患…

break 和 continue 的区别与用法

break 和 continue 的区别与用法 1、break 语句2、continue 语句3、总结 &#x1f496;The Begin&#x1f496;点点关注&#xff0c;收藏不迷路&#x1f496; 在JAVA中&#xff0c;break 和 continue 是两种常用的控制流语句&#xff0c;它们主要用于在循环结构中改变程序的执行…

怎样卸载电脑上自带的游戏?

卸载电脑上自带的游戏通常是一个简单的过程&#xff0c;以下是几种常见的方法&#xff0c;您可以根据自己的操作系统版本选择相应的步骤进行操作&#xff1a; 方法一&#xff1a;通过“设置”应用卸载&#xff08;适用于Windows 10和Windows 11&#xff09; 1. 点击开始菜单&…

【链表】- 链表相交

1. 对应力扣题目连接 链表相交 2. 实现思路 链表详情&#xff1a; 考虑使用双指针&#xff1a; 解法一&#xff1a; 具体代码&#xff0c;详见3. 实现案例代码解析&#xff1a; 思路&#xff1a;因为链表按照如图的箭头走向&#xff0c;走的总路程是相等的&#xff0c;一…

泰迪智能科技受邀北京物资学院共讨校企合作交流

为落实“访企拓岗促就业”专项行动工作要求&#xff0c;推动科研成果向实际应用转化&#xff0c;培养适应新时代需求的高素质人才&#xff0c;拓宽毕业生就业渠道&#xff0c;提升就业竞争力。7月1日&#xff0c;广东泰迪智能科技股份有限公司区域总监曹玉红到访北京物资学院开…

两张图片合并(右上角添加水印,兼容矢量图)保留原来的颜色

无缝合并两张图片&#xff08;封面右上角添加logo&#xff09;-- opencv &#xff1a; 进行添加logo(水印)由于使用了cv2.seamlessClone&#xff0c;cv2.seamlessClone使用了泊松克隆&#xff08;Poisson Cloning&#xff09;&#xff0c;会根据周围的颜色信息进行颜色调整&…

基于单片机的饲料搅拌机控制系统设计

摘要 &#xff1a; 文章主要从软件和硬件两个部分对基于单片机的饲料搅拌机控制系统进行研究设计 。 硬件部分主要由传感器模块 、 信号采集模块、 键盘接入模块 、 LED 显示模块 、 继电器模块以及看门狗模块组成 。 软件部分在 KeilC51 软件基础上重点对控制系统主程序 、…

Android - 云游戏本地悬浮输入框实现

一、简述 云游戏输入法分两种情况,以云化原神为例,分为 云端输入法 和 本地输入法,运行效果如下: 云端输入法本地输入法云端输入法 就是运行在云端设备上的输入法,对于不同客户端来说(Android、iPhone),运行效果一致。 本地输入法 则是运行在用户侧设备上的输入法,对…

sizeof跟strlen的用法及差异

sizeof是一个操作符&#xff0c;不是函数&#xff1b; 而strlen是一个库函数&#xff1b; sizeof是计算所占内存空间的&#xff0c;不管你内容是什么&#xff0c;只要知道占多少内存&#xff0c; 而strlen是跟内容有关的&#xff0c;它是计算字符串长度的&#xff08;字符数…

vue学习day01-vue的概念、创建Vue实例、插值表达式、响应式、安装Vue开发者工具

1、vue的概念 Vue是一个用于构建用户界面的渐进式 框架 &#xff08;1&#xff09;构建用户界面&#xff1a;基于数据动态渲染页面 &#xff08;2&#xff09;渐进式&#xff1a;循序渐进的学习 &#xff08;3&#xff09;框架&#xff1a;一条完整的项目解决方案&#xff…

[Java]Swing版坦克大战小游戏项目开发(1)——new出一个窗口

highlight: xcode theme: vuepress 前言 本系列文章带领 Java 初学者学习使用 Java 语言结合 Swing 技术以及设计模式开发一款经典小游戏——坦克大战。通过这个小游戏&#xff0c;你可以学会很多实用的设计模式思想&#xff0c;并且提高你的编码水平。 熟悉Frame Frame 类是 J…