【问题标题】:NAudio - Cannot set line control mute valueNAudio - 无法设置线路控制静音值
【发布时间】:2012-06-20 00:17:23
【问题描述】:

我正在使用 NAudio 完成一些任务:

  1. 找到“立体声混音”源代码行
  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");
    }
}

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

【问题讨论】:

    标签: c# naudio


    【解决方案1】:

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

    /// <summary>
    /// The current value of the control
    /// </summary>
    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 中。

    【讨论】:

    • 当然。我现在正在努力。您希望我如何提交补丁?我找不到你的联系方式。
    【解决方案2】:

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

    /// <summary>
    /// The current value of the control
    /// </summary>
    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);
        }
    

    这对我来说似乎很好用

    【讨论】:

    • Van Thilo - 感谢您在 StackOverflow 上的第一个回答。如果您能解释您从问题中改变了什么,以及为什么您的方法解决了您在他们的代码中看到的问题,那将会很有帮助。这样我们不只是得到答案,我们都向你学习!
    • 我很抱歉没有解释,我希望现在一切都清楚了!
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多