如果你不知道一个混音器有哪些线路(注:你不知道它所拥有的线路的类型,也不知道线路ID),有一个方法可以获取混音器线路的信息:首先调用mixerGetDevCaps()将混音器设备信息填入到MIXERCAPS结构体中。使用此结构体中的信息,你可以确定声卡上有多少个目标线路。然后,你就可以枚举每条目标线路和与每条目标线路关联的源线路。等你枚举完所有线路后,就可以枚举每个线路所关联的控件。
我们现在来检测一下此方法并学习一下和此Mixer API相关的结构体。
为了更好的理解Mixer API,我们应该看一下样例声卡内部结构,并看一下用于Mixer API的一些结构体。我们假设这个混音器设备使用C语言编写,因此使用C语言的结构体。
如前所述,Mixer API mixerGetDevCaps()可用来获取混音器设备信息。它会填充一个MIXERCAPS结构体。特别重要的是,MIXERCAPS的域成员cDestinations会告诉你此声卡上有多少个目标线路。但是它不会告诉你总共有多少个线路(注:目标线路和源线路的总数)。它仅仅指目标线路。你应该还记得,我们的样例声卡有2个目标线路。在这篇教程中,我们还会用任意值填充其它域,诸如混音器名称和ID。假设这是第一个安装到系统的混音器(注:ID=0)。如下是我们的混音器设备的MIXERCAPS结构体(在MMSYSTEM.H中定义)。
MIXERCAPS mixercaps = {
0, /* manufacturer id */
0, /* product id */
0x0100, /* driver version #. Note that the high 8-bits
are the version, and low 8-bits are revision. So,
our driver version is 1.0 */
"Example Sound Card", /* product name */
0, /* Support bits. None are currently defined */
2, /* # of destination lines */
};
如下是一个样例程序,其将传递一个MIXERCAPS结构体到mixerGetDevCaps()函数,
并让windows使用以上的值来填充它。(假设我们已经打开了此混音器并将其句柄保存到变量"mixerHandle"中)。
MIXERCAPS mixcaps;
MMRESULT err;
/* Get info about the first Mixer Device */
if (!(err = mixerGetDevCaps((UINT)mixerHandle, &mixcaps, sizeof(MIXERCAPS))))
{
/* Success. We can continue on with our tutorial below */
}
else
{
/* An error */
printf("Error #%d calling mixerGetDevCaps()\n", err);
}
线路的信息保存在一个MIXERLINE结构体中(在MMSYSTEM.H中定义)。我们假设Speaker Out目标线路有两个控件——一个音量滑动条(控制流向Speaker Out线路的总音量)和一个静音开关(控制Speaker Out线路的静音)。
如下是我们的Speaker Out目标线路的MIXERLINE 结构体:
MIXERLINE mixerline_SpkrOut = {
sizeof(MIXERLINE), /* size of MIXERLINE structure */
0, /* zero based index of destination line */
0, /* zero based source index (used only if
this is a source line) */
0xFFFF0000, /* unique ID # for this line */
MIXERLINE_LINEF_ACTIVE, /* state/information about line */
0, /* driver specific information */
MIXERLINE_COMPONENTTYPE_DST_SPEAKERS, /* component type */
2, /* # of channels this line supports */
4, /* # of source lines connected (used
only if this is a destination line) */
2, /* # of controls in this line */
"Spkr Out", /* Short name for this line */
"Speaker Out", /* Long name for this line */
MIXERLINE_TARGETTYPE_WAVEOUT, /* MIXERLINE_TARGETTYPE_xxxx */
/* The following info is just some info about the Mixer Device for this
line, prepended to the above info. It's mostly just for reference. */
0, /* device ID of our Mixer */
0, /* manufacturer id */
0, /* product id */
0x0100, /* driver version # */
"Example Sound Card", /* product name */
};
这里有一些要注意的地方。首先,Speaker Out目标线路的域成员dwComponentType是
目标线路类型——MIXERLINE_COMPONENTTYPE_DST_SPEAKERS。此类型值恰当的说明了这是一个Speaker Out目标线路。我已经选择了一个值0xFFFF000赋给dwLineID域。编程人员可以为其赋予任何值给这个域,但是其它任何线路的dwLineID域的值不能和此值相同。同样需要注意的是,既然我们的speaker output是立体声的,因此cChannels域值就是2。你应该还记得,这里有4个源线路连接到我们的Speaker Out目标线路,因此cConnections域的值是4。Name域是以null结尾的字符串。它可以是编程人员选择的任何值,shorter name域是用于标识那些紧挨存放的图形控件的。fdwLine域中设置有MIXERLINE_LINE_ACTIVE标志,这意味着此线路没有被禁用。
dwDestination域是一个从0开始的索引值,混音器中的第一个目标线路的索引值为0(如上示例程序所示)。第二个目标线路的索引值是1,第三个目标线路的索引值为2,等等。这和windows枚举混音器设备的概念一样(注:第一个安装的混音器的ID为0)。索引值和线路的ID并不一定相等(你可以从上面的示例程序中看出)。使用索引值的目的是什么?我们为什么要同时使用索引值和ID?或许有可以猜到,索引值主要在你需要枚举混音器的线路时使用。直到你枚举了所有的线路(注:获取每个线路的信息)之后,你才直到每个线路的ID。因此,你需要将这些索引值用于Mixer API函数中,去枚举所有的线路。但是一旦你已经取得了某个线路的信息,因此而知道了它的ID,然后你就可以更直接的更改它的设置。因此,索引值在起初枚举线路和控件以获取其ID及类型时很有用。在随后的控制线路和控件的过程中ID就起主要作用。
现在我们来看一下我们的目标线路ADC Wave Input的MIXERLINE结构。我们假设它有两个控件——一个音量控件和一个静音控件。如下是目标线路ADC Wave Input的MIXERLINE的结构:
MIXERLINE mixerline_WaveIn = {
sizeof(MIXERLINE), /* size of MIXERLINE structure */
1, /* zero based index of destination line */
0, /* zero based source index (used only if
this is a source line) */
0xFFFF0001, /* unique ID # for this line */
MIXERLINE_LINEF_ACTIVE, /* state/information about line */
0, /* driver specific information */
MIXERLINE_COMPONENTTYPE_DST_WAVEIN, /* component type */
2, /* # of channels this line supports */
2, /* # of source lines connected (used
only if this is a destination line) */
2, /* # of controls in this line */
"Wave In", /* Short name for this line */
"Wave Input", /* Long name for this line */
MIXERLINE_TARGETTYPE_WAVEIN, /* MIXERLINE_TARGETTYPE_xxxx */
/* The following info is just some info about the Mixer Device for this
line, prepended to the above info. It's mostly just for reference and is only valid if the
Target Type field is not MIXERLINE_TARGETTYPE_UNDEFINED. */
0, 0, 0, 0x0100, "Example Sound Card",
};
注意目标线路ADC Wave Input的域dwComponentType的值
是MIXERLINE_COMPONENTTYPE_DST_WAVEIN,这表明它是一个波形输入组件。同样的,我选择了值0xFFFF0001赋予域dwLineID —— 和目标线路Speaker Out的域dwLineID值不同的值。
因为我们的声卡还具有数字化功能,因此cChannels域值是2。你应该还记得,有2个源线路连接到我们的目标线路ADC Wave Input,因此域cConnections是2。最后,注意dwDestination域的值是1,因为这是混音器中第二个目标线路。
Mixer API mixerGetLineInfo()为指定的线路填充MIXERLINE结构。这就是你获取某个线路信息的方法。如果你不知道一个线路的ID(在你第一次枚举线路的情况下),那么你可以通过索引值来引用它。通过给mixerGetLineInfo()传递值
MIXER_GETLINEINFOF_DESTINATION来表明你想通过线路的索引值来引用线路。如下展示了怎样获取我们的样例混音器中第一个目标线路的信息。在调用mixerGetLineInfo()和传递MIXERLINE结构体之前,你必须初始化两个域。cbStruct域的值必须设置为你传递的MIXERLINE结构所占的字节数,dwDestination域值必须设置为你想获取信息的线路的索引值。
记住第一个目标线路的索引值为0,因此如果要获取它的信息,我们就将dwDestination设置为0。
MIXERLINE mixerline;
MMRESULT err;
/* Get info about the first destination line by its index */
mixerline.cbStruct = sizeof(MIXERLINE);
mixerline.dwDestination = 0;
if ((err = mixerGetLineInfo((HMIXEROBJ)mixerHandle, &mixerline, MIXER_GETLINEINFOF_DESTINATION)))
{
/* An error */
printf("Error #%d calling mixerGetLineInfo()\n", err);
}
When the above call returns, mixerGetLineInfo() will have filled in our MIXERLINE struct as per the Speaker Out (mixerline_SpkrOut) MIXERLINE struct shown above. (After all, the Speaker Out is the first line in our example Mixer -- it has an index of 0).
Now, if you want to fetch info about the second destination line in the mixer, the only thing different is the index value you stuff into the dwDestination field, as so:
/* Get info about the second destination line by its index */
mixerLine.cbStruct = sizeof(MIXERLINE);
mixerLine.dwDestination = 1;
if ((err = mixerGetLineInfo((HMIXEROBJ)mixerHandle, &mixerLine, MIXER_GETLINEINFOF_DESTINATION)))
{
/* An error */
printf("Error #%d calling mixerGetLineInfo()\n", err);
}
当上述调用返回之后,mixerGetLineInfo()函数将会使用如上所示的ADC Wave Input(mixerline_WaveIn)结构的值填充MIXERLINE结构(毕竟,ADC Wave Input是我们的样例混音器中的第二个线路——它的索引值为1)。
现在,你可以看到怎样通过每个线路的索引值来枚举所有的线路。如下是一个示例程序,将会打印出所有目标线路的名称。
MIXERCAPS mixcaps;
MIXERLINE mixerline;
MMRESULT err;
unsigned long i;
/* Get info about the first Mixer Device */
if (!(err = mixerGetDevCaps((UINT)mixerHandle, &mixcaps, sizeof(MIXERCAPS))))
{
/* Print out the name of each destination line */
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,如此等等。记住Speaker Out目标线路有4个与其关联的源线路:“Internal CD Audio”、“Synth”、“DAC Wave Out”、和“Microphone Input”。因此它们的索引值分别是0、1、2、3。我们看一下这4个源线路的MIXERLINE结构体,假定他们都有两个控件 —— 一个音量控件和一个静音控件。同样地,假定每个都是立体声控件。
MIXERLINE mixerline_CD = {
sizeof(MIXERLINE),
0, /* zero based index of destination line */
0, /* zero based source index */
0x00000000, /* unique ID # for this line */
MIXERLINE_LINEF_ACTIVE|MIXERLINE_LINEF_SOURCE, /* state/information about line */
0,
MIXERLINE_COMPONENTTYPE_SRC_COMPACTDISC, /* component type */
2, /* # of channels this line supports */
0, /* Not applicable for source lines */
2, /* # of controls in this line */
"CD Audio", /* Short name for this line */
"Internal CD Audio", /* Long name for this line */
MIXERLINE_TARGETTYPE_UNDEFINED, /* MIXERLINE_TARGETTYPE_xxxx */
0, 0, 0, 0x0100, "Example Sound Card",
};
MIXERLINE mixerline_Synth = {
sizeof(MIXERLINE),
0, /* zero based index of destination line */
1, /* zero based source index */
0x00000001, /* unique ID # for this line */
MIXERLINE_LINEF_ACTIVE|MIXERLINE_LINEF_SOURCE,
0,
MIXERLINE_COMPONENTTYPE_SRC_SYNTHESIZER, /* component type */
2, /* # of channels this line supports */
0, /* Not applicable for source lines */
2, /* # of controls in this line */
"Synth", /* Short name for this line */
"Synth", /* Long name for this line */
MIXERLINE_TARGETTYPE_UNDEFINED, /* MIXERLINE_TARGETTYPE_xxxx */
0, 0, 0, 0x0100, "Example Sound Card",
};
MIXERLINE mixerline_WaveOut = {
sizeof(MIXERLINE),
0, /* zero based index of destination line */
2, /* zero based source index */
0x00000002, /* unique ID # for this line */
MIXERLINE_LINEF_ACTIVE|MIXERLINE_LINEF_SOURCE,
0,
MIXERLINE_COMPONENTTYPE_SRC_WAVEOUT, /* component type */
2, /* # of channels this line supports */
0, /* Not applicable for source lines */
2, /* # of controls in this line */
"Wave Out", /* Short name for this line */
"DAC Wave Out", /* Long name for this line */
MIXERLINE_TARGETTYPE_WAVEOUT, /* MIXERLINE_TARGETTYPE_xxxx */
0, 0, 0, 0x0100, "Example Sound Card",
};
MIXERLINE mixerline_Mic = {
sizeof(MIXERLINE),
0, /* zero based index of destination line */
3, /* zero based source index */
0x00000003, /* unique ID # for this line */
MIXERLINE_LINEF_ACTIVE|MIXERLINE_LINEF_SOURCE,
0,
MIXERLINE_COMPONENTTYPE_SRC_MICROPHONE, /* component type */
2, /* # of channels this line supports */
0, /* Not applicable for source lines */
2, /* # of controls in this line */
"Mic", /* Short name for this line */
"Microphone Input", /* Long name for this line */
MIXERLINE_TARGETTYPE_WAVEIN, /* MIXERLINE_TARGETTYPE_xxxx */
0, 0, 0, 0x0100, "Example Sound Card",
};
你必须注意的一件事是:不同于目标线路,源线路的MIXERLINE中设置有
MIXERLINE_LINE_SOURCE标志。当有这个标志时,你就知道你获取的是一个源线路的信息。其次,注意这个目标线路的索引值是0。这是因为以上所有源线路连接的目标线路Speaker Out是我们的混音器中的第一个线路(因此它的索引值为0)。再次,注意以上4个源线路的索引值分别是0、1、2和3。最后,注意每个线路的ID都是唯一的——不像其它的线路,包括所有的目标线路。
Mixer API mixerGetLineInfo()同样会为源线路填充MIXERLINE结构体。同样的,你可以通过索引值来引用源线路,但是你必须知道每个目标线路的索引值。通过传递MIXER_GETLINEINFOF_SOURCE 标志给mixerGetLineInfo()函数,告知它你要通过索引值来引用源线路。如下是怎样获取我们的样例混音器中的第一个源线路(目标线路为Speaker Out)的样例程序。在调用mixerGetLineInfo()和传递MIXERLINE结构体之前,你必须初始化3个域:cbStruct域的值必须设置为你传递的MIXERLINE结构所占的字节数,dwSource域必须设置为你想获取信息的源线路的索引值,dwDestination域必须设置为和这个源线路相连的目标线路的索引值。
MIXERLINE mixerline;
MMRESULT err;
/* Get info about the first source line (of the first destination line) by its index */
mixerline.cbStruct = sizeof(MIXERLINE);
mixerline.dwDestination = 0;
mixerline.dwSource = 0;
if ((err = mixerGetLineInfo((HMIXEROBJ)mixerHandle, &mixerline, MIXER_GETLINEINFOF_SOURCE)))
{
/* An error */
printf("Error #%d calling mixerGetLineInfo()\n", err);
}
当上述调用返回后,mixerGetLineInfo()将会按照如上所示的"Internal CD Audio"
(mixerline_CD)结构的值来填充MIXERLINE结构体,你可以从MIXERLINE的dwLineID域获取它的线路ID。
现在,如果你想获取目标线路Speaker Out的第二条源线路的信息,唯一不同的就是给dwSource域填入一个不同的索引值,如下:
/* Get info about the second source line by its index */
mixerline.cbStruct = sizeof(MIXERLINE);
mixerline.dwDestination = 0;
mixerline.dwSource = 1;
if ((err = mixerGetLineInfo((HMIXEROBJ)mixerHandle, &mixerline, MIXER_GETLINEINFOF_SOURCE)))
{
/* An error */
printf("Error #%d calling mixerGetLineInfo()\n", err);
}
当上述调用返回之后,mixerGetLineInfo()将会按照前面所示的"Synth" (mixerline_Synth)结构的值来填充MIXERLINE结构体。
现在,你应该知道怎样通过源线路(属于某个目标线路)的索引值来枚举的所有源线路了。这里是一个示例程序,它输出了我们的样例混音器中所有目标线路的以及其各自关联的所有源线路的名称。
MIXERCAPS mixcaps;
MIXERLINE mixerline;
MMRESULT err;
unsigned long i, n, numSrc;
/* Get info about the first Mixer Device */
if (!(err = mixerGetDevCaps((UINT)mixerHandle, &mixcaps, sizeof(MIXERCAPS))))
{
/* Print out the name of each destination line */
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);
/* Print out the name of each source line in this destination */
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结构体的dwLine域,然后传递MIXER_GETLINEINFOF_LINEID参数,按如下方式调用mixerGetLineInfo():
/* Get info about the "Microphone Input" source line by its ID */
mixerline.cbStruct = sizeof(MIXERLINE);
mixerline.dwLineID = 0x00000003; /* The ID for "Microphone Input" */
if ((err = mixerGetLineInfo((HMIXEROBJ)mixerHandle, &mixerline, MIXER_GETLINEINFOF_LINEID)))
{
/* An error */
printf("Error #%d calling mixerGetLineInfo()\n", err);
}
以上方法均适用于源线路和目标线路。一旦你知道了一个线路的ID,你就可以根据它直接获取线路的信息,根本不需要知道索引值。
通常情况下,你不需要知道混音器中所有的线路。或许在你的程序中你仅仅和某个类型的线路打交道。比如,假设你正在编写一个MIDI文件播放器。现在,我们的样例声卡的某些组件对于你来说毫无用处。MIDI不是数字音频数据,因此"DAC Wave In"组件(以及所有连接到它的所有源线路)对你来说毫无意义。同样,目标线路Speaker Out 的源线路 "Internal CD Audio"、"DAC Wave Out" 和"Microphone Input"对你来说也毫无意义。我们的样例声卡上唯一一个可以处理MIDI数据回放的组件是目标线路Speaker Out的"Synth"源线路。正是这个线路的控件影响着MIDI数据的回放。
较之于枚举混音器中的所有线路直到碰到一个类型为
MIXERLINE_COMPONENTTYPE_SRC_SYNTHESIZER的线路的方法相比,
mixerGetLineInfo()让你直接获取指定类型的线路信息的方法更胜一筹。你只需要将MIXERLINE的dwComponentType域指定为你想要线路类型,然后指定
MIXER_GETLINEINFOF_COMPONENTTYPE并按如下方式调用mixerGetLineInfo():
/* Get info about a "Synth" type of source line */
mixerline.cbStruct = sizeof(MIXERLINE);
mixerline.dwComponentType = MIXERLINE_COMPONENTTYPE_SRC_SYNTHESIZER; /* We want a Synth type */
if ((err = mixerGetLineInfo((HMIXEROBJ)mixerHandle, &mixerline, MIXER_GETLINEINFOF_COMPONENTTYPE)))
{
/* An error */
printf("Error #%d calling mixerGetLineInfo()\n", err);
}
系统将会用混音器中第一个类型为 MIXERLINE_COMPONENTTYPE_SRC_SYNTHESIZER的线路的信息填充此MIXERLINE结构体。(如果混音器中没有此类型的线路,将会返回一个MIXERR_INVALLINE的错误)。一旦获取了此线路的信息,你就可以操纵它的控件了。这样当你有特殊的需求时就省去了通过枚举得到某个线路的麻烦。
你可以通过Mixer API mixerGetLineControls()来获取线路中控件的信息。这个API将会将某个控件的信息填入到一个MIXERCONTROL结构体中。
我们首先来看一下MIXERCONTROL结构体。如前所述,我们的Speaker Out目标线路有两个控件:一个音量滑动器和一个静音开关。每个控件都有一个MIXERCONTROL结构体与其相连。我们首先看一下这些控件的MIXERCONTROL结构:
MIXERCONTROL mixerctl_Spkr_Vol = {
sizeof(MIXERCONTROL), /* size of a MIXERCONTROL */
0x00000000, /* unique ID # for this control */
MIXERCONTROL_CONTROLTYPE_VOLUME, /* type of control */
MIXERCONTROL_CONTROLF_UNIFORM, /* flag bits */
0, /* # of items per channels (used only if the
MIXERCONTROL_CONTROLF_MULTIPLE flag bit is also set) */
"Volume", /* Short name for this control */
"Speaker Out Volume", /* Long name for this control */
0, /* Minimum value to which this control can be set */
65535, /* Maximum value to which this control can be set */
0, 0, 0, 0, /* These fields are reserved for future use */
31, /* Step amount for the value */
0, 0, 0, 0, 0, /* These fields are reserved for future use */
};
MIXERCONTROL mixerctl_Spkr_Mute = {
sizeof(MIXERCONTROL),
0x00000001, /* unique ID # for this control */
MIXERCONTROL_CONTROLTYPE_MUTE, /* type of control */
MIXERCONTROL_CONTROLF_UNIFORM,
0,
"Mute", /* Short name for this control */
"Speaker Out Mute", /* Long name for this control */
0, /* Minimum value to which this control can be set */
1, /* Maximum value to which this control can be set */
0, 0, 0, 0,
0, /* Step amount for the value */
0, 0, 0, 0, 0,
};
上面有几个问题要提及一下。首先,注意每个控件都有一个唯一的ID,这些ID可以和线路的ID相同(比如控件mixerctl_Spkr_Vol的ID恰巧和线路mixerline_CD的ID相同)。但是每个控件的ID都不能和其它控件的ID相同,包括同其他线路的控件。(例如,线路Speaker Out的音量滑动器控件的ID不能和线路静音开关"ADC Wave In"的任何一个控件的ID相同)。
我已设置了MIXERCONTROL_CONTROLF_UNIFORM标志,这个标志的意思是:虽然Speaker Out是立体声的(注:有两个声道),但是并不是每个控件都有一个单独的音量控件。(这里没有左右声道各自的音量设置)。对于这两个声道只有一个共同的音量设置。因此这两个声道总是被设置为相同的音量(后面我们会学非均衡(not uniform)的控件)。
同样需要注意的是每个控件都有一个适当的类型。
音量滑动器控件的类型为MIXERCONTROL_CONTROLTYPE_VOLUME,
静音开关控件的类型为MIXERCONTROL_CONTROLTYPE_MUTE。
MIXERCONTROL还会告诉你这个控件的最大值和最小值。例如,音量滑动器控件可以设置0到65535之前的任何一个值。其中0是最小值(注:此时音量最小),65535是最大值(注:此时音量最大),这是不是意味着音量滑动器控件有65535个离散的等级呢?(注:可以被设置为从0至65535之间(包括0和65535)的任何值?)。不一定。你还得看一下等级总数这个域。它会告诉你这个控件有多少个有效的等级。在目前情况下,我们有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的结构。
例如,我们知道我们的Speaker Out目标线路有两个控件:一个音量滑动器控件和一个静音开关(记住MIXERLINE结构的cControls域是2)。因此,如果要获取这两个控件的信息,我们就必须给mixerGetLineControls传递包含两个MIXERCONTROL结构的数组。同时我们必须传递MIXER_GETLINECONTROLSF_ALL标志表明我们需要获取所有控件的信息。为了告诉mixerGetLineControls()我们希望获取哪个线路的控件的信息,我们还必须初始化并传递一个类型为MIXERLINECONTROLS的结构。如下是获取线路Speaker Out的控件信息的样例程序:
/* Let's just declare an array of 2 MIXERCONTROL structs since
we know that the "Speaker Out" has 2 controls. For a real program,
you typically won't know ahead of time how big an array you'll need,
and therefore would instead allocate an appropriately sized array */
MIXERCONTROL mixerControlArray[2];
MIXERLINECONTROLS mixerLineControls;
MMSYSTEM err;
mixerLineControls.cbStruct = sizeof(MIXERLINECONTROLS);
/* The "Speaker Out" line has a total of 2 controls. And
that's how many we want to retrieve info for here */
mixerLineControls.cControls = 2;
/* Tell mixerGetLineControls() for which line we're retrieving info.
We do this by putting the desired line's ID number in dwLineID.
The "Speaker Out" line has an ID of 0xFFFF0000 */
mixerLineControls.dwLineID = 0xFFFF0000;
/* Give mixerGetLineControls() the address of our array of
MIXERCONTROL structs big enough to hold info on all controls */
mixerLineControls.pamxctrl = &mixerControlArray[0];
/* Tell mixerGetLineControls() how big each MIXERCONTROL is. This
saves having to initialize the cbStruct of each individual
MIXERCONTROL in the array */
mixerLineControls.cbmxctrl = sizeof(MIXERCONTROL);
/* Retrieve info on all controls for this line simultaneously */
if ((err = mixerGetLineControls((HMIXEROBJ)mixerHandle, &mixerLineControls, MIXER_GETLINECONTROLSF_ALL)))
{
/* An error */
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)和所有控件总数(不包括总数)之间个控件的信息是不行的。例如,假设我们的Speaker Out线路有5个控件(而不是目前的2个),你不能仅仅获取前3个控件。例如,你只能一次获取5个控件中1个控件的信息,或者一次获取着5个控件的信息。(注:也就是要么一次获取一个控件的信息,要么一次获取所有的信息)。
一旦你知道了一个控件的ID(你可以使用上面所示的方法,首先枚举所有控件,然后从MIXERCONTROL的dwControlID域获取控件的ID),你就可以通过这个ID仅仅获取这个控件的信息。你甚至不必知道这个控件所属的线路的ID。你也不必同时获取所有控件的信息。你仅仅只需要初始化MIXERCONTROL的dwControlID域,然后指定MIXER_GETLINECONTROLSF_ONEBYID标志并按如下方式调用mixerGetLineControls():
/* We need only 1 MIXERCONTROL struct since
we're fetching info for only 1 control */
MIXERCONTROL mixerControlArray;
MIXERLINECONTROLS mixerLineControls;
MMSYSTEM err;
mixerLineControls.cbStruct = sizeof(MIXERLINECONTROLS);
/* We want to fetch info on only 1 control */
mixerLineControls.cControls = 1;
/* Tell mixerGetLineControls() for which control we're retrieving
info. We do this by putting the desired control's ID number in
dwControlID. The "Speaker Out" line's volume slider has an ID
of 0x00000000 */
mixerLineControls.dwControlID = 0x00000000;
/* Give mixerGetLineControls() the address of the
MIXERCONTROL struct to hold info */
mixerLineControls.pamxctrl = &mixerControlArray;
/* Tell mixerGetLineControls() how big the MIXERCONTROL is. This
saves having to initialize the cbStruct of the MIXERCONTROL itself */
mixerLineControls.cbmxctrl = sizeof(MIXERCONTROL);
/* Retrieve info on only the volume slider control for the "Speaker Out" line */
if ((err = mixerGetLineControls((HMIXEROBJ)mixerHandle, &mixerLineControls, MIXER_GETLINECONTROLSF_ONEBYID)))
{
/* An error */
printf("Error #%d calling mixerGetLineControls()\n", err);
}
通过控件类型获取控件信息
通常情况下,你不必知道某个线路中所有控件的信息。或许在你的程序中你仅仅和某个类型的控件打交道。比如,假设你在写一个简单的MIDI文件回放程序,你给终端用户提供的仅仅是一个音量滑动器“Synth”。前面我们已经知道怎样通过组件的类型获取MIDI回放线路并获取此线路的信息,比如获取此线路的ID。你可以用这个线路ID来搜索此线路中某个特定类型的控件,例如,你可以寻找一个类型为MIXERCONTROL_CONTROLTYPE_VOLUME的控件。
因此,较之于通过枚举线路中所有控件直到你碰到一个类型为
MIXERCONTROL_CONTROLTYPE_VOLUME的控件的方法,mixerGetLineControls()提供的直接通过类型获取控件的方式更胜一筹。你仅仅只需要将MIXERCONTROLS的域dwControlType设定为你想要的类型,然后指定MIXER_GETLINECONTROLSF_ONEBYTYPE标志并按如下方式调用mixerGetLineControls():(假设你已经获取了线路”Synth”的ID,并存储在”SynthID”变量中)
MIXERCONTROL mixerControlArray;
MIXERLINECONTROLS mixerLineControls;
MMSYSTEM err;
mixerLineControls.cbStruct = sizeof(MIXERLINECONTROLS);
/* Tell mixerGetLineControls() for which line we're retrieving info.
We do this by putting the desired line's ID number in dwLineID */
mixerLineControls.dwLineID = SynthID;
/* We want to fetch info on only 1 control */
mixerLineControls.cControls = 1;
/* Tell mixerGetLineControls() for which type of control we're
retrieving info. We do this by putting the desired control type
in dwControlType */
mixerLineControls.dwControlType = MIXERCONTROL_CONTROLTYPE_VOLUME;
/* Give mixerGetLineControls() the address of the MIXERCONTROL
struct to hold info */
mixerLineControls.pamxctrl = &mixerControlArray;
/* Tell mixerGetLineControls() how big the MIXERCONTROL is. This
saves having to initialize the cbStruct of the MIXERCONTROL itself */
mixerLineControls.cbmxctrl = sizeof(MIXERCONTROL);
/* Retrieve info on only any volume slider control for this line */
if ((err = mixerGetLineControls((HMIXEROBJ)mixerHandle, &mixerLineControls, MIXER_GETLINECONTROLSF_ONEBYTYPE)))
{
/* An error */
printf("Error #%d calling mixerGetLineControls()\n", err);
}
系统将会使用此线路中第一个拥有MIXERCONTROL_CONTROLTYPE_VOLUME类型的控件的信息来填充MIXERCONTROL结构体。(如果此线路中没有此类型的控件,将会返回一个错误值MIXERR_INVALCONTROL)。一旦你获取到了控件的信息,就可以直接操作此控件了。例如,你可以从MIXERCONTROL的域dwControlID获取控件的ID。这样,当你想要某个类型的控件时就不用枚举所有的控件了。