/// <summary> /// Performs various sanity checks on the given request object and repairs it as well as possible. /// Returns null on any error that cannot be recovered from. /// Examples:<list type="bullet"> /// <item> /// No item <see cref="SoundPlaybackRequest.Sounds"/> may be null => returns null /// </item> /// <item> /// <see cref="SoundPlayback.Options"/> is null => Set to <see cref="PlaybackOptions.Default"/> instead /// </item> /// </list> /// </summary> /// <param name="request"></param> /// <returns></returns> protected SoundPlaybackRequest SanityCheck(SoundPlaybackRequest request) { if (request == null) { return(null); } if (request.Sounds == null || request.Sounds.Count == 0) { return(null); } for (int i = 0; i < request.Sounds.Count; ++i) { var soundPlayback = request.Sounds[i]; if (soundPlayback == null) { return(null); } soundPlayback.Sound = GetCleanFile(soundPlayback.Sound); if (soundPlayback.Sound == null) { return(null); } if (!soundPlayback.Options.SanityCheck()) { return(null); } } return(request); }
public void Play(SoundboxContext context, SoundPlaybackRequest sounds) { if (sounds.Sounds.Count == 0) { PlaybackChanged?.Invoke(this, new ISoundChainPlaybackService.PlaybackEventArgs( soundsPlaying: new List <SoundPlayback>(), finished: true, fromStop: false, fromStopGlobal: false )); return; } Play(context, sounds, 0); }
/// <summary> /// Plays the given request's sound(s) /// </summary> /// <param name="request"></param> /// <returns></returns> public async Task Play(Users.User user, SoundPlaybackRequest request) { request = SanityCheck(request); if (request == null) { return; } var player = this.ServiceProvider.GetService(typeof(ISoundChainPlaybackService)) as ISoundChainPlaybackService; player.PlaybackChanged += (sender, args) => { if (args.FromStopGlobal) { return; } //update the sounds the player is currently playing lock (PlaybackLock) { PlaybackContext playerContext; if (!PlayersPlaying.TryGetValue(player, out playerContext)) { //race condition, finished already? return; } if (args.Finished) { PlayersPlaying.Remove(player); } else { playerContext.PlayingNow = args.SoundsPlaying; } FirePlaybackChanged(); } }; var context = new PlaybackContext(user, request, player); lock (PlaybackLock) { PlayersPlaying[player] = context; player.Play(this, request); } }
/// <summary> /// Plays the sound at the given index and recursively calls <see cref="Play(SoundboxContext, SoundPlaybackRequest, int)"/> again /// to play the next sound as well as required. /// </summary> /// <param name="context"></param> /// <param name="sounds"></param> /// <param name="index"></param> protected void Play(SoundboxContext context, SoundPlaybackRequest sounds, int index) { if (Finished) { //aborted return; } var sound = sounds.Sounds[index]; //whether to play the next sound when we're done here (i.e. this is not the last track of the chain) bool continueNextSound = index < (sounds.Sounds.Count - 1); //whether to use SoundService's callback to start the next sound (more accurate) or to use start a timer on our own to play the next sound in the chain bool continueInCallback = continueNextSound && (sound.Options.ChainDelayMs == 0 || !sound.Sound.MetaData.HasLength); var player = GetSoundService(); player.PlaybackFinished += (sender, args) => { if (args.FromStop) { //nothing to do. handled in Stop() already return; } bool finished = false; ICollection <SoundPlayback> soundsPlaying; lock (this) { SoundsPlaying.Remove(sound); PlayersPlaying.Remove(player); if (++SoundsFinished == sounds.Sounds.Count) { //this was the last sound. we're all done now finished = true; } if (!this.Finished) { this.Finished = finished; if (!continueInCallback) { //don't fire the STOPPED event if we would send the START event right away anyway soundsPlaying = new List <SoundPlayback>(this.SoundsPlaying); //update our listeners PlaybackChanged?.Invoke(this, new ISoundChainPlaybackService.PlaybackEventArgs( soundsPlaying: soundsPlaying, finished: finished, fromStop: false, fromStopGlobal: false )); } } } if (continueInCallback) { //TODO use a timer here for ChainDelayMs > 0 //next sound Play(context, sounds, index + 1); } }; lock (this) { //TODO async player.Play(context, sound); this.SoundsPlaying.Add(sound); this.PlayersPlaying.Add(player); //update our listeners ICollection <SoundPlayback> soundsPlaying = new List <SoundPlayback>(this.SoundsPlaying); PlaybackChanged?.Invoke(this, new ISoundChainPlaybackService.PlaybackEventArgs( soundsPlaying: soundsPlaying, finished: false, fromStop: false, fromStopGlobal: false )); } if (continueNextSound && !continueInCallback) { //start a timer to trigger the next sound. var timer = new System.Timers.Timer(Math.Max(0, sound.GetActualLength() + sound.Options.ChainDelayMs)); timer.AutoReset = false; timer.Elapsed += (tSender, tArgs) => { //cleanup timer.Stop(); timer.Dispose(); //clip the current sound if required if (sound.Options.ChainDelayMs < 0 && sound.Options.ChainDelayClip) { player.Stop(); } //start the next sound Play(context, sounds, index + 1); }; timer.Start(); } }
public PlaybackContext(User user, SoundPlaybackRequest request, ISoundChainPlaybackService player) { User = user; Request = request; Player = player; }
/// <summary> /// Plays a single or multiple sounds. /// </summary> /// <param name="request"></param> /// <returns></returns> public Task Play(SoundPlaybackRequest request) { return(GetSoundbox().Play(GetUser(), request)); }