使用CODECs压缩Wave音频.docx

上传人:b****8 文档编号:10579365 上传时间:2023-02-21 格式:DOCX 页数:19 大小:24.18KB
下载 相关 举报
使用CODECs压缩Wave音频.docx_第1页
第1页 / 共19页
使用CODECs压缩Wave音频.docx_第2页
第2页 / 共19页
使用CODECs压缩Wave音频.docx_第3页
第3页 / 共19页
使用CODECs压缩Wave音频.docx_第4页
第4页 / 共19页
使用CODECs压缩Wave音频.docx_第5页
第5页 / 共19页
点击查看更多>>
下载资源
资源描述

使用CODECs压缩Wave音频.docx

《使用CODECs压缩Wave音频.docx》由会员分享,可在线阅读,更多相关《使用CODECs压缩Wave音频.docx(19页珍藏版)》请在冰豆网上搜索。

使用CODECs压缩Wave音频.docx

使用CODECs压缩Wave音频

使用CODECs压缩Wave音频

概要

微软的Win95和WinNT操作系统都包含有能够压缩解压缩Wave音频流的

CODECs。

将你的wave音频以压缩形式保存不但能够减少对存储空间的

需求,在网络上传送时也能减少数据传输的时间。

本文及其附带的实例代码告诉你怎样使用安装在Windows系统中的CODECs

来压缩和解压缩音频。

稍稍改变这些代码就可以用作解压缩经过压缩的

数据,执行数据格式转换。

所附实例代码是用MicrosoftVisualC++5.0

版本开发的,并在Win95和WinNT4.0操作系统上测试过。

简介

Win95及最近的WinNT都具有能过安装的CODECs处理压缩的wave格式的音

频和视频数据流的能力。

一个CODEC是一小段用于压缩(COmpress)及解压缩(DECompress)数

据流的代码(因此,得名CO-DEC)。

许多CODECs即能压缩又能解压缩。

而一些CODECs仅能用于解压缩,这样私有数据可以在系统上播放,但

数据格式不能在系统上创建。

尽管一个CODEC原则上能够用于压缩解压缩任一种数据流,还是设计有

各种各样的CODECs以实现以高的压缩比率,更好的保真度或实时压缩

性能来压缩某种数据类型。

例如,获取高的视频压缩数据压缩率的最

好方法应用于音频数据时未必就能得到相同的效果,反之也然。

本文着重于怎样在自己的代码中使用CODEC将音频数据以你的系统中CODECs

所支持的方式进行压缩。

压缩音频数据的一个主要原因是降低存储某

一声音序列所需数据量。

少的数据量意味着声音所占有的空间更少,

并且能够以更快的速度在MODEM和网络上传递。

如果数据以Windows系

统所支持的某一通用格式压缩的话,则可以不必手工解压缩就直接播放

--系统将使用它自己的CODECs解压缩数据并播放。

我的系统中有什么CODECs?

Win95和WinNT本身附带有几种标准的CODECs,也可由系统中所安装的应

用程序安装其他的CODECs。

例如,DSPGroup,Inc.TrueSpeechCODEC

随Win95发送,因此你写的任何应用于Win95的程序都可应用此CODEC

(假如用户没有在控制面板中删除它或禁止它的话)。

以后可能要安装

的CODEC的一个例子是微软网络(MSN)软件自已所用的音频数据。

所有安装的CODECs由音频压缩管理器(ACM)管理。

我们可以从一小程序

中查询ACM来查到安装了哪些CODECs,它们都支持什么格式。

你也可双

击控制面板中的多媒体选项,选择高级标签,就能看到系统中所安装的CODECs。

介绍应用ACM,得知它所管理每一个CODEC都可以做些什么的一个好方法

是写一个简单的查询ACM的命令行应用程序。

本文所附带的CAPS程序完

成的就是这个功能--让我们看看它的代码,我将给你一起分析此程序,

解释每一步完成的什么功能。

首先从调用ACM编程接口所需的包含的头文件开始:

#include

#include

#include//多媒体注册

#include//音频压缩管理器

#include

mmsystem.h头文件定了Windows支持大部分的多媒体功能,但不包含ACM

