Trying out naudio. Doesn’t support cross platform
This commit is contained in:
		
							
								
								
									
										230
									
								
								Aurora/Backend/Services/PlayerService/PlayerService.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										230
									
								
								Aurora/Backend/Services/PlayerService/PlayerService.cs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,230 @@ | ||||
| using System; | ||||
| using System.Diagnostics; | ||||
| using System.IO; | ||||
| using System.Timers; | ||||
| using System.Threading; | ||||
| using Aurora.Backend.Models; | ||||
| using Aurora.Backend.Utils; | ||||
| using NAudio.Wave; | ||||
|  | ||||
| namespace Aurora.Backend.Services.PlayerService | ||||
| { | ||||
|     public class PlayerService : BaseService<PlayerService> | ||||
|     { | ||||
|         #region Fields | ||||
|         private BufferedWaveProvider _bufferedWaveProvider; | ||||
|         private IWavePlayer _waveOut; | ||||
|         private volatile StreamingPlaybackState _playbackState; | ||||
|         private bool _fullyDownloaded = false; | ||||
|         private System.Timers.Timer _monitorTimer; | ||||
|         private VolumeWaveProvider16 _volumeProvider; | ||||
|  | ||||
|         private bool IsBufferNearlyFull | ||||
|         { | ||||
|             get | ||||
|             { | ||||
|                 return _bufferedWaveProvider != null && | ||||
|                        _bufferedWaveProvider.BufferLength - _bufferedWaveProvider.BufferedBytes | ||||
|                        < _bufferedWaveProvider.WaveFormat.AverageBytesPerSecond / 4; | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         #endregion Fields | ||||
|  | ||||
|         #region Constructor | ||||
|         public PlayerService() | ||||
|         { | ||||
|             _monitorTimer = new System.Timers.Timer(250); | ||||
|             _monitorTimer.Elapsed += OnMonitorTimerTick; | ||||
|         } | ||||
|  | ||||
|         ~PlayerService() | ||||
|         { | ||||
|             _monitorTimer.Elapsed -= OnMonitorTimerTick; | ||||
|         } | ||||
|  | ||||
|         #endregion Constructor | ||||
|  | ||||
|         #region Public Methods | ||||
|         public void Play(BaseSong song) | ||||
|         { | ||||
|             if (_playbackState == StreamingPlaybackState.Stopped) | ||||
|             { | ||||
|                 _playbackState = StreamingPlaybackState.Buffering; | ||||
|                 _bufferedWaveProvider = null; | ||||
|                 ThreadPool.QueueUserWorkItem(Stream, song); | ||||
|                 _monitorTimer.Enabled = true; | ||||
|             } | ||||
|             else if (_playbackState == StreamingPlaybackState.Paused) | ||||
|             { | ||||
|                 _playbackState = StreamingPlaybackState.Buffering; | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         public void Pause() | ||||
|         { | ||||
|             Pause(); | ||||
|         } | ||||
|  | ||||
|         #endregion Public Methods | ||||
|  | ||||
|         #region Private Methods | ||||
|         private void Stream(object state) | ||||
|         { | ||||
|             BaseSong song = state as BaseSong; | ||||
|             //Load song into stream | ||||
|             song.Load(); | ||||
|  | ||||
|  | ||||
|             //Buffer big enough to hold decompressed song | ||||
|             var buffer = new byte[16384 * 4]; | ||||
|  | ||||
|             IMp3FrameDecompressor decompressor = null; | ||||
|  | ||||
|             try | ||||
|             { | ||||
|                 ReadFullyStream rfs = new ReadFullyStream(song.DataStream); | ||||
|                 do | ||||
|                 { | ||||
|                     if (IsBufferNearlyFull) | ||||
|                     { | ||||
|                         Debug.WriteLine("Buffer getting full, taking a break"); | ||||
|                         Thread.Sleep(500); | ||||
|                     } | ||||
|                     else | ||||
|                     { | ||||
|                         Mp3Frame frame; | ||||
|                         try | ||||
|                         { | ||||
|                             frame = Mp3Frame.LoadFromStream(rfs); | ||||
|                         } | ||||
|                         catch (EndOfStreamException) | ||||
|                         { | ||||
|                             _fullyDownloaded = true; | ||||
|                             break; | ||||
|                         } | ||||
|  | ||||
|                         if (frame == null) | ||||
|                         { | ||||
|                             break; | ||||
|                         } | ||||
|                         if (decompressor == null) | ||||
|                         { | ||||
|                             decompressor = CreateFrameDecompressor(frame); | ||||
|                             _bufferedWaveProvider = new BufferedWaveProvider(decompressor.OutputFormat); | ||||
|                             _bufferedWaveProvider.BufferDuration = TimeSpan.FromSeconds(20); | ||||
|  | ||||
|                         } | ||||
|  | ||||
|                         int decompressed = decompressor.DecompressFrame(frame, buffer, 0); | ||||
|                         _bufferedWaveProvider.AddSamples(buffer, 0, decompressed); | ||||
|                     } | ||||
|                 } | ||||
|                 while (_playbackState != StreamingPlaybackState.Stopped); | ||||
|  | ||||
|                 Debug.WriteLine("Exiting"); | ||||
|                 decompressor.Dispose(); | ||||
|             } | ||||
|             finally | ||||
|             { | ||||
|                 if (decompressor != null) | ||||
|                 { | ||||
|                     decompressor.Dispose(); | ||||
|                 } | ||||
|  | ||||
|                 song.Unload(); | ||||
|             } | ||||
|         } | ||||
|  | ||||
|  | ||||
|  | ||||
|         private void PlayAudio() | ||||
|         { | ||||
|             _waveOut.Play(); | ||||
|             Debug.WriteLine(String.Format("Started playing, waveOut.PlaybackState={0}", _waveOut.PlaybackState)); | ||||
|             _playbackState = StreamingPlaybackState.Playing; | ||||
|         } | ||||
|  | ||||
|         private void PauseAudio() | ||||
|         { | ||||
|             _playbackState = StreamingPlaybackState.Buffering; | ||||
|             _waveOut.Pause(); | ||||
|             Debug.WriteLine(String.Format("Paused to buffer, waveOut.PlaybackState={0}", _waveOut.PlaybackState)); | ||||
|         } | ||||
|  | ||||
|         private void StopAudio() | ||||
|         { | ||||
|             if (_playbackState != StreamingPlaybackState.Stopped) | ||||
|             { | ||||
|                 if (!_fullyDownloaded) | ||||
|                 { | ||||
|                     //End song loading | ||||
|                 } | ||||
|  | ||||
|                 _playbackState = StreamingPlaybackState.Stopped; | ||||
|                 if (_waveOut != null) | ||||
|                 { | ||||
|                     _waveOut.Stop(); | ||||
|                     _waveOut.Dispose(); | ||||
|                     _waveOut = null; | ||||
|                 } | ||||
|                 _monitorTimer.Enabled = false; | ||||
|                 // n.b. streaming thread may not yet have exited | ||||
|                 Thread.Sleep(500); | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         private void OnPlaybackStopped(object sender, StoppedEventArgs e) | ||||
|         { | ||||
|             Debug.WriteLine("Playback Stopped"); | ||||
|             if (e.Exception != null) | ||||
|             { | ||||
|                 //TODO log exception | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         private static IMp3FrameDecompressor CreateFrameDecompressor(Mp3Frame frame) | ||||
|         { | ||||
|             WaveFormat waveFormat = new Mp3WaveFormat(frame.SampleRate, frame.ChannelMode == ChannelMode.Mono ? 1 : 2, | ||||
|                 frame.FrameLength, frame.BitRate); | ||||
|  | ||||
|             return new AcmMp3FrameDecompressor(waveFormat); | ||||
|         } | ||||
|  | ||||
|         private void OnMonitorTimerTick(object sender, EventArgs e) | ||||
|         { | ||||
|             if (_playbackState != StreamingPlaybackState.Stopped) | ||||
|             { | ||||
|                 //Data available but audio has not been initialized | ||||
|                 if (_waveOut == null && _bufferedWaveProvider != null) | ||||
|                 { | ||||
|                     _waveOut = new WaveOutEvent(); | ||||
|                     _waveOut.PlaybackStopped += OnPlaybackStopped; | ||||
|                     _volumeProvider = new VolumeWaveProvider16(_bufferedWaveProvider); | ||||
|                     _waveOut.Init(_volumeProvider); | ||||
|  | ||||
|                 } | ||||
|                 else | ||||
|                 { | ||||
|                     var bufferedSeconds = _bufferedWaveProvider.BufferedDuration.TotalSeconds; | ||||
|                     // make it stutter less if we buffer up a decent amount before playing | ||||
|                     if (bufferedSeconds < 0.5 && _playbackState == StreamingPlaybackState.Playing && !_fullyDownloaded) | ||||
|                     { | ||||
|                         PauseAudio(); | ||||
|                     } | ||||
|                     else if (bufferedSeconds > 4 && _playbackState == StreamingPlaybackState.Buffering) | ||||
|                     { | ||||
|                         PlayAudio(); | ||||
|                     } | ||||
|                     else if (_fullyDownloaded && bufferedSeconds == 0) | ||||
|                     { | ||||
|                         Debug.WriteLine("Reached end of stream"); | ||||
|                         StopAudio(); | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         #endregion Private Methods | ||||
|     } | ||||
| } | ||||
| @@ -0,0 +1,11 @@ | ||||
| using System; | ||||
| namespace Aurora.Backend.Services.PlayerService | ||||
| { | ||||
|     public enum StreamingPlaybackState | ||||
|     { | ||||
|         Stopped, | ||||
|         Playing, | ||||
|         Buffering, | ||||
|         Paused | ||||
|     } | ||||
| } | ||||
		Reference in New Issue
	
	Block a user