您的位置:首页 > 其它

MFC 音量控制相关资料

2018-03-06 15:58 597 查看
MMRESULT 可能是 WINMM (视窗多媒体 库 Winmm.dll 里 用的一个enum型变量),

public static enum MMRESULT : uint
{
MMSYSERR_NOERROR    = 0,
MMSYSERR_ERROR      = 1,
MMSYSERR_BADDEVICEID    = 2,
MMSYSERR_NOTENABLED     = 3,
MMSYSERR_ALLOCATED      = 4,
MMSYSERR_INVALHANDLE    = 5,
MMSYSERR_NODRIVER       = 6,
MMSYSERR_NOMEM      = 7,
MMSYSERR_NOTSUPPORTED   = 8,
MMSYSERR_BADERRNUM      = 9,
MMSYSERR_INVALFLAG      = 10,
MMSYSERR_INVALPARAM     = 11,
MMSYSERR_HANDLEBUSY     = 12,
MMSYSERR_INVALIDALIAS   = 13,
MMSYSERR_BADDB      = 14,
MMSYSERR_KEYNOTFOUND    = 15,
MMSYSERR_READERROR      = 16,
MMSYSERR_WRITEERROR     = 17,
MMSYSERR_DELETEERROR    = 18,
MMSYSERR_VALNOTFOUND    = 19,
MMSYSERR_NODRIVERCB     = 20,
WAVERR_BADFORMAT    = 32,
WAVERR_STILLPLAYING     = 33,
WAVERR_UNPREPARED       = 34
}
winmm - winmm.dll - DLL文件信息 
  DLL 文件: winmm 或者 winmm.dll
  DLL 名称: Windows Multimedia API
  描述: 
  winmm.dll是Windows多媒体相关应用程序接口,用于低档的音频和游戏手柄。
  函数汇总:
  auxGetDevCaps 查询指定的辅助输出设备以确定其性能
  auxGetNumDevs 检取系统中存在的辅助输出设备的数量
  auxGetVolume 返回指定的辅助输出设备的当前卷设备
  auxOutMessage 向指定的辅助输出设备发送一条消息
  auxSetVolume 在指定的辅助输出设备中设置卷
  CloseDirver 关闭指定的可安装驱动器
  DefDriverProc 为任何不由可安装驱动器处理的消息提供的缺省处理
  Drivercallback 调用一个回调函数,发送一条消息给窗口或将一个线程的阻塞解除
  DrvGetModuleHandle 返回包含指定可安装驱动器模块的实例句柄
  DrvsendMessage 把指定的消息发送给可安装驱动器
  GetDriverModuleHandle 返回包含指定可安装驱动器模块的实例句柄
  joyGetDevCaps 查询指定的游戏杆设备以确定其性能
  joyGetNumDevs 返回系统支持的游戏杆设备的数量
  joyGetPos 查询指定的游戏杆设备的位置和活动性
  joyGetPosEx 查询一个游戏杆设备的位置和它的按扭状态
  joyGetThreshold 查询指定的游戏杆设备的当前移动阈值
  joyReleaseCapture 释放由JoySetCapture函数设置的在指定游戏杆设备上的捕获
  joySetCapture 发送一个游戏杆消息到指定的窗口
  joySetThreshold 设置指定的游戏杆设备的移动阈值
  mciGetCreatorTask 为指定的MCI设备检取其创建的任务
  mciGetDeviceID 返回和打开设备名相匹配的设备标识符
  mciGetErrorString 检取描述指定媒介控制接口错误代码的字符串
  mciGetYieldProc 返回和媒介控制接口的WAIT标志相关的回调函数的地址
  mciSendCommand 向指定的媒介控制接口设备发送一条命令
  mciSendString 向指定的媒介控制接口设备发送一个字符串
  mciSetYieldProc 设置一个过程地址,在MCI设备因指定了WAIT标志而等待一个命令完成时,该过程被周期性调用
  midiConnect 将指定的MIDI输入设备连接到输出设备
  midiDisconnect 断开MIDI输入设备和输出设备的连接
  midiInAddBuffer 向指定的音乐仪器数字接口的输入设备增加一个缓冲区
  midiInClose 关闭指定的音乐仪器数字接口的输入设备
  midiInGetDveCaps 查询指定的音乐仪器数字接口的输入设备,以确定其性能
  midiInGetErrorText 检取有关音乐仪器数字接口的输入设备指定错误的文本说明
  midiInGetID 获得一个音乐一起数字接口的输入设备的标识符
  midiInGetNumDevs 检取系统中音乐仪器数字接口的输入设备的数量
  midiInMessage 向指定的音乐仪器数字接口的输入设备驱动器发送一条消息
  midiInOpen 打开指定的音乐仪器数字接口的输入设备
  midiInPrepareHeader 为音乐仪器数字接口的输入设备准备一个缓冲区
  midiInReset 在给定的MIDI输入设备上输入,并将所有挂起的输入缓冲区标记为已执行的
  midiInStart 启动在指定的音乐仪器数字接口的输入设备上的输入
  midiInStop 停止在给定的音乐仪器数字接口的输入设备上的输入
  midiInUnprepareHeader 消除由midiInPrepareHeader函数完成的准备
  midiOutCacheDrumPatches 请求内部的一个MIDI合成设备预装指定的基于键的击打音色集
  midiOutCachePatches 请求内部的音乐仪器数字接口的合成设备预装指定的音色集
  midiOutClose 关闭指定的音乐仪器数字接口的输出设备
  midiOutGetDevCaps 查询指定的音乐仪器数字接口的输出设备,以确定其性能
  midiOutGetErrorText 检取有关MIDI输出设备指定采取的文本说明
  midiOutGetID 检取指定的MIDI输出设备的标识符
  midiOutGetNumDevs 检取系统中存在的MIDI输出设备的数量
  midiOutGetVolume 返回一个MIDI输出设备的当前卷设置
  midiOutLongMsg 向指定的MIDI输出设备发送一条系统专用的MIDI消息
  midiOutMessage 向一MIDI输出设备驱动器发送一条消息
  midiOutOpen 打开指定的MIDI输出设备进行回放
  midiOutPrepareHeader 为MIDI输出设备准备一个缓冲区
  midiOutReset 为指定的MIDI输出设备关闭所有MIDI通道上的所有标志
  midiOutSetVolume 设置一个MIDI输出设备的卷
  midiOutShortMsg 向指定的MIDI输出设备发送一条短MIDI消息
  midiOutUnprepareHeader 清除由midiOutPrepareHeader函数完成的准备
  midiStreamClose 关闭一个打开的MIDI流
  midiStreamOpen 为输出,打开一个MIDI流
  midiStreamOut 在MIDI输出设备上播放或排队一个MIDI数据流
  midiStreamPause 暂停一个MIDI流的播放
  midiStreamPosition 在一个MIDI流中检取当前位置
  midiStreamProperty 设置或检取与MIDI输出设备相关MIDI数据流的特性
  midiStreamRestart 重新启动一个暂停的MIDI流
  midiStreamStop 关掉指定MIDI输出设备的所有MIDI通道
  mixerClose 关闭指定的混频器
  mixerGetControlDetails 检取和一个声频指线路相关的单一控件的细节
  mixerGetDevCaps 查询指定的混频器以确定其性能
  mixerGetID 获取指定混频器的标识符
  mixerGetLineContrils 检取和一个声频线路相关的一个或多个控件
  mixerGetLineInfo 检取混频器有关特有线路的信息
  mixerGetNumDevs 返回系统中存在的混频器的数量
  mixerMessage 把一个定制混频器驱动器消息直接发送给混频器驱动器
  mixerOpen 打开指定的混频器,在应用程序关闭该句柄前保证该设备不被移走
  mixerSetControlDetails 设置和一个声频指线路相关的单一控件的细节
  mmioAsvance 填充一个文件的IO缓冲区
  mmioAscend 取出一个RIFF文件块
  mmioClose 关闭有mmioOpen打开的文件
  mmioCreateChunk 创建由mmioOpen函数打开的RIFF文件中的一个块
  mmioDescend 进入由mmioOpen函数打开的RIFF文件的块中,并查找一个块
  mmioFlush 把文件缓冲区的数据写入磁盘中
  mmioGetInfo 检取有关由mmioOpen函数创建的RIFF文件的信息
  mmioInstallIOProcA 装入或删除一个自定义的IO过程
  mmioOpen 为输入输出打开一个文件
  mmioRead 从由mmioOpen函数打开的文件中读取指定字节数的数据
  mmioRename 重新命名指定的文件
  mmioSeek 改变由mmioOpen函数打开的文件中的当前指针位置
  mmioSendMessage 向与指定文件相联系的IO过程发送一条消息
  mmioSetBuffer 允许或禁止文件缓冲区的IO,或改变这个缓冲区,或改变这个缓冲区的大小
  mmioSetInfo 更新从被打开文件中检取的信息
  mmioStringToFOURCC 把一个以NULL结束的字符串转换成一个4字符代码
  mmioWrite 向由mmioOpen函数打开的文件中写入指定字节数的数据
  mmsystemGetVersion 返回多媒体扩展系统软件的当前版本号
  OpenDriver 打开一个可安装驱动器实例,并用缺省设置或指定值初始化该实例
  PlaySound 播放一个波形声音
  SendDriveMessage 向指定的可安装驱动器发送一条消息
  SndPlaySound 播放一个由文件名或由登记的[sound]段的入口指定的波形声音
  timeBeginPeriod 设置应用程序或驱动程序使用的最小定时器分辨率
  timeEndPeriod 清除应用程序或驱动程序使用的最小定时器分辨率
  timeGetDevCaps 查询定时器设备以确定其性能
  timeGetSystemTime 检取从WINDOWS开始已逝去的毫秒数
  timeGetTime 检取从WINDOWS开始已逝去的毫秒数,此函数比上一条函数开销小
  timeKillEvent 毁掉指定的定时器回调事件
  timeSetEvent 设置一个定时器回调事件
  waveInAddBuffer 向波形输入设备添加一个输入缓冲区
  WaveInClose 关闭指定的波形输入设置
  waveInGetDevCaps 查询指定的波形输入设备以确定其性能
  waveInGetErrorText 检取由指定的错误代码标识的文本说明
  waveInGetID 获取指定的波形输入设备的标识符
  waveInGetNumDevs 返回系统中存在的波形输入设备的数量
  waveInGetPosition 检取指定波形输入设备的当前位置
  waveInMessage 发送一条消息给波形输入设备的驱动器
  waveInOpen 为录音而打开一个波形输入设备
  waveInPrepareHeader 为波形输入准备一个输入缓冲区
  waveInReset 停止给定的波形输入设备的输入,且将当前位置清零
  waveInStart 启动在指定的波形输入设备的输入
  waveInStop 停止在指定的波形输入设备上的输入
  waveInUnprepareHeader 清除由waveInPrepareHeader函数实现的准备
  waveOutBreakLoop 中断给定的波形输出设备上一个循环,并允许播放驱动器列表中的下一个块
  waveOutClose 关闭指定的波形输出设备
  waveOutGetDevCaps 查询一个指定的波形输出设备以确定其性能
  waveOutGetErrorText 检取由指定的错误代码标识的文本说明
  waveOutGetID 检取指定的波形输出设备的标识符
  waveOutGetNumDevs 检取系统中存在的波形输出设备的数量
  waveOutGetPitch 查询一个波形输出设备的当前音调设置
  waveOutGetPlaybackRate 查询一个波形输出设备当前播放的速度
  waveOutGetPosition 检取指定波形输出设备的当前播放位置
  waveOutGetVolume 查询指定波形输出设备的当前音量设置
  waveOutMessage 发送一条消息给一个波形输出设备的驱动器
  waveOutOpen 为播放打开一个波形输出设备
  waveOutPause 暂停指定波形输出设备上的播放
  waveOutPrepareHeader 为播放准备一个波形缓冲区
  waveOutRestart 重新启动一个被暂停的波形输出设备
  waveOutSetPitch 设置一个波形输出设备的音调
  waveOutSetPlaybackRate 设置指定波形输出设备的速度
  waveOutSetVolume 设置指定的波形输出设备的音量
  waveOutUnprepareHeader 清除由waveOutPrepareHeader函数实现的准备
  waveOutWrite 向指定的波形输出设备发送一个数据块