接口及任何厂商定义。

mmreg.h包含了对不同厂商设计的各种wave数据

类型的格式标签的定义。

它也包含了用于处理不同的wave数据类型的结

构(基于WAVEFORAMTEX)的定义。

msacm.h包含了ACM所需的API,标志

等等。

我们要做的第一件事就是执行一些常见的ACM查询来判断版本号,获取诸

如它当前管理了多少个驱动程序的的信息。

下面是查询ACM的部分代码:

//取得ACM版本号

DWORDdwACMVer=acmGetVersion();

printf("ACMversion%u.%.02ubuild%u",

HIWORD(dwACMVer)>>8,

HIWORD(dwACMVer)&0x00FF,

LOWORD(dwACMVer));

if(LOWORD(dwACMVer)==0)printf("(Retail)");

printf("\n");

//显示一些ACM的信息

printf("ACMmetrics:

\n");

DWORDdwCodecs=0;

MMRESULTmmr=acmMetrics(NULL,ACM_METRIC_COUNT_CODECS,&dwCodecs);

if(mmr){

show_error(mmr);

}

else{

printf("%lucodecsinstalled\n",dwCodecs);

}

CAPS实例查询了更多的ACM信息。

你可以仔细查看它的代码,运行程序得知结果。

对ACM有了简单了解后,现在可以要求它枚举出系统中当前所有的驱动

程序。

我们在程序中所调用的枚举函数使用回调函数来汇报每个设备

的数据,这在Windows编程是一种很普遍的方法。

下面的调用就是枚举

当前ACM所管理的所有设备:

//枚举所有允许的驱动程序

printf("Enableddrivers:

\n");

mmr=acmDriverEnum(DriverEnumProc,0,0);

if(mmr)show_error(mmr);

如同其它多媒体函数,许多ACM函数调用返回一MMRESULT值,指出了可

能发生的错误。

此值为0表示函数成功执行。

现在,让我们看看枚举回

调函数DriverEnumProc,它由系统中的每一个驱动程序调用:

BOOLCALLBACKDriverEnumProc(HACMDRIVERIDhadid,DWORDdwInstance,DWORDfdwSupport)

{

printf("id:

%8.8lxH",hadid);

printf("supports:

\n");

if(fdwSupport&ACMDRIVERDETAILS_SUPPORTF_ASYNC)printf("asyncconversions\n");

if(fdwSupport&ACMDRIVERDETAILS_SUPPORTF_CODEC)printf("differentformatconversions\n");

if(fdwSupport&ACMDRIVERDETAILS_SUPPORTF_CONVERTER)printf("sameformatconversions\n");

if(fdwSupport&ACMDRIVERDETAILS_SUPPORTF_FILTER)printf("filtering\n");

//获得一些具体信息

ACMDRIVERDETAILSdd;

dd.cbStruct=sizeof(dd);

MMRESULTmmr=acmDriverDetails(hadid,&dd,0);

if(mmr){

printf("");show_error(mmr);

}

else{

printf("Shortname:

%s\n",dd.szShortName);

printf("Longname:

%s\n",dd.szLongName);

printf("Copyright:

%s\n",dd.szCopyright);

printf("Licensing:

%s\n",dd.szLicensing);

printf("Features:

%s\n",dd.szFeatures);

printf("Supports%uformats\n",dd.cFormatTags);

printf("Supports%ufilterformats\n",dd.cFilterTags);

}

//打开驱动程序

HACMDRIVERhad=NULL;

mmr=acmDriverOpen(&had,hadid,0);

if(mmr){

printf("");show_error(mmr);

}

else{

DWORDdwSize=0;

mmr=acmMetrics(had,ACM_METRIC_MAX_SIZE_FORMAT,&dwSize);

if(dwSizecbSize=LOWORD(dwSize)-sizeof(WAVEFORMATEX);

pwf->wFormatTag=WAVE_FORMAT_UNKNOWN;

ACMFORMATDETAILSfd;

memset(&fd,0,sizeof(fd));

fd.cbStruct=sizeof(fd);

fd.pwfx=pwf;

fd.cbwfx=dwSize;

fd.dwFormatTag=WAVE_FORMAT_UNKNOWN;

mmr=acmFormatEnum(had,&fd,FormatEnumProc,0,0);

if(mmr){

printf("");

show_error(mmr);

}

free(pwf);

acmDriverClose(had,0);

}

returnTRUE;//继续枚举

}

驱动程序向回调函数传递了描述驱动程序所支持类型的一组标志。

一些驱动程序可以异步操作,而另一些驱动程序则不能。

一些驱动程序能够将一种wave数据格式转换成另一种格式(称作CODECs),而另一些驱动程序仅能完成过滤操作,其输入输出格式是一样的。

注意ACM维护着这类数据及驱动程序的名字,版权信息等等,这样我们可以不必装载或打开指定的驱动程序就可以得到这些数据。

这样很方便,譬如当需将数据放在列表框中由用户选择时。

要获得有关某一驱动程序能力更多的详细信息,必须装载驱动程序并打开它,可通过调用acmOpenDriver实现。

一旦驱动程序打开,可请求枚举它所支持的wave数据格式。

同时有一个小问题--尽管所有wave格式描述结构基于WAVEFORAMTEX,许多格式使用此结构的扩展形式来保存其特定的信息。

如果我们想枚举所有格式,需要知道为此结构分配多少供驱动程序填写详细信息的空间。

可通过向acmMetrics函数传递ACM_METRIC_MAX_SIZE_FORMAT标志得到所需的最大的结构的尺寸。

如果你读过上面的代码,你会发现我只是简单的将分配的空间强制转换为WAVEFORMATEX指针。

我只对通用信息而不是任一特定类型的数据感兴趣,因此这个指针符合我的要求。

为结构分配了空间后,现在我可以acmFormatEnum来枚举所支持的格式。

这次又用到一个回调函数来取得枚举出的格式的相关数据:

BOOLCALLBACKFormatEnumProc(HACMDRIVERIDhadid,LPACMFORMATDETAILSpafd,

DWORDdwInstance,DWORDfdwSupport)

{

printf("%4.4lXH,%s\n",pafd->dwFormatTag,pafd->szFormat);

returnTRUE;//继续枚举

}

如你所看到的,这是一次尝试并仅打印出格式的某些信息。

这样,通过上面的代码,你能够查询ACM所有的驱动程序,查找每一

个驱动程序所支持的格式。

我建议你现在运行CAPS程序,看看你的

系统上安装了些什么。

使用特定的CODEC

好了,我们已得知你的系统上安装了什么CODECs--现在来看看怎样查

找某一特定的CODEC并使用它压缩音频数据。

让我们看看CONV实例,

它使用一种有效的CODEC压缩一个简单的wave数据包。

为了使代码更

趋简单,我以控制台应用程序的形式的实现它,也没有尝试去播放压

缩过的数据或将其存入文件。

这个实例的代码仅向你展示怎样找到你所

需要的驱动程序并使用它将数据转换为压缩格式。

剩下的就靠你了。

两步实现压缩

在理想的情况下,压缩一些数据可能只不过是向系统说:

“这有一些数据,

请压缩成这种格式。

”不幸的是,Windows编程与理想相去甚远,象通常一

样,我们得自已做许多琐碎的工作。

要解决的第一个也是最重要的问题是给

定的CODEC可能不能压缩你所使用的数据格式。

例如,我们录入了一些11025Hz,8位,

单声道的PCM数据(或许是用户向麦克风说的话),此种格式几乎所有的

多媒体PC都能录制。

我们可能要将数据通过MODEM传递,因此我们想尽可能

的压缩数据,使数据量减少。

我们选择了TrucSpeechCODEC,它安装在

Windows中,能够获得大约10:

1的压缩率。

我们所要碰到的问题就是

TrueSpeechCODEC不能处理11025Hz,8位,单声道的PCM数据。

它只能处

理8000Hz,16位,单声道的数据(某些情况下是8位)。

因此我们必须先将

源数据转换为TrueSpeechCODEC所支持的中间PCM格式,然后在使用它将中

间数据转换为最终所需的格式。

可使用随Windows分发的某种不同的CODEC将一种PCM格式转换为另一种格式,

因此你需使用某种CODEC将数据转换为其它CODEC能够处理的格式。

我们已知道

怎样去枚举CODECs及其所支持的格式,因此这样做是可以实现的。

但还有一个问题,我在实例代码中忽略了,留给你们解决。

如果某一CODEC能

够创建我们所想要的压缩格式,但支持几种不同的输入格式,我们怎样选择

最佳的中间格式呢?

按照Nigel的准则,那就是“总是做最少量的工作”,我

选择使用CODEC所支持的枚举出的第一种PCM格式。

由于很容易实现,可能会导

致数据失真。

假设我们要使用的某一CODEC有一些近乎无失真的压缩算法,能

够接收8位或16位的11025Hz或22050Hz的PCM数据。

我们要转换以441000Hz,

16位立体声录制的高保真的样本。

我们试图降低数据量,而不在乎损失质量。

如果我们枚举此CODEC所支持的格式,第一个得到的可能是11025Hz,8位单声

道的格式。

我们先将数据转换为此格式,然后进行压缩,这其间肯定要损失一

些质量,因为这种中间格式不够好。

如果使用16位22050Hz的话会好一些。

告诉过你这种缺憾啦,让我们瞧瞧CONV实例,看它是怎样工作的。

CONV实例程序

CONV实例分四个阶段:

它创建一些wave格式数据的样本,找到一个合适的CODEC,

将数据转换为此CODEC可处理的中间格式,最后将数据转换成所需的格式。

为了

简单其间,源数据用程序创建而不是录入或是从wave文件中读取:

//首先我们创建一个可能是刚刚才录制的wave其格式为11.025kHz,

//8位单声道PCM,这是一个所有机器上都可用的录入格式,我们的例

//子是1秒长的1kHz的正弦波wave,刚好1000个周期

WAVEFORMATEXwfSrc;

memset(&wfSrc,0,sizeof(wfSrc));

wfSrc.cbSize=0;

wfSrc.wFormatTag=WAVE_FORMAT_PCM;//PCM

wfSrc.nChannels=1;//Mono

wfSrc.nSamplesPerSec=11025;//11.025kHz

wfSrc.wBitsPerSample=8;//8bit

wfSrc.nBlockAlign=wfSrc.nChannels*wfSrc.wBitsPerSample/8;

wfSrc.nAvgBytesPerSec=wfSrc.nSamplesPerSec*wfSrc.nBlockAlign;

DWORDdwSrcSamples=wfSrc.nSamplesPerSec;

BYTE*pSrcData=newBYTE[dwSrcSamples];//1秒种的长度

BYTE*pData=pSrcData;

doublef=1000.0;

doublepi=4.0*atan(1.0);

doublew=2.0*pi*f;

for(DWORDdw=0;dw

上面的代码创建了一个WAVEFORMATEX结构用来描述源数据格式,并用简单的

数学方法生成了1

秒钟长的11.025kHz,8位单声道的PCM的wave数据。

下一步就是选择要将数据转换成什么格式及选定一个合适的CODEC。

WORDwFormatTag=WAVE_FORMAT_DSPGROUP_TRUESPEECH;

//现在我们选定一个支持目标格式标签的CODEC

HACMDRIVERIDhadid=find_driver(wFormatTag);

if(hadid==NULL){

printf("Nodriverfound\n");

exit

(1);

}

printf("Driverfound(hadid:

%4.4lXH)\n",hadid);

find_driver函数枚举所有的驱动程序直到找到一个支持给定标签值的驱动程序(本例为

WAVE_FORMAT_DSPGROUP_TRUESPEECH)。

我没有在此给出代码是因为它与前面的枚举代

码非常相象。

随后你可以查看它是怎样工作的。

选定了驱动程序,现在要为最终驱动程序将产生的压缩数据格式创建一个WAVEFORMATEX

结构,并为驱动程序用于输入的中间PCM格式产生一个WAVEFORMATEX结构。

//获得格式的详情

//注意:

这只是一个给定格式签的第一种或是最可能的格式

WAVEFORMATEX*pwfDrv=get_driver_format(hadid,wFormatTag);

if(pwfDrv==NULL){

printf("Errorgettingformatinfo\n");

exit

(1);

}

printf("Driverformat:

%ubits,%lusamplespersecond\n",

pwfDrv->wBitsPerSample,pwfDrv->nSamplesPerSec);

//获取驱动程序所支持的PCM格式标签

//注意:

我们只是选取第一支持的PCM格式但不一定是最好的选择

WAVEFORMATEX*pwfPCM=get_driver_format(hadid,WAVE_FORMAT_PCM);

if(pwfPCM==NULL){

printf("ErrorgettingPCMformatinfo\n");

exit

(1);

}

printf("PCMformat:

%ubits,%lusamplespersecond\n",

pwfPCM->wBitsPerSample,pwfPCM->nSamplesPerSec);

有点重复了,要注意的是get_driver_format函数仅仅枚举出第一种匹配的格式--也许不能获得

可能的最好的质量。

现在我们有了WAVEFORMATEX结构描述源格式,中间PCM格式,以及最终的压缩格式。

可以开始转

换数据了。

转换由被ACM称作流的对象来实现。

我们可以打开流,将源格式、目标格式传递给它,

要求它进行转换。

在此要注意如果CODEC的算法复杂的话同步转换是很耗时的。

一些CODEC可以异步工作,通过向

窗口发送一个消息,或调用一个回调函数,或设置一个事件告知你转换进程。

下面的代码是以

最少麻烦准则完成任务的--你必须等待它直到完成。

还有另外一点,很重要。

如你所知,我们打

开转换流,指明ACM_STREAMOPENF_NONREALTIME标志。

这非常重要。

若省略此标志,那么一些

驱动程序(例如TrueSpeech驱动程序)将会报告错误512(意思是不可能)。

此错误告诉你所要

求的转换不能实时进行。

我的实例代码中没有这个问题,但如果你试图在播放数据的同时转换

大量数据,就要注意这点了。

让我们看看第一步的转换,它完成的是将源数据转换为中间格式:

/////////////////////////////////////////////////////////////////////////////

//将源wave转换为CODEC所支持的PCM格式

//我们使用任一种能实现PCM格式间转换驱动程序

HACMSTREAMhstr=NULL;

mmr=acmStreamOpen(&hstr,

NULL,//任一驱动程序

&wfSrc,//源格式

pwfPCM,//目标格式

NULL,//无过滤

NULL,//没回调

0,//实例数据(未使用)

ACM_STREAMOPENF_NONREALTIME);//标志

if(mmr){

printf("FailedtoopenastreamtodoPCMtoPCMconversion\n");

exit

(1);

}

//为转换结果开辟一个缓冲区

DWORDdwSrcBytes=dwSrcSamples*wfSrc.wBitsPerSample/8;

DWORDdwDst1Samples=dwSrcSamples*pwfPCM->nSamplesPerSec/wfSrc.nSamplesPerSec;

DWORDdwDst1Bytes=dwDst1Samples*pwfPCM->wBitsPerSample/8;

BYTE*pDst1Data=newBYTE[dwDst1Bytes];//填写转换信息

ACMSTREAMHEADERstrhdr;

memset(&strhdr,0,sizeof(strhdr));

strhdr.cbStruct=sizeof(strhdr);

strhdr.pbSrc=pSrcData;//要转换的源数据

strhdr.cbSrcLength=dwSrcBytes;

strhdr.pbDst=pDst1Data;

strhdr.cbDstLength=dwDst1Bytes;//准备好头

mmr=acmStreamPrepareHeader(hstr,&strhdr,0);//转换数据

printf("ConvertingtointermediatePCMformat...\n");

mmr=acmStreamConvert(hstr,&strhdr,0);

if(mmr){

printf("FailedtodoPCMtoPCMconversion\n");

exit

(1);

}

printf("Conver

展开阅读全文
相关资源
猜你喜欢
相关搜索

当前位置:首页 > 求职职场 > 简历

copyright@ 2008-2022 冰豆网网站版权所有

经营许可证编号:鄂ICP备2022015515号-1