发新帖本帖赏金 20.00元(功能说明)我要提问
返回列表
打印
[技术讨论]

整天语音网聊,却搞不懂音频解编码?今天聊聊G.723解编码

[复制链接]
588|1
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
月上|  楼主 | 2023-4-12 17:10 | 只看该作者 |只看大图 回帖奖励 |倒序浏览 |阅读模式
[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_datawrite_pcm_data函数用于读取和写入PCM音频数据,write_compressed_dataread_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_EncoderG723_Init_Decoder函数分别用于初始化编码器和解码器的状态,G723_EncodeG723_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.3kbps5.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)



01.jpg (69.59 KB )

借用一下图片,请见谅

借用一下图片,请见谅

使用特权

评论回复

打赏榜单

21小跑堂 打赏了 20.00 元 2023-04-13
理由:恭喜通过原创审核!期待您更多的原创作品~

评论
21小跑堂 2023-4-13 10:45 回复TA
简述G.723音频解码原理以及简单的C语言实现,记得下次代码用代码编辑器插入哦,阅读性会更好。 

相关帖子

发新帖 本帖赏金 20.00元(功能说明)我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

4

主题

26

帖子

0

粉丝