Handling changed audio device event in c#
Asked Answered
B

4

8

I'm wondering how to process the event when I insert (or extract) my headphones or another output device to soundcard jack.

Searching here and on google gives me information about "naudio" library, but it has very poor documentation to examine and also one of coordinators of this project told me he isn't sure than it even possible in their library.

My eventual purpose is automatic controlling volume for different devices, e.g. when headphones are active - set 10% volume, and when speakers are active - set 100%.

Benzaldehyde answered 28/5, 2011 at 17:8 Comment(0)
P
21

You can do it using NAudio's MMDeviceEnumerator and IMMNotificationClient. However you add Implementations for RegisterEndpointNotificationCallback & UnRegisterEndpointNotificationCallback to MMDeviceEnumerator Class

The implementations are

 /// <summary>
       /// Registers a call back for Device Events
       /// </summary>
        /// <param name="client">Object implementing IMMNotificationClient type casted as IMMNotificationClient interface</param>
       /// <returns></returns>
        public int RegisterEndpointNotificationCallback([In] [MarshalAs(UnmanagedType.Interface)] IMMNotificationClient client)
        {
            //DeviceEnum declared below
            return deviceEnum.RegisterEndpointNotificationCallback(client);
        }

        /// <summary>
        /// UnRegisters a call back for Device Events
        /// </summary>
        /// <param name="client">Object implementing IMMNotificationClient type casted as IMMNotificationClient interface </param>
        /// <returns></returns>
        public int UnRegisterEndpointNotificationCallback([In] [MarshalAs(UnmanagedType.Interface)] IMMNotificationClient client)
        {
            //DeviceEnum declared below
            return deviceEnum.UnregisterEndpointNotificationCallback(client);
        } 

Then create a class that implements IMMNotificationClient

sample:

class NotificationClientImplementation : NAudio.CoreAudioApi.Interfaces.IMMNotificationClient
    {

        public void OnDefaultDeviceChanged(DataFlow dataFlow, Role deviceRole, string defaultDeviceId)
        {
            //Do some Work
            Console.WriteLine("OnDefaultDeviceChanged --> {0}", dataFlow.ToString());
        }

        public void OnDeviceAdded(string deviceId)
        {
             //Do some Work
            Console.WriteLine("OnDeviceAdded -->");
        }

        public void OnDeviceRemoved(string deviceId)
        {

            Console.WriteLine("OnDeviceRemoved -->");
             //Do some Work
        }

        public void OnDeviceStateChanged(string deviceId, DeviceState newState)
        {
            Console.WriteLine("OnDeviceStateChanged\n Device Id -->{0} : Device State {1}", deviceId, newState);
             //Do some Work
        }

        public NotificationClientImplementation()
        {
            //_realEnumerator.RegisterEndpointNotificationCallback();
            if (System.Environment.OSVersion.Version.Major < 6)
            {
                throw new NotSupportedException("This functionality is only supported on Windows Vista or newer.");
            }
        }

        public void OnPropertyValueChanged(string deviceId, PropertyKey propertyKey)
        {
             //Do some Work
             //fmtid & pid are changed to formatId and propertyId in the latest version NAudio
            Console.WriteLine("OnPropertyValueChanged: formatId --> {0}  propertyId --> {1}", propertyKey.formatId.ToString(), propertyKey.propertyId.ToString());
        }

    }

Then all you have to do is

  1. Declare the following NAudio Objects and your implementation of IMMNotificationClient

Sample:

private NAudio.CoreAudioApi.MMDeviceEnumerator deviceEnum = new NAudio.CoreAudioApi.MMDeviceEnumerator();
private NotificationClientImplementation notificationClient;
private NAudio.CoreAudioApi.Interfaces.IMMNotificationClient notifyClient;
  1. Then type cast notificationClient as IMMNotificationClient and pass it as a parameter to MMDeviceEnumerator

Sample:

notificationClient = new NotificationClientImplementation();
notifyClient = (NAudio.CoreAudioApi.Interfaces.IMMNotificationClient)notificationClient;
deviceEnum.RegisterEndpointNotificationCallback(notifyClient);

Hope this helps some body. Thanks to MSDN Forums and particularly Michael Taylor http://msmvps.com/blogs/p3net for helping me with this.

Thanks & Cheers.

Preferential answered 12/2, 2014 at 9:37 Comment(3)
Thank you very much for the answer! Unfortunately it's been 3 years since i've asked it and i quit researching the subject long ago. But i hope it will help others to solve the same problem.Benzaldehyde
@Preferential Thank you so much! I have been searching for hours for an implementation sample! I would thumbs up this 100 times for the well detailed answer with samples!!Ringent
I use the same way described here, I only see the onProperyChange Event run when I plug the headphone jack, I dont see anything when I unplug it any reason for that?Danube
S
4

You will be able to determine when a device is plugged into the system, you will have to implement the IMMNotificationClient through COM interop. Basically, you will have to define the implementations of the following methods:

Note that of the above, the ones you are mostly interested in are:

  • OnDefaultDeviceChanged
  • OnDeviceAdded
  • OnDeviceStateChanged

However, you should be aware that the underlying hardware has to support this functionality, and that this is only available on Windows Vista and Windows Server 2008 on.