博客
学院
下载
GitChat
论坛
问答
商城
VIP
活动



写博客
发Chat



混音器编程接口讨论

转载 2011年11月21日 17:19:02标签:
编程 /
class /
api /
windows /
dst /
图形
2574
转自:http://www.cnblogs.com/windviki/archive/2011/08/31/2160683.html

混音器编程接口讨论
翻译:windviki@gmail.com 转载请注明
译自:MIXER API ARGUMENTATION为了理解混音器API是如何工作的,必须先了解典型的声卡的硬件布局。我们有必要想象一下,声卡上有各种独立的,清楚的,但是却又互相连接的元件。先看一种典型的,最基本的声卡。首先,如果声卡有数字音频录音功能,它必定有一个麦克风输入接口(以及某种前置放大器(pre-amp)),还有一个模数转换器(ADC)用来转换麦克风的模拟信号为数字信号流。所以,声卡应该具有两个基本元件:麦克风输入元件,ADC元件。麦克风输入是和ADC管道联通的(麦克风输入的结果是ADC的输入)。我们可以用下面的流程图来展示这两个组件以及它们之间的信息流向。



一个典型的声卡也应该有数字音频的播放能力,所以它必定还有一个DAC元件用于把数字信号流转换回模拟信号,它应该还有一个扬声器输出接口(比如还带某种模拟放大器(analog amplifier))。至此,声卡又多了两种元件:DAC元件,以及扬声器元件。DAC元件和扬声器也是管道联通的。


 
一个典型的声卡应该还有其他的一些元件。例如,它或许有内置的某种声音模块(比如合成器)用于播放MIDI数据。这个元件的音频输出和DAC的输出一样,会被管道输出到扬声器。我们的数据流图现在看上去是这样:  

 

 

 
另外,一个典型的声卡内部有一个连接器挂接到电脑光驱的音频输出上(这样一张音频CD在光驱中播放的话会通过扬声器发声)。这个元件也是管道输出到扬声器的,如同合成器还有DAC元件一样。现在,我们的数据流图是这样:




 最后,我们假定声卡拥有一个线路输入(LineIn)的元件,以使从外部的磁带机和乐器或者外部硬件混音器输出的音频信号能被挂接到这个接口上并被数字化。这个元件管道输出到ADC元件,同麦克风输入一样。至此,我们完成了拥有7个元件的声卡图(5个信号流,箭头表示它们之间的连接):





一般来说,以上的每个元件都有它自己独特的参数设置。例如,合成器通常有自己的音量。外部的CD音频也有它自己的音量。于是,如果用户同时播放一直音乐CD,播放一个MIDI文件,回放一个WAVE文件,它们共同输出到扬声器元件,那么他可以在这三个元件的音量之间做调节,并且,扬声器本身也有它自己的音量调节,这个主音量会影响其他三个元件管道输出到扬声器的混合音频。同样,线路输入和麦克风输入通常有独立的音量,所以它们在同时通过各自的接口进行录音的时候能进行调节。ADC元件也可能会有某种主音量控制器来影响这两个元件的管道输入。一个给定的元件可能会有其他的独立可调整参数。例如,以上每个元件都有它自己的静音开关,以便于快速地开关各个元件的声音。
混音器设备每块给定的声卡有和它关联的混音器设备。声卡上所有的不同种类的元件都会通过该声卡的混音器设备来进行操控。Windows的混音器API用于访问混音器设备。混音器API有函数可以取得指定声卡上所有元件的列表,并且调节它们各自的参数。这个是新加入到Win95、Win98和WINNT的API,不过也可以通过一个提供给Windows3.1的扩展用到其他的老版本操作系统上。注意:声卡驱动需要额外的支持才能和混音器API很好的兼容。所以不是所有的Win95和WinNT驱动都能得到支持。Win3.1的驱动就是典型的不支持的例子。
在任何电脑上,都可以安装超过一张的声卡。你也许已经发现,windows在系统中维护了一份所有WAVE和MIDI输入输出设备的列表。由于每张声卡都对应它自己的混音器设备(只要其驱动支持),所以windows也在系统中维护了一份已安装的混音器设备的列表。例如,如果你在一个系统上安装了两张声卡,那么也会有两个混音器设备被安装(假设每张声卡的驱动都支持混音器API的话)。如同WAVE和MIDI输入输出设备,windows给每个混音器设备也分配了一个数字ID。所以,ID为0的混音器设备是系统的第一个混音器(默认是)。如果有第二张声卡,那么就会有ID为1的混音器设备存在。类似windows的其他设备,为使用某个混音器,你首先必须得打开它(mixerOpen()),然后你可以调用混音器API来控制声卡的线路输入输出。做完这些之后,你必须关闭它(mixerClose())。
打开一个混音器设备你的程序如何选中某个混音器设备来进行操作呢?你可以有好几种不同的方法,取决于你想你的程序有多特别,以及扩展性有多好。如果你只想简单地打开一个默认的混音器,那么可以用mixerOpen()打开ID为0的混音器设备,如下:
unsigned long err;HMIXER        mixerHandle;err = mixerOpen(&mixerHandle, 0, 0, 0, 0);if (err){    printf("ERROR: Can't open Mixer Device! -- %08X\n", err);}else{}
当然,如果用户没有安装任何的混音器设备,那么上面的这个调用会返回一个错误。所以,任何时候都要记得检查返回值(混音器API返回的可能的错误号都列举在MMSYSTEM.H文件里。不幸的是,不想WAVE和MIDI的底层API那样,没有一个API能够把这些错误号翻译为直观的字符串)。那么,首选的混音器设备究竟是什么呢?那就是第一个被安装在系统上的——无论什么混音器设备。如果系统只有一块声卡,那么你可以很确信你有一个需要的混音器设备了。但是,假如你想尝试使用第二块声卡上的WAVE输出呢?你绝不会想用第一块声卡的混音器设备去控制第二块声卡的WAVE输出音量的(第一块声卡的混音器不可能控制第二块声卡的WAVE输出)。
那么,你怎么去打开想要的那个声卡的混音器呢?幸运的是,mixerOpen()允许你传递一个设备ID号给它,或者是与你感兴趣的声卡的其他设备关联的句柄给它。这样的话,mixerOpen()会确保它返回那块声卡上与该设备相关联的一个混音器设备。举个例子,下面展示你如何去打开默认的WAVE输出设备(默认声卡的WAVE输出),然后取得这个声卡的混音器设备句柄: unsigned long err;HMIXER        mixerHandle;WAVEFORMATEX  waveFormat;HWAVEOUT      hWaveOut; err = waveOutOpen(&hWaveOut, WAVE_MAPPER, &waveFormat, (DWORD)WaveOutProc, 0, CALLBACK_FUNCTION);if (err){    printf("ERROR: Can't open WAVE Out Device! -- %08X\n", err);}else{    err = mixerOpen(&mixerHandle, hWaveOut, 0, 0, MIXER_OBJECTF_HWAVEOUT);    if (err)    {        printf("ERROR: Can't open Mixer Device! -- %08X\n", err);    }} 上面的代码关键点在于,你不仅要传递用 waveOutOpen() (或者waveInOpen(),或者midiOutOpen(),或者midiInOpen()) 取得的句柄,而且mixerOpen()的最后一个参数必须是 MIXER_OBJECTF_HWAVEOUT (或者 MIXER_OBJECTF_HWAVEIN,或者 MIXER_OBJECTF_HMIDIOUT,或者MIXER_OBJECTF_HMIDIIN) ,以确保此种的设备句柄传递给了mixerOpen()。另外,如果你已经知道了想要操纵的 WAVE OUT的设备ID号(但是你没有用waveOutOpen()去打开它),你也可以把这个ID号传递给mixerOpen() ,而不必指定MIXER_OBJECTF_WAVEOUT。mixerOpen()会找到与该指定的ID相关联的WAVE输出设备对应的混音器。如果有必要,你可以用上面的混音器的句柄取得它的的混音器ID(数字),用mixerGetID()即可: unsigned long mixerID;err = mixerGetID(mixerHandle, &mixerID, MIXER_OBJECTF_HMIXER);if (err){    printf("ERROR: Can't get Mixer Device ID! -- %08X\n", err);}else{    printf("Mixer Device ID = %d\n", mixerID);}
列举所有的混音器设备如果你在写一个程序,需要列举系统中所有的混音器设备,那么windows有一个函数可以帮你检测混音器列表里有多少个混音器设备。这个函数是mixerGetNumDevs()。该函数返回系统中的混音器设备个数。记住设备ID是从0开始增长的。所以如果windows说列表里有3个设备,那么你应该知道设备ID分别是0,1,2。你现在可以在其他函数中使用这些设备ID了。例如,有个函数可以取得列表里某个设备的信息,它的名字就代表了它的功能。这些信息包括它有多少元件,每个元件分别是什么类型。你可以将你想去的信息的混音器的设备ID传给它(还需要一个特殊的结构体MIXERCAPS的指针 ,windows把关于此设备的信息都放在其中)。这个取得设备信息的函数名字叫做mixerGetDevCaps()。这里是一个取得混音器列表的例子,它打印出每个混音器的名字:MIXERCAPS     mixcaps;unsigned long iNumDevs, i;iNumDevs = mixerGetNumDevs();for (i = 0; i < iNumDevs; i++){    if (!mixerGetDevCaps(i, &mixcaps, sizeof(MIXERCAPS)))    {        printf("Device ID #%u: %s\r\n", i, mixcaps.szPname);    }}
关于线路和控制器在此之前,我用“元件”来指代一种有自己独立可调整的参数的硬件区域。实际上,混音器API本身操控的是“信号流”(在我们的块状图里,信号流就是连接几个元件的5个箭头)。微软的文档指出,每个信号流(即对应于图里的每个箭头)对应于一条源线路。因此,一个混音器设备控制着“源线路”——而不是元件。每条源线路——而不是元件有着它们自己独立可调整的参数。我们的样例声卡拥有一个混音器关注的5条源线路。此后,无论什么时候看到“源线路”,就想想在元件之间的信号流。之前,我也用到了“参数”一词来指代每条线路上可调整的设置。例如一个音量调节器或者一个静音开关,或者在设置面板上的重低音(bass boast)选项。微软的文档称这些参数为“控制器”(Control这是个容易让人感到困惑的词汇,一般程序员会将其想象成某图形界面的窗体上的一个控件。但是对于混音器来说,它的意思是“音频控制”)。举个例子,混音器API关注的不是真的“麦克风输入”元件,而是在麦克风输入和ADC元件之间的信号流。“麦克风输入”的音量控制器调整的是在麦克风输入和ADC元件之间的信号流。为了更进一步的说明元件和源线路之间的区别,让我们为我们的样例声卡添加一个新功能。有时候,你会想让麦克风输入不仅仅是管道输入到ADC(这样你能对其进行录音),而且还要管道输出到扬声器(这样你就可以监测出你实际在录制的是什么信号,而且可以通过扬声器听到它的声音)。所以,我们调整一下我们的区块图,可以看到,从麦克风输入出来的信号,送到了ADC,也送到了扬声器。





