谁能提供各种音频格式转换的源代码?????????

解决方案 »

  1.   

    记得2002年有一期程序员杂志有源码。
    我也贴一份我收藏的,也许有帮助:使用CODECs压缩Wave音频概要
    微软的Win95和WinNT操作系统都包含有能够压缩解压缩Wave音频流的
    CODECs。将你的wave 音频以压缩形式保存不但能够减少对存储空间的
    需求,在网络上传送时也能减少数据传输 的时间。本文及其附带的实例代码告诉你怎样使用安装在Windows系统中的CODECs
    来压缩和解压缩音 频。稍稍改变这些代码就可以用作解压缩经过压缩的
    数据,执行数据格式转换。 所附实例代码是用Microsoft Visual C++ 5.0
    版本开发的,并在Win95和WinNT 4.0操作系 统上测试过。简介
    Win95及最近的WinNT都具有能过安装的CODECs处理压缩的wave格式的音
    频和视频数据流的 能力。一个CODEC是一小段用于压缩(COmpress)及解压缩(DECompress)数
    据流的代码(因此, 得名CO-DEC)。许多CODECs即能压缩又能解压缩。
    而一些CODECs仅能用于解压缩,这样私 有数据可以在系统上播放,但
    数据格式不能在系统上创建。尽管一个CODEC原则上能够用于压缩解压缩任一种数据流,还是设计有
    各种各样的CODECs 以实现以高的压缩比率,更好的保真度或实时压缩
    性能来压缩某种数据类型。例如,获取 高的视频压缩数据压缩率的最
    好方法应用于音频数据时未必就能得到相同的效果,反之也 然。本文着重于怎样在自己的代码中使用CODEC将音频数据以你的系统中CODECs
    所支持的方式进 行压缩。压缩音频数据的一个主要原因是降低存储某
    一声音序列所需数据量。少的数据量 意味着声音所占有的空间更少,
    并且能够以更快的速度在MODEM和网络上传递。如果数据 以Windows系
    统所支持的某一通用格式压缩的话,则可以不必手工解压缩就直接播放
    --系 统将使用它自己的CODECs解压缩数据并播放。我的系统中有什么CODECs?
    Win95和WinNT本身附带有几种标准的CODECs,也可由系统中所安装的应
    用程序安装其他的 CODECs。例如,DSP Group,Inc. TrueSpeech CODEC
    随Win95发送,因此你写的任何应用于 Win95的程序都可应用此CODEC
    (假如用户没有在控制面板中删除它或禁止它的话)。以后 可能要安装
    的CODEC的一个例子是微软网络(MSN)软件自已所用的音频数据。所有安装的CODECs由音频压缩管理器(ACM)管理。我们可以从一小程序
    中查询ACM来查到安 装了哪些CODECs,它们都支持什么格式。你也可双
    击控制面板中的多媒体选项,选择高级 标签,就能看到系统中所安装的CODECs。介绍应用ACM,得知它所管理每一个CODEC都可以做些什么的一个好方法
    是写一个简单的 查询ACM的命令行应用程序。本文所附带的CAPS程序完
    成的就是这个功能--让我们看看它的 代码,我将给你一起分析此程序,
    解释每一步完成的什么功能。首先从调用ACM编程接口所需的包含的头文件开始 :
    #include &ltwindows.h>
    #include &ltmmsystem.h>
    #include &ltmmreg.h> // 多媒体注册#include &ltmsacm.h> // 音频压缩管理器#include &ltstdio.h>mmsystem.h头文件定了Windows支持大部分的多媒体功能,但不包含ACM
    接口及任何厂商定义。 mmreg.h包含了对不同厂商设计的各种wave数据
    类型的格式标签的定义。它也包含了用于处理 不同的wave数据类型的结
    构( 基于WAVEFORAMTEX)的定义。msacm.h包含了ACM所需的API, 标志
    等等。我们要做的第一件事就是执行一些常见的ACM查询来判断版本号,获取诸
    如它当前管理了多少 个驱动程序的的信息。下面是查询ACM的部分代码:
    // 取得ACM版本号DWORD dwACMVer = acmGetVersion();
    printf("ACM version %u.%.02u build %u", HIWORD(dwACMVer) >> 8, HIWORD(dwACMVer) & 0x00FF, LOWORD(dwACMVer));
    if (LOWORD(dwACMVer) == 0) printf(" (Retail)");
    printf("\n");// 显示一些ACM的信息
    printf("ACM metrics:\n");DWORD dwCodecs = 0;
    MMRESULT mmr = acmMetrics(NULL, ACM_METRIC_COUNT_CODECS, &dwCodecs);
    if (mmr) {
    show_error(mmr);
    } else {
    printf("%lu codecs installed\n", dwCodecs);
    }
    CAPS实例查询了更多的ACM信息。你可以仔细查看它的代码,运行程序得知结果。对ACM有了简单了解后,现在可以要求它枚举出系统中当前所有的驱动
    程序。我们在程序中所 调用的枚举函数使用回调函数来汇报每个设备
    的数据,这在Windows编程是一种很普遍的方法。 下面的调用就是枚举
    当前ACM所管理的所有设备:
    // 枚举所有允许的驱动程序printf("Enabled drivers:\n");mmr = acmDriverEnum(DriverEnumProc, 0, 0); if (mmr) show_error(mmr);
    如同其它多媒体函数,许多ACM函数调用返回一MMRESULT值,指出了可
    能发生的错误。此值为0 表示函数成功执行。现在,让我们看看枚举回
    调函数DriverEnumProc,它由系统中的每一个驱 动程序调用:
    BOOL CALLBACK DriverEnumProc(HACMDRIVERID hadid, DWORD dwInstance, DWORD fdwSupport){printf(" id: %8.8lxH", hadid);printf(" supports:\n");if (fdwSupport & ACMDRIVERDETAILS_SUPPORTF_ASYNC) printf(" async conversions\n");if (fdwSupport & ACMDRIVERDETAILS_SUPPORTF_CODEC) printf(" different format conversions\n");if (fdwSupport & ACMDRIVERDETAILS_SUPPORTF_CONVERTER) printf(" same format conversions\n");if (fdwSupport & ACMDRIVERDETAILS_SUPPORTF_FILTER) printf(" filtering\n");// 获得一些具体信息ACMDRIVERDETAILS dd;dd.cbStruct = sizeof(dd);MMRESULT mmr = acmDriverDetails(hadid, &dd, 0);if (mmr) {printf(" "); show_error(mmr);} else {printf(" Short name: %s\n", dd.szShortName);printf(" Long name: %s\n", dd.szLongName);printf(" Copyright: %s\n", dd.szCopyright);printf(" Licensing: %s\n", dd.szLicensing);printf(" Features: %s\n", dd.szFeatures);printf(" Supports %u formats\n", dd.cFormatTags);printf(" Supports %u filter formats\n", dd.cFilterTags);}// 打开驱动程序HACMDRIVER had = NULL;mmr = acmDriverOpen(&had, hadid, 0);if (mmr) {printf(" "); show_error(mmr);} else {DWORD dwSize = 0;mmr = acmMetrics(had, ACM_METRIC_MAX_SIZE_FORMAT, &dwSize);if (dwSize cbSize = LOWORD(dwSize) - sizeof(WAVEFORMATEX);pwf->wFormatTag = WAVE_FORMAT_UNKNOWN;ACMFORMATDETAILS fd;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);}return TRUE; // 继续枚举}
      

  2.   

    驱动程序向回调函数传递了描述驱动程序所支持类型的一组标志。一些驱动程序可以异步 操作,而另一些驱动程序则不能。一些驱动程序能够将一种wave数据格式转换成另一种格 式(称作CODECs),而另一些驱动程序仅能完成过滤操作,其输入输出格式是一样的。注 意ACM维护着这类数据及驱动程序的名字,版权信息等等,这样我们可以不必装载或打开 指定的驱动程序就可以得到这些数据。这样很方便,譬如当需将数据放在列表框中由用户 选择时。要获得有关某一驱动程序能力更多的详细信息,必须装载驱动程序并打开它,可通过调用 acmOpenDriver实现。一旦驱动程序打开,可请求枚举它所支持的wave数据格式。同时有一 个小问题--尽管所有wave格式描述结构基于WAVEFORAMTEX,许多格式使用此结构的扩展形 式来保存其特定的信息。如果我们想枚举所有格式,需要知道为此结构分配多少供驱动 程序填写详细信息的空间。可通过向acmMetrics函数传递ACM_METRIC_MAX_SIZE_FORMAT标 志得到所需的最大的结构的尺寸。如果你读过上面的代码,你会发现我只是简单的将分配的空间强制转换为WAVEFORMATEX指针。 我只对通用信息而不是任一特定类型的数据感兴趣,因此这个指针符合我的要求。为结构分配了空间后,现在我可以acmFormatEnum来枚举所支持的格式。这次又用到一个回 调函数来取得枚举出的格式的相关数据:
    BOOL CALLBACK FormatEnumProc(HACMDRIVERID hadid, LPACMFORMATDETAILS pafd,DWORD dwInstance, DWORD fdwSupport){printf(" %4.4lXH, %s\n", pafd->dwFormatTag, pafd->szFormat);return TRUE; // 继续枚举}
    如你所看到的,这是一次尝试并仅打印出格式的某些信息。这样,通过上面的代码,你能够查询ACM所有的驱动程序,查找每一
    个驱动程序所支持的 格式。我建议你现在运行CAPS程序,看看你的
    系统上安装了些什么。 使用特定的CODEC
    好了,我们已得知你的系统上安装了什么CODECs--现在来看看怎样查
    找某一特定的CODEC并 使用它压缩音频数据。让我们看看CONV实例,
    它使用一种有效的CODEC压缩一个简单的wave 数据包。为了使代码更
    趋简单,我以控制台应用程序的形式的实现它,也没有尝试去 播放压
    缩过的数据或将其存入文件。这个实例的代码仅向你展示怎样找到你所
    需要的驱 动程序并使用它将数据转换为压缩格式。剩下的就靠你了。两步实现压缩
    在理想的情况下,压缩一些数据可能只不过是向系统说:“这有一些数据,
    请压缩成这种格式。” 不幸的是,Windows编程与理想相去甚远,象通常一
    样,我们得自已做许多琐碎的工作。 要解决的第一个也是最重要的问题是给
    定的CODEC可能不能压缩你所使用的数据格式。例 如,我们录入了一些11025Hz,8位,
    单声道的PCM数据(或许是用户向麦克风说的话),此 种格式几乎所有的
    多媒体PC都能录制。我们可能要将数据通过MODEM传递,因此我们想尽可能
    的压缩数据,使数据量减少。我们选择了TrucSpeech CODEC,它安装在
    Windows中,能够 获得大约10:1的压缩率。我们所要碰到的问题就是
    TrueSpeech CODEC不能处理11025Hz, 8位,单声道的PCM数据。它只能处
    理8000Hz,16位,单声道的数据(某些情况下是8位)。 因此我们必须先将
    源数据转换为TrueSpeech CODEC所支持的中间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个周期WAVEFORMATEX wfSrc;memset(&wfSrc, 0, sizeof(wfSrc)); wfSrc.cbSize = 0;wfSrc.wFormatTag = WAVE_FORMAT_PCM; // PCM wfSrc.nChannels = 1; // MonowfSrc.nSamplesPerSec = 11025; // 11.025 kHzwfSrc.wBitsPerSample = 8; // 8 bitwfSrc.nBlockAlign = wfSrc.nChannels * wfSrc.wBitsPerSample / 8;wfSrc.nAvgBytesPerSec = wfSrc.nSamplesPerSec * wfSrc.nBlockAlign;DWORD dwSrcSamples = wfSrc.nSamplesPerSec;BYTE* pSrcData = new BYTE [dwSrcSamples]; // 1秒种的长度BYTE* pData = pSrcData; double f = 1000.0;double pi = 4.0 * atan(1.0); double w = 2.0 * pi * f;for (DWORD dw = 0; dw 上面的代码创建了一个WAVEFORMATEX结构用来描述源数据格式,并用简单的
    数学方法生成了1秒钟长的11.025kHz,8位单声道的PCM的wave数据。下一步就是选择要将数据转换成什么格式及选定一个合适的CODEC。WORD wFormatTag = WAVE_FORMAT_DSPGROUP_TRUESPEECH;// 现在我们选定一个支持目标格式标签的CODECHACMDRIVERID hadid = find_driver(wFormatTag);if (hadid == NULL) {printf("No driver found\n");exit(1);}printf("Driver found (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("Error getting format info\n");exit(1); }printf("Driver format: %u bits, %lu samples per second\n",pwfDrv->wBitsPerSample, pwfDrv->nSamplesPerSec);// 获取驱动程序所支持的PCM格式标签// 注意:我们只是选取第一支持的PCM格式但不一定是最好的选择WAVEFORMATEX* pwfPCM = get_driver_format(hadid, WAVE_FORMAT_PCM);if (pwfPCM == NULL) { printf("Error getting PCM format info\n");exit(1);} printf("PCM format: %u bits, %lu samples per second\n",pwfPCM->wBitsPerSample, pwfPCM->nSamplesPerSec);
    有点重复了,要注意的是get_driver_format函数仅仅枚举出第一种匹配的格式--也许不能获得可能的最好的质量。现在我们有了WAVEFORMATEX结构描述源格式,中间PCM格式,以及最终的压缩格式。可以开始转换数据了。转换由被ACM称作流的对象来实现。我们可以打开流,将源格式、目标格式传递给它,要求它进行转换。在此要注意如果CODEC的算法复杂的话同步转换是很耗时的。一些CODEC可以异步工作,通过向窗口发送一个消息,或调用一个回调函数,或设置一个事件告知你转换进程。下面的代码是以最少麻烦准则完成任务的--你必须等待它直到完成。还有另外一点,很重要。如你所知,我们打开转换流,指明ACM_STREAMOPENF_NONREALTIME标志。这非常重要。若省略此标志,那么一些驱动程序(例如TrueSpeech驱动程序)将会报告错误512(意思是不可能)。此错误告诉你所要求的转换不能实时进行。我的实例代码中没有这个问题,但如果你试图在播放数据的同时转换大量数据,就要注意这点了。
      

  3.   

    让我们看看第一步的转换,它完成的是将源数据转换为中间格式://///////////////////////////////////////////////////////////////////////////// 将源wave转换为CODEC所支持的PCM格式// 我们使用任一种能实现PCM格式间转换驱动程序HACMSTREAM hstr = NULL; mmr = acmStreamOpen(&hstr,NULL, // 任一驱动程序&wfSrc, // 源格式pwfPCM, // 目标格式NULL, // 无过滤NULL, // 没回调0, // 实例数据(未使用)ACM_STREAMOPENF_NONREALTIME); // 标志if (mmr) {printf("Failed to open a stream to do PCM to PCM conversion\n");exit(1); } // 为转换结果开辟一个缓冲区DWORD dwSrcBytes = dwSrcSamples * wfSrc.wBitsPerSample / 8;DWORD dwDst1Samples = dwSrcSamples * pwfPCM->nSamplesPerSec / wfSrc.nSamplesPerSec;DWORD dwDst1Bytes = dwDst1Samples * pwfPCM->wBitsPerSample / 8;BYTE* pDst1Data = new BYTE [dwDst1Bytes]; // 填写转换信息ACMSTREAMHEADER strhdr; 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("Converting to intermediate PCM format...\n");mmr = acmStreamConvert(hstr, &strhdr, 0); if (mmr) {printf("Failed to do PCM to PCM conversion\n"); exit(1); }printf("Converted OK\n"); // 关闭流acmStreamClose(hstr, 0);
    当流打开时,第二个参数为NULL,表示接受任何驱动程序进行转换。复杂的只是计算需要给输出数据分配多大的缓冲区。由于PCM格式间的转换不牵扯压缩和解压缩,直接就计算出来了。你可能注意到调用了acmStreamPrepareHeader,它为驱动程序安排好一切并允许驱动程序在转换前锁定内存。最后一步是将中间格式转换为最终的压缩格式:///////////////////////////////////////////////////////////////////////////////////// 将中间格式转换为最终的压缩格式// 打开驱动程序HACMDRIVER had = NULL;mmr = acmDriverOpen(&had, hadid, 0); if (mmr) {printf("Failed to open driver\n"); exit(1); }// 打开转换流// 注意使用了ACM_STREAMOPENF_NONREALTIME标志. // 没有此标志一些软件压缩程序会报告512号错误--即不可能mmr = acmStreamOpen(&hstr, had, // 驱动程序句柄pwfPCM, // 源格式pwfDrv, // 目标格式NULL, // 不过滤NULL, // 无回调函数0, // 实例数据(未使用)ACM_STREAMOPENF_NONREALTIME); // 标志if (mmr) {printf("Failed to open a stream to do PCM to driver format conversion\n");exit(1); } // 为转换结果分配一个缓冲区// 根据以字节计的平均速率计算输出缓冲区的尺寸// 并加上一机动位(bit)// 没有此额外的空间IMA_ADPCM驱动程序将不能转换DWORD dwDst2Bytes = pwfDrv->nAvgBytesPerSec * dwDst1Samples /pwfPCM->nSamplesPerSec;dwDst2Bytes = dwDst2Bytes * 3 / 2; // 增加一点空间BYTE* pDst2Data = new BYTE [dwDst2Bytes]; // 填写转换信息ACMSTREAMHEADER strhdr2; memset(&strhdr2, 0, sizeof(strhdr2));strhdr2.cbStruct = sizeof(strhdr2);strhdr2.pbSrc = pDst1Data; // 要转换的源数据strhdr2.cbSrcLength = dwDst1Bytes; strhdr2.pbDst = pDst2Data;strhdr2.cbDstLength = dwDst2Bytes; // 准备头mmr = acmStreamPrepareHeader(hstr, &strhdr2, 0); // 转换数据printf("Converting to final format...\n");mmr = acmStreamConvert(hstr, &strhdr2, 0); if (mmr) {printf("Failed to do PCM to driver format conversion\n");exit(1); }printf("Converted OK\n");// 关闭流及驱动程序mmr = acmStreamClose(hstr, 0);mmr = acmDriverClose(had, 0);
    以上与PCM格式间转换很相似,但此次我们提供了打开流时想要使用的
    驱动程序的句柄。实际上,此处仍可使用NULL,因为已预知此驱动程序存在,但提供句柄避免了系
    统浪费时间为我们查找此驱动程序。
    计算用于压缩数据的缓冲区的尺寸有点难办,需要凭猜测。WAVEFORMATEX结
    构的nAvgBytesPerSec 域表示回放期间读取字节的平均速率。我们可使用它来
    估计存储压缩的wave需要多大空间。一 些驱动程序给出的数据确实是平均的,
    而不是最差场合下的值,因此我选择多增加50%的空间。 此方法在实践中虽然
    有些浪费但很有效。一旦转换完成,ACMSTREAMHEADER的结构的cbDstLengthUsed
    域指出缓冲区实际用了多少字节。我使用它来计算压缩比: 
    // 显示转换统计结果printf("Source wave had %lu bytes\n", dwSrcBytes);printf("Converted wave has %lu bytes\n", strhdr2.cbDstLengthUsed);printf("Compression ratio is %f\n", (double) dwSrcBytes /(double) strhdr2.cbDstLengthUsed);
    总结
    使用Windows操作系统中所附的CODECs来压缩wave格式数据易于使用,并使数据占用存储空间更少,传输时间更短。如果你有自已的压缩格式,可创建并安装自已的CODEC,象这里的代码一样使用它。
      

  4.   

    你好 !谢谢!能给我发一份源代码么??真的感谢!![email protected]
      

  5.   

    http://www.hoyoco.net/soft/vc/chinese/multimedia/01.txt
    http://cnprogram.myrice.com/article/vc/vc410.html