From baf401a012706cc04e821dbe1a1e756a65bf29e3 Mon Sep 17 00:00:00 2001 From: Brandon Chong Date: Sat, 24 Nov 2018 22:22:19 -0800 Subject: [PATCH 01/14] Add iTunes audio source --- src/AudioBand.sln | 6 + src/iTunesAudioSource/AudioSource.cs | 157 ++++++++++++++++ src/iTunesAudioSource/AudioSource.manifest | 1 + src/iTunesAudioSource/ITunesControls.cs | 170 ++++++++++++++++++ .../Properties/AssemblyInfo.cs | 36 ++++ .../iTunesAudioSource.csproj | 75 ++++++++ 6 files changed, 445 insertions(+) create mode 100644 src/iTunesAudioSource/AudioSource.cs create mode 100644 src/iTunesAudioSource/AudioSource.manifest create mode 100644 src/iTunesAudioSource/ITunesControls.cs create mode 100644 src/iTunesAudioSource/Properties/AssemblyInfo.cs create mode 100644 src/iTunesAudioSource/iTunesAudioSource.csproj diff --git a/src/AudioBand.sln b/src/AudioBand.sln index 47347930..60326643 100644 --- a/src/AudioBand.sln +++ b/src/AudioBand.sln @@ -11,6 +11,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SpotifyAudioSource", "Spoti EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AudioBand.Test", "AudioBand.Test\AudioBand.Test.csproj", "{E5E9CB59-C8CF-4042-9C13-F216153334B6}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "iTunesAudioSource", "iTunesAudioSource\iTunesAudioSource.csproj", "{6D881B7B-3F3F-4613-A83F-75E31EEA5252}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -33,6 +35,10 @@ Global {E5E9CB59-C8CF-4042-9C13-F216153334B6}.Debug|Any CPU.Build.0 = Debug|Any CPU {E5E9CB59-C8CF-4042-9C13-F216153334B6}.Release|Any CPU.ActiveCfg = Release|Any CPU {E5E9CB59-C8CF-4042-9C13-F216153334B6}.Release|Any CPU.Build.0 = Release|Any CPU + {6D881B7B-3F3F-4613-A83F-75E31EEA5252}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {6D881B7B-3F3F-4613-A83F-75E31EEA5252}.Debug|Any CPU.Build.0 = Debug|Any CPU + {6D881B7B-3F3F-4613-A83F-75E31EEA5252}.Release|Any CPU.ActiveCfg = Release|Any CPU + {6D881B7B-3F3F-4613-A83F-75E31EEA5252}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/src/iTunesAudioSource/AudioSource.cs b/src/iTunesAudioSource/AudioSource.cs new file mode 100644 index 00000000..98f2e117 --- /dev/null +++ b/src/iTunesAudioSource/AudioSource.cs @@ -0,0 +1,157 @@ +using AudioBand.AudioSource; +using System; +using System.ComponentModel; +using System.Threading; +using System.Threading.Tasks; +using System.Timers; +using Timer = System.Timers.Timer; + +namespace iTunesAudioSource +{ + public class AudioSource : IAudioSource + { + private Timer _checkiTunesTimer; + private string _currentTrack; + private bool _isPlaying; + private ITunesControls _itunesControls = new ITunesControls(); + + public string Name => "iTunes"; + public IAudioSourceLogger Logger { get; set; } + public event EventHandler TrackInfoChanged; + public event EventHandler TrackPlaying; + public event EventHandler TrackPaused; + public event EventHandler TrackProgressChanged; + public event PropertyChangedEventHandler PropertyChanged; + + public AudioSource() + { + _checkiTunesTimer = new Timer(100) + { + Enabled = false, + AutoReset = false + }; + _checkiTunesTimer.Elapsed += CheckItunes; + } + + public Task ActivateAsync(CancellationToken cancellationToken = default(CancellationToken)) + { + _itunesControls.Start(); + _checkiTunesTimer.Start(); + return Task.CompletedTask; + } + + public Task DeactivateAsync(CancellationToken cancellationToken = default(CancellationToken)) + { + _checkiTunesTimer.Stop(); + _itunesControls.Stop(); + + _isPlaying = false; + _currentTrack = null; + + return Task.CompletedTask; + } + + public Task NextTrackAsync(CancellationToken cancellationToken = default(CancellationToken)) + { + _itunesControls.Next(); + return Task.CompletedTask; + } + + public Task PauseTrackAsync(CancellationToken cancellationToken = default(CancellationToken)) + { + _itunesControls.Pause(); + return Task.CompletedTask; + } + + public Task PlayTrackAsync(CancellationToken cancellationToken = default(CancellationToken)) + { + _itunesControls.Play(); + return Task.CompletedTask; + } + + public Task PreviousTrackAsync(CancellationToken cancellationToken = default(CancellationToken)) + { + _itunesControls.Previous(); + return Task.CompletedTask; + } + + + private void NotifyTrackChange(Track track) + { + var trackInfo = new TrackInfoChangedEventArgs + { + Artist = track.Artist, + Album = track.Album, + AlbumArt = track.Artwork, + TrackLength = track.Length, + TrackName = track.Name, + }; + + TrackInfoChanged?.Invoke(this, trackInfo); + } + + private bool IsNewTrack(Track track) + { + var trackname = track.Artist + track.Name; + if (trackname == _currentTrack) + { + return false; + } + + _currentTrack = trackname; + return true; + } + + private void NotifyPlayerState() + { + var playing = _itunesControls.IsPlaying; + if (_isPlaying == playing) + { + return; + } + + if (playing) + { + TrackPlaying?.Invoke(this, EventArgs.Empty); + } + else + { + TrackPaused.Invoke(this, EventArgs.Empty); + } + + _isPlaying = playing; + } + + private void CheckItunes(object sender, ElapsedEventArgs eventArgs) + { + try + { + var track = _itunesControls.CurrentTrack; + if (track == null) + { + return; + } + + NotifyPlayerState(); + var isNewTrack = IsNewTrack(track); + if (isNewTrack) + { + NotifyTrackChange(track); + } + + if (_isPlaying || isNewTrack) + { + TrackProgressChanged?.Invoke(this, _itunesControls.Progress); + } + } + catch(Exception e) + { + Logger.Debug(e); + } + finally + { + _checkiTunesTimer.Enabled = true; + } + } + } +} diff --git a/src/iTunesAudioSource/AudioSource.manifest b/src/iTunesAudioSource/AudioSource.manifest new file mode 100644 index 00000000..4fcea7ec --- /dev/null +++ b/src/iTunesAudioSource/AudioSource.manifest @@ -0,0 +1 @@ +AudioSources = ["iTunesAudioSource.dll"] \ No newline at end of file diff --git a/src/iTunesAudioSource/ITunesControls.cs b/src/iTunesAudioSource/ITunesControls.cs new file mode 100644 index 00000000..89dbffb0 --- /dev/null +++ b/src/iTunesAudioSource/ITunesControls.cs @@ -0,0 +1,170 @@ +using iTunesLib; +using System; +using System.Diagnostics; +using System.Drawing; +using System.IO; +using System.Runtime.InteropServices; +using System.Timers; +using Timer = System.Timers.Timer; + +namespace iTunesAudioSource +{ + class ITunesControls + { + private iTunesApp _itunesApp = new iTunesApp(); + private Timer _checkProcessTimer; + private bool _itunesOpened; + + public bool IsPlaying => GetIsPlaying(); + public Track CurrentTrack => GetTrack(); + public TimeSpan Progress => TimeSpan.FromMilliseconds(_itunesApp.PlayerPositionMS); + + public ITunesControls() + { + _checkProcessTimer = new Timer(50) + { + AutoReset = false, + Enabled = false + }; + _checkProcessTimer.Elapsed += CheckProcess; + } + + public void Start() + { + _checkProcessTimer.Start(); + } + + public void Stop() + { + _checkProcessTimer.Stop(); + } + + public void Play() + { + try + { + _itunesApp.Play(); + } + catch (COMException) { } + } + + public void Pause() + { + try + { + _itunesApp.Pause(); + } + catch (COMException) { } + } + + public void Next() + { + try + { + _itunesApp.NextTrack(); + } + catch (COMException) {} + } + + public void Previous() + { + try + { + _itunesApp.PreviousTrack(); + } + catch (COMException) {} + } + + private Track GetTrack() + { + if (!ITunesIsRunning()) + { + return null; + } + + try + { + var track = _itunesApp.CurrentTrack as IITTrack; + if (track == null) + { + return null; + } + + return new Track + { + Album = track.Album, + Artist = track.Artist, + Length = TimeSpan.FromSeconds(track.Duration), + Name = track.Name, + Artwork = GetArtwork(track.Artwork) + }; + } + catch (COMException) + { + return null; + } + } + + private Image GetArtwork(IITArtworkCollection collection) + { + if (collection.Count == 0) + { + return null; + } + + // 1-based index + var artwork = collection[1]; + var tempPath = Path.GetTempFileName(); + + artwork.SaveArtworkToFile(tempPath); + + Image image; + using (var tmp = new Bitmap(tempPath)) + { + image = new Bitmap(tmp); + } + + File.Delete(tempPath); + return image; + } + + private bool ITunesIsRunning() + { + return Process.GetProcessesByName("iTunes").Length > 0; + } + + private bool GetIsPlaying() + { + try + { + return _itunesApp.PlayerState.HasFlag(ITPlayerState.ITPlayerStatePlaying); + } + catch (COMException) + { + return false; + } + } + + private void CheckProcess(object sender, ElapsedEventArgs e) + { + var opened = ITunesIsRunning(); + if (!_itunesOpened && opened) + { + _itunesApp = new iTunesApp(); + } + + _itunesOpened = opened; + + _checkProcessTimer.Enabled = true; + } + } + + class Track + { + public string Name { get; set; } + public string Album { get; set; } + public Image Artwork { get; set; } + public string Artist { get; set; } + public TimeSpan Length { get; set; } + } +} diff --git a/src/iTunesAudioSource/Properties/AssemblyInfo.cs b/src/iTunesAudioSource/Properties/AssemblyInfo.cs new file mode 100644 index 00000000..eadfcbd1 --- /dev/null +++ b/src/iTunesAudioSource/Properties/AssemblyInfo.cs @@ -0,0 +1,36 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("iTunesAudioSource")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("iTunesAudioSource")] +[assembly: AssemblyCopyright("Copyright © 2018")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("6d881b7b-3f3f-4613-a83f-75e31eea5252")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/src/iTunesAudioSource/iTunesAudioSource.csproj b/src/iTunesAudioSource/iTunesAudioSource.csproj new file mode 100644 index 00000000..d3de8642 --- /dev/null +++ b/src/iTunesAudioSource/iTunesAudioSource.csproj @@ -0,0 +1,75 @@ + + + + + Debug + AnyCPU + {6D881B7B-3F3F-4613-A83F-75E31EEA5252} + Library + Properties + iTunesAudioSource + iTunesAudioSource + v4.6.1 + 512 + true + + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + + + + + + + + + + + + + + + + + + {30f2bfea-788a-494d-88e7-f2070528ebea} + AudioBand.AudioSource + + + + + {9E93C96F-CF0D-43F6-8BA8-B807A3370712} + 1 + 13 + 0 + tlbimp + False + True + + + + + PreserveNewest + + + + + (robocopy "$(TargetDir)\" "$(SolutionDir)AudioBand\$(OutDir)AudioSources\iTunes\\" /xf AudioBand.AudioSource.dll) ^& IF %25ERRORLEVEL%25 LEQ 1 exit 0 + + \ No newline at end of file From b4135519972c5f2e0473436081f8879b94754435 Mon Sep 17 00:00:00 2001 From: Brandon Chong Date: Sun, 25 Nov 2018 16:46:01 -0800 Subject: [PATCH 02/14] Only create itunes api when activated --- src/iTunesAudioSource/ITunesControls.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/iTunesAudioSource/ITunesControls.cs b/src/iTunesAudioSource/ITunesControls.cs index 89dbffb0..452dc265 100644 --- a/src/iTunesAudioSource/ITunesControls.cs +++ b/src/iTunesAudioSource/ITunesControls.cs @@ -11,7 +11,7 @@ namespace iTunesAudioSource { class ITunesControls { - private iTunesApp _itunesApp = new iTunesApp(); + private iTunesApp _itunesApp; private Timer _checkProcessTimer; private bool _itunesOpened; @@ -31,6 +31,7 @@ public ITunesControls() public void Start() { + _itunesApp = new iTunesApp(); _checkProcessTimer.Start(); } From ae6302a317c4d18cd6f642af61185452a1c5f3cc Mon Sep 17 00:00:00 2001 From: Brandon Chong Date: Sun, 25 Nov 2018 16:50:37 -0800 Subject: [PATCH 03/14] Reset fields when deactivated --- src/SpotifyAudioSource/SpotifyAudioSource.cs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/SpotifyAudioSource/SpotifyAudioSource.cs b/src/SpotifyAudioSource/SpotifyAudioSource.cs index 20c64b49..080516cd 100644 --- a/src/SpotifyAudioSource/SpotifyAudioSource.cs +++ b/src/SpotifyAudioSource/SpotifyAudioSource.cs @@ -179,9 +179,15 @@ public SpotifyAudioSource() { _isActive = false; + _trackProgressStopwatch.Stop(); _checkSpotifyTimer.Stop(); _progressTimer.Stop(); + _lastSpotifyWindowTitle = ""; + _currentTrackId = null; + _baseTrackProgress = TimeSpan.Zero; + _currentTrackLength = TimeSpan.Zero; + return Task.CompletedTask; } From 0a7260034ad7b44f9459393cd3dea332597580d6 Mon Sep 17 00:00:00 2001 From: Brandon Chong Date: Sun, 25 Nov 2018 16:56:04 -0800 Subject: [PATCH 04/14] Reset track when audio source is deselected --- src/AudioBand/MainControl.cs | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/src/AudioBand/MainControl.cs b/src/AudioBand/MainControl.cs index 19788f64..f8cbb9d0 100644 --- a/src/AudioBand/MainControl.cs +++ b/src/AudioBand/MainControl.cs @@ -209,6 +209,8 @@ private async Task SubscribeToAudioSource(IAudioSource source) return; } + ResetTrack(); + source.TrackInfoChanged += AudioSourceOnTrackInfoChanged; source.TrackPlaying += AudioSourceOnTrackPlaying; source.TrackPaused += AudioSourceOnTrackPaused; @@ -240,6 +242,8 @@ private async Task UnsubscribeToAudioSource(IAudioSource source) _appSettings.AudioSource = null; _currentAudioSource = null; + ResetTrack(); + Logger.Debug("Audio source deactivated"); } @@ -289,5 +293,16 @@ private async Task SelectAudioSourceFromSettings() Logger.Error(e); } } + + private void ResetTrack() + { + _trackModel.AlbumArt = null; + _trackModel.AlbumName = null; + _trackModel.Artist = null; + _trackModel.TrackName = null; + _trackModel.IsPlaying = false; + _trackModel.TrackLength = TimeSpan.Zero; + _trackModel.TrackProgress = TimeSpan.Zero; + } } } From ed7304bd610e7cb05c9e5aae60ac91157b7939ce Mon Sep 17 00:00:00 2001 From: Brandon Chong Date: Sun, 25 Nov 2018 17:18:57 -0800 Subject: [PATCH 05/14] Clean up settings window --- src/AudioBand/MainControl.cs | 24 +++++++++++-------- .../Views/Wpf/SettingsWindow.xaml.cs | 16 ++++++------- 2 files changed, 21 insertions(+), 19 deletions(-) diff --git a/src/AudioBand/MainControl.cs b/src/AudioBand/MainControl.cs index f8cbb9d0..307629b5 100644 --- a/src/AudioBand/MainControl.cs +++ b/src/AudioBand/MainControl.cs @@ -107,6 +107,7 @@ await _uiDispatcher.InvokeAsync(() => { _settingsWindow = new SettingsWindow(_settingsWindowVm); _settingsWindow.Saved += Saved; + _settingsWindow.Canceled += Canceled; ElementHost.EnableModelessKeyboardInterop(_settingsWindow); }); @@ -162,7 +163,7 @@ private async Task SetupViewModels() await _uiDispatcher.InvokeAsync(() => InitializeBindingSources(albumArtPopup, albumArt, audioBand, nextButton, playPauseButton, prevButton, progressBar)); - var vm = new SettingsWindowVM + return new SettingsWindowVM { AlbumArtPopupVM = albumArtPopup, ProgressBarVM = progressBar, @@ -175,17 +176,25 @@ private async Task SetupViewModels() CustomLabelsVM = customLabels, AudioSourceSettingsVM = allAudioSourceSettings }; - - vm.BeginEdit(); - - return vm; } private void Saved(object o, EventArgs eventArgs) { + _settingsWindowVm.EndEdit(); _appSettings.Save(); } + private void Canceled(object sender, EventArgs e) + { + _settingsWindowVm.CancelEdit(); + } + + private void OpenSettingsWindow() + { + _settingsWindowVm.BeginEdit(); + _settingsWindow.Show(); + } + private List BuildContextMenu() { var pluginList = _audioSourceLoader.AudioSources.Select(audioSource => @@ -267,11 +276,6 @@ protected override void OnClose() _currentAudioSource.DeactivateAsync(); } - private void OpenSettingsWindow() - { - _settingsWindow.Show(); - } - private async Task SelectAudioSourceFromSettings() { try diff --git a/src/AudioBand/Views/Wpf/SettingsWindow.xaml.cs b/src/AudioBand/Views/Wpf/SettingsWindow.xaml.cs index e22c01cf..7a009aaa 100644 --- a/src/AudioBand/Views/Wpf/SettingsWindow.xaml.cs +++ b/src/AudioBand/Views/Wpf/SettingsWindow.xaml.cs @@ -11,12 +11,12 @@ namespace AudioBand.Views.Wpf internal partial class SettingsWindow { private bool _shouldSave; - private readonly SettingsWindowVM _vm; public RelayCommand CancelCloseCommand { get; } public RelayCommand SaveCloseCommand { get; } public EventHandler Saved; + public EventHandler Canceled; internal SettingsWindow(SettingsWindowVM vm) { @@ -26,22 +26,22 @@ internal SettingsWindow(SettingsWindowVM vm) SaveCloseCommand = new RelayCommand(SaveCloseCommandOnExecute); InitializeComponent(); - DataContext = _vm = vm; + DataContext = vm; vm.CustomLabelsVM.DialogService = new DialogService(this); } - - private static readonly HashSet _externalAssemblies = new HashSet + private static readonly HashSet _bindingHelpAssemblies = new HashSet { "MahApps.Metro.IconPacks.Material", "ColorPickerWPF" }; - // Problem loading some dlls from the code base + + // Problem with late binding? Fuslogvw shows its not probing the original location. private static Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEventArgs args) { // name is in this format Xceed.Wpf.Toolkit, Version=3.4.0.0, Culture=neutral, PublicKeyToken=3e4669d2f30244f4 var asmName = args.Name.Substring(0, args.Name.IndexOf(',')); - if (!_externalAssemblies.Contains(asmName)) + if (!_bindingHelpAssemblies.Contains(asmName)) { return null; } @@ -68,16 +68,14 @@ protected override void OnClosing(CancelEventArgs e) if (_shouldSave) { - _vm.EndEdit(); Saved?.Invoke(this, EventArgs.Empty); } else { - _vm.CancelEdit(); + Canceled?.Invoke(this, EventArgs.Empty); } _shouldSave = false; - _vm.BeginEdit(); Hide(); } } From 67b69bd58940dcc2295141ef79587c2e21c7f099 Mon Sep 17 00:00:00 2001 From: Brandon Chong Date: Sun, 25 Nov 2018 16:41:39 -0800 Subject: [PATCH 06/14] Create initial project --- src/MusicBeeAudioSource/Class1.cs | 12 +++++ .../MusicBeeAudioSource.csproj | 54 +++++++++++++++++++ .../Properties/AssemblyInfo.cs | 36 +++++++++++++ 3 files changed, 102 insertions(+) create mode 100644 src/MusicBeeAudioSource/Class1.cs create mode 100644 src/MusicBeeAudioSource/MusicBeeAudioSource.csproj create mode 100644 src/MusicBeeAudioSource/Properties/AssemblyInfo.cs diff --git a/src/MusicBeeAudioSource/Class1.cs b/src/MusicBeeAudioSource/Class1.cs new file mode 100644 index 00000000..9ca5588f --- /dev/null +++ b/src/MusicBeeAudioSource/Class1.cs @@ -0,0 +1,12 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace MusicBeeAudioSource +{ + public class Class1 + { + } +} diff --git a/src/MusicBeeAudioSource/MusicBeeAudioSource.csproj b/src/MusicBeeAudioSource/MusicBeeAudioSource.csproj new file mode 100644 index 00000000..5353acd6 --- /dev/null +++ b/src/MusicBeeAudioSource/MusicBeeAudioSource.csproj @@ -0,0 +1,54 @@ + + + + + Debug + AnyCPU + 231b7932-535b-4794-b6cf-f5d11b412ccd + Library + Properties + MusicBeeAudioSource + MusicBeeAudioSource + v4.6.1 + 512 + true + + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/MusicBeeAudioSource/Properties/AssemblyInfo.cs b/src/MusicBeeAudioSource/Properties/AssemblyInfo.cs new file mode 100644 index 00000000..cb329eca --- /dev/null +++ b/src/MusicBeeAudioSource/Properties/AssemblyInfo.cs @@ -0,0 +1,36 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("MusicBeeAudioSource")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("MusicBeeAudioSource")] +[assembly: AssemblyCopyright("Copyright © 2018")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("231b7932-535b-4794-b6cf-f5d11b412ccd")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] From a6940ca9ce84f14f1fe80b6b97d776311c43c8a2 Mon Sep 17 00:00:00 2001 From: Brandon Chong Date: Tue, 27 Nov 2018 21:33:29 -0800 Subject: [PATCH 07/14] Add music bee ipc --- src/AudioBand.sln | 6 + src/MusicBeeAudioSource/Class1.cs | 12 - .../MusicBeeAudioSource.csproj | 39 +- src/MusicBeeAudioSource/MusicBeeIPC/Enums.cs | 353 +++ .../MusicBeeIPC/LICENSE_MusicBeeIPCSDK.txt | 23 + .../MusicBeeIPC/MusicBeeIPC.cs | 2455 +++++++++++++++++ src/MusicBeeAudioSource/MusicBeeIPC/Pack.cs | 486 ++++ .../MusicBeeIPC/Structs.cs | 48 + src/MusicBeeAudioSource/MusicBeeIPC/Unpack.cs | 181 ++ .../Properties/AssemblyInfo.cs | 2 +- 10 files changed, 3574 insertions(+), 31 deletions(-) delete mode 100644 src/MusicBeeAudioSource/Class1.cs create mode 100644 src/MusicBeeAudioSource/MusicBeeIPC/Enums.cs create mode 100644 src/MusicBeeAudioSource/MusicBeeIPC/LICENSE_MusicBeeIPCSDK.txt create mode 100644 src/MusicBeeAudioSource/MusicBeeIPC/MusicBeeIPC.cs create mode 100644 src/MusicBeeAudioSource/MusicBeeIPC/Pack.cs create mode 100644 src/MusicBeeAudioSource/MusicBeeIPC/Structs.cs create mode 100644 src/MusicBeeAudioSource/MusicBeeIPC/Unpack.cs diff --git a/src/AudioBand.sln b/src/AudioBand.sln index 60326643..87a0d331 100644 --- a/src/AudioBand.sln +++ b/src/AudioBand.sln @@ -13,6 +13,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AudioBand.Test", "AudioBand EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "iTunesAudioSource", "iTunesAudioSource\iTunesAudioSource.csproj", "{6D881B7B-3F3F-4613-A83F-75E31EEA5252}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MusicBeeAudioSource", "MusicBeeAudioSource\MusicBeeAudioSource.csproj", "{741DB79C-921D-4D91-85F1-CD10C746F46E}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -39,6 +41,10 @@ Global {6D881B7B-3F3F-4613-A83F-75E31EEA5252}.Debug|Any CPU.Build.0 = Debug|Any CPU {6D881B7B-3F3F-4613-A83F-75E31EEA5252}.Release|Any CPU.ActiveCfg = Release|Any CPU {6D881B7B-3F3F-4613-A83F-75E31EEA5252}.Release|Any CPU.Build.0 = Release|Any CPU + {741DB79C-921D-4D91-85F1-CD10C746F46E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {741DB79C-921D-4D91-85F1-CD10C746F46E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {741DB79C-921D-4D91-85F1-CD10C746F46E}.Release|Any CPU.ActiveCfg = Release|Any CPU + {741DB79C-921D-4D91-85F1-CD10C746F46E}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/src/MusicBeeAudioSource/Class1.cs b/src/MusicBeeAudioSource/Class1.cs deleted file mode 100644 index 9ca5588f..00000000 --- a/src/MusicBeeAudioSource/Class1.cs +++ /dev/null @@ -1,12 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace MusicBeeAudioSource -{ - public class Class1 - { - } -} diff --git a/src/MusicBeeAudioSource/MusicBeeAudioSource.csproj b/src/MusicBeeAudioSource/MusicBeeAudioSource.csproj index 5353acd6..b603e612 100644 --- a/src/MusicBeeAudioSource/MusicBeeAudioSource.csproj +++ b/src/MusicBeeAudioSource/MusicBeeAudioSource.csproj @@ -1,10 +1,10 @@ - + Debug AnyCPU - 231b7932-535b-4794-b6cf-f5d11b412ccd + {741DB79C-921D-4D91-85F1-CD10C746F46E} Library Properties MusicBeeAudioSource @@ -31,24 +31,27 @@ 4 - - - - - - - - - - - - - - + + + + + + + + - + + + + + + + + PreserveNewest + + - + \ No newline at end of file diff --git a/src/MusicBeeAudioSource/MusicBeeIPC/Enums.cs b/src/MusicBeeAudioSource/MusicBeeIPC/Enums.cs new file mode 100644 index 00000000..c157c490 --- /dev/null +++ b/src/MusicBeeAudioSource/MusicBeeIPC/Enums.cs @@ -0,0 +1,353 @@ +//--------------------------------------------------------// +// MusicBeeIPCSDK C# v2.0.0 // +// Copyright © Kerli Low 2014 // +// This file is licensed under the // +// BSD 2-Clause License // +// See LICENSE_MusicBeeIPCSDK for more information. // +//--------------------------------------------------------// + +using System; + +public partial class MusicBeeIPC +{ + public enum Bool + { + False = 0, + True = 1 + } + + public enum Error + { + Error = 0, + NoError = 1, + CommandNotRecognized = 2 + } + + public enum PlayState + { + Undefined = 0, + Loading = 1, + Playing = 3, + Paused = 6, + Stopped = 7 + } + + public enum RepeatMode + { + None = 0, + All = 1, + One = 2 + } + + public enum ReplayGainMode + { + Off = 0, + Track = 1, + Album = 2, + Smart = 3 + } + + public enum FileProperty + { + Url = 2, + Kind = 4, + Format = 5, + Size = 7, + Channels = 8, + SampleRate = 9, + Bitrate = 10, + DateModified = 11, + DateAdded = 12, + LastPlayed = 13, + PlayCount = 14, + SkipCount = 15, + Duration = 16, + NowPlayingListIndex = 78, // only has meaning when called from NowPlayingList_* commands + ReplayGainTrack = 94, + ReplayGainAlbum = 95 + } + + public enum MetaData + { + TrackTitle = 65, + Album = 30, + AlbumArtist = 31, // displayed album artist + AlbumArtistRaw = 34, // stored album artist + Artist = 32, // displayed artist + MultiArtist = 33, // individual artists, separated by a null char + PrimaryArtist = 19, // first artist from multi-artist tagged file, otherwise displayed artist + Artists = 144, + ArtistsWithArtistRole = 145, + ArtistsWithPerformerRole = 146, + ArtistsWithGuestRole = 147, + ArtistsWithRemixerRole = 148, + Artwork = 40, + BeatsPerMin = 41, + Composer = 43, // displayed composer + MultiComposer = 89, // individual composers, separated by a null char + Comment = 44, + Conductor = 45, + Custom1 = 46, + Custom2 = 47, + Custom3 = 48, + Custom4 = 49, + Custom5 = 50, + Custom6 = 96, + Custom7 = 97, + Custom8 = 98, + Custom9 = 99, + Custom10 = 128, + Custom11 = 129, + Custom12 = 130, + Custom13 = 131, + Custom14 = 132, + Custom15 = 133, + Custom16 = 134, + DiscNo = 52, + DiscCount = 54, + Encoder = 55, + Genre = 59, + Genres = 143, + GenreCategory = 60, + Grouping = 61, + Keywords = 84, + HasLyrics = 63, + Lyricist = 62, + Lyrics = 114, + Mood = 64, + Occasion = 66, + Origin = 67, + Publisher = 73, + Quality = 74, + Rating = 75, + RatingLove = 76, + RatingAlbum = 104, + Tempo = 85, + TrackNo = 86, + TrackCount = 87, + Virtual1 = 109, + Virtual2 = 110, + Virtual3 = 111, + Virtual4 = 112, + Virtual5 = 113, + Virtual6 = 122, + Virtual7 = 123, + Virtual8 = 124, + Virtual9 = 125, + Virtual10 = 135, + Virtual11 = 136, + Virtual12 = 137, + Virtual13 = 138, + Virtual14 = 139, + Virtual15 = 140, + Virtual16 = 141, + Year = 88 + } + + [Flags()] + public enum LibraryCategory + { + Music = 0, + Audiobook = 1, + Video = 2, + Inbox = 4 + } + + public enum DataType + { + String = 0, + Number = 1, + DateTime = 2, + Rating = 3 + } + + public enum LyricsType + { + NotSpecified = 0, + Synchronised = 1, + UnSynchronised = 2 + } + + public enum PlayButtonType + { + PreviousTrack = 0, + PlayPause = 1, + NextTrack = 2, + Stop = 3 + } + + public enum PlaylistFormat + { + Unknown = 0, + M3u = 1, + Xspf = 2, + Asx = 3, + Wpl = 4, + Pls = 5, + Auto = 7, + M3uAscii = 8, + AsxFile = 9, + Radio = 10, + M3uExtended = 11, + Mbp = 12 + } + + public enum MusicBeeVersion + { + v2_0 = 0, + v2_1 = 1, + v2_2 = 2, + v2_3 = 3 + } + + public enum Command + { + PlayPause = 100, // WM_USER + Play = 101, // WM_USER + Pause = 102, // WM_USER + Stop = 103, // WM_USER + StopAfterCurrent = 104, // WM_USER + PreviousTrack = 105, // WM_USER + NextTrack = 106, // WM_USER + StartAutoDj = 107, // WM_USER + EndAutoDj = 108, // WM_USER + GetPlayState = 109, // WM_USER + GetPosition = 110, // WM_USER + SetPosition = 111, // WM_USER + GetVolume = 112, // WM_USER + SetVolume = 113, // WM_USER + GetVolumep = 114, // WM_USER + SetVolumep = 115, // WM_USER + GetVolumef = 116, // WM_USER + SetVolumef = 117, // WM_USER + GetMute = 118, // WM_USER + SetMute = 119, // WM_USER + GetShuffle = 120, // WM_USER + SetShuffle = 121, // WM_USER + GetRepeat = 122, // WM_USER + SetRepeat = 123, // WM_USER + GetEqualiserEnabled = 124, // WM_USER + SetEqualiserEnabled = 125, // WM_USER + GetDspEnabled = 126, // WM_USER + SetDspEnabled = 127, // WM_USER + GetScrobbleEnabled = 128, // WM_USER + SetScrobbleEnabled = 129, // WM_USER + ShowEqualiser = 130, // WM_USER + GetAutoDjEnabled = 131, // WM_USER + GetStopAfterCurrentEnabled = 132, // WM_USER + SetStopAfterCurrentEnabled = 133, // WM_USER + GetCrossfade = 134, // WM_USER + SetCrossfade = 135, // WM_USER + GetReplayGainMode = 136, // WM_USER + SetReplayGainMode = 137, // WM_USER + QueueRandomTracks = 138, // WM_USER + GetDuration = 139, // WM_USER + GetFileUrl = 140, // WM_USER + GetFileProperty = 141, // WM_USER + GetFileTag = 142, // WM_USER + GetLyrics = 143, // WM_USER + GetDownloadedLyrics = 144, // WM_USER + GetArtwork = 145, // WM_USER + GetArtworkUrl = 146, // WM_USER + GetDownloadedArtwork = 147, // WM_USER + GetDownloadedArtworkUrl = 148, // WM_USER + GetArtistPicture = 149, // WM_USER + GetArtistPictureUrls = 150, // WM_USER + GetArtistPictureThumb = 151, // WM_USER + IsSoundtrack = 152, // WM_USER + GetSoundtrackPictureUrls = 153, // WM_USER + GetCurrentIndex = 154, // WM_USER + GetNextIndex = 155, // WM_USER + IsAnyPriorTracks = 156, // WM_USER + IsAnyFollowingTracks = 157, // WM_USER + PlayNow = 158, // WM_COPYDATA + QueueNext = 159, // WM_COPYDATA + QueueLast = 160, // WM_COPYDATA + RemoveAt = 161, // WM_USER + ClearNowPlayingList = 162, // WM_USER + MoveFiles = 163, // WM_COPYDATA + ShowNowPlayingAssistant = 164, // WM_USER + GetShowTimeRemaining = 165, // WM_USER + GetShowRatingTrack = 166, // WM_USER + GetShowRatingLove = 167, // WM_USER + GetButtonEnabled = 168, // WM_USER + Jump = 169, // WM_USER + Search = 170, // WM_COPYDATA + SearchFirst = 171, // WM_COPYDATA + SearchIndices = 172, // WM_COPYDATA + SearchFirstIndex = 173, // WM_COPYDATA + SearchAndPlayFirst = 174, // WM_COPYDATA + NowPlayingList_GetListFileUrl = 200, // WM_COPYDATA + NowPlayingList_GetFileProperty = 201, // WM_COPYDATA + NowPlayingList_GetFileTag = 202, // WM_COPYDATA + NowPlayingList_QueryFiles = 203, // WM_COPYDATA + NowPlayingList_QueryGetNextFile = 204, // WM_USER + NowPlayingList_QueryGetAllFiles = 205, // WM_USER + NowPlayingList_QueryFilesEx = 206, // WM_COPYDATA + NowPlayingList_PlayLibraryShuffled = 207, // WM_USER + NowPlayingList_GetItemCount = 208, // WM_USER + Playlist_GetName = 300, // WM_COPYDATA + Playlist_GetType = 301, // WM_COPYDATA + Playlist_IsInList = 302, // WM_COPYDATA + Playlist_QueryPlaylists = 303, // WM_USER + Playlist_QueryGetNextPlaylist = 304, // WM_USER + Playlist_QueryFiles = 305, // WM_COPYDATA + Playlist_QueryGetNextFile = 306, // WM_USER + Playlist_QueryGetAllFiles = 307, // WM_USER + Playlist_QueryFilesEx = 308, // WM_COPYDATA + Playlist_CreatePlaylist = 309, // WM_COPYDATA + Playlist_DeletePlaylist = 310, // WM_COPYDATA + Playlist_SetFiles = 311, // WM_COPYDATA + Playlist_AppendFiles = 312, // WM_COPYDATA + Playlist_RemoveAt = 313, // WM_COPYDATA + Playlist_MoveFiles = 314, // WM_COPYDATA + Playlist_PlayNow = 315, // WM_COPYDATA + Playlist_GetItemCount = 316, // WM_COPYDATA + Library_GetFileProperty = 400, // WM_COPYDATA + Library_GetFileTag = 401, // WM_COPYDATA + Library_SetFileTag = 402, // WM_COPYDATA + Library_CommitTagsToFile = 403, // WM_COPYDATA + Library_GetLyrics = 404, // WM_COPYDATA + Library_GetArtwork = 405, // WM_COPYDATA + Library_GetArtworkUrl = 406, // WM_COPYDATA + Library_GetArtistPicture = 407, // WM_COPYDATA + Library_GetArtistPictureUrls = 408, // WM_COPYDATA + Library_GetArtistPictureThumb = 409, // WM_COPYDATA + Library_AddFileToLibrary = 410, // WM_COPYDATA + Library_QueryFiles = 411, // WM_COPYDATA + Library_QueryGetNextFile = 412, // WM_USER + Library_QueryGetAllFiles = 413, // WM_USER + Library_QueryFilesEx = 414, // WM_COPYDATA + Library_QuerySimilarArtists = 415, // WM_COPYDATA + Library_QueryLookupTable = 416, // WM_COPYDATA + Library_QueryGetLookupTableValue = 417, // WM_COPYDATA + Library_GetItemCount = 418, // WM_USER + Library_Jump = 419, // WM_USER + Library_Search = 420, // WM_COPYDATA + Library_SearchFirst = 421, // WM_COPYDATA + Library_SearchIndices = 422, // WM_COPYDATA + Library_SearchFirstIndex = 423, // WM_COPYDATA + Library_SearchAndPlayFirst = 424, // WM_COPYDATA + Setting_GetFieldName = 700, // WM_COPYDATA + Setting_GetDataType = 701, // WM_COPYDATA + Window_GetHandle = 800, // WM_USER + Window_Close = 801, // WM_USER + Window_Restore = 802, // WM_USER + Window_Minimize = 803, // WM_USER + Window_Maximize = 804, // WM_USER + Window_Move = 805, // WM_USER + Window_Resize = 806, // WM_USER + Window_BringToFront = 807, // WM_USER + Window_GetPosition = 808, // WM_USER + Window_GetSize = 809, // WM_USER + FreeLRESULT = 900, // WM_USER + MusicBeeVersion = 995, // WM_USER + PluginVersion = 996, // WM_USER + Test = 997, // WM_USER For debugging purposes + MessageBox = 998, // WM_COPYDATA For debugging purposes + Probe = 999 // WM_USER To test MusicBeeIPC hwnd is valid + } + + private const int WM_USER = 0x0400, + WM_COPYDATA = 0x004A; +} \ No newline at end of file diff --git a/src/MusicBeeAudioSource/MusicBeeIPC/LICENSE_MusicBeeIPCSDK.txt b/src/MusicBeeAudioSource/MusicBeeIPC/LICENSE_MusicBeeIPCSDK.txt new file mode 100644 index 00000000..86c297cd --- /dev/null +++ b/src/MusicBeeAudioSource/MusicBeeIPC/LICENSE_MusicBeeIPCSDK.txt @@ -0,0 +1,23 @@ +Copyright (c) 2014, Kerli Low +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this +list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, +this list of conditions and the following disclaimer in the documentation +and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/src/MusicBeeAudioSource/MusicBeeIPC/MusicBeeIPC.cs b/src/MusicBeeAudioSource/MusicBeeIPC/MusicBeeIPC.cs new file mode 100644 index 00000000..935fd80d --- /dev/null +++ b/src/MusicBeeAudioSource/MusicBeeIPC/MusicBeeIPC.cs @@ -0,0 +1,2455 @@ +//--------------------------------------------------------// +// MusicBeeIPCSDK C# v2.0.0 // +// Copyright © Kerli Low 2014 // +// This file is licensed under the // +// BSD 2-Clause License // +// See LICENSE_MusicBeeIPCSDK for more information. // +//--------------------------------------------------------// + +using System; +using System.Runtime.InteropServices; +using System.Windows.Forms; + +[System.Security.Permissions.PermissionSet(System.Security.Permissions.SecurityAction.Demand, Name = "FullTrust")] +public partial class MusicBeeIPC +{ + [DllImport("user32")] + public static extern IntPtr SendMessage(IntPtr hwnd, uint wMsg, UIntPtr wParam, IntPtr lParam); + [DllImport("user32")] + public static extern IntPtr FindWindow(IntPtr lpClassName, string lpWindowName); + + public MusicBeeIPC() + { + } + + public bool Probe() + { + return (Error)SendMessage(FindHwnd(), WM_USER, (UIntPtr)Command.Probe, IntPtr.Zero) != Error.Error; + } + + public Error PlayPause() + { + return (Error)SendMessage(FindHwnd(), WM_USER, (UIntPtr)Command.PlayPause, IntPtr.Zero); + } + + public Error Play() + { + return (Error)SendMessage(FindHwnd(), WM_USER, (UIntPtr)Command.Play, IntPtr.Zero); + } + + public Error Pause() + { + return (Error)SendMessage(FindHwnd(), WM_USER, (UIntPtr)Command.Pause, IntPtr.Zero); + } + + public Error Stop() + { + return (Error)SendMessage(FindHwnd(), WM_USER, (UIntPtr)Command.Stop, IntPtr.Zero); + } + + public Error StopAfterCurrent() + { + return (Error)SendMessage(FindHwnd(), WM_USER, (UIntPtr)Command.StopAfterCurrent, IntPtr.Zero); + } + + public Error PreviousTrack() + { + return (Error)SendMessage(FindHwnd(), WM_USER, (UIntPtr)Command.PreviousTrack, IntPtr.Zero); + } + + public Error NextTrack() + { + return (Error)SendMessage(FindHwnd(), WM_USER, (UIntPtr)Command.NextTrack, IntPtr.Zero); + } + + public Error StartAutoDj() + { + return (Error)SendMessage(FindHwnd(), WM_USER, (UIntPtr)Command.StartAutoDj, IntPtr.Zero); + } + + public Error EndAutoDj() + { + return (Error)SendMessage(FindHwnd(), WM_USER, (UIntPtr)Command.EndAutoDj, IntPtr.Zero); + } + + public PlayState GetPlayState() + { + return (PlayState)SendMessage(FindHwnd(), WM_USER, (UIntPtr)Command.GetPlayState, IntPtr.Zero); + } + + public string GetPlayStateStr() + { + switch ((PlayState)SendMessage(FindHwnd(), WM_USER, (UIntPtr)Command.GetPlayState, IntPtr.Zero)) + { + case PlayState.Loading: + return "Loading"; + case PlayState.Playing: + return "Playing"; + case PlayState.Paused: + return "Paused"; + case PlayState.Stopped: + return "Stopped"; + default: + return "Undefined"; + } + } + + public int GetPosition() + { + return (int)SendMessage(FindHwnd(), WM_USER, (UIntPtr)Command.GetPosition, IntPtr.Zero); + } + + public Error SetPosition(int position) + { + return (Error)SendMessage(FindHwnd(), WM_USER, (UIntPtr)Command.SetPosition, (IntPtr)position); + } + + public int Position + { + get + { + return (int)SendMessage(FindHwnd(), WM_USER, (UIntPtr)Command.GetPosition, IntPtr.Zero); + } + + set + { + SendMessage(FindHwnd(), WM_USER, (UIntPtr)Command.SetPosition, (IntPtr)value); + } + } + + // Volume: Value between 0 - 100 + public int GetVolume() + { + return (int)SendMessage(FindHwnd(), WM_USER, (UIntPtr)Command.GetVolume, IntPtr.Zero); + } + + // Volume: Value between 0 - 100 + public Error SetVolume(int volume) + { + return (Error)SendMessage(FindHwnd(), WM_USER, (UIntPtr)Command.SetVolume, (IntPtr)volume); + } + + // Volume: Value between 0 - 100 + public int Volume + { + get + { + return (int)SendMessage(FindHwnd(), WM_USER, (UIntPtr)Command.GetVolume, IntPtr.Zero); + } + + set + { + SendMessage(FindHwnd(), WM_USER, (UIntPtr)Command.SetVolume, (IntPtr)value); + } + } + + // Precise volume: Value between 0 - 10000 + public int GetVolumep() + { + return (int)SendMessage(FindHwnd(), WM_USER, (UIntPtr)Command.GetVolumep, IntPtr.Zero); + } + + // Precise volume: Value between 0 - 10000 + public Error SetVolumep(int volume) + { + return (Error)SendMessage(FindHwnd(), WM_USER, (UIntPtr)Command.SetVolumep, (IntPtr)volume); + } + + // Precise volume: Value between 0 - 10000 + public int Volumep + { + get + { + return (int)SendMessage(FindHwnd(), WM_USER, (UIntPtr)Command.GetVolumep, IntPtr.Zero); + } + + set + { + SendMessage(FindHwnd(), WM_USER, (UIntPtr)Command.SetVolumep, (IntPtr)value); + } + } + + // Floating point volume: Value between 0.0 - 1.0 + public float GetVolumef() + { + return (new FloatInt((int)SendMessage(FindHwnd(), WM_USER, (UIntPtr)Command.GetVolumef, IntPtr.Zero))).f; + } + + // Floating point volume: Value between 0.0 - 1.0 + public Error SetVolumef(float volume) + { + return (Error)SendMessage(FindHwnd(), WM_USER, (UIntPtr)Command.SetVolumef, (IntPtr)(new FloatInt(volume)).i); + } + + // Floating point volume: Value between 0.0 - 1.0 + public float Volumef + { + get + { + return (new FloatInt((int)SendMessage(FindHwnd(), WM_USER, (UIntPtr)Command.GetVolumef, IntPtr.Zero))).f; + } + + set + { + SendMessage(FindHwnd(), WM_USER, (UIntPtr)Command.SetVolumef, (IntPtr)(new FloatInt(value)).i); + } + } + + public bool GetMute() + { + return ToBool(SendMessage(FindHwnd(), WM_USER, (UIntPtr)Command.GetMute, IntPtr.Zero)); + } + + public Error SetMute(bool mute) + { + return (Error)SendMessage(FindHwnd(), WM_USER, (UIntPtr)Command.SetMute, ToIntPtr(mute)); + } + + public bool GetShuffle() + { + return ToBool(SendMessage(FindHwnd(), WM_USER, (UIntPtr)Command.GetShuffle, IntPtr.Zero)); + } + + public Error SetShuffle(bool shuffle) + { + return (Error)SendMessage(FindHwnd(), WM_USER, (UIntPtr)Command.SetShuffle, ToIntPtr(shuffle)); + } + + public RepeatMode GetRepeat() + { + return (RepeatMode)SendMessage(FindHwnd(), WM_USER, (UIntPtr)Command.GetRepeat, IntPtr.Zero); + } + + public Error SetRepeat(RepeatMode repeat) + { + return (Error)SendMessage(FindHwnd(), WM_USER, (UIntPtr)Command.SetRepeat, (IntPtr)repeat); + } + + public bool GetEqualiserEnabled() + { + return ToBool(SendMessage(FindHwnd(), WM_USER, (UIntPtr)Command.GetEqualiserEnabled, IntPtr.Zero)); + } + + public Error SetEqualiserEnabled(bool enabled) + { + return (Error)SendMessage(FindHwnd(), WM_USER, (UIntPtr)Command.SetEqualiserEnabled, ToIntPtr(enabled)); + } + + public bool GetDspEnabled() + { + return ToBool(SendMessage(FindHwnd(), WM_USER, (UIntPtr)Command.GetDspEnabled, IntPtr.Zero)); + } + + public Error SetDspEnabled(bool enabled) + { + return (Error)SendMessage(FindHwnd(), WM_USER, (UIntPtr)Command.SetDspEnabled, ToIntPtr(enabled)); + } + + public bool GetScrobbleEnabled() + { + return ToBool(SendMessage(FindHwnd(), WM_USER, (UIntPtr)Command.GetScrobbleEnabled, IntPtr.Zero)); + } + + public Error SetScrobbleEnabled(bool enabled) + { + return (Error)SendMessage(FindHwnd(), WM_USER, (UIntPtr)Command.SetScrobbleEnabled, ToIntPtr(enabled)); + } + + public Error ShowEqualiser() + { + return (Error)SendMessage(FindHwnd(), WM_USER, (UIntPtr)Command.ShowEqualiser, IntPtr.Zero); + } + + public bool GetAutoDjEnabled() + { + return ToBool(SendMessage(FindHwnd(), WM_USER, (UIntPtr)Command.GetAutoDjEnabled, IntPtr.Zero)); + } + + public bool GetStopAfterCurrentEnabled() + { + return ToBool(SendMessage(FindHwnd(), WM_USER, (UIntPtr)Command.GetStopAfterCurrentEnabled, IntPtr.Zero)); + } + + public Error SetStopAfterCurrentEnabled(bool enabled) + { + return (Error)SendMessage(FindHwnd(), WM_USER, (UIntPtr)Command.SetStopAfterCurrentEnabled, ToIntPtr(enabled)); + } + + public bool GetCrossfade() + { + return ToBool(SendMessage(FindHwnd(), WM_USER, (UIntPtr)Command.GetCrossfade, IntPtr.Zero)); + } + + public Error SetCrossfade(bool crossfade) + { + return (Error)SendMessage(FindHwnd(), WM_USER, (UIntPtr)Command.SetCrossfade, ToIntPtr(crossfade)); + } + + public ReplayGainMode GetReplayGainMode() + { + return (ReplayGainMode)SendMessage(FindHwnd(), WM_USER, (UIntPtr)Command.GetReplayGainMode, IntPtr.Zero); + } + + public Error SetReplayGainMode(ReplayGainMode mode) + { + return (Error)SendMessage(FindHwnd(), WM_USER, (UIntPtr)Command.SetReplayGainMode, (IntPtr)mode); + } + + public Error QueueRandomTracks(int count) + { + return (Error)SendMessage(FindHwnd(), WM_USER, (UIntPtr)Command.QueueRandomTracks, (IntPtr)count); + } + + public int GetDuration() + { + return (int)SendMessage(FindHwnd(), WM_USER, (UIntPtr)Command.GetDuration, IntPtr.Zero); + } + + public string GetFileUrl() + { + string r = ""; + + IntPtr hwnd = FindHwnd(); + + IntPtr lr = SendMessage(hwnd, WM_USER, (UIntPtr)Command.GetFileUrl, IntPtr.Zero); + + Unpack(lr, out r); + + SendMessage(hwnd, WM_USER, (UIntPtr)Command.FreeLRESULT, lr); + + return r; + } + + public string GetFileProperty(FileProperty fileProperty) + { + string r = ""; + + IntPtr hwnd = FindHwnd(); + + IntPtr lr = SendMessage(hwnd, WM_USER, (UIntPtr)Command.GetFileProperty, (IntPtr)fileProperty); + + Unpack(lr, out r); + + SendMessage(hwnd, WM_USER, (UIntPtr)Command.FreeLRESULT, lr); + + return r; + } + + public string GetFileTag(MetaData field) + { + string r = ""; + + IntPtr hwnd = FindHwnd(); + + IntPtr lr = SendMessage(hwnd, WM_USER, (UIntPtr)Command.GetFileTag, (IntPtr)field); + + Unpack(lr, out r); + + SendMessage(hwnd, WM_USER, (UIntPtr)Command.FreeLRESULT, lr); + + return r; + } + + public string GetLyrics() + { + string r = ""; + + IntPtr hwnd = FindHwnd(); + + IntPtr lr = SendMessage(hwnd, WM_USER, (UIntPtr)Command.GetLyrics, IntPtr.Zero); + + Unpack(lr, out r); + + SendMessage(hwnd, WM_USER, (UIntPtr)Command.FreeLRESULT, lr); + + return r; + } + + public string GetDownloadedLyrics() + { + string r = ""; + + IntPtr hwnd = FindHwnd(); + + IntPtr lr = SendMessage(hwnd, WM_USER, (UIntPtr)Command.GetDownloadedLyrics, IntPtr.Zero); + + Unpack(lr, out r); + + SendMessage(hwnd, WM_USER, (UIntPtr)Command.FreeLRESULT, lr); + + return r; + } + + public string GetArtwork() + { + string r = ""; + + IntPtr hwnd = FindHwnd(); + + IntPtr lr = SendMessage(hwnd, WM_USER, (UIntPtr)Command.GetArtwork, IntPtr.Zero); + + Unpack(lr, out r); + + SendMessage(hwnd, WM_USER, (UIntPtr)Command.FreeLRESULT, lr); + + return r; + } + + public string GetArtworkUrl() + { + string r = ""; + + IntPtr hwnd = FindHwnd(); + + IntPtr lr = SendMessage(hwnd, WM_USER, (UIntPtr)Command.GetArtworkUrl, IntPtr.Zero); + + Unpack(lr, out r); + + SendMessage(hwnd, WM_USER, (UIntPtr)Command.FreeLRESULT, lr); + + return r; + } + + public string GetDownloadedArtwork() + { + string r = ""; + + IntPtr hwnd = FindHwnd(); + + IntPtr lr = SendMessage(hwnd, WM_USER, (UIntPtr)Command.GetDownloadedArtwork, IntPtr.Zero); + + Unpack(lr, out r); + + SendMessage(hwnd, WM_USER, (UIntPtr)Command.FreeLRESULT, lr); + + return r; + } + + public string GetDownloadedArtworkUrl() + { + string r = ""; + + IntPtr hwnd = FindHwnd(); + + IntPtr lr = SendMessage(hwnd, WM_USER, (UIntPtr)Command.GetDownloadedArtworkUrl, IntPtr.Zero); + + Unpack(lr, out r); + + SendMessage(hwnd, WM_USER, (UIntPtr)Command.FreeLRESULT, lr); + + return r; + } + + public string GetArtistPicture(int fadingPercent) + { + string r = ""; + + IntPtr hwnd = FindHwnd(); + + IntPtr lr = SendMessage(hwnd, WM_USER, (UIntPtr)Command.GetArtistPicture, (IntPtr)fadingPercent); + + Unpack(lr, out r); + + SendMessage(hwnd, WM_USER, (UIntPtr)Command.FreeLRESULT, lr); + + return r; + } + + public Error GetArtistPictureUrls(bool localOnly, out string[] urls) + { + Error r = Error.Error; + + urls = new string[0]; + + IntPtr hwnd = FindHwnd(); + + IntPtr lr = SendMessage(hwnd, WM_USER, (UIntPtr)Command.GetArtistPicture, ToIntPtr(localOnly)); + + if (Unpack(lr, out urls)) + r = Error.NoError; + + SendMessage(hwnd, WM_USER, (UIntPtr)Command.FreeLRESULT, lr); + + return r; + } + + public string GetArtistPictureThumb() + { + string r = ""; + + IntPtr hwnd = FindHwnd(); + + IntPtr lr = SendMessage(hwnd, WM_USER, (UIntPtr)Command.GetArtistPictureThumb, IntPtr.Zero); + + Unpack(lr, out r); + + SendMessage(hwnd, WM_USER, (UIntPtr)Command.FreeLRESULT, lr); + + return r; + } + + public bool IsSoundtrack() + { + return ToBool(SendMessage(FindHwnd(), WM_USER, (UIntPtr)Command.IsSoundtrack, IntPtr.Zero)); + } + + public Error GetSoundtrackPictureUrls(bool localOnly, out string[] urls) + { + Error r = Error.Error; + + urls = new string[0]; + + IntPtr hwnd = FindHwnd(); + + IntPtr lr = SendMessage(hwnd, WM_USER, (UIntPtr)Command.GetSoundtrackPictureUrls, ToIntPtr(localOnly)); + + if (Unpack(lr, out urls)) + r = Error.NoError; + + SendMessage(hwnd, WM_USER, (UIntPtr)Command.FreeLRESULT, lr); + + return r; + } + + public int GetCurrentIndex() + { + return (int)SendMessage(FindHwnd(), WM_USER, (UIntPtr)Command.GetCurrentIndex, IntPtr.Zero); + } + + public int GetNextIndex(int offset) + { + return (int)SendMessage(FindHwnd(), WM_USER, (UIntPtr)Command.GetNextIndex, (IntPtr)offset); + } + + public bool IsAnyPriorTracks() + { + return ToBool(SendMessage(FindHwnd(), WM_USER, (UIntPtr)Command.IsAnyPriorTracks, IntPtr.Zero)); + } + + public bool IsAnyFollowingTracks() + { + return ToBool(SendMessage(FindHwnd(), WM_USER, (UIntPtr)Command.IsAnyFollowingTracks, IntPtr.Zero)); + } + + public Error PlayNow(string fileUrl) + { + Error r = Error.Error; + + IntPtr cdPtr = IntPtr.Zero; + + COPYDATASTRUCT cds = new COPYDATASTRUCT(); + + try + { + cds = Pack(fileUrl); + + cdPtr = Marshal.AllocHGlobal(Marshal.SizeOf(cds)); + + Marshal.StructureToPtr(cds, cdPtr, false); + + r = (Error)SendMessage(FindHwnd(), WM_COPYDATA, (UIntPtr)Command.PlayNow, cdPtr); + } + finally + { + Free(ref cds); + Marshal.FreeHGlobal(cdPtr); + } + + return r; + } + + public Error QueueNext(string fileUrl) + { + Error r = Error.Error; + + IntPtr cdPtr = IntPtr.Zero; + + COPYDATASTRUCT cds = new COPYDATASTRUCT(); + + try + { + cds = Pack(fileUrl); + + cdPtr = Marshal.AllocHGlobal(Marshal.SizeOf(cds)); + + Marshal.StructureToPtr(cds, cdPtr, false); + + r = (Error)SendMessage(FindHwnd(), WM_COPYDATA, (UIntPtr)Command.QueueNext, cdPtr); + } + finally + { + Free(ref cds); + Marshal.FreeHGlobal(cdPtr); + } + + return r; + } + + public Error QueueLast(string fileUrl) + { + Error r = Error.Error; + + IntPtr cdPtr = IntPtr.Zero; + + COPYDATASTRUCT cds = new COPYDATASTRUCT(); + + try + { + cds = Pack(fileUrl); + + cdPtr = Marshal.AllocHGlobal(Marshal.SizeOf(cds)); + + Marshal.StructureToPtr(cds, cdPtr, false); + + r = (Error)SendMessage(FindHwnd(), WM_COPYDATA, (UIntPtr)Command.QueueLast, cdPtr); + } + finally + { + Free(ref cds); + Marshal.FreeHGlobal(cdPtr); + } + + return r; + } + + public Error RemoveAt(int index) + { + return (Error)SendMessage(FindHwnd(), WM_USER, (UIntPtr)Command.RemoveAt, (IntPtr)index); + } + + public Error ClearNowPlayingList() + { + return (Error)SendMessage(FindHwnd(), WM_USER, (UIntPtr)Command.ClearNowPlayingList, IntPtr.Zero); + } + + public Error MoveFiles(int[] fromIndices, int toIndex) + { + Error r = Error.Error; + + IntPtr cdPtr = IntPtr.Zero; + + COPYDATASTRUCT cds = new COPYDATASTRUCT(); + + try + { + cds = Pack(fromIndices, toIndex); + + cdPtr = Marshal.AllocHGlobal(Marshal.SizeOf(cds)); + + Marshal.StructureToPtr(cds, cdPtr, false); + + r = (Error)SendMessage(FindHwnd(), WM_COPYDATA, (UIntPtr)Command.MoveFiles, cdPtr); + } + finally + { + Free(ref cds); + Marshal.FreeHGlobal(cdPtr); + } + + return r; + } + + public Error ShowNowPlayingAssistant() + { + return (Error)SendMessage(FindHwnd(), WM_USER, (UIntPtr)Command.ShowNowPlayingAssistant, IntPtr.Zero); + } + + public bool GetShowTimeRemaining() + { + return ToBool(SendMessage(FindHwnd(), WM_USER, (UIntPtr)Command.GetShowTimeRemaining, IntPtr.Zero)); + } + + public bool GetShowRatingTrack() + { + return ToBool(SendMessage(FindHwnd(), WM_USER, (UIntPtr)Command.GetShowRatingTrack, IntPtr.Zero)); + } + + public bool GetShowRatingLove() + { + return ToBool(SendMessage(FindHwnd(), WM_USER, (UIntPtr)Command.GetShowRatingLove, IntPtr.Zero)); + } + + public bool GetButtonEnabled(PlayButtonType button) + { + return ToBool(SendMessage(FindHwnd(), WM_USER, (UIntPtr)Command.GetButtonEnabled, (IntPtr)button)); + } + + public Error Jump(int index) + { + return (Error)SendMessage(FindHwnd(), WM_USER, (UIntPtr)Command.Jump, (IntPtr)index); + } + + public Error Search(string query, out string[] result) + { + return Search(query, "Contains", new[] { "ArtistPeople", "Title", "Album" }, out result); + } + + public Error Search(string query, string comparison, string[] fields, out string[] result) + { + result = new string[0]; + + Error r = Error.Error; + + IntPtr cdPtr = IntPtr.Zero; + + COPYDATASTRUCT cds = new COPYDATASTRUCT(); + + try + { + cds = Pack(query, comparison, fields); + + cdPtr = Marshal.AllocHGlobal(Marshal.SizeOf(cds)); + + Marshal.StructureToPtr(cds, cdPtr, false); + + IntPtr hwnd = FindHwnd(); + + IntPtr lr = SendMessage(hwnd, WM_COPYDATA, (UIntPtr)Command.Search, cdPtr); + + if (Unpack(lr, out result)) + r = Error.NoError; + + SendMessage(hwnd, WM_USER, (UIntPtr)Command.FreeLRESULT, lr); + } + finally + { + Free(ref cds); + Marshal.FreeHGlobal(cdPtr); + } + + return r; + } + + public string SearchFirst(string query) + { + return SearchFirst(query, "Contains", new[] { "ArtistPeople", "Title", "Album" }); + } + + public string SearchFirst(string query, string comparison, string[] fields) + { + string result = ""; + + IntPtr cdPtr = IntPtr.Zero; + + COPYDATASTRUCT cds = new COPYDATASTRUCT(); + + try + { + cds = Pack(query, comparison, fields); + + cdPtr = Marshal.AllocHGlobal(Marshal.SizeOf(cds)); + + Marshal.StructureToPtr(cds, cdPtr, false); + + IntPtr hwnd = FindHwnd(); + + IntPtr lr = SendMessage(hwnd, WM_COPYDATA, (UIntPtr)Command.SearchFirst, cdPtr); + + Unpack(lr, out result); + + SendMessage(hwnd, WM_USER, (UIntPtr)Command.FreeLRESULT, lr); + } + finally + { + Free(ref cds); + Marshal.FreeHGlobal(cdPtr); + } + + return result; + } + + public Error SearchIndices(string query, out int[] result) + { + return SearchIndices(query, "Contains", new[] { "ArtistPeople", "Title", "Album" }, out result); + } + + public Error SearchIndices(string query, string comparison, string[] fields, out int[] result) + { + result = new int[0]; + + Error r = Error.Error; + + IntPtr cdPtr = IntPtr.Zero; + + COPYDATASTRUCT cds = new COPYDATASTRUCT(); + + try + { + cds = Pack(query, comparison, fields); + + cdPtr = Marshal.AllocHGlobal(Marshal.SizeOf(cds)); + + Marshal.StructureToPtr(cds, cdPtr, false); + + IntPtr hwnd = FindHwnd(); + + IntPtr lr = SendMessage(hwnd, WM_COPYDATA, (UIntPtr)Command.SearchIndices, cdPtr); + + if (Unpack(lr, out result)) + r = Error.NoError; + + SendMessage(hwnd, WM_USER, (UIntPtr)Command.FreeLRESULT, lr); + } + finally + { + Free(ref cds); + Marshal.FreeHGlobal(cdPtr); + } + + return r; + } + + public int SearchFirstIndex(string query) + { + return SearchFirstIndex(query, "Contains", new[] { "ArtistPeople", "Title", "Album" }); + } + + public int SearchFirstIndex(string query, string comparison, string[] fields) + { + int result = -1; + + IntPtr cdPtr = IntPtr.Zero; + + COPYDATASTRUCT cds = new COPYDATASTRUCT(); + + try + { + cds = Pack(query, comparison, fields); + + cdPtr = Marshal.AllocHGlobal(Marshal.SizeOf(cds)); + + Marshal.StructureToPtr(cds, cdPtr, false); + + result = (int)SendMessage(FindHwnd(), WM_COPYDATA, (UIntPtr)Command.SearchFirstIndex, cdPtr); + } + finally + { + Free(ref cds); + Marshal.FreeHGlobal(cdPtr); + } + + return result; + } + + public Error SearchAndPlayFirst(string query) + { + return SearchAndPlayFirst(query, "Contains", new[] { "ArtistPeople", "Title", "Album" }); + } + + public Error SearchAndPlayFirst(string query, string comparison, string[] fields) + { + Error result = Error.Error; + + IntPtr cdPtr = IntPtr.Zero; + + COPYDATASTRUCT cds = new COPYDATASTRUCT(); + + try + { + cds = Pack(query, comparison, fields); + + cdPtr = Marshal.AllocHGlobal(Marshal.SizeOf(cds)); + + Marshal.StructureToPtr(cds, cdPtr, false); + + result = (Error)SendMessage(FindHwnd(), WM_COPYDATA, (UIntPtr)Command.SearchAndPlayFirst, cdPtr); + } + finally + { + Free(ref cds); + Marshal.FreeHGlobal(cdPtr); + } + + return result; + } + + public string NowPlayingList_GetListFileUrl(int index) + { + string r = ""; + + IntPtr hwnd = FindHwnd(); + + IntPtr lr = SendMessage(hwnd, WM_USER, (UIntPtr)Command.NowPlayingList_GetListFileUrl, (IntPtr)index); + + Unpack(lr, out r); + + SendMessage(hwnd, WM_USER, (UIntPtr)Command.FreeLRESULT, lr); + + return r; + } + + public string NowPlayingList_GetFileProperty(int index, FileProperty fileProperty) + { + string result = ""; + + IntPtr cdPtr = IntPtr.Zero; + + COPYDATASTRUCT cds = new COPYDATASTRUCT(); + + try + { + cds = Pack(index, (int)fileProperty); + + cdPtr = Marshal.AllocHGlobal(Marshal.SizeOf(cds)); + + Marshal.StructureToPtr(cds, cdPtr, false); + + IntPtr hwnd = FindHwnd(); + + IntPtr lr = SendMessage(hwnd, WM_COPYDATA, (UIntPtr)Command.NowPlayingList_GetFileProperty, cdPtr); + + Unpack(lr, out result); + + SendMessage(hwnd, WM_USER, (UIntPtr)Command.FreeLRESULT, lr); + } + finally + { + Free(ref cds); + Marshal.FreeHGlobal(cdPtr); + } + + return result; + } + + public string NowPlayingList_GetFileTag(int index, MetaData field) + { + string result = ""; + + IntPtr cdPtr = IntPtr.Zero; + + COPYDATASTRUCT cds = new COPYDATASTRUCT(); + + try + { + cds = Pack(index, (int)field); + + cdPtr = Marshal.AllocHGlobal(Marshal.SizeOf(cds)); + + Marshal.StructureToPtr(cds, cdPtr, false); + + IntPtr hwnd = FindHwnd(); + + IntPtr lr = SendMessage(hwnd, WM_COPYDATA, (UIntPtr)Command.NowPlayingList_GetFileTag, cdPtr); + + Unpack(lr, out result); + + SendMessage(hwnd, WM_USER, (UIntPtr)Command.FreeLRESULT, lr); + } + finally + { + Free(ref cds); + Marshal.FreeHGlobal(cdPtr); + } + + return result; + } + + public Error NowPlayingList_QueryFiles(string query) + { + Error r = Error.Error; + + IntPtr cdPtr = IntPtr.Zero; + + COPYDATASTRUCT cds = new COPYDATASTRUCT(); + + try + { + cds = Pack(query); + + cdPtr = Marshal.AllocHGlobal(Marshal.SizeOf(cds)); + + Marshal.StructureToPtr(cds, cdPtr, false); + + r = (Error)SendMessage(FindHwnd(), WM_COPYDATA, (UIntPtr)Command.NowPlayingList_QueryFiles, cdPtr); + } + finally + { + Free(ref cds); + Marshal.FreeHGlobal(cdPtr); + } + + return r; + } + + public string NowPlayingList_QueryGetNextFile() + { + string r = ""; + + IntPtr hwnd = FindHwnd(); + + IntPtr lr = SendMessage(hwnd, WM_USER, (UIntPtr)Command.NowPlayingList_QueryGetNextFile, IntPtr.Zero); + + Unpack(lr, out r); + + SendMessage(hwnd, WM_USER, (UIntPtr)Command.FreeLRESULT, lr); + + return r; + } + + public string NowPlayingList_QueryGetAllFiles() + { + string r = ""; + + IntPtr hwnd = FindHwnd(); + + IntPtr lr = SendMessage(hwnd, WM_USER, (UIntPtr)Command.NowPlayingList_QueryGetAllFiles, IntPtr.Zero); + + Unpack(lr, out r); + + SendMessage(hwnd, WM_USER, (UIntPtr)Command.FreeLRESULT, lr); + + return r; + } + + public Error NowPlayingList_QueryFilesEx(string query, out string[] result) + { + result = new string[0]; + + Error r = Error.Error; + + IntPtr cdPtr = IntPtr.Zero; + + COPYDATASTRUCT cds = new COPYDATASTRUCT(); + + try + { + cds = Pack(query); + + cdPtr = Marshal.AllocHGlobal(Marshal.SizeOf(cds)); + + Marshal.StructureToPtr(cds, cdPtr, false); + + IntPtr hwnd = FindHwnd(); + + IntPtr lr = SendMessage(hwnd, WM_COPYDATA, (UIntPtr)Command.NowPlayingList_QueryFilesEx, cdPtr); + + if (Unpack(lr, out result)) + r = Error.NoError; + + SendMessage(hwnd, WM_USER, (UIntPtr)Command.FreeLRESULT, lr); + } + finally + { + Free(ref cds); + Marshal.FreeHGlobal(cdPtr); + } + + return r; + } + + public Error NowPlayingList_PlayLibraryShuffled() + { + return (Error)SendMessage(FindHwnd(), WM_USER, (UIntPtr)Command.NowPlayingList_PlayLibraryShuffled, + IntPtr.Zero); + } + + public int NowPlayingList_GetItemCount() + { + return (int)SendMessage(FindHwnd(), WM_USER, (UIntPtr)Command.NowPlayingList_GetItemCount, IntPtr.Zero); + } + + public string Playlist_GetName(string playlistUrl) + { + string result = ""; + + IntPtr cdPtr = IntPtr.Zero; + + COPYDATASTRUCT cds = new COPYDATASTRUCT(); + + try + { + cds = Pack(playlistUrl); + + cdPtr = Marshal.AllocHGlobal(Marshal.SizeOf(cds)); + + Marshal.StructureToPtr(cds, cdPtr, false); + + IntPtr hwnd = FindHwnd(); + + IntPtr lr = SendMessage(hwnd, WM_COPYDATA, (UIntPtr)Command.Playlist_GetName, cdPtr); + + Unpack(lr, out result); + + SendMessage(hwnd, WM_USER, (UIntPtr)Command.FreeLRESULT, lr); + } + finally + { + Free(ref cds); + Marshal.FreeHGlobal(cdPtr); + } + + return result; + } + + public PlaylistFormat Playlist_GetType(string playlistUrl) + { + PlaylistFormat r = PlaylistFormat.Unknown; + + IntPtr cdPtr = IntPtr.Zero; + + COPYDATASTRUCT cds = new COPYDATASTRUCT(); + + try + { + cds = Pack(playlistUrl); + + cdPtr = Marshal.AllocHGlobal(Marshal.SizeOf(cds)); + + Marshal.StructureToPtr(cds, cdPtr, false); + + r = (PlaylistFormat)SendMessage(FindHwnd(), WM_COPYDATA, (UIntPtr)Command.Playlist_GetType, cdPtr); + } + finally + { + Free(ref cds); + Marshal.FreeHGlobal(cdPtr); + } + + return r; + } + + public bool Playlist_IsInList(string playlistUrl, string filename) + { + bool r = false; + + IntPtr cdPtr = IntPtr.Zero; + + COPYDATASTRUCT cds = new COPYDATASTRUCT(); + + try + { + cds = Pack(playlistUrl, filename); + + cdPtr = Marshal.AllocHGlobal(Marshal.SizeOf(cds)); + + Marshal.StructureToPtr(cds, cdPtr, false); + + r = ToBool(SendMessage(FindHwnd(), WM_COPYDATA, (UIntPtr)Command.Playlist_IsInList, cdPtr)); + } + finally + { + Free(ref cds); + Marshal.FreeHGlobal(cdPtr); + } + + return r; + } + + public Error Playlist_QueryPlaylists() + { + return (Error)SendMessage(FindHwnd(), WM_USER, (UIntPtr)Command.Playlist_QueryPlaylists, IntPtr.Zero); + } + + public string Playlist_QueryGetNextPlaylist() + { + string r = ""; + + IntPtr hwnd = FindHwnd(); + + IntPtr lr = SendMessage(hwnd, WM_USER, (UIntPtr)Command.Playlist_QueryGetNextPlaylist, IntPtr.Zero); + + Unpack(lr, out r); + + SendMessage(hwnd, WM_USER, (UIntPtr)Command.FreeLRESULT, lr); + + return r; + } + + public Error Playlist_QueryFiles(string playlistUrl) + { + Error r = Error.Error; + + IntPtr cdPtr = IntPtr.Zero; + + COPYDATASTRUCT cds = new COPYDATASTRUCT(); + + try + { + cds = Pack(playlistUrl); + + cdPtr = Marshal.AllocHGlobal(Marshal.SizeOf(cds)); + + Marshal.StructureToPtr(cds, cdPtr, false); + + r = (Error)SendMessage(FindHwnd(), WM_COPYDATA, (UIntPtr)Command.Playlist_QueryFiles, cdPtr); + } + finally + { + Free(ref cds); + Marshal.FreeHGlobal(cdPtr); + } + + return r; + } + + public string Playlist_QueryGetNextFile() + { + string r = ""; + + IntPtr hwnd = FindHwnd(); + + IntPtr lr = SendMessage(hwnd, WM_USER, (UIntPtr)Command.Playlist_QueryGetNextFile, IntPtr.Zero); + + Unpack(lr, out r); + + SendMessage(hwnd, WM_USER, (UIntPtr)Command.FreeLRESULT, lr); + + return r; + } + + public string Playlist_QueryGetAllFiles() + { + string r = ""; + + IntPtr hwnd = FindHwnd(); + + IntPtr lr = SendMessage(hwnd, WM_USER, (UIntPtr)Command.Playlist_QueryGetAllFiles, IntPtr.Zero); + + Unpack(lr, out r); + + SendMessage(hwnd, WM_USER, (UIntPtr)Command.FreeLRESULT, lr); + + return r; + } + + public Error Playlist_QueryFilesEx(string playlistUrl, out string[] result) + { + result = new string[0]; + + Error r = Error.Error; + + IntPtr cdPtr = IntPtr.Zero; + + COPYDATASTRUCT cds = new COPYDATASTRUCT(); + + try + { + cds = Pack(playlistUrl); + + cdPtr = Marshal.AllocHGlobal(Marshal.SizeOf(cds)); + + Marshal.StructureToPtr(cds, cdPtr, false); + + IntPtr hwnd = FindHwnd(); + + IntPtr lr = SendMessage(hwnd, WM_COPYDATA, (UIntPtr)Command.Playlist_QueryFilesEx, cdPtr); + + if (Unpack(lr, out result)) + r = Error.NoError; + + SendMessage(hwnd, WM_USER, (UIntPtr)Command.FreeLRESULT, lr); + } + finally + { + Free(ref cds); + Marshal.FreeHGlobal(cdPtr); + } + + return r; + } + + public string Playlist_CreatePlaylist(string folderName, string playlistName, string[] filenames) + { + string r = ""; + + IntPtr cdPtr = IntPtr.Zero; + + COPYDATASTRUCT cds = new COPYDATASTRUCT(); + + try + { + cds = Pack(folderName, playlistName, filenames); + + cdPtr = Marshal.AllocHGlobal(Marshal.SizeOf(cds)); + + Marshal.StructureToPtr(cds, cdPtr, false); + + IntPtr hwnd = FindHwnd(); + + IntPtr lr = SendMessage(hwnd, WM_COPYDATA, (UIntPtr)Command.Playlist_CreatePlaylist, cdPtr); + + Unpack(lr, out r); + + SendMessage(hwnd, WM_USER, (UIntPtr)Command.FreeLRESULT, lr); + } + finally + { + Free(ref cds); + Marshal.FreeHGlobal(cdPtr); + } + + return r; + } + + public Error Playlist_DeletePlaylist(string playlistUrl) + { + Error r = Error.Error; + + IntPtr cdPtr = IntPtr.Zero; + + COPYDATASTRUCT cds = new COPYDATASTRUCT(); + + try + { + cds = Pack(playlistUrl); + + cdPtr = Marshal.AllocHGlobal(Marshal.SizeOf(cds)); + + Marshal.StructureToPtr(cds, cdPtr, false); + + r = (Error)SendMessage(FindHwnd(), WM_COPYDATA, (UIntPtr)Command.Playlist_DeletePlaylist, cdPtr); + } + finally + { + Free(ref cds); + Marshal.FreeHGlobal(cdPtr); + } + + return r; + } + + public Error Playlist_SetFiles(string playlistUrl, string[] filenames) + { + Error r = Error.Error; + + IntPtr cdPtr = IntPtr.Zero; + + COPYDATASTRUCT cds = new COPYDATASTRUCT(); + + try + { + cds = Pack(playlistUrl, filenames); + + cdPtr = Marshal.AllocHGlobal(Marshal.SizeOf(cds)); + + Marshal.StructureToPtr(cds, cdPtr, false); + + r = (Error)SendMessage(FindHwnd(), WM_COPYDATA, (UIntPtr)Command.Playlist_SetFiles, cdPtr); + } + finally + { + Free(ref cds); + Marshal.FreeHGlobal(cdPtr); + } + + return r; + } + + public Error Playlist_AppendFiles(string playlistUrl, string[] filenames) + { + Error r = Error.Error; + + IntPtr cdPtr = IntPtr.Zero; + + COPYDATASTRUCT cds = new COPYDATASTRUCT(); + + try + { + cds = Pack(playlistUrl, filenames); + + cdPtr = Marshal.AllocHGlobal(Marshal.SizeOf(cds)); + + Marshal.StructureToPtr(cds, cdPtr, false); + + r = (Error)SendMessage(FindHwnd(), WM_COPYDATA, (UIntPtr)Command.Playlist_AppendFiles, cdPtr); + } + finally + { + Free(ref cds); + Marshal.FreeHGlobal(cdPtr); + } + + return r; + } + + public Error Playlist_RemoveAt(string playlistUrl, int index) + { + Error r = Error.Error; + + IntPtr cdPtr = IntPtr.Zero; + + COPYDATASTRUCT cds = new COPYDATASTRUCT(); + + try + { + cds = Pack(playlistUrl, index); + + cdPtr = Marshal.AllocHGlobal(Marshal.SizeOf(cds)); + + Marshal.StructureToPtr(cds, cdPtr, false); + + r = (Error)SendMessage(FindHwnd(), WM_COPYDATA, (UIntPtr)Command.Playlist_RemoveAt, cdPtr); + } + finally + { + Free(ref cds); + Marshal.FreeHGlobal(cdPtr); + } + + return r; + } + + public Error Playlist_MoveFiles(string playlistUrl, int[] fromIndices, int toIndex) + { + Error r = Error.Error; + + IntPtr cdPtr = IntPtr.Zero; + + COPYDATASTRUCT cds = new COPYDATASTRUCT(); + + try + { + cds = Pack(playlistUrl, fromIndices, toIndex); + + cdPtr = Marshal.AllocHGlobal(Marshal.SizeOf(cds)); + + Marshal.StructureToPtr(cds, cdPtr, false); + + r = (Error)SendMessage(FindHwnd(), WM_COPYDATA, (UIntPtr)Command.Playlist_MoveFiles, cdPtr); + } + finally + { + Free(ref cds); + Marshal.FreeHGlobal(cdPtr); + } + + return r; + } + + public Error Playlist_PlayNow(string playlistUrl) + { + Error r = Error.Error; + + IntPtr cdPtr = IntPtr.Zero; + + COPYDATASTRUCT cds = new COPYDATASTRUCT(); + + try + { + cds = Pack(playlistUrl); + + cdPtr = Marshal.AllocHGlobal(Marshal.SizeOf(cds)); + + Marshal.StructureToPtr(cds, cdPtr, false); + + r = (Error)SendMessage(FindHwnd(), WM_COPYDATA, (UIntPtr)Command.Playlist_PlayNow, cdPtr); + } + finally + { + Free(ref cds); + Marshal.FreeHGlobal(cdPtr); + } + + return r; + } + + public int Playlist_GetItemCount(string playlistUrl) + { + int r = 0; + + IntPtr cdPtr = IntPtr.Zero; + + COPYDATASTRUCT cds = new COPYDATASTRUCT(); + + try + { + cds = Pack(playlistUrl); + + cdPtr = Marshal.AllocHGlobal(Marshal.SizeOf(cds)); + + Marshal.StructureToPtr(cds, cdPtr, false); + + r = (int)SendMessage(FindHwnd(), WM_COPYDATA, (UIntPtr)Command.Playlist_GetItemCount, cdPtr); + } + finally + { + Free(ref cds); + Marshal.FreeHGlobal(cdPtr); + } + + return r; + } + + public string Library_GetFileProperty(string fileUrl, FileProperty fileProperty) + { + string result = ""; + + IntPtr cdPtr = IntPtr.Zero; + + COPYDATASTRUCT cds = new COPYDATASTRUCT(); + + try + { + cds = Pack(fileUrl, (int)fileProperty); + + cdPtr = Marshal.AllocHGlobal(Marshal.SizeOf(cds)); + + Marshal.StructureToPtr(cds, cdPtr, false); + + IntPtr hwnd = FindHwnd(); + + IntPtr lr = SendMessage(hwnd, WM_COPYDATA, (UIntPtr)Command.Library_GetFileProperty, cdPtr); + + Unpack(lr, out result); + + SendMessage(hwnd, WM_USER, (UIntPtr)Command.FreeLRESULT, lr); + } + finally + { + Free(ref cds); + Marshal.FreeHGlobal(cdPtr); + } + + return result; + } + + public string Library_GetFileTag(string fileUrl, MetaData field) + { + string result = ""; + + IntPtr cdPtr = IntPtr.Zero; + + COPYDATASTRUCT cds = new COPYDATASTRUCT(); + + try + { + cds = Pack(fileUrl, (int)field); + + cdPtr = Marshal.AllocHGlobal(Marshal.SizeOf(cds)); + + Marshal.StructureToPtr(cds, cdPtr, false); + + IntPtr hwnd = FindHwnd(); + + IntPtr lr = SendMessage(hwnd, WM_COPYDATA, (UIntPtr)Command.Library_GetFileTag, cdPtr); + + Unpack(lr, out result); + + SendMessage(hwnd, WM_USER, (UIntPtr)Command.FreeLRESULT, lr); + } + finally + { + Free(ref cds); + Marshal.FreeHGlobal(cdPtr); + } + + return result; + } + + public Error Library_SetFileTag(string fileUrl, MetaData field, string value) + { + Error r = Error.Error; + + IntPtr cdPtr = IntPtr.Zero; + + COPYDATASTRUCT cds = new COPYDATASTRUCT(); + + try + { + cds = Pack(fileUrl, (int)field, value); + + cdPtr = Marshal.AllocHGlobal(Marshal.SizeOf(cds)); + + Marshal.StructureToPtr(cds, cdPtr, false); + + r = (Error)SendMessage(FindHwnd(), WM_COPYDATA, (UIntPtr)Command.Library_SetFileTag, cdPtr); + } + finally + { + Free(ref cds); + Marshal.FreeHGlobal(cdPtr); + } + + return r; + } + + public Error Library_CommitTagsToFile(string fileUrl) + { + Error r = Error.Error; + + IntPtr cdPtr = IntPtr.Zero; + + COPYDATASTRUCT cds = new COPYDATASTRUCT(); + + try + { + cds = Pack(fileUrl); + + cdPtr = Marshal.AllocHGlobal(Marshal.SizeOf(cds)); + + Marshal.StructureToPtr(cds, cdPtr, false); + + r = (Error)SendMessage(FindHwnd(), WM_COPYDATA, (UIntPtr)Command.Library_CommitTagsToFile, cdPtr); + } + finally + { + Free(ref cds); + Marshal.FreeHGlobal(cdPtr); + } + + return r; + } + + public string Library_GetLyrics(string fileUrl, LyricsType type) + { + string result = ""; + + IntPtr cdPtr = IntPtr.Zero; + + COPYDATASTRUCT cds = new COPYDATASTRUCT(); + + try + { + cds = Pack(fileUrl, (int)type); + + cdPtr = Marshal.AllocHGlobal(Marshal.SizeOf(cds)); + + Marshal.StructureToPtr(cds, cdPtr, false); + + IntPtr hwnd = FindHwnd(); + + IntPtr lr = SendMessage(hwnd, WM_COPYDATA, (UIntPtr)Command.Library_GetLyrics, cdPtr); + + Unpack(lr, out result); + + SendMessage(hwnd, WM_USER, (UIntPtr)Command.FreeLRESULT, lr); + } + finally + { + Free(ref cds); + Marshal.FreeHGlobal(cdPtr); + } + + return result; + } + + public string Library_GetArtwork(string fileUrl, int index) + { + string result = ""; + + IntPtr cdPtr = IntPtr.Zero; + + COPYDATASTRUCT cds = new COPYDATASTRUCT(); + + try + { + cds = Pack(fileUrl, index); + + cdPtr = Marshal.AllocHGlobal(Marshal.SizeOf(cds)); + + Marshal.StructureToPtr(cds, cdPtr, false); + + IntPtr hwnd = FindHwnd(); + + IntPtr lr = SendMessage(hwnd, WM_COPYDATA, (UIntPtr)Command.Library_GetArtwork, cdPtr); + + Unpack(lr, out result); + + SendMessage(hwnd, WM_USER, (UIntPtr)Command.FreeLRESULT, lr); + } + finally + { + Free(ref cds); + Marshal.FreeHGlobal(cdPtr); + } + + return result; + } + + public string Library_GetArtworkUrl(string fileUrl, int index) + { + string result = ""; + + IntPtr cdPtr = IntPtr.Zero; + + COPYDATASTRUCT cds = new COPYDATASTRUCT(); + + try + { + cds = Pack(fileUrl, index); + + cdPtr = Marshal.AllocHGlobal(Marshal.SizeOf(cds)); + + Marshal.StructureToPtr(cds, cdPtr, false); + + IntPtr hwnd = FindHwnd(); + + IntPtr lr = SendMessage(hwnd, WM_COPYDATA, (UIntPtr)Command.Library_GetArtworkUrl, cdPtr); + + Unpack(lr, out result); + + SendMessage(hwnd, WM_USER, (UIntPtr)Command.FreeLRESULT, lr); + } + finally + { + Free(ref cds); + Marshal.FreeHGlobal(cdPtr); + } + + return result; + } + + public string Library_GetArtistPicture(string artistName, int fadingPercent, int fadingColor) + { + string result = ""; + + IntPtr cdPtr = IntPtr.Zero; + + COPYDATASTRUCT cds = new COPYDATASTRUCT(); + + try + { + cds = Pack(artistName, fadingPercent, fadingColor); + + cdPtr = Marshal.AllocHGlobal(Marshal.SizeOf(cds)); + + Marshal.StructureToPtr(cds, cdPtr, false); + + IntPtr hwnd = FindHwnd(); + + IntPtr lr = SendMessage(hwnd, WM_COPYDATA, (UIntPtr)Command.Library_GetArtistPicture, cdPtr); + + Unpack(lr, out result); + + SendMessage(hwnd, WM_USER, (UIntPtr)Command.FreeLRESULT, lr); + } + finally + { + Free(ref cds); + Marshal.FreeHGlobal(cdPtr); + } + + return result; + } + + public Error Library_GetArtistPictureUrls(string artistName, bool localOnly, out string[] urls) + { + urls = new string[0]; + + Error r = Error.Error; + + IntPtr cdPtr = IntPtr.Zero; + + COPYDATASTRUCT cds = new COPYDATASTRUCT(); + + try + { + cds = Pack(artistName, localOnly); + + cdPtr = Marshal.AllocHGlobal(Marshal.SizeOf(cds)); + + Marshal.StructureToPtr(cds, cdPtr, false); + + IntPtr hwnd = FindHwnd(); + + IntPtr lr = SendMessage(hwnd, WM_COPYDATA, (UIntPtr)Command.Library_GetArtistPictureUrls, cdPtr); + + if (Unpack(lr, out urls)) + r = Error.NoError; + + SendMessage(hwnd, WM_USER, (UIntPtr)Command.FreeLRESULT, lr); + } + finally + { + Free(ref cds); + Marshal.FreeHGlobal(cdPtr); + } + + return r; + } + + public string Library_GetArtistPictureThumb(string artistName) + { + string result = ""; + + IntPtr cdPtr = IntPtr.Zero; + + COPYDATASTRUCT cds = new COPYDATASTRUCT(); + + try + { + cds = Pack(artistName); + + cdPtr = Marshal.AllocHGlobal(Marshal.SizeOf(cds)); + + Marshal.StructureToPtr(cds, cdPtr, false); + + IntPtr hwnd = FindHwnd(); + + IntPtr lr = SendMessage(hwnd, WM_COPYDATA, (UIntPtr)Command.Library_GetArtistPictureThumb, cdPtr); + + Unpack(lr, out result); + + SendMessage(hwnd, WM_USER, (UIntPtr)Command.FreeLRESULT, lr); + } + finally + { + Free(ref cds); + Marshal.FreeHGlobal(cdPtr); + } + + return result; + } + + public string Library_AddFileToLibrary(string fileUrl, LibraryCategory category) + { + string result = ""; + + IntPtr cdPtr = IntPtr.Zero; + + COPYDATASTRUCT cds = new COPYDATASTRUCT(); + + try + { + cds = Pack(fileUrl, (int)category); + + cdPtr = Marshal.AllocHGlobal(Marshal.SizeOf(cds)); + + Marshal.StructureToPtr(cds, cdPtr, false); + + IntPtr hwnd = FindHwnd(); + + IntPtr lr = SendMessage(hwnd, WM_COPYDATA, (UIntPtr)Command.Library_AddFileToLibrary, cdPtr); + + Unpack(lr, out result); + + SendMessage(hwnd, WM_USER, (UIntPtr)Command.FreeLRESULT, lr); + } + finally + { + Free(ref cds); + Marshal.FreeHGlobal(cdPtr); + } + + return result; + } + + public Error Library_QueryFiles(string query) + { + Error r = Error.Error; + + IntPtr cdPtr = IntPtr.Zero; + + COPYDATASTRUCT cds = new COPYDATASTRUCT(); + + try + { + cds = Pack(query); + + cdPtr = Marshal.AllocHGlobal(Marshal.SizeOf(cds)); + + Marshal.StructureToPtr(cds, cdPtr, false); + + r = (Error)SendMessage(FindHwnd(), WM_COPYDATA, (UIntPtr)Command.Library_QueryFiles, cdPtr); + } + finally + { + Free(ref cds); + Marshal.FreeHGlobal(cdPtr); + } + + return r; + } + + public string Library_QueryGetNextFile() + { + string r = ""; + + IntPtr hwnd = FindHwnd(); + + IntPtr lr = SendMessage(hwnd, WM_USER, (UIntPtr)Command.Library_QueryGetNextFile, IntPtr.Zero); + + Unpack(lr, out r); + + SendMessage(hwnd, WM_USER, (UIntPtr)Command.FreeLRESULT, lr); + + return r; + } + + public string Library_QueryGetAllFiles() + { + string r = ""; + + IntPtr hwnd = FindHwnd(); + + IntPtr lr = SendMessage(hwnd, WM_USER, (UIntPtr)Command.Library_QueryGetAllFiles, IntPtr.Zero); + + Unpack(lr, out r); + + SendMessage(hwnd, WM_USER, (UIntPtr)Command.FreeLRESULT, lr); + + return r; + } + + public Error Library_QueryFilesEx(string query, out string[] result) + { + result = new string[0]; + + Error r = Error.Error; + + IntPtr cdPtr = IntPtr.Zero; + + COPYDATASTRUCT cds = new COPYDATASTRUCT(); + + try + { + cds = Pack(query); + + cdPtr = Marshal.AllocHGlobal(Marshal.SizeOf(cds)); + + Marshal.StructureToPtr(cds, cdPtr, false); + + IntPtr hwnd = FindHwnd(); + + IntPtr lr = SendMessage(hwnd, WM_COPYDATA, (UIntPtr)Command.Library_QueryFilesEx, cdPtr); + + if (Unpack(lr, out result)) + r = Error.NoError; + + SendMessage(hwnd, WM_USER, (UIntPtr)Command.FreeLRESULT, lr); + } + finally + { + Free(ref cds); + Marshal.FreeHGlobal(cdPtr); + } + + return r; + } + + public string Library_QuerySimilarArtists(string artistName, double minimumArtistSimilarityRating) + { + string result = ""; + + IntPtr cdPtr = IntPtr.Zero; + + COPYDATASTRUCT cds = new COPYDATASTRUCT(); + + try + { + cds = Pack(artistName, minimumArtistSimilarityRating); + + cdPtr = Marshal.AllocHGlobal(Marshal.SizeOf(cds)); + + Marshal.StructureToPtr(cds, cdPtr, false); + + IntPtr hwnd = FindHwnd(); + + IntPtr lr = SendMessage(hwnd, WM_COPYDATA, (UIntPtr)Command.Library_QuerySimilarArtists, cdPtr); + + Unpack(lr, out result); + + SendMessage(hwnd, WM_USER, (UIntPtr)Command.FreeLRESULT, lr); + } + finally + { + Free(ref cds); + Marshal.FreeHGlobal(cdPtr); + } + + return result; + } + + public Error Library_QueryLookupTable(string keyTags, string valueTags, string query) + { + Error r = Error.Error; + + IntPtr cdPtr = IntPtr.Zero; + + COPYDATASTRUCT cds = new COPYDATASTRUCT(); + + try + { + cds = Pack(keyTags, valueTags, query); + + cdPtr = Marshal.AllocHGlobal(Marshal.SizeOf(cds)); + + Marshal.StructureToPtr(cds, cdPtr, false); + + r = (Error)SendMessage(FindHwnd(), WM_COPYDATA, (UIntPtr)Command.Library_QueryLookupTable, cdPtr); + } + finally + { + Free(ref cds); + Marshal.FreeHGlobal(cdPtr); + } + + return r; + } + + public string Library_QueryGetLookupTableValue(string key) + { + string result = ""; + + IntPtr cdPtr = IntPtr.Zero; + + COPYDATASTRUCT cds = new COPYDATASTRUCT(); + + try + { + cds = Pack(key); + + cdPtr = Marshal.AllocHGlobal(Marshal.SizeOf(cds)); + + Marshal.StructureToPtr(cds, cdPtr, false); + + IntPtr hwnd = FindHwnd(); + + IntPtr lr = SendMessage(hwnd, WM_COPYDATA, (UIntPtr)Command.Library_QueryGetLookupTableValue, cdPtr); + + Unpack(lr, out result); + + SendMessage(hwnd, WM_USER, (UIntPtr)Command.FreeLRESULT, lr); + } + finally + { + Free(ref cds); + Marshal.FreeHGlobal(cdPtr); + } + + return result; + } + + public Error Library_Jump(int index) + { + return (Error)SendMessage(FindHwnd(), WM_USER, (UIntPtr)Command.Library_Jump, (IntPtr)index); + } + + public Error Library_Search(string query, out string[] result) + { + return Library_Search(query, "Contains", new[] { "ArtistPeople", "Title", "Album" }, out result); + } + + public Error Library_Search(string query, string comparison, string[] fields, out string[] result) + { + result = new string[0]; + + Error r = Error.Error; + + IntPtr cdPtr = IntPtr.Zero; + + COPYDATASTRUCT cds = new COPYDATASTRUCT(); + + try + { + cds = Pack(query, comparison, fields); + + cdPtr = Marshal.AllocHGlobal(Marshal.SizeOf(cds)); + + Marshal.StructureToPtr(cds, cdPtr, false); + + IntPtr hwnd = FindHwnd(); + + IntPtr lr = SendMessage(hwnd, WM_COPYDATA, (UIntPtr)Command.Library_Search, cdPtr); + + if (Unpack(lr, out result)) + r = Error.NoError; + + SendMessage(hwnd, WM_USER, (UIntPtr)Command.FreeLRESULT, lr); + } + finally + { + Free(ref cds); + Marshal.FreeHGlobal(cdPtr); + } + + return r; + } + + public string Library_SearchFirst(string query) + { + return Library_SearchFirst(query, "Contains", new[] { "ArtistPeople", "Title", "Album" }); + } + + public string Library_SearchFirst(string query, string comparison, string[] fields) + { + string result = ""; + + IntPtr cdPtr = IntPtr.Zero; + + COPYDATASTRUCT cds = new COPYDATASTRUCT(); + + try + { + cds = Pack(query, comparison, fields); + + cdPtr = Marshal.AllocHGlobal(Marshal.SizeOf(cds)); + + Marshal.StructureToPtr(cds, cdPtr, false); + + IntPtr hwnd = FindHwnd(); + + IntPtr lr = SendMessage(hwnd, WM_COPYDATA, (UIntPtr)Command.Library_SearchFirst, cdPtr); + + Unpack(lr, out result); + + SendMessage(hwnd, WM_USER, (UIntPtr)Command.FreeLRESULT, lr); + } + finally + { + Free(ref cds); + Marshal.FreeHGlobal(cdPtr); + } + + return result; + } + + public Error Library_SearchIndices(string query, out int[] result) + { + return Library_SearchIndices(query, "Contains", new[] { "ArtistPeople", "Title", "Album" }, out result); + } + + public Error Library_SearchIndices(string query, string comparison, string[] fields, out int[] result) + { + result = new int[0]; + + Error r = Error.Error; + + IntPtr cdPtr = IntPtr.Zero; + + COPYDATASTRUCT cds = new COPYDATASTRUCT(); + + try + { + cds = Pack(query, comparison, fields); + + cdPtr = Marshal.AllocHGlobal(Marshal.SizeOf(cds)); + + Marshal.StructureToPtr(cds, cdPtr, false); + + IntPtr hwnd = FindHwnd(); + + IntPtr lr = SendMessage(hwnd, WM_COPYDATA, (UIntPtr)Command.Library_SearchIndices, cdPtr); + + if (Unpack(lr, out result)) + r = Error.NoError; + + SendMessage(hwnd, WM_USER, (UIntPtr)Command.FreeLRESULT, lr); + } + finally + { + Free(ref cds); + Marshal.FreeHGlobal(cdPtr); + } + + return r; + } + + public int Library_SearchFirstIndex(string query) + { + return Library_SearchFirstIndex(query, "Contains", new[] { "ArtistPeople", "Title", "Album" }); + } + + public int Library_SearchFirstIndex(string query, string comparison, string[] fields) + { + int result = -1; + + IntPtr cdPtr = IntPtr.Zero; + + COPYDATASTRUCT cds = new COPYDATASTRUCT(); + + try + { + cds = Pack(query, comparison, fields); + + cdPtr = Marshal.AllocHGlobal(Marshal.SizeOf(cds)); + + Marshal.StructureToPtr(cds, cdPtr, false); + + result = (int)SendMessage(FindHwnd(), WM_COPYDATA, (UIntPtr)Command.Library_SearchFirstIndex, cdPtr); + } + finally + { + Free(ref cds); + Marshal.FreeHGlobal(cdPtr); + } + + return result; + } + + public Error Library_SearchAndPlayFirst(string query) + { + return Library_SearchAndPlayFirst(query, "Contains", new[] { "ArtistPeople", "Title", "Album" }); + } + + public Error Library_SearchAndPlayFirst(string query, string comparison, string[] fields) + { + Error result = Error.Error; + + IntPtr cdPtr = IntPtr.Zero; + + COPYDATASTRUCT cds = new COPYDATASTRUCT(); + + try + { + cds = Pack(query, comparison, fields); + + cdPtr = Marshal.AllocHGlobal(Marshal.SizeOf(cds)); + + Marshal.StructureToPtr(cds, cdPtr, false); + + result = (Error)SendMessage(FindHwnd(), WM_COPYDATA, (UIntPtr)Command.Library_SearchAndPlayFirst, cdPtr); + } + finally + { + Free(ref cds); + Marshal.FreeHGlobal(cdPtr); + } + + return result; + } + + public string Setting_GetFieldName(MetaData field) + { + string r = ""; + + IntPtr hwnd = FindHwnd(); + + IntPtr lr = SendMessage(hwnd, WM_USER, (UIntPtr)Command.Setting_GetFieldName, (IntPtr)field); + + Unpack(lr, out r); + + SendMessage(hwnd, WM_USER, (UIntPtr)Command.FreeLRESULT, lr); + + return r; + } + + public DataType Setting_GetDataType(MetaData field) + { + return (DataType)SendMessage(FindHwnd(), WM_USER, (UIntPtr)Command.Setting_GetDataType, (IntPtr)field); + } + + public IntPtr Window_GetHandle() + { + return SendMessage(FindHwnd(), WM_USER, (UIntPtr)Command.Window_GetHandle, IntPtr.Zero); + } + + public Error Window_Close() + { + return (Error)SendMessage(FindHwnd(), WM_USER, (UIntPtr)Command.Window_Close, IntPtr.Zero); + } + + public Error Window_Restore() + { + return (Error)SendMessage(FindHwnd(), WM_USER, (UIntPtr)Command.Window_Restore, IntPtr.Zero); + } + + public Error Window_Minimize() + { + return (Error)SendMessage(FindHwnd(), WM_USER, (UIntPtr)Command.Window_Minimize, IntPtr.Zero); + } + + public Error Window_Maximize() + { + return (Error)SendMessage(FindHwnd(), WM_USER, (UIntPtr)Command.Window_Maximize, IntPtr.Zero); + } + + public Error Window_Move(int x, int y) + { + Error r = Error.Error; + + IntPtr cdPtr = IntPtr.Zero; + + COPYDATASTRUCT cds = new COPYDATASTRUCT(); + + try + { + cds = Pack(x, y); + + cdPtr = Marshal.AllocHGlobal(Marshal.SizeOf(cds)); + + Marshal.StructureToPtr(cds, cdPtr, false); + + r = (Error)SendMessage(FindHwnd(), WM_COPYDATA, (UIntPtr)Command.Window_Move, cdPtr); + } + finally + { + Free(ref cds); + Marshal.FreeHGlobal(cdPtr); + } + + return r; + } + + public Error Window_Resize(int w, int h) + { + Error r = Error.Error; + + IntPtr cdPtr = IntPtr.Zero; + + COPYDATASTRUCT cds = new COPYDATASTRUCT(); + + try + { + cds = Pack(w, h); + + cdPtr = Marshal.AllocHGlobal(Marshal.SizeOf(cds)); + + Marshal.StructureToPtr(cds, cdPtr, false); + + r = (Error)SendMessage(FindHwnd(), WM_COPYDATA, (UIntPtr)Command.Window_Resize, cdPtr); + } + finally + { + Free(ref cds); + Marshal.FreeHGlobal(cdPtr); + } + + return r; + } + + public Error Window_BringToFront() + { + return (Error)SendMessage(FindHwnd(), WM_USER, (UIntPtr)Command.Window_BringToFront, IntPtr.Zero); + } + + public Error Window_GetPosition(out int x, out int y) + { + Error r = Error.Error; + + IntPtr hwnd = FindHwnd(); + + IntPtr lr = SendMessage(hwnd, WM_USER, (UIntPtr)Command.Window_GetPosition, IntPtr.Zero); + + if (Unpack(lr, out x, out y)) + r = Error.NoError; + + SendMessage(hwnd, WM_USER, (UIntPtr)Command.FreeLRESULT, lr); + + return r; + } + + public Error Window_GetSize(out int w, out int h) + { + Error r = Error.Error; + + IntPtr hwnd = FindHwnd(); + + IntPtr lr = SendMessage(hwnd, WM_USER, (UIntPtr)Command.Window_GetSize, IntPtr.Zero); + + if (Unpack(lr, out w, out h)) + r = Error.NoError; + + SendMessage(hwnd, WM_USER, (UIntPtr)Command.FreeLRESULT, lr); + + return r; + } + + public MusicBeeVersion GetMusicBeeVersion() + { + return (MusicBeeVersion)SendMessage(FindHwnd(), WM_USER, (UIntPtr)Command.MusicBeeVersion, IntPtr.Zero); + } + + public string GetMusicBeeVersionStr() + { + switch ((MusicBeeVersion)SendMessage(FindHwnd(), WM_USER, (UIntPtr)Command.MusicBeeVersion, IntPtr.Zero)) + { + case MusicBeeVersion.v2_0: + return "2.0"; + case MusicBeeVersion.v2_1: + return "2.1"; + case MusicBeeVersion.v2_2: + return "2.2"; + case MusicBeeVersion.v2_3: + return "2.3"; + default: + return "Unknown"; + } + } + + public string GetPluginVersionStr() + { + string r = ""; + + IntPtr hwnd = FindHwnd(); + + IntPtr lr = SendMessage(hwnd, WM_USER, (UIntPtr)Command.PluginVersion, IntPtr.Zero); + + Unpack(lr, out r); + + SendMessage(hwnd, WM_USER, (UIntPtr)Command.FreeLRESULT, lr); + + return r; + } + + public Error GetPluginVersion(out int major, out int minor) + { + major = minor = 0; + + Error r = Error.Error; + string v = ""; + + IntPtr hwnd = FindHwnd(); + + IntPtr lr = SendMessage(hwnd, WM_USER, (UIntPtr)Command.PluginVersion, IntPtr.Zero); + + if (Unpack(lr, out v)) + r = Error.NoError; + + SendMessage(hwnd, WM_USER, (UIntPtr)Command.FreeLRESULT, lr); + + if (r == Error.NoError) + { + string[] split = v.Split('.'); + + try + { + major = Int32.Parse(split[0]); + minor = Int32.Parse(split[1]); + } + catch + { + r = Error.Error; + } + } + + return r; + } + + public Error Test() + { + return (Error)SendMessage(FindHwnd(), WM_USER, (UIntPtr)Command.Test, IntPtr.Zero); + } + + public Error MessageBox(string text, string caption) + { + Error r = Error.Error; + + IntPtr cdPtr = IntPtr.Zero; + + COPYDATASTRUCT cds = new COPYDATASTRUCT(); + + try + { + cds = Pack(text, caption); + + cdPtr = Marshal.AllocHGlobal(Marshal.SizeOf(cds)); + + Marshal.StructureToPtr(cds, cdPtr, false); + + r = (Error)SendMessage(FindHwnd(), WM_COPYDATA, (UIntPtr)Command.MessageBox, cdPtr); + } + finally + { + Free(ref cds); + Marshal.FreeHGlobal(cdPtr); + } + + return r; + } + + private IntPtr FindHwnd() + { + return FindWindow(IntPtr.Zero, "MusicBee IPC Interface"); + } + + private IntPtr ToIntPtr(bool b) + { + return b ? (IntPtr)Bool.True : (IntPtr)Bool.False; + } + + private bool ToBool(IntPtr i) + { + return i == (IntPtr)Bool.False ? false : true; + } +} diff --git a/src/MusicBeeAudioSource/MusicBeeIPC/Pack.cs b/src/MusicBeeAudioSource/MusicBeeIPC/Pack.cs new file mode 100644 index 00000000..ab88f17a --- /dev/null +++ b/src/MusicBeeAudioSource/MusicBeeIPC/Pack.cs @@ -0,0 +1,486 @@ +//--------------------------------------------------------// +// MusicBeeIPCSDK C# v2.0.0 // +// Copyright © Kerli Low 2014 // +// This file is licensed under the // +// BSD 2-Clause License // +// See LICENSE_MusicBeeIPCSDK for more information. // +//--------------------------------------------------------// + +using System; +using System.Runtime.InteropServices; + +[System.Security.Permissions.PermissionSet(System.Security.Permissions.SecurityAction.Demand, Name = "FullTrust")] +public partial class MusicBeeIPC +{ + private int sizeofInt32 = Marshal.SizeOf(typeof(Int32)); + private int sizeofDouble = Marshal.SizeOf(typeof(double)); + + // -------------------------------------------------------------------------------- + // All strings are encoded in UTF-16 little endian + // -------------------------------------------------------------------------------- + + [System.Security.Permissions.PermissionSet(System.Security.Permissions.SecurityAction.Demand, Name = "FullTrust")] + private void Free(ref COPYDATASTRUCT cds) + { + Marshal.FreeHGlobal(cds.lpData); + cds.lpData = IntPtr.Zero; + } + + // -------------------------------------------------------------------------------- + // -Int32: 32 bit integer + // -Int32: 32 bit integer + // -... + // Free the returned COPYDATASTRUCT after use + // -------------------------------------------------------------------------------- + [System.Security.Permissions.PermissionSet(System.Security.Permissions.SecurityAction.Demand, Name = "FullTrust")] + private COPYDATASTRUCT Pack(params int[] int32s) + { + COPYDATASTRUCT cds = new COPYDATASTRUCT(); + + int cb = sizeofInt32 * int32s.Length; + + cds.cbData = (uint)cb; + + cds.lpData = Marshal.AllocHGlobal(cb); + + IntPtr ptr = cds.lpData; + + Marshal.Copy(int32s, 0, ptr, int32s.Length); + + return cds; + } + + // -------------------------------------------------------------------------------- + // -Int32: Byte count of string + // -byte[]: String data + // -... + // Free the returned COPYDATASTRUCT after use + // -------------------------------------------------------------------------------- + [System.Security.Permissions.PermissionSet(System.Security.Permissions.SecurityAction.Demand, Name = "FullTrust")] + private COPYDATASTRUCT Pack(string string_1) + { + COPYDATASTRUCT cds = new COPYDATASTRUCT(); + + int cb = sizeofInt32 + System.Text.Encoding.Unicode.GetByteCount(string_1); + + cds.cbData = (uint)cb; + + cds.lpData = Marshal.AllocHGlobal(cb); + + IntPtr ptr = cds.lpData; + + int byteCount = System.Text.Encoding.Unicode.GetByteCount(string_1); + + Marshal.WriteInt32(ptr, byteCount); + ptr += sizeofInt32; + + if (byteCount > 0) + Marshal.Copy(System.Text.Encoding.Unicode.GetBytes(string_1), 0, ptr, byteCount); + + return cds; + } + + // -------------------------------------------------------------------------------- + // -Int32: Byte count of 1st string + // -byte[]: 1st string data + // -Int32: Byte count of 2nd string + // -byte[]: 2nd string data + // -... + // Free the returned COPYDATASTRUCT after use + // -------------------------------------------------------------------------------- + [System.Security.Permissions.PermissionSet(System.Security.Permissions.SecurityAction.Demand, Name = "FullTrust")] + private COPYDATASTRUCT Pack(params string[] strings) + { + COPYDATASTRUCT cds = new COPYDATASTRUCT(); + + int cb = 0; + + foreach (string s in strings) + cb += sizeofInt32 + System.Text.Encoding.Unicode.GetByteCount(s); + + cds.cbData = (uint)cb; + + cds.lpData = Marshal.AllocHGlobal(cb); + + IntPtr ptr = cds.lpData; + + int byteCount = 0; + + foreach (string s in strings) + { + byteCount = System.Text.Encoding.Unicode.GetByteCount(s); + + Marshal.WriteInt32(ptr, byteCount); + ptr += sizeofInt32; + + if (byteCount > 0) + Marshal.Copy(System.Text.Encoding.Unicode.GetBytes(s), 0, ptr, byteCount); + + ptr += byteCount; + } + + return cds; + } + + // -------------------------------------------------------------------------------- + // -Int32: Byte count of string + // -byte[]: String data + // -Int32: 32 bit integer + // -Int32: 32 bit integer + // -... + // Free the returned COPYDATASTRUCT after use + // -------------------------------------------------------------------------------- + [System.Security.Permissions.PermissionSet(System.Security.Permissions.SecurityAction.Demand, Name = "FullTrust")] + private COPYDATASTRUCT Pack(string string_1, params int[] int32s) + { + COPYDATASTRUCT cds = new COPYDATASTRUCT(); + + int cb = sizeofInt32 + System.Text.Encoding.Unicode.GetByteCount(string_1) + sizeofInt32 * int32s.Length; + + cds.cbData = (uint)cb; + + cds.lpData = Marshal.AllocHGlobal(cb); + + IntPtr ptr = cds.lpData; + + int byteCount = System.Text.Encoding.Unicode.GetByteCount(string_1); + + Marshal.WriteInt32(ptr, byteCount); + ptr += sizeofInt32; + + if (byteCount > 0) + Marshal.Copy(System.Text.Encoding.Unicode.GetBytes(string_1), 0, ptr, byteCount); + + ptr += byteCount; + + Marshal.Copy(int32s, 0, ptr, int32s.Length); + + return cds; + } + + // -------------------------------------------------------------------------------- + // -Int32: Byte count of string + // -byte[]: String data + // -Int32: bool + // -Int32: bool + // -... + // Free the returned COPYDATASTRUCT after use + // -------------------------------------------------------------------------------- + [System.Security.Permissions.PermissionSet(System.Security.Permissions.SecurityAction.Demand, Name = "FullTrust")] + private COPYDATASTRUCT Pack(string string_1, params bool[] bools) + { + COPYDATASTRUCT cds = new COPYDATASTRUCT(); + + int cb = sizeofInt32 + System.Text.Encoding.Unicode.GetByteCount(string_1) + sizeofInt32 * bools.Length; + + cds.cbData = (uint)cb; + + cds.lpData = Marshal.AllocHGlobal(cb); + + IntPtr ptr = cds.lpData; + + int byteCount = System.Text.Encoding.Unicode.GetByteCount(string_1); + + Marshal.WriteInt32(ptr, byteCount); + ptr += sizeofInt32; + + if (byteCount > 0) + Marshal.Copy(System.Text.Encoding.Unicode.GetBytes(string_1), 0, ptr, byteCount); + + ptr += byteCount; + + foreach (bool b in bools) + { + Marshal.WriteInt32(ptr, b ? (int)Bool.True : (int)Bool.False); + ptr += sizeofInt32; + } + + return cds; + } + + // -------------------------------------------------------------------------------- + // -Int32: Byte count of string + // -byte[]: String data + // -double: 64-bit floating-point value + // -double: 64-bit floating-point value + // -... + // Free the returned COPYDATASTRUCT after use + // -------------------------------------------------------------------------------- + [System.Security.Permissions.PermissionSet(System.Security.Permissions.SecurityAction.Demand, Name = "FullTrust")] + private COPYDATASTRUCT Pack(string string_1, params double[] doubles) + { + COPYDATASTRUCT cds = new COPYDATASTRUCT(); + + int cb = sizeofInt32 + System.Text.Encoding.Unicode.GetByteCount(string_1) + sizeofDouble * doubles.Length; + + cds.cbData = (uint)cb; + + cds.lpData = Marshal.AllocHGlobal(cb); + + IntPtr ptr = cds.lpData; + + int byteCount = System.Text.Encoding.Unicode.GetByteCount(string_1); + + Marshal.WriteInt32(ptr, byteCount); + ptr += sizeofInt32; + + if (byteCount > 0) + Marshal.Copy(System.Text.Encoding.Unicode.GetBytes(string_1), 0, ptr, byteCount); + + ptr += byteCount; + + Marshal.Copy(doubles, 0, ptr, doubles.Length); + + return cds; + } + + // -------------------------------------------------------------------------------- + // -Int32: Byte count of string + // -byte[]: String data + // -Int32: Number of strings in string array + // -Int32: Byte count of 1st string in string array + // -byte[]: 1st string data in string array + // -Int32: Byte count of 2nd string in string array + // -byte[]: 2nd string data in string array + // -... + // Free the returned COPYDATASTRUCT after use + // -------------------------------------------------------------------------------- + [System.Security.Permissions.PermissionSet(System.Security.Permissions.SecurityAction.Demand, Name = "FullTrust")] + private COPYDATASTRUCT Pack(string string_1, string[] strings) + { + COPYDATASTRUCT cds = new COPYDATASTRUCT(); + + int cb = sizeofInt32 * 2 + System.Text.Encoding.Unicode.GetByteCount(string_1); + + foreach (string s in strings) + cb += sizeofInt32 + System.Text.Encoding.Unicode.GetByteCount(s); + + cds.cbData = (uint)cb; + + cds.lpData = Marshal.AllocHGlobal(cb); + + IntPtr ptr = cds.lpData; + + int byteCount = System.Text.Encoding.Unicode.GetByteCount(string_1); + + Marshal.WriteInt32(ptr, byteCount); + ptr += sizeofInt32; + + if (byteCount > 0) + Marshal.Copy(System.Text.Encoding.Unicode.GetBytes(string_1), 0, ptr, byteCount); + + ptr += byteCount; + + Marshal.WriteInt32(ptr, strings.Length); + ptr += sizeofInt32; + + foreach (string s in strings) + { + byteCount = System.Text.Encoding.Unicode.GetByteCount(s); + + Marshal.WriteInt32(ptr, byteCount); + ptr += sizeofInt32; + + if (byteCount > 0) + Marshal.Copy(System.Text.Encoding.Unicode.GetBytes(s), 0, ptr, byteCount); + + ptr += byteCount; + } + + return cds; + } + + // -------------------------------------------------------------------------------- + // -Int32: Byte count of 1st string + // -byte[]: 1st string data + // -Int32: Byte count of 2nd string + // -byte[]: 2nd string data + // -Int32: Number of strings in string array + // -Int32: Byte count of 1st string in string array + // -byte[]: 1st string data in string array + // -Int32: Byte count of 2nd string in string array + // -byte[]: 2nd string data in string array + // -... + // Free the returned COPYDATASTRUCT after use + // -------------------------------------------------------------------------------- + [System.Security.Permissions.PermissionSet(System.Security.Permissions.SecurityAction.Demand, Name = "FullTrust")] + private COPYDATASTRUCT Pack(string string_1, string string_2, string[] strings) + { + COPYDATASTRUCT cds = new COPYDATASTRUCT(); + + int cb = sizeofInt32 * 3 + + System.Text.Encoding.Unicode.GetByteCount(string_1) + + System.Text.Encoding.Unicode.GetByteCount(string_2); + + foreach (string s in strings) + cb += sizeofInt32 + System.Text.Encoding.Unicode.GetByteCount(s); + + cds.cbData = (uint)cb; + + cds.lpData = Marshal.AllocHGlobal(cb); + + IntPtr ptr = cds.lpData; + + int byteCount = System.Text.Encoding.Unicode.GetByteCount(string_1); + + Marshal.WriteInt32(ptr, byteCount); + ptr += sizeofInt32; + + if (byteCount > 0) + Marshal.Copy(System.Text.Encoding.Unicode.GetBytes(string_1), 0, ptr, byteCount); + + ptr += byteCount; + + byteCount = System.Text.Encoding.Unicode.GetByteCount(string_2); + + Marshal.WriteInt32(ptr, byteCount); + ptr += sizeofInt32; + + if (byteCount > 0) + Marshal.Copy(System.Text.Encoding.Unicode.GetBytes(string_2), 0, ptr, byteCount); + + ptr += byteCount; + + Marshal.WriteInt32(ptr, strings.Length); + ptr += sizeofInt32; + + foreach (string s in strings) + { + byteCount = System.Text.Encoding.Unicode.GetByteCount(s); + + Marshal.WriteInt32(ptr, byteCount); + ptr += sizeofInt32; + + if (byteCount > 0) + Marshal.Copy(System.Text.Encoding.Unicode.GetBytes(s), 0, ptr, byteCount); + + ptr += byteCount; + } + + return cds; + } + + // -------------------------------------------------------------------------------- + // -Int32: Byte count of 1st string + // -byte[]: 1st string data + // -Int32: 32 bit integer + // -Int32: Byte count of 2nd string + // -byte[]: 2nd string data + // -... + // Free the returned COPYDATASTRUCT after use + // -------------------------------------------------------------------------------- + [System.Security.Permissions.PermissionSet(System.Security.Permissions.SecurityAction.Demand, Name = "FullTrust")] + private COPYDATASTRUCT Pack(string string_1, int int32_1, string string_2) + { + COPYDATASTRUCT cds = new COPYDATASTRUCT(); + + int cb = sizeofInt32 * 3 + + System.Text.Encoding.Unicode.GetByteCount(string_1) + + System.Text.Encoding.Unicode.GetByteCount(string_2); + + cds.cbData = (uint)cb; + + cds.lpData = Marshal.AllocHGlobal(cb); + + IntPtr ptr = cds.lpData; + + int byteCount = System.Text.Encoding.Unicode.GetByteCount(string_1); + + Marshal.WriteInt32(ptr, byteCount); + ptr += sizeofInt32; + + if (byteCount > 0) + Marshal.Copy(System.Text.Encoding.Unicode.GetBytes(string_1), 0, ptr, byteCount); + + ptr += byteCount; + + Marshal.WriteInt32(ptr, int32_1); + ptr += sizeofInt32; + + byteCount = System.Text.Encoding.Unicode.GetByteCount(string_2); + + Marshal.WriteInt32(ptr, byteCount); + ptr += sizeofInt32; + + if (byteCount > 0) + Marshal.Copy(System.Text.Encoding.Unicode.GetBytes(string_2), 0, ptr, byteCount); + + return cds; + } + + // -------------------------------------------------------------------------------- + // -Int32: Number of integers in integer array + // -Int32: 32 bit integer + // -Int32: 32 bit integer + // -... + // -Int32: 32 bit integer + // Free the returned COPYDATASTRUCT after use + // -------------------------------------------------------------------------------- + [System.Security.Permissions.PermissionSet(System.Security.Permissions.SecurityAction.Demand, Name = "FullTrust")] + private COPYDATASTRUCT Pack(int[] int32s, int int32_1) + { + COPYDATASTRUCT cds = new COPYDATASTRUCT(); + + int cb = sizeofInt32 * (int32s.Length + 2); + + cds.cbData = (uint)cb; + + cds.lpData = Marshal.AllocHGlobal(cb); + + IntPtr ptr = cds.lpData; + + Marshal.WriteInt32(ptr, int32s.Length); + ptr += sizeofInt32; + + Marshal.Copy(int32s, 0, ptr, int32s.Length); + ptr += sizeofInt32 * int32s.Length; + + Marshal.WriteInt32(ptr, int32_1); + + return cds; + } + + // -------------------------------------------------------------------------------- + // -Int32: Byte count of string + // -byte[]: String data + // -Int32: Number of integers in integer array + // -Int32: 32 bit integer + // -Int32: 32 bit integer + // -... + // -Int32: 32 bit integer + // Free the returned COPYDATASTRUCT after use + // -------------------------------------------------------------------------------- + [System.Security.Permissions.PermissionSet(System.Security.Permissions.SecurityAction.Demand, Name = "FullTrust")] + private COPYDATASTRUCT Pack(string string_1, int[] int32s, int int32_1) + { + COPYDATASTRUCT cds = new COPYDATASTRUCT(); + + int byteCount = System.Text.Encoding.Unicode.GetByteCount(string_1); + + int cb = sizeofInt32 * (int32s.Length + 3) + byteCount; + + cds.cbData = (uint)cb; + + cds.lpData = Marshal.AllocHGlobal(cb); + + IntPtr ptr = cds.lpData; + + Marshal.WriteInt32(ptr, byteCount); + ptr += sizeofInt32; + + if (byteCount > 0) + Marshal.Copy(System.Text.Encoding.Unicode.GetBytes(string_1), 0, ptr, byteCount); + + ptr += byteCount; + + Marshal.WriteInt32(ptr, int32s.Length); + ptr += sizeofInt32; + + Marshal.Copy(int32s, 0, ptr, int32s.Length); + ptr += sizeofInt32 * int32s.Length; + + Marshal.WriteInt32(ptr, int32_1); + + return cds; + } +} diff --git a/src/MusicBeeAudioSource/MusicBeeIPC/Structs.cs b/src/MusicBeeAudioSource/MusicBeeIPC/Structs.cs new file mode 100644 index 00000000..e898624b --- /dev/null +++ b/src/MusicBeeAudioSource/MusicBeeIPC/Structs.cs @@ -0,0 +1,48 @@ +//--------------------------------------------------------// +// MusicBeeIPCSDK C# v2.0.0 // +// Copyright © Kerli Low 2014 // +// This file is licensed under the // +// BSD 2-Clause License // +// See LICENSE_MusicBeeIPCSDK for more information. // +//--------------------------------------------------------// + +using System; +using System.Runtime.InteropServices; + +[System.Security.Permissions.PermissionSet(System.Security.Permissions.SecurityAction.Demand, Name = "FullTrust")] +public partial class MusicBeeIPC +{ + [StructLayout(LayoutKind.Sequential)] + private struct COPYDATASTRUCT + { + public IntPtr dwData; + public UInt32 cbData; + public IntPtr lpData; + } + + [StructLayout(LayoutKind.Explicit)] + private struct FloatInt + { + [FieldOffset(0)] + public float f; + [FieldOffset(0)] + public int i; + + public FloatInt(float f) { this.i = 0; this.f = f; } + public FloatInt(int i) { this.f = 0.0f; this.i = i; } + } + + [StructLayout(LayoutKind.Explicit)] + private struct LRUShort + { + [FieldOffset(0)] + public IntPtr lr; + [FieldOffset(0)] + public ushort low; + [FieldOffset(2)] + public ushort high; + + public LRUShort(IntPtr ptr) { this.low = this.high = 0; this.lr = ptr; } + public LRUShort(ushort low, ushort high) { this.lr = IntPtr.Zero; this.low = low; this.high = high; } + } +} \ No newline at end of file diff --git a/src/MusicBeeAudioSource/MusicBeeIPC/Unpack.cs b/src/MusicBeeAudioSource/MusicBeeIPC/Unpack.cs new file mode 100644 index 00000000..7ea5a2d4 --- /dev/null +++ b/src/MusicBeeAudioSource/MusicBeeIPC/Unpack.cs @@ -0,0 +1,181 @@ +//--------------------------------------------------------// +// MusicBeeIPCSDK C# v2.0.0 // +// Copyright © Kerli Low 2014 // +// This file is licensed under the // +// BSD 2-Clause License // +// See LICENSE_MusicBeeIPCSDK for more information. // +//--------------------------------------------------------// + +using System; +using System.IO; +using System.IO.MemoryMappedFiles; +using System.Runtime.InteropServices; + +[System.Security.Permissions.PermissionSet(System.Security.Permissions.SecurityAction.Demand, Name = "FullTrust")] +public partial class MusicBeeIPC +{ + // -------------------------------------------------------------------------------- + // All strings are encoded in UTF-16 little endian + // -------------------------------------------------------------------------------- + + // -------------------------------------------------------------------------------- + // -Int32: Byte count of the string + // -byte[]: String data + // Free lr after use + // -------------------------------------------------------------------------------- + private bool Unpack(IntPtr lr, out string string_1) + { + string_1 = ""; + + BinaryReader reader = GetMmfReader(lr); + if (reader == null) + return false; + + try + { + int byteCount = reader.ReadInt32(); + + if (byteCount > 0) + { + byte[] bytes = reader.ReadBytes(byteCount); + + string_1 = System.Text.Encoding.Unicode.GetString(bytes); + } + + return true; + } + catch + { + return false; + } + } + + // -------------------------------------------------------------------------------- + // -Int32: Number of strings + // -Int32: Byte count of 1st string + // -byte[]: 1st string data + // -Int32: Byte count of 2nd string + // -byte[]: 2nd string data + // -... + // Free lr after use + // -------------------------------------------------------------------------------- + private bool Unpack(IntPtr lr, out string[] strings) + { + strings = new string[0]; + + BinaryReader reader = GetMmfReader(lr); + if (reader == null) + return false; + + try + { + int strCount = reader.ReadInt32(); + + int byteCount = 0; + + strings = new string[strCount]; + + for (int i = 0; i < strCount; i++) + { + byteCount = reader.ReadInt32(); + + if (byteCount > 0) + { + byte[] bytes = reader.ReadBytes(byteCount); + + strings[i] = System.Text.Encoding.Unicode.GetString(bytes); + } + else + strings[i] = ""; + } + + return true; + } + catch + { + return false; + } + } + + // -------------------------------------------------------------------------------- + // -Int32: 32 bit integer + // -Int32: 32 bit integer + // Free lr after use + // -------------------------------------------------------------------------------- + private bool Unpack(IntPtr lr, out int int32_1, out int int32_2) + { + int32_1 = int32_2 = 0; + + BinaryReader reader = GetMmfReader(lr); + if (reader == null) + return false; + + try + { + int32_1 = reader.ReadInt32(); + int32_2 = reader.ReadInt32(); + return true; + } + catch + { + return false; + } + } + + // -------------------------------------------------------------------------------- + // -Int32: Number of integers + // -Int32: 1st 32 bit integer + // -Int32: 2nd 32 bit integer + // -... + // Free lr after use + // -------------------------------------------------------------------------------- + private bool Unpack(IntPtr lr, out int[] int32s) + { + int32s = new int[0]; + + BinaryReader reader = GetMmfReader(lr); + if (reader == null) + return false; + + try + { + int int32Count = reader.ReadInt32(); + + int32s = new int[int32Count]; + + for (int i = 0; i < int32Count; i++) + int32s[i] = reader.ReadInt32(); + + return true; + } + catch + { + return false; + } + } + + private BinaryReader GetMmfReader(IntPtr lr) + { + if (lr == IntPtr.Zero) + return null; + + try + { + LRUShort ls = new LRUShort(lr); + + MemoryMappedFile mmf = + MemoryMappedFile.OpenExisting("mbipc_mmf_" + ls.low.ToString(), MemoryMappedFileRights.Read); + + long size = mmf.CreateViewAccessor(0, 0, MemoryMappedFileAccess.Read).ReadInt64(ls.high); + + MemoryMappedViewStream stream = + mmf.CreateViewStream(ls.high + Marshal.SizeOf(typeof(long)), size, MemoryMappedFileAccess.Read); + + return new BinaryReader(stream); + } + catch + { + return null; + } + } +} diff --git a/src/MusicBeeAudioSource/Properties/AssemblyInfo.cs b/src/MusicBeeAudioSource/Properties/AssemblyInfo.cs index cb329eca..55ffa266 100644 --- a/src/MusicBeeAudioSource/Properties/AssemblyInfo.cs +++ b/src/MusicBeeAudioSource/Properties/AssemblyInfo.cs @@ -20,7 +20,7 @@ [assembly: ComVisible(false)] // The following GUID is for the ID of the typelib if this project is exposed to COM -[assembly: Guid("231b7932-535b-4794-b6cf-f5d11b412ccd")] +[assembly: Guid("741db79c-921d-4d91-85f1-cd10c746f46e")] // Version information for an assembly consists of the following four values: // From edb0be21858a02efa8b7c0056220edb723c843f1 Mon Sep 17 00:00:00 2001 From: Brandon Chong Date: Tue, 27 Nov 2018 21:36:11 -0800 Subject: [PATCH 08/14] Add xml docs to output --- src/AudioBand.AudioSource/AudioBand.AudioSource.csproj | 1 + 1 file changed, 1 insertion(+) diff --git a/src/AudioBand.AudioSource/AudioBand.AudioSource.csproj b/src/AudioBand.AudioSource/AudioBand.AudioSource.csproj index fdfbbc19..5296c00e 100644 --- a/src/AudioBand.AudioSource/AudioBand.AudioSource.csproj +++ b/src/AudioBand.AudioSource/AudioBand.AudioSource.csproj @@ -20,6 +20,7 @@ DEBUG;TRACE prompt 4 + bin\Debug\AudioBand.AudioSource.xml pdbonly From 9fafa640b697ebba53ff0896b7e30d6046e6815d Mon Sep 17 00:00:00 2001 From: Brandon Chong Date: Tue, 27 Nov 2018 23:19:55 -0800 Subject: [PATCH 09/14] Add controls and status from musicbee --- src/MusicBeeAudioSource/AudioSource.cs | 165 ++++++++++++++++++ src/MusicBeeAudioSource/AudioSource.manifest | 1 + .../MusicBeeAudioSource.csproj | 17 ++ 3 files changed, 183 insertions(+) create mode 100644 src/MusicBeeAudioSource/AudioSource.cs create mode 100644 src/MusicBeeAudioSource/AudioSource.manifest diff --git a/src/MusicBeeAudioSource/AudioSource.cs b/src/MusicBeeAudioSource/AudioSource.cs new file mode 100644 index 00000000..7450b065 --- /dev/null +++ b/src/MusicBeeAudioSource/AudioSource.cs @@ -0,0 +1,165 @@ +using AudioBand.AudioSource; +using System; +using System.ComponentModel; +using System.Diagnostics; +using System.Drawing; +using System.IO; +using System.Threading; +using System.Threading.Tasks; +using System.Timers; +using Timer = System.Timers.Timer; + +namespace MusicBeeAudioSource +{ + public class AudioSource : IAudioSource + { + private MusicBeeIPC _ipc; + private Timer _checkMusicBeeTimer; + private bool _isPlaying; + private string _currentId; + private static readonly string[] TimeFormats = new string[] { @"m\:s", @"h\:m\:s" }; + + public string Name => "Music Bee"; + public IAudioSourceLogger Logger { get; set; } + public event EventHandler TrackInfoChanged; + public event EventHandler TrackPlaying; + public event EventHandler TrackPaused; + public event EventHandler TrackProgressChanged; + public event PropertyChangedEventHandler PropertyChanged; + + public AudioSource() + { + _ipc = new MusicBeeIPC(); + _checkMusicBeeTimer = new Timer(100) + { + AutoReset = false, + Enabled = false + }; + _checkMusicBeeTimer.Elapsed += CheckMusicBee; + } + + public Task ActivateAsync(CancellationToken cancellationToken = default(CancellationToken)) + { + _checkMusicBeeTimer.Start(); + return Task.CompletedTask; + } + + public Task DeactivateAsync(CancellationToken cancellationToken = default(CancellationToken)) + { + _checkMusicBeeTimer.Stop(); + return Task.CompletedTask; + } + + public Task NextTrackAsync(CancellationToken cancellationToken = default(CancellationToken)) + { + _ipc.NextTrack(); + return Task.CompletedTask; + } + + public Task PauseTrackAsync(CancellationToken cancellationToken = default(CancellationToken)) + { + _ipc.Pause(); + return Task.CompletedTask; + } + + public Task PlayTrackAsync(CancellationToken cancellationToken = default(CancellationToken)) + { + _ipc.Play(); + return Task.CompletedTask; + } + + public Task PreviousTrackAsync(CancellationToken cancellationToken = default(CancellationToken)) + { + _ipc.PreviousTrack(); + return Task.CompletedTask; + } + + private void CheckMusicBee(object sender, ElapsedEventArgs eventArgs) + { + try + { + if (Process.GetProcessesByName("MusicBee").Length == 0) + { + return; + } + + // The ipc plugin does not load right away + if (string.IsNullOrEmpty(_ipc.GetPluginVersionStr())) + { + return; + } + + NotifyState(); + NotifyTrackChange(); + + var time = TimeSpan.FromMilliseconds(_ipc.GetPosition()); + TrackProgressChanged?.Invoke(this, time); + } + catch (Exception e) + { + Logger.Debug(e); + } + finally + { + _checkMusicBeeTimer.Enabled = true; + } + } + + private void NotifyState() + { + var isPlaying = _ipc.GetPlayState().HasFlag(MusicBeeIPC.PlayState.Playing); + if (isPlaying == _isPlaying) + { + return; + } + + if (isPlaying) + { + TrackPlaying?.Invoke(this, EventArgs.Empty); + } + else + { + TrackPaused?.Invoke(this, EventArgs.Empty); + } + + _isPlaying = isPlaying; + } + + private void NotifyTrackChange() + { + var track = _ipc.GetFileTag(MusicBeeIPC.MetaData.TrackTitle); + var artist = _ipc.GetFileTag(MusicBeeIPC.MetaData.Artist); + + var id = artist + track; + if (_currentId == id) + { + return; + } + + _currentId = id; + + var album = _ipc.GetFileTag(MusicBeeIPC.MetaData.Album); + + if (!TimeSpan.TryParseExact(_ipc.GetFileProperty(MusicBeeIPC.FileProperty.Duration), TimeFormats, null, out var trackLength)) + { + Logger.Warn($"Unable to parse track length: {_ipc.GetFileProperty(MusicBeeIPC.FileProperty.Duration)}"); + } + + Image albumArt = null; + var bytes = Convert.FromBase64String(_ipc.GetArtwork()); + using (var ms = new MemoryStream(bytes)) + { + albumArt = new Bitmap(ms); + } + + TrackInfoChanged?.Invoke(this, new TrackInfoChangedEventArgs + { + Album = album, + Artist = artist, + AlbumArt = albumArt, + TrackLength = trackLength, + TrackName = track + }); + } + } +} diff --git a/src/MusicBeeAudioSource/AudioSource.manifest b/src/MusicBeeAudioSource/AudioSource.manifest new file mode 100644 index 00000000..99dd7556 --- /dev/null +++ b/src/MusicBeeAudioSource/AudioSource.manifest @@ -0,0 +1 @@ +AudioSources = ["MusicBeeAudioSource.dll"] \ No newline at end of file diff --git a/src/MusicBeeAudioSource/MusicBeeAudioSource.csproj b/src/MusicBeeAudioSource/MusicBeeAudioSource.csproj index b603e612..77aaf6e2 100644 --- a/src/MusicBeeAudioSource/MusicBeeAudioSource.csproj +++ b/src/MusicBeeAudioSource/MusicBeeAudioSource.csproj @@ -33,6 +33,8 @@ + + @@ -41,6 +43,7 @@ + @@ -53,5 +56,19 @@ PreserveNewest + + + {30f2bfea-788a-494d-88e7-f2070528ebea} + AudioBand.AudioSource + + + + + PreserveNewest + + + + (robocopy "$(TargetDir)\" "$(SolutionDir)AudioBand\$(OutDir)AudioSources\MusicBee\\" /xf AudioBand.AudioSource.dll) ^& IF %25ERRORLEVEL%25 LEQ 1 exit 0 + \ No newline at end of file From f369d1ec52e02886b5ecc08eca76eccfdd07531c Mon Sep 17 00:00:00 2001 From: Brandon Chong Date: Fri, 30 Nov 2018 21:21:12 -0800 Subject: [PATCH 10/14] Allow track progress updates when paused --- src/iTunesAudioSource/AudioSource.cs | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/src/iTunesAudioSource/AudioSource.cs b/src/iTunesAudioSource/AudioSource.cs index 98f2e117..fc463724 100644 --- a/src/iTunesAudioSource/AudioSource.cs +++ b/src/iTunesAudioSource/AudioSource.cs @@ -75,7 +75,6 @@ public AudioSource() return Task.CompletedTask; } - private void NotifyTrackChange(Track track) { var trackInfo = new TrackInfoChangedEventArgs @@ -133,16 +132,12 @@ private void CheckItunes(object sender, ElapsedEventArgs eventArgs) } NotifyPlayerState(); - var isNewTrack = IsNewTrack(track); - if (isNewTrack) + if (IsNewTrack(track)) { NotifyTrackChange(track); } - if (_isPlaying || isNewTrack) - { - TrackProgressChanged?.Invoke(this, _itunesControls.Progress); - } + TrackProgressChanged?.Invoke(this, _itunesControls.Progress); } catch(Exception e) { From 0843e5c8b05e665af6802984c12a250c593b03c1 Mon Sep 17 00:00:00 2001 From: Brandon Chong Date: Fri, 30 Nov 2018 21:51:34 -0800 Subject: [PATCH 11/14] Remove dependency of propertychange --- .../AudioBand.AudioSource.csproj | 1 + src/AudioBand.AudioSource/IAudioSource.cs | 8 +++++-- .../SettingChangedEventArgs.cs | 24 +++++++++++++++++++ .../ViewModels/AudioSourceSettingsVM.cs | 10 ++++---- src/MusicBeeAudioSource/AudioSource.cs | 3 +-- src/SpotifyAudioSource/SpotifyAudioSource.cs | 9 ++++--- src/iTunesAudioSource/AudioSource.cs | 3 +-- 7 files changed, 41 insertions(+), 17 deletions(-) create mode 100644 src/AudioBand.AudioSource/SettingChangedEventArgs.cs diff --git a/src/AudioBand.AudioSource/AudioBand.AudioSource.csproj b/src/AudioBand.AudioSource/AudioBand.AudioSource.csproj index 5296c00e..98043841 100644 --- a/src/AudioBand.AudioSource/AudioBand.AudioSource.csproj +++ b/src/AudioBand.AudioSource/AudioBand.AudioSource.csproj @@ -42,6 +42,7 @@ + diff --git a/src/AudioBand.AudioSource/IAudioSource.cs b/src/AudioBand.AudioSource/IAudioSource.cs index 4247ec40..09bb8c58 100644 --- a/src/AudioBand.AudioSource/IAudioSource.cs +++ b/src/AudioBand.AudioSource/IAudioSource.cs @@ -1,5 +1,4 @@ using System; -using System.ComponentModel; using System.ComponentModel.Composition; using System.Threading; using System.Threading.Tasks; @@ -10,7 +9,7 @@ namespace AudioBand.AudioSource /// Provides information from an audio source and exposes controls /// [InheritedExport(typeof(IAudioSource))] - public interface IAudioSource : INotifyPropertyChanged + public interface IAudioSource { /// /// Name of the audio source which is displayed @@ -22,6 +21,11 @@ public interface IAudioSource : INotifyPropertyChanged /// IAudioSourceLogger Logger { get; set; } + /// + /// Occurs when a setting has changed internally. + /// + event EventHandler SettingChanged; + /// /// Track information has changed /// diff --git a/src/AudioBand.AudioSource/SettingChangedEventArgs.cs b/src/AudioBand.AudioSource/SettingChangedEventArgs.cs new file mode 100644 index 00000000..23309c7a --- /dev/null +++ b/src/AudioBand.AudioSource/SettingChangedEventArgs.cs @@ -0,0 +1,24 @@ +using System; + +namespace AudioBand.AudioSource +{ + /// + /// Provides data for a event. + /// + public class SettingChangedEventArgs : EventArgs + { + /// + /// Name of the setting's property that changed. + /// + public string PropertyName { get; set; } + + /// + /// Creates a new instance of a with the setting name. + /// + /// Name of the setting's property that changed. + public SettingChangedEventArgs(string propertyName) + { + PropertyName = propertyName; + } + } +} diff --git a/src/AudioBand/ViewModels/AudioSourceSettingsVM.cs b/src/AudioBand/ViewModels/AudioSourceSettingsVM.cs index 4c459886..e8403d00 100644 --- a/src/AudioBand/ViewModels/AudioSourceSettingsVM.cs +++ b/src/AudioBand/ViewModels/AudioSourceSettingsVM.cs @@ -1,9 +1,7 @@ -using System; -using AudioBand.AudioSource; +using AudioBand.AudioSource; using AudioBand.Extensions; using AudioBand.Models; using System.Collections.Generic; -using System.ComponentModel; using System.Linq; namespace AudioBand.ViewModels @@ -19,12 +17,12 @@ internal class AudioSourceSettingsVM : ViewModelBase public AudioSourceSettingsVM(AudioSourceSettings settings, IAudioSource audioSource) : base(settings) { Settings = CreateSettingViewModels(Model, audioSource); - audioSource.PropertyChanged += AudioSourceOnPropertyChanged; + audioSource.SettingChanged += AudioSourceOnSettingChanged; } - private void AudioSourceOnPropertyChanged(object sender, PropertyChangedEventArgs propertyChangedEventArgs) + private void AudioSourceOnSettingChanged(object sender, SettingChangedEventArgs e) { - Settings.FirstOrDefault(s => s.PropertyName == propertyChangedEventArgs.PropertyName)?.ValueChanged(); + Settings.FirstOrDefault(s => s.PropertyName == e.PropertyName)?.ValueChanged(); } private List CreateSettingViewModels(AudioSourceSettings existingSettings, IAudioSource source) diff --git a/src/MusicBeeAudioSource/AudioSource.cs b/src/MusicBeeAudioSource/AudioSource.cs index 7450b065..119d2e87 100644 --- a/src/MusicBeeAudioSource/AudioSource.cs +++ b/src/MusicBeeAudioSource/AudioSource.cs @@ -1,6 +1,5 @@ using AudioBand.AudioSource; using System; -using System.ComponentModel; using System.Diagnostics; using System.Drawing; using System.IO; @@ -25,7 +24,7 @@ public class AudioSource : IAudioSource public event EventHandler TrackPlaying; public event EventHandler TrackPaused; public event EventHandler TrackProgressChanged; - public event PropertyChangedEventHandler PropertyChanged; + public event EventHandler SettingChanged; public AudioSource() { diff --git a/src/SpotifyAudioSource/SpotifyAudioSource.cs b/src/SpotifyAudioSource/SpotifyAudioSource.cs index 080516cd..00ce8f94 100644 --- a/src/SpotifyAudioSource/SpotifyAudioSource.cs +++ b/src/SpotifyAudioSource/SpotifyAudioSource.cs @@ -1,6 +1,5 @@ using AudioBand.AudioSource; using System; -using System.ComponentModel; using System.Diagnostics; using System.Net; using System.Threading; @@ -22,11 +21,11 @@ public class SpotifyAudioSource : IAudioSource { public string Name { get; } = "Spotify"; public IAudioSourceLogger Logger { get; set; } - public event PropertyChangedEventHandler PropertyChanged; public event EventHandler TrackInfoChanged; public event EventHandler TrackPlaying; public event EventHandler TrackPaused; public event EventHandler TrackProgressChanged; + public event EventHandler SettingChanged; [AudioSourceSetting("Spotify Client ID")] public string ClientId @@ -75,7 +74,7 @@ public string RefreshToken if (value == _refreshToken) return; _refreshToken = value; - OnPropertyChanged(); + OnSettingChanged(); } } @@ -435,9 +434,9 @@ private async void CheckSpotifyTimerOnElapsed(object sender, ElapsedEventArgs el return Task.CompletedTask; } - private void OnPropertyChanged([CallerMemberName] string propertyName = null) + private void OnSettingChanged([CallerMemberName] string propertyName = null) { - PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); + SettingChanged?.Invoke(this, new SettingChangedEventArgs(propertyName)); } private async Task RefreshAccessToken() diff --git a/src/iTunesAudioSource/AudioSource.cs b/src/iTunesAudioSource/AudioSource.cs index fc463724..0112edc0 100644 --- a/src/iTunesAudioSource/AudioSource.cs +++ b/src/iTunesAudioSource/AudioSource.cs @@ -1,6 +1,5 @@ using AudioBand.AudioSource; using System; -using System.ComponentModel; using System.Threading; using System.Threading.Tasks; using System.Timers; @@ -21,7 +20,7 @@ public class AudioSource : IAudioSource public event EventHandler TrackPlaying; public event EventHandler TrackPaused; public event EventHandler TrackProgressChanged; - public event PropertyChangedEventHandler PropertyChanged; + public event EventHandler SettingChanged; public AudioSource() { From 1b23e9da31360a2893e42039d8aa9e447b3f3fba Mon Sep 17 00:00:00 2001 From: Brandon Chong Date: Fri, 30 Nov 2018 22:02:55 -0800 Subject: [PATCH 12/14] Update documentation comments --- src/AudioBand.AudioSource/IAudioSource.cs | 28 ++++++++++++----------- 1 file changed, 15 insertions(+), 13 deletions(-) diff --git a/src/AudioBand.AudioSource/IAudioSource.cs b/src/AudioBand.AudioSource/IAudioSource.cs index 09bb8c58..94165d74 100644 --- a/src/AudioBand.AudioSource/IAudioSource.cs +++ b/src/AudioBand.AudioSource/IAudioSource.cs @@ -6,19 +6,21 @@ namespace AudioBand.AudioSource { /// - /// Provides information from an audio source and exposes controls + /// Provides information and notifies changes from an audio source and exposes playback controls. /// [InheritedExport(typeof(IAudioSource))] public interface IAudioSource { /// - /// Name of the audio source which is displayed + /// Gets the name of the audio source. /// + /// The name of the audio source. string Name { get; } /// - /// Audio source logger that will be injected + /// Gets or sets the used for logging. /// + /// Audio source logger that will be injected. IAudioSourceLogger Logger { get; set; } /// @@ -27,52 +29,52 @@ public interface IAudioSource event EventHandler SettingChanged; /// - /// Track information has changed + /// Occurs when track information has changed. /// event EventHandler TrackInfoChanged; /// - /// Track is now playing + /// Occurs when the playback state has changed to playing. /// event EventHandler TrackPlaying; /// - /// Track is paused + /// Occurs when the playback state has changed to paused. /// event EventHandler TrackPaused; /// - /// Track progress has changed to the current song duration + /// Occurs when the current track progress has changed. /// event EventHandler TrackProgressChanged; /// - /// This audio source has been selected + /// Called when the audio source is active. /// Task ActivateAsync(CancellationToken cancellationToken = default(CancellationToken)); /// - /// The audio source is no longer active + /// Called when the audio source is no longer active. /// Task DeactivateAsync(CancellationToken cancellationToken = default(CancellationToken)); /// - /// User requested to play the track. This should trigger if successful + /// Called when there is a request to start playback. /// Task PlayTrackAsync(CancellationToken cancellationToken = default(CancellationToken)); /// - /// User requested to pause the track. This should trigger if successful + /// Called when there is a request to stop playback. /// Task PauseTrackAsync(CancellationToken cancellationToken = default(CancellationToken)); /// - /// User requested the previous track + /// Called when there is a request to skip to the previous track. /// Task PreviousTrackAsync(CancellationToken cancellationToken = default(CancellationToken)); /// - /// User requested the next track + /// Called when there is a request to skip to the next track. /// Task NextTrackAsync(CancellationToken cancellationToken = default(CancellationToken)); } From 28b9bf0c478be7779770a96d282d3f2042748fa1 Mon Sep 17 00:00:00 2001 From: Brandon Chong Date: Fri, 30 Nov 2018 22:09:02 -0800 Subject: [PATCH 13/14] Update licenses --- LICENSE-3RD-PARTY | 122 +++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 121 insertions(+), 1 deletion(-) diff --git a/LICENSE-3RD-PARTY b/LICENSE-3RD-PARTY index d4dc9460..79d967f8 100644 --- a/LICENSE-3RD-PARTY +++ b/LICENSE-3RD-PARTY @@ -334,4 +334,124 @@ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ----------------------------------------------------- \ No newline at end of file +---------------------------------------------------- +https://raw.githubusercontent.com/MahApps/MahApps.Metro.IconPacks/dev/LICENSE +The MIT License (MIT) + +Copyright (c) 2016-2018 MahApps, Jan Karger + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +---------------------------------------------------- +https://raw.githubusercontent.com/Templarian/MaterialDesign/master/LICENSE +Copyright (c) 2014, Austin Andrews (http://materialdesignicons.com/), +with Reserved Font Name Material Design Icons. +Copyright (c) 2014, Google (http://www.google.com/design/) +uses the license at https://github.com/google/material-design-icons/blob/master/LICENSE + +This Font Software is licensed under the SIL Open Font License, Version 1.1. +This license is copied below, and is also available with a FAQ at: +http://scripts.sil.org/OFL + + +----------------------------------------------------------- +SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 +----------------------------------------------------------- + +PREAMBLE +The goals of the Open Font License (OFL) are to stimulate worldwide +development of collaborative font projects, to support the font creation +efforts of academic and linguistic communities, and to provide a free and +open framework in which fonts may be shared and improved in partnership +with others. + +The OFL allows the licensed fonts to be used, studied, modified and +redistributed freely as long as they are not sold by themselves. The +fonts, including any derivative works, can be bundled, embedded, +redistributed and/or sold with any software provided that any reserved +names are not used by derivative works. The fonts and derivatives, +however, cannot be released under any other type of license. The +requirement for fonts to remain under this license does not apply +to any document created using the fonts or their derivatives. + +DEFINITIONS +"Font Software" refers to the set of files released by the Copyright +Holder(s) under this license and clearly marked as such. This may +include source files, build scripts and documentation. + +"Reserved Font Name" refers to any names specified as such after the +copyright statement(s). + +"Original Version" refers to the collection of Font Software components as +distributed by the Copyright Holder(s). + +"Modified Version" refers to any derivative made by adding to, deleting, +or substituting -- in part or in whole -- any of the components of the +Original Version, by changing formats or by porting the Font Software to a +new environment. + +"Author" refers to any designer, engineer, programmer, technical +writer or other person who contributed to the Font Software. + +PERMISSION & CONDITIONS +Permission is hereby granted, free of charge, to any person obtaining +a copy of the Font Software, to use, study, copy, merge, embed, modify, +redistribute, and sell modified and unmodified copies of the Font +Software, subject to the following conditions: + +1) Neither the Font Software nor any of its individual components, +in Original or Modified Versions, may be sold by itself. + +2) Original or Modified Versions of the Font Software may be bundled, +redistributed and/or sold with any software, provided that each copy +contains the above copyright notice and this license. These can be +included either as stand-alone text files, human-readable headers or +in the appropriate machine-readable metadata fields within text or +binary files as long as those fields can be easily viewed by the user. + +3) No Modified Version of the Font Software may use the Reserved Font +Name(s) unless explicit written permission is granted by the corresponding +Copyright Holder. This restriction only applies to the primary font name as +presented to the users. + +4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font +Software shall not be used to promote, endorse or advertise any +Modified Version, except to acknowledge the contribution(s) of the +Copyright Holder(s) and the Author(s) or with their explicit written +permission. + +5) The Font Software, modified or unmodified, in part or in whole, +must be distributed entirely under this license, and must not be +distributed under any other license. The requirement for fonts to +remain under this license does not apply to any document created +using the Font Software. + +TERMINATION +This license becomes null and void if any of the above conditions are +not met. + +DISCLAIMER +THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT +OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE +COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL +DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM +OTHER DEALINGS IN THE FONT SOFTWARE. From a073700703c8a958a5ebb19594ae15b54e57236a Mon Sep 17 00:00:00 2001 From: Brandon Chong Date: Fri, 30 Nov 2018 22:15:43 -0800 Subject: [PATCH 14/14] Add itunes and musicbee to readme --- README.md | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 4508f531..21f5d102 100644 --- a/README.md +++ b/README.md @@ -31,8 +31,10 @@ There is currently no installer available, however there are prereleases in the **IMPORTANT** If nothing happens after selecting `Audio Band` from the toolbars menu or if there are no options in the `Audio Source` menu, some files are being blocked by windows. To fix it, run `unblock.ps1` with powershell. If that doesn't work you can manually fix it by right clicking the files -> properties and clicking unblock. If there are still problems, feel free to post an issue. -### Current Supported Audio Sources -- Spotify (see below for setup instructions and issues) +## Current Supported Audio Sources (see below for setup instructions and issues) +- Spotify +- iTunes +- MusicBee ### Spotify Setup 1. Login to the [Spotify dashboard](https://developer.spotify.com/dashboard/login) and create a new App. Fill in the details, you can name it whatever you want. This app will be just for AudioBand. @@ -49,7 +51,12 @@ There is currently no installer available, however there are prereleases in the #### Spotify Issues - The song progress will be out of sync if you change the song's progress. This is due to current limitations making the song progress being tracked locally. This can be fixed if you pause and play again or go to a different song. - The first time you open up Spotify, the current song may not be displayed. This is because Spotify doesn't report any song information if you have no devices playing songs. Just start playing a song and it will start displaying. -- It may randomly stop updating song information. You can deselect and reselect Spotify as the audio source to fix it. + +### iTunes +- iTunes will open when the audio source is selected, and cannot be changed (as far as I know). + +### MusicBee +- Make sure that the [Musicbee IPC plugin](https://getmusicbee.com/addons/plugins/138/musicbeeipc/) is installed for MusicBee. Use this [link](https://getmusicbee.com/forum/index.php?topic=11492.0) if that link doesn't work. ## Customization Right click audio band and select `Audio Band Settings` and a new window will appear where you can do your customization. A description of the options can be found [here](https://github.com/dsafa/audio-band/wiki/Audio-Band-settings). *In app help coming soon*