注意,我们现在有了6条源线路(箭头)。有两条源线路从麦克风输入出来,尽管它只是我们声卡上的单独一个元件。每条源线路都有它自己的设置。例如,麦克风输入到ADC的这条源线路有它的音量控制器(这样你可以设置你的WAVE录音软件的录音范围)。而且麦克风输入到扬声器输出的这条线路也有它的音量控制器(这样你可以独立于录音范围,设置你的麦克风的检测范围)。这些源线路也许每个都有它们自己的静音开关(这样你就可以让麦克风输入只送到ADC,而不送到扬声器)。它们当然也有其他的控制器,每条线路的控制器都独立于另外的线路存在。关于线路,还有另外一个重要的话题需要探讨。有种东西叫做“目标线路”,用以区分“源线路”。这些线路是什么呢?一条目标线路指的是有其他源线路指向它的线路。在我们的区块图里,扬声器输出就是一条目标线路,它有来自于内部CD音频,合成器,DAC WAVE输出和麦克风输入的信号来源指向它,而后面4个就是源线路。源线路总是会指向某个目标线路(即,不可能存在不关联于任何一条目标线路的源线路)。所以,鉴于源线路指的是我们区块图中的箭头,那么目标线路其实指的就是我们图中实际的那些元件。因此,ADC WAVE输入是我们样例声卡的一条目标线路。它有两条源线路指向它——麦克风输入和线路输入。这两条线路不是扬声器输出的源线路,因为它们并未指向它。类似地,扬声器输出的4条源线路不是ADC WAVE输入的源线路。同于源线路,目标线路也有自己的控制器。每个目标线路的控制器也和其他线路的控制器是独立的。例如,扬声器输出可能有一个音量调节器,这可以成为它的4条源线路的主音量调节器(这4条源线路也有自己的音量控制器)。注意:尽管一块声卡可能有立体声元件(例如,对大多数声卡来说扬声器输出通常就是一个立体声元件),这也被看做是一条线路。它有两个声道,然而它不过是混音器的一条线路。甚至,一个元件可能有着多余两个的声道,但是仍然被当作是一条线路。总体来说,线路总是不同于声道的存在。某些情况下,可能把线路当作一条MIDI连线会比较有用。一条MIDI连线连接两个元件,但是这个连线内部可能有多个声道贯穿其中。这和我们说的“线路”是一样的。总之,我们的样例声卡有6条源线路和两条目标线路。4条源线路分别标记为内部CD音频,合成器,DAC WAVE输出,和麦克风输入,它们连接到扬声器输出这个目标线路上。另外两条标记为麦克风输入,线路输入的源线路连接到ADC WAVE输入这个目标线路上。
线路ID和类型每条线路必须有一个唯一的ID数字。每个线路也具有一个类型。这只是一个描述具体它是哪种类型的数值。它们定义在MMSYSTEM.H里面。例如,一个MIXERLINE_COMPONENTTYPE_SRC_SYNTHESIZER类型的线路指的是它是来自内置声音模块的信号。源线路的所有可能的类型如下:
1)MIXERLINE_COMPONENTTYPE_SRC_DIGITAL 数字信号源,例如SPDIF输入接口.
2)MIXERLINE_COMPONENTTYPE_SRC_LINE       线路输入源。如果存在独立的麦克风输入的话,通常用于线路输入接口(即MIXERLINE_COMPONENTTYPE_SRC_MICROPHONE)
3)MIXERLINE_COMPONENTTYPE_SRC_MICROPHONE 麦克风输入(但如果不存在独立的线路输入源的话,经常和和Mic/Line输入联合起来使用)
4)MIXERLINE_COMPONENTTYPE_SRC_SYNTHESIZER 音乐合成器。通常用于带有一个能播放MIDI的合成器的声卡。这将是内置合成器的音频输出。
5)MIXERLINE_COMPONENTTYPE_SRC_COMPACTDISC 此音频信号来自内部CDROM光驱(连接至声卡)
6)MIXERLINE_COMPONENTTYPE_SRC_TELEPHONE 一般用于通过电脑的扬声器被管道输入进来的电话线路的输入音频,或者内置modem的电话线路接口。
7)MIXERLINE_COMPONENTTYPE_SRC_PCSPEAKER 通常,为了发声,音频是送到电脑的内置扬声器,而不是被送到声卡的扬声器输出。为了达到这样的目的,主板的系统扬声器连接器会被内部连接到声卡的某个连接器上。
8)MIXERLINE_COMPONENTTYPE_SRC_WAVEOUT WAVE重放(此即声卡的DAC)。
9)MIXERLINE_COMPONENTTYPE_SRC_AUXILIARY AUX接口意味着音频会被送到扬声器输出,或者ADC(如果是WAVE录音的话)。一般这被用来连接外部的模拟信号设备(如磁带机,乐器音频输出等)用来做音频数字化或者通过声卡作回放。
10)MIXERLINE_COMPONENTTYPE_SRC_ANALOG 同MIXERLINE_COMPONENTTYPE_SRC_AUXILIARY用途近似(尽管我曾见过一些混音器使用MIXERLINE_COMPONENTTYPE_SRC_PCSPEAKER)。一般这会是声卡上的某种只能被内部访问的模拟信号连接器,用来内部连接一些电脑内部的模拟信号元件,以通过扬声器发声。
11)MIXERLINE_COMPONENTTYPE_SRC_UNDEFINED 未定义的源类型。以上所列都不合适。

目标线路的所有可能的类型如下:
1)MIXERLINE_COMPONENTTYPE_DST_DIGITAL 数字信号目标,例如SPDIF输出接口。
2)MIXERLINE_COMPONENTTYPE_DST_LINE 线路输出目标。如果存在独立的扬声器输出的话,通常用于线路输出接口(即MIXERLINE_COMPONENTTYPE_DST_SPEAKERS)。
3)MIXERLINE_COMPONENTTYPE_DST_MONITOR 通常一个监视器输出接口用来作为独立于主要扬声器输出之外的扬声器系统。或者,也可能是声卡上本身的一些内置监视器扬声器,如内置modem上的扬声器。
4)MIXERLINE_COMPONENTTYPE_DST_SPEAKERS 输出到一对扬声器(即扬声器输出接口)的音频。
5)MIXERLINE_COMPONENTTYPE_DST_HEADPHONES 通常是头戴式耳机的输出接口。
6)MIXERLINE_COMPONENTTYPE_DST_TELEPHONE 通常用作串行连接(daisy-chain)电话到模拟调制解调器(analog modem)的电话输出接口上。
7)MIXERLINE_COMPONENTTYPE_DST_WAVEIN 声卡的ADC(用来数字化模拟信号源,例如录制WAVE文件)
8)MIXERLINE_COMPONENTTYPE_DST_VOICEIN 或许是某种用来做声音识别的硬件。通常麦克风源线路会被连接到这上面。
9)MIXERLINE_COMPONENTTYPE_DST_UNDEFINED 未定义的目标类型。以上所列都不合适。

