一区二区三区日韩精品-日韩经典一区二区三区-五月激情综合丁香婷婷-欧美精品中文字幕专区

分享

amr與wave編解碼

 開花結果 2010-12-14
目錄
1.    概述
2.    AMR編碼
3.    AMR解碼
4.    AMR幀讀取算法
5.    參考資料
 
1.      概述
現(xiàn)在很多智能手機都支持多媒體功能,特別是音頻和視頻播放功能,而AMR文件格式是手機端普遍支持的音頻文件格式。
 
AMR,全稱是:Adaptive Multi-Rate,自適應多速率,是一種音頻編碼文件格式,專用于有效地壓縮語音頻率。
 
AMR音頻主要用于移動設備的音頻壓縮,壓縮比非常高,但是音質(zhì)比較差,主要用于語音類的音頻壓縮,不適合對音質(zhì)要求較高的音樂類音頻的壓縮。
 
AMR的編解碼是基于“3GPP AMR Floating-point Speech Codec”來做的,3GPP還專門開放了基于ANSI-C實現(xiàn)的編解碼代碼,便于我們在各種平臺上進行移植。
 
#ifndef amrFileCodec_h
#define amrFileCodec_h
 
#define AMR_MAGIC_NUMBER "#!AMR\n"
 
#define PCM_FRAME_SIZE 160 // 8khz 8000*0.02=160
#define MAX_AMR_FRAME_SIZE 32
#define AMR_FRAME_COUNT_PER_SECOND 50
//int amrEncodeMode[] = {4750, 5150, 5900, 6700, 7400, 7950, 10200, 12200}; // amr 編碼方式
 
typedef struct
{
         char chChunkID[4];
         int nChunkSize;
}XCHUNKHEADER;
 
typedef struct
{
         short nFormatTag;
         short nChannels;
         int nSamplesPerSec;
         int nAvgBytesPerSec;
         short nBlockAlign;
         short nBitsPerSample;
}WAVEFORMAT;
 
typedef struct
{
         short nFormatTag;
         short nChannels;
         int nSamplesPerSec;
         int nAvgBytesPerSec;
         short nBlockAlign;
         short nBitsPerSample;
         short nExSize;
}WAVEFORMATX;
 
typedef struct
{
         char chRiffID[4];
         int nRiffSize;
         char chRiffFormat[4];
}RIFFHEADER;
 
typedef struct
{
         char chFmtID[4];
         int nFmtSize;
         WAVEFORMAT wf;
}FMTBLOCK;
 
// WAVE音頻采樣頻率是8khz
// 音頻樣本單元數(shù) = 8000*0.02 = 160 (由采樣頻率決定)
// 聲道數(shù) 1 : 160
//        2 : 160*2 = 320
// bps決定樣本(sample)大小
// bps = 8 --> 8位 unsigned char
//       16 --> 16位 unsigned short
int EncodeWAVEFileToAMRFile(const char* pchWAVEFilename, const char* pchAMRFileName, int nChannels, int nBitsPerSample);
 
// 將AMR文件解碼成WAVE文件
int DecodeAMRFileToWAVEFile(const char* pchAMRFileName, const char* pchWAVEFilename);
 
#endif

2.        AMR編碼
3GPP提供了編碼代碼,并提供了一個encoder.c程序,該程序示范了如何對一個16位的單聲道PCM數(shù)據(jù)進行壓縮的。(采樣頻率必須是8khz)
 
我對該程序進行一定的拓展,數(shù)據(jù)位支持8位和16位,可以是單聲道和雙聲道。
 
l         對于8位PCM只需要將每個采樣的sample數(shù)據(jù)位擴展成16位,并左移7位。
l         對于雙聲道,可以只對左聲道數(shù)據(jù)進行處理,也可以只對右聲道數(shù)據(jù)進行處理,或者將左右聲道數(shù)據(jù)求平均值就可。
 
這樣兩個小處理,就可以將PCM規(guī)范成3PGG的編碼器需要的數(shù)據(jù)格式。
 
代碼在 amrFileEncoder.c 中。
 
#include "amrFileCodec.h"
 
