diff --git a/Aurora/Design/Views/BaseViewModel.cs b/Aurora/Design/Views/BaseViewModel.cs index b8de4f6..221b0a4 100644 --- a/Aurora/Design/Views/BaseViewModel.cs +++ b/Aurora/Design/Views/BaseViewModel.cs @@ -75,9 +75,9 @@ namespace Aurora.Design.Views #endregion Player #region Lifecycle - public virtual void OnActive() { } + public virtual Task OnActive() { return Task.FromResult(null); } - public virtual void OnInactive() { } + public virtual Task OnInactive() { return Task.FromResult(null); } #endregion diff --git a/Aurora/Design/Views/Party/PartyViewModel.cs b/Aurora/Design/Views/Party/PartyViewModel.cs index a7db796..ddf4a9c 100644 --- a/Aurora/Design/Views/Party/PartyViewModel.cs +++ b/Aurora/Design/Views/Party/PartyViewModel.cs @@ -8,6 +8,7 @@ using Aurora.Proto.General; using Aurora.Proto.Party; using Aurora.Proto.Events; using Aurora.Services.ClientService; +using Aurora.Services.ClientService.Events; using Aurora.Services.PlayerService; using Aurora.Services.EventManager; using Aurora.Models.Media; @@ -47,9 +48,6 @@ namespace Aurora.Design.Views.Party PlayCommand = new Command(OnDoubleClickExecute, CanDoubleClickExecute); _client = ClientService.Instance; - - //Hook up event handler - _client.EventReceived += this.OnEventReceived; } ~PartyViewModel() @@ -151,70 +149,100 @@ namespace Aurora.Design.Views.Party #endregion Properties #region Events - public override void OnActive() + /// + /// Called by framework when view becomes active + /// + /// + public override async Task OnActive() { //TODO - //If in party subscribe to events - //If in party get events - } - - public override void OnInactive() - { - //TODO - //unsubscribe - //stop getting events + if (this._state == PartyState.Hosting) + { + await SubscribeToEvents(); + } + _client.OnMediaPaused += this.OnMediaPaused; + _client.OnMediaResumed += this.OnMediaResumed; + _client.OnNewMediaPlaying += this.OnNewMediaPlaying; + _client.OnPartyMemberJoined += this.OnPartyMemberJoined; + _client.OnPartyMemberLeft += this.OnPartyMemberLeft; } /// - /// An event handler for the client receiving update events + /// Called by framework when view becomes inactive /// - /// The object that sent the event - /// The event arguments - public void OnEventReceived(object sender, EventReceivedEventArgs eventArgs) + /// + public override async Task OnInactive() { - switch (eventArgs.BaseEvent.DerivedEventCase) + _client.StopEvents(); + await UnsubscribeFromEvents(); + //Stop event stream and un hook events + + _client.OnMediaPaused -= this.OnMediaPaused; + _client.OnMediaResumed -= this.OnMediaResumed; + _client.OnNewMediaPlaying -= this.OnNewMediaPlaying; + _client.OnPartyMemberJoined -= this.OnPartyMemberJoined; + _client.OnPartyMemberLeft -= this.OnPartyMemberLeft; + } + + /// + /// Remote media paused event + /// + /// + /// + public void OnMediaPaused(object sender, MediaPausedEventArgs args) + { + StopPlaying(); + } + + /// + /// Remote playing new media event + /// + /// + /// + public void OnNewMediaPlaying(object sender, NewMediaPlayingEventArgs args) + { + PlayFromBeginning(GetMediaFromQueue(args.Event.Media.Id)); + } + + /// + /// Remote resumed playing event + /// + /// + /// + public void OnMediaResumed(object sender, MediaResumedEventArgs args) + { + PlayResume(); + } + + /// + /// Member joined party event + /// + /// + /// + public void OnPartyMemberJoined(object sender, PartyMemberJoinedEventArgs args) + { + PartyMember member = new PartyMember { - case BaseEvent.DerivedEventOneofCase.None: - { - throw new InvalidOperationException(); - } - case BaseEvent.DerivedEventOneofCase.PartyMemberJoinedEvent: - { - PartyMemberJoinedEvent derivedEvent = eventArgs.BaseEvent.PartyMemberJoinedEvent; - PartyMember member = new PartyMember - { - UserName = derivedEvent.Member.UserName, - Id = derivedEvent.Member.Id, - IpAddress = derivedEvent.Member.IpAddress, - Port = derivedEvent.Member.Port - }; + UserName = args.Event.Member.UserName, + Id = args.Event.Member.Id, + IpAddress = args.Event.Member.IpAddress, + Port = args.Event.Member.Port + }; - Members.Add(member); + Members.Add(member); + } - break; - } - case BaseEvent.DerivedEventOneofCase.PartyMemberLeftEvent: - { - PartyMemberJoinedEvent derivedEvent = eventArgs.BaseEvent.PartyMemberJoinedEvent; - var found = Members.Where(x => x.Id == derivedEvent.Member.Id); - foreach (PartyMember member in found) - { - _members.Remove(member); - } - break; - } - case BaseEvent.DerivedEventOneofCase.MediaPlayingEvent: - { - MediaPlayingEvent derivedEvent = eventArgs.BaseEvent.MediaPlayingEvent; - Play(derivedEvent); - break; - } - case BaseEvent.DerivedEventOneofCase.MediaPausedEvent: - { - MediaPausedEvent derivedEvent = eventArgs.BaseEvent.MediaPausedEvent; - StopPlaying(); - break; - } + /// + /// Member left party event + /// + /// + /// + public void OnPartyMemberLeft(object sender, PartyMemberLeftEventArgs args) + { + var found = Members.Where(x => x.Id == args.Event.Member.Id); + foreach (PartyMember member in found) + { + _members.Remove(member); } } @@ -274,17 +302,32 @@ namespace Aurora.Design.Views.Party public override void OnPlayButtonExecute() { - _player.Play(); switch (_player.PlaybackState) { case PlaybackState.Buffering: { - _player.Play(); + //Fire play resume event + AudioMetadata meta = _selectedMedia.Metadata as AudioMetadata; + MediaResumedEvent mediaResumed = new MediaResumedEvent(); + + EventManager.Instance.FireEvent(new BaseEvent() + { + MediaResumedEvent = mediaResumed + }); + break; } case PlaybackState.Playing: { - _player.Pause(); + //Fire play stopped event + AudioMetadata meta = _selectedMedia.Metadata as AudioMetadata; + MediaPausedEvent mediaPaused = new MediaPausedEvent(); + + EventManager.Instance.FireEvent(new BaseEvent() + { + MediaPausedEvent = mediaPaused + }); + break; } } @@ -310,8 +353,9 @@ namespace Aurora.Design.Views.Party /// public void OnDoubleClickExecute() { + //Fire Playing event AudioMetadata meta = _selectedMedia.Metadata as AudioMetadata; - MediaPlayingEvent mediaPlaying = new MediaPlayingEvent() + NewMediaPlayingEvent mediaPlaying = new NewMediaPlayingEvent() { Media = new RemoteMediaData() { @@ -324,7 +368,7 @@ namespace Aurora.Design.Views.Party EventManager.Instance.FireEvent(new BaseEvent() { - MediaPlayingEvent = mediaPlaying + NewMediaPlayingEvent = mediaPlaying }); } @@ -355,19 +399,7 @@ namespace Aurora.Design.Views.Party RefreshMembers(); //Subscribe to events - SubscribeRequest req = new SubscribeRequest(); - req.EventTypes.Add(EventType.PartyMemberJoined); - req.EventTypes.Add(EventType.PartyMemberLeft); - req.EventTypes.Add(EventType.MediaPlaying); - req.EventTypes.Add(EventType.MediaStopped); - if (!string.IsNullOrWhiteSpace(SettingsService.Instance.ClientId)) - { - req.ClientId = SettingsService.Instance.ClientId; - } - - - Console.WriteLine(string.Format("CLIENT {0} - SubscribeToEvents called from client with id", SettingsService.Instance.ClientId)); - _client.RemoteEventClient.SubscribeToEvents(req); + await SubscribeToEvents(); QueueResponse queueResponse = _client.RemotePartyClient.GetQueue(new Empty()); @@ -403,19 +435,41 @@ namespace Aurora.Design.Views.Party _client.StopEvents(); //Unsubscribe - UnsubscribeAllRequest unsubscribeReq = new UnsubscribeAllRequest(); - await _client.RemoteEventClient.UnsubscribeFromAllAsync(unsubscribeReq); + await UnsubscribeFromEvents(); //Leave party LeavePartyRequest leaveReq = new LeavePartyRequest(); await _client.RemotePartyClient.LeavePartyAsync(leaveReq); } + private async Task SubscribeToEvents() + { + SubscribeRequest req = new SubscribeRequest(); + req.EventTypes.Add(EventType.PartyMemberJoined); + req.EventTypes.Add(EventType.PartyMemberLeft); + req.EventTypes.Add(EventType.MediaPlaying); + req.EventTypes.Add(EventType.MediaStopped); + if (!string.IsNullOrWhiteSpace(SettingsService.Instance.ClientId)) + { + req.ClientId = SettingsService.Instance.ClientId; + } + + + Console.WriteLine(string.Format("CLIENT {0} - SubscribeToEvents called from client with id", SettingsService.Instance.ClientId)); + await _client.RemoteEventClient.SubscribeToEventsAsync(req); + } + private async Task UnsubscribeFromEvents() + { + UnsubscribeAllRequest unsubscribeReq = new UnsubscribeAllRequest(); + await _client.RemoteEventClient.UnsubscribeFromAllAsync(unsubscribeReq); + } + /// /// Refresh members list. /// private void RefreshMembers() { + Members.Clear(); MembersResponse response = _client.RemotePartyClient.GetPartyMembers(new Empty()); //Add members foreach (PartyMember member in response.Members) @@ -431,17 +485,28 @@ namespace Aurora.Design.Views.Party OnPropertyChanged("IsNotSelectingHost"); } - private async void Play(MediaPlayingEvent args) + private BaseMedia GetMediaFromQueue(string Id) { - //TODO this design assumes all played music is in a queue - //TODO this is a slow design depending on size of queue - if (_queue.Any((BaseMedia media) => media.Id == args.Media.Id)) + if (_queue.Any((BaseMedia media) => media.Id == Id)) { - BaseMedia media = _queue.First((BaseMedia med) => med.Id == args.Media.Id); - base.Media = media; - await _player.LoadMedia(base.Media).ConfigureAwait(true); - _player.Play(); + BaseMedia media = _queue.First((BaseMedia med) => med.Id == Id); + return media; } + else + { + return null; + } + } + private async void PlayFromBeginning(BaseMedia args) + { + base.Media = args; + await _player.LoadMedia(base.Media).ConfigureAwait(true); + _player.Play(); + } + + private void PlayResume() + { + _player.Play(); } private void StopPlaying() diff --git a/Aurora/Design/Views/Songs/SongsViewModel.cs b/Aurora/Design/Views/Songs/SongsViewModel.cs index 3798aab..95fad12 100644 --- a/Aurora/Design/Views/Songs/SongsViewModel.cs +++ b/Aurora/Design/Views/Songs/SongsViewModel.cs @@ -93,7 +93,6 @@ namespace Aurora.Design.Views.Songs await _player.LoadMedia(base.Media).ConfigureAwait(true); } - _player.Play(); switch (_player.PlaybackState) { case PlaybackState.Buffering: @@ -106,6 +105,11 @@ namespace Aurora.Design.Views.Songs _player.Pause(); break; } + case PlaybackState.Stopped: + { + _player.Play(); + break; + } } } diff --git a/Aurora/Proto/events.proto b/Aurora/Proto/events.proto index 29c6fa7..08a0bf1 100644 --- a/Aurora/Proto/events.proto +++ b/Aurora/Proto/events.proto @@ -50,15 +50,20 @@ message BaseEvent { oneof derivedEvent { PartyMemberJoinedEvent partyMemberJoinedEvent = 3; PartyMemberLeftEvent partyMemberLeftEvent = 4; - MediaPlayingEvent mediaPlayingEvent = 5; + NewMediaPlayingEvent newMediaPlayingEvent = 5; MediaPausedEvent mediaPausedEvent = 6; + MediaResumedEvent mediaResumedEvent = 7; } } -message MediaPlayingEvent { +message NewMediaPlayingEvent { Aurora.Proto.Party.RemoteMediaData media = 1; } +message MediaResumedEvent { + Aurora.Proto.General.Empty empty = 1; +} + message MediaPausedEvent { Aurora.Proto.General.Empty empty = 1; } diff --git a/Aurora/RemoteImpl/RemoteEventImpl.cs b/Aurora/RemoteImpl/RemoteEventImpl.cs index 6350f97..78f7480 100644 --- a/Aurora/RemoteImpl/RemoteEventImpl.cs +++ b/Aurora/RemoteImpl/RemoteEventImpl.cs @@ -34,6 +34,7 @@ namespace Aurora.RemoteImpl Action callback = (BaseEvent bEvent) => { Console.WriteLine(string.Format("SERVER - Event fired for peer: {0}", peerId)); + //TODO need to remove callback if stream no longer exists IE. Client crashed or stopped responseStream.WriteAsync(bEvent); }; diff --git a/Aurora/Services/ClientService/ClientService.cs b/Aurora/Services/ClientService/ClientService.cs index f64aeaa..9c58d25 100644 --- a/Aurora/Services/ClientService/ClientService.cs +++ b/Aurora/Services/ClientService/ClientService.cs @@ -6,9 +6,12 @@ using Aurora.Proto.Events; using Aurora.Proto.Party; using Aurora.Proto.Playback; using Aurora.Proto.Sync; +using Aurora.Services.ClientService.Events; +using System.Collections.Generic; namespace Aurora.Services.ClientService { + public class ClientService : BaseService { private RemotePartyService.RemotePartyServiceClient _remotePartyClient; @@ -23,7 +26,11 @@ namespace Aurora.Services.ClientService { } - public EventReceivedEventHandler EventReceived; + public MediaPausedEventHandler OnMediaPaused; + public NewMediaPlayingEventHandler OnNewMediaPlaying; + public PartyMemberJoinedEventHandler OnPartyMemberJoined; + public PartyMemberLeftEventHandler OnPartyMemberLeft; + public MediaResumedEventHandler OnMediaResumed; public RemotePartyService.RemotePartyServiceClient RemotePartyClient { @@ -99,16 +106,36 @@ namespace Aurora.Services.ClientService while (!_eventCancellationTokenSource.Token.IsCancellationRequested && await eventStream.ResponseStream.MoveNext(_eventCancellationTokenSource.Token)) { - BaseEvent e = new BaseEvent(eventStream.ResponseStream.Current); - if (this.EventReceived != null) + try { - this.EventReceived.Invoke(this, new EventReceivedEventArgs(e)); + BaseEvent e = new BaseEvent(eventStream.ResponseStream.Current); + + Dictionary events = new Dictionary() + { + {BaseEvent.DerivedEventOneofCase.MediaPausedEvent, new EventInfo(this.OnMediaPaused, typeof(MediaPausedEventArgs))}, + {BaseEvent.DerivedEventOneofCase.MediaResumedEvent, new EventInfo(this.OnMediaResumed, typeof(MediaResumedEventArgs))}, + {BaseEvent.DerivedEventOneofCase.NewMediaPlayingEvent, new EventInfo(this.OnNewMediaPlaying, typeof(NewMediaPlayingEventArgs))}, + {BaseEvent.DerivedEventOneofCase.PartyMemberJoinedEvent, new EventInfo(this.OnPartyMemberJoined, typeof(PartyMemberJoinedEventArgs))}, + {BaseEvent.DerivedEventOneofCase.PartyMemberLeftEvent, new EventInfo(this.OnPartyMemberLeft, typeof(PartyMemberLeftEventArgs))} + }; + + events.TryGetValue(e.DerivedEventCase, out EventInfo eventInfo); + + if (eventInfo != null && eventInfo.Handler != null) + { + eventInfo.Handler.DynamicInvoke(new object[] { this, Activator.CreateInstance(eventInfo.ArgsType, new object[] { e }) }); + } } + catch (Exception ex) + { + Console.WriteLine("Exception while parsing event ---" + ex.Message); + } + } } catch (Exception ex) { - Console.WriteLine(string.Format("EXCEPTION --- " + ex.Message)); + Console.WriteLine(string.Format("EXCEPTION while parsing events --- " + ex.Message)); } } } diff --git a/Aurora/Services/ClientService/EventInfo.cs b/Aurora/Services/ClientService/EventInfo.cs new file mode 100644 index 0000000..f071e62 --- /dev/null +++ b/Aurora/Services/ClientService/EventInfo.cs @@ -0,0 +1,25 @@ +using System; +using System.Threading.Tasks; +using System.Threading; +using Grpc.Core; +using Aurora.Proto.Events; +using Aurora.Proto.Party; +using Aurora.Proto.Playback; +using Aurora.Proto.Sync; +using Aurora.Services.ClientService.Events; +using System.Collections.Generic; + +namespace Aurora.Services.ClientService +{ + public class EventInfo + { + public EventInfo(Delegate handler, Type argsType) + { + this.Handler = handler; + ArgsType = argsType; + } + + public Delegate Handler { get; private set; } + public Type ArgsType { get; private set; } + } +} \ No newline at end of file diff --git a/Aurora/Services/ClientService/EventReceivedEvent.cs b/Aurora/Services/ClientService/EventReceivedEvent.cs deleted file mode 100644 index 18aeb65..0000000 --- a/Aurora/Services/ClientService/EventReceivedEvent.cs +++ /dev/null @@ -1,16 +0,0 @@ -using System; -using Aurora.Proto.Events; - -namespace Aurora.Services.ClientService -{ - public delegate void EventReceivedEventHandler(object sender, EventReceivedEventArgs e); - - public class EventReceivedEventArgs - { - public BaseEvent BaseEvent { get; private set; } - public EventReceivedEventArgs(BaseEvent e) - { - BaseEvent = e; - } - } -} \ No newline at end of file diff --git a/Aurora/Services/ClientService/Events/MediaPausedEvent.cs b/Aurora/Services/ClientService/Events/MediaPausedEvent.cs new file mode 100644 index 0000000..9401103 --- /dev/null +++ b/Aurora/Services/ClientService/Events/MediaPausedEvent.cs @@ -0,0 +1,16 @@ +using System; +using Aurora.Proto.Events; + +namespace Aurora.Services.ClientService.Events +{ + public delegate void MediaPausedEventHandler(object sender, MediaPausedEventArgs e); + + public class MediaPausedEventArgs + { + public MediaPausedEvent Event { get; private set; } + public MediaPausedEventArgs(BaseEvent e) + { + Event = e.MediaPausedEvent; + } + } +} \ No newline at end of file diff --git a/Aurora/Services/ClientService/Events/MediaResumedEvent.cs b/Aurora/Services/ClientService/Events/MediaResumedEvent.cs new file mode 100644 index 0000000..5dbb87a --- /dev/null +++ b/Aurora/Services/ClientService/Events/MediaResumedEvent.cs @@ -0,0 +1,16 @@ +using System; +using Aurora.Proto.Events; + +namespace Aurora.Services.ClientService +{ + public delegate void MediaResumedEventHandler(object sender, MediaResumedEventArgs e); + + public class MediaResumedEventArgs + { + public MediaResumedEvent Event { get; private set; } + public MediaResumedEventArgs(BaseEvent e) + { + Event = e.MediaResumedEvent; + } + } +} \ No newline at end of file diff --git a/Aurora/Services/ClientService/Events/NewMediaPlayingEvent.cs b/Aurora/Services/ClientService/Events/NewMediaPlayingEvent.cs new file mode 100644 index 0000000..b47d669 --- /dev/null +++ b/Aurora/Services/ClientService/Events/NewMediaPlayingEvent.cs @@ -0,0 +1,16 @@ +using System; +using Aurora.Proto.Events; + +namespace Aurora.Services.ClientService.Events +{ + public delegate void NewMediaPlayingEventHandler(object sender, NewMediaPlayingEventArgs e); + + public class NewMediaPlayingEventArgs + { + public NewMediaPlayingEvent Event { get; private set; } + public NewMediaPlayingEventArgs(BaseEvent e) + { + Event = e.NewMediaPlayingEvent; + } + } +} \ No newline at end of file diff --git a/Aurora/Services/ClientService/Events/PartyMemberJoinedEvent.cs b/Aurora/Services/ClientService/Events/PartyMemberJoinedEvent.cs new file mode 100644 index 0000000..64eb86b --- /dev/null +++ b/Aurora/Services/ClientService/Events/PartyMemberJoinedEvent.cs @@ -0,0 +1,16 @@ +using System; +using Aurora.Proto.Events; + +namespace Aurora.Services.ClientService.Events +{ + public delegate void PartyMemberJoinedEventHandler(object sender, PartyMemberJoinedEventArgs e); + + public class PartyMemberJoinedEventArgs + { + public PartyMemberJoinedEvent Event { get; private set; } + public PartyMemberJoinedEventArgs(BaseEvent e) + { + Event = e.PartyMemberJoinedEvent; + } + } +} \ No newline at end of file diff --git a/Aurora/Services/ClientService/Events/PartyMemberLeftEvent.cs b/Aurora/Services/ClientService/Events/PartyMemberLeftEvent.cs new file mode 100644 index 0000000..18eb179 --- /dev/null +++ b/Aurora/Services/ClientService/Events/PartyMemberLeftEvent.cs @@ -0,0 +1,16 @@ +using System; +using Aurora.Proto.Events; + +namespace Aurora.Services.ClientService.Events +{ + public delegate void PartyMemberLeftEventHandler(object sender, PartyMemberLeftEventArgs e); + + public class PartyMemberLeftEventArgs + { + public PartyMemberLeftEvent Event { get; private set; } + public PartyMemberLeftEventArgs(BaseEvent e) + { + Event = e.PartyMemberLeftEvent; + } + } +} \ No newline at end of file diff --git a/Aurora/Services/EventManager/EventManager.cs b/Aurora/Services/EventManager/EventManager.cs index 5207817..ffdb1b3 100644 --- a/Aurora/Services/EventManager/EventManager.cs +++ b/Aurora/Services/EventManager/EventManager.cs @@ -1,16 +1,9 @@ using System; -using System.IO; -using System.Collections.Concurrent; using System.Collections.Generic; -using System.Collections.ObjectModel; -using System.Collections.Specialized; using System.Linq; using System.Threading; using System.Threading.Tasks; -using Google.Protobuf.WellKnownTypes; -using Google.Protobuf.Reflection; using Aurora.Proto.Events; -using Aurora.Models; namespace Aurora.Services.EventManager { @@ -183,8 +176,6 @@ namespace Aurora.Services.EventManager { foreach (KeyValuePair> pair in _subscriptionList) { - Console.WriteLine("SERVER - Invoking action for client: " + pair.Key); - Task.Delay(1000); //If action list contains an action for id, invoke if (actionsCopy.ContainsKey(pair.Key)) diff --git a/Aurora/Services/PlayerService/PlayerService.cs b/Aurora/Services/PlayerService/PlayerService.cs index baf6d7a..b82e5a3 100644 --- a/Aurora/Services/PlayerService/PlayerService.cs +++ b/Aurora/Services/PlayerService/PlayerService.cs @@ -4,6 +4,8 @@ using System.Threading; using Grpc.Core; using Aurora.Models.Media; using Aurora.Proto.Sync; +using Aurora.Proto.Events; +using Aurora.Proto.Party; using LibVLCSharp.Shared; namespace Aurora.Services.PlayerService @@ -110,14 +112,14 @@ namespace Aurora.Services.PlayerService RemoteAudio media = _currentMedia as RemoteAudio; if (!media.FromHost) { - RemoteSyncService.RemoteSyncServiceClient _remoteSyncClient = media.RemoteSyncClient; + RemoteSyncService.RemoteSyncServiceClient remoteSyncClient = media.RemoteSyncClient; //Sync playback in a separate task //Task completes when host stops syncing (when a song is complete) Task syncTask = new Task(async () => { CancellationTokenSource cancellationTokenSource = new CancellationTokenSource(); - using (AsyncServerStreamingCall syncStream = _remoteSyncClient + using (AsyncServerStreamingCall syncStream = remoteSyncClient .GetMediaSync(new Proto.General.Empty())) { try @@ -154,6 +156,7 @@ namespace Aurora.Services.PlayerService }); syncTask.Start(); + // Task syncTask = Task.Run(() => Sync(remoteSyncClient)); } } @@ -163,6 +166,50 @@ namespace Aurora.Services.PlayerService } } + /// + /// Async method to synchronize music playback with host + /// + /// + /// + private async Task Sync(RemoteSyncService.RemoteSyncServiceClient remoteSyncClient) + { + CancellationTokenSource cancellationTokenSource = new CancellationTokenSource(); + using (AsyncServerStreamingCall syncStream = remoteSyncClient + .GetMediaSync(new Proto.General.Empty())) + { + try + { + while (await syncStream.ResponseStream.MoveNext(cancellationTokenSource.Token)) + { + Sync sync = new Sync(syncStream.ResponseStream.Current); + if (sync != null) + { + //Adjust position based on sync + DateTime localTime = Utils.TimeUtils.GetNetworkTime(); + //Get offset converted to milliseconds + float offset = ((localTime.Ticks - sync.ServerTime) * 100) / (1000 * 1000); + + float length = CurrentMediaLength; + float newPosition = (sync.TrackTime + offset) / length; + + //Adjust position if greater than 10 percent difference + float oldPosition = _mediaPlayer.Position; + if (newPosition - oldPosition > 0.001 || + newPosition - oldPosition < -0.001) + { + _mediaPlayer.Position = newPosition; + Console.WriteLine("Audio synced"); + } + } + } + } + catch (Exception ex) + { + Console.WriteLine("Exception caught while attempting to sync: " + ex.Message); + } + } + } + /// /// Pause currently loaded media. /// @@ -191,7 +238,6 @@ namespace Aurora.Services.PlayerService { PlaybackStateChanged.Invoke(this, new PlaybackStateChangedEventArgs(oldState, _state)); } - } public void Enqueue(BaseMedia song)