您的当前位置:首页正文

mp3解码算法分析

来源:画鸵萌宠网
MP3解码算法分析(1)——MP3文件解剖

1. MP3 编码器(Encoder)常见问题 - 什么是 MP3 编码器?

一个 MP3 编码器是一个使用 MP3 编解码算法(压缩/解压)创建 mp3 文件的软件。 大部分编码器只转化一

个 WAV 文件到 MP3 文件,尽管很多可以转化其他格式,如 WMA, Real Audio, Ogg, 等等。 只有很小几个独立的编码器,此外的很多软件只是使用 4个主要的编码引擎,很大程度上因为 Fraunhofer

Gesellschaft 和为 ISO 源提供帮助的几家公司拥有专利权。尽管没有一家公司拥有派他权,MP3软件开发

商无论使用哪家专利的 MP3 编码器,都必须交付昂贵的许可费。主要的 MP3 编码引擎包括:LAME(非 ISO

源), BladeEnc, Fraunhofer 和 Real Networks 的 Xing 编码器。

- MP3 编码器如何工作?

MP3编码器中包含着 MPEG-Layer 3 下的核心技术。解码程序通过使用一系列的压缩音频的算法和规则。编

码器智能地发现那些对人耳太高或太低的音频数据,并把他们剔除掉。 它们也能发现那些同时发生的声音

并试着排除任何能由其它声音“屏蔽掉”或“使其听不到”的声音。

- 什么好的 MP3 编码器?

在速度上 Xing 是最快的编码器,但是质量是最差的。要获得较小的文件尺寸,Fraunhofer 的 FastEnc可

给出最好的质量。LAME是非常好的编码器,并且其一个版本都比上个版本快,BladeEnc 对大尺寸文件质量

是最好的,但很慢。

2. MP3 文件解剖

除熟练使用 MP3 编码器的基本选项之外,一般的用户不需要知道 MP3文件内部结构是如何编码的,正如面

对 JPEG 或 DOC 文件时的情形一样。基于一种病态的好奇,这里给出 MP3 文件的 X 光视图: - 帧头(Frame Header)

以前讲过,MP3文件是由成千上万的“帧 frame”组成,每帧包含一部分(第二部分)有价值的音频数据,以

供解码器重构音频数据,其前的第一部分就是帧头(Frame Header),它有与其后的数据相关的 32bits的元

数据组成,参见下图。MP3 头开始于一个11bits的“同步 sync”块,同步块可以让播放器搜索并锁定到第

一个可用的合法帧,在 MP3 广播中这很有用,它可从播放源中快速移动或跳过 ID3块到一个正常的播放位

置。但是,简单地发现同步块从理论上讲是不够的,因此头检查是必要的。

- 锁定数据流

MP3 的原始设计目标就是它可用于广播,结果就是 MP3 接受器可以在流中任何地方锁定信号变得很重要,

这就是为什么帧头放在任何一帧数据流的头部,这样当 MP3 接受器“调谐”进数据流时,可以立即锁定信

号并可立即播放。有趣的是,这个事实造成了可以切割 MPEG文件成小段,且每段都可独立播放。但是不幸

的是它在 3层(MP3)文件中是不可能的,因为其中帧往往依赖于其它帧,因此你不能随意在音频编辑器中编

辑你的 MP3 文件。

- 每秒帧数

正如电影工业有一个标准规定电影中每秒帧数以保证在任何投影机上都能正确显示一样,MP3 标准中也使用

了一个类似的标准,不管文件的比特率,MPEG-1 文件中一帧 26ms,大致每秒 38fps 帧。 如果比特率高一

点,帧尺寸就相应大一点,反之亦然。另外,一帧 MP3 中所含的采样数是常数,每帧 1,152 个采样。

任何给定帧的总尺寸可用下列公式计算:

FrameSize = 144 * BitRate / (SampleRate + Padding).

在此,比特率BitRate是以每秒多少比特计算,采样率 SampleRate是原始输入数据的采样率,对齐Padding

是指该帧的额外数据,例如,如果你用128kbps 来编码,原始采样率44.1kHz, 没有设置对齐位,每帧总的

尺寸为 417.96 bytes:

144 * 128000 / (44100 + 0) = 417.96 bytes.

记住,以上描述的每帧包含头信息,容易想到头信息是否会产生冗余。但是头仅占32bits长,在38fps下,

意味着每秒总共才1,223bits 的数据,而文件是以 128kbps 编码,却包含着 128,000bits每秒的数据,实

在只是个零头 1/100。

3. 关于“有损压缩”

压缩格式,无论它们应用于音频、视频还是图象, 甚至随机的文件集合,都分为“无损”和“有损”,其区

别也很简单,无损时,解压文件和原始文件完全一样,而有损压缩则不然。 无损压缩格式的一个很好示例就

是到处使用的 .zip 档案压缩方法。当你把上个月的压缩档案 zip 文件解开时,一个比特的改变都是不可接

受的。但另外类型的文件如偶尔丢点信息对你无所谓, 你甚至都不会注意到,也可能你愿意对不重要的信息

牺牲点东西换取更高的压缩比,如图象、电影、音乐等。

一个好的有损压缩的示例是 JPEG 图象文件格式。MP3 对于音乐也是有损压缩。 4. Open issue JPEG codec

G.723.1(5.3k/6.3k), G.729, G.729a, G.729b, G.729ab, G.711A-Law, μ-Law, GSM610 Compression codec standards comparison:

G.723.1 Dual rate MP-MLQ/ACELP coder, 5.3, 6.3 kbps G.729 CS-ACELP codec, 8 kbps GSM 13 kbps G.728 LD-CELP, 16kbps

G.726/727 ADPCM codec, 64kbps to 40, 32, 24,16 kbps G.711 telephone, A-Law, μ-Law 64 kbps MPEG L3 MP3, 56-128 kbps MPEG4 Video depends on content, frame rate

MP1 和 MP2 的压缩率分别为 4:1 和 6:1 - 8:1,而 MP3 的压缩率则高达 10:1 - 12:1。 5. Miscs

SEDA solution = Algorithm Design + Processor Knowledge + System Know-how Know-how 实际知识、技术诀窍 rundown 纲要

morbidly curious 病态的好奇 discrepancy 差异、矛盾 mimics 模仿 ripping 极好的

MP3解码算法分析(2)——MP3帧头(Frame Header)数据结构描述

1. 帧头(Frame Header)数据结构描述

A B C D E F G H I J K L M AudioData 12 1 2 1 4 2 1 1 2 2 1 1 2

Sync ID Layer Prot BitRate Freq Pad Priv Channel ModeExt Copy Home Emph AudioData 位置 作用 长度(Bits) ---- -------------------------------------------------- ---------- A 帧同步 12 20-31 B MPEG 音频版本(MPEG-1, 2 等) 1 19 C MPEG 层(Layer I, II, III 等) 2 17-18 D 保护(On/Off, 如果开,校验和附在头后) 1 16 E 比特绿索引(查找表用于说明本 MPEG 版本和层的比特率) 4 12-15 F 采样率频率(44.1kHz 等,由查找表决定) 2 10-11 G 补齐位(On/Off, 是否对未填满的帧进行补齐) 1 9 H 保留位(On/Off, 允许特定应用触发) 1 8 I 声道模式(立体声,联合立体声,双声道、单声道) 2 6-7 J 扩展模式(只用于联合立体声,以联合声道数据) 2 4-5 K 版权(On/Off) 1 3 L 原始(On/Off, Off 表示复制版,On 表示原创) 1 2 M 强调(尊重强调原始录制,现基本废弃) 2 0-1 --- -------------------------------------------------- ---------- Table 2-1: MP3 文件中十三个帧头属性

2. 32 位帧头(Frame Header)

跟随同步块之后的是 ID 双位,它指明该帧是 MPEG-1/MPEG-2 编码; 之后是层 Layer 双位,它决定了该帧

是 Layer I/II/III;如果没设保护位(Prot),一个 16bit 的校验和会插在音频数据之前。 比特率域,自然地就是指当前帧的比特率(如 128kbps);之后是采样频率(从16,000Hz 到 44,100Hz, 取决于

当前使用的是 MPEG-1 还是 MPEG-2)。 补齐位用于确认每帧是否严格按比特率对齐, 例如,一个 128 kbps Layer II 的比特率采样率为 44.1kHz 的情况下,某些帧会以 417 bytes 和 418 结束,417 byte 的帧将设

定补齐位(1),以补偿差异。

声道模式域可设定帧为立体声/单声道/混合立体声等模式,如果设定为联合立体声, 还可进一步设定扩展模

式,以让解码器知道如何处理它们,即:高频部分是否已经结合进各声道。

版权位并不装载版权信息(明显,因为它只有一个比特), 其实更像是在模仿 CD 或 DAT 上的版权位。 如果

设定该位,拷贝该声道是严格讲是违法的(一些好的程序会给你警告)。 如果数据是从原始媒体上发现的,

原始位将被设置,“保留位Priv” 用以说明特定的应用触发用户事件。

“强调位 Emphasis”用作一个标志, 在原始录制时加一个强调位,该位几乎不被使用, 尽管一些录制程序

确实仍在使用它。

最后,解码器移动过“校验和”部位 (如果它存在), 到达实际的音频数据帧, 并开始处理,循环往复。每

个音频文件都有成千上万帧。

MP3解码算法分析(3)——CRC16算法

1. CRC(Cyclic Redundancy Checksum) 校验原理

随着数据传输速度的增大,设备间所传递的数据的准确性变得越来越重要。设备利用类似于校验和的 CRC 技

术给每个传输的数据增加附加的代码位。接受设备通过解译附加的代码以核实数据被准确传送。 流行的多项式常数(Polynominal):

CCITT: x^16 + x^12 + x^5 + x^0 (0x1021) CRC-16: x^16 + x^15 + x^2 + x^0 (0x8005) CRC-32: ... (0x04C11DB7)

如果两个文件具有相同的 CRC,就意味着两个文件是相同的,CRC是一个检查一个文件每个字节的函数,文件

的任何哪怕微小改变,都会产生一个不同的 CRC 数,CRC 函数常用于很多程序以验证文件,如 Zip 等。

2. Modulo-2 Binary Division

Generator Polynominal, Quotient, Message, Remainder 10101100 Quotient Polyminal ______________

11011 / 11100101zzzz Message, Last zzzz is c-bits Zero. 11011 01111 00000 11110 11011 01011 00000 10110 11011 11010 11011 00010 00000 00100 00000

0100 Remainder = CRC (c-bits)

以2为模的除法流程如下:

1) 把消息的前 c+1 位调进余数(remainder);

2) 从原始消息的最高位开始,逐位检查 c+1 位余数:

- 如果余数的最高位为 1,除数(divisor/polynominal)就被“说成”去除,商数标 1, 同时计算新余

数,做法如下:

* 设定商数相应位为 1,且:

* 异或(XOR)余数和除数,并存储结果为新余数; - 否则(首位为 0):

* 设定商数相应位为 0,且:

* 异或(XOR)余数和 0,并存储结果为新余数(无效); - 左移余数一位, 末位为新进来的消息位;

3. 类 CRC16 定义:

private short crc = (short)0xFFFF; // CRC16 类属性 crc 校验和,初值 private static short polynominal = (short)0x8005; // CRC16 类静态常数?

// 填入一个位串到 crc 计算中。

public void add_bits(int bitstring, int length) { int bitmask = 1 << (length - 1); // 0 ~ 31 位,

do { // 对要加入校验的值从高到低逐位循环 if (((crc & 0x8000) == 0) ^ // 如果 CRC 高位为 1 而要校验的位为 0

((bitstring & bitmask) == 0 )) // 或 CRC 高位为 0 而要校验的位为 1,则执行: {

crc <<= 1; // crc 向左循环一位 crc ^= polynomial; // crc 1062 } else

crc <<= 1; // crc 向左循环一位 } while ((bitmask >>>= 1) != 0); // 循环掩码右移一位 }

MP3解码算法分析(4)——Java MP3 player项目系统分析

1. jlp.main (FileName:mp3) [javazoom.jl.player] // 主程序、入口 play (mp3) // 转入播放函数

2. jlp.play (mp3) [javazoom.jl.player] // 播放函数 Player ( // 播放器创建 InputStream : BufferedInputStream ( FileInputStream ( mp3 ) )

AudioDevice : ? // 先创建其输入流和音频设备 FactoryRegistry.systemRegistry().createAudioDevice )

Bitstream (InputStream) // 将输入流转化为比特流 Decoder // 新建解码器

AudioDevice.open (Decoder) // 将解码器与音频设备连接 Player.play // 播放器播放

3. Player.play [javazoom.jl.player]

+ decodeFrame // 逐帧循环解码

Header = Bitstream.readFrame // 读一帧,并返回头到 Header

SampleBuffer = Decoder.decodeFrame (Header, Bitstream...); // 解码器帧解码,返回解码样

AudioDevice.write (SampleBuffer...) // 将解码的音频样数据写进音频设备 4. Decoder.decodeFrame (Header, Bitstream...) [javazoom.jl.decoder] ? Initialize(Header) // Outout.clearBuffer

FrameDecoder = retrieveDecoder(Header, Bitstream, Header.Layer) // 每帧的层号不同,解码器就不同

decodeFrame

output.write_buffer(1) return output

5. AudioDevice.write (SampleBuffer...) [ ] Start = 0

Length = SampleBuffer.getLength

6. Bitstream.readFrame : readNextFrame : nextFrame [javazoom.jl.decoder] Crc16

Header.read_header(this, Crc16[])

7. Object Algorithm Description Language Note: : Type declaration 类型声明 = Object Assign 对象赋值 . Execute methods 执行方法 , Arguments separator 变量分割

# Construct execute block 构建执行块 + Loop 循环 $ Display 显示 ? Question 问题 [ ] Package spec 包说明 { } Procedure 过程 ( ) Arguments list, none can omit. 变量列表 ... Object related parameters 对象参数

8. Test

java -cp C:\\JYC2005SZ\\Java\\Jamp3\\build\\classes javazoom.jl.player.jlp C:\\JYC2005SZ\\Java\\Jamp3\\sjcy.mp3

2005-12-20

1. 读 32bit 的帧头,头处理的主处理流程

Header.read_header(Bitstream, Crc16[] crcp) [javazoom.jl.decoder]

int headerstring; // 帧头的32位对应的32位整数 int channel_bitrate; // 比特率 boolean sync = false; // 同步判断

do {

headerstring = stream.syncHeader(syncmode);// 同步并获取帧头的32位整数 _headerstring = headerstring;

if (syncmode==Bitstream.INITIAL_SYNC) { // 如果是初始化同步 h_version = ((headerstring >>> 19) & 1); // 求取第 19 位的版本号

if (((headerstring >>> 20) & 1) == 0) // 第 20 位为: MPEG2.5 detection if (h_version == MPEG2_LSF) h_version = MPEG25_LSF; else

throw stream.newBitstreamException(Bitstream.UNKNOWN_ERROR); // 第 10-11 位为采样率 if ((h_sample_frequency = ((headerstring >>> 10) & 3)) == 3) throw stream.newBitstreamException(Bitstream.UNKNOWN_ERROR); }

h_layer = 4 - (headerstring >>> 17) & 3; // 第 17-18 位为层号 Layer I/II/III h_protection_bit = (headerstring >>> 16) & 1; // 第 16 位为保护位 Protect h_bitrate_index = (headerstring >>> 12) & 0xF; // 第 12-15 位为比特率 h_padding_bit = (headerstring >>> 9) & 1; // 第 9 位为补齐位 h_mode = ((headerstring >>> 6) & 3); // 第 6-7 位为模式位 h_mode_extension = (headerstring >>> 4) & 3; // 第 4-5 位为模式扩展位

if (h_mode==JOINT_STEREO) h_intensity_stereo_bound = (h_mode_extension << 2) + 4; else h_intensity_stereo_bound = 0; // 用不到

if (((headerstring >>> 3) & 1)==1) h_copyright = true; // 第 3 位版权等信息 if (((headerstring >>> 2) & 1)==1) h_original = true; // 第 2 位为 Home 信息

if (h_layer==1) h_number_of_subbands = 32; // 计算子带数(subbands),Layer I 有 32 个子带

else {

channel_bitrate = h_bitrate_index; // 计算每个信道的比特率 if (h_mode != SINGLE_CHANNEL)

if (channel_bitrate == 4) channel_bitrate = 1; else channel_bitrate -= 4;

if ((channel_bitrate == 1) || (channel_bitrate == 2))

if (h_sample_frequency == THIRTYTWO) h_number_of_subbands = 12; else h_number_of_subbands = 8; else

if ((h_sample_frequency == FOURTYEIGHT) || ((channel_bitrate >= 3) && (channel_bitrate <= 5))) h_number_of_subbands = 27; else

h_number_of_subbands = 30; }

if (h_intensity_stereo_bound > h_number_of_subbands) h_intensity_stereo_bound = h_number_of_subbands;

calculate_framesize(); // 计算帧尺寸和槽数(framesize/nSlots) stream.read_frame_data(framesize); // 读取帧数据(framedata):

if(stream.isSyncCurrentPosition(syncmode)){// 当前位置同步 if (syncmode==Bitstream.INITIAL_SYNC) { // 如果是初始化同步 syncmode = Bitstream.STRICT_SYNC; // 设定为严格同步

stream.set_syncword(headerstring & 0xFFF80CC0); // 设定同步字,0xFFF80CC0 为同步字掩码 }

sync = true; // 目前同步 } else {

stream.unreadFrame(); // 当前不同步,把帧数据退回去缓冲区中 }

} while (!sync); // 如果未同步重读 stream.parse_frame(); // 分析帧

if (h_protection_bit == 0) { // 帧中包含 16 位的 crc 校验和

checksum = (short)stream.get_bits(16); // 取出 16 位的 crc 校验和到 checksum if (crc == null) crc = new Crc16();

crc.add_bits(headerstring, 16); // ?把头 16 位加入校验 crcp[0] = crc; // 把校验器返回 crcp[] } else

crcp[0] = null; // 无校验 }

2. 帧尺寸计算,计算帧尺寸(单位为字节),抛掉头尺寸。 public int calculate_framesize() {

if (h_layer == 1) { // 层 Layer I,公式:fs = 12 * br / sf framesize = (12 * bitrates[h_version][0][h_bitrate_index]) / frequencies[h_version][h_sample_frequency]; if (h_padding_bit != 0 ) framesize++;

framesize <<= 2; // 每槽 4bytes nSlots = 0;

} else { // 层 Layer II/III,公式:fs = 144 * br / sf

framesize = (144 * bitrates[h_version][h_layer - 1][h_bitrate_index]) / frequencies[h_version][h_sample_frequency];

if (h_version == MPEG2_LSF || h_version == MPEG25_LSF) framesize >>= 1; // 每槽 4bits if (h_padding_bit != 0) // 对齐位 framesize++;

if (h_layer == 3) { // 层 Layer III 槽调整 if (h_version == MPEG1) { // MPEG1 nSlots = framesize

- ((h_mode==SINGLE_CHANNEL) ? 17:32) // 减去 边(side)信息尺寸 - ((h_protection_bit!=0) ? 0 : 2) // 减去 CRC 尺寸 - 4; // 减去 头尺寸

} else { // MPEG-2 LSF, MPEG-2.5 LSF nSlots = framesize

- ((h_mode==SINGLE_CHANNEL) ? 9:17) // 减去 边(side)信息尺寸 - ((h_protection_bit!=0) ? 0 : 2) // 减去 CRC 尺寸 - 4; // 减去 头尺寸 }

} else { // 层 Layer II 槽为 0 nSlots = 0; } }

framesize -= 4; // 再扣去一遍头尺寸? return framesize; // 返回 }

3. 帧头是否同步,即检查头32位整数的合法性

public boolean isSyncMark(int headerstring, int syncmode, int syncword) { boolean sync = false; // 同步变量

if (syncmode==INITIAL_SYNC) { // 初始化同步 sync=((headerstring&0xFFE00000)==0xFFE00000); // MPEG 2.5

} else { // 否则要检查内部合法性 sync=((headerstring&0xFFF80C00)==syncword) // 同步字相同,且: &&(((headerstring&0x000000C0)==0x000000C0)==single_ch_mode); }

if (sync) // 过滤掉非法采样率 sync = (((headerstring >>> 10) & 3)!=3);

if (sync) // 过滤掉非法层 sync = (((headerstring >>> 17) & 3)!=0);

if (sync) // 过滤掉非法版本 sync = (((headerstring >>> 19) & 3)!=1);

return sync; // 返回是否同步 }

4. 帧数据分析,转化读入的字节为每4个转化为一个32位整数。问题:为什么要这样做? void parse_frame() throws BitstreamException { int b=0;

byte[] byteread = frame_bytes; // 予处理:帧字节缓冲 int bytesize = framesize; // 帧尺寸(单位字节)

for (int k=0; kif (k+1if (k+2framebuffer[b++] = (b0 << 24) | (b1 << 16) // 再合成32位整数:先读的 b0, b1 在高位,

| (b2 << 8) | b3; // 后读的 b2, b3 在低位 }

wordpointer = 0; // 后处理:字指针复位 0,范围:0 ~ 整数字个数-1

bitindex = 0; // 位指针复位 0,范围:0 ~ 31 }

5. 从缓冲区读入指定位数到一个无符号整数的低位部分 LSB(1~16) public int get_bits(int number_of_bits) { int returnvalue = 0;

int sum = bitindex + number_of_bits; // bitindex为当前字wordpointer下的当前位数指针

if (wordpointer < 0) wordpointer = 0; // 这儿有个问题,wordpointer 能是-1吗?! if (sum <= 32) { // 所有位包含在当前字中*wordpointer,没跨32位字

returnvalue = (framebuffer[wordpointer] >>> (32 - sum)) & bitmask[number_of_bits]; // bitmask[] = {0,1,3,7,15,31,63,127,...}

if ((bitindex+=number_of_bits)==32) { // 位指针前移 number_of_bits 位 bitindex = 0; // bitindex==32, 即 bitindex = 0 wordpointer++; // 字指针前移一个字 }

} else { // 跨字处理

int Right = (framebuffer[wordpointer] & 0x0000FFFF); // 上字后半部分

wordpointer++; // 字指针前移一个字

int Left = (framebuffer[wordpointer] & 0xFFFF0000); // 下子前半部分 returnvalue = ((Right << 16) & 0xFFFF0000)

| ((Left >>> 16)& 0x0000FFFF); // 整字合并

returnvalue >>>= 48 - sum; // 截尾便于掩码操作 returnvalue &= bitmask[number_of_bits]; // 掩码出想读的位数字 bitindex = sum - 32; // 位指针前移到位结束处 }

return returnvalue; // 返回想读的位数字 }

6. 首个帧头处理初始化——Decoder.initialize(header)

private void initialize(Header header) throws DecoderException { float scalefactor = 32700.0f; // 评论:允许客户化缩放系数(Scale Factor) int mode = header.mode(); int layer = header.layer();

int channels = (mode==Header.SINGLE_CHANNEL ? 1 : 2);

if (output==null) // 设置创建输出缓冲区,如果客户端未设置

output = new SampleBuffer(header.frequency(), channels);

float[] factors = equalizer.getBandFactors();// 均衡器在解码器创建时已创建,获取其各波段系数

filter1 = new SynthesisFilter(0, scalefactor, factors); // 创建合成过滤器 1 if (channels==2) // 评论:允许为立体声输出单声道 filter2 = new SynthesisFilter(1, scalefactor, factors); // 创建合成过滤器 2 outputChannels = channels;

outputFrequency = header.frequency();

initialized = true; // 初始化完成标志,以后不必再初始化。405 }

MP3解码算法分析(6)——帧解码器接口定义与选择

1. 帧解码器接口的定义

帧解码器负责实施解码一个 MPEG 音频帧

回顾:当前该接口太薄,应该增加诸如说明输出缓冲、合成过滤以及其他可能的解码器对象等方法。

public interface FrameDecoder { // 解码一帧 MPEG 音频

public void decodeFrame(); }

2. MP3 分层帧解码器的选择

protected FrameDecoder retrieveDecoder(Header header, Bitstream stream, int layer) throws DecoderException {

FrameDecoder decoder = null; // 帧解码器变量 // 评论:允许通道输出选择类型(LEFT, RIGHT, BOTH, DOWNMIX)

switch (layer) { // 每帧的层号不同,帧解码器就不同 case 3: // 对层 Layer III

if (l3decoder==null) { // 如果L3层帧解码器没创建, l3decoder = new LayerIIIDecoder(stream, // 就创建它

header, filter1, filter2, // 把流、头、过滤器、输出池、需要的通道数传给它

output, OutputChannels.BOTH_CHANNELS); }

decoder = l3decoder; break; // 当前帧解码器就是它;退出; case 2:

if (l2decoder==null) { // 如果L2层帧解码器没创建, l2decoder = new LayerIIDecoder(); // 就创建它 l2decoder.create(stream, header, filter1, filter2, output, OutputChannels.BOTH_CHANNELS); }

decoder = l2decoder; break; // 当前帧解码器就是它;退出; case 1:

if (l1decoder==null) { // 如果L1层帧解码器没创建, l1decoder = new LayerIDecoder(); // 就创建它 l1decoder.create(stream, header, filter1, filter2, output, OutputChannels.BOTH_CHANNELS); }

decoder = l1decoder; break; // 当前帧解码器就是它;退出; }

if (decoder==null) // 如果当前帧解码器没选上;抛出异常; throw newDecoderException(UNSUPPORTED_LAYER, null);

return decoder; // 返回选择的帧解码器; }

MP3解码算法分析(7)——L1帧解码器分析

1. L1帧解码 LayerIDecoder.java public void decodeFrame() {

num_subbands = header.number_of_subbands();

subbands = new Subband[32]; // Layer I 有 32 个子带 mode = header.mode();

createSubbands(); // 创建子带 readAllocation(); //

readScaleFactorSelection(); // L1 层没有比例系数选择

if ((crc != null) || header.checksum_ok()) { // readScaleFactors(); //

readSampleData(); // 读取解样数据 } }

2. 读取分配 LayerIDecoder.java,

protected void readAllocation() { // 开始读取音频数据

for (int i = 0; i < num_subbands; ++i) // 对 32 个子带依次读取“分配” subbands[i].read_allocation(stream, header, crc); }

class SubbandLayer1 { // L1 子带

protect int allocation, samplelength; // 注:Subband 类属性 protected float factor, offset; ...

public void read_allocation(Bitstream stream, Header header, Crc16 crc) { if ((allocation=stream.get_bits(4))==15); // 警告:流中包含一个非法分配,MPEG 流已被破坏!

if (crc!=null) crc.add_bits(allocation, 4);// 加入校验 if (allocation != 0) {

samplelength = allocation + 1; // 计算采样长度 factor = table_factor[allocation]; // 系数 offset = table_offset[allocation]; // 偏移 } } }

3. 校验和检查 Header class:

// 比较计算的 checksum 和流 checksum,返回成功与否 // 校验参与部分有:Header 和 32个4字节 Allocaltion

public boolean checksum_ok () { return (checksum == crc.checksum()); }

4. 读取比例系数 LayerIDecoder class: protected void readScaleFactors() {

for (int i=0; iclass SubbandLayer1 { // L1子带类的比例系数读取 public void read_scalefactor(Bitstream stream, Header header) { if (allocation != 0) scalefactor =

scalefactors[stream.get_bits(6)]; // 每个子带有 6bits 的比例系数位 } }

5. 读取样本数据并写入解样数据 LayerIDecoder class: protected void readSampleData() { boolean read_ready = false; boolean write_ready = false; int i, mode = header.mode();

do {

for (i = 0; i < num_subbands; ++i) // 对 32 个子带依次读取“样本数据” read_ready = subbands[i].read_sampledata(stream); do {

for (i = 0; i < num_subbands; ++i) // 对 32 个子带解样数据依次写入样本缓冲

write_ready = subbands[i].put_next_sample(which_channels, filter1, filter2); filter1.calculate_pcm_samples(buffer); // 计算施加过滤器 1 if ((which_channels==OutputChannels.BOTH_CHANNELS) && (mode!=Header.SINGLE_CHANNEL))

filter2.calculate_pcm_samples(buffer); // 计算施加过滤器 2 } while (!write_ready); } while (!read_ready); }

class SubbandLayer1 {

public boolean read_sampledata(Bitstream stream) { if (allocation != 0) sample = (float)

(stream.get_bits(samplelength)); // 读取样本数据,长度=samplelength=allocation+1

if (++samplenumber == 12) { // 读取的样本数类计,超过 12 复位 samplenumber = 0;

return true; // 样本读取结束 32|12 = 96? }

return false; } ... }

class SynthesisFilter { // 计算 32 PCM 采样并放进输出缓冲区 Obuffer 对象

public void calculate_pcm_samples(Obuffer buffer) { compute_new_v();

compute_pcm_samples(buffer);

actual_write_pos = (actual_write_pos + 1) & 0xf; actual_v = (actual_v == v1) ? v2 : v1; for (int p=0; p<32; p++) samples[p] = 0.0f; }

// Compute new values via a fast cosine transform. private void compute_new_v() { ... }

private void compute_pcm_samples(Obuffer buffer) { switch (actual_write_pos) { case 0:

compute_pcm_samples0(buffer); break;

case 1: ... }

if (buffer!=null)

buffer.appendSamples(channel, _tmpOut);

// MDM: I was considering putting in quality control for // low-spec CPUs, but the performance gain (about 10-15%) // did not justify the considerable drop in audio quality. switch (inc) { case 16:

buffer.appendSamples(channel, tmpOut); break; case 32:

for (int i=0; i<16; i++) {

buffer.append(channel, (short)tmpOut[i]); buffer.append(channel, (short)tmpOut[i]); }

break; case 64:

for (int i=0; i<8; i++) {

buffer.append(channel, (short)tmpOut[i]); buffer.append(channel, (short)tmpOut[i]); buffer.append(channel, (short)tmpOut[i]); buffer.append(channel, (short)tmpOut[i]); }

break; } }

private void compute_pcm_samples0(Obuffer buffer) { final float[] vp = actual_v; // int inc = v_inc;

final float[] tmpOut = _tmpOut; int dvp =0;

// fat chance of having this loop unroll for ( int i=0; i<32; i++) { float pcm_sample;

final float[] dp = d16[i]; pcm_sample = (float)(

( (vp[0 + dvp] * dp[0]) + (vp[15 + dvp] * dp[1]) + (vp[14 + dvp] * dp[2]) + (vp[13 + dvp] * dp[3]) + (vp[12 + dvp] * dp[4]) + (vp[11 + dvp] * dp[5]) + (vp[10 + dvp] * dp[6]) + (vp[ 9 + dvp] * dp[7]) + (vp[ 8 + dvp] * dp[8]) + (vp[ 7 + dvp] * dp[9]) + (vp[6 + dvp] * dp[10]) + (vp[5 + dvp] * dp[11]) + (vp[4 + dvp] * dp[12]) + (vp[3 + dvp] * dp[13])

+ (vp[2 + dvp] * dp[14]) + (vp[1 + dvp] * dp[15]) ) * scalefactor );

tmpOut[i] = pcm_sample; dvp += 16; } } }

MP3解码算法分析(8)——解码器概念/类框架

1. 解码器概念/类框架

1) 播放器 Player 比特流 BitStream 音频设备 AudioDevice 2) 帧 Frame 帧头 Header 通道 Channel 采样频率 Frequency 比特率 BitRate 校验器 CRC16 3) 解码器 Decoder

帧解码器 FrameDecoder L1帧解码器 LayerIDecoder

子带 Subband 用于 L1/L2 的抽象类 SubbandLayer1

SubbandLayer1Stereo Allocation 波段系数 BandFactor

合成过滤器 SynthesisFilter 比例系数 ScaleFactor

2. MPEG音频文件根据压缩质量和编码复杂程度的不同可分为三层(MPEG Audio Layer 1/2/3),分别与MP1,MP2和

MP3 这三种声音文件相对应。MPEG 音频编码具有很高的压缩率,MP1和MP2的压缩率分别为4:1和6:1~8:1,而

MP3的压缩率则高达10:1~12:1。

层次 压缩率 数据速率kb/s 延迟ms ---- ----------- ------------ ------ 1 4 : 1 384 19/50 2 6:1 ~ 8:1 192 ~ 256 35/100 3 10:1 ~ 12:1 112 ~ 128 59/150

MP3为降低声音失真采取了名为“感官编码技术”的编码算法:编码时先对音频文件进行频谱分析,然后用过

滤器滤掉噪音电平,接着通过量化的方式将剩下的每一位打散排列,最后形成具有较高压缩比的

MP3文件,并

使压缩后的文件在回放时能够达到比较接近原音源的声音效果。 虽然它是一种有损压缩,但是它的最大优势

是以极小的声音失真换来了较高的压缩比。

3. 听觉系统的感知特性: 1) 听阈-频率曲线 2) 频域掩蔽

一个强纯音会掩蔽在其附近同时发声的弱纯音,这种特性称为频域掩蔽,也称同时掩蔽 3) 时域掩蔽

在时间上相邻的声音之间也有掩蔽现象,称为时域掩蔽。时域掩蔽又分为超前掩蔽和滞后掩蔽。 4. 感知子带压缩算法

算法以心理声学模型为基础,主要利用了听觉阈值和听觉掩蔽特性 感知子带压缩算法:

1、将音频信号用滤波器组分成32个子带; 2、用FFT将子带变换到频率域

3、根据心理声学模型估计各个子带的感知阈值

4、根据对感知阈值的估计对各个子带进行比特分配和量化。 MP3采用了与MP1、MP2不同的滤波器和心理声学模型。

MP3解码算法分析(9)——MP3解码流程

1. 解码流程 Decode flow:

MP3 Stream -> Sync Read Header -> Decode Side Info -> Decode Scale Factors MP3 比特流 同步并读取帧头 解码边信息 解码比例系数 -> Decode Huffman Data -> Requantization -> Stereo Process 解码哈夫曼数据 重新量化 立体声处理

-> Alias Reduction -> IMDCT Transform -> IDCT Filterbank 剔除伪信号 逆离散余弦变换 逆离散余弦变换过滤 -> Windowing -> PCM Samples 窗口化 转化为 PCM 采样

2. MP3 解码流程的九个阶段 MP3 decoding can be divided into 9 stages: 同步 Synchronization 找到帧的开始位置、检查错误(CRC) 霍夫曼解码 Huffman decoding 解包信息,并输出量化的频率信息 再量化 Requantize 用比例系数放大输出信息 再排序 Reorder 增加频率后重新排序频率线

立体声合成 Stereo decoding 从结合的信号中解码左右声道

为了更高的压缩比,双声道一起编码; 立体声解码模块重新构造左右声道: Li = (Mi + Si) / Sqrt(2)

Ri = (Mi - Si) / Sqrt(2)

假象重构 Alias reconstruction

假象重构就是使用蝴蝶结构结合相临子带以避免在域变换(IMDCT)中的锯齿效应。 逆修饰离散傅立叶变换 IMDCT

IMDCT 变换的目的就是将每个子带从频域转换到时域。 频率倒置 Frequency inversion 子带合成 Subband synthesis

MP3解码算法分析(10)——MP3压缩五项技术原理

要压缩如此大量的数据,MP3 格式使用了一些技术和窍门,这里将对其大部分进行解释。在这些技术中,他

们普遍地受到了名为“知觉编码”技术的影响。它们是: 最少听觉阈限 屏蔽效果 字节蓄积 接合立体声 Huffman 编码

1. 最少听觉阈限(The minimal audition threshold):

人类耳朵的最少的听觉阈限并非线性的,根据 Fletcher 和 Munson 法则,它可被描述为一条 2KHz 到 5KHz

的曲线。因此,没有必要对这个阈限以外的声音进行编码,因为我们听(感知)不到它。 2. 屏蔽效果(The masking effect): 这一系统是基于人类耳朵的屏蔽特性, 我来解释一下:当你看着太阳时,一只小鸟在你前面飞过,你不会看

到它,因为有太阳光的强烈对比。 在声音上的表现也类似, 在有强大的声音出现时, 你不会听到弱小的声

音。 以管风琴为例,在不弹奏管风琴的平静中,你会听到心跳的声音,当管风琴响起时,你就再也听不到它

的声音,因为它被屏蔽掉了。

因此没有必要编码所有的声音,这是 MP3格式赢得空间所用的第一个属性。基于该原理,MP3编码器使用了一

个称为 psychoacoustic 的模型来描述人类的耳朵行为。

3. 字节蓄积(The reservoir of bytes):

一些音乐片段不能在不改变音乐质量的同时以给定的速率编码是常有的事。这时MP3使用小量的字节蓄水池,

它就像一个缓冲器,其容量来自于那些可以用比给定速率低一些就可编码的段落。 4. 接合立体声(The Joint Stereo):

在立体音响信号的情形下,MP3 格式则使用少量的手段,它们称为“接合立体声(Joint Stereo)”JS 编码,

来进一步缩小压缩文件尺寸。

在很多中型的高保真组合音响中,有一个独特的低音子扩音器。 但通常你不会感到声音来自这个轰鸣器,倒

更像是来自周遍的喇叭。人类的耳朵对于非常高和非常低频率的声音,确实已不能很精确地确定其位置。MP3

格式也因此(可选)用一个称为“强度立体声(Intensity Stereo)”IS 的技术来复原这一把戏。一些频率记录

成单声道信号,再跟一些额外的信息以恢复最少的空间声音信息。

第二个接合立体声方法被称为“中/边(Mid/Side)” M/S 立体声。当左右声道非常想象时,就用中间(L+R)和

边分(L-R)通道来编码,来取代左右声道。这样会最终减少文件尺寸,因为边分通道只占用少量的比特。当回

放时,MP3 解码器将重构左右声道。

5. Huffman 编码(The Huffman coding):

MP3 也使用经典的 Huffman 算法技术,它只在压缩编码信息的最后阶段起作用,因此它不像是自身的压缩算

法, 而更像是编码方法。 这个编码在整个的比特码流上创建变长的码, 高出现概率的符号具有较短的码。

Huffman 码具有唯一前缀的特性,它们因此可被正确地解码而无论它们有多长。解码步骤非常快(根据一张相

应的码表)。这一类的编码平均而言可以节省 20% 的比特空间。

它是对“知觉编码”的理想补充:当有很多复音时,知觉编码非常有效, 因为很多声音被掩盖货弱化掉了,

只用少量的信息就够了, 此时 Huffman 编码作用不大, 当只有纯净的声音时,因为很少有屏蔽效应,此时

Huffman 编码很有效,因为数字化的声音包含很多的重复字节,它们可以用少量码来替换。

-- 写于2002.11.01

MP3解码算法分析(11)——MP3/AAC中用到的术语汇编

1. 概念集合

采样率、频率、通道数 插值算法以补偿失真 音量调节

数字滤波算法进行的变换,如高通、低通滤波器 三维声音效果

听觉通道可以与视觉通道同步

声音的空间信息,而且与视觉信息,虚拟空间 声源的位置

双工理论,两种因素:两耳间声音的到达时间差和两耳间声音的强度差。 时间差是由于距离的原因造成

强度差是由于信号的衰减造成,信号的衰减是因为距离而自然产生的 音场的宽度和深度

音场的宽度利用时间差,延时不能太长,否则就会变为回音 音场的深度利用强度差

三维音效可使普通双声道声音听起来具有三维音场的效果 熵编码 AAC

多相积分滤波 PQF

子带 Subband,对于每一个子带都传输一个独立的增益,作为边信息(side information)。 边信息 Side information

恺撒窗 Kaiser-Bessel derived 正弦窗

最小均方预报器 LMS-adapted(Least Mean Square) 暂时噪音抑制 TNS(Temporal Noise Shaping) 霍夫曼(Huffman)编码 量化和比例缩放 M/S 矩阵 强度立体声 耦合通道 增益控制 混合滤波段 回溯的适应预报

掩蔽型通用子带综合编码和复用算法 MusicAM 自适应谱分析听觉熵编码 ASPEC 比特池

2. 缩写词汇编

PLA 专利许可协议 Patents Licence Agreement

AAC 高级音频编码 Advanced Audio Coding 简称A2B或AAC,1997年形成国际标准ISO 13818-7

PQF 多相积分滤波 Polyphase Quadrature Filter Side 边信息 side information)。

MDCT 修饰离散余弦变换 Modified Discrete Cosine Transform KBD 开窗 Kaiser-Bessel derived LMS 最小均方预报器 Least Mean Square

TNS 暂时噪音抑制 Temporal Noise Shaping Huffman 霍夫曼编码

IMDCT 逆向修饰离散余弦变换

MusicAM 掩蔽型通用子带综合编码和复用算法 ASPEC 自适应谱分析听觉熵编码 STB 机顶盒

DAB 数字音频广播 PE 心理声学熵 VLC 变长编码

3. Miscs

entropy 熵

Ctrl+B, Match Brace, Find matching brace (,[,{ or },],)

MP3解码算法分析(12)——L3帧解码器分析

1. 涉及的概念

比特池 BitReserve 类:实施 L3 比特池 变量 br 在 L3Decoder 类中;

帧 Frame 声道 Channel 边 Side

三边信息 III_side_info_t 子带索引 SBI

颗粒信息 gr_info_s 时域信息 Temporaire 槽 Slot

2. L3 表与数据结构 - 子带索引 SBI

static class SBI {

public int[] l; //

public int[] s; // Scalefac_Band 子带比例系数

public SBI() { l=new int[23]; s=new int[14]; }

public SBI(int[] thel, int[] thes) { l=thel; s=thes; } }

- 颗粒信息 gr_info_s

static class gr_info_s {

public int part2_3_length = 0; // 主数据长度 ?

public int big_values = 0; // Huffman 相关的概念 ? public int global_gain = 0; // 全局增益 ?

public int scalefac_compress = 0; // 比例系数压缩开关位 public int window_switching_flag = 0; // 窗口切换标志位 ...

public int count1table_select = 0; //

public gr_info_s() { table_select=new int[3]; subblock_gain=new int[3]; } }

- 时域信息 Temporaire // 声道 ? static class temporaire {

public int[] scfsi; // 四个位的比例系数选择信息 scfsi

public gr_info_s[] gr; // 颗粒数组 public temporaire() {

scfsi = new int[4]; gr = new gr_info_s[2];

gr[0] = new gr_info_s(); gr[1] = new gr_info_s(); // 每帧两个颗粒 } }

- 时域信息 temporaire2

static class temporaire2 { // 层3比例系数 public int[] l; // [cb=23]

public int[][] s; // [window=3][cb=13] public temporaire2() { l=new int[23]; s=new int[3][13]; } }

- 三边信息类结构 III_side_info_t static class III_side_info_t {

public int main_data_begin = 0; // 主数据开始 public int private_bits = 0; // 私有位

public temporaire[] ch; // 声道数组 public III_side_info_t() {

ch = new temporaire[2]; // 两个声道 ch[0] = new temporaire(); ch[1] = new temporaire(); } }

- 比例系数表 Sftable class Sftable { public int[] l; public int[] s;

public Sftable() { l = new int[5]; s = new int[3]; } }

3. 比特蓄水池分析

MP3 比特流中,帧间是关联的,N 帧依赖于 N-1 帧;第 N 帧的数据或在当前帧或也在比特蓄水池中。

该类实施单比特作为整数的存储操作,以提高存储速度,其操作是建立在队列的基础上,只是将 bit 当作整

数 int 来操作,表面看来有点浪费,但和把 8bits 打包/提取一个 byte 相比,可以提高2倍速度。

// 评论: 没有范围检查,因此可能存在缓冲区下溢或溢出问题 final class BitReserve {

private static final int BUFSIZE = 4096*8; // 内部比特缓冲区尺寸,必须2的幂

private static final int BUFSIZE_MASK = BUFSIZE-1; // 快速实施模操作用的掩码 private final int[] buf = new int[BUFSIZE]; // 比特缓冲区

private int offset, totbit, buf_byte_idx; // 写偏移 offset、读的总比特数、读位置

BitReserve() { offset=0; totbit=0; buf_byte_idx=0; } // 构造函数

public int hsstell() { return(totbit); } // 返回蓄水池总的比特数 public int hgetbits(int N) { // 从蓄水池中读取 N 个比特 totbit += N; int val = 0, pos = buf_byte_idx; if (pos+N < BUFSIZE) while (N-- > 0) {

val <<= 1; val |= ((buf[pos++]!=0) ? 1 : 0); // 每个 bit 合成成一个 val 整数

} else while (N-- > 0) {

val <<= 1; val |= ((buf[pos]!=0) ? 1 : 0);

pos = (pos+1) & BUFSIZE_MASK; // 缓冲区绕回起点,循环队列操作 }

buf_byte_idx = pos; // 缓冲区读取指针(尾)重置 return val; }

public int hget1bit() { // 从蓄水池中取出下个比特 totbit++; int val = buf[buf_byte_idx];

buf_byte_idx = (buf_byte_idx+1) & BUFSIZE_MASK;

return val; }

public void hputbuf(int val) { // 写 8bits 到蓄水池中 int ofs = offset;

buf[ofs++] = val & 0x80; buf[ofs++] = val & 0x40; buf[ofs++] = val & 0x20; buf[ofs++] = val & 0x10; buf[ofs++] = val & 0x08; buf[ofs++] = val & 0x04; buf[ofs++] = val & 0x02; buf[ofs++] = val & 0x01;

offset = (ofs==BUFSIZE) ? 0 : ofs; // 缓冲区写入指针(头)重置 }

public void rewindNbits(int N) { // 在流中后退 N 个比特 totbit -= N; buf_byte_idx -= N;

if (buf_byte_idx<0) buf_byte_idx += BUFSIZE; // 退到头,越过低,就到顶 }

public void rewindNbytes(int N) { // 在流中后退 N 个字节

int bits = (N << 3); // 每字节含 8 = 1<<3 个比特 totbit -= bits; buf_byte_idx -= bits;

if (buf_byte_idx<0) buf_byte_idx += BUFSIZE; } }

4. 获取边信息 LayerIIIDecoder class:

// 从流中读取边信息,假定整个帧数据已经准备好。单声道: 136bits/17bytes 立体声: 256bits/32bytes

private boolean get_side_info() { int ch, gr;

if (header.version()==Header.MPEG1) { // MPEG1 标准

si.main_data_begin = stream.get_bits(9); // 读 9bits 的边信息的主数据开始

if (channels==1) si.private_bits=stream.get_bits(5); // 如果是单声道 读取 5bits 的私有位

else si.private_bits = stream.get_bits(3); // 否则 读取 3bits 的私有位 for (ch=0; chsi.ch[ch].scfsi[0] = stream.get_bits(1); si.ch[ch].scfsi[1] = stream.get_bits(1); si.ch[ch].scfsi[2] = stream.get_bits(1); si.ch[ch].scfsi[3] = stream.get_bits(1); }

for (gr=0; gr<2; gr++) { // 颗粒循环 for (ch=0; chsi.ch[ch].gr[gr].scalefac_compress = stream.get_bits(4); si.ch[ch].gr[gr].window_switching_flag = stream.get_bits(1); if ((si.ch[ch].gr[gr].window_switching_flag) != 0) {

si.ch[ch].gr[gr].block_type = stream.get_bits(2);

si.ch[ch].gr[gr].mixed_block_flag = stream.get_bits(1); ...

// 设置 region_count 参数,在这种情况下是固定的。 if (si.ch[ch].gr[gr].block_type == 0) { //

return false; // 边信息损坏 } else if (si.ch[ch].gr[gr].block_type == 2 // && si.ch[ch].gr[gr].mixed_block_flag == 0) // {

si.ch[ch].gr[gr].region0_count = 8; } else {

si.ch[ch].gr[gr].region0_count = 7; }

si.ch[ch].gr[gr].region1_count = 20 - // si.ch[ch].gr[gr].region0_count;

} else { //

si.ch[ch].gr[gr].table_select[0] = stream.get_bits(5); si.ch[ch].gr[gr].table_select[1] = stream.get_bits(5); si.ch[ch].gr[gr].table_select[2] = stream.get_bits(5); si.ch[ch].gr[gr].region0_count = stream.get_bits(4); si.ch[ch].gr[gr].region1_count = stream.get_bits(3); si.ch[ch].gr[gr].block_type = 0; }

si.ch[ch].gr[gr].preflag = stream.get_bits(1);

si.ch[ch].gr[gr].scalefac_scale = stream.get_bits(1); si.ch[ch].gr[gr].count1table_select = stream.get_bits(1); } // for ch } // for gr

} else { // MPEG-2 LSF, SZD: MPEG-2.5 LSF

si.main_data_begin = stream.get_bits(8);

if (channels == 1) si.private_bits = stream.get_bits(1); else si.private_bits = stream.get_bits(2);

for (ch=0; chsi.ch[ch].gr[0].part2_3_length = stream.get_bits(12); si.ch[ch].gr[0].big_values = stream.get_bits(9); ...

si.ch[ch].gr[0].scalefac_scale = stream.get_bits(1); si.ch[ch].gr[0].count1table_select = stream.get_bits(1);

} // for (ch=0; ch} // if (header.version() == MPEG1)

return true; }

5. decodeFrame 主函数 LayerIIIDecoder class public void decodeFrame() { decode(); }

// 解码一帧,并将解样填充进输出样本缓冲。子带样本是缓冲的并同时传递给合成过滤器。

private float[] samples1 = new float[32]; private float[] samples2 = new float[32];

public void decode() { // 帧解码主函数 int i, nSlots = header.slots(); // 槽数 int flush_main; // int gr, ch, ss, sb, sb18; // 颗粒 granule,声道 channel, int main_data_end; // 主数据结束指针 int bytes_to_discard; //

get_side_info(); // 读取边信息

for (i=0; ibr.hputbuf(stream.get_bits(8));

main_data_end = br.hsstell() >>> 3; // 前一帧的

if ((flush_main = (br.hsstell() & 7)) != 0) { // 尾部不足一个字节(8bits)的位数

br.hgetbits(8 - flush_main); // 舍取多余的头

main_data_end ++; // 主数据两端多跨一个字节 }

bytes_to_discard = frame_start - main_data_end // 丢掉的字节数 - si.main_data_begin; // ? frame_start += nSlots;

if (bytes_to_discard < 0) return; // 数据非法返回 if (main_data_end > 4096) { // ? frame_start -= 4096; br.rewindNbytes(4096); }

for (; bytes_to_discard>0; bytes_to_discard--) // 丢掉该丢的字节 br.hgetbits(8);

for (gr=0; grelse // 否则是 MPEG-2 LSF, SZD: MPEG-2.5 LSF

get_LSF_scale_factors(ch, gr); // 获取 LSF 比例系数

huffman_decode(ch, gr); // Huffman 解码 dequantize_sample(ro[ch], ch, gr); // 逆量化样本 }

stereo(gr); // 立体声处理 if ((which_channels==OutputChannels.DOWNMIX_CHANNELS)// 如果是混合声道 && (channels > 1)) // 且确有多声道 do_downmix(); // 混合各声道

for (ch=first_channel; ch<=last_channel; ch++) { // 为每个要输出的声道计算输出

reorder(lr[ch], ch, gr); // 重新排序 antialias(ch, gr); // 消除假象 hybrid(ch, gr); // 立体声处理 for (sb18=18; sb18<576; sb18+=36) // 频率倒置 for (ss=1; ssout_1d[sb18 + ss] = -out_1d[sb18 + ss];

if ((ch==0) || (which_channels==OutputChannels.RIGHT_CHANNEL)) { for (ss=0; ssfor (sb18=0; sb18<576; sb18+=18) { samples1[sb] = out_1d[sb18+ss]; sb++; }

filter1.input_samples(samples1);

filter1.calculate_pcm_samples(buffer); }

} else {

for (ss=0; ssfor (sb18=0; sb18<576; sb18+=18) { samples2[sb] = out_1d[sb18+ss]; sb++; }

filter2.input_samples(samples2);

filter2.calculate_pcm_samples(buffer);

} // for ss } // if ch

} // 输出声道循环结束 channels

} // 颗粒循环结束 granule counter++; // 帧数累加 buffer.write_buffer(1); // 缓冲区写入 }

MP3解码算法分析(13)——音频编码基础

1. 数字音频的文件格式

文件扩展名 说明

---------- ------------------------------------------------------------ .PCM PCM 数据序列

.VOC Creative 公司的波形音频文件格式。 .WAV Microsoft 公司的波形音频文件格式。 .SND NeXT 计算机的波形音频文件格式。 .AIF Apple 计算机的波形音频文件格式。 .MID MIDI 文件格式。

.RMI Microsoft 公司的 MIDI 文件格式。它可以包括图片、标记和文本。

2. 音频编码基础

从信息保持的角度讲,只有当信源本身具有冗余度,才能对其进行压缩。 根据统计分析结果,语音信号存在

着多种冗余度,其最主要部分可以分别从时域和频域来考虑。 另外由于语音主要是给人听的,所以考虑了人

的听觉机理,也能对语音信号实行压缩 压缩编码的可能性

- 声音信号中包含有大量的冗余信息, - 可以利用人的听觉感知特性, - 可以利用语音信号的生成机理,

语音压缩编码方法分类

基于感知模型的压缩 (波形编码 waveform codecs) – benefits : generic

– drawbacks : highest compression rates are difficult to achieve – Examples : PCM, ADPCM, Subband CCITT G.711 PCM 64kb/s CCITT G.721 ADPCM 32Kb/s

CCITT G.726 ADPCM 48, 32, 24, 16 Kb/s 基于产生模型的压缩 (参数编码,源编码)

– benefits : highest possible compression – drawbacks : signal source(s) must be know – Examples : vocoder

混合编码 Hybrid compression – Examples : CELP

子带编码(Sub-band coding)基本原理:

– 利用带通滤波器(BPF)把声音信号按频率范围划分成几个组成部分(子频带,子带) – 低频部分能量较集中,量化精度要高,取样频率可稍低。

– 高频部分是摩擦音、噪音,量化精度可低些,但取样频率要稍高。 – 不同子频带作不同的ADPCM编码处理,然后再复合在一起。 3. 声音信号的数字化 - 取样 sampling

* 原理:用一定速率的离散取样序列可以代替一个连续的频带有限的信号而不丢失任何信息。 * Nyquist sampling theorem “For lossless digitization, the sampling rate should be at least

twice the maximum frequency response.” - 量化 quantization,AD conversion

* what? 使用有限位数的整数来近似地表示实型量的样本值,也称为 A/D conversion。 * 量化精度:用多少个二进位来表示每一个样本,也称为量化位数。声音信号的量化位数一般是 4/6/8,

12或16 bits 。

* 量化位数的多少决定了动态范围和噪声大小. - 编码 encoding 数字化声音举例

质量 采样频率(kHz) 样本精度(bit) 声道数 数据率 频率范围(Hz) ---- ------------- ------------- ------ ---------- ------------ 电话 8 8 1 8 KB/s 200~3,400 AM 11.025 8 1 11.0 KB/s 50~7,000 FM 22.050 16 2 88.2 KB/s 20~15,000 CD 44.1 16 2 176.4 KB/s 20~20,000 DAT 48 16 2 192.0 KB/s 20~20,000 4. 声音还原

- 解码 Decoding

- 解量化 Dequantization (D/A conversion) - 插值 Interpolation

重建声音的质量评价(客观法)

* 声音质量的客观度量主要用信噪比SNR(signal to noise ratio)来度量。 SNR=10 log10(s2/n2)

其中,s为原始声音信号,n为原始声音信号与重建声音信号的误差信号) * 计算并不复杂,但与人对声音的感知不完全一致。 重建声音的质量评价(主观法)

分数 质量级别 失真程度 ---- -------------- -------- 5 优(Excellent) 无察觉

4 良(Good) (刚)察觉但不讨厌 3 中(Fair) (察觉)有点讨厌 2 差(Poor) 讨厌但不反感 1 劣(Bad) 极讨厌(令人反感)

5. MPEG-1音频编码的原理(MP3)

- 将audio信号分割成32个不同的子频带;

- 充分利用听觉系统的掩蔽特性(主要是利用频域掩蔽特性), 保留能被感知的信号而扔掉被掩蔽的信号;

- 计算出以频率为自变量的噪声掩蔽阈值(masking threshold),按照信掩比 SMR来决定分配给子带信号的量

化位数,控制和调节各个不同子频带的量化编码。

支持4种不同模式: - 单通道,

- 双通道(二个独立的声音信号编码在一个比特流中), - 立体声(左、右声道的信号编码在一个比特流中),

- 联合立体声(利用左、右声道信号的相关性,降低输出比特流的码率)。 MPEG-1音频压缩的层(layer)

MPEG-1音频压缩分为3个不同的层, 基本模型相同,层号越高,性能越好,也越复杂。高层次的解码器能对所有

低层比特流数据进行解码。在保持CD立体声音质的前提下,3个层次的编码效率(压缩倍数)和输出码率:

编码器层号 压缩方法 压缩倍数 输出码率 复杂度 延迟时间(ms) 应用

------------ -------- -------- ------------ ------ ------------ -------------------

层1(Layer 1) MUSICAM 1: 4 384 kbps 较简单 19-50 小型数字合式磁带

层2(Layer 2) MUSICAM 1: 6-8 256-192 kbps 中等 35-100 数字音频广播, VCD等

层3(Layer 3) ASPEC 1: 10-12 128-112 kbps 最复杂 59-150 ISDN上的声音传输

MPEG-1音频压缩(层1)

- 多相滤波器组把输入信号变换到32个子带中去, 子带是线性划分的; - 每帧包含384个样本,32个子带分别输出12个样本; - 心理声学模型仅使用频域掩蔽特性;

- 以12个样本为一组,“量化和编码器”根据SMR确定每个子带的比特分配,然后按比特分配进行量化和编码。

- 被高度掩蔽的子带不需要进行编码。

MPEG-1音频压缩(层 2 )

- 每帧有1152个样本, “量化和编码器”对一个子带中的三个样本组(3x12个样本)一起进行编码;

- 除了使用频域掩蔽特性之外还利用了时间掩蔽特性;

- 在低、中和高频段对比特分配作了限制(低频段子带:4位,中频段子带:3位,高频段子带:2位),对比例因子

和量化样本值的编码也更紧凑。

MPEG-1音频压缩(层3)

- 使用比较好的临界频带滤波器,把声音频带分成非等带宽的子带,

- 心理声学模型除了使用频域掩蔽特性和时间掩蔽特性之外,还考虑了立体声数据的冗余, - 使用了霍夫曼(Huffman)编码器。

多相滤波器组

- 多相滤波器组把输入信号变换到32个频域子带中去。子带的划分是非线性的,因为人耳的听觉特性是以“临

界频带”来划分的,在一个临界频带之内,很多心理声学特性都是一样的。

MPEG-1 声音(层1/2)编码原理 - MUSICAM(Masking pattern adapted Universal Subband Integrated Coding And Multiplexing) 声音掩蔽特性自适应的通用子带综合编码和复合技术

MPEG-1 声音(层3)编码原理

- ASPEC(Adaptive Spectral Perceptual Entropy Coding of high quality musical signal) 高质量音乐信号自适应谱感知熵编码(技术) - 使用ASPEC(Adaptive Spectral Perceptual Entropy Encoding)和OCF(Optimal Coding In The Frequency

domain)导出的算法,

- 使用了改进离散余弦变换MDCT(modified discrete cosine transform),对层1和层2的滤波器组的不足作了

一些补偿。MDCT把子带的输出在频域里进一步细分, 以达到更高的频域分辨率。

在各种速率下MP3的性能比较

20-20kHz 的全频带数字声音(即44.1kHz取样的, 量化精度为16位的数字声音),若采用MP3编码,在各种不同

数据速率下其输出(简称为MP3数字声音)所能达到的声音质量: 数据率(kb/s) 压缩倍数 声音质量 声音带宽(kHz) 声道 ------------ -------- -------- ------------- ---- 8 96:1 电话 2.5 单 16 48:1 优于短波 4.5 单 32 24:1 优于调幅广播 7.5 单 56-64 26-24:1 相当调频广播 11 立体 96 16:1 接近CD 15 立体 112-128 14-12:1 CD >15 立体

MP3解码算法分析(14)——算法框架与体系结构

1. 数字信号处理的基本算法框架

变换 Transform: 傅立叶变换 FFT, 逆傅立叶变换 IFFT, 离散余弦变换 DCT, 逆离散余弦变换 IDCT

修饰离散余弦变换 MDCT, 逆修饰离散余弦变换 IMDCT 过滤器 Filter: FIR, IIR, IIR Buquad, FIR LMS Adaptive

窗口器 Windowing: 恺撒取窗 Kaiser, Bartlett, 汉明窗 Hamming, Hann

信号产生: 正弦波 Sinusoidal, Square, Triangular, 均衡随机数 Uniform Random,

Signal Generation 高斯随机数 Gaussian Random

矢量统计: 最小 Min, 最大 Max, 平均 Mean, 标准差 Std_Dev, AutoCorr, CrossCorr,

Vector Statistics Norms

矢量操作: 加 Add, 减 Sub, 乘 Mul, 绝对值 Abs, 平方 Sqr, 平方根 Sqrt, 自然对数 Ln,

Vector Manipulation 对数 Log10, Exp 指数, 点积 Dotprod, Set, 零 Zero, 复制 Copy 2. MP3 解码器体系结构 Decoder Architecture

| +----------------+ +------------------------------------+ | MP3比特流 | | | | 霍夫曼解码 Huffman Decoder | | Bitstream | -> | 帧解包 | -> +----------+-------------------------+ | | | Unpacking: | | | | | | +----------V-------------------------+ | | | 头信息 | | 解量化 Requantization | | | | Header | | 立体声处理 Stereo Processing | | | | | -> +----------+-------------------------+ | | | 边信息 | | | | | Sideinfo, | +----------V-------------------------+ | | | | | 合成过滤器组 Synthesis Filter Bank | | | | 比例系数 | | 反锯齿 Anti-aliasing | | | | Scalefactors | | 逆修饰离散傅立叶变换 IDMCT | | 音频输出

| | | -> | 多相正交镜象滤波器 PQMF | -> | PCM Audio

| +----------------+ +------------------------------------+ | Out

3. Intel 的 IPP/RMS 概要

IPP - Integrated Performance Primitives 集成执行元 RMS - Realtime Media Stream 实时媒体流 什么是“集成执行元 IPP”?

- 通用的跨平台、跨设备的低层软件层: * 手持设备、桌面、服务器处理器; * Windows、Linux 操作系统; - 一个实用的多媒体功能 API:

* 数学、信号、音频、图象、视频、图形; - 高度优化的特定处理器代码: * 完全开拓处理器的潜力;

IPP 功能组

- 横向:矢量代数、信号处理、图象处理

- 纵向:视频编码、图象编码、音频编码、语音编码 IPP 编解码块

语音 Speech GSM AMR G.723.1 音频 Audio MP3, AAC 视频 Video MPEG-4 H.263

图象 Image JPEG, JPEG-2000

什么是 “实时媒体流 RMS”?

- 一个流媒体框架:语音、音频和视频

主机流(On-host streaming): 在软件之间流动;

网络流(Network streaming): 在网络上的主机之间流动;

- 一个可插拔的、面向图形的且拥有实时可配置特性的流框架 * 用于媒体处理通用的高层 API 函数 * 通过配置动态编解码开关的扩展能力 - 跨平台的框架

* 操作系统:Win32, WinCE, Linux

* 硬件体系结构:IA-32, StrongARM, XScale - 轻量级

不大于 120K (软件过滤除外)

多媒体编解码挑战 - 算法

* 复杂的概念体系 Conceptually complex

* 计算密集性 Computationally intensive

* 多学科的专门知识 Demand multidisciplinary expertise - 成功关键

* 重量级优化 Heavy optimization

* 体系结构开拓 Architectural Exploitation * 定点数的调谐 Fixed-point tuning - 限制因素

* 优化就是资源集中 Optimization is resource intensive * 优化的代码是不可移置的 Optimized code is non-portable * 资源转向会影响上市时间 Diverted resources = TTM impact * 确定 CPU 的路线图是困难的 Pacing CPU roadmap difficult

MP3 解码器应用编程接口 Decoder API

比特流处理器 Bitstrem processor 霍夫曼解码器 Huffman decoder 重新量化 Requantization 立体声处理 Stereo processing

逆离散傅立叶变换 IMDCT (include anti-alias butterflies) 合成多相正交镜像过滤组 Synthesis PQMF bank

4. 关于数字影院系统 DTS

“DTS系统中采用的数字音频压缩算法——相干声学编码(CAC), 主要目的就是用于提高民用音频重放设备重

放的音频质量的,其音频重放质量可以超越原有的如 CD唱片的质量。同时通过更多扬声器的使用,使得听众

可以感受到普通立体声无法达到的声音效果。 因此总体目标就是将听众真正的带入专业的音响领域及多声道

环绕声的天地。” —— 胡泽(北京广播学院录音艺术学院)

“DTS是由美国数码影院系统公司(Digital Theater System)开发的一种数码环绕声多声道系统,它采用相干

声学数字音频处理技术,以可逆式数字音频压缩处理。 在解压还原时,可以完整地恢复原始的音频信息,对

数字音频信息没有任何删减。DTS相干声学所采用的数字压缩技术,与其他同类技术相比,为数字音频信号提

供了更为独特的编码和处理方式,它能让数码的音频处理变得更为简洁和高效率。” —— Jon

Kirchner(美

国 DTS 数字影院系统公司的副总裁)

5. 数学符号处理软件 Maple 的 Blog 介绍

如果知道Matlab,多半会知道Mathmatica和Maple,他们都是数学软件,前者以数值计算和工程仿真著名,后

者则以符号计算立足。

我现在放弃 Mathmatica 改用Maple 了,因为 Maple 能够解几乎所有的数学问题,包括偏微分方程,图论,

数论,数据分析等等, 总之如果参加数学建模比赛的话,你不希望你手中的软件功能少的。但对于初学数学

软件的人,我还是推荐Mathmatica,毕竟文档组织的比较好。

Maple的最新版v9.5,在中文环境下十分令人恼火的事是输入计算指令的时候,Maple的Java环境输入7个英文

字符后光标就会错一位,输入到14个字符后,光标就相差一个字,这样修改公式变得极为困难, 因为鼠标点

下的地方如果按下删除键,会删掉前面几位前的字符!经过整整两天的折腾,发现即使把Maple打上9.5.1 的

补丁也于事无补,但是总算发现,如果将MathInput中的字体设置为“宋体”而不是缺省的“monospaced”,

那么这个定位bug就不存在了。显然要我每新建一行公式就手动设置一下字体,绝对是在羞辱我资深电脑用户

的威名,因此,马上想到修改Maple的Math Input缺省字体,火气很大的是Maple中没有提供这个选项。 难道

要让我放弃Maple的漂亮的Java环境,改用原始的C编的Classic Worksheet Maple 9.5吗? 我决定还是深入分析Maple程序的结构。通过使用UltraEdt工具Find in files功能,我发现的monospaced 实

际上是指一类字体,如果在中文Windows下,定义就在Maple 9.5\\jre\\lib\\font.properties.zh中,分别修改

下面的注释行文本为其下一行的文本,马上可以解决这个恼人的问题:

-- 丁丁的blog: 大成若缺,其用不弊。大盈若沖,其用不窮。大直若屈,大巧若拙,大辯若訥,大智若愚。

-- http://ericguo.cnblogs.com/archive/2005/02/06/102642.aspx

MaPLe ['Meipl] [植]枫,枫木,淡棕色。我取的意思是两年前使用的数学软件 Maple,当然,现在我更喜欢

Mathematica,但是总不能把名字叫Mathematica吧

-- Maple's Blog 我不在江湖,江湖却有我的传说 http://my.opera.com/Maple2005/

Maple 是加拿大滑铁卢大学(University of Waterloo)和 Waterloo Maple Software 公司注册的一套为微积 分、线性代数和微分方程等高等数学使用的软件包。 它是当今世界上最优秀的几个数学软件之一,它以良好

的使用环境、强有力的符号计算、高精度的数值计算、灵活的图形显示和高效的编程功能, 为越

来越多的教

师、学生和科研人员所喜爱,并成为他们进行数学处理的工具。

Maple软件适用于解决微积分、解析几何、线性代数、微分方程、计算方法、概率统计等数学分支中的常见计

算问题。Maple采用字符行输入方式,输入时需要按照规定的格式输入,虽然与常见的数学格式不同,但灵活

方式,也很容易理解。输出则可以显字符方式和图形方式,产生的图形结果可以很方便地剪贴到Windows应用 程序内。

-- http://lib.verycd.com/2005/01/01/0000033302.html

6. New words, terms and commands

From deterministic to stochastic self-similarity 从确定论到随机论,自相似性 Pace 踱步,步幅 multidisciplinary 多学科的 exploit 开拓、开采 collateral 间接的 interpolation 内插值 extrapolation 外推插值 exponential 指数、幂数 maple 枫树

greedy heuristic algorithm 贪心启发算法 TTM impact - Time-to-Market 上市时间影响 DTS - Digital Theater System 数字影院系统 CAC - COHERENT ACOUSTICS CODING 相干声学编码

PQMF - Polyphase Quarter Mirror Filter 多相正交镜象滤波器

MP3解码算法分析(15)——心理声学模型及比特噪声分配

1. MP3 编解码算法涉及的概念

每个帧 Frame 有 2 个颗粒 Granule,或 1152 个样 Sample 每个颗粒 Granule 有 576 个样 Sample 每个颗粒 Granule 有 32 个子带 Subband 每个样 Sample 有 1 或 2 个通道 Channel 每个子带 Subband 有 18 个样 Sample

子带编码 Subband Coding 下降采样 Downsampling

多率信号处理 Multirate Signal Processing 差分脉冲编码调制 DPCM

频率响应 Frequency Response 量级、大小(dB) Magnitude

过滤器组 Filter Bank

频率划分 Frequency separation.

时间到频率的影射 Time to frequency mapping.

子带滤波分析 Analysis Subband Filter

分离宽带信号为 Split the broadband signal with sampling 32个等宽的子带 frequency fs into 32 equally spaced

subbands with sampling frequencies fs/32. 子带滤波器响应 Subband Filter Response

2. 心理声学模型II

安静阈值 The Threshold in Quiet

SPL - Sound Pressure Level 声压级,是一个评测听觉刺激强度的标准 理想的临界子带 Ideal Critical Bands

人耳对不同频率的分辩率是不同的 Different frequency resolution in human hear.

Bark : unit of critical bands. Frequency translation to Bark :

每个临界子带的带宽 Bandwidth of each critical bands : 频域掩蔽 Frequency Masking MNR(m) = SNR - SMR(m) 时域掩蔽 Temporal Masking

* 在一个很短的时间内,中 SPL较大

的声音(遮噪者,masker)会遮蔽 * 这时 SPL 若 maskee 出现在前,称为 pre-masking;反之则称为 post-masking 其他的声音若出现在掩蔽中,就会被遮蔽。值得注意的是所影响的时间

较长,至少有的时间;

* 时轴上的遮蔽效应重要之处在于能够让前迴音(pre-echo)的杂讯不被人耳察觉;

Pre-echo Noise Cancellation 感知熵 PE Perceptual Entropy

压缩的限度 Limit 每个取样在

维持 建议 CD 质量的熵为 2.1;Suggest CD quality at 2.1bits/sample. 窗跳转状态图 Window Switch:

No Attack

+--------------------------------------------------+ | ___ | V Attack Attack \\/ \\ No Attack | Normal --------> Start --------> Short ----------> Stop /\\ / A | \\__/ | Attack | NA +--------------------------------+

若出现了两个声音,不论声音出现的先后顺序,其SPL较小的声音(被遮噪者,maskee); 较大的声音在时轴上会产生两种遮蔽效应:

post-masking160ms,而pre-masking大约只有post-masking十分之一on compressibility. PE 的单位是 bits/sample,代表CD音质的前提下,能够压缩到最低的位元数。

颗粒熵 Limit on compressibility of a granule.

每颗粒 576 样,要达到 CD 质量,最少 PE > 576*2.1 > 1209 取窗决策: 如果 PE > 1800 选择短窗;否则取长窗 3. MP3 编码流程

| +------------+ +------+ +-----------------+ +-----------+ | Digital | | 32-Channel | | | | Bit Allocation | | | | Audio | -> | Polyphase | -> | MDCT | -> | Loop | -> | Bitstream | | Signal | | Analysis | | | | Quantization | | Formating | | | | Filterbank | | | | Huffman Coding | | | | | +------------+ +--^---+ +--^---------+----+ | | | | | | | | | | | +--------------------+---+ | +------V----+ | | | Coded

| -> | Psychoacoustic | ------+ | Coding of | -> | | -> | Audio | | Model II | | Side Info | | | | Data

| +------------------------+ +-----------+ +-----------+ | 编码的四个主要部分:

- 滤波器组 The Filterbank

* 分析子带过滤器 Analysis Subband Filter

* 修饰离散傅立叶变换 MDCT (Modified Discrete Cosine Transform) - 心理声学模型II The Psychoacoustic Model II - 比特或噪音分配 Bit or Noise Allocation * 非均衡量化 Non-uniform quantization - 比特流格式器 The Bitstream Formatter * 霍夫曼编码 Huffman coding

MDCT 含三部分:

MDCT Window 窗框,取窗类型 : Normal, Start, Short, Stop * 正常窗 Normal : 较好的频率解析度 * 短窗 Short : 较好的时间解析度

* 动态窗口 : 动态自适应取窗动态适配输入子带信号 DCT 离散余弦变换

* 窗框的选择是依据是第二声响心理模型分析音讯特性之后所得到的资讯, * 在一般音讯稳定的情形下,使用长窗框来提供最细的频谱解析度。 * 然而当子频带讯号变动大时,需变化窗框长度以提供较精细的时间轴(pre-echo)杂讯不被

人耳察觉。Short window, 12点;others, 36点。 Anti-alias 长窗框的假象处理

* 在使用长窗框得到较细的频谱解析度时,同时会有假象(Aliasing)的产生。 * 由于滤波器排的特性,当原始音讯被分成32个子频带时,在频谱上可见邻近的子频带间有

明显的重叠现象,而处于重叠区间的讯号将会同时影响两个子频带。

* 所以在经过MDCT转成频线讯号时,需对邻近相对应的频线讯号做特别处理,以减少因假象

所造成的杂讯,影响音讯品质。

* 假象处理的方式是将处在相对应位置的频线之能量做一定比例的增减。 4. 比特和噪声分配 BANA - Bit and Noise Allocation

对每个颗粒最终有多少比特可用于真正的音频主数据(main data)呢?

每帧可用来编码比特数(1152取样个数/frame)

以单声道为例,在位速128kbps且取样频率为44.1kHz时,每帧有3344个编码位元。 此外还要扣掉其它的资

料所用到的位元数,如头(header)需要32位元,而边信息(side info)需要136/256位元, 另外可选择是否

要加入16位元CRC。所以平均每帧可用来对音乐讯号编码的位元数为bits/frame,又因为实作上编码时的最

小单位为granule(一帧相当于两个granules),可以算出每个granule可用来编码的位元数为3040/2 = 1520

bits/granule。 位元分配的目的:

在于使得每个比例因子频带之遮罩杂讯比(MNR)达到最大,以得到最佳之音讯品质。其为一反覆的过程,每

次找出最小MNR的频带,分配位元给此频带以提高 MNR,接著重新计算各频带的 MNR,然后再不断重覆此调

整过程, 直到没有足够的位元可供调整因此在进行位元分配之前,需要知道可以用来编码之位元数,以及

各子频带的MNR。 比特噪声分配的流程

i Scalefactor band index

Delta Nonuniform quantizer parameter stepsize SFi Scale factor of ith ScaleFactor band max_bits_granule 576 * 2.1 = 1209

Noise Allocation: Given dt

Sum(i, Huffman Codeword in the ith band <= max_bits_granule Bit Allocation: Given SFi

MNR(i, SF'i) >= MNR(i, SFi) > 0 BANA Process: Start:

Select Delta

Quantization, Huffman Encoding

IF Coding bits < max_bits ? No Start

Distortion Calculation

Compare the masking threshold, Tuning scalefactors IF Perform Quantization Agina ? Yes Start Finish:

MP3编码在量化时,将频域划分成21个 scalefactorband 为单位来处理。

* 量化器对频谱量化后,计算需要多少位元来对做霍夫曼编码,若超过可用之位元数,就要调整 stepsize

的大小,如此可以降低所需的位元数。

* 决定好量化后的频线后,再和未量化前的频线做杂讯估计, distortion(band) = Sum(i=low..high, (xr(i) - ix(i)^(4/3) * Sqrt(2,4)^step)/bandwidth(band))

其中 xr(i) 是经过MDCT转换后所得到的频线、ix(i)是量化后的结果、bandwidth(band)代表该频带的频

宽、low(band)与high(band)分别代表该频带的边界,而 ix(i)^(4/3) * Sqrt(2,4)^step 则是反量化的 运算。

5. DCT MDCT DFT 之间的关系

MPEG-1 的层次3保留层次2的 1152 采样窗口及反向兼容的FFT多相滤波器,此外,增加了一个改进的DCT滤波

器。DCT优于DFT(离散傅利叶变换)之处包括多个乘-累积运算的一半和生成系数的一半, 因为计算的正弦部

分缺省,而 DCT一般包括比较简单的数学。普通DCT的带通脉冲响应的有限长度可能会导致块边界效应。MDCT

覆盖了分析块,低通译码音频滤去了假频,从而消除了这些效应。MDCT还有一个比标准DCT高一些的变换编码

增益,而且它们的基本功能适应于更好的带通响应。

MPEG-1层次3的DCT副频带大小不等,适应于人的听觉系统的临界频带。在第3层次中,译码器必须支持恒定位

速(Bitrate)和可变位速的位流。(然而,许多层次的1和12译码器还要处理可变的位速)。最后,第3层次编码

器在归档或发送增加的无损耗压缩前要对量化系数进行赫夫曼编码,位流范围为32~320kbps,而128kbps速率

就能获得近CD音质效果,是一种能使双频道ISDN成为未来家庭的宽带传送管道的重要技术规格。 -- 作者:Brian Oipert http://www.ednc.com.cn/txt/001102.htm

6. New words, terms and commands

Psychoacoustic 心理声学

Chaotic system 混沌系统、无秩序系统 SNR - Signal Noise Ratio 信噪比 SMR - Signal-to-Mask Ratio (MPEG) 信掩比 MNR - Masked Noise Ratio 掩噪比 Granule 颗粒 MPEG-2 BC - MPEG-2 Back Compatibility 向后兼容

MPEG-2 LSF - Low Sample Frequency 低采样频率?低比特率?

SPL - Sound Pressure Level 声压级,是一个评测听觉刺激强度的标准

MP3编解码算法分析(16)——比特流格式化

1. 比特流格式化器

霍夫曼编码 Huffman coding

* 霍夫曼编码用于表示量化的频率样本

Huffman codes are used to represent the quantized frequency samples.

* 这些霍夫曼编码是量化样本数据的高效比特流,它们是变长编码,其代价是增加了编码复杂度。

These Huffman codes are variable-length codes that allow for more efficient bitstream representation of the quantized samples at the cost of additional complexity. * 相关概念

不同的存储区域采用不同的霍夫曼码表 Different Huffman book used in different region. 以下参数将记录在MP3的边信息中 Following parameter will be recorded in the side info of MP3.

| Scale Factor | Frequency lines (576) Huffman Code |

+------------------+----------+----------+----------+------------+-----------------+

| | region 0 | region 1 | region 2 | 1 or 0 | 00000000 | | |<---------- big_value --------->|<- count1 ->|<- zero_region ->|

| | | |

|<- part2_length ->|<-------------part3_length ----------------->| |

| | |

|<--------------- part2_3_length

------------------------------->| |

* 霍夫曼编码过程

- Search Zero region first without encoding by 2-outcome grouping. - Search count 1 region by 4-outcome group.

- If it has components greater than 1, then it belongs to region 2.

- According to the location of region 2 to find out region 0 and region 1 by formula in Spec.

- Search big value by 2-outcome group.

* 霍夫曼码表 Huffman Book There are 32 pre-defined books used in big_value region. The max. input 15 + 2^linbits - 1.

For index ≥16, which implies the max of the outcome (x,y) ≥ 16, it will use a 256-entry

table to encode the part less than 16 in Human books. * 问题:什么是 big_value, linbits? 2-outcome group?

比特蓄水池 Bit reservoir

比特池操作的目的是在帧间微调比特量,以达到比特流的帧间压缩。

比特流格式化 Bitstream format

0~575 频率线对应 0~22 KHz 的频率,如频率 11.84KHz 对应第 310 条频率线 Frame = Header#32 + CRC#16 + SideInfo#256 + MainData#VL

头包括帧信息,如:层、位速、采样频率、模式等等。Header includes frame information like layer,

bit-rate, sampling frequency, mode and etc.

校验位用于错误发现和纠正 CRC is used in error detection and correction.

边信息包括解码信息,如:量花器参数、修饰离散傅立叶变换块类型、霍夫曼码表选择等。 边信息尺寸单

声道 136 位,立体声 256 位; Side information includes decoding information like quantizer parameters, MDCT block type,

Huffman table selection and etc. 136 or 256 bits for mono or stereo.

主数据包括比例系数和霍夫曼编码位。Main data includes scale factors and Huffman coded bits.

2. 计算音质纹理特征的四种方法

时频分析(傅立叶变换) Time-Frequency analysis

信号处理(短时傅立叶变换和离散小波变换) Signal processing (STFT, DWT) 感知编码(梅尔倒频谱系数) Perceptual (MFCC, MPEG)

纹理窗统计 Statistics over texture window

3. 音乐内容特征 Musical Content Feature 音质纹理 Timbral Texture (19) 频谱形状 Spectral Shape

梅尔倒频谱系数 MFCC (perceptually motivated features, ASR) 节奏结构 Rhythmic structure (6) 节奏柱状图特性 Beat Histogram Features 和声内容 Harmonic content (5)

音高柱状图特征 Pitch Histogram Features

4. 知识思考关键词

Knowlet 小知识 mindmatrix 意识矩阵 mindmap 意识图 mindstream 意识流 Microcontent 微内容

syntax 语法,有秩序的句子、句子构造、句法 grammar 文法、入门的书、有关原理的书

semantic 语义的 semantic shift 语义转移 semantic interpretation rule 语义解释规则 System Integration 系统集成, Application Integration 应用集成, Knowledge Integration 知识集成

Dynamic real time classification of radio signals 电台实时搜索分类识别 Personal QQ/MSN IM search engine 个体即时搜索引擎 Blog / Knowledge Search Engine 知识搜索引擎 5. New words, terms and commands

CCD - Charge Couple Device 充电藕荷装置 Oscillator 振荡器 Breakthrough 突破

Timbre 音品, 音色, 音质

Timbral Texture Feature 音质纹理特征

Centroid Rolloff Flux 质心印出变迁,bandwidth moments Spectrum and shape descriptors 频谱和形状描述符

Feature space, feature vector and feature extractor 特征空间、特征矢量和特征抽提 STFT Wavelet 小波 Time-frequency Heisenberg uncertainty 时频海森堡不确定性

Perceptual Audio Coding (slow encoding, fast decoding) 感知音频编码(编码慢、解码快) STFT - Short-Time Fourier Transform 短时傅立叶变换 DWT - Discrete Wavelet Transform 离散小波变换 MFCC 梅尔倒频谱系数

rhythm analysis 节奏、韵律分析

poetry 作诗方法 (iamb 抑扬格, trochaic 抑扬格诗句) Peak picking 峰拣取 Histogram 柱状图

Trajectory 轨迹、轨道 Genre 类型、流派

parametric classifiers 参数分类器, Gaussian Classifier 高斯分类器

GMM - Gaussian Mixture Model 高斯混合模型 Non-parametric classifiers 非参数分类器

Nearest-neighbor classifiers(K-NN) 最近邻居分类

CAR - Computer Audition Research 计算机听力研究,MARSYAS Real time music-speech discrimination 实时音乐语音辨别 Work-Collaborations 协作分工

Auditory Scene Analysis 听觉场景分析 6. Miscs

音视频标准框架汇总

ISO/IEC 11172-3, MPEG-1, Layer 3

ISO/IEC 13818-3, MPEG-2 BC/LSF, Layer 3

中国上调国内GDP数据 日本丧失亚洲经济控股权

http://finance.sina.com.cn/j/20051229/23532239043.shtml

日本经济从 1956 年开始起飞,到 1968年,日本经济规模已超过联邦德国,仅次于美国,在西方世界居第二

位。以1985年“广场协议”决定日元升值为标志, 日本在经济上实现明治时代制订的追赶美欧目标已基本实

现。而到1987年,日本的人均国民收入超过美国。

在日本经济实现“脱亚入欧”的同时,日本的经济力量已全面压过亚洲邻国。根据IMF的历史数据,自上世纪

60年代晚期始,日本经济便开始了“一家独大”的霸主地位。这一“大”就维持了30多年。

MP3编解码算法分析(17)——帧头帧尾关联表

1. 头结构 HEADER

bits name comments

---- ----------------- ------------------------------ 12 sync 0xFFF

1 version 1=mpeg1.0, 0=mpeg2.0

2 lay 4-lay = layerI, II or III 1 error protection 0=yes, 1=no 4 bitrate_index see table below 2 sampling_freq see table below 1 padding

1 extension see table below 2 mode see table below

2 mode_ext used with \"joint stereo\" mode 1 copyright 0=no 1=yes 1 original 0=no 1=yes

2 emphasis see table below

2. 位速表 bitrate_index

Mpeg Layer 1 2 3 4 5 6 7 8 9 10 11 12 13 14 ------- --- -- -- -- --- --- --- --- --- --- --- --- --- --- --- mpeg1.0 1 32 64 96 128 160 192 224 256 288 320 352 384 416 448 2 32 48 56 64 80 96 112 128 160 192 224 256 320 384 3 32 40 48 56 64 80 96 112 128 160 192 224 256 320 mpeg2.0 1 32 48 56 64 80 96 112 128 144 160 176 192 224 256 2 8 16 24 32 40 48 56 64 80 96 112 128 144 160 3 8 16 24 32 40 48 56 64 80 96 112 128 144 160

3. 采样率表 sampling_freq 强调 emphasis: Mpeg 0 1 2 0 \"none\"

------- ----- ----- ----- 1 \"50/15 microsecs\"

mpeg1.0 44100 48000 32000 2 \"reserved\" must not be used! 22050 24000 16000 3 \"CCITT J 17\"

4. 模式 mode: 扩展模式 mode extension: 接合立体声模式扩展 jsbound mode_ext:

0 \"stereo\" 0 MPG_MD_LR_LR layer 0 1 2 3 1 \"joint stereo\" 1 MPG_MD_LR_I 1 4 8 12 16 2 \"dual channel\" 2 MPG_MD_MS_LR 2 4 8 12 16 3 \"single channel\" 3 MPG_MD_MS_I 3 0 4 8 16 5. ID3 帧尾结构 TRAILER,尾部 128 字节 帧尾结构

offset type len name

-------------------------------------------- 0 char 3 tag \"TAG\" 标记 3 char 30 title 标题 33 char 30 artist 艺术家 63 char 30 album 专集 93 char 4 year 年月 97 char 30 comments 评论

127 byte 1 genre 流派 --------------------------------------------

流派表 genre:

0 \"Blues\" 20 \"Alternative\" 40 \"AlternRock\" 60 \"Top 40\"

1 \"Classic Rock\" 21 \"Ska\" 41 \"Bass\" 61 \"Christian Rap\" 2 \"Country\" 22 \"Death Metal\" 42 \"Soul\" 62 \"Pop/Funk\" 3 \"Dance\" 23 \"Pranks\" 43 \"Punk\" 63 \"Jungle\" 4 \"Disco\" 24 \"Soundtrack\" 44 \"Space\" 64 \"Native American\"

5 \"Funk\" 25 \"Euro-Techno\" 45 \"Meditative\" 65 \"Cabaret\" 6 \"Grunge\" 26 \"Ambient\" 46 \"Instrumental Pop\" 66 \"New Wave\" 7 \"Hip-Hop\" 27 \"Trip-Hop\" 47 \"Instrumental Rock\" 67 \"Psychadelic\" 8 \"Jazz\" 28 \"Vocal\" 48 \"Ethnic\" 68 \"Rave\"

9 \"Metal\" 29 \"Jazz+Funk\" 49 \"Gothic\" 69 \"Showtunes\" 10 \"New Age\" 30 \"Fusion\" 50 \"Darkwave\" 70 \"Trailer\" 11 \"Oldies\" 31 \"Trance\" 51 \"Techno-Industrial\" 71 \"Lo-Fi\" 12 \"Other\" 32 \"Classical\" 52 \"Electronic\" 72 \"Tribal\" 13 \"Pop\" 33 \"Instrumental\" 53 \"Pop-Folk\" 73 \"Acid Punk\" 14 \"R&B\" 34 \"Acid\" 54 \"Eurodance\" 74 \"Acid Jazz\" 15 \"Rap\" 35 \"House\" 55 \"Dream\" 75 \"Polka\" 16 \"Reggae\" 36 \"Game\" 56 \"Southern Rock\" 76 \"Retro\" 17 \"Rock\" 37 \"Sound Clip\" 57 \"Comedy\" 77 \"Musical\" 18 \"Techno\" 38 \"Gospel\" 58 \"Cult\" 78 \"Rock & Roll\" 19 \"Industrial\" 39 \"Noise\" 59 \"Gangsta\" 79 \"Hard Rock\" 80 \"Unknown\"

6. 帧长计算公式 frame length: MpegVer Layer Length

------- ----- ------------------------------------------ mpeg1.0 1 (48000 * bitrate)/sampling_freq + padding 2&3 (144000 * bitrate)/sampling_freq + padding mpeg2.0 1 (24000 * bitrate)/sampling_freq + padding 2&3 (72000 * bitrate)/sampling_freq + padding

MP3编解码算法分析(18)——“边信息Side Info”数据结构

1. 数据结构

属性变量 window_switching_flag, block, subblock, block_type, mixed_block_flag, subblock_gain[],

SBI, l/s, ro, lr, scfsi, linbit, region_count 等是干什么用的? 如何从整体上理解它们?看来从理解数据结构开始可能会顺利些。 结构的结构:

III_side_info_t : temporaire[2] : gr_info_s[2]

Sftable : l[5], s[3] // 比例系数 ? SBI : l[23], s[14]

宏常量:

private static final int SSLIMIT = 18; // 每个子带样条限制数 private static final int SBLIMIT = 32; // 子带限制数 帧结构:

Header(32) + CRC(0,16) + Side Information(136,256) + Main Data 注意:因为蓄水池的作用,主数据不必从本帧开始。

表1 缩放因子数据表格

缩放因子带(21) 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 ----------------- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 缩放因子比特数(l) 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 缩放因子 (s) 1 1 0 0 1 1 4 5 3 7 5 3 0 3 0 1 0 7 5 0 3 si : // : 边信息的数据结构 si 由 ... 组成 main_data_begin : 9 // 读 9bits 的边信息的主数据开始 private_bits : 3 // 读取 3bits 的私有位,对多声道 ch* : // 为每个声道读取 4bits 的比例系数选择信息scfsi

scfsi*4 : 1

gr* : // * 由多个颗粒 gr 数组组成 ch* : // * 由多个声道 ch 数组组成 part2_3_length : 12 // : n 主数据由 n 个比特位组成 big_values : 9 //

global_gain : 8 // 全局增益 scalefac_compress : 4 // 比例系数压缩 window_switching_flag : 1 // 窗口切换开关位

block_type : 2 // 块类型 window_switching_flag!=0 mixed_block_flag : 1 // 混合块标记 table_select[0] : 5 // 表选择 table_select[1] : 5

subblock_gain[0] : 3 // 子块增益 subblock_gain[1] : 3 subblock_gain[2] : 3

region0_count = 8 | 7 // block_type==2 && mixed_block_flag==1 region1_count = 20 - region0_count; preflag : 1 // ? scalefac_scale : 1 count1table_select : 1

SCFSI 表示 \"比例系数选择信息 SCale Factor Selection Information\",它是一个在 MPEG音频数据帧中的

技术标记,用以表示用于帧中两个颗粒 granule 数据公用的比例系数。

增加 frame-discard 表示引入两个标记仍然没有足够的主数据,命名为 'bytes_to_discard' 和 'sig'。此

外还包括变量 main_data_end1 以保持对主数据结束的跟踪。

在边信息块中增加变量 subblock_gain 和 mixed_block_flag,用于当 window_switching_flag 为 0时,以

避免当该条件满足时程序瘫痪。

当边信息确定后,在 main_data_start 的计算之后,移动 start(2) 和 start(1) 计算。增加变量 'new'以

替换 start(1) 函数,替换的 start(1) 而非 start(2)。须注意那些帧1中 main_data_begin 非 0 的文件。

2. 算法投影概念

MP3 解码算法含 MP1, MP2, MP3-1, MP3-2BC, MP3-2.5LSF 等版本,另处理又可根据模式分为Mono, Stereo,

Joint Stereo, Dual Channel 等, 如何针对其中一种进行算法投影?或如何获取算法视图? 3. Miscs

Run the eleMentSoft.com website, IP: 219.146.62.234

Bugs found in GBHZ.js simple and traditional chinese translation match table. 回 -> 迴 New words, terms and commands

sftable - Scale Factor Table 比例系数表 ancillary data 辅助数据

MP3编解码算法分析(19)——数量分析与Huffman解码

1. 关于MP3文件的数量计算

文件名称: C:\\JYC2005SZ\\Java\\Jamp3\\世纪春雨-彭丽媛.mp3 最后修改(本地): 9/7/2005 18:07:17.281

文件格式: mp3PRO?(FhG), MPEG Layer-3, 128 Kbps, 44100Hz, 16bits, Stereo 文件尺寸: 2.21 MB = 2,326,528 Byte 每帧采样点(固定): 1152 = 576 * 2 = 18 * 32 * 2 每帧时长: 26.122 ms = 1152 / 44.1

帧数: 5566 = int(2326528 * 8.0 / 128 / 26.122) 每帧样本字节数(固定):144 = 1152 / 8.0

帧长: 418 Byte = 144.0 * 128000 / 44100 = 2326528 / 5566.0 采样点: 6,412,032 = 5566 * 1152

未压缩状态下大小: 24.45 MB = 25,648,128 = 6,412,032 * 2 * 2 时间长度: 2:25.397 = 6412032 / 44100 = 145.397 s

感知熵(PE): 2.903 bps = 2326528 * 8 / 6412032 = 128000 / 44100.0 2. Huffman 解码“声道颗粒”数据

private void huffman_decode(int ch, int gr) { x[0] = 0; y[0] = 0; v[0] = 0; w[0] = 0;

int part2_3_end = part2_start + si.ch[ch].gr[gr].part2_3_length; int num_bits, region1Start, region2Start; int index, buf, buf1;

huffcodetab h; // Huffman 码表

// 为短块寻找区域边界

if (si.ch[ch].gr[gr].window_switching_flag!=0 && si.ch[ch].gr[gr].block_type==2) {

// Region2, MS: Extrahandling for 8KHZ region1Start = (sfreq==8) ? 72 : 36; // sfb[9/3]*3=36 or in case 8KHZ = 72 region2Start = 576; // 短块没有 Region2 } else { // 为长块寻找区域边界 buf = si.ch[ch].gr[gr].region0_count + 1;

buf1 = buf + si.ch[ch].gr[gr].region1_count + 1;

if (buf1 > sfBandIndex[sfreq].l.length - 1) buf1 = sfBandIndex[sfreq].l.length - 1; region1Start = sfBandIndex[sfreq].l[buf]; // SBI.l(si.ch.gr.region0_count) region2Start = sfBandIndex[sfreq].l[buf1]; // MI }

index = 0; // 读 bigvalues 区 for (int i=0; i<(si.ch[ch].gr[gr].big_values<<1); i+=2) { if (ih = huffcodetab.ht[si.ch[ch].gr[gr].table_select[0]]; else if (ih = huffcodetab.ht[si.ch[ch].gr[gr].table_select[1]]; else

h = huffcodetab.ht[si.ch[ch].gr[gr].table_select[2]]; huffcodetab.huffman_decoder(h, x, y, v, w, br);

is_1d[index++] = x[0]; is_1d[index++] = y[0];

CheckSumHuff = CheckSumHuff + x[0] + y[0]; }

// 读 count1 区 h = huffcodetab.ht[si.ch[ch].gr[gr].count1table_select+32]; num_bits = br.hsstell();

while ((num_bits < part2_3_end) && (index < 576)) { huffcodetab.huffman_decoder(h, x, y, v, w, br); is_1d[index++] = v[0]; is_1d[index++] = w[0]; is_1d[index++] = x[0]; is_1d[index++] = y[0];

CheckSumHuff = CheckSumHuff + v[0] + w[0] + x[0] + y[0]; num_bits = br.hsstell(); }

if (num_bits > part2_3_end) { // 读过头,绕回 br.rewindNbits(num_bits - part2_3_end); index -= 4; }

num_bits = br.hsstell();

if (num_bits < part2_3_end) // 解散资料位 br.hgetbits(part2_3_end - num_bits);

nonzero[ch] = (index < 576) ? index : 576; // 其余为零位 if (index < 0) index = 0;

for (; index<576; index++) is_1d[index] = 0; // 复位 is_1d,也许无必要 }

3. 解码器 LayerIIIDecoder 类全局属性释义 is_1d = new int[SBLIMIT*SSLIMIT+4]; out_1d = new float[SBLIMIT*SSLIMIT];

ro = new float[2][SBLIMIT][SSLIMIT]; // ch=2 lr = new float[2][SBLIMIT][SSLIMIT]; // ch=2 prevblck = new float[2][SBLIMIT*SSLIMIT]; // ch=2

k = new float[2][SBLIMIT*SSLIMIT]; // ch=2 sfb=SBLIMIT*SSLIMIT=32*18=576 nonzero = new int[2]; // ch=2 III_scalefac_t = new temporaire2[2]; // ch=2 III_scalefac_t[0] = new temporaire2(); III_scalefac_t[1] = new temporaire2(); scalefac = III_scalefac_t;

temporaire2[] scalefac; //

int max_gr, sfreq; // 采样频率 int frame_start, part2_start; //

sfBandIndex = new SBI[9]; // SZD: MPEG2.5 +3 indices // 采样率比例系数子带索引? int[] l0 = {0, 6, 12, 18, 24, 30, 36, 44, // 子带系数 l0:长度为 cb=23 ? 54, 66, 80, 96, 116, 140, 168, 200, 238, 284, 336, 396, 464, 522, 576 }; int[] s0 = {0, 4, 8, 12, 18, 24, 32, 42, // 子带系数 s0:长度为 14 56, 74, 100, 132, 174, 192 };

int[] l1 = {0, 6, 12, 18, 24, 30, 36, 44,

54, 66, 80, 96, 114, 136, 162, 194, 232, 278, 330, 394, 464, 540, 576 }; int[] s1 = {0, 4, 8, 12, 18, 26, 36, 48, 62, 80, 104, 136, 180, 192 }; int[] x = {0}; int[] y = {0}; // int[] v = {0}; int[] w = {0};

4. common.c in FHG decoder

/* Double and SANE Floating Point Type Definitions */ typedef struct IEEE_DBL_struct { unsigned long hi; unsigned long lo; } IEEE_DBL;

typedef struct SANE_EXT_struct { unsigned long l1; unsigned long l2; unsigned short s1; } SANE_EXT;

int eob; /* end of buffer index */ int eobs; /* end of bit stream flag */

5. AAC与AC-3的关系

AAC 是 MPEG-2 音频编码的一部分;

AC-3 是杜比公司所开发的Dolby AC-3是美国ATSC标准中所采用的音频编码方法;

AAC和AC-3都是变换编码算法,但AAC使用了分辨率更高的滤波器组,因此它可以达到更高的压缩比。另外AAC

还使用了临时噪声重整、后向自适应线性预测、联合立体声技术和量化哈夫曼编码等最新技术, 这些新技术

的使用都使压缩比得到进一步的提高。而且,AAC 比AC-3更灵活,它支持更多种采样率和比特率、支持1个到

48个音轨、支持多达15个低频音轨、具有多种语言的兼容能力、还有多达15个内嵌数据流。 6. New words,terms and commands

AIFF - Audio Interchange File Format

MP3编解码算法分析(20)——关于多相滤波器组Polyphase Filter Bank

1. 基本公式

图3展示了 Rothweiler 建议的 MPEG 音频编码过滤器组是怎样的结构,作为比较,图4 则展示了同样的 ISO

MPEG音频标准的过滤器组的流程图。

通过结合等式和流程图中的步骤,人们便会推出如下过滤器组输出的公式: St[i] = Sum(k=0..63, Sum(j=0..7, M[i][k] * (C[k+64j] * x[k+64j])) (1)

其中:

i 是子带编号,范围从 0 到 31;

st[i] 是第 i 个过滤器在时间 t 时样条输出值, t 是一个 32 个音频样条的间隔的整数倍; C[n] 是标准中定义的分析窗口的 512 个系数之一; x[n] 是音频输入样条值,而

M[i][k] = cos[(2*i+1)*(k-16)*pi/64] 是分析系数矩阵;

2. 算法分析

以上等式已进行部分优化以减少计算的数量。因为括号内的函数与 i 值无关, 而 M[i][k] 与 j 无关,因此

32 过滤器组实际上只需要 512 + 32*64 = 2,560 次乘法和 64*7+32*63 = 2,464 次加法, 大致每个滤波器

输出需要 80 次乘法和加法。进一步充分的简化还是可能的,如快速 FDCT[9,10] 或 FFT[11]。 注意:该过滤器组实施是紧急采样的,每 32 个输入样条,过滤器组产生 32 个输出样条。实际上,32个子带

滤波器中的每一个都子采样它的输出(by 32)以对每 32 个新的音频采样仅产生一个输出样条。 3. 算法卷绕形式与分析

人们可从 (1) 式转化为熟知的过滤器卷绕等式:

St[i] = Sum(n=0..511,

x[t-n]*Hi[n]) (2) 其中:

x[t] 是时间 t 的音频采样数据,而:

Hi[n] = h[n]*cos[(2*i+1)*(n-16)*pi/64],其中: h[n] = -C[n], 如果 (n/64) 的整数部分是奇数, = C[n] 否则,n = 0 to 511。

在这种形式中,每个子带有它自己的带通(band-pass)滤波响应 Hi[n], 尽管该种形式分析起来容易一些,很

显然它不是一个有效率的方案:该形式的直接实施需要 32 * 512 = 16,384 次乘法和 32* 511 = 16,352 次

加法才能计算出 32 个滤波器的输出。

MP3编解码算法分析(21)——关于MP3的数据表数量考察

1. 比特率 14 种,采样率 6-9 种

Mpeg Layer 1 2 3 4 5 6 7 8 9 10 11 12 13 14 ------- --- -- -- -- --- --- --- --- --- --- --- --- --- --- --- mpeg1.0 3 32 40 48 56 64 80 96 112 128 160 192 224 256 320

采样率表 sampling_freq, sfreq ?

sfreq 0 1 2 3 4 5 6 7 8 Mpeg 0 1 2 Mpeg 0 1 2 Mpeg 0 1 2 ------- ----- ----- ----- ------- ----- ----- ----- ------- ----- ----- ----- mpeg2.0 22050 24000 16000 mpeg1.0 44100 48000 32000 mpeg2.5 11025 12000 8000 2. block_type 表 问题:

块是否就是窗?是同一状态的窗的集合吗?是否就是短窗、长窗、窗跳转状态? 窗切换标记 window_switching_flag 意味着什么? 混合块标记 mixed_block_flag 又意味着什么? 块类型 block_type: 2 短块, 0,1,3 长块

3. LUT 表

LUT 表就是重新排序表 reorder_table? 其尺寸为 9 x 576 每种采样率一张 576 长的子表。 reorder_table = new int[9][]; // 尺寸:9 x 576

for (int i=0; i<9; i++) reorder_table[i] = reorder(sfBandIndex[i].s);

sfBandIndex[0] = {

int[] l0 = {0, 6, 12, 18, 24, 30, 36, 44, // 子带系数 l0:长度为 cb=23 ? 54, 66, 80, 96, 116, 140, 168, 200, 238, 284, 336, 396, 464, 522, 576 }; int[] s0 = {0, 4, 8, 12, 18, 24, 32, 42, // 子带系数 s0:长度为 14 56, 74, 100, 132, 174, 192 }; }

static int[] reorder(int scalefac_band[]) { // 从 LAME 版本转化而来 int j = 0;

int ix[] = new int[576];

for (int sfb=0; sfb<13; sfb++) {

int start = scalefac_band[sfb], end = scalefac_band[sfb + 1];

for (int window=0; window<3; window++) for (int i=start; iix[3 * i + window] = j++; // 最大索引 3*191+2 = 575,最大值 13*3*192 = 7488 }

return ix; }

4. sfreq 和 region_count、cb_width 的关系 sfreq 采样频率编号,0~8 region_count 区计数 ? cb_width 关键子带带宽 ?

buf = si.ch[ch].gr[gr].region0_count + 1; buf1 = buf + si.ch[ch].gr[gr].region1_count + 1;

region1Start = sfBandIndex[sfreq].l[buf]; // SBI.l(si.ch.gr.region0_count) region2Start = sfBandIndex[sfreq].l[buf1]; // MI

cb_width = sfBandIndex[sfreq].s[1];

next_cb_boundary = sfBandIndex[sfreq].l[1]; // 长块: 0,1,3 next_cb_boundary = sfBandIndex[sfreq].s[4]; cb_begin = sfBandIndex[sfreq].s[3];

sfb_start = sfBandIndex[sfreq].s[sfb],

sfb_lines=sfBandIndex[sfreq].s[4] - sfb_start; sfb = sfBandIndex[sfreq].s[10];

sb = sfBandIndex[sfreq].s[11] - sfb;

int[] l0 = {0, 6, 12, 18, 24, 30, 36, 44, // 子带系数 l0:长度为 cb=23 ? 54, 66, 80, 96, 116, 140, 168, 200, 238, 284, 336, 396, 464, 522, 576 }; int[] s0 = {0, 4, 8, 12, 18, 24, 32, 42, // 子带系数 s0:长度为 14 56, 74, 100, 132, 174, 192 };

5. 常数表及其意义分析

- private static final int slen[2][16]

= {{0, ..., 3, 4, 4}, {0, 1, 2, 3, 0, ...3}};

- private final int[] new_slen // 变量 new_slen 使用前已完全初始化,

= new int[4]; // 没必要重新分配数组 - public static final int pretab[22] // 用于解量化 = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, ...}; if (gr_info.preflag != 0) idx += pretab[cb];

- private SBI[9] sfBandIndex // 在解码器构造时,进行初始化 = {SBI(l0={0,6,12,18,24,30,36,44, // 子带系数 l0:长度为 cb=23 ? 54,66,80,96,116,140,168,200, 238,284,336,396,464,522,576},

s0={0,4,8,12,18,24,32,42, // 子带系数 s0:长度为 14 56,74,100,132,174,192} ),...}

- public static final float two_to_negative_half_pow[64] // 用于解量化 = {1.0000000000E+00f, ...};

xr_1d[quotien][reste] *= two_to_negative_half_pow[idx]; // 放缩操作 - public static final float t_43[8192] // 用于解量化 = create() { t_43[i] = (float)Math.pow(i, 4/3.0);}; xr_1d[quotien][reste] = g_gain * t_43[abv]; // dequantize_sample(xr, ch, gr) - public static final float io[2][32] // 用于立体声处理 = {{1.0000000000E+00f, ...}, {..., 2.1579186439E-05f}};

k[0][i] = io[io_type][(is_pos + 1) >>> 1]; // i_stereo_k_values(is_pos, io_type, i)

- int[] is_pos = new int[576]; // 用于立体声处理 float[] is_ratio = new float[576];

- public static final float TAN12[16] // 用于立体声处理 = {0.0f, 0.26794919f, ..., 0.57735027f, 1.0f}; is_pos[i] = scalefac[1].s[j][sfb];

if (lsf) i_stereo_k_values(is_pos[i], io_type, i); // if (is_pos[i] != 7) else is_ratio[i] = TAN12[is_pos[i]];

- private static final float cs[8] // 8 个蝶形操作用的 sin/cos ? = {0.857492925712f, ..., 0.999993155067f}; private static final float ca[8]

= {-0.5144957554270f, ..., -0.00369997467375f};

out_1d[src_idx1] = (bu * cs[ss]) - (bd * ca[ss]); // antialias(ch, gr) 中用到 out_1d[src_idx2] = (bd * cs[ss]) + (bu * ca[ss]);

- public static final float win[4][36] // 四种 Block,36 个 ? = {{-1.6141214951E-02f,...},...,{...,-1.4790705759E-02f}};

win_bt = win[block_type]; // 用在 IMDCT 里

- public static final int nr_of_sfb_block[6][3][4] // 仅用于 get_LSF_scale_data() = {{{6, 5, 5, 5}...},...{...{6,18, 9, 0}}};

- public Sftable sftable[5,3] // LayerIIIDecoder 类里没用到 = Sftable(ll0 = {0, 6, 11, 16, 21}, ss0 = {0, 6, 12});

- private static int reorder_table[9][576]; // 将根据要求生成

MP3编解码算法分析(22)——解码各步骤的输入输出

1. lr, ro, xr 之类的变量是什么? - get_scale_factors(ch, gr); 输出:scalefac[ch]

for (sfb=0; sfb<8; sfb++)

scalefac[ch].l[sfb] = br.hgetbits(slen[0][gr_info.scalefac_compress]); for (sfb=3; sfb<6; sfb++) for (window=0; window<3; window++)

scalefac[ch].s[window][sfb] = br.hgetbits(slen[0][gr_info.scalefac_compress]); - huffman_decode(ch, gr); 输出:is_1d

is_1d[index++] = v[0]; is_1d[index++] = w[0]; is_1d[index++] = x[0]; is_1d[index++] = y[0];

- dequantize_sample(ro[ch], ch, gr); 输出:ro

float[][] xr_1d = xr; // 输出 xr = ro - stereo(gr);

输出:is_pos, is_ratio

if (lsf) i_stereo_k_values(is_pos[i], io_type, i); else is_ratio[i] = TAN12[is_pos[i]]; - reorder(lr[ch], ch, gr); 输出:lr

float[][] xr_1d = xr; // xr = lr - antialias(ch, gr);

输出:out_1d[src_idx1] = (bu * cs[ss]) - (bd * ca[ss]); out_1d[src_idx2] = (bd * cs[ss]) + (bu * ca[ss]); - hybrid(ch, gr); 输出:

inv_mdct(tsOutCopy, rawout, bt);

for (int cc=0; cc<18; cc++) tsOut[cc+sb18] = tsOutCopy[cc]; tsOut[0 + sb18] = rawout[0] + prvblk[ch][sb18 + 0]; prvblk[ch][sb18 + 0] = rawout[18]; 2. *_1d 是什么变量?一维? xr_1d 临时局部变量 is_1d 全局变量 out_1d 全局变量

3. Miscs

FDCT - Forward Discrete Cosine Transform 正向离散余弦变换, 把空间域表示的图变换成频率域的。

MP3编解码算法分析与语音识别系统分析 —— 前言

1. 工作介绍

内容涵盖:主要有MP3编解码、语音识别系统算法分析两部分,以及一些与嵌入式开发及语音信号处理相关的

基础知识;

时间周期:一个月 2005.12.05 - 2006.01.10 2. 工作方法

迭代搜索:网络搜索与概念迭代, 在搜索的内容中,中文有深度的可操作的知识不多;语音识别部分有深度

的内容大都来自台湾, 中国大陆的开放性的知识很少,让人感到可喜的是,虽内容泛泛但质量很

高的教学内容,在大陆也开始大量涌现。

内容形式:统篇均以日志 Blog、小知识 Knowlet、微内容 Microcontent 的形式表达,尽量在离散的概念、

算法、应用之间建立关联; 3. 总体情况

工作状态:应该说本报告处于未完成状态,大部分深度离可实施商有一定距离,但均已处于 We're ready 的

状态;问题、错误在所难免,敬请见谅并请提出宝贵意见。

体会感觉:算法框架庞杂、逐一体察比较艰难; 算法关联性很强,本报告的目的即是尽量通过这种形式摸清

楚语音信号处理的算法谱,为进一步的视频处理算法分析做好准备; 4. 参考资料

请参见 http://del.icio.us/vdml/audio+mp3+mpeg+compress+algorith 云图

因篇幅问题不能全部显示,请点此查看更多更全内容

Top