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语言编译器应该都可以编译它。