public TrackViewModel( Track track, IAudioPlaybackEngine playbackService) { this._track = track ?? throw new ArgumentNullException(nameof(track)); this._playbackService = playbackService ?? throw new ArgumentNullException(nameof(playbackService)); this._isLoaded_OAPH = this._playbackService.WhenTrackChanged .Select(loadedTrack => loadedTrack == this.Track) .ToProperty(this, nameof(this.IsLoaded)) .DisposeWith(this._disposables); this._isLoved_OAPH = this.Track.WhenAnyValue(x => x.IsLoved) .ToProperty(this, nameof(this.IsLoved)) .DisposeWith(this._disposables); this.PlayTrack = ReactiveCommand.CreateFromTask( async() => { await this._playbackService.StopAsync(); await this._playbackService.LoadAndPlayAsync(this._track); } , Observable.CombineLatest( this._playbackService.WhenCanLoadChanged, this._playbackService.WhenCanPlayChanged, (canLoad, canPlay) => (canLoad || canPlay))) .DisposeWith(this._disposables); this.PlayTrack.ThrownExceptions.Subscribe(ex => Debug.WriteLine(ex.Message)).DisposeWith(this._disposables); }
private static void loadOSLibrary() { OperatingSystem os = Environment.OSVersion; Assembly asm; string libName; if ((os.Platform == PlatformID.Unix) || (os.Platform == PlatformID.MacOSX)) { // need to load the OpenAL devices asm = Assembly.LoadFrom("SFX-Engine-OAL.dll"); libName = "OpenAL"; } else { // need to load the NAudio devices asm = Assembly.LoadFrom("SFX-Engine-NAudio.dll"); libName = "NAudio"; } Type type = asm.GetType("com.kintoshmalae.SFXEngine." + libName + ".AudioFileFactory"); _FileFactory = Activator.CreateInstance(type) as AudioFileFactory; type = asm.GetType("com.kintoshmalae.SFXEngine." + libName + ".AudioPlaybackEngine"); _LoadedDevice = Activator.CreateInstance(type) as IAudioPlaybackEngine; _DriverNames = (type.GetMethod("driverNames").Invoke(null, null)) as ReadOnlyCollection <string>; _loadDriverByName = Delegate.CreateDelegate(typeof(LoadDriverInstance), type.GetMethod("loadDevice", new Type[] { typeof(string) })) as LoadDriverInstance; _loadDriverWithSample = Delegate.CreateDelegate(typeof(LoadDriverWithSample), type.GetMethod("loadDevice", new Type[] { typeof(string), typeof(AudioSampleFormat) })) as LoadDriverWithSample; }
public SimplePlaylistViewModel( IAudioPlaybackEngine audioPlaybackEngine, IWriteLibraryService writeLibraryService, IDialogService dialogService, TracksSubsetViewModel parentTracksSubsetViewModel, IObservable <IChangeSet <TrackViewModel, uint> > sourceTrackViewModelsChangesFlow, SimplePlaylist playlist) : base(audioPlaybackEngine, writeLibraryService, dialogService, parentTracksSubsetViewModel, sourceTrackViewModelsChangesFlow, playlist) { this.RemoveTrackFromSubset = ReactiveCommand.CreateFromTask( async(TrackViewModel trackViewModel) => { var selectedItem = this.SelectedTrackViewModel; if (this.SelectedTrackViewModel == trackViewModel) { // TODO: use dynamicdata watch overload to expose selected item as filter by id this.SelectedTrackViewModel = null; } try { // TODO: handle null? should not happen await this.Playlist?.Remove(trackViewModel.Id); } catch { this.SelectedTrackViewModel = selectedItem; } }) .DisposeWith(this._disposables); this.RemoveTrackFromSubset.ThrownExceptions .Subscribe(ex => Debug.WriteLine(ex)) .DisposeWith(this._disposables); }
public FolderPlaylistViewModel( IAudioPlaybackEngine audioPlaybackEngine, IDialogService dialogService, IObservable <IChangeSet <TrackViewModel, uint> > sourceTrackViewModelsChangesFlow, Func <Track, EditTrackTagsViewModel> editTrackViewModelFactoryMethod, FolderPlaylist playlistFolder, Func <PlaylistBase, PlaylistBaseViewModel> playlistViewModelFactoryMethod) : base(audioPlaybackEngine, dialogService, editTrackViewModelFactoryMethod, sourceTrackViewModelsChangesFlow, playlistFolder) { this._playlistViewModelFactoryMethod = playlistViewModelFactoryMethod ?? throw new ArgumentNullException(nameof(playlistViewModelFactoryMethod)); //this._serialChildrenPlaylistViewModelsSubscription = new SerialDisposable().DisposeWith(this._disposables); // TODO: move to Expand, if can expand is known from playlist entity (this._playlist as FolderPlaylist).Playlists .Connect() .Transform(playlistBaseImpl => this._playlistViewModelFactoryMethod.Invoke(playlistBaseImpl)) .DisposeMany() .RefCount() .AddKey(x => x.PlaylistId) .Sort(SortExpressionComparer <PlaylistBaseViewModel> .Ascending(vm => vm.Name)) .ObserveOn(RxApp.MainThreadScheduler) .Bind(out var newRooc) .Subscribe() .DisposeWith(this._disposables); this.PlaylistViewModelsROOC = newRooc; }
public AllTracksViewModel( IAudioPlaybackEngine audioPlaybackEngine, IDialogService dialogService, Func <Track, EditTrackTagsViewModel> editTrackViewModelFactoryMethod, IObservable <IChangeSet <TrackViewModel, uint> > sourceTrackViewModelsChanges) : base(audioPlaybackEngine, dialogService, editTrackViewModelFactoryMethod, sourceTrackViewModelsChanges) { }
public AudioWaveformPlayerWrapper(IAudioPlaybackEngine audioPlaybackEngine) { this._audioPlaybackEngine = audioPlaybackEngine ?? throw new ArgumentNullException(nameof(audioPlaybackEngine)); this._isPlayingOAPH = this._audioPlaybackEngine.WhenStatusChanged .Select(status => status == PlaybackStatus.Playing) .ToProperty(this, nameof(this.IsPlaying)); }
public SimplePlaylistViewModel( IAudioPlaybackEngine audioPlaybackEngine, IDialogService dialogService, Func <Track, EditTrackTagsViewModel> editTrackViewModelFactoryMethod, IObservable <IChangeSet <TrackViewModel, uint> > sourceTrackViewModelsChangesFlow, SimplePlaylist playlist) : base(audioPlaybackEngine, dialogService, editTrackViewModelFactoryMethod, sourceTrackViewModelsChangesFlow, playlist) { }
public AllTracksViewModel( IAudioPlaybackEngine audioPlaybackEngine, IWriteLibraryService writeLibraryService, IDialogService dialogService, TracksSubsetViewModel parentTracksSubsetViewModel, IObservable <IChangeSet <TrackViewModel, uint> > sourceTrackViewModelsChanges) : base(audioPlaybackEngine, writeLibraryService, dialogService, parentTracksSubsetViewModel, sourceTrackViewModelsChanges) { }
public Player(IAudioPlaybackEngine audioPlaybackEngine) { this._audioPlaybackEngine = audioPlaybackEngine; this._isPlayingOAPH = this._audioPlaybackEngine .WhenStatusChanged .Select(status => status == PlaybackStatus.Playing) .ToProperty(this, nameof(this.IsPlaying)); }
public PlaylistBaseViewModel( IAudioPlaybackEngine audioPlaybackEngine, IDialogService dialogService, Func <Track, EditTrackTagsViewModel> editTrackViewModelFactoryMethod, IObservable <IChangeSet <TrackViewModel, uint> > sourceTrackViewModelsChangesFlow, PlaylistBase playlist) : base(audioPlaybackEngine, dialogService, editTrackViewModelFactoryMethod, sourceTrackViewModelsChangesFlow) { this._playlist = playlist ?? throw new ArgumentNullException(nameof(playlist)); }
public PlaylistBaseViewModel( IAudioPlaybackEngine audioPlaybackEngine, IWriteLibraryService writeLibraryService, IDialogService dialogService, TracksSubsetViewModel parentTracksSubsetViewModel, IObservable <IChangeSet <TrackViewModel, uint> > sourceTrackViewModelsChangesFlow, PlaylistBase playlist) : base(audioPlaybackEngine, writeLibraryService, dialogService, parentTracksSubsetViewModel, sourceTrackViewModelsChangesFlow) { this._playlist = playlist ?? throw new ArgumentNullException(nameof(playlist)); }
//private readonly Random _random = new Random((int)DateTime.Now.Ticks); #endregion #region ctor public PlaybackQueue( IAudioPlaybackEngine audioPlayer //, IObservable<IChangeSet<Track> tracksCacheChanges ) { this._audioPlayer = audioPlayer ?? throw new ArgumentNullException(nameof(audioPlayer)); this._sourcedEntries = new SourceList <PlaybackQueueEntry>().DisposeWith(this._disposables); Observable.CombineLatest( this._audioPlayer.WhenCanLoadChanged, this._audioPlayer.WhenCanPlayChanged, (canLoad, canPlay) => canLoad && !canPlay) .Where(canLoadButNotPlay => canLoadButNotPlay == true) .Subscribe(async(s) => { PlaybackQueueEntry next = null; // = this._playlistEntries.Items.FirstOrDefault(); this._playlistEntries.Edit(list => { next = list.FirstOrDefault(); if (next == null) { // unsubscribe from playlist? return; } list.RemoveAt(0); }); if (next == null) { return; } switch (next.Track) { case Track nextTrack: await this._audioPlayer.LoadAsync(nextTrack); await this._audioPlayer.PlayAsync(); break; } }) .DisposeWith(this._disposables); //this._upNextEntries = new SourceList<PlaybackQueueEntry>().DisposeWith(this._disposables); this._playlistEntries = new SourceList <PlaybackQueueEntry>().DisposeWith(this._disposables); //this._upNextEntries.Connect().Bind(out this._upNextEntriesROOC); this._playlistEntries.Connect().Bind(out this._playlistEntriesROOC); }
public FolderPlaylistViewModel( IAudioPlaybackEngine audioPlaybackEngine, IWriteLibraryService writeLibraryService, IDialogService dialogService, TracksSubsetViewModel parentTracksSubsetViewModel, IObservable <IChangeSet <TrackViewModel, uint> > sourceTrackViewModelsChangesFlow, FolderPlaylist playlistFolder, Func <PlaylistBase, FolderPlaylistViewModel, PlaylistBaseViewModel> playlistViewModelFactoryMethod) : base(audioPlaybackEngine, writeLibraryService, dialogService, parentTracksSubsetViewModel, sourceTrackViewModelsChangesFlow, playlistFolder) { this._playlistViewModelFactoryMethod = playlistViewModelFactoryMethod ?? throw new ArgumentNullException(nameof(playlistViewModelFactoryMethod)); this._childrenSubscription = new SerialDisposable().DisposeWith(this._disposables); }
public static async Task LoadAndPlayAsync(this IAudioPlaybackEngine playbackService, Track track) { if (playbackService == null) { throw new ArgumentNullException(nameof(playbackService)); } if (track == null) { throw new ArgumentNullException(nameof(track)); } await playbackService.LoadAsync(track) /*.ConfigureAwait(false)*/; await playbackService.PlayAsync() /*.ConfigureAwait(false)*/; }
public PlaybackHistory(IAudioPlaybackEngine audioPlaybackEngine) { this._audioPlaybackEngine = audioPlaybackEngine ?? throw new ArgumentNullException(nameof(audioPlaybackEngine)); this.Entries = this._audioPlaybackEngine.WhenTrackChanged .SkipLast(1) .Where(t => t != null) .ToObservableChangeSet(50) //.Replay(3) //.Select(track => new PlaybackHistoryEntry(track)) .Transform(track => new PlaybackHistoryEntry(track)) .DisposeMany() .AsObservableList() .DisposeWith(this._disposables); //this.Entries.CountChanged.Subscribe(c => Debug.WriteLine("items: " + c)); }
public LocalLibraryService( IReadLibraryService readLibraryService, IWriteLibraryService writeLibraryService, IAudioPlaybackEngine audioPlaybackEngine, IDialogService dialogService, Func <Track, TrackViewModel> trackViewModelFactoryMethod) { this._readLibraryService = readLibraryService ?? throw new ArgumentNullException(nameof(readLibraryService)); this._writeLibraryService = writeLibraryService ?? throw new ArgumentNullException(nameof(writeLibraryService)); this._audioPlaybackEngine = audioPlaybackEngine ?? throw new ArgumentNullException(nameof(audioPlaybackEngine)); this._dialogService = dialogService ?? throw new ArgumentNullException(nameof(dialogService)); this._trackViewModelFactoryMethod = trackViewModelFactoryMethod ?? throw new ArgumentNullException(nameof(trackViewModelFactoryMethod)); //this._readLibraryService.TracksChanges.DeferUntilLoaded().Subscribe().DisposeWith(this._disposables); //this._readLibraryService.PlaylistsChanges.DeferUntilLoaded().Subscribe().DisposeWith(this._disposables); this._tracksSubscription = new SerialDisposable().DisposeWith(this._disposables); this._playlistsSubscription = new SerialDisposable().DisposeWith(this._disposables); this.TrackViewModelsChangeSets = this._readLibraryService.TracksChanges .Transform(track => this._trackViewModelFactoryMethod.Invoke(track), new ParallelisationOptions(ParallelType.Parallelise)) .DisposeMany() // TODO: is RefCount needed with multicast+replay? .Multicast(new ReplaySubject <IChangeSet <TrackViewModel, uint> >()) .AutoConnect(1, subscription => this._tracksSubscription.Disposable = subscription); //.RefCount(); this.PlaylistViewModelsChanges = this._readLibraryService.PlaylistsChanges .Transform(playlist => this.CreatePlaylistViewModel(playlist, null), new ParallelisationOptions(ParallelType.Parallelise)) .DisposeMany() .Multicast(new ReplaySubject <IChangeSet <PlaylistBaseViewModel, uint> >()) .AutoConnect(1, subscription => this._playlistsSubscription.Disposable = subscription); //.RefCount(); //this.TrackViewModelsChangeSets.DeferUntilLoaded().Subscribe().DisposeWith(this._disposables); //this.PlaylistViewModelsChanges.DeferUntilLoaded().Subscribe().DisposeWith(this._disposables); this.AllTracksViewModel = new AllTracksViewModel( this._audioPlaybackEngine, //this._readLibraryService, this._writeLibraryService, this._dialogService, null, this.TrackViewModelsChangeSets); }
public TracksViewModel( IAudioPlaybackEngine audioPlaybackEngine, IReadLibraryService readLibraryService, Func <Track, TrackViewModel> trackViewModelFactoryMethod) { this._audioPlaybackEngine = audioPlaybackEngine ?? throw new ArgumentNullException(nameof(audioPlaybackEngine)); this._readLibraryService = readLibraryService ?? throw new ArgumentNullException(nameof(readLibraryService)); this._trackViewModelFactoryMethod = trackViewModelFactoryMethod ?? throw new ArgumentNullException(nameof(trackViewModelFactoryMethod)); this._serialViewModelsChangesSubscription = new SerialDisposable().DisposeWith(this._disposables); this._tracksSubscriber = this._readLibraryService.TracksChanges //.RefCount() .Transform(track => this._trackViewModelFactoryMethod(track), new ParallelisationOptions(ParallelType.Parallelise)) .DisposeMany() //.Sort(SortExpressionComparer<TrackViewModel>.Ascending(vm => this._random.Next())) .Sort(SortExpressionComparer <TrackViewModel> .Ascending(vm => vm.Id)) .Multicast(new ReplaySubject <IChangeSet <TrackViewModel, uint> >()) .AutoConnect(1, subscription => this._serialViewModelsChangesSubscription.Disposable = subscription) .ObserveOn(RxApp.MainThreadScheduler) .SubscribeOn(RxApp.TaskpoolScheduler) .Bind(out this._trackViewModels) ; this.PlayTrack = ReactiveCommand.CreateFromTask( async(TrackViewModel trackVM) => { // TODO: add ConfigureAwait await this._audioPlaybackEngine.StopAsync() /*.ConfigureAwait(false)*/; await this._audioPlaybackEngine.LoadAndPlayAsync(trackVM.Track) /*.ConfigureAwait(false)*/; }, Observable.CombineLatest( this.WhenAnyValue(subset => subset.SelectedTrackViewModel), this._audioPlaybackEngine.WhenCanLoadChanged, this._audioPlaybackEngine.WhenCanPlayChanged, this._audioPlaybackEngine.WhenCanStopChanged, (selectedTrackViewModel, canLoad, canPlay, canStop) => selectedTrackViewModel != null && (canLoad || canPlay || canStop))); this.PlayTrack.ThrownExceptions //.ObserveOn(RxApp.MainThreadScheduler) .Subscribe(ex => Debug.WriteLine(ex.Message)) .DisposeWith(this._disposables); this.PlayTrack.DisposeWith(this._disposables); }
public TracksSubsetViewModel( IAudioPlaybackEngine audioPlaybackEngine, IDialogService dialogService, Func <Track, EditTrackTagsViewModel> editTrackViewModelFactoryMethod, IObservable <IChangeSet <TrackViewModel, uint> > sourceTrackViewModelsChanges) { this._audioPlaybackEngine = audioPlaybackEngine ?? throw new ArgumentNullException(nameof(audioPlaybackEngine)); this._dialogService = dialogService ?? throw new ArgumentNullException(nameof(dialogService)); this._editTrackTagsViewModelFactoryMethod = editTrackViewModelFactoryMethod ?? throw new ArgumentNullException(nameof(editTrackViewModelFactoryMethod)); this._sourceTrackViewModelsChanges = sourceTrackViewModelsChanges ?? throw new ArgumentNullException(nameof(sourceTrackViewModelsChanges)); this._serialViewModelsChangesSubscription = new SerialDisposable().DisposeWith(this._disposables); this.PlayTrack = ReactiveCommand.CreateFromTask( async(TrackViewModel trackVM) => { // TODO: add ConfigureAwait await this._audioPlaybackEngine.StopAsync() /*.ConfigureAwait(false)*/; await this._audioPlaybackEngine.LoadAndPlayAsync(trackVM.Track) /*.ConfigureAwait(false)*/; }, Observable.CombineLatest( this.WhenAnyValue(t => t.SelectedTrackViewModel), this._audioPlaybackEngine.WhenCanLoadChanged, this._audioPlaybackEngine.WhenCanPlayChanged, this._audioPlaybackEngine.WhenCanStopChanged, (selectedTrackViewModel, canLoad, canPlay, canStop) => selectedTrackViewModel != null && (canLoad || canPlay || canStop))) .DisposeWith(this._disposables); this.PlayTrack.ThrownExceptions .Subscribe(ex => Debug.WriteLine(ex.Message)) .DisposeWith(this._disposables); this.EditTrackTags = ReactiveCommand.Create( (TrackViewModel vm) => { this._dialogService.ShowDialog(this._editTrackTagsViewModelFactoryMethod(vm.Track)); }, this.WhenAny(x => x.SelectedTrackViewModel, x => x.Value != null)) .DisposeWith(this._disposables); this.EditTrackTags.ThrownExceptions .Subscribe(ex => Debug.WriteLine(ex.Message)) .DisposeWith(this._disposables); }
public PlaybackProgressViewModel( IAudioPlaybackEngine audioPlaybackEngine) { this._audioPlaybackEngine = audioPlaybackEngine ?? throw new ArgumentNullException(nameof(audioPlaybackEngine)); // timespans this._positionOAPH = this._audioPlaybackEngine.WhenPositionChanged.ToProperty(this, nameof(this.Position)).DisposeWith(this._disposables); this._durationOAPH = this._audioPlaybackEngine.WhenDurationChanged.ToProperty(this, nameof(this.Duration)).DisposeWith(this._disposables); this._isDurationKnownOAPH = this._audioPlaybackEngine .WhenDurationChanged .Select(duration => duration.HasValue) .ToProperty(this, nameof(this.IsDurationKnown)) .DisposeWith(this._disposables); this._isPositionKnownOAPH = this._audioPlaybackEngine .WhenPositionChanged .Select(position => position.HasValue) .ToProperty(this, nameof(this.IsPositionKnown)) .DisposeWith(this._disposables); }
public ShellViewModel( IAudioPlaybackEngine playbackService, //IWriteLibraryService writeLibraryService, IReadLibraryService readLibraryService, IDialogService dialogService, LibraryViewModel libraryViewModel, PlaybackControlsViewModel playbackControlsViewModel, PlaybackHistoryViewModel playbackHistoryViewModel, ShellMenuViewModel shellMenuViewModel) { this._playbackService = playbackService ?? throw new ArgumentNullException(nameof(playbackService)); //this._writeLibraryService = writeLibraryService ?? throw new ArgumentNullException(nameof(writeLibraryService)); this._readLibraryService = readLibraryService ?? throw new ArgumentNullException(nameof(readLibraryService)); this._dialogService = dialogService ?? throw new ArgumentNullException(nameof(dialogService)); this.LibraryViewModel = libraryViewModel ?? throw new ArgumentNullException(nameof(libraryViewModel)); this.PlaybackControlsViewModel = playbackControlsViewModel ?? throw new ArgumentNullException(nameof(playbackControlsViewModel)); this.PlaybackHistoryViewModel = playbackHistoryViewModel ?? throw new ArgumentNullException(nameof(playbackHistoryViewModel)); this.ShellMenuViewModel = shellMenuViewModel ?? throw new ArgumentNullException(nameof(shellMenuViewModel)); this._isEnabled_OAPH = Observable .Return(true) .ToProperty(this, nameof(this.IsEnabled)) .DisposeWith(this._disposables); this._taskbarProgressState_OAPH = Observable.CombineLatest( this._playbackService.WhenStatusChanged, this._playbackService.WhenDurationChanged, this._playbackService.WhenPositionChanged, (status, duration, position) => { switch (status) { case PlaybackStatus.Loaded: case PlaybackStatus.PlayedToEnd: case PlaybackStatus.ManuallyInterrupted: case PlaybackStatus.None: return(TaskbarItemProgressState.None); case PlaybackStatus.Playing: if (duration.HasValue && position.HasValue) { return(TaskbarItemProgressState.Normal); } return(TaskbarItemProgressState.Indeterminate); case PlaybackStatus.Paused: return(TaskbarItemProgressState.Paused); case PlaybackStatus.Loading: return(TaskbarItemProgressState.Indeterminate); case PlaybackStatus.Exploded: return(TaskbarItemProgressState.Error); default: return(TaskbarItemProgressState.None); } }) .DistinctUntilChanged() .ToProperty(this, nameof(this.TaskbarProgressState)) .DisposeWith(this._disposables); this._taskbarProgressValue_OAPH = Observable.CombineLatest( this._playbackService.WhenDurationChanged, this._playbackService.WhenPositionChanged, (duration, position) => { if (duration.HasValue && position.HasValue) { return(position.Value.TotalMilliseconds / duration.Value.TotalMilliseconds); } return(Double.NaN); }) .DistinctUntilChanged() .ToProperty(this, nameof(this.TaskbarProgressValue)) .DisposeWith(this._disposables); this._playbackService.WhenTrackChanged .Subscribe(track => this.UpdateDisplayName(track)) .DisposeWith(this._disposables); this.ActivateItem(this.LibraryViewModel); this.ActivateItem(this.PlaybackControlsViewModel); this.ActivateItem(this.PlaybackHistoryViewModel); this.ActivateItem(this.ShellMenuViewModel); }
public TrackViewModel( Track track, IAudioPlaybackEngine playbackService, IDialogService dialogService, Func <Track, EditTrackViewModel> editTrackViewModelFactoryMethod) { this._track = track ?? throw new ArgumentNullException(nameof(track)); this._playbackService = playbackService ?? throw new ArgumentNullException(nameof(playbackService)); this._dialogService = dialogService ?? throw new ArgumentNullException(nameof(dialogService)); this._editTrackViewModelFactoryMethod = editTrackViewModelFactoryMethod ?? throw new ArgumentNullException(nameof(editTrackViewModelFactoryMethod)); this._isLoaded_OAPH = this._playbackService.WhenTrackChanged .ObserveOn(RxApp.MainThreadScheduler) .Select(loadedTrack => loadedTrack == this.Track) .ToProperty(this, nameof(this.IsLoaded), deferSubscription: true) .DisposeWith(this._disposables); // TODO: or use whenanyvalue? this._isLoved_OAPH = //this.Track.ObservableForProperty(x => x.IsLoved, skipInitial: false) this.WhenAnyValue(x => x.Track.IsLoved) //.ObserveOn(RxApp.MainThreadScheduler) //.Select(x => x.Value) .ToProperty(this, nameof(this.IsLoved), deferSubscription: true) .DisposeWith(this._disposables); this.PlayTrack = ReactiveCommand.CreateFromTask( async() => { await this._playbackService.StopAsync(); await this._playbackService.LoadAndPlayAsync(this._track); }, Observable.CombineLatest( this._playbackService.WhenCanLoadChanged, this._playbackService.WhenCanPlayChanged, (canLoad, canPlay) => canLoad || canPlay)) .DisposeWith(this._disposables); this.PlayTrack.ThrownExceptions.Subscribe(ex => Debug.WriteLine(ex)).DisposeWith(this._disposables); this.EditTrackTags = ReactiveCommand.CreateFromTask( async() => { await this._dialogService.ShowDialogAsync(this._editTrackViewModelFactoryMethod?.Invoke(this.Track)); }) .DisposeWith(this._disposables); this.EditTrackTags.ThrownExceptions.Subscribe(ex => Debug.WriteLine(ex)).DisposeWith(this._disposables); this.ShowInFileManager = ReactiveCommand.Create( () => { // TODO: handle exceptions if (this.TrackLocation.IsFile) { Process.Start( "explorer.exe", $"/select, \"{this.TrackLocation.GetComponents(UriComponents.AbsoluteUri, UriFormat.UriEscaped)}\""); } else { Process.Start(this.TrackLocation.GetComponents(UriComponents.AbsoluteUri, UriFormat.UriEscaped)); } }, this.WhenAnyValue(x => x.TrackLocation).Select(x => x != null)) .DisposeWith(this._disposables); this.ShowInFileManager.ThrownExceptions.Subscribe(ex => Debug.WriteLine(ex)).DisposeWith(this._disposables); }
//private readonly PlaybackQueue _playbackQueue; //private readonly PlaybackHistory _playbackHistory; //private readonly IReadLibraryService _readLibraryService; #endregion #region constructors public PlaybackControlsViewModel( //IPlaybackService playbackService, IAudioPlaybackEngine audioPlaybackEngine, //PlaybackQueue playbackQueue, //PlaybackHistory playbackHistory, //IReadLibraryService readLibraryService PlaybackTimelineViewModel playbackTimelineViewModel ) { // TODO: log //this._playbackService = playbackService ?? throw new ArgumentNullException(nameof(playbackService)); this._audioPlaybackEngine = audioPlaybackEngine ?? throw new ArgumentNullException(nameof(audioPlaybackEngine)); //this._playbackQueue = playbackQueue ?? throw new ArgumentNullException(nameof(playbackQueue)); //this._playbackHistory = playbackHistory ?? throw new ArgumentNullException(nameof(playbackHistory)); //this._readLibraryService = readLibraryService ?? throw new ArgumentNullException(nameof(readLibraryService)); this.PlaybackTimelineViewModel = playbackTimelineViewModel ?? throw new ArgumentNullException(nameof(playbackTimelineViewModel)); this.Pause = ReactiveCommand.CreateFromTask(() => this._audioPlaybackEngine.PauseAsync(), this._audioPlaybackEngine.WhenCanPauseChanged).DisposeWith(this._disposables); this.Resume = ReactiveCommand.CreateFromTask(() => this._audioPlaybackEngine.ResumeAsync(), this._audioPlaybackEngine.WhenCanResumeChanged).DisposeWith(this._disposables); this.Stop = ReactiveCommand.CreateFromTask(() => this._audioPlaybackEngine.StopAsync(), this._audioPlaybackEngine.WhenCanStopChanged).DisposeWith(this._disposables); //this.PlayPrevious = ReactiveCommand // .CreateFromTask(async () => // { // await this._playbackService.StopAsync(); // var next = this._playbackQueue.Remove(); // if (next != null) // { // await this._playbackService.LoadAsync(next); // await this._playbackService.PlayAsync(); // } // }, Observable.CombineLatest( // this._playbackQueue.Items.Connect().IsEmpty(), // this._playbackService.WhenCanStopChanged, // (isEmpty, canStop) => !isEmpty && canStop)) // .DisposeWith(this._disposables); //this.PlayNext = ReactiveCommand // .CreateFromTask(async () => // { // await this._playbackService.StopAsync(); // var next = this._playbackQueue.Remove(); // if (next != null) // { // await this._playbackService.LoadAsync(next); // await this._playbackService.PlayAsync(); // } // }, Observable.CombineLatest( // this._playbackQueue.Items.Connect().IsEmpty(), // this._playbackService.WhenCanStopChanged, // (isEmpty, canStop) => !isEmpty && canStop)) // .DisposeWith(this._disposables); this._volumeOAPH = this._audioPlaybackEngine.WhenVolumeChanged .ToProperty(this, nameof(this.Volume)) .DisposeWith(this._disposables); this._canPauseOAPH = this._audioPlaybackEngine.WhenCanPauseChanged .ToProperty(this, nameof(this.CanPause)) .DisposeWith(this._disposables); this._canResumeOAPH = this._audioPlaybackEngine.WhenCanResumeChanged .ToProperty(this, nameof(this.CanResume)) .DisposeWith(this._disposables); }
public LibraryViewModel( IAudioFileInfoProvider audioFileInfoProvider, //IReadLibraryService readLibraryService, IWriteLibraryService writeLibraryService, IAudioPlaybackEngine audioPlaybackEngine, IDialogService dialogService, Services.LocalLibraryService libraryViewModelsProxy //Func<Track, EditTrackTagsViewModel> editTrackViewModelFactoryMethod, //Func<PlaylistBase, PlaylistBaseViewModel> playlistBaseViewModelFactoryMethod ) { this._audioFileInfoProvider = audioFileInfoProvider ?? throw new ArgumentNullException(nameof(audioFileInfoProvider)); //this._readLibraryService = readLibraryService ?? throw new ArgumentNullException(nameof(readLibraryService)); this._writeLibraryService = writeLibraryService ?? throw new ArgumentNullException(nameof(writeLibraryService)); this._audioPlaybackEngine = audioPlaybackEngine ?? throw new ArgumentNullException(nameof(audioPlaybackEngine)); this._dialogService = dialogService ?? throw new ArgumentNullException(nameof(dialogService)); this._libraryViewModelsProxy = libraryViewModelsProxy ?? throw new ArgumentNullException(nameof(libraryViewModelsProxy)); //this._editTrackTagsViewModelFactoryMethod = editTrackViewModelFactoryMethod ?? throw new ArgumentNullException(nameof(editTrackViewModelFactoryMethod)); //this._playlistBaseViewModelFactoryMethod = playlistBaseViewModelFactoryMethod ?? throw new ArgumentNullException(nameof(playlistBaseViewModelFactoryMethod)); this._serialViewModelsChangesSubscription = new SerialDisposable().DisposeWith(this._disposables); this.ShowFilePicker = ReactiveCommand.CreateFromTask( async() => { var openFileDialogResult = await this._dialogService.OpenFileDialogAsync( "Add files to library ...", Environment.GetFolderPath(Environment.SpecialFolder.MyMusic), true, new Dictionary <string, IReadOnlyCollection <string> > { { "Audio files", this._audioPlaybackEngine.SupportedExtensions } }); if (openFileDialogResult.IsConfirmed != true) { return; } IList <AddTrackCommand> atc = new List <AddTrackCommand>(); foreach (var filePath in openFileDialogResult.Content) { var audioFileInfo = await this._audioFileInfoProvider.ExtractAudioFileInfo(new Uri(filePath)); if (audioFileInfo == null) { // TODO: handle exceptions // TODO: log } atc.Add(new AddTrackCommand( audioFileInfo.Location, audioFileInfo.Duration, audioFileInfo.LastModifiedDateTime, audioFileInfo.SizeBytes, audioFileInfo.Tags.Title, audioFileInfo.Tags.PerformersNames, audioFileInfo.Tags.ComposersNames, audioFileInfo.Tags.Year, new TrackAlbumAssociation( new Album( audioFileInfo.Tags.AlbumTitle, audioFileInfo.Tags.AlbumAuthors, audioFileInfo.Tags.AlbumTracksCount, audioFileInfo.Tags.AlbumDiscsCount), audioFileInfo.Tags.AlbumTrackNumber, audioFileInfo.Tags.AlbumDiscNumber))); } //var addedTracks = await this._writeLibraryService.AddTracksAsync(atc); }) .DisposeWith(this._disposables); this.ShowFilePicker.ThrownExceptions .Subscribe(ex => Debug.WriteLine(ex)) .DisposeWith(this._disposables); this.AllTracksViewModel = this._libraryViewModelsProxy.AllTracksViewModel; //this._libraryViewModelsProxy.PlaylistViewModelsChanges.Bind(out var playlists).Subscribe(_ => this.PlaylistViewModelsROOC = playlists).DisposeWith(this._disposables); //this._libraryViewModelsProxy.PlaylistViewModels // .Cast<IChangeSet<TracksSubsetViewModel, uint>>() // .StartWithItem(this.AllTracksViewModel, 0u) // .Bind(out var subsets).Subscribe(_ => this.PlaylistViewModelsROOC = playlists).DisposeWith(this._disposables); }
public PlaybackControlsViewModel( //IPlaybackService playbackService, IAudioPlaybackEngine audioPlaybackEngine, //PlaybackQueue playbackQueue, //PlaybackHistory playbackHistory, //IReadLibraryService readLibraryService PlaybackTimelineViewModel playbackTimelineViewModel, IDialogService dialogService ) { // TODO: log //this._playbackService = playbackService ?? throw new ArgumentNullException(nameof(playbackService)); this._audioPlaybackEngine = audioPlaybackEngine ?? throw new ArgumentNullException(nameof(audioPlaybackEngine)); //this._playbackQueue = playbackQueue ?? throw new ArgumentNullException(nameof(playbackQueue)); //this._playbackHistory = playbackHistory ?? throw new ArgumentNullException(nameof(playbackHistory)); //this._readLibraryService = readLibraryService ?? throw new ArgumentNullException(nameof(readLibraryService)); this.PlaybackTimelineViewModel = playbackTimelineViewModel ?? throw new ArgumentNullException(nameof(playbackTimelineViewModel)); this._dialogService = dialogService ?? throw new ArgumentNullException(nameof(dialogService)); this._volume_OAPH = this._audioPlaybackEngine.WhenVolumeChanged .ObserveOn(RxApp.MainThreadScheduler) .ToProperty(this, nameof(this.Volume)) .DisposeWith(this._disposables); this._hasLoadedTrack_OAPH = this._audioPlaybackEngine.WhenTrackChanged .ObserveOn(RxApp.MainThreadScheduler) .Select(x => x == null) .ToProperty(this, nameof(this.HasLoadedTrack)) .DisposeWith(this._disposables); this._canPause_OAPH = this._audioPlaybackEngine.WhenCanPauseChanged .ObserveOn(RxApp.MainThreadScheduler) .ToProperty(this, nameof(this.CanPause)) .DisposeWith(this._disposables); this._canResume_OAPH = this._audioPlaybackEngine.WhenCanResumeChanged .ObserveOn(RxApp.MainThreadScheduler) .ToProperty(this, nameof(this.CanResume)) .DisposeWith(this._disposables); this.PlayAll = ReactiveCommand.Create( () => throw new NotImplementedException(), Observable.Return(false)) .DisposeWith(this._disposables); this.PlayAll.ThrownExceptions .Subscribe(ex => Debug.WriteLine(ex)) .DisposeWith(this._disposables); this.Pause = ReactiveCommand.CreateFromTask( () => this._audioPlaybackEngine.PauseAsync(), this._audioPlaybackEngine.WhenCanPauseChanged.ObserveOn(RxApp.MainThreadScheduler)) .DisposeWith(this._disposables); this.Pause.ThrownExceptions .Subscribe(ex => Debug.WriteLine(ex)) .DisposeWith(this._disposables); this.Resume = ReactiveCommand.CreateFromTask( () => this._audioPlaybackEngine.ResumeAsync(), this._audioPlaybackEngine.WhenCanResumeChanged.ObserveOn(RxApp.MainThreadScheduler)) .DisposeWith(this._disposables); this.Resume.ThrownExceptions .Subscribe(ex => Debug.WriteLine(ex)) .DisposeWith(this._disposables); this.Stop = ReactiveCommand.CreateFromTask( () => this._audioPlaybackEngine.StopAsync(), this._audioPlaybackEngine.WhenCanStopChanged.ObserveOn(RxApp.MainThreadScheduler)) .DisposeWith(this._disposables); this.Stop.ThrownExceptions .Subscribe(ex => Debug.WriteLine(ex)) .DisposeWith(this._disposables); //this.PlayPrevious = ReactiveCommand // .CreateFromTask(async () => // { // await this._playbackService.StopAsync(); // var next = this._playbackQueue.Remove(); // if (next != null) // { // await this._playbackService.LoadAsync(next); // await this._playbackService.PlayAsync(); // } // }, Observable.CombineLatest( // this._playbackQueue.Items.Connect().IsEmpty(), // this._playbackService.WhenCanStopChanged, // (isEmpty, canStop) => !isEmpty && canStop)) // .DisposeWith(this._disposables); //this.PlayNext = ReactiveCommand // .CreateFromTask(async () => // { // await this._playbackService.StopAsync(); // var next = this._playbackQueue.Remove(); // if (next != null) // { // await this._playbackService.LoadAsync(next); // await this._playbackService.PlayAsync(); // } // }, Observable.CombineLatest( // this._playbackQueue.Items.Connect().IsEmpty(), // this._playbackService.WhenCanStopChanged, // (isEmpty, canStop) => !isEmpty && canStop)) // .DisposeWith(this._disposables); }
public PlaybackTimelineViewModel( IAudioPlaybackEngine audioPlaybackEngine) { this._audioPlaybackEngine = audioPlaybackEngine ?? throw new ArgumentNullException(nameof(audioPlaybackEngine)); this._seekingSemaphore = new SemaphoreSlim(1, 1).DisposeWith(this._disposables); // timespans this._positionOAPH = this._audioPlaybackEngine.WhenPositionChanged .ObserveOn(RxApp.MainThreadScheduler) .ToProperty(this, nameof(this.Position)) .DisposeWith(this._disposables); this._durationOAPH = this._audioPlaybackEngine.WhenDurationChanged .ObserveOn(RxApp.MainThreadScheduler) .ToProperty(this, nameof(this.Duration)) .DisposeWith(this._disposables); // milliseconds this._positionAsTickssOAPH = this._audioPlaybackEngine.WhenPositionChanged .ObserveOn(RxApp.MainThreadScheduler) .Where(p => !this._isSeeking) // TODO: use an observable to toggle observing .Select(p => p != null && p.HasValue ? Convert.ToInt64(p.Value.Ticks) : 0L) .ToProperty(this, nameof(this.PositionAsTicks)) .DisposeWith(this._disposables); this._durationAsTicksOAPH = this._audioPlaybackEngine.WhenDurationChanged .ObserveOn(RxApp.MainThreadScheduler) .Select(p => p != null && p.HasValue ? Convert.ToInt64(p.Value.Ticks) : 0L) .ToProperty(this, nameof(this.DurationAsTicks)) .DisposeWith(this._disposables); //this._currentTrackLocationOAPH = this._audioPlaybackEngine // .WhenAudioSourceLocationChanged // .Select(l => l?.ToString()) // .ToProperty(this, nameof(this.CurrentTrackLocation)) // .DisposeWith(this._disposables); //this._isSeekableStatusOAPH = this._audioPlaybackEngine // .WhenStatusChanged // .Select(status => PlaybackStatusHelper.CanSeekPlaybackStatuses.Contains(status)) // .ToProperty(this, nameof(this.IsSeekableStatus)) // .DisposeWith(this._disposables); //this._isPositionSeekableOAPH = this._audioPlaybackEngine // .WhenCanSeekChanged // .ToProperty(this, nameof(this.IsPositionSeekable)) // .DisposeWith(this._disposables); this._isDurationKnownOAPH = this._audioPlaybackEngine.WhenDurationChanged .ObserveOn(RxApp.MainThreadScheduler) .Select(duration => duration.HasValue) .ToProperty(this, nameof(this.IsDurationKnown)) .DisposeWith(this._disposables); this._isPositionKnownOAPH = this._audioPlaybackEngine.WhenPositionChanged .ObserveOn(RxApp.MainThreadScheduler) .Select(position => position.HasValue) .ToProperty(this, nameof(this.IsPositionKnown)) .DisposeWith(this._disposables); this._isLoadingOAPH = this._audioPlaybackEngine.WhenStatusChanged .ObserveOn(RxApp.MainThreadScheduler) // TODO: can remove? should others use it? .Select(status => status == PlaybackStatus.Loading) .ToProperty(this, nameof(this.IsLoading)) .DisposeWith(this._disposables); this.StartSeeking = ReactiveCommand.CreateFromTask(async() => { await this._seekingSemaphore.WaitAsync(); this._isSeeking = true; this._lastSoughtTicks = null; this._seekingSemaphore.Release(); }, this._audioPlaybackEngine.WhenCanSeekChanged.ObserveOn(RxApp.MainThreadScheduler)) .DisposeWith(this._disposables); this.StartSeeking.ThrownExceptions.Subscribe(ex => Debug.WriteLine(ex)).DisposeWith(this._disposables); this.SeekTo = ReactiveCommand.CreateFromTask <long>( async ticks => { await this._seekingSemaphore.WaitAsync(); if (this._isSeeking) { this._lastSoughtTicks = ticks; await this._audioPlaybackEngine.SeekToAsync(TimeSpan.FromTicks(ticks)); } this._seekingSemaphore.Release(); }, this._audioPlaybackEngine.WhenCanSeekChanged.ObserveOn(RxApp.MainThreadScheduler)) .DisposeWith(this._disposables); this.SeekTo.ThrownExceptions.Subscribe(ex => Debug.WriteLine(ex)).DisposeWith(this._disposables); this.EndSeeking = //ReactiveCommand.CreateFromTask<long>(async () => { this._isSeeking = false; await this._audioPlaybackEngine.ResumeAsync(); } ReactiveCommand.CreateFromTask <long>(async ticks => { await this._seekingSemaphore.WaitAsync(); //if (!this._lastSoughtTicks.HasValue || this._lastSoughtTicks.Value != ticks) //{ await this._audioPlaybackEngine.SeekToAsync(TimeSpan.FromTicks(ticks)); //} this._isSeeking = false; this._seekingSemaphore.Release(); }, this._audioPlaybackEngine.WhenCanSeekChanged.ObserveOn(RxApp.MainThreadScheduler)) .DisposeWith(this._disposables); this.EndSeeking.ThrownExceptions.Subscribe(ex => Debug.WriteLine(ex)).DisposeWith(this._disposables); }
public ShellViewModel( IAudioPlaybackEngine audioPlaybackEngine, //IWriteLibraryService writeLibraryService, IReadLibraryService readLibraryService, IDialogService dialogService, LibraryViewModel libraryViewModel, PlaybackControlsViewModel playbackControlsViewModel, //PlaybackHistoryViewModel playbackHistoryViewModel, ShellMenuViewModel shellMenuViewModel, Func <MiniPlayerViewModel> _miniplayerViewModelFactoryMethod) { this._audioPlaybackEngine = audioPlaybackEngine ?? throw new ArgumentNullException(nameof(audioPlaybackEngine)); //this._writeLibraryService = writeLibraryService ?? throw new ArgumentNullException(nameof(writeLibraryService)); this._readLibraryService = readLibraryService ?? throw new ArgumentNullException(nameof(readLibraryService)); this._dialogService = dialogService ?? throw new ArgumentNullException(nameof(dialogService)); this.LibraryViewModel = libraryViewModel ?? throw new ArgumentNullException(nameof(libraryViewModel)); this.PlaybackControlsViewModel = playbackControlsViewModel ?? throw new ArgumentNullException(nameof(playbackControlsViewModel)); //this.PlaybackHistoryViewModel = playbackHistoryViewModel ?? throw new ArgumentNullException(nameof(playbackHistoryViewModel)); this.ShellMenuViewModel = shellMenuViewModel ?? throw new ArgumentNullException(nameof(shellMenuViewModel)); this.miniplayerViewModelFactoryMethod = _miniplayerViewModelFactoryMethod ?? throw new ArgumentNullException(nameof(_miniplayerViewModelFactoryMethod)); this._isEnabled_OAPH = Observable .Return(true) .ToProperty(this, nameof(this.IsEnabled)) .DisposeWith(this._disposables); this._taskbarProgressState_OAPH = Observable.CombineLatest( this._audioPlaybackEngine.WhenStatusChanged, this._audioPlaybackEngine.WhenDurationChanged, this._audioPlaybackEngine.WhenPositionChanged, (status, duration, position) => { switch (status) { case PlaybackStatus.Loaded: case PlaybackStatus.PlayedToEnd: case PlaybackStatus.ManuallyInterrupted: case PlaybackStatus.None: return(TaskbarItemProgressState.None); case PlaybackStatus.Playing: if (duration.HasValue && position.HasValue) { return(TaskbarItemProgressState.Normal); } return(TaskbarItemProgressState.Indeterminate); case PlaybackStatus.Paused: return(TaskbarItemProgressState.Paused); case PlaybackStatus.Loading: return(TaskbarItemProgressState.Indeterminate); case PlaybackStatus.Exploded: return(TaskbarItemProgressState.Error); default: return(TaskbarItemProgressState.None); } }) .DistinctUntilChanged() .ToProperty(this, nameof(this.TaskbarProgressState)) .DisposeWith(this._disposables); this._taskbarProgressValue_OAPH = Observable.CombineLatest( this._audioPlaybackEngine.WhenDurationChanged, this._audioPlaybackEngine.WhenPositionChanged, (duration, position) => { if (duration.HasValue && position.HasValue) { return(position.Value.TotalMilliseconds / duration.Value.TotalMilliseconds); } return(Double.NaN); }) .DistinctUntilChanged() .ToProperty(this, nameof(this.TaskbarProgressValue)) .DisposeWith(this._disposables); this._audioPlaybackEngine.WhenTrackChanged //.ObserveOn(RxApp.MainThreadScheduler) .Subscribe(track => this.UpdateDisplayName(track)) .DisposeWith(this._disposables); this.HideShellAndShowMiniPlayer = ReactiveCommand.CreateFromTask( async() => { var miniPlayerVM = this.miniplayerViewModelFactoryMethod.Invoke(); this.IsVisible = false; await this._dialogService.ShowWindowAsync(miniPlayerVM); //this.IsVisible = true; }) .DisposeWith(this._disposables); this.Items.Add(this.LibraryViewModel); this.Items.Add(this.PlaybackHistoryViewModel); this.Items.Add(this.PlaybackControlsViewModel); this.Items.Add(this.ShellMenuViewModel); }
public TracksSubsetViewModel( IAudioPlaybackEngine audioPlaybackEngine, IWriteLibraryService writeLibraryService, IDialogService dialogService, TracksSubsetViewModel parentTracksSubsetViewModel, IObservable <IChangeSet <TrackViewModel, uint> > sourceTrackViewModelsChanges) { this._audioPlaybackEngine = audioPlaybackEngine ?? throw new ArgumentNullException(nameof(audioPlaybackEngine)); this._writeLibraryService = writeLibraryService ?? throw new ArgumentNullException(nameof(writeLibraryService)); this._dialogService = dialogService ?? throw new ArgumentNullException(nameof(dialogService)); this._sourceTrackViewModelsChanges = sourceTrackViewModelsChanges ?? throw new ArgumentNullException(nameof(sourceTrackViewModelsChanges)); this.ParentTracksSubsetViewModel = parentTracksSubsetViewModel; this._serialViewModelsChangesSubscription = new SerialDisposable().DisposeWith(this._disposables); this._areTracksLoaded = this .WhenAnyValue(x => x.SortedFilteredTrackViewModelsROOC) .Select(x => x != null) .StartWith(this.SortedFilteredTrackViewModelsROOC != null) .ToProperty(this, nameof(this.AreTracksLoaded), deferSubscription: true) .DisposeWith(this._disposables); //this._tracksCount_OAPH = this // .WhenAnyObservable( // x => x.WhenPropertyChanged(e => e.SortedFilteredTrackViewModelsROOC, true, null) // ) // .Select(p => p?.Value?.Count) // .ToProperty(this, nameof(this.TracksCount)) // .DisposeWith(this._disposables); this.PlayTrack = ReactiveCommand.CreateFromTask( async(TrackViewModel trackVM) => { // TODO: add ConfigureAwait await this._audioPlaybackEngine.StopAsync() /*.ConfigureAwait(false)*/; await this._audioPlaybackEngine.LoadAndPlayAsync(trackVM.Track) /*.ConfigureAwait(false)*/; }, Observable.CombineLatest( this.WhenAnyValue(t => t.SelectedTrackViewModel), this._audioPlaybackEngine.WhenCanLoadChanged, this._audioPlaybackEngine.WhenCanPlayChanged, this._audioPlaybackEngine.WhenCanStopChanged, (selectedTrackViewModel, canLoad, canPlay, canStop) => selectedTrackViewModel != null && (canLoad || canPlay || canStop))) .DisposeWith(this._disposables); this.PlayTrack.ThrownExceptions .ObserveOn(RxApp.MainThreadScheduler) .Subscribe(ex => Debug.WriteLine(ex)) .DisposeWith(this._disposables); this.RemoveTrackFromLibrary = ReactiveCommand.CreateFromTask( async(TrackViewModel trackViewModel) => { if (this.SelectedTrackViewModel == trackViewModel) { this.SelectedTrackViewModel = null; } await this._writeLibraryService.RemoveTrackAsync(new RemoveTrackCommand(trackViewModel.Id)); }) .DisposeWith(this._disposables); this.RemoveTrackFromLibrary.ThrownExceptions .Subscribe(ex => Debug.WriteLine(ex)) .DisposeWith(this._disposables); }