音频库第一部分 - (Windows 混音器控件)
从 C# 控制 Windows 混音器的库。

引言
在本文中,我将向您展示如何从 C# 使用 Windows Mixer
。
一段时间以来,我一直在尝试获取有关如何从 C# 编程 mixer
的信息。我并没有取得太多进展,而且找到的少数示例都是 C++ 的,所以……我选择了艰难而有趣的方式……自己动手。
这个库是我音频库的一部分,用于控制波形音频(Playback
/Recording
)、Mixer
、使用 ACM 播放/录制压缩文件、基本语音识别以及我将在未来文章中发布的其他一些内容。
AudioMixer 命名空间
分层对象视图

分层视图显示了类的组织方式。
主对象 Mixers
包含两个 Mixer
对象,Playback
和 Recording
;它们将使用默认设备,但可以通过设置 Mixers.Playback.DeviceId
或 Mixers.Recording.DeviceId
属性进行更改。
我通过隐藏所有原始 Win32 API 实现,并创建一个分层的对象集,使其尽可能简单。
每个 mixer
都是独立的,这意味着您可以将 Playback mixer
设置为一块声卡,而将 Recording mixer
设置为另一块声卡。此外,每个 mixer
都包含两个 MixerLines
数组(Lines
、UserLines
)。
Lines
对象将包含 mixer
中的所有线路,例如所有没有关联控件的线路,或者不是源线路的线路。
UserLines
将包含开发人员可以与之交互的所有线路,例如具有 Volume
、Mute
、Fadder
、Bass
等控件的线路(基本上它与 Lines
对象相同,但对其应用了过滤器)。
每个 Line
都将包含一个控件集合,例如 Mute
、Volume
、Bass
、Fader
等。
如果您有兴趣了解更多关于 Windows Mixer
的工作原理,这里有一个非常棒的链接,包含所有相关信息。
让我们看一些代码
获取计算机上的音频设备
foreach(MixerDetail mixerDetail in mMixers.Devices)
{
...
...
}
获取输入设备
foreach(MixerDetail mixerDetail in mMixers.Recording.Devices)
{
...
...
}
更改输出混音器设备
Mixers mixers = new Mixers();
foreach(MixerDetail mixerDetail in mixers.Playback.Devices)
{
if (mixerDetail.MixerName == "Sound Blaster Live")
mixers.Playback.DeviceId = mixerDetail.DeviceId;
}
将输出混音器设备更改为默认设备
Mixers mixers = new Mixers();
mixers.Playback.DeviceId = -1;
或
mixers.Playback.DeviceId = mixers.Playback.DeviceIdDefault;
获取播放扬声器主音量
mixers.Playback.Lines.GetMixerFirstLineByComponentType(
MIXERLINE_COMPONENTTYPE.DST_SPEAKERS).Volume;
仅为左声道设置播放扬声器主音量
MixerLine line = mixers.Playback.Lines.GetMixerFirstLineByComponentType(
MIXERLINE_COMPONENTTYPE.DST_SPEAKERS);
line.Channel = Channel.Left;
line.Volume = 32000;
将麦克风选为默认输入
mixers.Recording.Lines.GetMixerFirstLineByComponentType(
MIXERLINE_COMPONENTTYPE.SRC_MICROPHONE).Selected = true;
获取线路更改时的回调通知
/* Initialization */
mMixers = new Mixers();
mMixers.Playback.MixerLineChanged +=
new WaveLib.AudioMixer.Mixer.MixerLineChangeHandler(mMixer_MixerLineChanged);
mMixers.Recording.MixerLineChanged +=
new WaveLib.AudioMixer.Mixer.MixerLineChangeHandler(mMixer_MixerLineChanged);
/* Events */
private void mMixer_MixerLineChanged(Mixer mixer, MixerLine line)
{
Console.WriteLine("Mixer: " + mixer.DeviceDetail.MixerName);
Console.WriteLine("Mixer Type: " + mixer.MixerType);
Console.WriteLine("Mixer Line: " + line.Name);
}
获取和设置不常用控件
可以使用 MixerControl
对象通过 ValueAsSigned
、ValueAsUnsigned
和 ValueAsBoolean
属性访问特定的控件,如 Fadder、麦克风增强、低音、高音等,但它们并未作为标准属性实现,因为它们不属于所有控件。
x86 与 x64
终于我得到了一个 Core 2 Duo,现在我可以在 x86 和 x64 下编译这个库了。起初我以为不会有太大区别,但最大的问题是 IntPtr
指针是平台特定的,所以 x86 指针是 4 字节,而 x64 指针是 8 字节,这使得事情非常混乱,而且在某些情况下没有简单的解决方法,例如在以下结构中,我找不到一种方法使其在 C# 中同时适用于 x86 和 x64。看起来 .NET Framework 在处理结构体内的联合体方面缺少一些东西……我不得不声明两个结构体并分开处理它们。
以前
[StructLayout(LayoutKind.Explicit, CharSet = CharSet.Auto)]
public struct MIXERCONTROLDETAILS
{
[FieldOffset(0)] public UInt32 cbStruct;
[FieldOffset(4)] public UInt32 dwControlID;
[FieldOffset(8)] public UInt32 cChannels;
/* Union start */
[FieldOffset(12)] public IntPtr hwndOwner;
[FieldOffset(12)] public UInt32 cMultipleItems;
/* Union end */
[FieldOffset(16)] public UInt32 cbDetails;
[FieldOffset(20)] public IntPtr paDetails;
}
操作后
[StructLayout(LayoutKind.Explicit, CharSet = CharSet.Unicode, Pack = 2)]
public struct MIXERCONTROLDETAILS
{
[FieldOffset(0)] public UInt32 cbStruct;
[FieldOffset(4)] public UInt32 dwControlID;
[FieldOffset(8)] public UInt32 cChannels;
/* Union start */
[FieldOffset(12)] public IntPtr hwndOwner;
[FieldOffset(12)] public UInt32 cMultipleItems;
/* Union end */
[FieldOffset(16)] public UInt32 cbDetails;
[FieldOffset(20)] public IntPtr paDetails;
}
[StructLayout(LayoutKind.Explicit, CharSet = CharSet.Unicode, Pack = 2)]
public struct MIXERCONTROLDETAILS64
{
[FieldOffset(0)] public UInt32 cbStruct;
[FieldOffset(4)] public UInt32 dwControlID;
[FieldOffset(8)] public UInt32 cChannels;
/* Union start */
[FieldOffset(12)] public IntPtr hwndOwner;
[FieldOffset(12)] public UInt32 cMultipleItems;
/* Union end */
[FieldOffset(20)] public UInt32 cbDetails;
[FieldOffset(24)] public IntPtr paDetails;
}
我曾希望声明一个动态大小的 FieldOffset
,然后我就可以解决这个问题了,但它在 .NET 中无法编译。
[StructLayout(LayoutKind.Explicit, CharSet = CharSet.Unicode, Pack = 2)]
public struct MIXERCONTROLDETAILS
{
[FieldOffset(0)] public UInt32 cbStruct;
[FieldOffset(4)] public UInt32 dwControlID;
[FieldOffset(8)] public UInt32 cChannels;
/* Union start */
[FieldOffset(12)] public IntPtr hwndOwner;
[FieldOffset(12)] public UInt32 cMultipleItems;
/* Union end */
[FieldOffset(12 + IntPtr.Size)] public UInt32 cbDetails;
[FieldOffset(12 + IntPtr.Size + 4)] public IntPtr paDetails;
}
注释
目前所有的功能都经过测试并且可以工作,我尽量不包含错误,但它们确实存在,我时不时地会发现它们。如果您发现错误,我将非常感谢您提供反馈来更新这篇文章。
目前我不需要库中的其他任何东西,但如果您想到一些未包含在其中但可以使其更好的功能,请告诉我,我会尽力将其包含进去。
如果您在使用过程中遇到问题,请随时给我发送电子邮件。