控制器ID和类型每条线路可以有一个或者多个可调节的音频控制器(也可能没有任何一个控制器)。例如,合成器线路会有一个音量调节器和一个静音开关。每个控制器都有一个类型。它们定义在MMSYSTEM.H里面。例如,音量减淡器具有类型MIXERCONTROL_CONTROLTYPE_VOLUME。静音开关具有类型MIXERCONTROL_CONTROLTYPE_MUTE。每个控制器都有个唯一的ID。没有具有同一个ID号的两个控制器存在,即使它们分属不同线路。控制器类型划分为一些类。这些类粗略地表示了一个控制器调节的是什么类型的值,也表示了通常情况下你作为一个终端用户去调节这个值的图形界面种类。例如,通常你会提供一个图形化的音量调节器来允许你的用户调节MIXERCONTROL_CONTROLTYPE_VOLUME类型的控制器。另一方面,你通常会用一个带选中标记的按钮来让用户调节MIXERCONTROL_CONTROLTYPE_MUTE 控制器(因为这种控制器只有两个可能的值)。
控制器允许的类型如下所示:1)MIXERCONTROL_CT_CLASS_FADER 这是通过竖向的调节器(vertical fader)来调节的控制器,它带线性的正值刻度(0是最小的可选值)。MIXERCONTROLDETAILS_UNSIGNED 结构体用来获取或者设置该控制器的值。2)MIXERCONTROL_CT_CLASS_LIST 这是通过提供多个值供选择的列表框来调节的控制器。用户可以单选,或者多选其中的值。MIXERCONTROLDETAILS_BOOLEAN 结构体用来获取或者设置该控制器的值。也可以用MIXERCONTROLDETAILS_LISTTEXT 结构体来获取该控制器的值的每个元素的描述文本。3)MIXERCONTROL_CT_CLASS_METER 这是通过图形化仪表(graphical meter)来调节的控制器。使用MIXERCONTROLDETAILS_BOOLEAN,或者MIXERCONTROLDETAILS_SIGNED,或者MIXERCONTROLDETAILS_UNSIGNED 结构体来获取或者设置该控制器的值。4)MIXERCONTROL_CT_CLASS_NUMBER 这是通过数字输入框( numeric entry)来调节的控制器。用户可以输入有符号整数,无符号整数,或者普通整数的分贝值。5)MIXERCONTROL_CT_CLASS_SLIDER 这是通过水平滑动的推子(fader)来调节的控制器,它带线性的正负值刻度(通常0是中间点或者中性值)。MIXERCONTROLDETAILS_SIGNED 结构体用来获取或者设置该控制器的值。6)MIXERCONTROL_CT_CLASS_SWITCH 这是只有两个状态(值)可供选择的控制器,所以通过一个按钮来调节。MIXERCONTROLDETAILS_BOOLEAN 结构体用来获取或者设置该控制器的值。7)MIXERCONTROL_CT_CLASS_TIME 这是允许用户输入一个时间值的控制器。例如混响衰减时间( Reverb Decay Time)。控制器的值是正整数。8)MIXERCONTROL_CT_CLASS_CUSTOM 用户定义的控制器类。上面都不适合的时候使用。
每个类都关联于某些对应的类型。例如,MIXERCONTROL_CT_CLASS_FADER类关联有下面5个类型:1)MIXERCONTROL_CONTROLTYPE_VOLUME 音量调节器(Volume fader)。取值范围0-65535。2)MIXERCONTROL_CONTROLTYPE_BASS 低音增强调节器(Bass boost fader)。取值范围0-65535。3)MIXERCONTROL_CONTROLTYPE_TREBLE 高音增强调节器(Treble boost fader)。取值范围0-65535。4)MIXERCONTROL_CONTROLTYPE_EQUALIZER图形化均衡器(Graphic EQ)。每个栏(band )取值范围0-65535。MIXERCONTROLDETAILS_LISTTEXT结构体用于查询据衡器的每个通道的文本标签。通常,该控制器也有5个MIXERCONTROL_CONTROLF_MULTIPLE 比特位标记,因为均衡器可能有多个通道。5)MIXERCONTROL_CONTROLTYPE_FADER 一般调机器(Generic fader)。以上都不合适的时候使用。取值范围0-65535。
事实上,如果你去看MMSYSTEM.H,你会注意到MIXERCONTROL_CONTROLTYPE_VOLUME类型的控制器是被定义为MIXERCONTROL_CT_CLASS_FADER | MIXERCONTROL_CT_UNITS_UNSIGNED + 1。这个类其实包含在类型数字的高4比特上。所以如果你知道了一个控制器类型,你可以去掉它的高4比特的值来得到它对应的类。 例如,假如你已经查询到一个控制器的类型,然后将它存放在了混音器API返回给你的名叫type的变量里。下面演示你如何来找到它对应的类类型:
unsigned long   type; switch (MIXERCONTROL_CT_CLASS_MASK & type){    case MIXERCONTROL_CT_CLASS_FADER:    {        printf("It's a fader class.");        break;    }    case MIXERCONTROL_CT_CLASS_LIST:    {        printf("It's a list class.");        break;    }    case MIXERCONTROL_CT_CLASS_METER:    {        printf("It's a meter class.");        break;    }    case MIXERCONTROL_CT_CLASS_NUMBER:    {        printf("It's a number class.");        break;    }    case MIXERCONTROL_CT_CLASS_SLIDER:    {        printf("It's a slider class.");        break;    }    case MIXERCONTROL_CT_CLASS_TIME:    {        printf("It's a time class.");        break;    }    case MIXERCONTROL_CT_CLASS_CUSTOM:    {        printf("It's a custom class.");        break;    }} MIXERCONTROL_CT_CLASS_SWITCH类有7种关联类型:1)MIXERCONTROL_CONTROLTYPE_BOOLEAN 值为bool型的控制器。值是整数,要么为0(FALSE),要么非0(TRUE)。2)MIXERCONTROL_CONTROLTYPE_BUTTON 按钮被按下的时候值为1(某功能或者动作被启用),没被按下的时候值为0(不采取行动)的控制器。例如,这个类型的控制器可以被用来作为对讲按钮或者延音踏板(pedal sustain)——仅当按钮按下的时候行动/功能才是打开的,相反则是禁用的。3)MIXERCONTROL_CONTROLTYPE_LOUDNESS 值为1的时候打开超重低音(增加低音频率boost bass frequencies),为0时候是正常状态(不增强低音)。在该控制器打开超重低音的时候,MIXERCONTROL_CONTROLTYPE_BASS 增减益控制器可以用来设置具体的增强值。4)MIXERCONTROL_CONTROLTYPE_MONO 值为1的时候用来进行单声道操作(所有声道被合并为一个),为0时候是正常状态(立体声或者多声道)。5)MIXERCONTROL_CONTROLTYPE_MUTE 值为1的时候用来对某功能静音,为0时候是正常状态(不静音)。6)MIXERCONTROL_CONTROLTYPE_ONOFF 值为1的时候启用某功能或行为,为0时候禁用此功能或行为。和MIXERCONTROL_CONTROLTYPE_BUTTON不同的是,后者的0值不会禁用此功能或行为本身,而只是简单的表现出此功能或行为不应用的状态。MIXERCONTROL_CONTROLTYPE_ONOFF和真实的开关更类似(按windows的说法就是带选中标记的按钮),然而MIXERCONTROL_CONTROLTYPE_BUTTON 更类似于一个暂时的开关(按压式按钮)。MIXERCONTROL_CONTROLTYPE_ONOFF 和MIXERCONTROL_CONTROLTYPE_BOOLEAN 的不同仅仅是在用户界面表现上的标记或者图像不同。值为1时前者表示为ON而后者表示为TRUE。所以通常,这两个按钮在图形界面上展示为不同的标签,以此反应语义上的不同。7)MIXERCONTROL_CONTROLTYPE_STEREOENH 值为1的时候启用立体声增强功能(增强立体声的分割),为0时候是正常状态(不增强)。
MIXERCONTROL_CT_CLASS_LIST类有4种关联类型:1)MIXERCONTROL_CONTROLTYPE_SINGLESELECT 允许从众多的可选项里面选择一个值。例如,可以用来从很多类型(Hall,Plate,Room等)里面选择一个混响类型。2)MIXERCONTROL_CONTROLTYPE_MULTIPLESELECT 类似MIXERCONTROL_CONTROLTYPE_SINGLESELEC,但是允许同时选中多个的值。 3)MIXERCONTROL_CONTROLTYPE_MUX 允许从一些音频线路中选择一条音频线路。例如,为了让一条源线路被连接到多个可能的目标线路中的一条——该控制器应该列出所有可能的目标线路,允许其中一条被选中。4)MIXERCONTROL_CONTROLTYPE_MIXER类似MIXERCONTROL_CONTROLTYPE_MUX,但是允许同时选中多于一条的线路。例如,这个控制器可以用于一个混响元件,允许一些源线路同时连接到它,该控制器决定哪些源线路被选中用于连接到混响元件。
MIXERCONTROL_CT_CLASS_METER类有4种关联类型:1)MIXERCONTROL_CONTROLTYPE_BOOLEANMETER 表计,整数值为0(FALSE)或者非0(TRUE)。采用 MIXERCONTROLDETAILS_BOOLEAN 结构体获取或者设置此值。2)MIXERCONTROL_CONTROLTYPE_PEAKMETER 一个整数值的控制器,最大范围从-32,768(最低)到32,767(最高)。也就是说,该值为SHORT类型。MIXERCONTROLDETAILS_SIGNED 结构体用来获取或设置它的值。3)MIXERCONTROL_CONTROLTYPE_SIGNEDMETER 一个整数值的控制器,最大范围从-2,147,483,648(最低)到2,147,483,647(最高)(包括)。也就是说,该值为ULONG类型。MIXERCONTROLDETAILS_SIGNED 结构体用来获取或设置它的值。4)MIXERCONTROL_CONTROLTYPE_UNSIGNEDMETER 类似MIXERCONTROL_CONTROLTYPE_SIGNEDMETER。但最大范围从0(最低)到4,294,967,295。也就是说,该值为ULONG类型。MIXERCONTROLDETAILS_UNSIGNED 结构体用来获取或设置它的值。其他MIXERCONTROL_CT_CLASS_NUMBER 类允许的类型类似于MIXERCONTROL_CT_CLASS_METER类。但是对于MIXERCONTROL_CT_CLASS_NUMBER类,你通常会用一个Edit控件放置在图形界面上以供用户输入某个值。而对于 MIXERCONTROL_CT_CLASS_METER 类,通常会用某种近似于音频表计的图形来做展示。
MIXERCONTROL_CT_CLASS_NUMBER类有4种关联类型:1)MIXERCONTROL_CONTROLTYPE_SIGNED 一个整数值的控制器,最大范围从-2,147,483,648(最低)到2,147,483,647(最高)(包括)。也就是说,该值为LONG类型。MIXERCONTROLDETAILS_SIGNED 结构体用来获取或设置它的值。2)MIXERCONTROL_CONTROLTYPE_UNSIGNED 类似MIXERCONTROL_CONTROLTYPE_SIGNEDMETER。但最大范围从0(最低)到4,294,967,295。也就是说,该值为ULONG类型。MIXERCONTROLDETAILS_UNSIGNED 结构体用来获取或设置它的值。3)MIXERCONTROL_CONTROLTYPE_PERCENT 该控制器的整数值代表百分数。MIXERCONTROLDETAILS_UNSIGNED 结构体用来获取或设置它的值。4)MIXERCONTROL_CONTROLTYPE_DECIBELS 一个整数值的控制器,最大范围从-32,768(最低)到32,767(最高)。也就是说,该值为SHORT类型。增长步长是10分贝的倍数。MIXERCONTROLDETAILS_SIGNED 结构体用来获取或设置它的值。
MIXERCONTROL_CT_CLASS_SLIDER类有3种关联类型:1)MIXERCONTROL_CONTROLTYPE_SLIDER 一个带整数值的滑动条,最大范围从-32,768(最低)到32,767(最高)。也就是说,该值为SHORT类型。2)MIXERCONTROL_CONTROLTYPE_PAN 一个带整数值的滑动条,最大范围从-32,768(最左边)到32,767(最右边)。也就是说,该值为SHORT类型。代表立体声频谱中的左右声道均衡(pan)位置,0为最中间的值。3)MIXERCONTROL_CONTROLTYPE_QSOUNDPAN一个带整数值的滑动条,最大范围从-15(最低)到15(最高)。也就是说,该值为SHORT类型。代表Qsound的扩展声音设置。
MIXERCONTROL_CT_CLASS_TIME类有两种关联类型:1)MIXERCONTROL_CONTROLTYPE_MICROTIME 一个带整数值的控制器,最大范围从0(最低)到4,294,967,295。也就是说,该值为ULONG类型。代表微秒数的时间。2)MIXERCONTROL_CONTROLTYPE_MILLITIME 一个带整数值的控制器,最大范围从0(最低)到4,294,967,295。也就是说,该值为ULONG类型。代表毫秒数的时间。MIXERCONTROL_CT_CLASS_CUSTOM类是一个专属类。一个使用这个类型的控制器的混音器只期望一个专门为此混音器编写的程序会理解这个类里面究竟是什么类型的控制器,以及该用什么样的结构体去获取或者是设置该控制器的值(或许也只有专属的结构体可以拿来使用)。
MIXERLINE结构体,以及枚举所有线路一个取得混音器线路信息的方法是,如果你不知道它具体有哪些线路(即你不知道线路的具体类型或者它们的ID),应该先调用mixerGetDevCaps()来将混音器设备的信息取到一个MIXERCAPS结构体中。通过这个结构体中的信息,你能得知声卡上有几个目标线路。这样,你可以枚举每条目标线路(即取得信息),以及和它们关联的各自的源线路。在枚举完线路之后,你可以枚举每条线路上的控制器。让我们来验证这样的做法,并学习和混音器API相关的这些结构体。为了更好的理解混音器API,我们应该对我们样例声卡的内部进行一个概览,查看我们使用混音器API时候的内部结构体。我们假设混音器设备是用C语言写成,这里使用C的结构体进行描述。就像之前提到的那样,混音器API mixerGetDevCaps()用来取得一个混音器设备的信息。信息被填充到MIXERCAPS 结构体中。特别地,cDestinations 字段可以告诉你声卡上有多少条目标线路。它不会告诉你总的有多少条线路(即目标线路加上源线路),它只计算目标线路。我们的样例声卡只有两条目标线路。由于是例子,我们用任意值来填充其他字段,例如混音器名字和产品ID。假设这个混音器是系统安装的第一个混音器(即ID为0)。那么这就是我们的混音器设备的MIXERCAPS结构体(定义在MMSYSTEM.H内): MIXERCAPS mixercaps = {    0,          0,          0x0100,     "Example Sound Card",    0,          2,      }; 下面是个将MIXERCAPS传递给mixerGetDevCaps()以使windows帮助我们填充以上所有字段的例子(假设我们已经打开了混音器并且已经将其句柄存放到变量mixerHandle中): MIXERCAPS     mixcaps;MMRESULT      err; if (!(err = mixerGetDevCaps((UINT)mixerHandle, &mixcaps, sizeof(MIXERCAPS)))){}else{    printf("Error #%d calling mixerGetDevCaps()\n", err);} 一条线路的信息被存放在MIXERLINE结构体(也定义在MMSYSTEM.H)内。我们假设我们的扬声器输出目标线路有两个控制器:一个滑动音量调节器(用来控制输出到扬声器的混音音量)和一个静音开关(用来对所有混音静音)。下面是用于扬声器输出目标线路的MIXERLINE结构体的例子:  MIXERLINE mixerline_SpkrOut = {    sizeof(MIXERLINE),                 0,                                 0,                                 0xFFFF0000,                        MIXERLINE_LINEF_ACTIVE,            0,                                 MIXERLINE_COMPONENTTYPE_DST_SPEAKERS,     2,                                 4,                                 2,                                 "Spkr Out",                        "Speaker Out",                     MIXERLINE_TARGETTYPE_WAVEOUT,         0,          0,          0,          0x0100,     "Example Sound Card",}; 这里有一些需要注意的地方。首先,扬声器输出目标线路的dwComponentType是目标线路类型——MIXERLINE_COMPONENTTYPE_DST_SPEAKERS。这个类型值恰当的表明这是一个扬声器输出。我选择了一个值0xFFFF000赋给dwLineID字段。混音器设备开发者可以为其赋予任何想要的值给这个字段,但是混音器内其它任何线路的dwLineID字段的值不能和此值相同(接下来你就会注意到这点)。同样需要注意的是,既然我们的扬声器输出是立体声的,因此cChannels的值就是2。你应该还记得,有4个源线路关联到我们的扬声器输出目标线路,因此cConnections字段的值是4。Name字段是以null结尾的字符串,它可以是开发者选择的任意值,但短一些的名字意味着它可以用来作为图形界面控件上的狭窄空间的标签显示。当fdwLine字段中设置有MIXERLINE_LINE_ACTIVE标志,这意味着此线路没有被禁用(就好像静音的时候会发生的事)。dwDestination字段是一个从0开始的索引值,混音器中的第一个目标线路的索引值为0(如上面示例)。第二个目标线路的索引值是1,第三个目标线路的索引值为2,以此类推。这和windows枚举混音器设备的概念一样(即第一个安装的混音器的ID将为0)。索引值和线路的ID并不一定相同(你可以从上面的示例中看出),那么为什么使用索引值呢?为什么要同时使用索引值和ID号呢?或许你可以猜到,索引值主要在你需要枚举一个混音器有哪些线路时使用。直到你枚举了所有的线路(即获取每个线路的信息)之后,你才能知道每个线路的ID。因此,当你需要使用混音器API去枚举线路的时候会用到这些索引值。但是一旦你已经取得了某个线路的信息,就知道了它的ID,然后你就可以通过ID更直接的更改它的设置。因此,索引值在起初枚举线路和控制器以获取其ID及类型时很有用。在随后的操纵线路和控制的过程中ID就起主要作用。

