[url=home.php?mod=space&uid=3348146]@21ic小可爱 @21小跑堂 #申请原创#[/url]
昨天看到论坛里面有一条寻求“G.723/G.729音频编解码C语言实现 ”的外包项目,然后回想到之前做的视频传输项目中也有音频传输的部分,于是就大概分享一下本人对音频G.723音频解码的理解。
插一段知识普及,哈哈:G.723是ITU-T在1996年制订成型的一种多媒体语音编解码标准。其典型应用包括VoIP服务、H.324视频电话、无线电话、数字卫星系统、数电倍增设备(DCME)、公共交换电话网(PSTN)、ISDN及各种多媒体语音信息产品。G.723的特点是能以相对32kbpsPCM极低的码率获得与PCM同等的语音质量,并且具有检错、纠错、节省带宽的功能。G.723的变码方式码率有6.3kbps和5.3kbps两种,高速率(6.3kbps)具有较高的重建语音质量,而低码率(5.3kbps)的计算复杂度较低,所以重建语言质量虽然不如前者,但是传输的延迟性也低。
本次分享的主要是G.723协议的音频处理方式,因此不过多赘述基础知识和硬件的问题。G.723编码器采用LPC合成-分析法和感觉加权误差最小化原理编码。流程图可参考下图:
编码流程图(1)
G.723解码器是以帧为单位进行解码的。编码器输出的基音周期和差分值都被传送到解码器。首先通过激励解码器,基音解码器和LSP解码器对量化的LPC进行解码,构造LPC合成滤波器,然后进行后波处理和放大。解码流程图如下:
解码流程图(2)
好了,接下来实战:
先简单介绍一个基于G.723的音频编解码C语言实现例子,有助于大家理解。过程包括读取PCM音频数据、G.723压缩、G.723解压缩和写入PCM音频数据
首先,需要包含相关的头文件: #include <stdio.h> #include <stdlib.h> #include <string.h> #include "g723.h" 接下来,定义一些常量和函数: #define INPUT_FILE "input.pcm" #define OUTPUT_FILE "output.pcm" #define COMPRESSED_FILE"compressed.g723" #define DECOMPRESSED_FILE"decompressed.pcm" #define FRAME_SIZE 24 void read_pcm_data(char *filename, short*data, int len) { FILE *fp = fopen(filename, "rb"); if (fp == NULL) { printf("failed to open file: %s\n", filename); exit(1); } fread(data, sizeof(short), len, fp); fclose(fp); } void write_pcm_data(char *filename, short*data, int len) { FILE *fp = fopen(filename, "wb"); if (fp == NULL) { printf("failed to open file: %s\n", filename); exit(1); } fwrite(data, sizeof(short), len, fp); fclose(fp); } void write_compressed_data(char *filename,char *data, int len) { FILE *fp = fopen(filename, "wb"); if (fp == NULL) { printf("failed to open file: %s\n", filename); exit(1); } fwrite(data, 1, len, fp); fclose(fp); } void read_compressed_data(char *filename,char *data, int len) { FILE *fp = fopen(filename, "rb"); if (fp == NULL) { printf("failed to open file: %s\n", filename); exit(1); } fread(data, 1, len, fp); fclose(fp); } 其中,read_pcm_data和write_pcm_data函数用于读取和写入PCM音频数据,write_compressed_data和read_compressed_data函数分别用于写入和读取G.723压缩后的数据。 接下来,定义G.723压缩和解压缩的函数: void g723_compress(short *in, char *out,int len) { G723_state_t state; int i, j, k; short *speech = in; unsigned char *bitstream = (unsigned char *)out; int nframes = len / FRAME_SIZE; Init_Codeword_Tbl(); G723_Init_Encoder(&state); for (i = 0, j = 0; i < nframes; i++, j += 24) { G723_Encode(&speech[i * FRAME_SIZE], bitstream + j, &state); } k= len % FRAME_SIZE; if (k > 0) { memset(speech + nframes * FRAME_SIZE, 0, FRAME_SIZE - k); G723_Encode(&speech[nframes * FRAME_SIZE], bitstream + j,&state); } } void g723_decompress(char *in, short *out,int len) { G723_state_t state; int i, j, k; unsigned char *bitstream = (unsigned char *)in; short *synth = out; int nframes = len / FRAME_SIZE; Init_Codeword_Tbl(); G723_Init_Decoder(&state); for (i = 0, j = 0; i < nframes; i++, j += 24) { G723_Decode(bitstream + j, &synth[i * FRAME_SIZE], &state); } k= len % FRAME_SIZE; if(k > 0) { memset(synth + nframes * FRAME_SIZE, 0, FRAME_SIZE - k); G723_Decode(bitstream + j, &synth[nframes * FRAME_SIZE],&state); } } 其中,Init_Codeword_Tbl函数用于初始化码本表,G723_Init_Encoder和G723_Init_Decoder函数分别用于初始化编码器和解码器的状态,G723_Encode和G723_Decode函数分别用于压缩和解压缩一帧音频数据。 最后,主函数中调用以上函数: int main() { short in[8000]; short out[8000]; char compressed[1000]; char decompressed[8000]; int len = 8000; read_pcm_data(INPUT_FILE, in, len); g723_compress(in, compressed, len); write_compressed_data(COMPRESSED_FILE, compressed, 1000); read_compressed_data(COMPRESSED_FILE, compressed, 1000); g723_decompress(compressed, out, len); write_pcm_data(DECOMPRESSED_FILE, out, len); return 0; } 下面写一个完整的参考程序:
G.723有两种码率:6.3kbps和5.3kbps。以下是6.3kbps码率的C语言实现示例: #include <math.h> #include <stdio.h> #define N 24 #define L 240 #define M 60 #define B 40 #define A 8 int sgn(int x) { return (x >= 0) ? 1 : -1; } void adpcm_coder(int *x, int *y, int *i,int *q) { int d[N], e[N], f[N], g[N], h[N]; int j, k, l, m, n, o; for (j = 0; j < N; j++) { d[j] = x[j] - y[j]; } e[0] = d[0]; e[1] = d[1]; e[2] = d[2]; e[3] = d[3] - (77 * e[1] + 51 * e[0] + 38 * d[0]) / 128; e[4] = d[4] - (77 * e[2] + 51 * e[1] + 38 * d[1]) / 128; e[5] = d[5] - (77 * e[3] + 51 * e[2] + 38 * d[2]) / 128; e[6] = d[6] - (77 * e[4] + 51 * e[3] + 38 * d[3]) / 128; e[7] = d[7] - (77 * e[5] + 51 * e[4] + 38 *d[4]) / 128; e[8] = d[8] - (77 * e[6] + 51 * e[5] + 38 * d[5]) / 128; e[9] = d[9] - (77 * e[7] + 51 * e[6] + 38 * d[6]) / 128; e[10] = d[10] - (77 * e[8] + 51 * e[7] + 38 * d[7]) / 128; e[11] = d[11] - (77 * e[9] + 51 * e[8] + 38 * d[8]) / 128; for (j = 12; j < N; j++) { e[j] = d[j] - (77 * e[j - 10] + 51 * e[j - 11] + 38 * e[j - 12]) / 128; } for (j = 0; j < N; j++) { f[j] = sgn(e[j]); g[j] = abs(e[j]); h[j] = (int)(log(g[j] + 1) / log(2) +0.5); if (h[j] < 1) { h[j] = 1; } if (f[j] < 0) { h[j] = -h[j]; } } for (j = 0; j < N; j += 2) { k = 0; for (l = 0; l < 6; l++) { k += (h[j + l] << (5 * l)); } i[j / 2] = k; } n= i[0]; o= i[1]; for (j = 0; j < N; j++) { m = (n >> (5 * j)) & 31; q[j] = (int)(pow(2, abs(m)) - 1 + 0.5); if (m < 0) { q[j] = -q[j]; } m = (o >> (5 * j)) & 31; q[j + N] = (int)(pow(2, abs(m)) - 1 + 0.5); if (m < 0) { q[j + N] = -q[j + N]; } } } void adpcm_decoder(int *q, int *y, int *i){ int d[N], e[N], f[N], g[N], h[N]; int j, k, l, m, n, o; n= i[0]; o= i[1]; for (j = 0; j < N; j++) { m = q[j]; if (m < 0) { m = -m; } k = (int)(log(m + 1) / log(2) + 0.5); if (k < 1) { k = 1; } if (q[j] < 0) { k = -k; } n += (k << (5 * j)); m = q[j + N]; if (m < 0) { m = -m; } k = (int)(log(m + 1) / log(2) + 0.5); if (k < 1) { k = 1; } if (q[j + N] < 0) { k = -k; } o += (k << (5 * j)); } for (j = 0; j < N; j += 2) { k = (n >> (5 * j)) & 31; l = (o >> (5 * j)) & 31; d[j] = (q[j] >= 0) ? q[j] : -q[j]; d[j + 1] = (q[j + N] >= 0) ? q[j + N] : -q[j + N]; e[j] = (d[j] + 1) * (3 * (k % 3) + 1) / 8; e[j + 1] = (d[j + 1] + 1) * (3 * (l % 3) + 1) / 8; f[j] = (k / 3) % 4; f[j + 1] = (l / 3) % 4; g[j] = ((k / 12) << 1) + (f[j] >> 1); g[j + 1] = ((l / 12) << 1)+ (f[j + 1] >> 1); h[j] = (k >= 24) ? 2 : (k / 8); h[j + 1] = (l >= 24) ? 2 : (l / 8); } y[0] = e[0] + e[1] + e[2] + e[3] + e[4] + e[5] + e[6] + e[7]; y[1] = e[0] - e[1] - e[3] - e[5] - e[7]; y[2] = e[0] - e[2] - e[6]; y[3] = e[0] + e[1] + e[3] + e[5] + e[7]; y[4] = e[0] + e[2] + e[6]; y[5] = e[0] + e[1] - e[3] - e[5] + e[7]; y[6] = e[0] - e[2] + e[6]; y[7] = e[0] - e[1] + e[3] - e[5] + e[7]; for (j = 8; j < L; j += 8) { y[j] = (g[j] == 0) ? y[j - 8] + ((h[j] * y[j - 8]) / 8) : (g[j] == 1) ?y[j - 8] - ((h[j] * y[j - 8]) / 8) : (g[j] == 2) ? y[j - 8] + ((h[j] * y[j -16]) / 8) : y[j - 8] - ((h[j] * y[j - 16]) / 8); y[j + 1] = (g[j + 1] == 0) ? y[j] + ((h[j + 1] * y[j - 7]) / 8) : (g[j +1] == 1) ? y[j] - ((h[j + 1] * y[j - 7]) / 8) : (g[j + 1] == 2) ? y[j] + ((h[j+ 1] * y[j - 15]) / 8) : y[j] - ((h[j + 1] * y[j - 15]) / 8); y[j + 2] = (g[j + 2] == 0) ? y[j + 1] + ((h[j + 2] * y[j - 6]) / 8) :(g[j + 2] == 1) ? y[j + 1] - ((h[j + 2] * y[j - 6]) / 8) : (g[j + 2] == 2) ?y[j + 1] + ((h[j + 2] * y[j - 14]) / 8) : y[j + 1] - ((h[j + 2] * y[j - 14]) /8); y[j + 3] = (g[j + 3] == 0) ? y[j + 2] + ((h[j + 3] * y[j - 5]) / 8) :(g[j + 3] == 1) ? y[j + 2] - ((h[j + 3] * y[j - 5]) / 8) : (g[j + 3] == 2) ?y[j + 2] + ((h[j + 3] * y[j - 13]) / 8) : y[j + 2] - ((h[j + 3] * y[j - 13]) /8); y[j + 4] = (g[j + 4] == 0) ? y[j + 3] + ((h[j + 4] * y[j - 4]) / 8) :(g[j + 4] == 1) ? y[j + 3] - ((h[j + 4] * y[j - 4]) / 8) : (g[j + 4] == 2) ?y[j + 3] + ((h[j + 4] * y[j - 12]) / 8) : y[j + 3] - ((h[j + 4] * y[j - 12]) /8); y[j + 5] = (g[j + 5] == 0) ? y[j + 4] + ((h[j + 5] * y[j - 3]) / 8) :(g[j + 5] == 1) ? y[j + 4] - ((h[j + 5] * y[j - 3]) / 8) : (g[j + 5] == 2) ?y[j + 4] + ((h[j + 5] * y[j - 11]) / 8) : y[j + 4] - ((h[j + 5] * y[j - 11]) /8); y[j + 6] = (g[j + 6] == 0) ? y[j + 5] + ((h[j + 6] * y[j - 2]) / 8) :(g[j + 6] == 1) ? y[j + 5] - ((h[j + 6] * y[j - 2]) / 8) : (g[j + 6] == 2) ?y[j + 5] + ((h[j + 6] * y[j - 10]) / 8) : y[j + 5] - ((h[j + 6] * y[j - 10]) /8); y[j + 7] = (g[j + 7] == 0) ? y[j + 6] + ((h[j + 7] * y[j - 1]) / 8) :(g[j + 7] == 1) ? y[j + 6] - ((h[j + 7] * y[j - 1]) / 8) : (g[j + 7] == 2) ?y[j + 6] + ((h[j + 7] * y[j - 9]) / 8) : y[j + 6] - ((h[j + 7] * y[j - 9]) /8); } } int main() { int x[L], y[L], q[N * 2], i[N / 2]; int j; for (j = 0; j < L; j++) { x[j] = 0; // 填充输入数据 y[j] = 0; // 初始化解码器状态 } for (j = 0; j < N / 2; j++) { i[j] = 0; // 初始化编码器状态 } adpcm_coder(x, y, i, q); // 编码 for (j = 0; j < N / 2; j++) { i[j] = 0; // 初始化解码器状态 } adpcm_decoder(q, y, i); // 解码 for (j = 0; j < L; j++) { printf("%d ", y[j]); // 输出解码结果 } return 0; } 总结,以上介绍的仅仅是G.723编码和解码的基本知识,可以实现对语音信号的编码和解码。实际上打包过程和解码过程软件中还需要做很多的处理,比如输入信号的滤波处理和重建,避免解码后输出音频失真。
图(3)
|
简述G.723音频解码原理以及简单的C语言实现,记得下次代码用代码编辑器插入哦,阅读性会更好。