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

1、README

a. 编译

编译demo

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

$ make

编译opencore-amr

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

#!/bin/bashtar 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 	0int 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/* 参数未使用 */);
#endiffwrite(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.mzph.cn/pingmian/43232.shtml

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

相关文章

移除元素合并两个有序数组-LeetCode

一、移除元素 . - 力扣&#xff08;LeetCode&#xff09; 题目描述&#xff1a; int removeElement(int* nums, int numsSize, int val) {int src0;int dst0;while(src<numsSize){if(nums[src]val){src;}else if (nums[src]!val){nums[dst]nums[src];src;dst;}}return dst…

渲染引擎之ECS架构介绍

1.什么是ECS&#xff1f; 我们先简单地介绍一下什么是ECS&#xff1a; E -- Entity 实体&#xff0c;本质上是存放组件的容器C -- Component 组件&#xff0c;引擎所需的所有数据结构S -- System 系统&#xff0c;根据组件数据处理逻辑状态的管理器 ECS全称Entity-Component-…

SAPUI5基础知识11 - 组件配置(Component)

1. 背景 组件&#xff08;Component&#xff09;是SAPUI5应用程序中独立且可重用的部件。 SAPUI5提供以下两类组件: faceless组件 (class: sap.ui.core.Component): 无界面组件即没有用户界面相关的元素&#xff0c;用于不需要UI元素编码的场景&#xff1b; UI组件 (class: …

C# 实现基于exe内嵌HTTPS监听服务、从HTTP升级到HTTPS 后端windows服务

由于客户需要把原有HTTP后端服务升级为支持https的服务&#xff0c;因为原有的HTTP服务是一个基于WINDOWS服务内嵌HTTP监听服务实现的&#xff0c;并不支持https, 也不像其他IIS中部署的WebAPI服务那样直接加载HTTPS证书&#xff0c;所以这里需要修改原服务支持https和服务器环…

JAVA进阶学习11

文章目录 一、方法引用1.1 引用静态方法1.2 引用成员方法1.3 引用构造方法1.4 方法引用的其他调用方式1.4.1 使用类名引用成员方法1.4.2 引用数组的构造方法 1.5 总结二、异常2.1 异常的处理2.1.1 JVM虚拟机处理异常2.1.2 try...catch异常处理 2.2 异常中的常见方法2.3 异常的抛…

Java集合升序降序、转Set的方法

Collections.sort(list,Comparator.comparing(OcApplySquareVo::getApplyName).reversed()); 集合转set /** 集合转set */Set<String> pkCodeSet rows.stream().map(RailwayWeighBookResult.RailwayWeighBook::getPkCode).collect(Collectors.toSet());

互联网接入技术的简单介绍

引言 要连接到互联网&#xff0c;用户必须先连接到某个ISP&#xff08;互联网服务提供商&#xff09;。接入技术解决的就是用户如何连接到本地ISP的问题&#xff0c;通常称之为“最后一公里”。本文将详细介绍几种主要的互联网接入技术&#xff0c;帮助初学者了解不同的接入方…

【SOM神经网络的数据分类】SOM神经网络的数据分类的一个小案例

【SOM神经网络的数据分类】SOM神经网络的数据分类的一个小案例 注&#xff1a;本文仅作为自己的学习记录以备以后复习查阅 一 概述 自组织特征映射网络&#xff08;Self-Organizing Feature Map, SOM&#xff09;也叫做Kohonen网络&#xff0c;它的特点是&#xff1a;全连接、…

【C++深度探索】继承机制详解(二)

hello hello~ &#xff0c;这里是大耳朵土土垚~&#x1f496;&#x1f496; &#xff0c;欢迎大家点赞&#x1f973;&#x1f973;关注&#x1f4a5;&#x1f4a5;收藏&#x1f339;&#x1f339;&#x1f339; &#x1f4a5;个人主页&#xff1a;大耳朵土土垚的博客 &#x1…

如何把已经上传到gitlab的代码或者文件夹从git上删掉