现在我们来看一下目标线路ADC WAVE输入的MIXERLINE结构。我们假设它有两个控制器——一个滑动音量调节器和一个静音开关。这是目标线路ADC WAVE 输入的MIXERLINE结构的一个例子:MIXERLINE mixerline_WaveIn = {    sizeof(MIXERLINE),                 1,                                 0,                                 0xFFFF0001,                        MIXERLINE_LINEF_ACTIVE,            0,                                 MIXERLINE_COMPONENTTYPE_DST_WAVEIN,     2,                                 2,                                 2,                                 "Wave In",                         "Wave Input",                      MIXERLINE_TARGETTYPE_WAVEIN,          0, 0, 0, 0x0100, "Example Sound Card",}; 注意目标线路ADC WAVE输入的字段dwComponentType是MIXERLINE_COMPONENTTYPE_DST_WAVEIN类型,这表明它是一个波形输入。同样,我选择了值0xFFFF0001赋给dwLineID —— 不同于和扬声器输出目标线路的dwLineID的值。同时,注意我们的声卡还具有数字化到立体声的功能,因此cChannels字段值是2。你应该还记得,有2条源线路连接到我们的目标线路ADC WAVE输入,因此cConnections的值是2。最后,注意dwDestination字段的值是1,因为这是混音器中第二个目标线路。

