Fixed occasional COMException in GetSpotifyVolumeObject (#222)

* Added volume controls in Example app

* Fixed occasional COMException when using Get- or SetSpotifyVolume

This exception only happens if Spotify is using an audio device different from the default one. Such a thing is only possible (as of v1.0.75.483.g7ff4a0dc) when using the "--enable-audio-graph" command line argument, that makes available the "Playback device" advanced option in Spotify.
This commit is contained in:
Alessandro Attard Barbini 2018-03-12 00:08:37 +01:00 committed by Jonas Dellinger
parent 4b40a1b477
commit 159b60331e
3 changed files with 167 additions and 12 deletions

View File

@ -61,6 +61,10 @@
this.artistLinkLabel = new System.Windows.Forms.LinkLabel();
this.titleLinkLabel = new System.Windows.Forms.LinkLabel();
this.smallAlbumPicture = new System.Windows.Forms.PictureBox();
this.volumeDownBtn = new System.Windows.Forms.Button();
this.volumeUpBtn = new System.Windows.Forms.Button();
this.label9 = new System.Windows.Forms.Label();
this.volumeMixerLabel = new System.Windows.Forms.Label();
((System.ComponentModel.ISupportInitialize)(this.bigAlbumPicture)).BeginInit();
this.groupBox1.SuspendLayout();
this.trackInfoBox.SuspendLayout();
@ -69,7 +73,7 @@
//
// bigAlbumPicture
//
this.bigAlbumPicture.Location = new System.Drawing.Point(327, 13);
this.bigAlbumPicture.Location = new System.Drawing.Point(330, 13);
this.bigAlbumPicture.Name = "bigAlbumPicture";
this.bigAlbumPicture.Size = new System.Drawing.Size(640, 640);
this.bigAlbumPicture.TabIndex = 2;
@ -77,6 +81,10 @@
//
// groupBox1
//
this.groupBox1.Controls.Add(this.volumeMixerLabel);
this.groupBox1.Controls.Add(this.label9);
this.groupBox1.Controls.Add(this.volumeUpBtn);
this.groupBox1.Controls.Add(this.volumeDownBtn);
this.groupBox1.Controls.Add(this.repeatShuffleLabel);
this.groupBox1.Controls.Add(this.label6);
this.groupBox1.Controls.Add(this.versionLabel);
@ -428,6 +436,50 @@
this.smallAlbumPicture.TabIndex = 5;
this.smallAlbumPicture.TabStop = false;
//
// volumeDownBtn
//
this.volumeDownBtn.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
this.volumeDownBtn.Location = new System.Drawing.Point(247, 134);
this.volumeDownBtn.Margin = new System.Windows.Forms.Padding(0);
this.volumeDownBtn.Name = "volumeDownBtn";
this.volumeDownBtn.Size = new System.Drawing.Size(65, 24);
this.volumeDownBtn.TabIndex = 32;
this.volumeDownBtn.Text = "Volume-";
this.volumeDownBtn.UseVisualStyleBackColor = true;
this.volumeDownBtn.Click += new System.EventHandler(this.volumeDownBtn_Click);
//
// volumeUpBtn
//
this.volumeUpBtn.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
this.volumeUpBtn.Location = new System.Drawing.Point(247, 110);
this.volumeUpBtn.Margin = new System.Windows.Forms.Padding(0);
this.volumeUpBtn.Name = "volumeUpBtn";
this.volumeUpBtn.Size = new System.Drawing.Size(65, 24);
this.volumeUpBtn.TabIndex = 33;
this.volumeUpBtn.Text = "Volume+";
this.volumeUpBtn.UseVisualStyleBackColor = true;
this.volumeUpBtn.Click += new System.EventHandler(this.volumeUpBtn_Click);
//
// label9
//
this.label9.AutoSize = true;
this.label9.Font = new System.Drawing.Font("Microsoft Sans Serif", 10F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
this.label9.Location = new System.Drawing.Point(7, 117);
this.label9.Name = "label9";
this.label9.Size = new System.Drawing.Size(155, 17);
this.label9.TabIndex = 34;
this.label9.Text = "Volume Mixer\'s volume:";
//
// volumeMixerLabel
//
this.volumeMixerLabel.AutoSize = true;
this.volumeMixerLabel.Font = new System.Drawing.Font("Microsoft Sans Serif", 10F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
this.volumeMixerLabel.Location = new System.Drawing.Point(168, 117);
this.volumeMixerLabel.Name = "volumeMixerLabel";
this.volumeMixerLabel.Size = new System.Drawing.Size(13, 17);
this.volumeMixerLabel.TabIndex = 35;
this.volumeMixerLabel.Text = "-";
//
// LocalControl
//
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
@ -483,5 +535,9 @@
private System.Windows.Forms.Label label6;
private System.Windows.Forms.Label repeatShuffleLabel;
private System.Windows.Forms.Label advertLabel;
private System.Windows.Forms.Button volumeUpBtn;
private System.Windows.Forms.Button volumeDownBtn;
private System.Windows.Forms.Label volumeMixerLabel;
private System.Windows.Forms.Label label9;
}
}

View File

@ -72,6 +72,8 @@ namespace SpotifyAPI.Example
if (status.Track != null) //Update track infos
UpdateTrack(status.Track);
RefreshVolumeMixerVolume();
}
public async void UpdateTrack(Track track)
@ -106,6 +108,11 @@ namespace SpotifyAPI.Example
isPlayingLabel.Text = playing.ToString();
}
public void RefreshVolumeMixerVolume()
{
volumeMixerLabel.Text = _spotify.GetSpotifyVolume().ToString(CultureInfo.InvariantCulture);
}
private void _spotify_OnVolumeChange(object sender, VolumeChangeEventArgs e)
{
if (InvokeRequired)
@ -178,6 +185,24 @@ namespace SpotifyAPI.Example
_spotify.Skip();
}
private void volumeUpBtn_Click(object sender, EventArgs e)
{
float currentVolume = _spotify.GetSpotifyVolume();
float newVolume = currentVolume + 2.0f;
_spotify.SetSpotifyVolume(newVolume >= 100.0f ? 100.0f : newVolume);
RefreshVolumeMixerVolume();
}
private void volumeDownBtn_Click(object sender, EventArgs e)
{
float currentVolume = _spotify.GetSpotifyVolume();
float newVolume = currentVolume - 2.0f;
_spotify.SetSpotifyVolume(newVolume <= 0.0f ? 0.0f : newVolume);
RefreshVolumeMixerVolume();
}
private static String FormatTime(double sec)
{
TimeSpan span = TimeSpan.FromSeconds(sec);

View File

@ -67,11 +67,13 @@ namespace SpotifyAPI.Local
Marshal.ReleaseComObject(volume);
}
private static ISimpleAudioVolume GetSpotifyVolumeObject() {
return (from p in Process.GetProcessesByName(SpotifyProcessName)
private static ISimpleAudioVolume GetSpotifyVolumeObject()
{
var audioVolumeObjects = from p in Process.GetProcessesByName(SpotifyProcessName)
let vol = GetVolumeObject(p.Id)
where vol != null
select vol).FirstOrDefault();
select vol;
return audioVolumeObjects.FirstOrDefault();
}
private static ISimpleAudioVolume GetVolumeObject(int pid)
@ -81,10 +83,59 @@ namespace SpotifyAPI.Local
IMmDevice speakers;
deviceEnumerator.GetDefaultAudioEndpoint(EDataFlow.ERender, ERole.EMultimedia, out speakers);
string defaultDeviceId;
speakers.GetId(out defaultDeviceId);
ISimpleAudioVolume volumeControl = GetVolumeObject(pid, speakers);
Marshal.ReleaseComObject(speakers);
if (volumeControl == null)
{
// If volumeControl is null, then the process's volume object might be on a different device.
// This happens if the process doesn't use the default device.
//
// As far as Spotify is concerned, if using the "--enable-audio-graph" command line argument,
// a new option becomes available in the Settings that makes it possible to change the playback device.
IMmDeviceCollection deviceCollection;
deviceEnumerator.EnumAudioEndpoints(EDataFlow.ERender, EDeviceState.Active, out deviceCollection);
int count;
deviceCollection.GetCount(out count);
for (int i = 0; i < count; i++)
{
IMmDevice device;
deviceCollection.Item(i, out device);
string deviceId;
device.GetId(out deviceId);
try
{
if (deviceId == defaultDeviceId)
continue;
volumeControl = GetVolumeObject(pid, device);
if (volumeControl != null)
break;
}
finally
{
Marshal.ReleaseComObject(device);
}
}
}
Marshal.ReleaseComObject(deviceEnumerator);
return volumeControl;
}
private static ISimpleAudioVolume GetVolumeObject(int pid, IMmDevice device)
{
// activate the session manager. we need the enumerator
Guid iidIAudioSessionManager2 = typeof(IAudioSessionManager2).GUID;
object o;
speakers.Activate(ref iidIAudioSessionManager2, 0, IntPtr.Zero, out o);
device.Activate(ref iidIAudioSessionManager2, 0, IntPtr.Zero, out o);
IAudioSessionManager2 mgr = (IAudioSessionManager2)o;
// enumerate sessions for on this device
@ -105,15 +156,13 @@ namespace SpotifyAPI.Local
if (cpid == pid)
{
volumeControl = (ISimpleAudioVolume) ctl;
volumeControl = (ISimpleAudioVolume)ctl;
break;
}
Marshal.ReleaseComObject(ctl);
}
Marshal.ReleaseComObject(sessionEnumerator);
Marshal.ReleaseComObject(mgr);
Marshal.ReleaseComObject(speakers);
Marshal.ReleaseComObject(deviceEnumerator);
return volumeControl;
}
@ -121,7 +170,6 @@ namespace SpotifyAPI.Local
[Guid("BCDE0395-E52F-467C-8E3D-C4579291692E")]
private class MMDeviceEnumerator
{
}
private enum EDataFlow
@ -140,10 +188,21 @@ namespace SpotifyAPI.Local
ERoleEnumCount
}
[Flags]
private enum EDeviceState
{
Active = 0x00000001,
Disabled = 0x00000002,
NotPresent = 0x00000004,
UnPlugged = 0x00000008,
All = 0x0000000F
}
[Guid("A95664D2-9614-4F35-A746-DE8DB63617E6"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
private interface IMmDeviceEnumerator
{
int NotImpl1();
[PreserveSig]
int EnumAudioEndpoints(EDataFlow dataFlow, EDeviceState stateMask, [Out] out IMmDeviceCollection deviceCollection);
[PreserveSig]
int GetDefaultAudioEndpoint(EDataFlow dataFlow, ERole role, out IMmDevice ppDevice);
@ -154,6 +213,21 @@ namespace SpotifyAPI.Local
{
[PreserveSig]
int Activate(ref Guid iid, int dwClsCtx, IntPtr pActivationParams, [MarshalAs(UnmanagedType.IUnknown)] out object ppInterface);
int OpenPropertyStore_NotImpl();
[PreserveSig]
int GetId([Out, MarshalAs(UnmanagedType.LPWStr)] out string ppstrId);
}
[Guid("0BD7A1BE-7A1A-44DB-8397-CC5392387B5E"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
private interface IMmDeviceCollection
{
[PreserveSig]
int GetCount(out int deviceCount);
[PreserveSig]
int Item(int deviceIndex, [Out] out IMmDevice device);
}
[Guid("77AA99A0-1BD6-484F-8BC7-2C654C9A9B6F"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]