把vox文件转换为pcm编码格式的wave文件

jianggh_wuhee 2012-10-25 06:50:37

最近搞一个小项目,其中需要把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;

}

...全文
479 3 打赏 收藏 转发到动态 举报
写回复
用AI写文章
3 条回复
切换为时间正序
请发表友善的回复…
发表回复
jianggh_wuhee 2012-11-01
  • 打赏
  • 举报
回复
"ffmpeg"? 能说清楚一些么,是个大神,还是……?
真的折腾好久了,就是没效果啊,肯定是算法问题,但是,没有参考,没人指点,也没进展,好郁闷!
CyberLogix 2012-10-26
  • 打赏
  • 举报
回复
ffmpeg就可以完成请参考他的代码
jianggh_wuhee 2012-10-26
  • 打赏
  • 举报
回复
挂了一天,一条回复都没有啊,自己顶一下!

2,543

社区成员

发帖
与我相关
我的任务
社区描述
专题开发/技术/项目 多媒体/流媒体开发
社区管理员
  • 多媒体/流媒体开发社区
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