// 從WAVE文件中跳過WAVE文件頭,直接到PCM音頻數(shù)據(jù)
void SkipToPCMAudioData(FILE* fpwave)
{
         RIFFHEADER riff;
         FMTBLOCK fmt;
         XCHUNKHEADER chunk;
         WAVEFORMATX wfx;
         int bDataBlock = 0;
 
         // 1. 讀RIFF頭
         fread(&riff, 1, sizeof(RIFFHEADER), fpwave);
 
         // 2. 讀FMT塊 - 如果 fmt.nFmtSize>16 說明需要還有一個附屬大小沒有讀
         fread(&chunk, 1, sizeof(XCHUNKHEADER), fpwave);
         if ( chunk.nChunkSize>16 )
         {
                   fread(&wfx, 1, sizeof(WAVEFORMATX), fpwave);
         }
         else
         {
                   memcpy(fmt.chFmtID, chunk.chChunkID, 4);
                   fmt.nFmtSize = chunk.nChunkSize;
                   fread(&fmt.wf, 1, sizeof(WAVEFORMAT), fpwave);
         }
 
         // 3.轉(zhuǎn)到data塊 - 有些還有fact塊等。
         while(!bDataBlock)
         {
                   fread(&chunk, 1, sizeof(XCHUNKHEADER), fpwave);
                   if ( !memcmp(chunk.chChunkID, "data", 4) )
                   {
                            bDataBlock = 1;
                            break;
                   }
                   // 因為這個不是data塊,就跳過塊數(shù)據(jù)
                   fseek(fpwave, chunk.nChunkSize, SEEK_CUR);
         }
}
 
// 從WAVE文件讀一個完整的PCM音頻幀
// 返回值: 0-錯誤 >0: 完整幀大小
int ReadPCMFrame(short speech[], FILE* fpwave, int nChannels, int nBitsPerSample)
{
         int nRead = 0;
         int x = 0, y=0;
         unsigned short ush1=0, ush2=0, ush=0;
 
         // 原始PCM音頻幀數(shù)據(jù)
         unsigned char pcmFrame_8b1[PCM_FRAME_SIZE];
         unsigned char pcmFrame_8b2[PCM_FRAME_SIZE<<1];
         unsigned short pcmFrame_16b1[PCM_FRAME_SIZE];
         unsigned short pcmFrame_16b2[PCM_FRAME_SIZE<<1];
 
         if (nBitsPerSample==8 && nChannels==1)
         {
                   nRead = fread(pcmFrame_8b1, (nBitsPerSample/8), PCM_FRAME_SIZE*nChannels, fpwave);
                   for(x=0; x<PCM_FRAME_SIZE; x++)
                   {
                            speech[x] =(short)((short)pcmFrame_8b1[x] << 7);
                   }
         }
         else
         if (nBitsPerSample==8 && nChannels==2)
         {
                   nRead = fread(pcmFrame_8b2, (nBitsPerSample/8), PCM_FRAME_SIZE*nChannels, fpwave);
                   for( x=0, y=0; y<PCM_FRAME_SIZE; y++,x+=2 )
                   {
                            // 1 - 取兩個聲道之左聲道
                            speech[y] =(short)((short)pcmFrame_8b2[x+0] << 7);
                            // 2 - 取兩個聲道之右聲道
                            //speech[y] =(short)((short)pcmFrame_8b2[x+1] << 7);
                            // 3 - 取兩個聲道的平均值
                            //ush1 = (short)pcmFrame_8b2[x+0];
                            //ush2 = (short)pcmFrame_8b2[x+1];
                            //ush = (ush1 + ush2) >> 1;
                            //speech[y] = (short)((short)ush << 7);
                   }
         }
         else
         if (nBitsPerSample==16 && nChannels==1)
         {
                   nRead = fread(pcmFrame_16b1, (nBitsPerSample/8), PCM_FRAME_SIZE*nChannels, fpwave);
                   for(x=0; x<PCM_FRAME_SIZE; x++)
                   {
                            speech[x] = (short)pcmFrame_16b1[x+0];
                   }
         }
         else
         if (nBitsPerSample==16 && nChannels==2)
         {
                   nRead = fread(pcmFrame_16b2, (nBitsPerSample/8), PCM_FRAME_SIZE*nChannels, fpwave);
                   for( x=0, y=0; y<PCM_FRAME_SIZE; y++,x+=2 )
                   {
                            //speech[y] = (short)pcmFrame_16b2[x+0];
                            speech[y] = (short)((int)((int)pcmFrame_16b2[x+0] + (int)pcmFrame_16b2[x+1])) >> 1;
                   }
         }
 
         // 如果讀到的數(shù)據(jù)不是一個完整的PCM幀, 就返回0
         if (nRead<PCM_FRAME_SIZE*nChannels) return 0;
 
         return nRead;
}
 