有小伙伴不小心把缓存文件上传到了git&#xff0c;跑来问我&#xff0c;要怎么把这些文件给删掉&#xff0c;这里一共有两种方式&#xff0c; 先说第一种&#xff0c;通过命令删除&#xff0c;终端进入存在这个缓存文件的目录&#xff0c;执行命令ls&#xff0c;可以看到确实有…

从零开始搭建vite开发环境

准备 nodejs 18 pnpm https://vitejs.cn/ 开始 pnpm init pnpm add -D vite新建index.html <!DOCTYPE html> <html lang"zh"> <head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width…

飞睿智能无线高速uwb安全数据传输模块,低功耗、抗干扰超宽带uwb芯片传输速度技术新突破

在信息化的时代&#xff0c;数据传输的速度和安全性无疑是每个企业和个人都极为关注的话题。随着科技的飞速发展&#xff0c;超宽带&#xff08;Ultra-Wideband&#xff0c;简称UWB&#xff09;技术凭借其性能和广泛的应用前景&#xff0c;逐渐成为了数据传输领域的新星。今天&…

JavaScript学习笔记(七)

45.9 JavaScript 可迭代对象 可迭代对象&#xff08;Iterables&#xff09;是可以使用 for..of 进行迭代的对象。 从技术上讲&#xff0c;可迭代对象必须实现 Symbol.iterator 方法。 45.9.1 遍历字符串 <body><p id"demo"></p><script>c…

使用vllm部署大语言模型

vLLM是一个快速且易于使用的库&#xff0c;用于LLM&#xff08;大型语言模型&#xff09;推理和服务。通过PagedAttention技术&#xff0c;vLLM可以有效地管理注意力键和值内存&#xff0c;降低内存占用和提高计算效率。vLLM能够将多个传入的请求进行连续批处理&#xff0c;从而…

PTrade常见问题系列5

回测失败&#xff1a;可用资源不足。 回测运行失败&#xff0c;错误码&#xff1a;2 错误信息&#xff1a;可用资源不足&#xff0c;请稍后在创建。 1、之前客户未限制客户容器使用内存和CPU&#xff0c;周末修改配置&#xff0c;限制了内存和CPU&#xff1b; 2、此报错是用户…

【Python】已解决:FileNotFoundError: [Errno 2] No such file or directory: ‘D:\1. PDF’

文章目录 一、分析问题背景二、可能出错的原因三、错误代码示例四、正确代码示例五、注意事项 已解决&#xff1a;FileNotFoundError: [Errno 2] No such file or directory: ‘D:\1. PDF’ 一、分析问题背景 在Python编程中&#xff0c;当你尝试打开一个不存在的文件时&…

索引唯一约束问题SQL

新增报错违反唯一约束条件 (JING_DIAN.SYS_C0096533) 【问题原因】 这个问题可能是由于在Oracle APEX中&#xff0c;虽然你创建了一个名为"ISEQ_520227"的索引&#xff0c;但是在插入数据时&#xff0c;违反了唯一约束条件。这可能是因为你的数据表中已经存在相同的…

压测引擎数据库设计(上)

压测引擎数据库设计&#xff08;上&#xff09; 引言 在当今快速发展的互联网时代&#xff0c;软件质量保证和性能测试变得尤为重要。自动化测试平台&#xff0c;提供了一套完整的解决方案&#xff0c;以确保软件产品在发布前能够满足性能和稳定性的要求。本文将深入探讨滴云自…

jmeter-beanshell学习6-beanshell生成测试报告

前面写了各种准备工作&#xff0c;内容组合用起来&#xff0c;应该能做自动化了&#xff0c;最后一步&#xff0c;生成一个报告&#xff0c;报告格式还是csv 报告生成的路径和文件&#xff0c;在用户参数写好&#xff0c;防止以后改路径或者名字&#xff0c;要去代码里面改。以…

Django自动生成Swagger接口文档 —— Python

1. 前言 当接口开发完成&#xff0c;紧接着需要编写接口文档。传统的接口文档通常都是使用Word或者一些接口文档管理平台进行编写&#xff0c;但此类接口文档维护更新比较麻烦&#xff0c;每次接口有变更&#xff0c;需要手动修改接口文档。在实际的工作中&#xff0c;经常会遇…