1

I want bind the master sound volume of windows to a slider in my program. So I searched and found some ways to GET or SET master volume + some libraries like these:


EDIT:

Now i have the following class. i create an instance of it and use propertchange event to show volume by Trace.WriteLine. but when i change the windows volume it cause an unhandeled error!

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));
    }
}

Use the class:

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


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

Runtime Error:

......................................enter image description here

The Output panel show Access violation error:

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

Edit

I tested the above code by breakpoints and trace. the above error happens sometimes in the bellow part:

audioEndpointVolume.OnVolumeNotification += data =>
{
    VolumePercent = Convert.ToInt16(data.MasterVolume*100);
    _deviceIsMuted = data.Muted;
};

For example sometimes it happens in this line:

_deviceIsMuted = data.Muted;

But when i go to the next step by F11 it dose not show a normal error inside the program! It cause the bellow error window and application force closing!

......................................enter image description here

Access violation

Community
  • 1
  • 1
Ramin Bateni
  • 16,499
  • 9
  • 69
  • 98
  • Either way looks promising. Have you tried any of them? I think you have answered your own question, since you have found so many possible solutions. – kennyzx Dec 26 '14 at 03:38
  • @kennyzx, i don't know is there a safe way base on "Event" in .NET or i must implement it by a "Timer"...! – Ramin Bateni Dec 26 '14 at 11:06
  • @kennyzx, i added codes of my selected way. what do you think about it's error? – Ramin Bateni Dec 26 '14 at 14:03
  • You should set a breakpoint on the first line of your method and step it through to figure out what line causes the access violation. – dymanoid Dec 27 '14 at 00:57
  • @dymanoid, i updated my question with append some info about tracing the program by breakpoints and....! Now what do you think?! – Ramin Bateni Dec 27 '14 at 13:43
  • I've taken a look on the code you provided with the last link. There is an external assembly (_CoreAudioApi.dll_) which is used for the whole thing. So, I bet this access violation comes from that assembly. You could try using COM interfaces directly rather than via this lib. There is a lightweight .NET wrapper available [here](https://netcoreaudio.codeplex.com). – dymanoid Dec 27 '14 at 21:34

2 Answers2

8

You can use the NAudio library as such:

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());
}

This example uses a WinForms project, but you could also create the event elsewhere.

NAudio can be installed as a NuGet package, or from https://naudio.codeplex.com/

The volume ranges from 0.0f to 1.0f, to get 0-100 simply do:

(int)(data.MasterVolume * 100f)
steeveeet
  • 639
  • 6
  • 25
0

Does your code update a control in response to the event, say a trackbar or a mute button? If so, you may need to use a thread-safe approach. Controls can only be updated by the UI thread. Me.InvokeRequired checks to see if it's the UI thread that wants to do the update. If not, it returns True. Me.Invoke then uses a delegate to handle communication between the two threads. If you don't use Me.Invoke, there will be an access violation.

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