作者:xiaojiewen1128 ADPCM(Adaptive Differential Pulse Code Modulation),是一種針對 16bits( 或8bits或者更高) 聲音波形數據的一種有損壓縮算法,它將聲音流中每次采樣的 16bit 數據以 4bit 存儲,所以壓縮比 1:4. 而且壓縮/解壓縮算法非常簡單,所以是一種低空間消耗,高質量高效率聲音獲得的好途徑。保存聲音的數據文件后綴名為 .AUD 的大多用ADPCM 壓縮。 ADPCM 主要是針對連續的波形數據的,保存的是波形的變化情況,以達到描述整個波形的目的,由于它的編碼和解碼的過程卻很簡潔,列在后面,相信大家能夠看懂。 8bits采樣的聲音人耳是可以勉強接受的,而 16bit 采樣的聲音可以算是高音質了。ADPCM 算法卻可以將每次采樣得到的 16bit 數據壓縮到 4bit 。需要注意的是,如果要壓縮/解壓縮得是立體聲信號,采樣時,聲音信號是放在一起的,需要將兩個聲道分別處理。 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); // 加上符號位保存起來 } 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; } 附 ADPCM壓縮算法代碼實現 /*********************************************************** Copyright 1992 by Stichting Mathematisch Centrum, Amsterdam, The Netherlands. All Rights Reserved Permission to use, copy, modify, and distribute this software and its documentation for any purpose and without fee is hereby granted, provided that the above copyright notice appear in all copies and that both that copyright notice and this permission notice appear in supporting documentation, and that the names of Stichting Mathematisch Centrum or CWI not be used in advertising or publicity pertaining to distribution of the software without specific, written prior permission. STICHTING MATHEMATISCH CENTRUM DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL STICHTING MATHEMATISCH CENTRUM BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. ******************************************************************/ /* ** Intel/DVI ADPCM coder/decoder. ** ** The algorithm for this coder was taken from the IMA Compatability Project ** proceedings, Vol 2, Number 2; May 1992. ** ** Version 1.2, 18-Dec-92. ** ** Change log: ** - Fixed a stupid bug, where the delta was computed as ** stepsize*code/4 in stead of stepsize*(code+0.5)/4. ** - There was an off-by-one error causing it to pick ** an incorrect delta once in a blue moon. ** - The NODIVMUL define has been removed. Computations are now always done ** using shifts, adds and subtracts. It turned out that, because the standard ** is defined using shift/add/subtract, you needed bits of fixup code ** (because the div/mul simulation using shift/add/sub made some rounding ** errors that real div/mul don't make) and all together the resultant code ** ran slower than just using the shifts all the time. ** - Changed some of the variable names to be more meaningful. */ #include "adpcm.h" #include #ifndef __STDC__ #define signed #endif /* Intel ADPCM step variation table */ static int indexTable[16] = { -1, -1, -1, -1, 2, 4, 6, 8, -1, -1, -1, -1, 2, 4, 6, 8, }; static int stepsizeTable[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 }; void adpcm_coder(indata, outdata, len, state) short indata[]; char outdata[]; int len; struct adpcm_state *state; { short *inp; /* Input buffer pointer */ signed char *outp; /* output buffer pointer */ int val; /* Current input sample value */ int sign; /* Current adpcm sign bit */ int delta; /* Current adpcm output value */ int diff; /* Difference between val and valprev */ int step; /* Stepsize */ int valpred; /* Predicted output value */ int vpdiff; /* Current change to valpred */ int index; /* Current step change index */ int outputbuffer; /* place to keep previous 4-bit value */ int bufferstep; /* toggle between outputbuffer/output */ outp = (signed char *)outdata; inp = indata; valpred = state->valprev; index = state->index; step = stepsizeTable[index]; bufferstep = 1; for ( ; len > 0 ; len-- ) { val = *inp++; /* Step 1 - compute difference with previous value */ diff = val - valpred; sign = (diff < 0) ? 8 : 0; if ( sign ) diff = (-diff); /* Step 2 - Divide and clamp */ /* Note: ** This code *approximately* computes: ** delta = diff*4/step; ** vpdiff = (delta+0.5)*step/4; ** but in shift step bits are dropped. The net result of this is ** that even if you have fast mul/div hardware you cannot put it to ** good use since the fixup would be too expensive. */ delta = 0; vpdiff = (step >> 3); if ( diff >= step ) { delta = 4; diff -= step; vpdiff += step; } step >>= 1; if ( diff >= step ) { delta |= 2; diff -= step; vpdiff += step; } step >>= 1; if ( diff >= step ) { delta |= 1; vpdiff += step; } /* Step 3 - Update previous value */ if ( sign ) valpred -= vpdiff; else valpred += vpdiff; /* Step 4 - Clamp previous value to 16 bits */ if ( valpred > 32767 ) valpred = 32767; else if ( valpred < -32768 ) valpred = -32768; /* Step 5 - Assemble value, update index and step values */ delta |= sign; index += indexTable[delta]; if ( index < 0 ) index = 0; if ( index > 88 ) index = 88; step = stepsizeTable[index]; /* Step 6 - Output value */ if ( bufferstep ) { outputbuffer = (delta << 4) & 0xf0; } else { *outp++ = (delta & 0x0f) | outputbuffer; } bufferstep = !bufferstep; } /* Output last step, if needed */ if ( !bufferstep ) *outp++ = outputbuffer; state->valprev = valpred; state->index = index; } void adpcm_decoder(indata, outdata, len, state) char indata[]; short outdata[]; int len; struct adpcm_state *state; { signed char *inp; /* Input buffer pointer */ short *outp; /* output buffer pointer */ int sign; /* Current adpcm sign bit */ int delta; /* Current adpcm output value */ int step; /* Stepsize */ int valpred; /* Predicted value */ int vpdiff; /* Current change to valpred */ int index; /* Current step change index */ int inputbuffer; /* place to keep next 4-bit value */ int bufferstep; /* toggle between inputbuffer/input */ outp = outdata; inp = (signed char *)indata; valpred = state->valprev; index = state->index; step = stepsizeTable[index]; bufferstep = 0; for ( ; len > 0 ; len-- ) { /* Step 1 - get the delta value */ if ( bufferstep ) { delta = inputbuffer & 0xf; } else { inputbuffer = *inp++; delta = (inputbuffer >> 4) & 0xf; } bufferstep = !bufferstep; /* Step 2 - Find new index value (for later) */ index += indexTable[delta]; if ( index < 0 ) index = 0; if ( index > 88 ) index = 88; /* Step 3 - Separate sign and magnitude */ sign = delta & 8; delta = delta & 7; /* Step 4 - Compute difference and new predicted value */ /* ** Computes 'vpdiff = (delta+0.5)*step/4', but see comment ** in adpcm_coder. */ vpdiff = step >> 3; if ( delta & 4 ) vpdiff += step; if ( delta & 2 ) vpdiff += step>>1; if ( delta & 1 ) vpdiff += step>>2; if ( sign ) valpred -= vpdiff; else valpred += vpdiff; /* Step 5 - clamp output value */ if ( valpred > 32767 ) valpred = 32767; else if ( valpred < -32768 ) valpred = -32768; /* Step 6 - Update step value */ step = stepsizeTable[index]; /* Step 7 - Output value */ *outp++ = valpred; } state->valprev = valpred; state->index = index; } |