@@ -67,11 +67,13 @@ internal static void MuteSpotify(bool mute)
6767 Marshal . ReleaseComObject ( volume ) ;
6868 }
6969
70- private static ISimpleAudioVolume GetSpotifyVolumeObject ( ) {
71- return ( from p in Process . GetProcessesByName ( SpotifyProcessName )
72- let vol = GetVolumeObject ( p . Id )
73- where vol != null
74- select vol ) . FirstOrDefault ( ) ;
70+ private static ISimpleAudioVolume GetSpotifyVolumeObject ( )
71+ {
72+ var audioVolumeObjects = from p in Process . GetProcessesByName ( SpotifyProcessName )
73+ let vol = GetVolumeObject ( p . Id )
74+ where vol != null
75+ select vol ;
76+ return audioVolumeObjects . FirstOrDefault ( ) ;
7577 }
7678
7779 private static ISimpleAudioVolume GetVolumeObject ( int pid )
@@ -81,10 +83,59 @@ private static ISimpleAudioVolume GetVolumeObject(int pid)
8183 IMmDevice speakers ;
8284 deviceEnumerator . GetDefaultAudioEndpoint ( EDataFlow . ERender , ERole . EMultimedia , out speakers ) ;
8385
86+ string defaultDeviceId ;
87+ speakers . GetId ( out defaultDeviceId ) ;
88+
89+ ISimpleAudioVolume volumeControl = GetVolumeObject ( pid , speakers ) ;
90+ Marshal . ReleaseComObject ( speakers ) ;
91+
92+ if ( volumeControl == null )
93+ {
94+ // If volumeControl is null, then the process's volume object might be on a different device.
95+ // This happens if the process doesn't use the default device.
96+ //
97+ // As far as Spotify is concerned, if using the "--enable-audio-graph" command line argument,
98+ // a new option becomes available in the Settings that makes it possible to change the playback device.
99+
100+ IMmDeviceCollection deviceCollection ;
101+ deviceEnumerator . EnumAudioEndpoints ( EDataFlow . ERender , EDeviceState . Active , out deviceCollection ) ;
102+
103+ int count ;
104+ deviceCollection . GetCount ( out count ) ;
105+ for ( int i = 0 ; i < count ; i ++ )
106+ {
107+ IMmDevice device ;
108+ deviceCollection . Item ( i , out device ) ;
109+
110+ string deviceId ;
111+ device . GetId ( out deviceId ) ;
112+
113+ try
114+ {
115+ if ( deviceId == defaultDeviceId )
116+ continue ;
117+
118+ volumeControl = GetVolumeObject ( pid , device ) ;
119+ if ( volumeControl != null )
120+ break ;
121+ }
122+ finally
123+ {
124+ Marshal . ReleaseComObject ( device ) ;
125+ }
126+ }
127+ }
128+
129+ Marshal . ReleaseComObject ( deviceEnumerator ) ;
130+ return volumeControl ;
131+ }
132+
133+ private static ISimpleAudioVolume GetVolumeObject ( int pid , IMmDevice device )
134+ {
84135 // activate the session manager. we need the enumerator
85136 Guid iidIAudioSessionManager2 = typeof ( IAudioSessionManager2 ) . GUID ;
86137 object o ;
87- speakers . Activate ( ref iidIAudioSessionManager2 , 0 , IntPtr . Zero , out o ) ;
138+ device . Activate ( ref iidIAudioSessionManager2 , 0 , IntPtr . Zero , out o ) ;
88139 IAudioSessionManager2 mgr = ( IAudioSessionManager2 ) o ;
89140
90141 // enumerate sessions for on this device
@@ -105,23 +156,20 @@ private static ISimpleAudioVolume GetVolumeObject(int pid)
105156
106157 if ( cpid == pid )
107158 {
108- volumeControl = ( ISimpleAudioVolume ) ctl ;
159+ volumeControl = ( ISimpleAudioVolume ) ctl ;
109160 break ;
110161 }
111162 Marshal . ReleaseComObject ( ctl ) ;
112163 }
113164 Marshal . ReleaseComObject ( sessionEnumerator ) ;
114165 Marshal . ReleaseComObject ( mgr ) ;
115- Marshal . ReleaseComObject ( speakers ) ;
116- Marshal . ReleaseComObject ( deviceEnumerator ) ;
117166 return volumeControl ;
118167 }
119168
120169 [ ComImport ]
121170 [ Guid ( "BCDE0395-E52F-467C-8E3D-C4579291692E" ) ]
122171 private class MMDeviceEnumerator
123172 {
124-
125173 }
126174
127175 private enum EDataFlow
@@ -140,10 +188,21 @@ private enum ERole
140188 ERoleEnumCount
141189 }
142190
191+ [ Flags ]
192+ private enum EDeviceState
193+ {
194+ Active = 0x00000001 ,
195+ Disabled = 0x00000002 ,
196+ NotPresent = 0x00000004 ,
197+ UnPlugged = 0x00000008 ,
198+ All = 0x0000000F
199+ }
200+
143201 [ Guid ( "A95664D2-9614-4F35-A746-DE8DB63617E6" ) , InterfaceType ( ComInterfaceType . InterfaceIsIUnknown ) ]
144202 private interface IMmDeviceEnumerator
145203 {
146- int NotImpl1 ( ) ;
204+ [ PreserveSig ]
205+ int EnumAudioEndpoints ( EDataFlow dataFlow , EDeviceState stateMask , [ Out ] out IMmDeviceCollection deviceCollection ) ;
147206
148207 [ PreserveSig ]
149208 int GetDefaultAudioEndpoint ( EDataFlow dataFlow , ERole role , out IMmDevice ppDevice ) ;
@@ -154,6 +213,21 @@ private interface IMmDevice
154213 {
155214 [ PreserveSig ]
156215 int Activate ( ref Guid iid , int dwClsCtx , IntPtr pActivationParams , [ MarshalAs ( UnmanagedType . IUnknown ) ] out object ppInterface ) ;
216+
217+ int OpenPropertyStore_NotImpl ( ) ;
218+
219+ [ PreserveSig ]
220+ int GetId ( [ Out , MarshalAs ( UnmanagedType . LPWStr ) ] out string ppstrId ) ;
221+ }
222+
223+ [ Guid ( "0BD7A1BE-7A1A-44DB-8397-CC5392387B5E" ) , InterfaceType ( ComInterfaceType . InterfaceIsIUnknown ) ]
224+ private interface IMmDeviceCollection
225+ {
226+ [ PreserveSig ]
227+ int GetCount ( out int deviceCount ) ;
228+
229+ [ PreserveSig ]
230+ int Item ( int deviceIndex , [ Out ] out IMmDevice device ) ;
157231 }
158232
159233 [ Guid ( "77AA99A0-1BD6-484F-8BC7-2C654C9A9B6F" ) , InterfaceType ( ComInterfaceType . InterfaceIsIUnknown ) ]
0 commit comments