科技行者

行者学院 转型私董会 科技行者专题报道 网红大战科技行者

知识库

知识库 安全导航

至顶网软件频道基础软件VC调用ACM音频编程接口压缩Wave音频

VC调用ACM音频编程接口压缩Wave音频

  • 扫一扫
    分享文章到微信

  • 扫一扫
    关注官方公众号
    至顶头条

本文介绍了在Microsoft Visual C++ 6.0下如何调用ACM音频压缩编程接口对原始录入的Wave音频进行数据压缩.

作者:中国电子科技集团公司 来源:天极网 2007年10月19日

关键字: VC ACM 音频编程 Wave

  • 评论
  • 分享微博
  • 分享邮件
引言

  音频和视频是多媒体应用程序向用户提供信息的主要方式,这些音频、视频数据一般都具有较高的采样率,经过压缩的原始数据才具有实用价值,否则不仅要占用大量存储空间而且在播放或进行网络传输时效率也是非常低下的,所以音频、视频数字压缩编码在多媒体应用中有着广泛而又重要的用途。本文主要对音频的编码压缩作了阐述。

  音频的编码压缩方式有许多种,如基于ITU-T G.728语音编码协议的LD-CELP 低时延码激励线性预测编码、基于ITU-T G.711语音编码协议的PCM(Pulse Code Modulation ,脉冲编码调制)编码以及我们非常熟悉的GSM数字蜂窝移动电话的语音编码标准等等。这些不同的压缩方式有着不同的数据压缩比和还原音质,具体的编码格式和算法更是大相径庭。多数协议都比较复杂,普通程序难以实现其加、解压算法,而为多媒体提供了较强支持的Windows 98操作系统引入了ACM和VCM技术,用来管理系统中存在的所有的音频和视频编、解码器(Coder-Decoder,即CODECs,用来实现音频、视频数据编解码的驱动程序)。可以通过它们提供的编程接口调用系统中存在的现成的编解码器来实现音频数据的加、解压。Windows 98系统自带的音频CODECs 支持一些早期的音频数据压缩标准,如ADPCM (Adaptive Differential Pulse Code Modulation,自适应差分脉冲编码调制)编码等,而Internet Explorer 5.0 等应用程序包含的音频CODECs支持一些较新 的压缩标准, 如MPEG Layer 3等。本文所要介 绍的就是ACM音频压缩接口的编程方法,所使用的编程工具为Microsoft Visual C++ 6.0。

  实现思路

  尽管一个CODEC在理论上能够用于压缩、解压缩任一种数据流,但还是设计有各种各样的CODECs 以实现更高的压缩比、更高的保真度或实时压缩性能来压缩某种特定的数据类型。例如,把获取很高的视频压缩数据压缩率的最好方法应用到音频数据时未必就能得到相同的效果。

  压缩音频数据的主要原理是降低存储某一声音序列所需的数据量。少的数据量就意味着声音所占有的空间更少,就能够以更快的速度通过MODEM在网络上传递。如果数据以Windows系统所支持的某种通用格式压缩的话,就可不经手工解压缩而直接播放--系 统将使用它自己的CODECs解压缩数据并播放。Windows 98本身附带有几种标准的CODECs,如DSP Group,Inc. TrueSpeech CODEC等。因此我们写的任何应用于 Windows 98下的程序都可应用这些CODEC,具体系统中都存在有哪些CODECs可以在控制面版的"多媒体"选项的"设备"标签页中查到。

  CODEC 支持从源音频格式到目标格式的转换,而在实际应用中, 可能某种CODEC 不支持直接将源音频格式转换成目标格式,比如我们通过麦克风向多媒体计算机录入了一些频率为11025Hz、8位数据、单声道的PCM数据,如果选用系统的TrueSpeech CODEC进行处理,就会引起失败,因为这种CODEC只能处理频率为8KHz,16位单声道的数据。所以转换时要采取两步转换法,即先将源格式转换成一种中间格式,再将此中间格式转换成目标格式,因为线性PCM 编码 最为简单,且为绝大多数CODEC 所支持,所以一般中间格式都选为线性PCM 格式的一种。比如就可以先将原始数据转换成TrueSpeech CODEC所支持的中间PCM格式,然后再将其通过TrueSpeech CODEC转换成最终的压缩格式。

  程序的设计实现

  有关ACM的API函数定义在头文件msacm.h中, 除了在工程中加入对此头文件的引用之外, 对ACM编程还必须包含头文件mmsystem.h和mmreg.h,这两个头文件定义了多媒体编程中最基本 的常量和数据结构。为了避免有些高 版 本ACM才提供的函数和功能在较低版本的ACM中上不可用,程序中应调用acmGetVersion函 数查询用户机器中ACM 的版本信息。

  虽然可以根据控制面版手工得到关于某种音频CODECs的信息,但在应用程序中也常常需要知道某种音频CODECs是否存在,并获取其编解码参数等信息,可以通过回调函数find_format_enum来枚举系统中的音频压缩格式:

BOOL CALLBACK find_format_enum(HACMDRIVERID hadid, LPACMFORMATDETAILS pafd, DWORD dwInstance, DWORD fdwSupport)
{
 FIND_DRIVER_INFO* pdi = (FIND_DRIVER_INFO*) dwInstance;
 if (pafd->dwFormatTag == (DWORD)pdi->wFormatTag) {
  pdi->hadid = hadid;
  return FALSE; //停止枚举
 }
 return TRUE; //继续枚举
}

  在该回调函数中用到的FIND_DRIVER_INFO是自定义的数据结构,其两个成员变量分别用来保存ACM驱动器号的句柄和要转换的数据格式:

