NAudio – 无法设置线路控制静音值

我正在使用NAudio做一些任务:

  1. 找到“Stereo Mix”源代码行
  2. 通过启用任何线控制来取消静音“立体声混音”源线
  3. 通过禁用存在的任何行控件,使同一输入设备上的所有其他源行静音

我编写的程序可以执行任务1,但任务2和3失败。

具体来说,这段代码导致抛出ArgumentException:

if( control.IsBoolean ) { BooleanMixerControl boolControl = (BooleanMixerControl)control; boolControl.Value = isMuted; set = true; if( boolControl.Value != isMuted ) throw new ArgumentException("Could not set line muted value."); } 

这是我用来执行这些任务的静态类。 它依赖于当前版本的NAudio:

 public static class RecordSourceManager { public static Boolean GetMicrophoneMuted(String deviceName) { Mixer mixer = GetMixer( deviceName ); if( mixer == null ) throw new ArgumentException("Specified device \"" + deviceName + "\" does not exist."); foreach(MixerLine line in mixer.Destinations) { foreach(MixerLine sourceLine in line.Sources) { if( sourceLine.ComponentType == MixerLineComponentType.SourceMicrophone ) { return GetLineMuted( sourceLine ); } } } throw new ArgumentException("Specified device \"" + deviceName + "\" does not contain a microphone device."); } public static void SetMicrophoneExclusive(String deviceName, Boolean enableMicrophoneExclusivity) { Mixer mixer = GetMixer( deviceName ); if( mixer == null ) throw new ArgumentException("Specified device \"" + deviceName + "\" does not exist."); foreach(MixerLine line in mixer.Destinations) { foreach(MixerLine sourceLine in line.Sources) { if( sourceLine.ComponentType == MixerLineComponentType.SourceMicrophone ) { SetLineMuted( sourceLine, !enableMicrophoneExclusivity ); } else { SetLineMuted( sourceLine, enableMicrophoneExclusivity ); } } } } public static Boolean GetStereoMixMuted(String deviceName) { Mixer mixer = GetMixer( deviceName ); if( mixer == null ) throw new ArgumentException("Specified device \"" + deviceName + "\" does not exist."); foreach(MixerLine line in mixer.Destinations) { foreach(MixerLine sourceLine in line.Sources) { if( IsStereoMix( sourceLine.Name ) ) { return GetLineMuted( sourceLine ); } } } throw new ArgumentException("Specified device \"" + deviceName + "\" does not contain a microphone device."); } public static void SetStereoMixExclusive(String deviceName, Boolean enableStereoMixExclusivity) { Mixer mixer = GetMixer( deviceName ); if( mixer == null ) throw new ArgumentException("Specified device \"" + deviceName + "\" does not exist."); MixerLine stereoMix; MixerLine parentLine; GetStereoMixLine( mixer, out stereoMix, out parentLine ); if( stereoMix == null ) throw new ArgumentException("Specified device \"" + deviceName + "\" does not contain a Stereo Mix line."); foreach(MixerLine source in parentLine.Sources) { Boolean ok; if( IsStereoMix( source.Name ) ) { ok = SetLineMuted( source, !enableStereoMixExclusivity ); } else { ok = SetLineMuted( source, enableStereoMixExclusivity ); } if( !ok ) throw new ArgumentException("Could not set line muted state."); } } private static Mixer GetMixer(String deviceName) { foreach(Mixer mixer in Mixer.Mixers) { //wtr.WriteLine("Mixer: {0}, Mfg: {1}", mixer.Name, mixer.Manufacturer ); if( String.Equals( mixer.Name, deviceName, StringComparison.OrdinalIgnoreCase ) ) return mixer; } return null; } private static void GetStereoMixLine(Mixer device, out MixerLine stereoMix, out MixerLine parentLine) { foreach(MixerLine line in device.Destinations) { foreach(MixerLine source in line.Sources) { if( IsStereoMix( source.Name ) ) { stereoMix = source; parentLine = line; return; } } } stereoMix = null; parentLine = null; } private static Boolean IsStereoMix(String sourceName) { String[] names = new String[] { "Stereo Mix", "What U Hear", "What \"U\" Hear", "What-U-Hear", "Playback Redirect", "Wave Out", "Wave Out Mix", "Wave-Out Mix" }; foreach(String name in names) { if( String.Equals( sourceName, name, StringComparison.OrdinalIgnoreCase ) ) return true; } return false; } private static Boolean SetLineMuted(MixerLine line, Boolean isMuted) { Boolean set = false; foreach(MixerControl control in line.Controls) { // Can't test if control's name == "Mute" because sometimes it's "Mic Volume" (even though it's boolean). Same goes for GetLineMuted. if( control.IsBoolean ) { BooleanMixerControl boolControl = (BooleanMixerControl)control; boolControl.Value = isMuted; set = true; if( boolControl.Value != isMuted ) throw new ArgumentException("Could not set line muted value."); } } return set; } private static Boolean GetLineMuted(MixerLine line) { foreach(MixerControl control in line.Controls) { if( control.IsBoolean ) { BooleanMixerControl boolControl = (BooleanMixerControl)control; return boolControl.Value; } } return false; } } 

我想我会看一下NAudio的BooleanMixerControl类,我看到了这个:

 public bool Value { get { base.GetControlDetails(); return (this.boolDetails.fValue == 1); } set { MmException.Try(MixerInterop.mixerSetControlDetails(base.mixerHandle, ref this.mixerControlDetails, base.mixerHandleType), "mixerSetControlDetails"); } } 

有趣的是,似乎忽略了属性setter的value参数,因此mixerSetControlDetails调用将不会做任何有用的工作。 这是NAudio中的一个错误吗?

此function未实现,可能应替换为NotImplementedException 。 它是2002年我为NAudio写的第一个代码之一,当时我刚开始学习PInvoke和pinning,如果你看看NAudio源,你会看到使用该值的代码被注释掉,大概是因为它我最初尝试时会导致某种内存exception。

 ///  /// The current value of the control ///  public bool Value { get { GetControlDetails(); // make sure we have the latest value return (boolDetails.fValue == 1); } set { //GetControlDetails(); //MixerInterop.MIXERCONTROLDETAILS_BOOLEAN boolDetails = (MixerInterop.MIXERCONTROLDETAILS_BOOLEAN) Marshal.PtrToStructure(mixerControlDetails.paDetails,typeof(MixerInterop.MIXERCONTROLDETAILS_BOOLEAN)); //boolDetails.fValue = (value) ? 1 : 0; // TODO: pin the memory MmException.Try(MixerInterop.mixerSetControlDetails(mixerHandle, ref mixerControlDetails, MixerFlags.Value | mixerHandleType), "mixerSetControlDetails"); } } 

它从未被修复的原因是我自己从未需要使用它,但如果有人想要提供修复,我很乐意将它包含在NAudio中。

我也有这个问题。 除了Mark Heaths的回复,我调整了BooleanMixerControl的属性Value来检索正确的值:

 ///  /// The current value of the control ///  public bool Value { get { GetControlDetails(); // make sure we have the latest value return (boolDetails.fValue == 1); } set { int structSize = Marshal.SizeOf(boolDetails); mixerControlDetails.paDetails = Marshal.AllocHGlobal(structSize * nChannels); for (int channel = 0; channel < nChannels; channel++) { boolDetails.fValue = value ? 1 : 0; long pointer = mixerControlDetails.paDetails.ToInt64() + (structSize * channel); Marshal.StructureToPtr(boolDetails, (IntPtr)pointer, false); } MmException.Try(MixerInterop.mixerSetControlDetails(mixerHandle, ref mixerControlDetails, MixerFlags.Value | mixerHandleType), "mixerSetControlDetails"); Marshal.FreeHGlobal(mixerControlDetails.paDetails); } 

这似乎对我来说很好