将主音频音量的变化确定为事件(音量变化监听器)

Posted

技术标签:

【中文标题】将主音频音量的变化确定为事件(音量变化监听器)【英文标题】:Determine changes of master audio volume as an event (volume change listener) 【发布时间】:2014-12-25 21:41:15 【问题描述】:

我想将窗口的主音量绑定到我程序中的滑块。所以我搜索并找到了一些方法来GETSET主卷+一些像这样的库:

Change master audio volume from XP to Windows 8 in C# Handling volume control in C#

NAudio

在某些地方我看到一个带有 LOOP 的代码来获取音量值:loop

有些地方我看到一个带有 TIMER 的代码来获取音量值... 我还看到了一些用于确定音量的示例,但在测试其中一个之后,我在 Windows 8 运行时看到了一些错误:C# – Adjust master volume in Vista and Windows 7

编辑:

现在我有以下课程。我创建它的一个实例并使用propertchange 事件通过 Trace.WriteLine 显示音量。但是当我更改 Windows 音量时,它会导致一个未处理的错误!

public class AudioEndpointVolumeEnforcer : INotifyPropertyChanged

    private MMDeviceEnumerator mmDeviceEnumerator;
    private MMDevice mmDevice;
    private AudioEndpointVolume audioEndpointVolume;
    private bool _deviceIsMuted;
    private int _desiredVolume;
    private int _volumePercent;

    public AudioEndpointVolumeEnforcer()
    
        try
        
            mmDeviceEnumerator = new MMDeviceEnumerator();
            mmDevice = mmDeviceEnumerator.GetDefaultAudioEndpoint(EDataFlow.eRender, ERole.eMultimedia);
            audioEndpointVolume = mmDevice.AudioEndpointVolume;
            audioEndpointVolume.OnVolumeNotification += data =>
            
                VolumePercent = Convert.ToInt16(data.MasterVolume*100);
                _deviceIsMuted = data.Muted;
            ;
            DesiredVolume = 65;
        
        catch (Exception ex)
        
            // Logging logic here
        
    

    public int DesiredVolume
    
        get  return _desiredVolume; 
        private set
        
            if (_desiredVolume == value) return;
            _desiredVolume = value;

            //NotifyOfPropertyChange();
            OnPropertyChanged("DesiredVolume");

            Enforce(_desiredVolume);
        
    


    public int VolumePercent
    
        get  return _volumePercent; 
        private set
        
            if (_volumePercent == value) return;
            _volumePercent = value;
            if (_volumePercent != _desiredVolume)
            
                _volumePercent = _desiredVolume;
                Enforce(_volumePercent);
            
        
    

    public void Enforce(int pct, bool mute = false)
    
        var adjusted = Convert.ToInt16(audioEndpointVolume.MasterVolumeLevelScalar*100);
        if (adjusted != DesiredVolume)
        
            audioEndpointVolume.MasterVolumeLevelScalar = pct/100f;
        
    

    public event PropertyChangedEventHandler PropertyChanged;

    [NotifyPropertyChangedInvocator]
    protected virtual void OnPropertyChanged(string propertyName)
    
        PropertyChangedEventHandler handler = PropertyChanged;
        if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
    

使用类:

 // Inside my window cunstractor >>
 audioVolume = new AudioEndpointVolumeEnforcer();
 audioVolume.PropertyChanged += MasterAudioVolumeChanged;


 private void MasterAudioVolumeChanged(object obj, PropertyChangedEventArgs eventArgs)
 
   Trace.WriteLine(eventArgs.PropertyName+" - "+audioVolume.DesiredVolume);
 

运行时错误:

.......................

输出面板显示Access violation 错误:

The program '[18488] Audio.exe' has exited with code -1073741819 (0xc0000005) 'Access violation'

编辑

我通过断点和跟踪测试了上面的代码。上述错误有时发生在以下部分:

audioEndpointVolume.OnVolumeNotification += data =>

    VolumePercent = Convert.ToInt16(data.MasterVolume*100);
    _deviceIsMuted = data.Muted;
;

例如有时它发生在这一行:

_deviceIsMuted = data.Muted;

但是当我通过F11进入下一步时程序内部没有显示正常错误!它导致波纹管错误窗口和应用程序强制关闭!

.......................

Access violation

【问题讨论】:

无论哪种方式看起来都有希望。你尝试过其中任何一个吗?我想你已经回答了你自己的问题,因为你已经找到了很多可能的解决方案。 @kennyzx,我不知道.NET 中是否有基于“事件”的安全方法,或者我必须通过“计时器”来实现它......! @kennyzx,我添加了我选择的方式的代码。你怎么看它的错误? 你应该在你的方法的第一行设置一个断点,并逐步找出导致访问冲突的行。 @dymanoid,我更新了我的问题,添加了一些关于通过断点跟踪程序的信息......!现在你怎么看?! 【参考方案1】:

您可以这样使用 NAudio 库:

using NAudio;
using NAudio.CoreAudioApi;

private static MMDeviceEnumerator enumer = new MMDeviceEnumerator();
private MMDevice dev = enumer.GetDefaultAudioEndpoint(DataFlow.Render, Role.Multimedia);

public void Form1_Load(object sender, EventArgs e)
    dev.AudioEndpointVolume.OnVolumeNotification += AudioEndpointVolume_OnVolumeNotification;


void AudioEndpointVolume_OnVolumeNotification(AudioVolumeNotificationData data)

    // This shows data.MasterVolume, you can do whatever you want here
    MessageBox.Show(data.MasterVolume.ToString());

此示例使用 WinForms 项目,但您也可以在其他地方创建事件。

NAudio 可以作为 NuGet 包安装,也可以从 https://naudio.codeplex.com/ 安装

音量范围从 0.0f 到 1.0f,要得到 0-100 只需这样做:

(int)(data.MasterVolume * 100f)

【讨论】:

【参考方案2】:

您的代码是否会更新控件以响应事件,例如轨迹栏或静音按钮?如果是这样,您可能需要使用线程安全的方法。控件只能由 UI 线程更新。 Me.InvokeRequired 检查是否是 UI 线程想要进行更新。如果不是,则返回 True。 Me.Invoke 然后使用委托来处理两个线程之间的通信。如果不使用 Me.Invoke,会出现访问冲突。

Delegate Sub UpdateCallback(Volume As Integer, Muted As Boolean)
Public Overloads Sub Update(Volume As Integer, Muted As Boolean)
    If tbVolume.InvokeRequired Then
        Dim d As New UpdateCallback(AddressOf Update)
        Me.Invoke(d, Volume, Muted)
    Else
        tbVolume.Value = Volume
        _Mute = Muted
        btnMuteUnmute.BackgroundImage = DirectCast(If(_Mute, _
           My.Resources.mute, My.Resources.Unmute), Icon).ToBitmap
    End If
End Sub

【讨论】:

以上是关于将主音频音量的变化确定为事件(音量变化监听器)的主要内容,如果未能解决你的问题,请参考以下文章

手机影音第十天,控制屏幕上下滑动改变音量变化,监听物理键改变音量

OpenHarmony音量管理开发

Swift:监听 iOS 设备上的物理按钮向上/向下事件

如何注册 ContentObserver 以更改媒体音量?

iOS-蓝牙拍照

Flutter监听音量实现波形可视化的音频