前面两节讲完了g729基音周期搜索的基本流程与依据
这里结合代码,进一步讲述这一过程
整数基音周期搜索比较简单
分成三段搜索,每段的权值不同,这在之前分析过了,不详述了
分数基音周期搜索是针对每个子帧进行的
每个子帧在开环基音周期附近,搜索闭环基音周期
首先构造出冲激响应 W(z)/A(z) 即感知加权滤器和综合滤波器
串联而成的系统
代码片段:
/*---------------------------------------------------------------*
* Find the weighted LPC coefficients for the weighting filter. *
*---------------------------------------------------------------*/
Weight_Az(A, gamma1[i_gamma], M, Ap1);
Weight_Az(A, gamma2[i_gamma], M, Ap2);
i_gamma = add(i_gamma,1);
/*---------------------------------------------------------------*
* Compute impulse response, h1[], of weighted synthesis filter *
*---------------------------------------------------------------*/
//lsc 有单位冲激响应去激励这样的一个系统 (1/Aq(z)) * (Ap1(z)/Ap2(z)), 即,将量化后的预测系数A 与 感知加权滤波器做一个串联,得到一个冲激响应
for (i = 0; i <= M; i++) {
ai_zero[i] = Ap1[i];
}
//lsc 这里得到完整的冲激响应,加入感知加权滤波,用未量化的Az系数生成感知加权,然后用经由量化的Az系数滤波
Syn_filt(Aq, ai_zero, h1, L_SUBFR, zero, 0);
Syn_filt(Ap2, h1, h1, L_SUBFR, zero, 0);
得到的冲激响应保存在h1数组当中
根据这个冲激响应,得到残差信号(激励信号),并推算出扣除零输入响应的目标语音信号xn
代码片段:
//lsc 这里计算目标向量,有可能做了去除零输入响应之类的操作,先用包含零输入响应的信号滤波(量化后的Az系数),得到残差信号
Residu(Aq, &speech[i_subfr], &exc[i_subfr], L_SUBFR); /* LPC residual */
//lsc 再用残差信号还原,就得到去除零输入响应的语音信息
Syn_filt(Aq, &exc[i_subfr], error, L_SUBFR, mem_err, 0);
//lsc 对语音信号进行感知加权(采用的是未量化的Az系数)
Residu(Ap1, error, xn, L_SUBFR);
Syn_filt(Ap2, xn, xn, L_SUBFR, mem_w0, 0); /* target signal xn[]*/
接下来是这个函数:
Pitch_fr3
它负责闭环基音周期搜索,如必要再进一步做更精细的分数基音延迟搜索
(当基音周基小于84就需要搜索分数延迟的基音周期,保证在基音周期较短的情况下,得到的自适应激励更精确一些)
调用:
//lsc 进行更精细的分数基音延迟搜索(每个子帧)
T0 = Pitch_fr3(&exc[i_subfr], xn, h1, L_SUBFR, T0_min, T0_max,
i_subfr, &T0_frac);
TO_min TO_max 是开环基音周期TO-3 TO+6得到的,
Pitch_fr3 在这个范围内进一步搜索自相关最大的基音延迟
与开环用信号的自相关最大作为搜索依据不同,
闭环搜索的依据是自适应激励与h1卷积与目标信号的相关最大(这点与g723的五阶闭环基音周期搜索类似)
Norm_Corr就是计算
39
Σ x[n] * yk[n]
n=0
corr(k)= -----------------------
39
开方( Σ yk[n]*yk[n])
n=0
其中 x[n]是目标语音信号 yk[n]是自适应激励与h1卷积 自然yk[n]应该有九组
而我们就是使找出corr[k]最大的那个yk[n] 对应的,也就是找到了最佳的自适应激励
Norm_Corr中有一小段代码,与g723的类似,为了节省计算量,
由于每个循环我们都在不断更新yk[n],而这个可以根据卷积的性质做一些化简,代码片段如下:
k=sub(k,1);//lsc 这里往 t_max方向移动一格,然后再更新卷积,参照723的作法,节省一些运算量
for (j = L_subfr-(Word16)1; j > 0; j--)
{
s = L_mult(exc[k], h[j]);
s = L_shl(s, h_fac); /* h is in Q(12-scaling) */
s_excf[j] = add(extract_h(s), s_excf[j-1]);
}
s_excf[0] = shr(exc[k], scaling);
这个类似的技巧在分析g723时分析过了,这里不再详细说明,读者只需要注意一下即可
接下来说是找corr(k)最大的
代码片段如下:
//lsc 在基音周期附近,抽取历史激励源,与h1进行卷积,最后得到的结果与xn进行关联,找出关联最大的那个,认为是闭环基音搜索的结果
for(i= t0_min+(Word16)1; i<=t0_max; i++)
{
if( sub(corr[i], max) >= 0)
{
max = corr[i];
lag = i;
}
}
比较简单的代码,不多分析了.
然后就是分数基音延迟搜索了,因为搜索涉及到升抽样,
g729采用了一些取巧的做法,即直接对互相关的结果corr(k)这个序列进行升抽样.
Interpol_3 这个函数就是负责升抽样的,原来在上两节讲过了,就是将序列与sinc函数进行卷积.
应该注意 corr(k)这个序列因为要进行升抽样,而间隔要被拉开,所以对应的inter_3在循环跳变时的间隔为UP_SAMP
而本身sinc函数是关于y轴对称的,以及一些非因果系统的原因,卷积的过程会比较怪异,但它终究只是在计算卷积.
小技巧就不分析了
代码片段如下:
Word16 Interpol_3( /* (o) : interpolated value */
Word16 *x, /* (i) : input vector */
Word16 frac /* (i) : fraction */
)
{//lsc 这个是升抽样, 通信信息与sinc函数卷积来达到升抽样的目的
//lsc 抽样的过程,在频域的表现,是将原始信号的频域按抽样频率复制多份,要还原信号,
//lsc 我们可以通过在频域上还原信号,即把抽样后的信号做一个低通滤波,去掉重复的高频成份,就达到还原的目的了
//lsc 这段代码就是在做这些事情,而频域上的矩形滤波器对应时域的信号就是sinc
//lsc 在时域上,只要将原始信号与sinc进行卷积,就完成了升抽样的工作
Word16 i, k;
Word16 *x1, *x2, *c1, *c2;
Word32 s;
if(frac < 0)
{
frac = add(frac, UP_SAMP);
x--;
}
x1 = &x[0];
x2 = &x[1];
c1 = &inter_3[frac];
c2 = &inter_3[sub(UP_SAMP,frac)];
s = 0;
for(i=0, k=0; i< L_INTER4; i++, k+=UP_SAMP)
{
s = L_mac(s, x1[-i], c1[k]);//lsc sinc是关于y轴对称的,所以 x1[-i] x2[i] 而k的跳跃数为UP_SAMP则是因为升抽样,每个x[i]之间的间隔变宽了3格
s = L_mac(s, x2[i], c2[k]);
}
return round(s);
}
得到corr(k)的升抽样后,处理就简单了,循环3次,最大的那个corr(k)就是分数基音延迟了
代码片段:
//lsc 为了计算分数延迟,这里可能做了插值,升抽样的处理
max = Interpol_3(&corr[lag], -2);
frac = -2;
for (i = -1; i <= 2; i++)
{
corr_int = Interpol_3(&corr[lag], i);
if (sub(corr_int, max) > 0)
{
max = corr_int;
frac = i;
}
}
扫尾工作,代码片段如下:
//lsc 基音周期参数打包
index = Enc_lag3(T0, T0_frac, &T0_min, &T0_max,PIT_MIN,PIT_MAX,i_subfr);
//lsc 奇偶校验?
*ana++ = index;
if (i_subfr == 0) {
*ana++ = Parity_Pitch(index);
}
然后获取自适应激励,这里也涉及到升抽样,原理与Interpol_3一样,
代码片段如下:
//lsc 对最佳激励进行升抽样
Pred_lt_3(&exc[i_subfr], T0, T0_frac, L_SUBFR);
//lsc 升抽样后的历史激励与冲激响应卷积
Convolve(&exc[i_subfr], h1, y1, L_SUBFR);
Pred_lt_3 就是对历史激励源进行升抽样,然后取出一组激励作为自适应激励源,
可以看出g729对自适应激励作了化简,当基音周期较短时,引入分数基音延迟,这个对应g723的五阶加权闭环基音预测.
当基音周期较大时,则简化了处理,当取自适应激励时,我们仍然看到做了升抽样,这在一定程度上,仍然是对应g723
的五阶加权.
至此,g729编码部分分析得差不多了,剩下的是固定码本搜索,这一点与g723的固定码本搜索算法几乎是一模一样的.
g729与g723另一个不同的是,增益的估值与量化,这些笔者将在后继的章节分析.
林绍川
2012.4.17于杭州