// WAVE音頻采樣頻率是8khz
// 音頻樣本單元數(shù) = 8000*0.02 = 160 (由采樣頻率決定)
// 聲道數(shù) 1 : 160
//        2 : 160*2 = 320
// bps決定樣本(sample)大小
// bps = 8 --> 8位 unsigned char
//       16 --> 16位 unsigned short
int EncodeWAVEFileToAMRFile(const char* pchWAVEFilename, const char* pchAMRFileName, int nChannels, int nBitsPerSample)
{
         FILE* fpwave;
         FILE* fpamr;
 
         /* input speech vector */
         short speech[160];
 
         /* counters */
         int byte_counter, frames = 0, bytes = 0;
 
         /* pointer to encoder state structure */
         int *enstate;
        
         /* requested mode */
         enum Mode req_mode = MR122;
         int dtx = 0;
 
         /* bitstream filetype */
         unsigned char amrFrame[MAX_AMR_FRAME_SIZE];
 
         fpwave = fopen(pchWAVEFilename, "rb");
         if (fpwave == NULL)
         {
                   return 0;
         }
 
         // 創(chuàng)建并初始化amr文件
         fpamr = fopen(pchAMRFileName, "wb");
         if (fpamr == NULL)
         {
                   fclose(fpwave);
                   return 0;
         }
         /* write magic number to indicate single channel AMR file storage format */
         bytes = fwrite(AMR_MAGIC_NUMBER, sizeof(char), strlen(AMR_MAGIC_NUMBER), fpamr);
 
         /* skip to pcm audio data*/
         SkipToPCMAudioData(fpwave);
 
         enstate = Encoder_Interface_init(dtx);
 
         while(1)
         {
                   // read one pcm frame
                   if (!ReadPCMFrame(speech, fpwave, nChannels, nBitsPerSample)) break;
 
                   frames++;
 
                   /* call encoder */
                   byte_counter = Encoder_Interface_Encode(enstate, req_mode, speech, amrFrame, 0);
 
                   bytes += byte_counter;
                   fwrite(amrFrame, sizeof (unsigned char), byte_counter, fpamr );
         }
 
         Encoder_Interface_exit(enstate);
 
         fclose(fpamr);
         fclose(fpwave);
 
         return frames;
}

3.      AMR解碼
3GPP提供了解碼代碼,并提供了一個decoder.c程序,該程序示范了如何對amr音頻進行解碼。解碼成一個wave文件(8khz 16位單聲道)。
 
解碼是需要注意AMR壞幀的處理。在AMR讀幀算法中有說明。
 
文件解碼器代碼在 amrFileDecoder.c 中。
 
#include "amrFileCodec.h"
 
void WriteWAVEFileHeader(FILE* fpwave, int nFrame)
{
         char tag[10] = "";
 
         // 1. 寫RIFF頭
         strcpy(tag, "RIFF");
         memcpy(riff.chRiffID, tag, 4);
         riff.nRiffSize = 4                                     // WAVE
                   + sizeof(XCHUNKHEADER)               // fmt
                   + sizeof(WAVEFORMATX)           // WAVEFORMATX
                   + sizeof(XCHUNKHEADER)               // DATA
                   + nFrame*160*sizeof(short);    //
         strcpy(tag, "WAVE");
         memcpy(riff.chRiffFormat, tag, 4);
         fwrite(&riff, 1, sizeof(RIFFHEADER), fpwave);
 
         // 2. 寫FMT塊
         strcpy(tag, "fmt ");
         memcpy(chunk.chChunkID, tag, 4);
         chunk.nChunkSize = sizeof(WAVEFORMATX);
         fwrite(&chunk, 1, sizeof(XCHUNKHEADER), fpwave);
         memset(&wfx, 0, sizeof(WAVEFORMATX));
         wfx.nFormatTag = 1;
         wfx.nChannels = 1; // 單聲道
         wfx.nSamplesPerSec = 8000; // 8khz
         wfx.nAvgBytesPerSec = 16000;
         wfx.nBlockAlign = 2;
         wfx.nBitsPerSample = 16; // 16位
         fwrite(&wfx, 1, sizeof(WAVEFORMATX), fpwave);
 
         // 3. 寫data塊頭
         strcpy(tag, "data");
         memcpy(chunk.chChunkID, tag, 4);
         chunk.nChunkSize = nFrame*160*sizeof(short);
         fwrite(&chunk, 1, sizeof(XCHUNKHEADER), fpwave);
}
 
const int round(const double x)
{
         return((int)(x+0.5));
}
 
// 根據(jù)幀頭計算當前幀大小
int caclAMRFrameSize(unsigned char frameHeader)
{
         int mode;
         int temp1 = 0;
         int temp2 = 0;
         int frameSize;
 
         temp1 = frameHeader;
 
         // 編碼方式編號 = 幀頭的3-6位
         temp1 &= 0x78; // 0111-1000
         temp1 >>= 3;
 
         mode = amrEncodeMode[temp1];
 
         // 計算amr音頻數(shù)據(jù)幀大小
         // 原理: amr 一幀對應20ms,那么一秒有50幀的音頻數(shù)據(jù)
         temp2 = round((double)(((double)mode / (double)AMR_FRAME_COUNT_PER_SECOND) / (double)8));
 
         frameSize = round((double)temp2 + 0.5);
         return frameSize;
}
 
// 讀第一個幀 - (參考幀)
// 返回值: 0-出錯; 1-正確
int ReadAMRFrameFirst(FILE* fpamr, unsigned char frameBuffer[], int* stdFrameSize, unsigned char* stdFrameHeader)
{
         memset(frameBuffer, 0, sizeof(frameBuffer));
 
         // 先讀幀頭
         fread(stdFrameHeader, 1, sizeof(unsigned char), fpamr);
         if (feof(fpamr)) return 0;
 
         // 根據(jù)幀頭計算幀大小
         *stdFrameSize = caclAMRFrameSize(*stdFrameHeader);
 
         // 讀首幀
         frameBuffer[0] = *stdFrameHeader;
         fread(&(frameBuffer[1]), 1, (*stdFrameSize-1)*sizeof(unsigned char), fpamr);
         if (feof(fpamr)) return 0;
 
         return 1;
}
 
// 返回值: 0-出錯; 1-正確
int ReadAMRFrame(FILE* fpamr, unsigned char frameBuffer[], int stdFrameSize, unsigned char stdFrameHeader)
{
         int bytes = 0;
         unsigned char frameHeader; // 幀頭
 
         memset(frameBuffer, 0, sizeof(frameBuffer));
 
         // 讀幀頭
         // 如果是壞幀(不是標準幀頭),則繼續(xù)讀下一個字節(jié),直到讀到標準幀頭
         while(1)
         {
                   bytes = fread(&frameHeader, 1, sizeof(unsigned char), fpamr);
                   if (feof(fpamr)) return 0;
                   if (frameHeader == stdFrameHeader) break;
         }
 
         // 讀該幀的語音數(shù)據(jù)(幀頭已經(jīng)讀過)
         frameBuffer[0] = frameHeader;
         bytes = fread(&(frameBuffer[1]), 1, (stdFrameSize-1)*sizeof(unsigned char), fpamr);
         if (feof(fpamr)) return 0;
 
         return 1;
}
 
// 將AMR文件解碼成WAVE文件
int DecodeAMRFileToWAVEFile(const char* pchAMRFileName, const char* pchWAVEFilename)
{
         FILE* fpamr = NULL;
         FILE* fpwave = NULL;
         char magic[8];
         int * destate;
         int nFrameCount = 0;
         int stdFrameSize;
         unsigned char stdFrameHeader;
 
         unsigned char amrFrame[MAX_AMR_FRAME_SIZE];
         short pcmFrame[PCM_FRAME_SIZE];
 
         fpamr = fopen(pchAMRFileName, "rb");
         if ( fpamr==NULL ) return 0;
 
         // 檢查amr文件頭
         fread(magic, sizeof(char), strlen(AMR_MAGIC_NUMBER), fpamr);
         if (strncmp(magic, AMR_MAGIC_NUMBER, strlen(AMR_MAGIC_NUMBER)))
         {
                   fclose(fpamr);
                   return 0;
         }
 
         // 創(chuàng)建并初始化WAVE文件
         fpwave = fopen(pchWAVEFilename, "wb");
         WriteWAVEFileHeader(fpwave, nFrameCount);
 
         /* init decoder */
         destate = Decoder_Interface_init();
 
         // 讀第一幀 - 作為參考幀
         memset(amrFrame, 0, sizeof(amrFrame));
         memset(pcmFrame, 0, sizeof(pcmFrame));
         ReadAMRFrameFirst(fpamr, amrFrame, &stdFrameSize, &stdFrameHeader);
 
         // 解碼一個AMR音頻幀成PCM數(shù)據(jù)
         Decoder_Interface_Decode(destate, amrFrame, pcmFrame, 0);
         nFrameCount++;
         fwrite(pcmFrame, sizeof(short), PCM_FRAME_SIZE, fpwave);
 
         // 逐幀解碼AMR并寫到WAVE文件里
         while(1)
         {
                   memset(amrFrame, 0, sizeof(amrFrame));
                   memset(pcmFrame, 0, sizeof(pcmFrame));
                   if (!ReadAMRFrame(fpamr, amrFrame, stdFrameSize, stdFrameHeader)) break;
 
                   // 解碼一個AMR音頻幀成PCM數(shù)據(jù) (8k-16b-單聲道)
                   Decoder_Interface_Decode(destate, amrFrame, pcmFrame, 0);
                   nFrameCount++;
                   fwrite(pcmFrame, sizeof(short), PCM_FRAME_SIZE, fpwave);
         }
 
         Decoder_Interface_exit(destate);
 
         fclose(fpwave);
 
         // 重寫WAVE文件頭
         fpwave = fopen(pchWAVEFilename, "r+");
         WriteWAVEFileHeader(fpwave, nFrameCount);
         fclose(fpwave);
 
         return nFrameCount;
}

4.      AMR幀讀取算法
因為可能存在異常幀,所以不一定所有的語音幀大小一致,對于跟正常幀大小不一致的,或者幀頭跟正常幀頭不一致的,就不交給解碼器,直接拋棄該壞幀。
 
讀取幀的算法,用C語言來編寫,readAMRFrame.c,JAVA可以用類似的方法。
下面是算法描述流程圖。
讀首幀(標準幀)
ReadFirstAMRFrame
根據(jù)幀頭計算標準幀的大小
caclAMRFrameSize
AMR音頻文件流
讀幀頭(字節(jié))
frameHeader
判斷是否為壞幀?
Y
N
讀本幀音頻數(shù)據(jù)
幀頭 + 音頻數(shù)據(jù) = 當前幀數(shù)據(jù) 

5.      參考資料
l         rfc3267
http://www./rfc/rfc3267.txt
http://ietfreport./rfc/PDF/rfc3267.pdf
l         3GPP TS 26.104 V 6.1.0 (2004-03)
http://www./ftp/Specs/html-info/26104-CRs.htm
l         3GPP AMR Floating-point Speech Codec
http://www./ftp/Specs/html-info/26104.htm
l         “amr編程匯總”
http://blog.csdn.net/windcao/archive/2006/01/04/570348.aspx
l         關于AMR文件格式的解釋
http://www./blog/user1/11409/archives/2006/16832.html
 
本文來自CSDN博客,轉(zhuǎn)載請標明出處:http://blog.csdn.net/jinlking/archive/2009/06/10/4256311.aspx

    本站是提供個人知識管理的網(wǎng)絡存儲空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點。請注意甄別內(nèi)容中的聯(lián)系方式、誘導購買等信息,謹防詐騙。如發(fā)現(xiàn)有害或侵權內(nèi)容,請點擊一鍵舉報。
    轉(zhuǎn)藏 分享 獻花(0

    0條評論

    發(fā)表

    請遵守用戶 評論公約

    類似文章 更多

    亚洲综合香蕉在线视频| 亚洲国产成人精品一区刚刚| 色婷婷在线视频免费播放| 97人妻精品一区二区三区免| 精品亚洲av一区二区三区| 中文字幕人妻综合一区二区| 日本丁香婷婷欧美激情| 日本高清加勒比免费在线| 草草视频精品在线观看| 可以在线看的欧美黄片| 亚洲日本加勒比在线播放| 婷婷基地五月激情五月| 又黄又硬又爽又色的视频| 最新国产欧美精品91| 精品人妻少妇二区三区| 视频在线播放你懂的一区| 日本中文字幕在线精品| 麻豆精品在线一区二区三区| 九九热精彩视频在线播放| 成人国产激情福利久久| 中文字字幕在线中文乱码二区| 国内九一激情白浆发布| 国产成人免费激情视频| 国产精品制服丝袜美腿丝袜| 丰满人妻熟妇乱又伦精另类视频| 欧美偷拍一区二区三区四区| 91爽人人爽人人插人人爽| 男人把女人操得嗷嗷叫| 乱女午夜精品一区二区三区| 草草视频精品在线观看| 久久精视频免费视频观看| 91在线国内在线中文字幕| 亚洲精品欧美精品日韩精品| 高清一区二区三区大伊香蕉| 国产又粗又长又爽又猛的视频| 日韩精品区欧美在线一区| 国产色偷丝袜麻豆亚洲| 精品一区二区三区三级视频| 久热这里只有精品九九| 国产精品午夜一区二区三区| 日韩一级欧美一级久久|