作用:将PCM样本变换到32个子带的频域信号。
首先,对窗口中的512个样本 X [ i ] , i = 1 , 2 , ⋯ , 511 X[i],i = 1,2,\cdots,511 X[i],i=1,2,⋯,511进行计算
Z [ i ] = C [ i ] ⋅ X [ i ] , i = 1 , 2 , ⋯ , 511 Z[i] = C[i] \cdot X[i] ,i=1,2,\cdots,511 Z[i]=C[i]⋅X[i],i=1,2,⋯,511
将样本分成64个分组,并计算64个 Y k Y_k Yk值。
Y [ k ] = ∑ j = 0 7 Z [ k + 64 j ] Y[k]=\sum^7_{j=0}Z[k+64j] Y[k]=j=0∑7Z[k+64j]
计算32个子带样本:
S [ i ] = ∑ k = 0 63 Y [ k ] ⋅ M [ i ] [ k ] S[i]=\sum^{63}_{k =0}Y[k]\cdot M[i][k] S[i]=k=0∑63Y[k]⋅M[i][k]
多项滤波器组的公式 S [ i ] S[i] S[i]可以由卷积公式代替:
S t [ i ] = ∑ n = 0 511 X [ n − t ] h i [ n ] S_t[i]=\sum^{511}_{n=0}X[n-t]h_i[n] St[i]=n=0∑511X[n−t]hi[n]
其中 h i [ n ] = h [ n ] × c o s ( ( 2 i + 1 ) ( k − 16 ) π 64 ) h_i[n]=h[n]\times cos(\frac{(2i+1)(k-16)\pi}{64}) hi[n]=h[n]×cos(64(2i+1)(k−16)π),当 n 64 \frac{n}{64} 64n的整数部分(也就是所在组的组号)为奇数时, h [ n ] = − C [ n ] h[n]=-C[n] h[n]=−C[n],饭之 h [ n ] = C [ n ] h[n]=C[n] h[n]=C[n]
缺点
通过子带分析滤波器组使信号具有高的时间分辨率,确保在短暂冲击信号情况下,编码的声音信号具有足够高的质量。同时,令信号通过多点FFT运算具有高的频率分辨率,因为掩蔽阈值是从功率谱密度推出来的。在低频子带中,为了保护音调和共振峰的结构,就要求用较小的量化阶、较多的量化级数,即分配较多的位数来表示样本值。而话音中的摩擦音和类似噪声的声音,通常出现在高频子带中,对它分配较少的位数。而具体如何分配,则是要参考码率和心理声学模型。
作用:计算信号中不可听觉感知的部分
MPEG-1标准定义了两个心里声学模型。
心理声学模型1
特点:
心理声学模型2
特点:
但实际实现的模型复杂度取决所需要的压缩因子。如大的压缩因子不重要,则可以完全不用心理声学模型。此时位分配算法不使用SMR( Signal Mask Ratio ),而是使用SNR。
作用:根据心理声学模型的计算结果,为每个子带信号分配比特数。
在调整到固定的码率之前要先确定可用于样值编码的有效比特数。这个数值取决于比例因子、比例因子选择信息、比特分配信息以及辅助数据所需比特。
码率分配的过程
算法:循环,直到没有比特可用
与Layer I类似,但对Layer I有增强
变量 | 含义 |
---|---|
window_subband | 某个子带的滑动窗口 |
filter_subband | 某个子带的滤波器 |
combine_LR | 结合左右声道 |
_scale_factor_calc | 缩放因子计算 |
psycho_i, psycho_ii | 心理声学模型I或II |
_main_bit_allocation | 比特分配 |
_CRC_calc | 冗余校验计算 |
_encode_bit_alloc | 编码比特分配 |
_encode_scale | 编码比例因子 |
transmission_pattern | 传送图样 |
_subband_quantization | 子带量化 |
_sample_encoding | 对采样进行编码 |
程序大体上可以分为八个主要部分:
感知音频编码主要分为两条线。
第一条线为红色部分,实际上是比较常规的想法。即将一个信号按照人耳的听力特点,分成32个子带,并对每个子带中的频率成分进行量化。最后进一步通过颗粒形成来进一步减小数据量流。
第二条线则是继续深挖人耳的听力特点。由于人耳有听力掩蔽效应,所以对于人耳听不到的频率干脆不进行编码,同时对于人耳敏感的区域分配较多bit,以保证每个bit都能起到最大的效果。通过这种方式来使量化更加高效,进而使得总数据量减小。
此外,频域分辨力与时域分辨力有着矛盾,当增大时域分辨力时,会对频域分辨力造成不利影响。于是就需要选择一个较为折中的方案。
详细原理在原理部分已经说明,流程可以参考流程图。这里主要结合代码进行分析。
k为通道数,t选择三个比例因子中的一个,i为需要参与比较的子带数。在所有子带中找到最大值,并通过二分法,在表格中找到大于等于最大值的量化值。
void scale_factor_calc(double sb_sample[][3][SCALE_BLOCK][SBLIMIT],
unsigned int scalar[][3][SBLIMIT], int nch,
int sblimit)
{
/* Optimized to use binary search instead of linear scan through the
scalefactor table; guarantees to find scalefactor in only 5
jumps/comparisons and not in {0 (lin. best) to 63 (lin. worst)}.
Scalefactors for subbands > sblimit are no longer computed.
Uses a single sblimit-loop.
Patrick De Smet Oct 1999.
*/
int k, t;
/* Using '--' loops to avoid possible "cmp value + bne/beq" compiler */
/* inefficiencies. Below loops should compile to "bne/beq" only code */
for (k = nch; k--;)
for (t = 3; t--;) {
int i;
for (i = sblimit; i--;) {
int j;
unsigned int l;
register double temp;
unsigned int scale_fac;
/* Determination of max. over each set of 12 subband samples: */
/* PDS TODO: maybe this could/should ??!! be integrated into */
/* the subband filtering routines? */
register double cur_max = fabs(sb_sample[k][t][SCALE_BLOCK - 1][i]);
for (j = SCALE_BLOCK - 1; j--;) {
if ((temp = fabs(sb_sample[k][t][j][i])) > cur_max)
cur_max = temp;
}
//在所有采样值寻找最大的一个。
/* PDS: binary search in the scalefactor table: */
/* This is the real speed up: */
for (l = 16, scale_fac = 32; l; l >>= 1) {
if (cur_max <= multiple[scale_fac])
scale_fac += l;
else
scale_fac -= l;
}
if (cur_max > multiple[scale_fac])
scale_fac--;
scalar[k][t][i] = scale_fac;
}
}
}
以一个较简单的心理声学模型为例,首先找到每个子带中最小的ATH。随后,对于每个子带或通道找到最小的缩放因子。最后按照公式进行计算。
void psycho_0(double SMR[2][SBLIMIT], int nch, unsigned int scalar[2][3][SBLIMIT], FLOAT sfreq) {
int ch, sb, gr;
int minscaleindex[2][SBLIMIT]; /* Smaller scale indexes mean bigger scalefactors */
static FLOAT ath_min[SBLIMIT];
int i;
static int init = 0;
if (!init) {
FLOAT freqperline = sfreq / 1024.0;
for (sb = 0; sb < SBLIMIT; sb++) {
ath_min[sb] = 1000; /* set it huge */
}
/* Find the minimum ATH in each subband */
for (i = 0; i < 512; i++) {
FLOAT thisfreq = i * freqperline;
FLOAT ath_val = ATH_dB(thisfreq, 0);
if (ath_val < ath_min[i >> 4])
ath_min[i >> 4] = ath_val;
}
init++;
}
/* Find the minimum scalefactor index for each ch/sb */
for (ch = 0; ch < nch; ch++)
for (sb = 0; sb < SBLIMIT; sb++)
minscaleindex[ch][sb] = scalar[ch][0][sb];
for (ch = 0; ch < nch; ch++)
for (gr = 1; gr < 3; gr++)
for (sb = 0; sb < SBLIMIT; sb++)
if (minscaleindex[ch][sb] > scalar[ch][gr][sb])
minscaleindex[ch][sb] = scalar[ch][gr][sb];
/* Oh yeah. Fudge the hell out of the SMR calculations
by combining the scalefactor table index and the min ATH in that subband
There are probably more elegant/correct ways of combining these values,
but who cares? It works pretty well
MFC Mar 03 */
for (ch = 0; ch < nch; ch++)
for (sb = 0; sb < SBLIMIT; sb++)
SMR[ch][sb] = 2.0 * (30.0 - minscaleindex[ch][sb]) - ath_min[sb];
}
首先找到具有最大MNR的子带。如果找到了,就开始对他分配bit。
int a_bit_allocation(double perm_smr[2][SBLIMIT],
unsigned int scfsi[2][SBLIMIT],
unsigned int bit_alloc[2][SBLIMIT], int* adb,
frame_info* frame)
{
int i, min_ch, min_sb, oth_ch, k, increment, scale, seli, ba;
int bspl, bscf, bsel, ad, bbal = 0;
double mnr[2][SBLIMIT];
char used[2][SBLIMIT];
int nch = frame->nch;
int sblimit = frame->sblimit;
int jsbound = frame->jsbound;
al_table* alloc = frame->alloc;
static char init = 0;
static int banc = 32, berr = 0;
static int sfsPerScfsi[] = { 3, 2, 1, 2 }; /* lookup # sfs per scfsi */
if (!init) {
init = 1;
if (frame->header->error_protection)
berr = 16; /* added 92-08-11 shn */
}
for (i = 0; i < jsbound; ++i)
bbal += nch * (*alloc)[i][0].bits;
for (i = jsbound; i < sblimit; ++i)
bbal += (*alloc)[i][0].bits;
*adb -= bbal + berr + banc;
ad = *adb;
for (i = 0; i < sblimit; i++)
for (k = 0; k < nch; k++) {
mnr[k][i] = snr[0] - perm_smr[k][i];
bit_alloc[k][i] = 0;
used[k][i] = 0;
}
bspl = bscf = bsel = 0;
do {
/* locate the subband with minimum SMR */
maxmnr(mnr, used, sblimit, nch, &min_sb, &min_ch);
if (min_sb > -1) { /* there was something to find */
/* find increase in bit allocation in subband [min] */
increment =
SCALE_BLOCK * ((*alloc)[min_sb][bit_alloc[min_ch][min_sb] + 1].group *
(*alloc)[min_sb][bit_alloc[min_ch][min_sb] + 1].bits);
if (used[min_ch][min_sb])
increment -=
SCALE_BLOCK * ((*alloc)[min_sb][bit_alloc[min_ch][min_sb]].group *
(*alloc)[min_sb][bit_alloc[min_ch][min_sb]].bits);
/* scale factor bits required for subband [min] */
oth_ch = 1 - min_ch; /* above js bound, need both chans */
if (used[min_ch][min_sb])
scale = seli = 0;
else { /* this channel had no bits or scfs before */
seli = 2;
scale = 6 * sfsPerScfsi[scfsi[min_ch][min_sb]];
if (nch == 2 && min_sb >= jsbound) {
/* each new js sb has L+R scfsis */
seli += 2;
scale += 6 * sfsPerScfsi[scfsi[oth_ch][min_sb]];
}
}
/* check to see enough bits were available for */
/* increasing resolution in the minimum band */
if (ad >= bspl + bscf + bsel + seli + scale + increment) {
ba = ++bit_alloc[min_ch][min_sb]; /* next up alloc */
bspl += increment; /* bits for subband sample */
bscf += scale; /* bits for scale factor */
bsel += seli; /* bits for scfsi code */
used[min_ch][min_sb] = 1; /* subband has bits */
mnr[min_ch][min_sb] =
-perm_smr[min_ch][min_sb] + snr[(*alloc)[min_sb][ba].quant + 1];
/* Check if subband has been fully allocated max bits */
if (ba >= (1 << (*alloc)[min_sb][0].bits) - 1)
used[min_ch][min_sb] = 2; /* don't let this sb get any more bits */
}
else
used[min_ch][min_sb] = 2; /* can't increase this alloc */
if (min_sb >= jsbound && nch == 2) {
/* above jsbound, alloc applies L+R */
ba = bit_alloc[oth_ch][min_sb] = bit_alloc[min_ch][min_sb];
used[oth_ch][min_sb] = used[min_ch][min_sb];
mnr[oth_ch][min_sb] =
-perm_smr[oth_ch][min_sb] + snr[(*alloc)[min_sb][ba].quant + 1];
}
}
} while (min_sb > -1); /* until could find no channel */
/* Calculate the number of bits left */
ad -= bspl + bscf + bsel;
*adb = ad;
for (k = 0; k < nch; k++)
for (i = sblimit; i < SBLIMIT; i++)
bit_alloc[k][i] = 0;
return 0;
}
fprintf(trace_file, "采样率=%.1f kHz ", s_freq[header.version][header.sampling_frequency]);
fprintf(trace_file, "目标码率=%d kbps\n", bitrate[header.version][header.bitrate_index]);
使用上述语句可以获得采样率以及目标码率。
在处理过程中添加:
fprintf(trace_file, "帧[%d]被分配了%d比特\n", frameNum - 1, adb);
fflush(trace_file);
for (int i = 0; i < 2; ++i)
{
fprintf(trace_file, "通道%d信息\n", i);
for (int j = 0; j < 32; ++j)
{
fprintf(trace_file, "子带[%d]:\t分配%d比特\t ", j, bit_alloc[i][j]);
fprintf(trace_file, "比例因子%d %d %d\n", scalar[i][0][j], scalar[i][1][j], scalar[i][2][j]);
}
fflush(trace_file);
}