一、搞了幾天終於搞定這個ADPCM解碼了,之前找了很多的資料,大致描述的都是千篇一律,但是基本上都沒有說到細節上,讓我也走了不少彎路,其實主要在細節,網上給的算法是正確的,但是直接運用根本就不行,噪音很大。這一點讓我一直很頭疼,最后還是看了英文資料,才得到解答,還是老外的原始資料好。
二、給個英文參考網址吧
http://www.moon-soft.com/program/FORMAT/windows/wavec.htm
https://ccrma.stanford.edu/courses/422/projects/WaveFormat/
這兩個講的很詳細,請仔細閱讀!通過閱讀我發現細節在與adpcm格式的wav文件的block的特點,每一個block包含header和data兩部分,
Typedef struct{
short sample0; //block中第一個采樣值(未壓縮)
BYTE index; //上一個block最后一個index,第一個block的index=0;
BYTE reserved; //尚未使用
}MonoBlockHeader
關鍵是我們要抓住每一個block的header里面的信息,即sample0,運算的時候注意運用!
三、還是給個代碼吧,多的也不說了!
1、adpcm.c文件代碼
#include"adpcm.h"
/* Intel ADPCM step variation table */
staticintindexTable[16]={
-1,-1,-1,-1,2,4,6,8,
-1,-1,-1,-1,2,4,6,8,
};
staticintstepsizeTable[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
};
voidadpcm_decoder(char*inbuff,char*outbuff,intlen_of_in,structadpcm_state *state )
{
int i=0,j=0;
chartmp_data;
structadpcm_state *tmp_state =state;
longstep;/* Quantizer step size */
signedlongpredsample;/* Output of ADPCM predictor */
signedlongdiffq;/* Dequantized predicted difference */
intindex;/* Index into step size table */
intSamp;
unsignedcharSampH,SampL;
unsignedcharinCode;
/* Restore previous values of predicted sample and quantizer step
size index
*/
predsample =state->valprev;
index =state->index;
for(i=0;i<len_of_in*2;i++)
{
tmp_data=inbuff[i/2];
if(i%2)
inCode=(tmp_data&0xf0)>>4;
else
inCode=tmp_data &0x0f;
step =stepsizeTable[index];
/* Inverse quantize the ADPCM code into a predicted difference
using the quantizer step size
*/
diffq =step >>3;
if(inCode &4)
diffq +=step;
if(inCode &2)
diffq +=step >>1;
if(inCode &1)
diffq +=step >>2;
/* Fixed predictor computes new predicted sample by adding the
old predicted sample to predicted difference
*/
if(inCode &8)
predsample -=diffq;
else
predsample +=diffq;
/* Check for overflow of the new predicted sample
*/
if(predsample >32767)
predsample =32767;
elseif(predsample <-32768)
predsample =-32768;
/* Find new quantizer stepsize index by adding the old index
to a table lookup using the ADPCM code
*/
index +=indexTable[inCode];
/* Check for overflow of the new quantizer step size index
*/
if(index <0)
index =0;
if(index >88)
index =88;
/* Return the new ADPCM code */
Samp=predsample;
if(Samp>=0)
{
SampH=Samp/256;
SampL=Samp-256*SampH;
}
else
{
Samp=32768+Samp;
SampH=Samp/256;
SampL=Samp-256*SampH;
SampH+=0x80;
}
outbuff[j++]=SampL;
outbuff[j++]=SampH;
}
/* Save the predicted sample and quantizer step size index for
next iteration
*/
state->valprev =(short)predsample;
state->index =(char)index;
}
#ifndefADPCM_H
#defineADPCM_H
#ifdef__cplusplus
extern"C"{
#endif
struct adpcm_state {
short valprev; /* Previous output value */
char index; /* Index into stepsize table */
};
voidadpcm_decoder(char*inbuff,char*outbuff,intlen_of_in,structadpcm_state *state );
#ifdef__cplusplus
} /* extern "C" */
#endif
#endif/* ADPCM_H*/
3、main.c文件代碼
#include"stdio.h"
#include "stdlib.h"
#include "adpcm.h"
#defineCFG_BlkSize 256
charch[CFG_BlkSize]; //用來存儲wav文件的頭信息
charsavedata[CFG_BlkSize*4];
unsignedcharRiffHeader[]={
'R','I','F','F',// Chunk ID (RIFF)
0x70,0x70,0x70,0x70,// Chunk payload size (calculate after rec!)
'W','A','V','E',// RIFF resource format type
'f','m','t',' ',// Chunk ID (fmt )
0x10,0x00,0x00,0x00,// Chunk payload size (0x14 = 20 bytes)
0x01,0x00, // Format Tag ()
0x01,0x00, // Channels (1)
0x40,0x1f,0x00,0x00,// Sample Rate, = 16.0kHz
0x80,0x3e,0x00,0x00,// Byte rate 32.0K
0x02,0x00, // BlockAlign == NumChannels * BitsPerSample/8
0x10,0x00 // BitsPerSample
};
unsignedcharRIFFHeader504[]={
'd','a','t','a',// Chunk ID (data)
0x70,0x70,0x70,0x70 // Chunk payload size (calculate after rec!)
};
/****************************************************************
函數名稱: main
功能描述:
輸入參數: none
輸出參數: none
****************************************************************/
voidmain(void)
{
FILE *fpi,*fpo;
unsignedlongiLen,temp;
structadpcm_state ADPCMstate;
unsignedlongi =0;
unsignedlongj;
fpi=fopen("f:\\lk\\test.adpcm","rb"); //為讀,打開一個wav文件
if((fpi=fopen("f:\\lk\\test.adpcm","rb"))==NULL) //若打開文件失敗,退出
{
printf("can't open this file\n");
printf("\nread error!\n");
printf("\n%d\n",i);
exit(0);
}
fseek(fpi,0,SEEK_END);
iLen=ftell(fpi);
printf("\n======================================================\n");
printf("\n========================%d========================\n",iLen);
printf("\n======================================================\n");
if((iLen-44)%CFG_BlkSize)
iLen =(iLen-44)/CFG_BlkSize+1;
else
iLen =(iLen-44)/CFG_BlkSize;
fpo=fopen("f:\\lk\\new.pcm","rb+"); //為寫,打開一個wav文件
if((fpo=fopen("f:\\lk\\new.pcm","rb+"))==NULL) //若打開文件失敗,退出
{
printf("can't open this file\n");
printf("\nwrite error!\n");
exit(0);
}
fseek(fpo,0,SEEK_SET);
fwrite(RiffHeader,sizeof(RiffHeader),1,fpo); //寫文件riff
fwrite(RIFFHeader504,sizeof(RIFFHeader504),1,fpo); //寫 data塊頭
while(i<iLen)
{
fseek(fpi,48+i*CFG_BlkSize,SEEK_SET);
fread(ch,1,CFG_BlkSize,fpi);
printf("\n======================================================\n");
for(j=0;j<100;j++)
printf("| %d |",ch[j]);
printf("\n======================================================\n");
////////////////////////添加讀取BlockHeader部分開始////////////////////////////////
if(i ==0)
{
ADPCMstate.index =0; //第一個block的index為 0 當前的BlockSize為 256 即采樣點數為 (256-4)*2+1 = 505
}
else
{
ADPCMstate.index =ch[2];
}
ADPCMstate.valprev =(short)ch[0]+((short)(ch[1]))*256; //每一個block里面幀頭有一個未壓縮的數據 存儲時 先低后高
savedata[0]=ch[0]; //存儲第一個沒有被壓縮的數據
savedata[1]=ch[1]; //存儲第一個沒有被壓縮的數據
////////////////////////添加讀取BlockHeader部分結束////////////////////////////////
adpcm_decoder(&ch[4],&savedata[2],CFG_BlkSize-4,&ADPCMstate);//解碼出來了 (256-4)*4 個字節
temp =(CFG_BlkSize-4)*4+2;
fseek(fpo,44+i*temp,SEEK_SET); //開始寫聲音數據
fwrite(savedata,temp,1,fpo);
i++;
}
temp *=i;
RiffHeader[4]=(unsignedchar)((40+temp)&0x000000ff);
RiffHeader[5]=(unsignedchar)(((40+temp)&0x0000ff00)>>8);
RiffHeader[6]=(unsignedchar)(((40+temp)&0x00ff0000)>>16);
RiffHeader[7]=(unsignedchar)(((40+temp)&0xff000000)>>24);
fseek(fpo,4,SEEK_SET);
fwrite(&RiffHeader[4],4,1,fpo);
RiffHeader[40]=(unsignedchar)(temp&0x000000ff);
RiffHeader[41]=(unsignedchar)((temp&0x0000ff00)>>8);
RiffHeader[42]=(unsignedchar)((temp&0x00ff0000)>>16);
RiffHeader[43]=(unsignedchar)((temp&0xff000000)>>24);
fseek(fpo,40,SEEK_SET);
fwrite(&RiffHeader[40],4,1,fpo);
fclose(fpi);
fclose(fpo);
printf("\n==========================OK!=========================\n");
}
1. 音頻簡介
經常見到這樣的描述: 44100HZ 16bit stereo 或者 22050HZ 8bit mono 等等.
44100HZ 16bit stereo: 每秒鍾有 44100 次采樣, 采樣數據用 16 位(2字節)記錄, 雙聲道(立體聲);
22050HZ 8bit mono: 每秒鍾有 22050 次采樣, 采樣數據用 8 位(1字節)記錄, 單聲道;
當然也可以有 16bit 的單聲道或 8bit 的立體聲, 等等。
采樣率是指:聲音信號在“模→數”轉換過程中單位時間內采樣的次數。采樣值是指每一次采樣周期內聲音模擬信號的積分值。
對於單聲道聲音文件,采樣數據為八位的短整數(short int 00H-FFH);
而對於雙聲道立體聲聲音文件,每次采樣數據為一個16位的整數(int),高八位(左聲道)和低八位(右聲道)分別代表兩個聲道。
人對頻率的識別范圍是 20HZ - 20000HZ, 如果每秒鍾能對聲音做 20000 個采樣, 回放時就足可以滿足人耳的需求. 所以 22050 的采樣頻率是常用的, 44100已是CD音質, 超過48000的采樣對人耳已經沒有意義。這和電影的每秒 24 幀圖片的道理差不多。
每個采樣數據記錄的是振幅, 采樣精度取決於儲存空間的大小:
1 字節(也就是8bit) 只能記錄 256 個數, 也就是只能將振幅划分成 256 個等級;
2 字節(也就是16bit) 可以細到 65536 個數, 這已是 CD 標准了;
4 字節(也就是32bit) 能把振幅細分到 4294967296 個等級, 實在是沒必要了.
如果是雙聲道(stereo), 采樣就是雙份的, 文件也差不多要大一倍.
這樣我們就可以根據一個 wav 文件的大小、采樣頻率和采樣大小估算出一個 wav 文件的播放長度。
譬如 "Windows XP 啟動.wav" 的文件長度是 424,644 字節, 它是 "22050HZ / 16bit / 立體聲" 格式(這可以從其 "屬性->摘要" 里看到),
那么它的每秒的傳輸速率(位速, 也叫比特率、取樣率)是 22050*16*2 = 705600(bit/s), 換算成字節單位就是 705600/8 = 88200(字節/秒),
播放時間:424644(總字節數) / 88200(每秒字節數) ≈ 4.8145578(秒)。
但是這還不夠精確, 包裝標准的 PCM 格式的 WAVE 文件(*.wav)中至少帶有 42 個字節的頭信息, 在計算播放時間時應該將其去掉,
所以就有:(424644-42) / (22050*16*2/8) ≈ 4.8140816(秒). 這樣就比較精確了.
關於聲音文件還有一個概念: "位速", 也有叫做比特率、取樣率, 譬如上面文件的位速是 705.6kbps 或 705600bps, 其中的 b 是 bit, ps 是每秒的意思;
壓縮的音頻文件常常用位速來表示, 譬如達到 CD 音質的 MP3 是: 128kbps / 44100HZ.
2. PCM數據格式
PCM(Pulse Code Modulation)也被稱為 脈碼編碼調制。PCM中的聲音數據沒有被壓縮,如果是單聲道的文件,采樣數據按時間的先后順序依次存入。(它的基本組織單位是BYTE(8bit)或WORD(16bit))
一般情況下,一幀PCM是由2048次采樣組成的( 參 http://discussion.forum.nokia.com/forum/showthread.php?129458-請問PCM格式的音頻流,每次讀入或輸出的塊的大小是必須固定為4096B么&s=e79e9dd1707157281e3725a163844c49)。
如果是雙聲道的文件,采樣數據按時間先后順序交叉地存入。如圖所示:
PCM的每個樣本值包含在一個整數i中,i的長度為容納指定樣本長度所需的最小字節數。
首先存儲低有效字節,表示樣本幅度的位放在i的高有效位上,剩下的位置為0,這樣8位和16位的PCM波形樣本的數據格式如下所示。
樣本大小 數據格式 最小值 最大值
8位PCM unsigned int 0 225
16位PCM int -32767 32767
一、概述:
本文敘述了如何通過IMA-ADPCM壓縮和解壓縮算法來完成從IMA-ADPCM文件轉換為PCM文件的過程。主要包括的內容有:PCM和IMA-ADPCM WAVE文件內部結構的介紹,IMA-ADPCM壓縮與解壓縮算法,以及如何生成特有的音頻壓縮格式文件等三方面的內容。
二、WAVE文件的認識
WAVE文件是計算機領域最常用的數字化聲音文件格式之一,它是微軟專門為Windows系統定義的波形文件格式(Waveform Audio),由於其擴展名為"*.wav"。
wave文件有很多不同的壓縮格式,而且現在一些程序生成的wave文件都或多或少地含有一些錯誤。這些錯誤的產生不是因為單個數據壓縮和解壓縮算法的問題,而是因為在壓縮和解壓縮后沒有正確地組織好文件的內部結構。所以,正確而詳細地了解各種WAVE文件的內部結構是成功完成壓縮和解壓縮的基礎,也是生成特有音頻壓縮格式文件的前提。
最基本的WAVE文件是PCM(脈沖編碼調制)格式的,這種文件直接存儲采樣的聲音數據沒有經過任何的壓縮,是聲卡直接支持的數據格式,要讓聲卡正確播放其它被壓縮的聲音數據,就應該先把壓縮的數據解壓縮成PCM格式,然后再讓聲卡來播放。
1.Wave文件的內部結構
WAVE文件是以RIFF(Resource Interchange File Format,"資源交互文件格式")格式來組織內部結構的。RIFF文件結構可以看作是樹狀結構,其基本構成是稱為"塊"(Chunk)的單元,最頂端是一個“RIFF”塊,下面的每個塊有“類型塊標識(可選)”、“標志符”、“數據大小”及“數據”等項所組成,塊的結構如表1所示:
上面說到的“類型塊標識”只在部分chunk中用到,如“WAVE”chunk中,這時表示下面嵌套有別的chunk,當使用了“類型塊標識”時,該chunk就沒有別的項(如塊標志符,數據大小等),它只作為文件讀取時的一個標識。先找到這個“類型塊標識”,再以它為起來讀取它下面嵌套的其它chunk。
每個文件最前端寫入的是RIFF塊,每個文件只有一個RIFF塊。從表2中可以看出它的結構:
非PCM格式的文件會至少多加入一個“fact”塊,它用來記錄數據解壓縮后的大小。(注意是數據而不是文件)這個“fact”塊一般加在“data”塊的前面。
2.WAVEFORMAT結構的認識
PCM和非PCM的主要區別是聲音數據的組織不同,這些區別可以通過兩者的WAVEFORMAT結構來區分。下面以PCM和IMA-ADPCM來進行對比:
WAVE的基本結構WAVEFORMATEX結構定義如下:
typedef struct
{
WORD wFormatag; //編碼格式,包括WAVE_FORMAT_PCM,//WAVEFORMAT_ADPCM等
WORD nChannls; //聲道數,單聲道為1,雙聲道為2;
DWORD nSamplesPerSec;//采樣頻率;
DWORD nAvgBytesperSec;//每秒的數據量;
WORD nBlockAlign;//塊對齊;
WORD wBitsPerSample;//WAVE文件的采樣大小;
WORD sbSize; //PCM中忽略此值
}WAVEFORMATEX;
PCM的結構就是基本結構;
IMAADPCMWAVEFORMAT結構定義如下:
Typedef struct
{
WAVEFORMATEX wfmt;
WORD nSamplesPerBlock;
}IMAADPCMWAVEFORMAT;
IMA-ADPCM的wfmt->cbsize不能忽略,一般取值為2,表示此類型的WAVEFORMAT比一般的WAVEFORMAT多出2個字節。這兩個字符也就是nSamplesPerBlock。
3.“fact”chunk的內部組織
在非PCM格式的文件中,一般會在WAVEFORMAT結構后面加入一個“fact”chunk,結構如下:
typedef struct{
char[4]; //“fact”字符串
DWORD chunksize;
DWORD datafactsize; //數據轉換為PCM格式后的大小。
}factchunk;
datafactsize是這個chunk中最重要的數據,如果這是某種壓縮格式的聲音文件,那么從這里就可以知道他解壓縮后的大小。對於解壓時的計算會有很大的好處!
4.“data”chunk的內部組織
從“data”chunk的第9個字節開始,存儲的就是聲音信息的數據了,(前八個字節存儲的是標志符“data”和后接數據大小size(DWORD)。這些數據可能是壓縮的,也可能是沒有壓縮的。
PCM中的聲音數據沒有被壓縮,如果是單聲道的文件,采樣數據按時間的先后順序依次存入。(它的基本組織單位是BYTE(8bit)或WORD(16bit))如果是雙聲道的文件,采樣數據按時間先后順序交叉地存入。如圖所示:
IMA-ADPCM是壓縮格式,它是從PCM的16位采樣壓縮成4位的。對於單聲道的IMA-ADPCM來說,它是將PCM的數據按時間次序依次壓縮並寫入文件中的,每個byte中含兩個采樣,低四位對應第一個采樣,高四位對應第二個采樣。而對於雙聲道的IMA-ADPCM來說,它的存儲相對就麻煩一些了,它是將PCM的左聲道的前8個采樣依次壓縮並寫入到一個DWORD中,然后寫入“data”chunk里。緊接着是右聲道的前8個采樣。以此循環,當采樣數不足8時(到數據尾端),應該把多出來的采樣用0填充。其示意圖如下:
特別注意:
在IMA-ADPCM中,“data”chuck中的數據是以block形式來組織的,我把它叫做“段”,也就是說在進行壓縮時,並不是依次把所有的數據進行壓縮保存,而是分段進行的,這樣有一個十分重要的好處:那就是在只需要文件中的某一段信息時,可以在解壓縮時可以只解所需數據所在的段就行了,沒有必要再從文件開始起一個一個地解壓縮。這對於處理大文件將有相當的優勢。同時,這樣也可以保證聲音效果。
Block一般是由block header (block頭) 和data 兩者組成的。其中block header是一個結構,它在單聲道下的定義如下:
Typedef struct
{
short sample0; //block中第一個采樣值(未壓縮)
BYTE index; //上一個block最后一個index,第一個block的index=0;
BYTE reserved; //尚未使用
}MonoBlockHeader;
有了blockheader的信息后,就可以不需要知道這個block前面和后面的數據而輕松地解出本block中的壓縮數據。對於雙聲道,它的blockheader應該包含兩個MonoBlockHeader其定義如下:
typedaf struct
{
MonoBlockHeader leftbher;
MonoBlockHeader rightbher;
}StereoBlockHeader;
在解壓縮時,左右聲道是分開處理的,所以必須有兩個MonoBlockHeader;
注1:上述的index是解壓縮算法中必須用到的一個參數。詳見后面。
注2: 關於block的大小,通常會有以下幾種情況:
對於單聲道,大小一般為512byte,顯然這里面可以保存的sample個數為(512-sizeof(MonoBlockHeader))/4 + 1 = 1017個<其中"+1"是第一個存在頭結構中的沒有壓縮的sample.
對於雙聲道,大小一般為1024byte,按上面的算法可以得出,其中的sample個數也是1017個.
4.讀取WAVE文件的方法.
在知道了WAVE文件的內部數據組織后,可以直接通過FILE或HFILE來實現文件的讀取。但由於WAVE文件是以RIFF格式來組織的,所以用多媒體輸入輸出流來操作將更加方便,可以直接在文件中查找chunk並定位數據。
三、IMA-ADPCM 編碼和解碼算法
IMA-ADPCM 是Intel公司首先開發的是一種主要針對16bit采樣波形數據的有損壓縮算法, 壓縮比為4:1.它與通常的DVI-ADPCM是同一算法。(對8bit數據壓縮時是3.2:1,也有非標准的IMA-ADPCM壓縮算法,可以達到5:1甚至更高的壓縮比)4:1的壓縮是目前使用最多的壓縮方式。
ADPCM(Adaptive Differential Pulse Code Modulation 差分脈沖編碼調制)主要是針對連續的波形數據的, 保存的是相臨波形的變化情況, 以達到描述整個波形的目的。算法中必須用到兩個一維數組,setptab[] 和index_adjust[],附在下面的代碼之后。
--------------------------------------------------------------------------------
IMA-ADPCM 壓縮過程
首先我們認為聲音信號都是從零開始的,那么需要初始化兩個變量
int index = 0,prev_sample = 0;
但在實際使用中,prev_sample的值是每個block中第一個采樣的值。(這點在后面的block中會詳細介紹)
假設已經寫好了兩個函數:
GetNextSamp() ——得到一個16bit 的采樣數據;
SaveComCode() ——保存一個4bit 的壓縮樣品;
下面的循環將依次壓縮聲音數據流:
while (還有數據要處理) {
cur_sample = GetNextSamp(); // 得到PCM中的當前采樣數據
diff = cur_sample-prev_sample; // 計算出和上一個的增量
if (diff<0)
{
diff=-diff;
fg=8;
}
else fg=0; // fg 保存的是符號位
code = 4*diff / steptab[index];
if (code>7) code=7; // 根據steptab[] 得到一個0~7 的值,它描述了采樣振幅的變化量
index+=index_adjust[code]; // 根據聲音強度調整下次取steptab 的序號,便於下次得到更精確的變化量的描述
if (index<0) index=0; // 調整index的值
else if (index>88) index=88;
prev_sample=cur_sample;
SaveComCode(code|fg); // 加上符號位保存起來
}
--------------------------------------------------------------------------------
IMA-ADPCM 解壓縮過程
解壓縮實際是壓縮的一個逆過程,假設寫好了以下兩個函數:
GetNextCode() ——得到一個編碼(4bit)
OutputSamp() ——將解碼出來的聲音信號保存起來(16bit).
int index=0,cur_sample=0;
while (還有數據要處理) {
code=GetNextCode(); // 得到下一個壓縮樣品Code 4bit
if ((code & 8) != 0) fg=1 else fg=0;
code&=7; // 將code 分離為數據和符號
diff = (steptab[index]*code) /4 + steptab[index] / 8; // 后面加的一項是為了減少誤差
if (fg==1) diff=-diff;
cur_sample+=diff; // 計算出當前的波形數據
if (cur_sample>32767) OutputSamp(32767);
else if (cur_sample<-32768) OutputSamp(-32768);
else OutputSamp(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 steptab[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 };
四、設計自己特用的壓縮聲音文件格式。
有了上述幾節的知識,要基於IMA-ADPCM Encode/Dcode算法來設計出特有的壓縮聲音文件格式就不難了!只要 先設計好特有的文件內部結構和特殊的數據組織結構,再以此為標准編寫壓縮和解壓縮程序就行了。由於我們已經搞清楚了PCM里面的數據組織,所以我們還可以進行PCM文件的截取、連接、壓縮等更多功能。