混音器API mixerGetLineInfo()为指定的线路填充MIXERLINE结构。这就是你获取某个线路信息的方法。如果你不知道一条线路的ID(在你第一次枚举线路的情况下),那么你可以通过索引值来引用它。通过给mixerGetLineInfo()传递值MIXER_GETLINEINFOF_DESTINATION来表明你想通过线路的索引值来引用线路。例如,下面是如何获取我们的样例混音器中第一条目标线路的信息。在调用mixerGetLineInfo()和传递MIXERLINE结构体去填充之前,你必须初始化两个字段。cbStruct字段必须设置为你传递的MIXERLINE结构所占的字节数,dwDestination必须设置为你想获取信息的线路的索引值。记住第一条目标线路的索引值为0,因此如果要获取它的信息,我们将dwDestination设置为0。MIXERLINE     mixerline;MMRESULT      err; mixerline.cbStruct = sizeof(MIXERLINE);mixerline.dwDestination = 0;if ((err = mixerGetLineInfo((HMIXEROBJ)mixerHandle, &mixerline, MIXER_GETLINEINFOF_DESTINATION))){    printf("Error #%d calling mixerGetLineInfo()\n", err);} 当上面的调用返回后, mixerGetLineInfo() 会根据扬声器输出(mixerline_SpkrOut)的MIXERLINE结构体填充我们的MIXERLINE结构体(毕竟,扬声器输出是我们样例混音器的第一条线路,它索引值为0)。现在,如果你想取得混音器的第二条目标线路的信息,唯一不同的就是你设置给dwDestination字段的值是第二个目标线路的索引(1),如下:
mixerLine.cbStruct = sizeof(MIXERLINE);mixerLine.dwDestination = 1;if ((err = mixerGetLineInfo((HMIXEROBJ)mixerHandle, &mixerLine, MIXER_GETLINEINFOF_DESTINATION))){    printf("Error #%d calling mixerGetLineInfo()\n", err);} 当上面的调用返回后, mixerGetLineInfo() 会根据ADC WAVE输入(mixerline_WaveIn)的MIXERLINE结构体填充我们的MIXERLINE结构体(毕竟,ADC WAVE输入是我们样例混音器的第二个线路,它索引值为1)。现在,你应该明白了如何根据索引值去枚举目标线路。下面是个示例,它打印出混音器中所有目标线路的名字:
MIXERCAPS     mixcaps;MIXERLINE     mixerline;MMRESULT      err;unsigned long i; if (!(err = mixerGetDevCaps((UINT)mixerHandle, &mixcaps, sizeof(MIXERCAPS)))){    for (i = 0; i < mixercaps.cDestinations; i++)    {        mixerline.cbStruct = sizeof(MIXERLINE);        mixerline.dwDestination = i;        if (!(err = mixerGetLineInfo((HMIXEROBJ)mixerHandle, &mixerline, MIXER_GETLINEINFOF_DESTINATION)))        {            printf("Destination #%lu = %s\n", i, mixerline.szName);        }    }} 现在,我们需要枚举每条目标线路的所有源线路。我们也用索引值来引用每条源线路。对于一条给定的目标线路来说,第一条源线路索引值为0。该目标线路的第二条源线路索引值为1,第三条源线路索引值为2,以此类推。记住扬声器输出关联有4条源线路:内部CD音频,合成器,DAC WAVE输出,麦克风输入。所以它们各自的索引值为0,1,2,3。我们来看看这4条源线路的MIXERLINE 结构体。假设每条源线路有两个控制器,一个滑动音量调节器和一个静音开关。另外假设每条源线路都是一个立体声源。 MIXERLINE mixerline_CD = {    sizeof(MIXERLINE),    0,                                 0,                                 0x00000000,                        MIXERLINE_LINEF_ACTIVE|MIXERLINE_LINEF_SOURCE,    0,    MIXERLINE_COMPONENTTYPE_SRC_COMPACTDISC,     2,                                 0,                                 2,                                 "CD Audio",                        "Internal CD Audio",               MIXERLINE_TARGETTYPE_UNDEFINED,    0, 0, 0, 0x0100, "Example Sound Card",}; MIXERLINE mixerline_Synth = {    sizeof(MIXERLINE),    0,                                 1,                                 0x00000001,                        MIXERLINE_LINEF_ACTIVE|MIXERLINE_LINEF_SOURCE,    0,    MIXERLINE_COMPONENTTYPE_SRC_SYNTHESIZER,     2,                                 0,                                 2,                                 "Synth",                           "Synth",                           MIXERLINE_TARGETTYPE_UNDEFINED,    0, 0, 0, 0x0100, "Example Sound Card",}; MIXERLINE mixerline_WaveOut = {    sizeof(MIXERLINE),    0,                                 2,                                 0x00000002,                        MIXERLINE_LINEF_ACTIVE|MIXERLINE_LINEF_SOURCE,    0,    MIXERLINE_COMPONENTTYPE_SRC_WAVEOUT,     2,                                 0,                                 2,                                 "Wave Out",                        "DAC Wave Out",                    MIXERLINE_TARGETTYPE_WAVEOUT,      0, 0, 0, 0x0100, "Example Sound Card",}; MIXERLINE mixerline_Mic = {    sizeof(MIXERLINE),    0,                                 3,                                 0x00000003,                        MIXERLINE_LINEF_ACTIVE|MIXERLINE_LINEF_SOURCE,    0,    MIXERLINE_COMPONENTTYPE_SRC_MICROPHONE,     2,                                 0,                                 2,                                 "Mic",                             "Microphone Input",                MIXERLINE_TARGETTYPE_WAVEIN,       0, 0, 0, 0x0100, "Example Sound Card",}; 你必须注意的一件事是:不同于目标线路,源线路的MIXERLINE中有它们的MIXERLINE_LINE_SOURCE标志。当这个标志被设置时,你就知道你获取的是一条源线路的信息。其次,注意这个目标线路的索引值是0。这是因为以上所有源线路连接的目标线路扬声器输出是我们的混音器的第一条线路(因此它的索引值为0)。再次,注意以上4个源线路的索引值分别是0、1、2和3。最后,注意每个源线路的ID都是唯一的——不像包括任何目标线路的其它所有线路。混音器API mixerGetLineInfo()同样会为源线路填充MIXERLINE结构体。同样的,你可以通过索引值来引用源线路,但是你必须知道每个目标线路的索引值。通过传递MIXER_GETLINEINFOF_SOURCE 标志给mixerGetLineInfo()函数,告知它你要通过索引值来引用线路。例如,如下展示怎样获取我们的样例混音器中的第一条源线路(目标线路为扬声器输出)。在调用mixerGetLineInfo()和传递MIXERLINE结构体之前,你必须初始化3个字段:cbStruct字段的值必须设置为你传递的MIXERLINE结构所占的字节数,dwSource字段必须设置为你想获取信息的源线路的索引值,dwDestination字段必须设置为和这个源线路相连的目标线路的索引值。
MIXERLINE     mixerline;MMRESULT      err; mixerline.cbStruct = sizeof(MIXERLINE);mixerline.dwDestination = 0;mixerline.dwSource = 0;if ((err = mixerGetLineInfo((HMIXEROBJ)mixerHandle, &mixerline, MIXER_GETLINEINFOF_SOURCE))){    printf("Error #%d calling mixerGetLineInfo()\n", err);} 当上面的调用返回后, mixerGetLineInfo() 会根据内部CD音频(mixerline_CD)的MIXERLINE结构体填充我们的MIXERLINE结构体(毕竟,扬声器输出是我们样例混音器的第一条线路,它索引值为0)。现在根据示例,你可以从MIXERLINE结构体的dwLineID字段取得线路的ID。现在,如果你想取得扬声器输出目标线路的第二条源线路的信息,唯一不同的就是你设置给dwSource字段的值是第二个源线路的索引(1),如下: mixerline.cbStruct = sizeof(MIXERLINE);mixerline.dwDestination = 0;mixerline.dwSource = 1;if ((err = mixerGetLineInfo((HMIXEROBJ)mixerHandle, &mixerline, MIXER_GETLINEINFOF_SOURCE))){     printf("Error #%d calling mixerGetLineInfo()\n", err);}当上面的调用返回后, mixerGetLineInfo() 会根据合成器(mixerline_Synth)的MIXERLINE结构体填充我们的MIXERLINE结构体。现在,你应该明白了如何根据索引值去枚举(每条目标线路的)源线路。下面是个示例,它打印出混音器中所有目标线路以及它们各自的所有源线路的名字: MIXERCAPS     mixcaps;MIXERLINE     mixerline;MMRESULT      err;unsigned long i, n, numSrc; if (!(err = mixerGetDevCaps((UINT)mixerHandle, &mixcaps, sizeof(MIXERCAPS)))){      for (i = 0; i < mixercaps.cDestinations; i++)    {        mixerline.cbStruct = sizeof(MIXERLINE);        mixerline.dwDestination = i;        if (!(err = mixerGetLineInfo((HMIXEROBJ)mixerHandle, &mixerline, MIXER_GETLINEINFOF_DESTINATION)))        {            printf("Destination #%lu = %s\n", i, mixerline.szName);                       numSrc = mixerline.cConnections;            for (n = 0; n < numSrc; n++)            {                mixerline.cbStruct = sizeof(MIXERLINE);                mixerline.dwDestination = i;                mixerline.dwSource = n;                if (!(err = mixerGetLineInfo((HMIXEROBJ)mixerHandle, &mixerline, MIXER_GETLINEINFOF_SOURCE)))                {                    printf("\tSource #%lu = %s\n", i, mixerline.szName);                }            }        }    }} 通过线路ID取得信息一旦你知道了一条线路的ID(按照上面所示的方法枚举出线路,然后从MIXERLINE的dwLine字段取得),你就可以通过此ID来获取线路的信息(代替它的索引值)。如果你在处理一条源线路,你不需要知道其所连接的目标线路的索引值。你只需要初始化MIXERLINE结构体的dwLineID字段为想要的目标线路的ID值,然后在调用mixerGetLineInfo()的时候传递MIXER_GETLINEINFOF_LINEID参数,如下所示:mixerline.cbStruct = sizeof(MIXERLINE);mixerline.dwLineID = 0x00000003;if ((err = mixerGetLineInfo((HMIXEROBJ)mixerHandle, &mixerline, MIXER_GETLINEINFOF_LINEID))){    printf("Error #%d calling mixerGetLineInfo()\n", err);}
以上方法均适用于源线路和目标线路。只要你知道了一条线路的ID,你就可以根据它直接获取线路的信息,而不需要知道其索引。 通过类型取得线路信息通常,你不需要知道混音器中所有的线路。你的程序可能仅仅只和某个类型的线路打交道。比如,假设你正在编写一个MIDI文件播放器。现在,我们的样例声卡的某些元件对于你来说根本毫无用处。MIDI不是数字音频数据,因此DAC WAVE输入元件(以及所有连接到它的所有源线路)对你来说毫无意义。同样,目标线路扬声器输出的源线路内部CD音频、DAC WAVE输出和麦克风输入对你来说也毫无意义。我们的样例声卡上唯一一个可以处理MIDI数据回放的元件是连接扬声器输出的合成器元件。正是这个线路的控制器影响着MIDI数据的回放。所以,与其枚举混音器中的所有线路直到碰到一条类型为MIXERLINE_COMPONENTTYPE_SRC_SYNTHESIZER的线路,不如用mixerGetLineInfo()直接获取指定类型的线路信息。你只需要将MIXERLINE的dwComponentType字段指定为你想要的线路类型,然后在调用mixerGetLineInfo()的时候指定MIXER_GETLINEINFOF_COMPONENTTYPE即可,如下所示:mixerline.cbStruct = sizeof(MIXERLINE);mixerline.dwComponentType = MIXERLINE_COMPONENTTYPE_SRC_SYNTHESIZER;if ((err = mixerGetLineInfo((HMIXEROBJ)mixerHandle, &mixerline, MIXER_GETLINEINFOF_COMPONENTTYPE))){    printf("Error #%d calling mixerGetLineInfo()\n", err);} 系统将会用混音器中第一条类型为MIXERLINE_COMPONENTTYPE_SRC_SYNTHESIZER的线路的信息填充此MIXERLINE结构体。(如果混音器中没有此类型的线路,将会返回一个MIXERR_INVALLINE的错误)。一旦获取了此线路的信息,你就可以操纵它的控制器了。这样当你的需求是明确的时候,就省去了通过枚举得到某个线路的麻烦。MIXERCONTROL结构体,以及枚举控制器你可以通过混音器API mixerGetLineControls()来获取线路中控制器的信息。这个API将会将某个控制器的信息填入到一个MIXERCONTROL结构体中。我们首先来看一下MIXERCONTROL结构体。如前所述,我们的扬声器输出目标线路有两个控制器:一个滑动音量调节器和一个静音开关。每个控制器都有一个MIXERCONTROL结构体与其关连。我们首先看一下每个控制器的MIXERCONTROL结构:MIXERCONTROL mixerctl_Spkr_Vol = {    sizeof(MIXERCONTROL),               0x00000000,                         MIXERCONTROL_CONTROLTYPE_VOLUME,    MIXERCONTROL_CONTROLF_UNIFORM,      0,                                  "Volume",                           "Speaker Out Volume",               0,                                  65535,                              0, 0, 0, 0,                         31,                                 0, 0, 0, 0, 0,                  };MIXERCONTROL mixerctl_Spkr_Mute = {    sizeof(MIXERCONTROL),    0x00000001,                         MIXERCONTROL_CONTROLTYPE_MUTE,      MIXERCONTROL_CONTROLF_UNIFORM,    0,    "Mute",                             "Speaker Out Mute",                 0,                                  1,                                  0, 0, 0, 0,    0,                                  0, 0, 0, 0, 0,}; 上面有几个问题需要注意。首先,注意每个控制器都有一个唯一的ID,这些ID不必和线路的ID不同(比如控制器mixerctl_Spkr_Vol的ID恰巧和线路mixerline_CD的ID相同),但是每个控制器的ID都不能和其它控制器的ID相同,包括同其他线路的所有控制器(例如,线路扬声器输出的滑动音量调节器的ID不能和ADC WAVE输入线路的静音开关的ID相同)。我已设置了MIXERCONTROL_CONTROLF_UNIFORM标志,这个标志意味着,虽然扬声器输出是立体声的(有两个声道),但是并不是每个声道都有一个单独的音量控制器(这里左右声道没有各自独立的音量设置)。对于这两个声道而言,只有一个共同的音量设置,因此这两个声道总是被设置为相同的音量(稍候我们会学非均衡(not uniform)的控制器)。同样需要注意的是每个控制器都有一个适当的类型。滑动音量调节器的类型为MIXERCONTROL_CONTROLTYPE_VOLUME,静音开关控制器的类型为MIXERCONTROL_CONTROLTYPE_MUTE。MIXERCONTROL还会告诉你这个控制器可以设置的最大值和最小值。例如,滑动音量调节器可以设置0到65535之前的任何一个值。其中0是最小值(此时音量最小),65535是最大值(此时音量最大),这是不是意味着滑动音量调节器有65535个离散的等级呢?(可以被设置为从0至65535之间(包括0和65535)的任何值?)。不一定。你还得看一下等级数(step amount)这个字段。它会告诉你这个控制器有多少个有效的等级。在目前情况下,我们有31个有效的等级。这意味着第一个有效的设置值是0,但是第二个有效的设置值是65,535 - (65,535/31) ,第三个有效的设置值是65,535 - (65,535/(31*2)),以此类推。换句话说,我们只能设置0到65535之间的31个值(注意:dwMinimum和dwMaximum字段同lMinimum和lMaximum字段在一个联合体中声明。在处理unsigned类型的值时,我们会用到前面的一组值dwMinimum和dwMaximum,比如处理类型为MIXERCONTROLDETAILS_BOOLEAN或MIXERCONTROLDETAILS_UNSIGNED的控制器的值。在处理signed类型的值时,我们会用到后面一组值lMinimum和lMaximum,比如处理类型为MIXERCONTROLDETAILS_SIGNE的控制器的值)。
枚举控制器和枚举线路稍微有点不同。首先,你不必使用控制器的索引值。其次,你只有在知道某个控制器的ID的情况下才可以只取这个控制器的信息。否则,你必须同时获取给定线路的所有控制器的信息。显然,当你第一次枚举某条线路的控制器时,你不知道每个控制器的ID。因此,你必须通过一个mixerGetLineControls()调用获取所有线路的信息。这样的话,你必须给mixerGetLineControls()传递一个MIXERCONTROL结构的数组。对线路中每个控制器都必须有一个类型为MIXERCONTROL的结构。例如,我们知道我们的扬声器输出目标线路有两个控制器:一个滑动音量调节器和一个静音开关(记住MIXERLINE结构的cControls字段是2)。因此,如果要获取这两个控制器的信息,我们就必须给mixerGetLineControls传递包含两个MIXERCONTROL结构的数组。同时我们必须传递MIXER_GETLINECONTROLSF_ALL标志表明我们需要获取所有控制器的信息。为了告诉mixerGetLineControls()我们希望获取哪个线路的控制器的信息,我们还必须初始化并传递一个类型为MIXERLINECONTROLS的结构。同时我们提供一个指向我们MIXERCONTROL结构体数组的指针作为额外的结构体。下面是获取线路扬声器输出的所有控制器信息的示例:MIXERCONTROL       mixerControlArray[2];MIXERLINECONTROLS  mixerLineControls;MMRESULT           err;mixerLineControls.cbStruct = sizeof(MIXERLINECONTROLS); mixerLineControls.cControls = 2; mixerLineControls.dwLineID = 0xFFFF0000; mixerLineControls.pamxctrl = &mixerControlArray[0]; mixerLineControls.cbmxctrl = sizeof(MIXERCONTROL); if ((err = mixerGetLineControls((HMIXEROBJ)mixerHandle, &mixerLineControls, MIXER_GETLINECONTROLSF_ALL))){    printf("Error #%d calling mixerGetLineControls()\n", err);}当mixerGetLineControls()返回后,我们的mixerControlArray[]数组里就保存着已填充的信息。mixerControlArray[0]将会按照上面显示的mixerctl_Spkr_Vol控制器的内容填充。mixerControlArray[1]会按照上面显示的mixerctl_Spkr_Mute控制器的内容填充。现在,你可以通过每个MIXERCONTROL的dwControlID字段获得每个控制器的ID。如果你知道控制器的ID或类型的话,每次获取一个控制器的信息也是可以的。但是一次获取数目介于1(不包括1)和所有控制器总数(不包括总数)之间个控制器的信息是不行的。例如,假设我们的扬声器输出线路有5个控制器(而不是目前的2个),你不能仅仅获取前3个控制器。例如,你只能一次获取5个控制器中1个控制器的信息,或者一次获取着5个控制器的信息(要么一次一个,要么一次所有)。
通过控制器ID取得信息一旦你知道了一个控制器的ID(你可以使用上面所示的方法,首先枚举所有控制器,然后从MIXERCONTROL的dwControlID字段获取控制器的ID),你就可以通过这个ID获取这个控制器的信息,甚至不必知道这个控制器所属的线路的ID。你也不必同时获取这条线路所有控制器的信息。你仅仅只需要初始化MIXERCONTROL的dwControlID字段,然后在调用mixerGetLineControls()的时候指定MIXER_GETLINECONTROLSF_ONEBYID标志,如下:MIXERCONTROL       mixerControlArray;MIXERLINECONTROLS  mixerLineControls;MMRESULT           err;mixerLineControls.cbStruct = sizeof(MIXERLINECONTROLS);mixerLineControls.cControls = 1;mixerLineControls.dwControlID = 0x00000000;mixerLineControls.pamxctrl = &mixerControlArray;mixerLineControls.cbmxctrl = sizeof(MIXERCONTROL); if ((err = mixerGetLineControls((HMIXEROBJ)mixerHandle, &mixerLineControls, MIXER_GETLINECONTROLSF_ONEBYID))){    printf("Error #%d calling mixerGetLineControls()\n", err);} 通过类型取得控制器信息通常情况下,你不必知道某个线路中所有控制器的信息。在你的程序中你可能仅仅只和某个类型的控制器打交道。比如,假设你在写一个简单的MIDI文件回放程序,你提供给终端用户的只是音乐合成器的一个滑动音量调节器。前面我们已经知道怎样通过类型获取MIDI回放线路并获取此线路的信息,比如线路ID。你可以用这个线路ID来搜索此线路中某个特定类型的控制器,比如你可以寻找一个类型为MIXERCONTROL_CONTROLTYPE_VOLUME的控制器。所以,与其通过枚举线路中所有控制器直到你碰到一个类型为MIXERCONTROL_CONTROLTYPE_VOLUME的控制器,不如用mixerGetLineControls()直接通过类型获取控制器。你只需要将MIXERCONTROLS的字段dwControlType设定为你想要的类型,然后在调用mixerGetLineControls()的时候指定MIXER_GETLINECONTROLSF_ONEBYTYPE标志(假设你已经获取了合成器线路的ID,并存储在变量SynthID中):MIXERCONTROL       mixerControlArray;MIXERLINECONTROLS  mixerLineControls;MMRESULT           err;mixerLineControls.cbStruct = sizeof(MIXERLINECONTROLS);mixerLineControls.dwLineID = SynthID;mixerLineControls.cControls = 1;mixerLineControls.dwControlType = MIXERCONTROL_CONTROLTYPE_VOLUME;mixerLineControls.pamxctrl = &mixerControlArray;mixerLineControls.cbmxctrl = sizeof(MIXERCONTROL); if ((err = mixerGetLineControls((HMIXEROBJ)mixerHandle, &mixerLineControls, MIXER_GETLINECONTROLSF_ONEBYTYPE))){    printf("Error #%d calling mixerGetLineControls()\n", err);}
系统将会使用此线路中第一个拥有MIXERCONTROL_CONTROLTYPE_VOLUME类型的控制器的信息来填充MIXERCONTROL结构体。(如果此线路中没有此类型的控制器,将会返回一个错误值MIXERR_INVALCONTROL)。一旦你获取到了控制器的信息,就可以直接操作此控制器了。例如,你可以从MIXERCONTROL的字段dwControlID获取控制器的ID。这样,当你想要某个类型的控制器时就不用枚举所有的控制器了。获取和设置控制器的值现在,我们来讲混音器的最终目的:获取一个控制器的值(这样你就可以将其当前的值显示给终端用户),和设置一个控制器的值(这样可以让终端用户调整控制器的值)。想要获取或设置某个控制器的值,你必须知道控制器的ID。然后就可以用mixerGetControlDetails()获取控制器当前值,用mixerSetControlDetails()设置某个特定值。这些函数都使用一个类型为MIXERCONTROLDETAILS的结构体。你可以初始化其中的部分字段来告诉mixerGetControlDetails()/mixerSetControlDetails()你想设置/获取哪个控制器的值。你同时还得提供指向另外一个即将存放值的结构的指针。例如,我们获取扬声器输出线路的滑动音量调节器的当前值。到现在为止,我们已知道了怎样获取这个控制器的信息(例如控制器的ID)。为了获取控制器的值,我们需要提供一个特殊的结构体来存放返回值。我们需要使用什么样的结构?哦,这要看控制器的类型了。滑动音量调节器的类型为MIXERCONTROL_CONTROLTYPE_VOLUME,如果你回头看一下关于调节器(Fader)类和控制器的图表,就知道了这个值将使用一个类型为MIXERCONTROLDETAILS_UNSIGNED的结构。这个结构只有一个字段dwValue,用于存放返回值。因此我们提供一个MIXERCONTROLDETAILS_UNSIGNED类型的结构给mixerGetControlDetails()(通过MIXERCONTROLDETAILS结构),如下是一个样例程序,获取并打印扬声器输出线路中滑动音量调节器的当前值。MIXERCONTROLDETAILS_UNSIGNED value;MIXERCONTROLDETAILS          mixerControlDetails;MMRESULT                     err;mixerControlDetails.cbStruct = sizeof(MIXERCONTROLDETAILS);mixerControlDetails.dwControlID = 0x00000000;mixerControlDetails.cChannels = 1;mixerControlDetails.cMultipleItems = 0;mixerControlDetails.paDetails = &value;mixerControlDetails.cbDetails = sizeof(MIXERCONTROLDETAILS_UNSIGNED); if ((err = mixerGetControlDetails((HMIXEROBJ)mixerHandle, &mixerControlDetails, MIXER_GETCONTROLDETAILSF_VALUE))){    printf("Error #%d calling mixerGetControlDetails()\n", err);}else{    printf("It's value is %lu\n", value.dwValue);}若要设置一个控制器的值,你只需填充此结构,然后将其传递给mixerSetControlDetails()。你同时还要指定MIXER_SETCONTROLDETAILSF_VALUE结构。这里是一个样例程序,其将扬声器输出线路的滑动音量调节器的值设置为31。MIXERCONTROLDETAILS_UNSIGNED value;MIXERCONTROLDETAILS          mixerControlDetails;MMRESULT                     err;mixerControlDetails.cbStruct = sizeof(MIXERCONTROLDETAILS);mixerControlDetails.dwControlID = 0x00000000;mixerControlDetails.cChannels = 1;mixerControlDetails.cMultipleItems = 0;mixerControlDetails.paDetails = &value;mixerControlDetails.cbDetails = sizeof(MIXERCONTROLDETAILS_UNSIGNED);value.dwValue = 31; if ((err = mixerSetControlDetails((HMIXEROBJ)mixerHandle, &mixerControlDetails, MIXER_SETCONTROLDETAILSF_VALUE))){    printf("Error #%d calling mixerSetControlDetails()\n", err);} 多声道控制器前面已说过,当将设置了控制器的MIXERCONTROL_CONTROLF_UNIFORM标志时,所有声道都共享同一个值。例如,对于扬声器输出线路,它是立体声线路,但是其左右声道并没有独立的音量值。但是,若一个控制器的MIXERCONTROL_CONTROLF_UNIFORM值没有设置,并且此控制器有一个以上的声道,那么每个声道都有一个独立的值。这样,当你设置/获取某个控制器的值时,你必须提供多个特殊的结构来获取或设置所有声道的值。例如,假设扬声器输出线路的滑动音量调节器没有设置MIXERCONTROL_CONTROLF_UNIFORM标志,由于此线路有两个声道,所以我们必须提供两个MIXERCONTROLDETAILS_UNSIGNED结构来存放获取/设置其左右声道的值,我们需要使用一个类型为MIXERCONTROLDETAILS_UNSIGNED的数组。第一个MIXERCONTROLDETAILS_UNSIGNED结构将存放第一个声道(左)的值,第二个结构将存放第二个声道(右)的值。如下获取我们的扬声器输出线路的滑动音量调节器的左右声道的值的一个示例:MIXERCONTROLDETAILS_UNSIGNED value[2];MIXERCONTROLDETAILS          mixerControlDetails;MMRESULT                     err;mixerControlDetails.cbStruct = sizeof(MIXERCONTROLDETAILS);mixerControlDetails.dwControlID = 0x00000000;mixerControlDetails.cChannels = 2;mixerControlDetails.cMultipleItems = 0;mixerControlDetails.paDetails = &value[0];mixerControlDetails.cbDetails = sizeof(MIXERCONTROLDETAILS_UNSIGNED); if ((err = mixerGetControlDetails((HMIXEROBJ)mixerHandle, &mixerControlDetails, MIXER_GETCONTROLDETAILSF_VALUE))){    printf("Error #%d calling mixerGetControlDetails()\n", err);}else{    printf("The left channel's volume is %lu\n", value[0].dwValue);    printf("The right channel's volume is %lu\n", value[1].dwValue);}
为设置全部声道的值,你需要填充MIXERCONTROLDETAILS_UNSIGNED 结构体。下面是个示例,设置左声道音量为31,右声道音量为0. MIXERCONTROLDETAILS_UNSIGNED value[2];MIXERCONTROLDETAILS          mixerControlDetails;MMRESULT                     err;mixerControlDetails.cbStruct = sizeof(MIXERCONTROLDETAILS);mixerControlDetails.dwControlID = 0x00000000;mixerControlDetails.cChannels = 2;mixerControlDetails.cMultipleItems = 0;mixerControlDetails.paDetails = &value[0];mixerControlDetails.cbDetails = sizeof(MIXERCONTROLDETAILS_UNSIGNED); value[0].dwValue = 31;value[1].dwValue = 0; if ((err = mixerSetControlDetails((HMIXEROBJ)mixerHandle, &mixerControlDetails, MIXER_SETCONTROLDETAILSF_VALUE))){    printf("Error #%d calling mixerSetControlDetails()\n", err);} 当然,一个控制器或许有2个以上的声道。对一个给定的控制器,你必须提供足够大的结构来容纳所有的声道的值。因此,通常你必须根据需要来开辟数组空间。一次存取一个控制器中的某几个声道是非法的。例如,一个控制器有8个声道,但是你只取其前2个声道的值,这是不允许的。你必须同时存取一个控制器的所有声道才行。但是这里有一条有关设置一个值的规则:如果你仅仅设置第一个声道的值,那么mixerSetControlDetails()自动将控制器设置MIXERCONTROL_CONTROLF_UNIFORM标志。最终结果是此控制器的所有声道都被设置为这个值。因此,你可以通过仅仅设置第一个声道的值来达到将所有的声道设置为同一个值的目的。
多元素控制器你不会常常碰到多元素的控制器。一个多元素控制器就是每个声道关联着多个值的控制器。图形化均衡器是一个例子。让我们来看一个简单的,假设一个声卡内置有以下带着3个通道(band)的图形化均衡器:



