HMIXER mixerHandle;
err = mixerOpen(&mixerHandle, 0, 0, 0, 0);
if (err)
{
printf("ERROR: Can't open Mixer Device! -- %08Xn", 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! -- %08Xn", err);
}
else
{
err = mixerOpen(&mixerHandle, hWaveOut, 0, 0, MIXER_OBJECTF_HWAVEOUT);
if (err)
{
printf("ERROR: Can't open Mixer Device! -- %08Xn", 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! -- %08Xn", err);
}
else
{
printf("Mixer Device ID = %dn", 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: %srn", 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种关联类型:
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_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_TIME类有两种关联类型:
1)MIXERCONTROL_CONTROLTYPE_MICROTIME 一个带整数值的控制器, 最大范围从0(最低)到 4,294,967,295 。 也就是说,该值为 ULONG 类型。代表微秒数的时间。 2)MIXERCONTROL_CONTROLTYPE_MILLITIME 一个带整数值的控制器,最大范围从0(最低)到4,294,967,295。也就是说,该值为ULONG类型。代表毫秒数的时间。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就起主要作用。
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,因为这是混音器中第二个目标线路。
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 = %sn", 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);
}
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 = %sn", i, mixerline.szName);
numsrc=".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 = %sn", 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);
}
通过类型取得线路信息
通常,你不需要知道混音器中所有的线路。你的程序可能仅仅只和某个类型的线路打交道。比如,假设你正在编写一个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的错误)。一旦获取了此线路的信息,你就可以操纵它的控制器了。这样当你的需求是明确的时候,就省去了通过枚举得到某个线路的麻烦。
你可以通过混音器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的 控制器 的值)。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_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);
}
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 %lun", 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标志时,所有声道都共享同一个值。例如,对于扬声器输出线路,它是立体声线路,但是其左右声道并没有独立的音量值。
但是,若一个 控制器 的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 %lun", value[0].dwValue);
printf("The right channel's volume is %lun", 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个以上的声道。对一个给定的控制器,你必须提供足够大的结构来容纳所有的声道的值。因此,通常你必须根据需要来开辟数组空间。
一次存取一个控制器中的某几个声道是非法的。例如,一个控制器有8个声道,但是你只取其前2个声道的值,这是不允许的。你必须同时存取一个控制器的所有声道才行。但是这里有一条有关设置一个值的规则:如果你仅仅设置第一个声道的值,那么mixerSetControlDetails()自动将控制器设置MIXERCONTROL_CONTROLF_UNIFORM标志。最终结果是此控制器的所有声道都被设置为这个值。因此,你可以通过仅仅设置第一个声道的值来达到将所有的声道设置为同一个值的目的。多元素控制器
你不会常常碰到多元素的控制器。一个多元素控制器就是每个声道关联着多个值的控制器。图形化均衡器是一个例子。让我们来看一个简单的,假设一个声卡内置有以下带着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 %lun", value[0].dwValue);
printf("The Mid band is %lun", value[1].dwValue);
printf("The High band is %lun", 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 %lun", label[3 * i + n].szName, value[3 * i + n].dwValue);
}
}
}
}
一次获取或设置某个控制器的几个元素的值是不支持的。例如,若一个控制器有8个元素,你尝试仅仅获取其前2个元素的信息是不允许的。你必须同时设置/获取所有的声道的所有元素的值。关于设置元素的值,这里有一个规则:如果你仅仅设置第一个声道的元素的值,那么mixerSetControlDetails()会自动将控制器设置MIXERCONTROL_CONTROLF_UNIFORM标记。最终结果就是所有声道的所有元素的值都和第一个声道的元素的值相同。因此,你可以通过只设置第一个声道的元素的值达到快速将所有的声道的元素的值设为相同值的目的。
在上面的样例程序中,我已展示了通过mixerOpen()函数打开混音器然后将其返回值用于其它混音器函数。不一定非得这样做。实际上,混音器API被设计为可以按以下方式使用:你可以传递混音器ID,而不必将打开的混音器句柄作为混音器函数的某个参数去传递。所以,你不必显式的打开一个混音器。
但是如果你想在混音器上做某些操作,显式的打开混音器设备(通过mixerOpen()函数)还是会有一些好处的。本文发布于:2024-02-01 12:08:51,感谢您对本站的认可!
本文链接:https://www.4u4v.net/it/170676053236485.html
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。
留言与评论(共有 0 条评论) |