Sambar answered 28/5, 2011 at 17:13 Comment(4)
Ok, it will be enough for me to receive message about "something" plugged or unplugged. How is this possible? I also tried another way - to search this event in windows event viewer, but i've got nothing - may be i was searching not so good, or may be this event just doesn't record there.Benzaldehyde
@xapon: Unfortunately, I said "at best" you would be able to get that information. If you do, it will be specific to the soundcard or motherboard (if the jack is integrated); I'm not sure that there is a standard event that the hardware abstraction layer exposes that you can pick up on.Sambar
I think it can be resolved at low-level; for example, here is windows interface for tracking audio events msdn.microsoft.com/en-us/library/dd371417(v=vs.85).aspx. So if i'll go deeper, i'll find the way to do that. But i hoped that there is already some abstraction like library or class that could help me. Anyway thanks for explanation.Benzaldehyde
@xapon - Expanded my answer, ultimately, you have to use COM interop to implement this device and then register your implementation.Sambar
A
3

I modified @randall-deetz code above to make it self contained and you can drop this right into any application using NAudio. Thanks for a great sample, it really helped me with my project!

internal class AudioDeviceChangeNotifier : NAudio.CoreAudioApi.Interfaces.IMMNotificationClient, IDisposable
{
    private NAudio.CoreAudioApi.MMDeviceEnumerator _deviceEnum = new NAudio.CoreAudioApi.MMDeviceEnumerator();

    public AudioDeviceChangeNotifier()
    {
        if (System.Environment.OSVersion.Version.Major < 6)
            throw new NotSupportedException("This functionality is only supported on Windows Vista or newer.");

        _deviceEnum.RegisterEndpointNotificationCallback(this);
    }

    public delegate void DefaultDeviceChangedHandler(DataFlow dataFlow, Role deviceRole, string defaultDeviceId);
    /// <summary>
    /// Raised when the default audio device is changed. 
    /// </summary>
    public event DefaultDeviceChangedHandler DefaultDeviceChanged;

    /// <summary>
    ///  Triggered by NAudio.CoreAudioApi.MMDeviceEnumerator when the default device changes. 
    /// </summary>
    /// <param name="dataFlow"></param>
    /// <param name="deviceRole"></param>
    /// <param name="defaultDeviceId"></param>
    public void OnDefaultDeviceChanged(DataFlow dataFlow, Role deviceRole, string defaultDeviceId)
    {
        Debug.WriteLine($"AudioDeviceChangeNotifier::OnDefaultDeviceChanged - dataFlow: {dataFlow}, deviceRole: {deviceRole}, defaultDeviceId: {defaultDeviceId}");

        if (DefaultDeviceChanged != null)
            DefaultDeviceChanged(dataFlow, deviceRole, defaultDeviceId);
    }

    public delegate void DeviceAddedHandler(string deviceId);
    /// <summary>
    /// Raised when a new audio device is added.
    /// </summary>
    public event DeviceAddedHandler DeviceAdded;

    /// <summary>
    /// Triggered by NAudio.CoreAudioApi.MMDeviceEnumerator when an audio device is added. 
    /// </summary>
    /// <param name="deviceId"></param>
    public void OnDeviceAdded(string deviceId)
    {
        Debug.WriteLine($"AudioDeviceChangeNotifier::OnDeviceAdded - deviceId: {deviceId}");

        if (DeviceAdded != null)
            DeviceAdded(deviceId);
    }

    public delegate void DeviceRemovedHandler(string deviceId);
    /// <summary>
    /// Raised when an audio device is removed.
    /// </summary>
    public event DeviceRemovedHandler DeviceRemoved;

    /// <summary>
    /// Triggered by NAudio.CoreAudioApi.MMDeviceEnumerator when an audio device is removed. 
    /// </summary>
    /// <param name="deviceId"></param>
    public void OnDeviceRemoved(string deviceId)
    {
        Debug.WriteLine($"AudioDeviceChangeNotifier::OnDeviceRemoved - deviceId: {deviceId}");

        if (DeviceAdded != null)
            DeviceAdded(deviceId);
    }

    public delegate void DeviceStateChangedHandler(string deviceId, DeviceState newState);
    /// <summary>
    /// Raised when an audio device's state is changed.
    /// </summary>
    public event DeviceStateChangedHandler DeviceStateChanged;

    /// <summary>
    /// Triggered by NAudio.CoreAudioApi.MMDeviceEnumerator when an audio device's state is changed. 
    /// </summary>
    /// <param name="deviceId"></param>
    /// <param name="newState"></param>
    public void OnDeviceStateChanged(string deviceId, DeviceState newState)
    {
        Debug.WriteLine($"AudioDeviceChangeNotifier::OnDeviceStateChanged - deviceId: {deviceId}, newState: {newState}");

        if (DeviceStateChanged != null)
            DeviceStateChanged(deviceId, newState);
    }
    
    public delegate void PropertyValueChangedHandler(string deviceId);
    /// <summary>
    /// Raised when a property value is changed.
    /// </summary>
    public event PropertyValueChangedHandler PropertyValueChanged;

    /// <summary>
    /// Triggered by NAudio.CoreAudioApi.MMDeviceEnumerator when an audio device's property is changed. 
    /// </summary>
    /// <param name="deviceId"></param>
    /// <param name="propertyKey"></param>
    public void OnPropertyValueChanged(string deviceId, PropertyKey propertyKey)
    {
        Debug.WriteLine($"AudioDeviceChangeNotifier::OnPropertyValueChanged - deviceId: {deviceId}, propertyKey: {propertyKey}");

        if (PropertyValueChanged != null)
            PropertyValueChanged(deviceId);
    }

    public void Dispose()
    {
        _deviceEnum.UnregisterEndpointNotificationCallback(this);
    }
}
Assiduity answered 17/12, 2023 at 23:46 Comment(0)
C
-2

I was pretty sure that plugging/unplugging headphones or anything else in audiocard doesn't generate any system event whatsoever

Christianize answered 28/5, 2011 at 17:13 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.