diff --git a/SpotifyAPI/Local/SpotifyLocalAPI.cs b/SpotifyAPI/Local/SpotifyLocalAPI.cs
index 5c599189..383b582f 100644
--- a/SpotifyAPI/Local/SpotifyLocalAPI.cs
+++ b/SpotifyAPI/Local/SpotifyLocalAPI.cs
@@ -2,6 +2,7 @@
using System;
using System.ComponentModel;
using System.Diagnostics;
+using System.Diagnostics.Contracts;
using System.IO;
using System.Runtime.InteropServices;
using System.Timers;
@@ -13,9 +14,6 @@ namespace SpotifyAPI.Local
[DllImport("user32.dll")]
private static extern void keybd_event(byte bVk, byte bScan, uint dwFlags, int dwExtraInfo);
- [DllImport("nircmd.dll", CharSet = CharSet.Auto)]
- private static extern bool DoNirCmd(String nirCmdStr);
-
private bool _listenForEvents;
public bool ListenForEvents
@@ -159,21 +157,72 @@ namespace SpotifyAPI.Local
}
///
- /// Mutes Spotify (Requires nircmd.dll)
+ /// Mutes Spotify (Requires Windows 7 or newer)
///
public void Mute()
{
- if (File.Exists("nircmd.dll"))
- DoNirCmd("muteappvolume spotify.exe 1");
+ //Windows < Windows Vista Check
+ Contract.Requires(Environment.OSVersion.Version.Major >= 6);
+ //Windows Vista Check
+ if (Environment.OSVersion.Version.Major == 6)
+ Contract.Requires(Environment.OSVersion.Version.Minor != 0);
+ VolumeMixerControl.MuteSpotify(true);
}
///
- /// Unmutes Spotify (Requires nircmd.dll)
+ /// Unmutes Spotify (Requires Windows 7 or newer)
///
public void UnMute()
{
- if (File.Exists("nircmd.dll"))
- DoNirCmd("muteappvolume spotify.exe 0");
+ //Windows < Windows Vista Check
+ Contract.Requires(Environment.OSVersion.Version.Major >= 6);
+ //Windows Vista Check
+ if (Environment.OSVersion.Version.Major == 6)
+ Contract.Requires(Environment.OSVersion.Version.Minor != 0);
+ VolumeMixerControl.MuteSpotify(false);
+ }
+
+ ///
+ /// Checks whether Spotify is muted in the Volume Mixer control
+ ///
+ /// Null if an error occured, otherwise the muted state
+ public bool? IsSpotifyMuted()
+ {
+ //Windows < Windows Vista Check
+ Contract.Requires(Environment.OSVersion.Version.Major >= 6);
+ //Windows Vista Check
+ if (Environment.OSVersion.Version.Major == 6)
+ Contract.Requires(Environment.OSVersion.Version.Minor != 0);
+ return VolumeMixerControl.IsSpotifyMuted();
+ }
+
+ ///
+ /// Sets the Volume Mixer volume (requires Windows 7 or newer)
+ ///
+ /// A Value between 0 and 100
+ public void SetSpotifyVolume(float volume = 100)
+ {
+ Contract.Requires(0 <= volume && volume <= 100);
+ //Windows < Windows Vista Check
+ Contract.Requires(Environment.OSVersion.Version.Major >= 6);
+ //Windows Vista Check
+ if (Environment.OSVersion.Version.Major == 6)
+ Contract.Requires(Environment.OSVersion.Version.Minor != 0);
+ VolumeMixerControl.SetSpotifyVolume(volume);
+ }
+
+ ///
+ /// Return the Volume Mixer volume of Spotify (requires Windows 7 or newer)
+ ///
+ /// Null if an error occured, otherwise a float between 0 and 100
+ public float? GetSpotifyVolume()
+ {
+ //Windows < Windows Vista Check
+ Contract.Requires(Environment.OSVersion.Version.Major >= 6);
+ //Windows Vista Check
+ if (Environment.OSVersion.Version.Major == 6)
+ Contract.Requires(Environment.OSVersion.Version.Minor != 0);
+ return VolumeMixerControl.GetSpotifyVolume();
}
///
diff --git a/SpotifyAPI/Local/VolumeMixerControl.cs b/SpotifyAPI/Local/VolumeMixerControl.cs
new file mode 100644
index 00000000..29247818
--- /dev/null
+++ b/SpotifyAPI/Local/VolumeMixerControl.cs
@@ -0,0 +1,250 @@
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.Linq;
+using System.Runtime.InteropServices;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace SpotifyAPI.Local
+{
+ internal class VolumeMixerControl
+ {
+ private const String SPOTIFY_PROCESS_NAME = "spotify";
+
+ internal static float? GetSpotifyVolume()
+ {
+ Process[] p = Process.GetProcessesByName(SPOTIFY_PROCESS_NAME);
+ if (p.Length == 0)
+ return null;
+
+ int pid = p[0].Id;
+
+ ISimpleAudioVolume volume = GetVolumeObject(pid);
+ if (volume == null)
+ return null;
+
+ float level;
+ volume.GetMasterVolume(out level);
+ Marshal.ReleaseComObject(volume);
+ return level * 100;
+ }
+
+ internal static bool? IsSpotifyMuted()
+ {
+ Process[] p = Process.GetProcessesByName(SPOTIFY_PROCESS_NAME);
+ if (p.Length == 0)
+ return null;
+
+ int pid = p[0].Id;
+
+ ISimpleAudioVolume volume = GetVolumeObject(pid);
+ if (volume == null)
+ return null;
+
+ bool mute;
+ volume.GetMute(out mute);
+ Marshal.ReleaseComObject(volume);
+ return mute;
+ }
+
+ internal static void SetSpotifyVolume(float level)
+ {
+ Process[] p = Process.GetProcessesByName(SPOTIFY_PROCESS_NAME);
+ if (p.Length == 0)
+ return;
+
+ int pid = p[0].Id;
+
+ ISimpleAudioVolume volume = GetVolumeObject(pid);
+ if (volume == null)
+ return;
+
+ Guid guid = Guid.Empty;
+ volume.SetMasterVolume(level / 100, ref guid);
+ Marshal.ReleaseComObject(volume);
+ }
+
+ internal static void MuteSpotify(bool mute)
+ {
+ Process[] p = Process.GetProcessesByName(SPOTIFY_PROCESS_NAME);
+ if (p.Length == 0)
+ return;
+
+ int pid = p[0].Id;
+
+ ISimpleAudioVolume volume = GetVolumeObject(pid);
+ if (volume == null)
+ return;
+
+ Guid guid = Guid.Empty;
+ volume.SetMute(mute, ref guid);
+ Marshal.ReleaseComObject(volume);
+ }
+
+ private static ISimpleAudioVolume GetVolumeObject(int pid)
+ {
+ // get the speakers (1st render + multimedia) device
+ IMMDeviceEnumerator deviceEnumerator = (IMMDeviceEnumerator)(new MMDeviceEnumerator());
+ IMMDevice speakers;
+ deviceEnumerator.GetDefaultAudioEndpoint(EDataFlow.eRender, ERole.eMultimedia, out speakers);
+
+ // activate the session manager. we need the enumerator
+ Guid IID_IAudioSessionManager2 = typeof(IAudioSessionManager2).GUID;
+ object o;
+ speakers.Activate(ref IID_IAudioSessionManager2, 0, IntPtr.Zero, out o);
+ IAudioSessionManager2 mgr = (IAudioSessionManager2)o;
+
+ // enumerate sessions for on this device
+ IAudioSessionEnumerator sessionEnumerator;
+ mgr.GetSessionEnumerator(out sessionEnumerator);
+ int count;
+ sessionEnumerator.GetCount(out count);
+
+ // search for an audio session with the required name
+ // NOTE: we could also use the process id instead of the app name (with IAudioSessionControl2)
+ ISimpleAudioVolume volumeControl = null;
+ for (int i = 0; i < count; i++)
+ {
+ IAudioSessionControl2 ctl;
+ sessionEnumerator.GetSession(i, out ctl);
+ int cpid;
+ ctl.GetProcessId(out cpid);
+
+ if (cpid == pid)
+ {
+ volumeControl = ctl as ISimpleAudioVolume;
+ break;
+ }
+ Marshal.ReleaseComObject(ctl);
+ }
+ Marshal.ReleaseComObject(sessionEnumerator);
+ Marshal.ReleaseComObject(mgr);
+ Marshal.ReleaseComObject(speakers);
+ Marshal.ReleaseComObject(deviceEnumerator);
+ return volumeControl;
+ }
+
+
+ [ComImport]
+ [Guid("BCDE0395-E52F-467C-8E3D-C4579291692E")]
+ private class MMDeviceEnumerator
+ {
+ }
+
+ private enum EDataFlow
+ {
+ eRender,
+ eCapture,
+ eAll,
+ EDataFlow_enum_count
+ }
+
+ private enum ERole
+ {
+ eConsole,
+ eMultimedia,
+ eCommunications,
+ ERole_enum_count
+ }
+
+ [Guid("A95664D2-9614-4F35-A746-DE8DB63617E6"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
+ private interface IMMDeviceEnumerator
+ {
+ int NotImpl1();
+
+ [PreserveSig]
+ int GetDefaultAudioEndpoint(EDataFlow dataFlow, ERole role, out IMMDevice ppDevice);
+ }
+
+ [Guid("D666063F-1587-4E43-81F1-B948E807363F"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
+ private interface IMMDevice
+ {
+ [PreserveSig]
+ int Activate(ref Guid iid, int dwClsCtx, IntPtr pActivationParams, [MarshalAs(UnmanagedType.IUnknown)] out object ppInterface);
+
+ }
+
+ [Guid("77AA99A0-1BD6-484F-8BC7-2C654C9A9B6F"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
+ private interface IAudioSessionManager2
+ {
+ int NotImpl1();
+ int NotImpl2();
+
+ [PreserveSig]
+ int GetSessionEnumerator(out IAudioSessionEnumerator SessionEnum);
+ }
+
+ [Guid("E2F5BB11-0570-40CA-ACDD-3AA01277DEE8"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
+ private interface IAudioSessionEnumerator
+ {
+ [PreserveSig]
+ int GetCount(out int SessionCount);
+
+ [PreserveSig]
+ int GetSession(int SessionCount, out IAudioSessionControl2 Session);
+ }
+
+ [Guid("87CE5498-68D6-44E5-9215-6DA47EF883D8"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
+ private interface ISimpleAudioVolume
+ {
+ [PreserveSig]
+ int SetMasterVolume(float fLevel, ref Guid EventContext);
+
+ [PreserveSig]
+ int GetMasterVolume(out float pfLevel);
+
+ [PreserveSig]
+ int SetMute(bool bMute, ref Guid EventContext);
+
+ [PreserveSig]
+ int GetMute(out bool pbMute);
+ }
+
+ [Guid("bfb7ff88-7239-4fc9-8fa2-07c950be9c6d"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
+ private interface IAudioSessionControl2
+ {
+ [PreserveSig]
+ int NotImpl0();
+
+ [PreserveSig]
+ int GetDisplayName([MarshalAs(UnmanagedType.LPWStr)] out string pRetVal);
+
+ [PreserveSig]
+ int SetDisplayName([MarshalAs(UnmanagedType.LPWStr)]string Value, [MarshalAs(UnmanagedType.LPStruct)] Guid EventContext);
+
+ [PreserveSig]
+ int GetIconPath([MarshalAs(UnmanagedType.LPWStr)] out string pRetVal);
+
+ [PreserveSig]
+ int SetIconPath([MarshalAs(UnmanagedType.LPWStr)] string Value, [MarshalAs(UnmanagedType.LPStruct)] Guid EventContext);
+
+ [PreserveSig]
+ int GetGroupingParam(out Guid pRetVal);
+
+ [PreserveSig]
+ int SetGroupingParam([MarshalAs(UnmanagedType.LPStruct)] Guid Override, [MarshalAs(UnmanagedType.LPStruct)] Guid EventContext);
+
+ [PreserveSig]
+ int NotImpl1();
+
+ [PreserveSig]
+ int NotImpl2();
+
+ [PreserveSig]
+ int GetSessionIdentifier([MarshalAs(UnmanagedType.LPWStr)] out string pRetVal);
+
+ [PreserveSig]
+ int GetSessionInstanceIdentifier([MarshalAs(UnmanagedType.LPWStr)] out string pRetVal);
+
+ [PreserveSig]
+ int GetProcessId(out int pRetVal);
+
+ [PreserveSig]
+ int IsSystemSoundsSession();
+
+ [PreserveSig]
+ int SetDuckingPreference(bool optOut);
+ }
+ }
+}
diff --git a/SpotifyAPI/SpotifyAPI.csproj b/SpotifyAPI/SpotifyAPI.csproj
index de89e8af..faaaafb5 100644
--- a/SpotifyAPI/SpotifyAPI.csproj
+++ b/SpotifyAPI/SpotifyAPI.csproj
@@ -56,6 +56,7 @@
Component
+