// ClassicalSharp copyright 2014-2016 UnknownShadow200 | Licensed under MIT using System; using System.IO; using System.Threading; using SharpWave; using SharpWave.Codecs.Vorbis; namespace ClassicalSharp.Audio { public sealed partial class AudioPlayer : IGameComponent { IAudioOutput musicOut; IAudioOutput[] monoOutputs, stereoOutputs; string[] files, musicFiles; Thread musicThread; Game game; public void Init( Game game ) { this.game = game; string path = Path.Combine( Program.AppDirectory, "audio" ); if( Directory.Exists( path ) ) files = Directory.GetFiles( path ); else files = new string[0]; game.UseMusic = Options.GetBool( OptionsKey.UseMusic, false ); SetMusic( game.UseMusic ); game.UseSound = Options.GetBool( OptionsKey.UseSound, false ); SetSound( game.UseSound ); game.UserEvents.BlockChanged += PlayBlockSound; } public void Ready( Game game ) { } public void Reset( Game game ) { } public void OnNewMap( Game game ) { } public void OnNewMapLoaded( Game game ) { } public void SetMusic( bool enabled ) { if( enabled ) InitMusic(); else DisposeMusic(); } const StringComparison comp = StringComparison.OrdinalIgnoreCase; void InitMusic() { int musicCount = 0; for( int i = 0; i < files.Length; i++ ) { if( files[i].EndsWith( ".ogg", comp ) ) musicCount++; } musicFiles = new string[musicCount]; for( int i = 0, j = 0; i < files.Length; i++ ) { if( !files[i].EndsWith( ".ogg", comp ) ) continue; musicFiles[j] = files[i]; j++; } disposingMusic = false; musicThread = MakeThread( DoMusicThread, ref musicOut, "ClassicalSharp.DoMusic" ); } EventWaitHandle musicHandle = new EventWaitHandle( false, EventResetMode.AutoReset ); void DoMusicThread() { if( musicFiles.Length == 0 ) return; Random rnd = new Random(); while( !disposingMusic ) { string file = musicFiles[rnd.Next( 0, musicFiles.Length )]; string path = Path.Combine( Program.AppDirectory, file ); Utils.LogDebug( "playing music file: " + file ); using( FileStream fs = File.OpenRead( path ) ) { OggContainer container = new OggContainer( fs ); try { musicOut.PlayStreaming( container ); } catch( InvalidOperationException ex) { HandleMusicError( ex ); return; } catch( Exception ex ) { ErrorHandler.LogError( "AudioPlayer.DoMusicThread()", ex ); game.Chat.Add( "&cError while trying to play music file " + Path.GetFileName( file ) ); } } if( disposingMusic ) break; int delay = 2000 * 60 + rnd.Next( 0, 5000 * 60 ); musicHandle.WaitOne( delay, false ); } } void HandleMusicError( InvalidOperationException ex ) { ErrorHandler.LogError( "AudioPlayer.DoMusicThread()", ex ); if( ex.Message == "No audio devices found" ) game.Chat.Add( "&cNo audio devices found, disabling music." ); else game.Chat.Add( "&cAn error occured when trying to play music, disabling music." ); SetMusic( false ); game.UseMusic = false; } bool disposingMusic; public void Dispose() { DisposeMusic(); DisposeSound(); musicHandle.Close(); game.UserEvents.BlockChanged -= PlayBlockSound; } void DisposeMusic() { disposingMusic = true; musicHandle.Set(); DisposeOf( ref musicOut, ref musicThread ); } Thread MakeThread( ThreadStart func, ref IAudioOutput output, string name ) { output = GetPlatformOut(); output.Create( 10 ); Thread thread = new Thread( func ); thread.Name = name; thread.IsBackground = true; thread.Start(); return thread; } IAudioOutput GetPlatformOut() { if( OpenTK.Configuration.RunningOnWindows ) return new WinMmOut(); return new OpenALOut(); } void DisposeOf( ref IAudioOutput output, ref Thread thread ) { if( output == null ) return; output.Stop(); thread.Join(); output.Dispose(); output = null; thread = null; } } }