typedef struct {
 HACMDRIVERID hadid;
 WORD wFormatTag;
} FIND_DRIVER_INFO;

  现在可以枚举出系统中当前所有的驱动程序。我们在程序中所调用的枚举函数使用回调函数来汇报每个设备的数据,这在Windows编程是一种很普遍的方法。要获得有关某一驱动程序能力更多的详细信息,必须装载驱动程序并打开它,可通过调用 acmOpenDriver实现。一旦驱动程序打开,可请求枚举它所支持的wave数据格式。但这就存在一个问题:所有wave格式描述结构都基于WAVEFORAMTEX,许多格式使用此结构的扩展形式来保存其特定的信息。如果我们想枚举所有格式,需要知道为此结构分配多少供驱动 程序填写详细信息的空间。可以通过向acmMetrics函数传递ACM_METRIC_MAX_SIZE_FORMAT标 志得到所需的最大的结构的尺寸。打开驱动程序后要通过acmMetrics函数枚举到所支持的格式,该函数可以获取到许多ACM对象的有用信息。实现该过程的主要代码如下:

BOOL CALLBACK find_driver_enum (HACMDRIVERID hadid, DWORD dwInstance, DWORD fdwSupport)
{
 ……
 MMRESULT mmr = acmDriverOpen(&had, hadid, 0);
 //枚举所支持的格式
 ……
 mmr = acmMetrics((HACMOBJ)had, ACM_METRIC_MAX_SIZE_FORMAT, &dwSize);
 if (dwSize < sizeof(WAVEFORMATEX)) dwSize = sizeof(WAVEFORMATEX);
 WAVEFORMATEX* pwf = (WAVEFORMATEX*) malloc(dwSize);
 ……
 pwf->cbSize = LOWORD(dwSize) - sizeof(WAVEFORMATEX);
 pwf->wFormatTag = pdi->wFormatTag;
 ACMFORMATDETAILS fd;
 ……
 fd.cbStruct = sizeof(fd);
 fd.pwfx = pwf;
 fd.cbwfx = dwSize;
 fd.dwFormatTag = pdi->wFormatTag;
 mmr=acmFormatEnum(had, &fd, find_format_enum, (DWORD)(VOID*)pdi, 0); //枚举格式
 ……
 acmDriverClose(had, 0); //关闭驱动器
 ……
}

  根据指定的格式要找到其所对应的ACM驱动器号可以用枚举所有音频CODECs的ACM API函数acmDriverEnum来实现,在acmDriverEnum() 的参数中指定了在前面描述过的回调函数find_driver_enum,可以 进 一 步查询每个CODEC的信息,最终可以获取到ACM驱动器号的句柄。实现此功能的回调函数名为find_driver,本文后面将会用到。

  在把原始Wave音频数据转换到中间PCM格式数据之前,需要做些前期准备工作,填充一些相关的结构信息,具体有:WAVEFORMATEX结构描述源格式、中间PCM格式、以及最终的压缩格式等。下面先填充一个用来描述源数据格式的WAVEFORMATEX结构:

WAVEFORMATEX wfSrc;
memset(&wfSrc, 0, sizeof(wfSrc));
wfSrc.cbSize = 0;
wfSrc.wFormatTag = WAVE_FORMAT_PCM; //PCM脉冲编码调制
wfSrc.nChannels = 1; //单声道
wfSrc.nSamplesPerSec = 11025; //11.025kHz
wfSrc.wBitsPerSample = 8; //8 bit
wfSrc.nBlockAlign = wfSrc.nChannels * wfSrc.wBitsPerSample / 8;
wfSrc.nAvgBytesPerSec = wfSrc.nSamplesPerSec * wfSrc.nBlockAlign;

  然后通过前面提到的回调函数find_driver来获取由wFormatTag指定的中间数据格式所对应的驱动程序的ACM驱动器号,在此设定的是由WAVE_FORMAT_DSPGROUP_TRUESPEECH指定的有Windows 98系统自带的TrueSpeech CODEC:

WORD wFormatTag = WAVE_FORMAT_DSPGROUP_TRUESPEECH;
HACMDRIVERID hadid = find_driver(wFormatTag);

  选定了驱动程序,现在要为最终驱动程序将产生的压缩数据格式创建一个WAVEFORMATEX结构,并为驱动程序用于输入的中间PCM格式产生一个WAVEFORMATEX结构:

WAVEFORMATEX* pwfDrv = get_driver_format(hadid, wFormatTag); // 获得格式的详情

  在结构pwfDrv的成员变量wBitsPerSample里存放着驱动格式的位数,在nSamplesPerSec里存放着驱动格式的采样率。然后可以用非常类似的方法获取驱动程序所支持的PCM格式标签:

WAVEFORMATEX* pwfPCM = get_driver_format(hadid, WAVE_FORMAT_PCM);

  当以上所需信息都以获取到后就可以开始转换数据了。转换由被ACM称作流的对象来实现。我们可以打开流,将源格式、目标格式传递给它,要求它进行转换。先将其转换成中间PCM格式。
    • 评论
    • 分享微博
    • 分享邮件
    邮件订阅

    如果您非常迫切的想了解IT领域最新产品与技术信息,那么订阅至顶网技术邮件将是您的最佳途径之一。

    重磅专题
    往期文章
    最新文章