这个控制器有3个值与其关联—与“低通道”关联的值、与“中通道”关联的值、与“高通道”关联的值(假设每个通道都能设置为不同的值,否则它就是一个无用的图形化均衡器了)。这样表现出来的就是一个多元素控制器,它有3个元素(值)与其关联。我们再进一步假设这个控制器属于扬声器输出线路。我们来看一下MIXERCONTROL结构体。一个多元素控制器的MIXERCONTROL的字段dwControlType的值会设置有MIXERCONTROL_CONTROLF_MULTIPLE位标志,同时MIXERCONTROL的cMultipleItems字段会告诉你每个声道有几个元素。 MIXERCONTROL mixerctl_EQ = {    sizeof(MIXERCONTROL),               0x00000002,                         MIXERCONTROL_CONTROLTYPE_EQUALIZER,    MIXERCONTROL_CONTROLF_UNIFORM|MIXERCONTROL_CONTROLF_MULTIPLE,      3,                                  "EQ",                               "Graphic Equalizer",                0,                                  65535,                              0, 0, 0, 0,                         31,                                 0, 0, 0, 0, 0,                  };
首先,注意控制器ID不同于此混音器中其它控制器的ID。同样要注意的是,这里还设置了MIXERCONTROL_CONTROLF_MULTIPLE位标志。cMultipleItems被设置为3,表明每个声道有3个元素(但是即使我已将此控制器设置了MIXERCONTROL_CONTROLF_UNIFORM标志,总共仍然只有3个值,虽然扬声器输出线路是立体声的。换句话说,每个通道的值相同地影响着所有声道)。为了获取3个通道的值,我们需要一个包含3个结构的数组。需要什么类型的结构呢?恩,类型为MIXERCONTROL_CONTROLTYPE_EQUALIZER的控制器的类是调节器类(Fader,即MIXERCONTROL_CT_CLASS_FADER),你会想起这个类(Class)的所有类型(Type)的值都使用MIXERCONTROLDETAILS_UNSIGNED类型的结构。如下演示怎样获取3个通道的值:MIXERCONTROLDETAILS_UNSIGNED value[3];MIXERCONTROLDETAILS          mixerControlDetails;MMRESULT                     err;mixerControlDetails.cbStruct = sizeof(MIXERCONTROLDETAILS);mixerControlDetails.dwControlID = 0x00000002;mixerControlDetails.cChannels = 1;mixerControlDetails.cMultipleItems = 3;mixerControlDetails.paDetails = &value[0];mixerControlDetails.cbDetails = sizeof(MIXERCONTROLDETAILS_UNSIGNED); if ((err = mixerGetControlDetails((HMIXEROBJ)mixerHandle, &mixerControlDetails, MIXER_GETCONTROLDETAILSF_VALUE))){    printf("Error #%d calling mixerGetControlDetails()\n", err);}else{    printf("The Low band is %lu\n", value[0].dwValue);    printf("The Mid band is %lu\n", value[1].dwValue);    printf("The High band is %lu\n", value[2].dwValue);}为设置这3个通道的值,你需要填充MIXERCONTROLDETAILS_UNSIGNED 结构体。下面是个示例,其设置低通道为31,中通道为0,高通道为62。 MIXERCONTROLDETAILS_UNSIGNED value[3];MIXERCONTROLDETAILS          mixerControlDetails;MMRESULT                     err;mixerControlDetails.cbStruct = sizeof(MIXERCONTROLDETAILS);mixerControlDetails.dwControlID = 0x00000002;mixerControlDetails.cChannels = 1;mixerControlDetails.cMultipleItems = 3;mixerControlDetails.paDetails = &value[0];mixerControlDetails.cbDetails = sizeof(MIXERCONTROLDETAILS_UNSIGNED); value[0].dwValue = 31;value[1].dwValue = 0;value[2].dwValue = 62; if ((err = mixerSetControlDetails((HMIXEROBJ)mixerHandle, &mixerControlDetails, MIXER_SETCONTROLDETAILSF_VALUE))){    printf("Error #%d calling mixerSetControlDetails()\n", err);} 现在我们将控制器的MIXERCONTROL_CONTROLF_UNIFORM标志去掉。因此,每个声道的每个元素都有其自己的值。因为扬声器输出有两个声道,那意味着我们的控制器需要总计为2 (声道) * 3 (元素)个结构,也就是6个值。我们的图形化均衡器如下图所示: 


