目录
1. 概述
3. AMR解码
4. AMR帧读取算法
5. 参考资料
现在很多智能手机都支持多媒体功能,特别是音频和视频播放功能,而 AMR 文件格式是手机端普遍支持的音频文件格式。
AMR ,全称是: Adaptive Multi-Rate ,自适应多速率,是一种音频编码文件格式,专用于有效地压缩语音频率。
AMR 音频主要 用于移动设备的音频压缩,压缩比非常高,但是音质比较差,主要用于语音类的音频压缩,不适合对音质要求较高的音乐类音频的压缩。
AMR 的编解码是基于“ 3GPP AMR Floating-point Speech Codec ”来做的, 3GPP 还专门开放了基于 ANSI-C 实现的编解码代码,便于我们在各种平台上进行移植。
#ifndef amrFileCodec_h
#define amrFileCodec_h
#define AMR_MAGIC_NUMBER "#!AMR/n"
#define PCM_FRAME_SIZE 160 // 8khz 8000*0.02=160
#define MAX_AMR_FRAME_SIZE 32
#define AMR_FRAME_COUNT_PER_SECOND 50
//int amrEncodeMode[] = {4750, 5150, 5900, 6700, 7400, 7950, 10200, 12200}; // amr
编码方式
typedef struct
{
char chChunkID[4];
int nChunkSize;
}XCHUNKHEADER;
typedef struct
{
short nFormatTag;
short nChannels;
int nSamplesPerSec;
int nAvgBytesPerSec;
short nBlockAlign;
short nBitsPerSample;
}WAVEFORMAT;
typedef struct
{
short nFormatTag;
short nChannels;
int nSamplesPerSec;
int nAvgBytesPerSec;
short nBlockAlign;
short nBitsPerSample;
short nExSize;
}WAVEFORMATX;
typedef struct
{
char chRiffID[4];
int nRiffSize;
char chRiffFormat[4];
}RIFFHEADER;
typedef struct
{
char chFmtID[4];
int nFmtSize;
WAVEFORMAT wf;
}FMTBLOCK;
// WAVE
音频采样频率是
8khz
//
音频样本单元数
= 8000*0.02 = 160 (
由采样频率决定
)
//
声道数
1 : 160
// 2 : 160*2 = 320
// bps
决定样本
(sample)
大小
// bps = 8 --> 8
位
unsigned char
// 16 --> 16
位
unsigned short
int EncodeWAVEFileToAMRFile(const char* pchWAVEFilename, const char* pchAMRFileName, int nChannels, int nBitsPerSample);
//
将
AMR
文件解码成
WAVE
文件
int DecodeAMRFileToWAVEFile(const char* pchAMRFileName, const char* pchWAVEFilename);
#endif
3GPP 提供了编码代码,并提供了一个 encoder.c 程序,该程序示范了如何对一个 16 位的单声道 PCM 数据进行压缩的。(采样频率必须是 8khz )
我对该程序进行一定的拓展,数据位支持 8 位和 16 位,可以是单声道和双声道。
l
对于 8 位 PCM 只需要将每个采样的 sample 数据位扩展成 16 位,并左移 7 位。
l
对于双声道,可以只对左声道数据进行处理,也可以只对右声道数据进行处理,或者将左右声道数据求平均值就可。
这样两个小处理,就可以将 PCM 规范成 3PGG 的编码器需要的数据格式。
代码在 amrFileEncoder.c 中。
#include "amrFileCodec.h"
//
从
WAVE
文件中跳过
WAVE
文件头,直接到
PCM
音频数据
void SkipToPCMAudioData(FILE* fpwave)
{
RIFFHEADER riff;
FMTBLOCK fmt;
XCHUNKHEADER chunk;
WAVEFORMATX wfx;
int bDataBlock = 0;
// 1.
读
RIFF
头<