65.9K
CodeProject 正在变化。 阅读更多。
Home

音频库第一部分 - (Windows 混音器控件)

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.52/5 (79投票s)

2005 年 9 月 19 日

CPOL

3分钟阅读

viewsIcon

636278

downloadIcon

15503

从 C# 控制 Windows 混音器的库。

Sample Image - AudioLib.jpg

引言

在本文中,我将向您展示如何从 C# 使用 Windows Mixer

一段时间以来,我一直在尝试获取有关如何从 C# 编程 mixer 的信息。我并没有取得太多进展,而且找到的少数示例都是 C++ 的,所以……我选择了艰难而有趣的方式……自己动手。

这个库是我音频库的一部分,用于控制波形音频(Playback/Recording)、Mixer、使用 ACM 播放/录制压缩文件、基本语音识别以及我将在未来文章中发布的其他一些内容。

AudioMixer 命名空间

分层对象视图

Sample screenshot

分层视图显示了类的组织方式。

主对象 Mixers 包含两个 Mixer 对象,PlaybackRecording;它们将使用默认设备,但可以通过设置 Mixers.Playback.DeviceIdMixers.Recording.DeviceId 属性进行更改。

我通过隐藏所有原始 Win32 API 实现,并创建一个分层的对象集,使其尽可能简单。

每个 mixer 都是独立的,这意味着您可以将 Playback mixer 设置为一块声卡,而将 Recording mixer 设置为另一块声卡。此外,每个 mixer 都包含两个 MixerLines 数组(LinesUserLines)。

Lines 对象将包含 mixer 中的所有线路,例如所有没有关联控件的线路,或者不是源线路的线路。

UserLines 将包含开发人员可以与之交互的所有线路,例如具有 VolumeMuteFadderBass 等控件的线路(基本上它与 Lines 对象相同,但对其应用了过滤器)。

每个 Line 都将包含一个控件集合,例如 MuteVolumeBassFader 等。

如果您有兴趣了解更多关于 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 对象通过 ValueAsSignedValueAsUnsignedValueAsBoolean 属性访问特定的控件,如 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;
 }

注释

目前所有的功能都经过测试并且可以工作,我尽量不包含错误,但它们确实存在,我时不时地会发现它们。如果您发现错误,我将非常感谢您提供反馈来更新这篇文章。

目前我不需要库中的其他任何东西,但如果您想到一些未包含在其中但可以使其更好的功能,请告诉我,我会尽力将其包含进去。

如果您在使用过程中遇到问题,请随时给我发送电子邮件。

© . All rights reserved.