/src/AudioSwitcher/AudioSwitcher/Audio/AudioDeviceManager.cs

https://gitlab.com/Ianvdl/audio-switcher · C# · 173 lines · 118 code · 26 blank · 29 comment · 24 complexity · cc534cb120bcabf3812af8227c12c80f MD5 · raw file

  1. // -----------------------------------------------------------------------
  2. // Copyright (c) David Kean.
  3. // -----------------------------------------------------------------------
  4. // This source file was altered for use in AudioSwitcher.
  5. /*
  6. LICENSE
  7. -------
  8. Copyright (C) 2007 Ray Molenkamp
  9. This source code is provided 'as-is', without any express or implied
  10. warranty. In no event will the authors be held liable for any damages
  11. arising from the use of this source code or the software it produces.
  12. Permission is granted to anyone to use this source code for any purpose,
  13. including commercial applications, and to alter it and redistribute it
  14. freely, subject to the following restrictions:
  15. 1. The origin of this source code must not be misrepresented; you must not
  16. claim that you wrote the original source code. If you use this source code
  17. in a product, an acknowledgment in the product documentation would be
  18. appreciated but is not required.
  19. 2. Altered source versions must be plainly marked as such, and must not be
  20. misrepresented as being the original source code.
  21. 3. This notice may not be removed or altered from any source distribution.
  22. */
  23. using System;
  24. using System.ComponentModel.Composition;
  25. using System.Runtime.InteropServices;
  26. using AudioSwitcher.Interop;
  27. namespace AudioSwitcher.Audio
  28. {
  29. [Export(typeof(AudioDeviceManager))]
  30. internal class AudioDeviceManager : IMMNotificationClient, IDisposable
  31. {
  32. private readonly IMMDeviceEnumerator _underlyingEnumerator = (IMMDeviceEnumerator)new MMDeviceEnumeratorComObject();
  33. public AudioDeviceManager()
  34. {
  35. int hr = _underlyingEnumerator.RegisterEndpointNotificationCallback(this);
  36. if (hr != HResult.OK)
  37. throw Marshal.GetExceptionForHR(hr);
  38. }
  39. public event EventHandler<AudioDeviceEventArgs> DeviceAdded;
  40. public event EventHandler<AudioDeviceRemovedEventArgs> DeviceRemoved;
  41. public event EventHandler<AudioDeviceEventArgs> DevicePropertyChanged;
  42. public event EventHandler<DefaultAudioDeviceEventArgs> DefaultDeviceChanged;
  43. public event EventHandler<AudioDeviceStateEventArgs> DeviceStateChanged;
  44. public AudioDeviceCollection GetAudioDevices(AudioDeviceKind kind, AudioDeviceState state)
  45. {
  46. IMMDeviceCollection result;
  47. Marshal.ThrowExceptionForHR(_underlyingEnumerator.EnumAudioEndpoints(kind, state, out result));
  48. return new AudioDeviceCollection(result);
  49. }
  50. public void SetDefaultAudioDevice(AudioDevice device)
  51. {
  52. SetDefaultAudioDevice(device, AudioDeviceRole.Multimedia);
  53. SetDefaultAudioDevice(device, AudioDeviceRole.Communications);
  54. SetDefaultAudioDevice(device, AudioDeviceRole.Console);
  55. }
  56. public void SetDefaultAudioDevice(AudioDevice device, AudioDeviceRole role)
  57. {
  58. if (device == null)
  59. throw new ArgumentNullException("device");
  60. // BADNESS: The following code uses an undocumented interface provided by the Audio SDK. This is completely
  61. // unsupported, and should be used for amusement purposes only. This is *extremely likely* to be broken
  62. // in future updates and/or versions of Windows. If Larry Osterman was dead, he would be rolling over
  63. // in his grave if he knew you were using this for nefarious purposes.
  64. IPolicyConfig config = (IPolicyConfig)new PolicyConfig();
  65. int hr = config.SetDefaultEndpoint(device.Id, role);
  66. if (hr != HResult.OK)
  67. throw Marshal.GetExceptionForHR(hr);
  68. }
  69. public bool IsDefaultAudioDevice(AudioDevice device, AudioDeviceRole role)
  70. {
  71. AudioDevice defaultDevice = GetDefaultAudioDevice(device.Kind, role);
  72. if (defaultDevice == null)
  73. return false;
  74. return String.Equals(defaultDevice.Id, device.Id, StringComparison.OrdinalIgnoreCase);
  75. }
  76. public AudioDevice GetDefaultAudioDevice(AudioDeviceKind kind, AudioDeviceRole role)
  77. {
  78. const int E_NOTFOUND = unchecked((int)0x80070490);
  79. IMMDevice underlyingDevice;
  80. int hr = _underlyingEnumerator.GetDefaultAudioEndpoint(kind, role, out underlyingDevice);
  81. if (hr == HResult.OK)
  82. return new AudioDevice(underlyingDevice);
  83. if (hr == E_NOTFOUND)
  84. return null;
  85. throw Marshal.GetExceptionForHR(hr);
  86. }
  87. public AudioDevice GetDevice(string id)
  88. {
  89. IMMDevice underlyingDevice;
  90. Marshal.ThrowExceptionForHR(_underlyingEnumerator.GetDevice(id, out underlyingDevice));
  91. return new AudioDevice(underlyingDevice);
  92. }
  93. void IMMNotificationClient.OnDeviceStateChanged(string deviceId, AudioDeviceState newState)
  94. {
  95. var handler = DeviceStateChanged;
  96. if (handler != null)
  97. {
  98. AudioDevice device = GetDevice(deviceId);
  99. handler(this, new AudioDeviceStateEventArgs(device, newState));
  100. }
  101. }
  102. void IMMNotificationClient.OnDeviceAdded(string deviceId)
  103. {
  104. var handler = DeviceAdded;
  105. if (handler != null)
  106. {
  107. AudioDevice device = GetDevice(deviceId);
  108. handler(this, new AudioDeviceEventArgs(device));
  109. }
  110. }
  111. void IMMNotificationClient.OnDeviceRemoved(string deviceId)
  112. {
  113. var handler = DeviceRemoved;
  114. if (handler != null)
  115. {
  116. handler(this, new AudioDeviceRemovedEventArgs(deviceId));
  117. }
  118. }
  119. void IMMNotificationClient.OnDefaultDeviceChanged(AudioDeviceKind kind, AudioDeviceRole role, string deviceId)
  120. {
  121. var handler = DefaultDeviceChanged;
  122. if (handler != null)
  123. {
  124. AudioDevice device = GetDevice(deviceId);
  125. handler(this, new DefaultAudioDeviceEventArgs(device, kind, role));
  126. }
  127. }
  128. void IMMNotificationClient.OnPropertyValueChanged(string deviceId, PropertyKey key)
  129. {
  130. var handler = DevicePropertyChanged;
  131. if (handler != null)
  132. {
  133. AudioDevice device = GetDevice(deviceId);
  134. handler(this, new AudioDeviceEventArgs(device));
  135. }
  136. }
  137. public void Dispose()
  138. {
  139. int hr = _underlyingEnumerator.UnregisterEndpointNotificationCallback(this);
  140. if (hr != HResult.OK)
  141. throw Marshal.GetExceptionForHR(hr);
  142. }
  143. }
  144. }