MFC 音量控制相关资料
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 向指定的波形输出设备发送一个数据块
混音器编程接口讨论
转自:http://www.cnblogs.com/windviki/archive/2011/08/31/2160683.html
翻译:[email protected] 转载请注明
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
{
}
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);
}
}
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);
}
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);
}
}
事实上,如果你去看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种关联类型:
MIXERCONTROL_CT_CLASS_METER类有4种关联类型:
MIXERCONTROL_CT_CLASS_TIME类有两种关联类型:
MIXERLINE结构体,以及枚举所有线路
MIXERCAPS mixercaps = {
0,
0,
0x0100,
"Example Sound Card",
0,
2,
};
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",
};
现在我们来看一下目标线路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",
};
混音器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);
}
mixerLine.cbStruct = sizeof(MIXERLINE);
mixerLine.dwDestination = 1;
if ((err = mixerGetLineInfo((HMIXEROBJ)mixerHandle, &mixerLine, MIXER_GETLINEINFOF_DESTINATION)))
{
printf("Error #%d calling mixerGetLineInfo()\n", err);
}
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);
}
}
}
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;
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);
}
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);
}
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(按照上面所示的方法枚举出线路,然后从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);
}
通过类型取得线路信息
通常,你不需要知道混音器中所有的线路。你的程序可能仅仅只和某个类型的线路打交道。比如,假设你正在编写一个MIDI文件播放器。现在,我们的样例声卡的某些元件对于你来说根本毫无用处。MIDI不是数字音频数据,因此DAC WAVE输入元件(以及所有连接到它的所有源线路)对你来说毫无意义。同样,目标线路扬声器输出的源线路内部CD音频、DAC WAVE输出和麦克风输入对你来说也毫无意义。我们的样例声卡上唯一一个可以处理MIDI数据回放的元件是连接扬声器输出的合成器元件。正是这个线路的控制器影响着MIDI数据的回放。
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);
}
你可以通过混音器API mixerGetLineControls()来获取线路中控制器的信息。这个API将会将某个控制器的信息填入到一个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的情况下才可以只取这个控制器的信息。否则,你必须同时获取给定线路的所有控制器的信息。
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);
}
一旦你知道了一个控制器的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 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);
}
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);
}
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标志时,所有声道都共享同一个值。例如,对于扬声器输出线路,它是立体声线路,但是其左右声道并没有独立的音量值。
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 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个以上的声道。对一个给定的控制器,你必须提供足够大的结构来容纳所有的声道的值。因此,通常你必须根据需要来开辟数组空间。
多元素控制器
你不会常常碰到多元素的控制器。一个多元素控制器就是每个声道关联着多个值的控制器。图形化均衡器是一个例子。让我们来看一个简单的,假设一个声卡内置有以下带着3个通道(band)的图形化均衡器:
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,
};
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);
}
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);
}
Left Channel | Right 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,而不必将打开的混音器句柄作为混音器函数的某个参数去传递。所以,你不必显式的打开一个混音器。
首先,这会防止混音器被卸载(大概是声卡的驱动所为)。其次,在你打开了一个混音器后,当此混音器的任何线路的状态发生改变时(比如线路被设置为静音),或者其中某个控制器的值改变时,你可以指示windows系统发送一个消息(到你创建的自定义的窗口处理例程)通知你。你不仅仅会在你改变某个线路的状态或某个控制器的值时收到这些消息,而且当其它程序打开混音器(多个程序可以同时打开一个混音器)并改变某个线路的状态或某个控制器的值时也会收到。因此,当其它程序对混音器做改变时,你可以使你的程序与混音器的状态保持同步。
当你调用mixerOpen()时,你应该将接受通知消息的窗口句柄作为第三个参数传递给此函数,并将CALLBACK_WINDOW指定为最后一个参数。
这里有2个特殊的“混音器消息”。
MM_MIXM_LINE_CHANGE:当混音器的任何一条线路的状态发生改变时,系统会发送此消息到你的窗口处理程序。
对于MM_MIXM_LINE_CHANGE,WPARAM参数表示发生改变的线路所属的混音器句柄,LPARAM参数表示此线路的ID。
对于MM_MIXM_CONTROL_CHANGE,WPARAM参数表示发生改变的线路所属的混音器句柄,LPARAM参数表示值发生改变的控制器的ID。
想获取更多关于混音器结构和API的信息,请参考Microsoft Developer Network上关于音频混音器的文章(audio mixers) 。
微软提供了一个免费下载的关于如何使用混音器API的样例程序。但是我发现它的代码的注释太简单了,同时有很多和混音器API无关的且不必要的代码。我已将此样例程序精简,使它展示的都是如何使用混音器API的关键代码,并添加了很多注释。你可以下载我修改的版本Microsoft's Mixer Device Example,它将展示如何显示所有的混音器设备和它们的线路/控制器,以及调整它们控制器的值。这个工程是基于Visual C++4.0的,因为它是一个普通的用C语言编写的windows应用程序,因此任何windows的C语言编译器应该都可以编译它。
UINT uNum = mixerGetNumDevs();
MIXER 混响器编程
- fakine
- 2013年10月14日 17:17
- 2998
Windows混音器API使用
- thanklife
- 2014年03月12日 14:58
- 1402
2018,UI设计师如何少走弯路快速提升设计能力!
少走弯路快速提升诀窍:
对声卡输出进行录音的设置.
- axman
- 2007年08月02日 17:40
- 7230
混音器编程接口讨论
- yi7900
- 2011年11月21日 17:19
- 2574
声卡输入端子在DirectShow中的自动选择
- happydeer
- 2005年03月15日 10:46
- 5123
混音器编程接口讨论
- wwqingyue
- 2013年10月24日 17:17
- 960
Linux的混音设备/dev/mixer
- b02330224
- 2012年09月23日 14:29
- 1731
音量调节及静音
- Haofei
- 2003年03月04日 08:56
- 1126
c++进行音频管理
- tyuiof
- 2017年02月16日 14:53
- 936
混音器原理及Mixer API函数介绍2
- LearnITing
- 2013年06月11日 18:36
- 3079
编程环境应该和讨论社区完美的进行结合
- arui319
- 2004年11月27日 18:28
- 1318
vc关闭立体声混音
- kaizi318
- 2017年04月05日 15:15
- 3050
一个函数实现调节录音麦克风输入音量
- wishfly
- 2015年10月09日 15:39
- 1871
如何安装音频混音器和扬声器
- mollrris
- 2014年07月25日 19:24
- 497
没有活动混音设备可用。要安装混音器设备,请转到控制面板,单击打印机和其他硬件,然后单击添加硬件
- niufujiang008
- 2010年07月14日 09:33
- 32515
一段设置和取消windows静音效果的Delphi代码
- jinjazz
- 2008年07月18日 15:13
- 3405
实时采集并显示录音波形WaveNative.cs
- ec8483
- 2007年04月19日 19:07
- 1978
没有活动混音器设备可用的解决方法
- jkhere
- 2013年09月27日 20:10
- 1607
Unity5混音器DSP插件编写教程【一】
- u012842807
- 2015年08月27日 09:34
- 969
混音器音量控制
- zgl7903
- 2010年01月25日 13:51
- 1713
他的最新文章
更多文章文章分类
-
专业知识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 `[email protected]')3252
-
SMB2.0 读写文件优化3058
-
WAVEFORMATEX wFormatTag2758
-
混音器编程接口讨论2571
-
DirectSound初步教程1756
转自:http://www.cnblogs.com/windviki/archive/2011/08/31/2160683.html
翻译:[email protected] 转载请注明
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
{
}
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);
}
}
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);
}
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);
}
}
事实上,如果你去看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种关联类型:
MIXERCONTROL_CT_CLASS_METER类有4种关联类型:
MIXERCONTROL_CT_CLASS_TIME类有两种关联类型:
MIXERLINE结构体,以及枚举所有线路
MIXERCAPS mixercaps = {
0,
0,
0x0100,
"Example Sound Card",
0,
2,
};
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",
};
现在我们来看一下目标线路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",
};
混音器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);
}
mixerLine.cbStruct = sizeof(MIXERLINE);
mixerLine.dwDestination = 1;
if ((err = mixerGetLineInfo((HMIXEROBJ)mixerHandle, &mixerLine, MIXER_GETLINEINFOF_DESTINATION)))
{
printf("Error #%d calling mixerGetLineInfo()\n", err);
}
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);
}
}
}
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;
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);
}
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);
}
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(按照上面所示的方法枚举出线路,然后从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);
}
通过类型取得线路信息
通常,你不需要知道混音器中所有的线路。你的程序可能仅仅只和某个类型的线路打交道。比如,假设你正在编写一个MIDI文件播放器。现在,我们的样例声卡的某些元件对于你来说根本毫无用处。MIDI不是数字音频数据,因此DAC WAVE输入元件(以及所有连接到它的所有源线路)对你来说毫无意义。同样,目标线路扬声器输出的源线路内部CD音频、DAC WAVE输出和麦克风输入对你来说也毫无意义。我们的样例声卡上唯一一个可以处理MIDI数据回放的元件是连接扬声器输出的合成器元件。正是这个线路的控制器影响着MIDI数据的回放。
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);
}
你可以通过混音器API mixerGetLineControls()来获取线路中控制器的信息。这个API将会将某个控制器的信息填入到一个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的情况下才可以只取这个控制器的信息。否则,你必须同时获取给定线路的所有控制器的信息。
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);
}
一旦你知道了一个控制器的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 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);
}
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);
}
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标志时,所有声道都共享同一个值。例如,对于扬声器输出线路,它是立体声线路,但是其左右声道并没有独立的音量值。
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 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个以上的声道。对一个给定的控制器,你必须提供足够大的结构来容纳所有的声道的值。因此,通常你必须根据需要来开辟数组空间。
多元素控制器
你不会常常碰到多元素的控制器。一个多元素控制器就是每个声道关联着多个值的控制器。图形化均衡器是一个例子。让我们来看一个简单的,假设一个声卡内置有以下带着3个通道(band)的图形化均衡器:
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,
};
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);
}
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);
}
Left Channel | Right 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,而不必将打开的混音器句柄作为混音器函数的某个参数去传递。所以,你不必显式的打开一个混音器。
首先,这会防止混音器被卸载(大概是声卡的驱动所为)。其次,在你打开了一个混音器后,当此混音器的任何线路的状态发生改变时(比如线路被设置为静音),或者其中某个控制器的值改变时,你可以指示windows系统发送一个消息(到你创建的自定义的窗口处理例程)通知你。你不仅仅会在你改变某个线路的状态或某个控制器的值时收到这些消息,而且当其它程序打开混音器(多个程序可以同时打开一个混音器)并改变某个线路的状态或某个控制器的值时也会收到。因此,当其它程序对混音器做改变时,你可以使你的程序与混音器的状态保持同步。
当你调用mixerOpen()时,你应该将接受通知消息的窗口句柄作为第三个参数传递给此函数,并将CALLBACK_WINDOW指定为最后一个参数。
这里有2个特殊的“混音器消息”。
MM_MIXM_LINE_CHANGE:当混音器的任何一条线路的状态发生改变时,系统会发送此消息到你的窗口处理程序。
对于MM_MIXM_LINE_CHANGE,WPARAM参数表示发生改变的线路所属的混音器句柄,LPARAM参数表示此线路的ID。
对于MM_MIXM_CONTROL_CHANGE,WPARAM参数表示发生改变的线路所属的混音器句柄,LPARAM参数表示值发生改变的控制器的ID。
想获取更多关于混音器结构和API的信息,请参考Microsoft Developer Network上关于音频混音器的文章(audio mixers) 。
微软提供了一个免费下载的关于如何使用混音器API的样例程序。但是我发现它的代码的注释太简单了,同时有很多和混音器API无关的且不必要的代码。我已将此样例程序精简,使它展示的都是如何使用混音器API的关键代码,并添加了很多注释。你可以下载我修改的版本Microsoft's Mixer Device Example,它将展示如何显示所有的混音器设备和它们的线路/控制器,以及调整它们控制器的值。这个工程是基于Visual C++4.0的,因为它是一个普通的用C语言编写的windows应用程序,因此任何windows的C语言编译器应该都可以编译它。