把vox文件转换为pcm编码格式的wave文件
最近搞一个小项目,其中需要把vox文件数据转换为pcm编码格式的wave文件数据,在网上找了好久没有找到可以直接使用的算法编码实现,最后参照网文“[Delphi]转换Dialogic Vox文件为Wave文件@http://www.cnblogs.com/xujh/archive/2005/12/04/290384.html ”中的转换算法,我编写了下面的C++实现算法,把vox文件转换为pcm编码格式的数据之后,添加wave文件头,形成pcm编码格式的wave文件,采用cool edit等软件播放时,发现有以下问题:
1、对比播放转换得到的wave文件和原来的vox文件,发现播放前者的音量较小,且混入的噪声、杂音较多;
2、采用ultraedit软件对比查看应用程序和cool edit软件转换得到的wave文件时,发现它们的长度、文件头(文件头部的44个字节)完全一致,但是,音频数据编码部分差异很大;
希望大家不吝赐教,给出意见和建议,不胜感激!我的联系方式:2000ghjiang@sina.com, QQ: 5189654,希望能得到你的指点,谢谢!
我的vox文件是4位编码的,采样速率为8000bps,转换所得的wave文件为16位的、编码格式为pcm、采样速率为8000bps。
下面是我的转换算法实现编码:
//****************************************************************************
//Vox ADPCM与PCM编码转换时使用到的常量表
//vox_SStable表格索引调整表
static int vox_SSAdjust[8] = {-1,-1,-1,-1,2,4,6,8};
//PCM编码差修正值表
static int vox_SSTable[50] =
{
0, 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
};
//把4位的ADPCM转换为16位的PCM,并更新算法输入参数last_sample,delta,index
//code_4bit:需要转换的ADPCM编码值,低4位有效
//last_pcmsample:上一个ADPCM编码的转换结果,PCM编码值,执行完毕之后更新为当前编码的转换结果
//pcmdelta:PCM编码差修正值
//tableindex:查表索引(vox_SStable表)
//输出结果为16位的pcm编码值
WORD VoxADPCM4b_to_PCM16b(WORD code_4bit, short& last_pcmsample, short& pcmdelta, short& tableindex)
{
//转换公式如下
//d(n) = (ss(n)*B2)+(ss(n)/2*B1)+(ss(n)/4*BO)+(ss(n)/8)
//转换公式如下(另一种表达形式)
//d(n) = (pcmdelta * B2)+(pcmdelta / 2 * B1)+(pcmdelta / 4 * B0)+(pcmdelta / 8)
//if (B3 = 1)
// d(n) = d(n) * (-1)
//X(n) = X(n-1) + d(n)
//注:
/*pcmdelta为转换第n个ADPCM编码时的编码差值
d(n)为中间变量,
X(n)和X(n-1)分别为第n、n-1个ADPCM编码的转换结果(PCM编码),
函数进入时参数last_pcmsample为X(n-1),函数退出时,参数last_pcmsample为X(n)
B3、B2、B1、B0分别指位adpcm采样值的最高位、次高位、次低位和最低位二进制数
NOTE:ss(n),X(n)都是自更新变量,即,这两个参数应该在执行转换操作时同时更新。
*/
int temp = (pcmdelta >> 3); //除以, 临时变量,相当于公式中的d(n)
//suanfa#1
if(code_4bit & 4) //次高位
temp = pcmdelta;
if(code_4bit & 2) //次低位
temp = temp + (pcmdelta>>1); //除以
if(code_4bit & 1) //最低位
temp = temp + (pcmdelta >> 2); //除以
if(code_4bit & 8) //最高位,符号位,1=负
last_pcmsample = last_pcmsample - temp;
else
last_pcmsample = last_pcmsample + temp;
//修正当前ADPCM编码的转换结果,使之总在有效范围-2048 —2047
if(last_pcmsample > 2047)
last_pcmsample = 2047;
else if(last_pcmsample < -2048)
last_pcmsample = -2048;
//修正tableindex的取值,使之总在有效范围-49
tableindex = tableindex + vox_SSAdjust[code_4bit & 7]; //取低位
if(tableindex < 0)
tableindex = 0;
else if(tableindex > 49)
tableindex = 49;
//更新上两个ADPCM编码转换结果的差值,为后续转换做准备
pcmdelta = vox_SSTable[tableindex];
return last_pcmsample;
}
//把Vox文件的ADPCM编码转换为PCM编码数据
//srcbuff:转换之前的ADPCM编码数据缓存
//srclen:ADPCM编码数据字节长度
//dstbuff:转换所得的PCM编码数据缓存
//返回值为转换所得的PCM编码数据的字节长度
long VoxADPCM_Decode_Method6(char* srcbuff, UINT srclen, char* dstbuff)
{
const short delta_init = 16;
const int reset_count = 48; //复位计数值,即,每检查到reset_count个和之后复位算法参数
short index = 0; //index为vox_step_table的索引
short last_sample = 0; //解码上一个adpcm编码得出的PCM编码值
short delta = delta_init; //当前采样转换修正值
WORD code; //4位的ADPCM编码值,低位有效
int count = 0; //采样数据中累计和的数目
UINT dolen = 0; //当前正在处理的字节序号
UINT dstlen = 0; //解码出的数据长度,单位为字节
int result;
//while(还有数据要处理)
while(dolen < srclen)
{
// 取出一个位的ADPCM编码数据
//code = (srcbuff[dolen] >> 4); //先解码高位
code = srcbuff[dolen] & 0xF;
result = last_sample = VoxADPCM4b_to_PCM16b(code, last_sample, delta, index);
//放大音量,last_sample的范围是-2048至,放大至-32768至,放大倍数为16倍
result = (last_sample << 4);
if(result > 32767)
result = 32767;
else if(result < -32768)
result = -32768;
Write_16bit_PCM_LittleEndian(result, dstbuff + dstlen); //把转换所得的16位pcm编码数据按小端字节序
写入到输出缓存
//累计48个0或者1之后,复位转换算法
if(code == 0x00 || code == 0x08)
count ++;
if(count == 48)
{
last_sample = 0;
delta = delta_init;
count = 0;
}
// 再次取出一个位的ADPCM编码数据
code = (srcbuff[dolen] >> 4);
//code = srcbuff[dolen] & 0xF;
result = last_sample = VoxADPCM4b_to_PCM16b(code, last_sample, delta, index);
//放大音量,last_sample的范围是-2048至,放大至-32768至,放大倍数为16倍
result = (last_sample << 4);
if(result > 32767)
result = 32767;
else if(result < -32768)
result = -32768;
Write_16bit_PCM_LittleEndian(result, dstbuff + dstlen + 2);
//累计48个1或者0之后,复位算法
if(code == 0x00 || code == 0x08)
count ++;
if(count == 48)
{
last_sample = 0;
delta = delta_init;
count = 0;
}
//
dstlen += 4;
dolen ++;
}
return dstlen;
}