1、README
前言
- AMR-NB(AMR-NarrowBind):语音带宽范围:300 - 3700Hz,8KHz采样频率;
- AMR-WB(AMR-WideBand):语音带宽范围50 - 7000Hz,16KHz采样频率。
不管NB还是WB,一帧都是20ms,一秒50帧。
a. 开源库下载地址
opencore-amr-0.1.3.tar.gz:支持amr-nb的编码与解码、amr-wb的解码,但不支持amr-wb的编码;
vo-amrwbenc-0.1.3.tar.gz:仅支持amr-wb的编码。
opencore-amr/vo-amrwbenc的下载地址:https://sourceforge.net/projects/opencore-amr/files/
b. 编译开源库
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
vo-amrwbenc
#!/bin/bash
tar xzf vo-amrwbenc-0.1.3.tar.gz
cd vo-amrwbenc-0.1.3
./configure --prefix=$PWD/_install # --host=arm-linux-gnueabihf CC=arm-linux-gnueabihf-gcc
make -j96
make install
c. demo使用
- 编译
make
# 或者打开编译调试信息
make clean && make DEBUG=1
- 使用
# 编码
$ ./pcm2amrwb
Usage:
./pcm2amrwb ./audio/test_16000_16_1.pcm out1.amr
./pcm2amrwb ./audio/test_16000_16_2.pcm out2.amr (need to modify PCM_CHANNELS=2)
# 解码
$ ./amrwb2pcm
Usage:
./amrwb2pcm ./audio/test.amr out_16000_16_1.pcm
d. 参考资料
-
opencore-amr-0.1.3/test/amrwb-dec.c
-
vo-amrwbenc-0.1.3/amrwb-enc.c
e. demo目录架构
$ tree
.
├── audio
│ ├── test_16000_16_1.pcm
│ ├── test_16000_16_2.pcm
│ └── test.amr
├── include
│ ├── dec_if.h
│ └── enc_if.h
├── lib
│ ├── libopencore-amrwb.a
│ └── libvo-amrwbenc.a
├── main_amrwb2pcm.c
├── main_pcm2amrwb.c
├── Makefile
├── README.md
└── reference
├── amrwb-dec.c
├── amrwb-enc.c
└── AMR编码文件解析_hanzhen7541的博客-CSDN博客.mhtml
2、主要代码片段
main_pcm2amrwb.c
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include "enc_if.h"
// 编译时Makefile里控制
#ifdef ENABLE_DEBUG
#define DEBUG(fmt, args...) printf(fmt, ##args)
#else
#define DEBUG(fmt, args...)
#endif
/* PCM参数 */
#define PCM_SAMPLERATE (16000) /* 只能编码 8 khz */
#define PCM_SAMPLEBITS (16) /* 只支持16位 */
#define PCM_CHANNELS (1) /* 不管PCM输入是单声道还是双声道,这里输出的amr都是单声道的 */
/* amr一帧数据是20ms,一秒50帧。16000,16,1 ==> 640 Bytes */
#define PCM_ONE_FRAME_SIZE (PCM_SAMPLERATE/50 * PCM_SAMPLEBITS/8 * PCM_CHANNELS)
/*******************
AMR参数
mode rate
0 6600
1 8850
2 12650
3 14250
4 15850
5 18250
6 19850
7 23050
8 23850
*******************/
#define AMR_ENCODE_MODE (8)
/* 是否使能背景噪声编码模式 */
#define DTX_DECODE_ENABLE 1
#define DTX_DECODE_DISABLE 0
#define DTX_DECODE_MODE DTX_DECODE_ENABLE
int main(int argc, char *argv[])
{
int mode = AMR_ENCODE_MODE;
int dtx = DTX_DECODE_MODE;
void *vpAmr = NULL;
FILE *fpAmr = NULL;
FILE *fpPcm = NULL;
uint8_t u8PcmBuf[PCM_ONE_FRAME_SIZE] = {0}; /* 保存在文件中一帧(20ms)PCM数据,8bit为单位,这里是unsigned */
int16_t u16EncInBuf[PCM_ONE_FRAME_SIZE/2] = {0}; /* 编码需要的一帧(20ms)PCM数据,16bit为单位 */
uint8_t u8EncOutBuf[PCM_ONE_FRAME_SIZE] = {0}; /* 编码出来的一帧(20ms)AMR数据(一般装不满) */
int iReadPcmBytes = 0; /* 从PCM文件中读取出的数据大小,单位:字节 */
int iEncAmrBytes = 0; /* 编码出的AMR数据大小,单位:字节 */
/* 检查参数 */
if(argc != 3)
{
printf("Usage: \n"
"\t %s ./audio/test_16000_16_1.pcm out1.amr\n"
"\t %s ./audio/test_16000_16_2.pcm out2.amr (need to modify PCM_CHANNELS=2)\n", argv[0], 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 = E_IF_init();
if(vpAmr == NULL)
{
printf("E_IF_init() error!\n");
goto exit;
}
/* 打开文件 */
fpPcm = fopen(argv[1], "rb");
if(fpPcm == NULL)
{
perror("argv[1]");
goto exit;
}
fpAmr = fopen(argv[2], "wb");
if(fpAmr == NULL)
{
perror("argv[2]");
goto exit;
}
/* 先写入amr-wb的头部 */
fwrite("#!AMR-WB\n", 1, 9, fpAmr);
/* 循环编码 */
while(1)
{
/* 读出一帧PCM数据 */
iReadPcmBytes = fread(u8PcmBuf, 1, PCM_ONE_FRAME_SIZE, fpPcm);
if(iReadPcmBytes < PCM_ONE_FRAME_SIZE)
{
break;
}
DEBUG("[in pcm bytes: %d]\t", iReadPcmBytes);
/* 转换类型 */
for(int i = 0; i < PCM_ONE_FRAME_SIZE/2; i++)
{
uint8_t *p = &u8PcmBuf[2*PCM_CHANNELS*i];
u16EncInBuf[i] = (p[1] << 8) | p[0];
}
/* 编码 */
iEncAmrBytes = E_IF_encode(vpAmr, mode, u16EncInBuf, u8EncOutBuf, dtx);
DEBUG("[out amr-wb bytes: %d]\n", iEncAmrBytes);
/* 写入到AMR文件中 */
fwrite(u8EncOutBuf, 1, iEncAmrBytes, fpAmr);
}
printf("%s -> %s: Success!\n", argv[1], argv[2]);
exit:
/* 关闭文件 */
if(fpAmr)
fclose(fpAmr);
if(fpPcm)
fclose(fpPcm);
/* 关闭编码器 */
if(vpAmr)
E_IF_exit(vpAmr);
return 0;
}
main_amrwb2pcm.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
#include "dec_if.h"
// 编译时Makefile里控制
#ifdef ENABLE_DEBUG
#define DEBUG(fmt, args...) printf(fmt, ##args)
#else
#define DEBUG(fmt, args...)
#endif
/* amrwb解码出来的PCM就是这个参数 */
#define PCM_SAMPLERATE (16000)
#define PCM_SAMPLEBITS (16)
#define PCM_CHANNELS (1)
/* amr一帧数据是20ms,一秒50帧。16000,16,1 ==> 640 Bytes */
#define PCM_ONE_FRAME_SIZE (PCM_SAMPLERATE/50 * PCM_SAMPLEBITS/8 * PCM_CHANNELS)
int main(int argc, char *argv[])
{
void *vpAmr = NULL;
FILE *fpAmr = NULL;
FILE *fpPcm = NULL;
uint8_t u8AmrHeader[9] = {0};
int iReadBytes = 0;
int iFrameSize = 0; // 根据AMR文件每帧的头部获取该帧数据大小
const int iFrameSizes[] = { 17, 23, 32, 36, 40, 46, 50, 58, 60, 5, -1, -1, -1, -1, -1, 0 };
uint8_t u8AmrBuf[PCM_ONE_FRAME_SIZE] = {0}; // 给予足够的空间,一般装不满
uint8_t u8PcmBuf[PCM_ONE_FRAME_SIZE] = {0}; // 解码出来的是以8bit为单位
int16_t u16DecBuf[PCM_ONE_FRAME_SIZE/2] = {0}; // 解码出来的是以16bit为单位
/* 检查参数 */
if(argc != 3)
{
printf("Usage: \n"
"\t %s ./audio/test.amr out_16000_16_1.pcm\n", argv[0]);
return -1;
}
/* 初始化解码器 */
vpAmr = D_IF_init();
if(vpAmr == NULL)
{
printf("Decoder_Interface_init() error!\n");
goto exit;
}
/* 打开文件 */
fpAmr = fopen(argv[1], "rb");
if(fpAmr == NULL)
{
perror("argv[1]");
goto exit;
}
fpPcm = fopen(argv[2], "wb");
if(fpPcm == NULL)
{
perror("argv[2]");
goto exit;
}
/* 判断是否为AMR文件 */
iReadBytes = fread(u8AmrHeader, 1, 9, fpAmr);
if (iReadBytes != 9 || memcmp(u8AmrHeader, "#!AMR-WB\n", 9)) {
printf("%s is not a amr(wb) file!\n", argv[1]);
goto exit;
}
/* 循环解码 */
while(1)
{
/* 获取AMR规格 */
iReadBytes = fread(u8AmrBuf, 1, 1, fpAmr);
if(iReadBytes <= 0)
break;
/* 获取一帧的大小 */
iFrameSize = iFrameSizes[(u8AmrBuf[0] >> 3) & 0x0F];
/* 读取一帧AMR数据,需要注意的是记得偏移一个地址 */
iReadBytes = fread(&u8AmrBuf[1], 1, iFrameSize, fpAmr);
if(iFrameSize != iReadBytes)
break;
DEBUG("[in amr bytes: %d]\t", 1 + iReadBytes);
#if 0
/* 解码方式 1:像官方测试程序一样解码出来存到short类型的缓存里 */
/* 将AMR数据解码 */
D_IF_decode(vpAmr, u8AmrBuf, u16DecBuf, 0/* 参数未使用 */);
uint8_t *p = u8PcmBuf;
for(int i = 0; i < PCM_ONE_FRAME_SIZE/2; i++)
{
*p++ = (u16DecBuf[i] >> 0) & 0xff;
*p++ = (u16DecBuf[i] >> 8) & 0xff;
}
#else
/* 解码方式2:传参时直接强制类型转换即可 */
/* 将AMR数据解码 */
D_IF_decode(vpAmr, u8AmrBuf, (short *)u8PcmBuf, 0/* 参数未使用 */);
#endif
DEBUG("[out pcm bytes: %d]\n", PCM_ONE_FRAME_SIZE);
fwrite(u8PcmBuf, 1, PCM_ONE_FRAME_SIZE, fpPcm);
}
printf("%s -> %s : Success!\n", argv[1], argv[2]);
exit:
/* 关闭文件 */
if(fpAmr)
fclose(fpAmr);
if(fpPcm)
fclose(fpPcm);
/* 关闭解码器 */
if(vpAmr)
D_IF_exit(vpAmr);
return 0;
}