Left ChannelRight Channel
左声道右声道我们需要6个类型为MIXERCONTROLDETAILS_UNSIGNED的结构来存放所有声道的所有元素的值。对了,在前面的样例中,我都将这些元素的标签设置过了。如果你希望将它们输出,你就应该向混音器查询这些值。为了达到目的,你必须提供一个类型为MIXERCONTROLDETAILS_LISTTEXT结构的数组,就如同你提供一组类型为MIXERCONTROLDETAILS_UNSIGNED的结构来获取所有声道的所有元素的值一样。 MIXERCONTROLDETAILS_UNSIGNED value[6];MIXERCONTROLDETAILS_LISTTEXT label[6];MIXERCONTROLDETAILS          mixerControlDetails;MMRESULT                     err;mixerControlDetails.cbStruct = sizeof(MIXERCONTROLDETAILS);mixerControlDetails.dwControlID = 0x00000002;mixerControlDetails.cChannels = 2;mixerControlDetails.cMultipleItems = 3;mixerControlDetails.paDetails = &value[0];mixerControlDetails.cbDetails = sizeof(MIXERCONTROLDETAILS_UNSIGNED); if ((err = mixerGetControlDetails((HMIXEROBJ)mixerHandle, &mixerControlDetails, MIXER_GETCONTROLDETAILSF_VALUE))){    printf("Error #%d calling mixerGetControlDetails()\n", err);}else{    unsigned long   i,n;    mixerControlDetails.cbStruct = sizeof(MIXERCONTROLDETAILS);    mixerControlDetails.dwControlID = 0x00000002;    mixerControlDetails.cChannels = 2;    mixerControlDetails.cMultipleItems = 3;    mixerControlDetails.paDetails = &label[0];    mixerControlDetails.cbDetails = sizeof(MIXERCONTROLDETAILS_LISTTEXT);       if ((err = mixerGetControlDetails((HMIXEROBJ)mixerHandle, &mixerControlDetails, MIXER_GETCONTROLDETAILSF_LISTTEXT)))    {        printf("Error #%d calling mixerGetControlDetails()\n", err);    }    else    {        for (i = 0; i < 2; i++)        {            printf("Channel %lu:\n", i+1);            for (n = 0; n < 3; n++)            {                printf("\tThe %s item is %lu\n", label[3 * i + n].szName, value[3 * i + n].dwValue);            }        }    }} 一次获取或设置某个控制器的几个元素的值是不支持的。例如,若一个控制器有8个元素,你尝试仅仅获取其前2个元素的信息是不允许的。你必须同时设置/获取所有的声道的所有元素的值。关于设置元素的值,这里有一个规则:如果你仅仅设置第一个声道的元素的值,那么mixerSetControlDetails()会自动将控制器设置MIXERCONTROL_CONTROLF_UNIFORM标记。最终结果就是所有声道的所有元素的值都和第一个声道的元素的值相同。因此,你可以通过只设置第一个声道的元素的值达到快速将所有的声道的元素的值设为相同值的目的。
更改通知在上面的样例程序中,我已展示了通过mixerOpen()函数打开混音器然后将其返回值用于其它混音器函数。不一定非得这样做。实际上,混音器API被设计为可以按以下方式使用:你可以传递混音器ID,而不必将打开的混音器句柄作为混音器函数的某个参数去传递。所以,你不必显式的打开一个混音器。但是如果你想在混音器上做某些操作,显式的打开混音器设备(通过mixerOpen()函数)还是会有一些好处的。
首先,这会防止混音器被卸载(大概是声卡的驱动所为)。其次,在你打开了一个混音器后,当此混音器的任何线路的状态发生改变时(比如线路被设置为静音),或者其中某个控制器的值改变时,你可以指示windows系统发送一个消息(到你创建的自定义的窗口处理例程)通知你。你不仅仅会在你改变某个线路的状态或某个控制器的值时收到这些消息,而且当其它程序打开混音器(多个程序可以同时打开一个混音器)并改变某个线路的状态或某个控制器的值时也会收到。因此,当其它程序对混音器做改变时,你可以使你的程序与混音器的状态保持同步。

当你调用mixerOpen()时,你应该将接受通知消息的窗口句柄作为第三个参数传递给此函数,并将CALLBACK_WINDOW指定为最后一个参数。
这里有2个特殊的“混音器消息”。
MM_MIXM_LINE_CHANGE:当混音器的任何一条线路的状态发生改变时,系统会发送此消息到你的窗口处理程序。MM_MIXM_CONTROL_CHANGE:当混音器中的任何一个控制器的值发生改变时,系统会发送此消息到你的窗口处理程序。

对于MM_MIXM_LINE_CHANGE,WPARAM参数表示发生改变的线路所属的混音器句柄,LPARAM参数表示此线路的ID。
对于MM_MIXM_CONTROL_CHANGE,WPARAM参数表示发生改变的线路所属的混音器句柄,LPARAM参数表示值发生改变的控制器的ID。总结混音器API是windows多媒体API中最复杂的一组。你可能需要一点时间来吸收这篇教程然后将它应用于你的程序中。但是混音器API使得你可以操作任何已安装的声卡,而不必对某个特定的声卡编写特定的程序。

想获取更多关于混音器结构和API的信息,请参考Microsoft Developer Network上关于音频混音器的文章(audio mixers) 。

微软提供了一个免费下载的关于如何使用混音器API的样例程序。但是我发现它的代码的注释太简单了,同时有很多和混音器API无关的且不必要的代码。我已将此样例程序精简,使它展示的都是如何使用混音器API的关键代码,并添加了很多注释。你可以下载我修改的版本Microsoft's Mixer Device Example,它将展示如何显示所有的混音器设备和它们的线路/控制器,以及调整它们控制器的值。这个工程是基于Visual C++4.0的,因为它是一个普通的用C语言编写的windows应用程序,因此任何windows的C语言编译器应该都可以编译它。
1.首先用mixerGetNumDevs()函数获取系统中的混音器设备的数量。一般,机器上都至少有一个混音器设备——声卡,如果机器上没有连接其它的音频设备,那么也就只有声卡这一个混音器设备。我的机器上接有一个名为USB EMP Audio Dedice的录音设备,所以该函数返回2,表示有两个混音器设备:Realtec AC97 Audio和USB EMP Audio Dedice。
UINT uNum = mixerGetNumDevs();

 

MIXER 混响器编程



fakine
2013年10月14日 17:17
2998
得到系统中一共有多少个混音器设备: (在一般的系统中只有一块声卡,所以返回的值应该为1;我的系统中装有一块PIC创新声卡,一块主板载C-media声卡,还安装了Totalrecorder软件,所以返...

Windows混音器API使用



thanklife
2014年03月12日 14:58
1402
1.首先用mixerGetNumDevs()函数获取系统中的混音器设备的数量。一般,机器上都至少有一个混音器设备——声卡,如果机器上没有连接其它的音频设备,那么也就只有声卡这一个混音器设备。我的机器上...

2018,UI设计师如何少走弯路快速提升设计能力!

少走弯路快速提升诀窍:

对声卡输出进行录音的设置.



axman
2007年08月02日 17:40
7230
一般正常的录音都是对Line in进行录音,但有些需要对line out进行录音,因为有些音源是没有line in的.如即时聊天时录取对方的语音.但这些声音都要经过声卡播放,所以它们的音源就是声卡输出...

混音器编程接口讨论



yi7900
2011年11月21日 17:19
2574
转自:http://www.cnblogs.com/windviki/archive/2011/08/31/2160683.html 混音器编程接口讨论 翻译:windviki@g...

声卡输入端子在DirectShow中的自动选择



happydeer
2005年03月15日 10:46
5123
在一些使用声卡进行音频采集的应用程序中,通常有如下这样的功能需求:程序启动的时候,要求自动为声卡选择某个特定的输入端子——比如一些通用的采集程序,希望将“Line In”作为默认输入;而一些视频会议软...

一秒创造无法计算的价值

每满2000返200,最高返5000元代金券

混音器编程接口讨论



wwqingyue
2013年10月24日 17:17
960
译自:MIXER API ARGUMENTATION 为了理解混音器API是如何工作的,必须先了解典型的声卡的硬件布局。 我们有必要想象一下,声卡上有各种独立的,清楚的,但是却又互相连接的元件...

Linux的混音设备/dev/mixer



b02330224
2012年09月23日 14:29
1731
Linux的音频输入输出是通过/dev/dsp设备的,但对于这些声音信号的处理则是通过/dev/mixer设备来完成的. 一、数字音频 音频信号是一种连续变化的模拟信号,但计算机只能处...

音量调节及静音



Haofei
2003年03月04日 08:56
1126
在进行多媒体软件开发时,经常要调整各种设备的音量和设置静音,本人编写了一个单元,四个函数,分别用于获取音量(GetVolume(DN))、设置音量(SetVolume(DN,Value))、获取静音(...

c++进行音频管理



tyuiof
2017年02月16日 14:53
936
最近做VR开发时需要对麦克风分的录音进行操作,其中涉及到调节录音的大小,检测录音的的大小等功能,我主要使用c++调用windows的音频相关的api来进行对录音的操作,在这里记录一下。对音频的控制主要...

混音器原理及Mixer API函数介绍2



LearnITing
2013年06月11日 18:36
3079
MIXERLINE 结构、枚举线路     如果你不知道一个混音器有哪些线路(注:你不知道它所拥有的线路的类型,也不知道线路ID),有一个方法可以获取混音器线路的信息:首先调用mixerGetDev...

编程环境应该和讨论社区完美的进行结合



arui319
2004年11月27日 18:28
1318
        在学习.NET过程中,经常会遇到很多问题,有的时候会找些参考书,有的时候会看MSDN,有的时候会去微软中国的社区去找答案。想一想,像.NET等等这些编程环境不仅应该提供相关文档,完全可...

vc关闭立体声混音



kaizi318
2017年04月05日 15:15
3050
#include BOOL  SetMicrPhoneVolume(DWORD dwVolume)   {   BOOL bResult = FALSE;   BOOL bfind =FALSE...

一个函数实现调节录音麦克风输入音量



wishfly
2015年10月09日 15:39
1871
头文件包含: [cpp] view plaincopy #include "mmsystem.h"   #pragma comment(lib, "winmm.lib")   ...

如何安装音频混音器和扬声器



mollrris
2014年07月25日 19:24
497
如何安装音频混音器和扬声器   对于转换的音频信号,音频混频器被使用。它无非是电子设备,主要用于改变,转向音频信号。这也被称为音板。还音板改变了节拍,音调,或者它可能会改变声音信号。音板给小改变在每一...

没有活动混音设备可用。要安装混音器设备,请转到控制面板,单击打印机和其他硬件,然后单击添加硬件

没有活动混音器设备可用。要安装混音器设备,请转到“控制面板”,单击“打印机和其他硬件”,然后单击“添加硬件”。 对这个问题,网上有3中说法: 1是 因为装了迅雷5的原因,把迅雷卸载了就...


niufujiang008
2010年07月14日 09:33
32515

一段设置和取消windows静音效果的Delphi代码



jinjazz
2008年07月18日 15:13
3405
 看到完美mm在windows版提问求这么一个dos命令,发现没有,于是自己摘录了以下代码编译了一个控制台程序unit Unit1;interfaceuses MMSystem, Dialogs;ty...

实时采集并显示录音波形WaveNative.cs



ec8483
2007年04月19日 19:07
1978
using System;using System.Runtime.InteropServices;using System.Drawing;using System.Text;namespace W...

没有活动混音器设备可用的解决方法



jkhere
2013年09月27日 20:10
1607
没有活动混音器设备可用的解决方法 一、右击“我的电脑”,点“属性”,点“硬件”,点“设备管理器”,查看声音前面有没有黄色问号,有说明声卡驱动不在了,将驱动放入重装。一般情况下,主板集成的声卡是ac9...

Unity5混音器DSP插件编写教程【一】



u012842807
2015年08月27日 09:34
969
转载:http://www.unitymanual.com/thread-42180-1-1.html      Hello!大家好,我是爱国者,很高兴又在游戏蛮牛论坛和大家见面了。这一期我...

混音器音量控制



zgl7903
2010年01月25日 13:51
1713
//MixVolumeCtrl.h #pragma once #include #pragma comment(lib, "winmm.lib") class CMixVolum...

yi7900

关注原创8 粉丝4 喜欢2 评论4等级: 

访问量: 6万+积分: 834排名: 6万+

他的最新文章

更多文章linux下定时任务
DS Write Filter中IStream情况
SMB2.0 读写文件优化
Windows2008 R2 x64 VCM没有CVID编解码能力
错误诊断:0xC0000139:DLL entry point not found

文章分类

专业知识4篇
软件开发12篇
DirectSound2篇
流媒体2篇

文章存档

2015年9月1篇
2014年1月3篇
2013年4月1篇
2012年8月4篇
2012年4月2篇
2011年12月1篇
2011年11月3篇
2011年9月4篇
2011年8月2篇
2011年7月1篇
2011年1月1篇
2010年11月5篇
2010年10月1篇
展开

他的热门文章

一步一步创建GStreamer插件(10824
SIP与RTP综合应用7146
网络传输层工作原理6798
转载]Linux Gstreamer and GST-OMX插件5619
错误诊断:0xC0000139:DLL entry point not found3683
mingw编译faac-1.28(undefined reference to `ntohl@4')3252
SMB2.0 读写文件优化3058
WAVEFORMATEX wFormatTag2758
混音器编程接口讨论2571
DirectSound初步教程1756

联系我们


请扫描二维码联系客服

webmaster@csdn.net400-660-0108QQ客服 客服论坛关于招聘广告服务  百度©1999-2018 CSDN版权所有京ICP证09002463号经营性网站备案信息网络110报警服务中国互联网举报中心北京互联网违法和不良信息举报中心0

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: