IMA-ADPCM 算法

本文介绍了IMA - ADPCM声音压缩算法,它是针对16bit声音波形数据的有损压缩算法,压缩比1:4,能以低空间消耗获得高质量声音。著名游戏公司WestWood在多款游戏中使用该技术。文中还介绍了声音信号储存知识,以及该算法的压缩和解压缩过程。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

 

  IMA-ADPCM (ADPCM Adaptive Differential Pulse Code Modulation), 是一种针对 16bit (或者更高?) 声音波形数据的一种有损压缩算法, 它将声音流中每次采样的 16bit 数据以 4bit 存储, 所以压缩比 1:4. 而压缩/解压缩算法非常的简单, 所以是一种低空间消耗,高质量声音获得的好途径. 著名的 WestWood 在它的许多游戏里都使用了这个技术, DUNE II, C&C, RA 等等, 保存声音的数据文件后缀名为 .AUD 的大多用 IMA-ADPCM 压缩. (不过 WestWood 的游戏数据文件大多经过打包, 这些小文件统统放进了一个 .MIX 文件包中, 关于解开 .MIX 文件包, 见 http://www.geocities.com/SiliconValley/8682)

  ADPCM 主要是针对连续的波形数据的, 保存的是波形的变化情况, 以达到描述整个波形的目的. 本文并不想详细介绍 ADPCM 算法原理, 那些是数学知识,有高等数学基础的朋友可以自己研究, 云风数学马马虎虎, 这里也讲不清楚, 但是它的编码和解码的过程却很简洁, 列在后面, 相信大家能够看明白.

  先给不熟悉声音信号的储存的朋友补一课, 不想看就跳过吧 ^_^: 一般游戏中用到的声音有两种不同性质的, 一是波形数据, 是经过事先声音采样录制下来的, 采样时一般按每秒 8千到 4 万次的频率(8Khz ~44.4Khz)记录每次采样时的声音强度, 在播放时, 再以同一频率, 按样本声音的强弱变化触发扬声器, 声音就被重现了, 如果你将采样数据流标在坐标纸上,就会发现是一条波形曲线, 如果采样时将声音信号强弱分为 256 级, 就是我们说的 8bit 采样, 如果分为 65536 级, 就是 16bit 采样了; 另一是 MIDI 类的, 它是将各种乐器的声学性质都事先记录下来, 而数据流中仍旧是按一定频率记录, 但不是每秒数千上万次了, 大约只有几 Hz 到几十 Hz, 将几种乐器按某一音频和强度触发描述下来, 经过声卡合成为波形信号就可以播放了.

  8bit 采样的声音人耳是可以接受的, 比如 Win95 启动的音乐, 而 16bit 采样的声音可以算是高音质了, 现代游戏中也多采用它. (将声音强度分的更细没有太多的意义, 通常都是提高采样频率来近一步提高音质) ADPCM 算法却可以将每次采样得到的 16bit 数据压缩到 4bit ;-) 需要注意的是, 如果要压缩/接压缩立体声信号, 请注意采样时, 声音信号是放在一起的, 需要将两个声道分别处理. OK, 下面列出了其中的奥妙, 请细细品味:


IMA-ADPCM 压缩过程

首先我们认为声音信号都是从零开始的,那么需要初始化两个变量

int index=0,prev_sample:=0;
下面的循环将依次处理声音数据流, 注意其中的 getnextsample() 应该得到一个 16bit 的采样数据, 而 outputdata() 可以将计算出来的数据保存起来,程序中用到的 step_table[], index_adjust[] 附在后面:

int index=0,prev_sample:=0;

while (还有数据要处理) {
    cur_sample=getnextsample();       // 得到当前的采样数据
    delta=cur_sample-prev_sample;     // 计算出和上一个的增量
    if (delta<0) delta=-delta,sb=8;   
    else sb=0;                        // sb 保存的是符号位

    code = 4*delta / step_table[index]; // 根据 steptable[] 得到一个 0~7 的值
    if (code>7) code=7;                // 它描述了声音强度的变化量

    index+=index_adjust[code];          // 根据声音强度调整下次取 steptable 的序号
    if (index<0) index=0;            // 便于下次得到更精确的变化量的描述
    else if (index>88) index=88;

    prev_sample=cur_sample;

    outputode(code|sb);                 // 加上符号位保存起来
    }

IMA-ADPCM 解压缩过程

  接压缩实际是压缩的一个逆过程, 同样其中的 getnextcode() 应该得到一个编码, 而 outputsample() 可以将解码出来的声音信号保存起来. 这段代码同样使用了同一个的 setp_table[] 和 index_adjust() 附在后面:

int index=0,cur_sample:=0;

while (还有数据要处理) {
    code=getnextcode();           // 得到下一个数据

    if ((code & 8) != 0) sb=1 else sb=0;
    code&=7;                      // 将 code 分离为数据和符号

    delta=(step_table[index]*code) /4 + step_table[index] / 8;
    // 后面加的一项是为了减少误差 

    if (sb==1) delta=-delta;

    cur_sample+=delta;            // 计算出当前的波形数据
    if (cur_sample>32767) output_sample(32767);
    else if (cur_sample<-32768) output_sample(-32768);
	else output_sample(cur_sample);

    index+=index_adjust[code];
    if (index<0) index=0;
    if (index>88) index=88;
  }

附表

int index_adjust[8] = {-1,-1,-1,-1,2,4,6,8};

int step_table[89] = { 7, 8, 9, 10, 11, 12, 13, 14, 16, 17, 19, 21, 23, 25, 28, 31, 34, 37, 41, 45, 50, 55, 60, 66, 73, 80, 88, 97, 107, 118, 130, 143, 157, 173, 190, 209, 230, 253, 279, 307, 337, 371, 408, 449, 494, 544, 598, 658, 724, 796, 876, 963, 1060, 1166, 1282, 1411, 1552, 1707, 1878, 2066, 2272, 2499, 2749, 3024, 3327, 3660, 4026, 4428, 4871, 5358, 5894, 6484, 7132, 7845, 8630, 9493, 10442, 11487, 12635, 13899, 15289, 16818, 18500, 20350, 22385, 24623, 27086, 29794, 32767 };


  关于 WestWood 的 .AUD 文件,结构比较简单, 这里顺带提一下, 有兴趣可以自己写处理 AUD 文件的程序 ;-) 其 8bit 的声音压缩算法尚不知晓, 但用的最广泛的 16bit 声音正是用 IMA-ADPCM 压缩, 每个 AUD 文件都有一个文件头, 结构如下:
struct {
    unsigned short int samplespersec;  // 频率
    long int           size;           // 除掉文件头的大小
    long int           outsize;        // 输出数据大小 (通常是 4 倍)
    unsigned char      flags;          // 位 0 描述是否立体声, 位 1 描述是否 16 bit
    unsigned char      type;           // 1=WW 压缩, 99=IMA ADPCM
    }
AUD 文件的声音信号是按块存放的, 每块大约 512 字节, 没一块都有一个块头结构:
struct {
    unsigned short int size;           // 压缩过的数据大小
    unsigned short int outsize;        // 输出数据大小 (通常是 4 倍)
    long int           id;             // 永远是 0x0000DEAF
    }

本文参考了 Vladan Bato 写的 AUD 文件格式描述. 可以去他的网页 http://www.geocities.com/SiliconValley/8682 找到原文和他写的 AUD,WAV 转换程序.另外, Allegro 的爱好者可能想自己加入 AUD 的支持(Allegro 3.1 新增 Plug-In 支持, 增加新文件类型很方便), 不妨看看 http://www.alphalink.com.au/~tjaden, 这里有完成了的 AUD 支持库.
### IMA-ADPCM 编码解释 #### 定义 IMA-ADPCM (Adaptive Differential Pulse Code Modulation) 是一种用于16位或更高级别的声音波形数据的有损压缩算法[^2]。这种算法特别适合处理音频信号,能够有效地减小文件大小而不显著降低音质。 #### 工作原理 在工作过程中,IMA-ADPCM采用了一种简化的方法来进行预测编码。具体来说: - **采样与量化**:原始的声音信号被采集并转化为数字化形式,即从模拟域转移到数字域,在此阶段会进行初步的取样和量化操作。 - **差分脉冲编码调制**:接着利用自适应机制调整步长因子,基于前一时刻样本值的变化趋势对未来样本做出预估;随后计算实际样本值同预期之间的差异,并仅对该误差项实施进一步的四比特量化处理而非完整的十六比特表示方式。这样的做法不仅减少了所需传输的信息量,同时也保持了较好的听觉效果[^3]。 #### 特点优势 - **高效压缩比率**:实现了大约1:4的比例缩减,意味着每四个字节可压缩成一个字节存储或传送。 - **简易实现**:由于采用了查找表代替复杂的浮点运算,使得整个过程变得相对容易编程且运行效率高。 - **跨平台兼容性好**:可以在多种操作系统上找到相应的支持库函数,比如Windows下的`.wav`格式就内置对此类编解码的支持功能[^1]。 ```python def ima_adpcm_encode(sample_data): """ A simplified pseudo-code representation of the IMA-ADPCM encoding process. Args: sample_data (list): List containing original 16-bit PCM samples Returns: list: Encoded ADPCM data as a sequence of nibbles (half-bytes) """ encoded_output = [] predictor_state = initial_step_size = previous_sample_value = 0 for current_sample in sample_data: difference = current_sample - previous_sample_value quantized_diff, new_predictor_state = apply_quantization(difference, predictor_state) # Store only lower four bits representing quantized difference value encoded_output.append(quantized_diff & 0xF) reconstructed_signal = update_reconstruction(previous_sample_value, quantized_diff, new_predictor_state) previous_sample_value = clamp(reconstructed_signal) predictor_state = adjust_predictor(new_predictor_state, abs(difference)) return encoded_output def apply_quantization(diff, state): """Quantizes differences using adaptive step size based on given state.""" pass # Placeholder function to demonstrate concept def update_reconstruction(prev_val, qdiff, state): """Updates signal reconstruction with applied quantization and returns clamped result.""" pass # Placeholder function to demonstrate concept def adjust_predictor(state, diff_magnitude): """Adjusts prediction parameters according to magnitude of change between consecutive samples.""" pass # Placeholder function to demonstrate concept def clamp(value): """Ensures that values do not exceed valid range after processing.""" min_val, max_val = -(2**15), 2**15 - 1 return max(min(max_val, value), min_val) # Example usage would involve passing actual audio sample data into this encoder method. ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值