数字蜂窝系统自适应多码率语音传输编解码器(Adaptive Multi-Rate Speech Codec:AMR)是欧洲电信标准化协会(ETSI)下属的SMG11(Special Mobile Group11)为GSM系统所定制的语音编解码标准,其目的是在半速率信道容量的情况下得到有线语音编解码质量。 AMR作为第四代GSM语音编解码标准,提供了一种自适应的解决方法来跟踪快速变化的无线信道情况和本地流量情况。现今的GSM语音和信道编码器工作在固定码率上,这些码率在设计阶段就已经选定,是理想信道性能和信道误码鲁棒性的一个折衷。另一方面,AMR编码器实时根据信道类型(全速率或半速率)选择多种码率中的一种,从而达到语音编码和信道编码的最优组合以满足瞬时的无线信道条件和本地容量需求。AMR提供了从4.75kbits/s到12.2kbits/s的多种码率选择。AMR凭借其优异的性能成为UMTS和ITU第三代系统候选编码。
GSMAMR Codec适用于对每秒8000次采样,16位,单声道音频数据进行编码。在16位的采样精度下,每一个采样值都是16位的半字,并且线性连续存放。GSMAMR Codec每次对一帧数据(160个采样值)进行编码,根据不同的编码速率,产生编码后的内容长度不同,详见表一。
速率 |
编码后长度(位) |
编码后长度(字节) |
4.75 kbps |
95 |
12 |
5.15 kbps |
103 |
13 |
5.9 kbps |
118 |
15 |
6.7 kbps |
134 |
17 |
7.4 kbps |
148 |
19 |
7.95 kbps |
159 |
20 |
10.2 kbps |
204 |
26 |
12.2 kbps (GSM EFR) |
244 |
31 |
表一
对一帧语音数据编码后,可以形成相应的一个GSMAMR数据帧,如下图:
Frame Type (16 bit) |
Speech Rate (16 bit) |
Speech Content( n*8 bit) |
Frame Type占一个半字,十六位,表示帧类型。
帧类型 |
值 |
NON_SPEECH |
-1 |
SPEECH_GOOD |
0 |
SPEECH_DEGRADED |
1 |
ONSET |
2 |
SPEECH_BAD |
3 |
SID_FIRST |
4 |
SID_UPDATE |
5 |
SID_BAD |
6 |
NO_DATA |
7 |
N_FRAMETYPES |
8 |
Speech Rate占一个半字,十六位,表示编码速率。
编码速率 |
值 |
RATE_UNDEFINED |
-1 |
RATE_4750 |
0 |
RATE_5150 |
1 |
RATE_5900 |
2 |
RATE_6700 |
3 |
RATE_7400 |
4 |
RATE_7950 |
5 |
RATE_10200 |
6 |
RATE_12200 |
7 |
RATE_DTX |
8 |
Speech Content长度由Frame Type和Speech Rate决定,详见表一。
AMR Codec API相关源文件一共有八个,如下表:
文件名 |
功能 |
bitpack.c |
位流打包/解包函数 |
encgsmamr.c |
GSMAMR编码函数 |
decgsmamr.c |
GSMAMR解码函数 |
dtx_util.c |
DTX函数 |
gain_util.c |
增益函数 |
tab_util.c |
GSMAMR编码参数表 |
util.c |
辅助函数 |
owngsmamr.h |
常量定义,类型定义和内联函数定义 |
其中encgsmamr.c和decgsmamr.c为主要文件,面向程序员的函数主要有如下几个:
======================================================================
APIGSMAMR_Status apiGSMAMREncoder_GetSize
(GSMAMREncoder_Obj* encoderObj, unsigned int *pCodecSize)
功能:取得GSMAMR编码对象的大小。
参数定义:
GSMAMREncoder_Obj* encoderObj
GSMAMR编码对象指针。
unsigned int *pCodecSize
存放GSMAMR编码对象的大小的整型数指针。
代码分析:
GSMAMREncoder_Obj 定义如下:
struct _GSMAMREncoder_Obj {
GSMAMRCoder_Obj objPrm;
/* preprocess state */
char *preProc; /* High pass pre processing filter memory */
Encoder_State cod_amr_state;
GSMAMR_Rate_t rate; /* encode rate */
};
GSMAMREncoder_Obj结构体中的GSMAMRCoder_Obj 定义如下:
typedef struct _GSMAMRCoder_Obj{
int objSize;
int key;
int mode; /* encoder mode's */
GSMAMRCodec_Type codecType;
}GSMAMRCoder_Obj;
其中成员变量objSize 即为GSMAMR编码对象的大小,这个变量在函数apiGSMAMREncoder_Init中进行初始化。
======================================================================
APIGSMAMR_Status apiGSMAMREncoder_Alloc
(const GSMAMREnc_Params *gsm_Params, unsigned int *pSizeTab)
功能:计算GSMAMR编码对象需要分配多少内存区域。
参数定义:
const GSMAMREnc_Params *gsm_Params
GSMAMR编码参数指针。
unsigned int *pSizeTab
存放GSMAMR编码对象所需内存区域的大小。
代码分析:
GSMAMR编码参数结构体定义如下:
typedef struct _GSMAMREnc_Params{
GSMAMRCodec_Type codecType;
int mode;
}GSMAMREnc_Params;
GSMAMRCodec_Type为一个枚举类型,表示编码器类型是GSMAMR
typedef enum _GSMAMRCodec_Type{
GSMAMR_CODEC=0
}GSMAMRCodec_Type;
mode表示GSMAMR编码器的编码模式
typedef enum _GSMAMREncode_Mode{
GSMAMREncode_DefaultMode=0, /*缺省模式*/
GSMAMREncode_VAD1_Enabled, /*VAD1模式*/
GSMAMREncode_VAD2_Enabled /*VAD2模式*/
}GSMAMREncode_Mode;
======================================================================
APIGSMAMR_Status apiGSMAMREncoder_Init
(GSMAMREncoder_Obj* encoderObj, unsigned int mode)
功能:初始化GSMAMR编码对象
参数定义:
GSMAMREncoder_Obj* encoderObj
GSMAMR编码对象指针。
unsigned int mode
GSMAMR编码模式,参照如下定义
代码分析:
编码模式为如下三种之一:
typedef enum _GSMAMREncode_Mode{
GSMAMREncode_DefaultMode=0, /*缺省模式*/
GSMAMREncode_VAD1_Enabled, /*VAD1模式*/
GSMAMREncode_VAD2_Enabled /*VAD2模式*/
}GSMAMREncode_Mode;
======================================================================
APIGSMAMR_Status apiGSMAMREncode
(GSMAMREncoder_Obj* encoderObj,const short* src, GSMAMR_Rate_t rate,
unsigned char* dst, int *pVad )
功能:GSMAMR编码
参数定义及分析:
GSMAMREncoder_Obj* encoderObj
GSMAMR编码对象指针。
const short* src
需要进行编码的内容的指针,由于GSMAMR一次对160个采样进行编码(在8000Hz的采样率下是20ms长的语音内容),所以指针长度为160。
GSMAMR_Rate_t rate
采用的编码速率,参照如下定义
typedef enum {
GSMAMR_RATE_UNDEFINED = -1,
GSMAMR_RATE_4750 = 0, /* MR475 4.75 kbps */
GSMAMR_RATE_5150, /* MR515 5.15 kbps */
GSMAMR_RATE_5900, /* MR59 5.9 kbps */
GSMAMR_RATE_6700, /* MR67 6.7 kbps */
GSMAMR_RATE_7400, /* MR74 7.4 kbps */
GSMAMR_RATE_7950, /* MR795 7.95 kbps */
GSMAMR_RATE_10200, /* MR102 10.2 kbps */
GSMAMR_RATE_12200, /* MR122 12.2 kbps (GSM EFR) */
GSMAMR_RATE_DTX /* MRDTX Discontinuous TX mode */
} GSMAMR_Rate_t;
unsigned char* dst
存放编码后的数据。
int *pVad
返回编码的这一个数据帧是否有效,如果有效,返回1,否则返回0。
======================================================================
APIGSMAMR_Status apiGSMAMRDecoder_GetSize
(GSMAMRDecoder_Obj* decoderObj, unsigned int *pCodecSize)
功能:取得GSMAMR解码对象的大小。
参数定义:
GSMAMRDecoder_Obj* decoderObj
GSMAMR解码对象指针。
unsigned int *pCodecSize
存放GSMAMR解码对象的大小的整型数指针。
代码分析:
GSMAMRDecoder_Obj结构体定义如下:
struct _GSMAMRDecoder_Obj {
/* post process state */
GSMAMRCoder_Obj objPrm;
char *postProc; /* High pass post processing filter memory */
Decoder_State decoder_amrState;
/* postfiltering state */
Post_FilterState filt_state;
GSMAMR_Rate_t rate; /* decode rate */
};
GSMAMRDecoder_Obj结构体中的GSMAMRCoder_Obj 定义如下:
typedef struct _GSMAMRCoder_Obj{
int objSize;
int key;
int mode; /* encoder mode's */
GSMAMRCodec_Type codecType;
}GSMAMRCoder_Obj;
其中成员变量objSize 即为GSMAMR解码对象的大小,这个变量在函数apiGSMAMRDecoder_Init中进行初始化。
======================================================================
APIGSMAMR_Status apiGSMAMRDecoder_Alloc
(const GSMAMRDec_Params *gsm_Params, unsigned int *pSizeTab)
功能:计算GSMAMR解码对象需要分配多少内存区域。
参数定义:
const GSMAMRDec_Params *gsm_Params
GSMAMR解码参数指针。
unsigned int *pSizeTab
存放GSMAMR解码对象所需内存区域的大小。
代码分析:
GSMAMR解码参数结构体定义如下:
typedef struct _GSMAMRDec_Params{
GSMAMRCodec_Type codecType;
int mode;
}GSMAMRDec_Params;
GSMAMRCodec_Type为一个枚举类型,表示解码器类型是GSMAMR
typedef enum _GSMAMRCodec_Type{
GSMAMR_CODEC=0
}GSMAMRCodec_Type;
mode表示GSMAMR解码器的解码模式
typedef enum _GSMAMRDecode_Mode{
GSMAMRDecode_DefaultMode=0
}GSMAMRDecode_Mode;
======================================================================
APIGSMAMR_Status apiGSMAMRDecoder_Init
(GSMAMRDecoder_Obj* decoderObj, unsigned int mode)
功能:初始化GSMAMR解码对象
参数定义和分析:
GSMAMRDecoder_Obj* decoderObj
GSMAMR编码对象指针。
unsigned int mode
GSMAMR编码模式,参照如下定义:
typedef enum _GSMAMRDecode_Mode{
GSMAMRDecode_DefaultMode=0
}GSMAMRDecode_Mode;
======================================================================
APIGSMAMR_Status apiGSMAMRDecode
(GSMAMRDecoder_Obj* decoderObj,const unsigned char* src,
GSMAMR_Rate_t rate,RXFrameType rx_type, short* dst)
功能:GSMAMR解码
GSMAMRDecoder_Obj* decoderObj
GSMAMR解码对象指针。
const unsigned char* src
需要进行解码的内容的指针,根据编码速率不同,长度不同。
GSMAMR_Rate_t rate
采用的解码速率,参照如下定义
typedef enum {
GSMAMR_RATE_UNDEFINED = -1,
GSMAMR_RATE_4750 = 0, /* MR475 4.75 kbps */
GSMAMR_RATE_5150, /* MR515 5.15 kbps */
GSMAMR_RATE_5900, /* MR59 5.9 kbps */
GSMAMR_RATE_6700, /* MR67 6.7 kbps */
GSMAMR_RATE_7400, /* MR74 7.4 kbps */
GSMAMR_RATE_7950, /* MR795 7.95 kbps */
GSMAMR_RATE_10200, /* MR102 10.2 kbps */
GSMAMR_RATE_12200, /* MR122 12.2 kbps (GSM EFR) */
GSMAMR_RATE_DTX /* MRDTX Discontinuous TX mode */
} GSMAMR_Rate_t;
RXFrameType rx_type
收到的编码数据帧的类型,参照如下定义
typedef enum {
RX_NON_SPEECH = -1,
RX_SPEECH_GOOD = 0,
RX_SPEECH_DEGRADED,
RX_ONSET,
RX_SPEECH_BAD,
RX_SID_FIRST,
RX_SID_UPDATE,
RX_SID_BAD,
RX_NO_DATA,
RX_N_FRAMETYPES /* number of frame types */
}RXFrameType;
short* dst
存放解码后内容的指针,由于GSMAMR一次解码产生160个采样值(在8000Hz的采样率下是20ms长的语音内容),所以指针长度为160。
为了在sitsang板上使用GSMAMR Codec,我们将其编成动态库,makefile如下
###########################################################
# Change the definitions according to your system setting #
###########################################################
ifndef IPPROOT
IPPROOT := /usr/local/ipp/ippxsc30
endif
###############################
# DO NOT EDIT BELOW THIS LINE #
###############################
# System Specific
INCLSFX=.h
CPPSFX=.c
OBJSFX=.o
EXESFX=
# IPP Dependencies
IPPINCLS=-I$(IPPROOT)/include
IPPLIBS=$(IPPROOT)/lib/ippSC_XSC30LNX_r.a
# Compiler Specifics
CC=arm-linux-gcc
CFLAGS=-Wall -L.
LIBCFLAGS= $(CFLAGS) -fPIC
CCOPTS=-c -O2
CCDEFS=-D_IPP_PCA -DLINUX32
CCINCLS=-I./include $(IPPINCLS)
LNK=arm-linux-ld
LNKOUT=-o
LNKOPTS=
LNKLIBS=$(IPPLIBS)
build: prepare_directory build_sharedlib; @echo "Done all"
##### api object #####
CPPSRC_API=./src/api/bitpack.c /
./src/api/decgsmamr.c /
./src/api/dtx_util.c /
./src/api/encgsmamr.c /
./src/api/gain_util.c /
./src/api/tab_util.c /
./src/api/util.c
OBJECT_API=$(patsubst %$(CPPSFX), %$(OBJSFX), $(CPPSRC_API))
##### ipp object #####
CPPSRC_IPP=./src/_ipp/extra_ipps.c /
./src/_ipp/extra_ippsc.c /
./src/_ipp/owncts.c
OBJECT_IPP=$(patsubst %$(CPPSFX), %$(OBJSFX), $(CPPSRC_IPP))
##### link shared lib #####
OBJECT_SHARED_NAME=libamrcodec.so.1.0.0
OBJECT_SHARED=./lib/$(OBJECT_SHARED_NAME)
SONAME=libamrcodec.so.1
build_sharedlib: $(OBJECT_SHARED) ; @echo "Done Sharedlib!!"
$(OBJECT_SHARED):$(OBJECT_API) $(OBJECT_IPP)
$(CC) -shared -Wl,-soname,$(SONAME) -o$@ $(LNKOPTS) $^ $(LNKLIBS)
cd lib
ln -sf $(OBJECT_SHARED_NAME) ./lib/libamrcodec.so
ln -sf $(OBJECT_SHARED_NAME) ./lib/$(SONAME)
cd ..
##### compile shared lib objects all together #####
OBJECTS=$(OBJECT_API) $(OBJECT_IPP)
$(OBJECTS): %$(OBJSFX): %$(CPPSFX)
$(CC) $(LIBCFLAGS) $(CCINCLS) $(CCDEFS) $(CCOPTS) -o$@ $<
prepare_directory: FORCE
@if test ! -d ./lib; then mkdir ./lib; fi
clean: FORCE
rm -f $(OBJECTS)
distclean: clean
rm -f ./lib/*
FORCE:
这样,可以生成libamrcodec.so,如果其他程序需要调用,编译的时候加上-lamrcodec参数即可。
我们约定,pSpeechIn指向需要编码的音频内容的内存单元的地址,pEncodedOut指向存放编码后数据帧的内存单元的地址。
/*定义编码器对象指针*/
static GSMAMREncoder_Obj *encoder;
/*定义编码模式*/
static int emode = GSMAMREncode_DefaultMode;
/*定义编码速率*/
GSMAMR_Rate_t WrkRate = GSMAMR_RATE_12200;
/*编码参数*/
static GSMAMREnc_Params gsmenc_params;
/*定义临时变量*/
unsigned int eSize=0,vad;
/*需进行编码的音频内容*/
short *pSpeechIn;
/*存放编码结果的地址*/
unsigned char *pEncodedOut;
/*为编码器分配内存空间*/
gsmenc_params.mode = emode;
gsmenc_params.codecType = (GSMAMRCodec_Type)0;
apiGSMAMREncoder_Alloc(&gsmenc_params, &eSize);
if(!(encoder = (GSMAMREncoder_Obj*)ippsMalloc_8u(eSize))) {
printf("/nLow memory: %u bytes not allocated/n",eSize);
exit(1);
}
/*初始化编码器*/
apiGSMAMREncoder_Init((GSMAMREncoder_Obj*)encoder,emode);
/*进行编码*/
apiGSMAMREncode(encoder, pSpeechIn, WrkRate,pEncodedOut+2,&vad);
if(vad) /*如果编码有效*/
{
pEncodedOut[0]=SPEECH_GOOD; /*Set Frame Type*/
pEncodedOut[1]=WrkRate; /*Set Speech Rate*/
}
我们约定,pSpeechOut指向存放解码后音频内容的内存单元的地址,pEncodedIn指需要解码的数据帧的内存单元的地址。
/*定义解码器对象指针*/
static GSMAMRDecoder_Obj *decoder;
/*定义解码模式*/
static int dmode=GSMAMRDecode_DefaultMode;
/*定义解码速率,*/
GSMAMR_Rate_t WrkRate = GSMAMR_RATE_12200;
/*定义帧类型*/
RXFrameType rx_type;
/*编码参数*/
static GSMAMRDec_Params gsmdec_params;
/*定义临时变量*/
unsigned int eSize=0;
/*需进行解码的音频内容*/
unsigned char *pEncodedIn;
/*存放解码结果的地址*/
short *pSpeechOut;
/*为解码器分配内存空间*/
gsmdec_params.mode = dmode;
gsmdec_params.codecType = (GSMAMRCodec_Type)0;
apiGSMAMRDecoder_Alloc(&gsmdec_params, &eSize);
if(!(encoder = (GSMAMRDecoder_Obj*)ippsMalloc_8u(eSize))) {
printf("/nLow memory: %u bytes not allocated/n",eSize);
exit(1);
}
/*初始化解码器*/
apiGSMAMRDecoder_Init((GSMAMRDecoder_Obj*)decoder,dmode);
/*读取帧信息*/
rx_type=(RXFrameType)pEncodedIn[0];
WrkRate=(GSMAMR_Rate_t)pEncodedIn[1];
/*进行编码*/
apiGSMAMRDecode(decoder,pEncodedIn+2, WrkRate, rx_type, pSpeechOut);