diff --git a/ClassicalSharp/Audio/AudioPlayer.Sounds.cs b/ClassicalSharp/Audio/AudioPlayer.Sounds.cs index 5e1e22571..521e9ee26 100644 --- a/ClassicalSharp/Audio/AudioPlayer.Sounds.cs +++ b/ClassicalSharp/Audio/AudioPlayer.Sounds.cs @@ -19,18 +19,16 @@ namespace ClassicalSharp.Audio { } void InitSound() { - if( digBoard == null ) - InitSoundboards(); - + if( digBoard == null ) InitSoundboards(); monoOutputs = new IAudioOutput[maxSounds]; stereoOutputs = new IAudioOutput[maxSounds]; } void InitSoundboards() { digBoard = new Soundboard(); - digBoard.Init( "dig" ); + digBoard.Init( "dig_", files ); stepBoard = new Soundboard(); - stepBoard.Init( "step" ); + stepBoard.Init( "step_", files ); } public void Tick( double delta ) { @@ -45,13 +43,12 @@ namespace ClassicalSharp.Audio { if( type == SoundType.None || monoOutputs == null ) return; Sound snd = board.PickRandomSound( type ); - snd.Metadata = board == digBoard ? (byte)1 : (byte)2; chunk.Channels = snd.Channels; chunk.Frequency = snd.SampleRate; chunk.BitsPerSample = snd.BitsPerSample; - chunk.BytesOffset = snd.Offset; - chunk.BytesUsed = snd.Length; - chunk.Data = board.Data; + chunk.BytesOffset = 0; + chunk.BytesUsed = snd.Data.Length; + chunk.Data = snd.Data; if( snd.Channels == 1 ) PlayCurrentSound( monoOutputs ); diff --git a/ClassicalSharp/Audio/AudioPlayer.cs b/ClassicalSharp/Audio/AudioPlayer.cs index 30deb611f..4f9d61d94 100644 --- a/ClassicalSharp/Audio/AudioPlayer.cs +++ b/ClassicalSharp/Audio/AudioPlayer.cs @@ -11,12 +11,15 @@ namespace ClassicalSharp.Audio { IAudioOutput musicOut; IAudioOutput[] monoOutputs, stereoOutputs; - string[] musicFiles; + string[] files, musicFiles; Thread musicThread; Game game; public AudioPlayer( Game game ) { this.game = game; + string path = Path.Combine( Program.AppDirectory, "audio" ); + files = Directory.GetFiles( path ); + game.UseMusic = Options.GetBool( OptionsKey.UseMusic, false ); SetMusic( game.UseMusic ); game.UseSound = Options.GetBool( OptionsKey.UseSound, false ); @@ -30,9 +33,19 @@ namespace ClassicalSharp.Audio { DisposeMusic(); } + const StringComparison comp = StringComparison.OrdinalIgnoreCase; void InitMusic() { - string path = Path.Combine( Program.AppDirectory, "audio" ); - musicFiles = Directory.GetFiles( path, "*.ogg" ); + 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" ); diff --git a/ClassicalSharp/Audio/Soundboard.cs b/ClassicalSharp/Audio/Soundboard.cs index c0760aea6..2e3f052a7 100644 --- a/ClassicalSharp/Audio/Soundboard.cs +++ b/ClassicalSharp/Audio/Soundboard.cs @@ -8,89 +8,89 @@ namespace ClassicalSharp.Audio { public class Soundboard { public byte[] Data; - internal List rawSounds = new List(); - Dictionary groupFlags = new Dictionary(); + Dictionary> allSounds = new Dictionary>(); Random rnd = new Random(); - static string[] soundNames; + static string[] soundNames; static Soundboard() { soundNames = Enum.GetNames( typeof( SoundType ) ); for( int i = 0; i < soundNames.Length; i++ ) soundNames[i] = soundNames[i].ToLower(); } - public void Init( string group ) { - string basePath = Path.Combine( Program.AppDirectory, "audio" ); - string binPath = Path.Combine( basePath, group + ".bin" ); - string txtPath = Path.Combine( basePath, group + ".txt" ); - - Data = File.ReadAllBytes( binPath ); - ReadMetadata( txtPath ); - rawSounds.Sort( (a, b) => a.Name.CompareTo( b.Name ) ); - GetGroups(); + const StringComparison comp = StringComparison.OrdinalIgnoreCase; + public void Init( string group, string[] files ) { + for( int i = 0; i < files.Length; i++ ) { + string name = Path.GetFileNameWithoutExtension( files[i] ); + if( !name.StartsWith( group, comp )) continue; + + // Convert dig_grass1.wav to grass + name = name.Substring( group.Length ).ToLower(); + name = name.Substring( 0, name.Length - 1 ); + + List sounds = null; + if( !allSounds.TryGetValue( name, out sounds ) ) { + sounds = new List(); + allSounds[name] = sounds; + } + + try { + Sound snd = ReadWave( files[i] ); + sounds.Add( snd ); + } catch ( Exception ex ) { + ErrorHandler.LogError( "Soundboard.ReadWave()", ex ); + } + } + } + + Sound ReadWave( string file ) { + using( FileStream fs = File.OpenRead( file ) ) + using( BinaryReader r = new BinaryReader( fs ) ) + { + CheckFourCC( r, "RIFF" ); + r.ReadInt32(); // file size, but we don't care + CheckFourCC( r, "WAVE" ); + Sound snd = new Sound(); + + CheckFourCC( r, "fmt " ); + int size = r.ReadInt32(); + if( r.ReadUInt16() != 1 ) + throw new InvalidDataException( "Only PCM audio is supported." ); + size -= 2; + + snd.Channels = r.ReadUInt16(); size -= 2; + snd.SampleRate = r.ReadInt32(); size -= 4; + r.ReadInt32(); r.ReadUInt16(); size -= 6; + snd.BitsPerSample = r.ReadUInt16(); size -= 2; + if( size > 0 ) + fs.Seek( size, SeekOrigin.Current ); + + CheckFourCC( r, "data" ); + size = r.ReadInt32(); + byte[] data = r.ReadBytes( size ); + snd.Data = data; + return snd; + } + } + + void CheckFourCC( BinaryReader r, string fourCC ) { + if( r.ReadByte() != (byte)fourCC[0] || r.ReadByte() != (byte)fourCC[1] + || r.ReadByte() != (byte)fourCC[2] || r.ReadByte() != (byte)fourCC[3] ) + throw new InvalidDataException( "Expected " + fourCC + " fourcc" ); } public Sound PickRandomSound( SoundType type ) { - if( type == SoundType.None ) - return null; + if( type == SoundType.None ) return null; string name = soundNames[(int)type]; - int flags = groupFlags[name]; - int offset = flags & 0xFFF, count = flags >> 12; - return rawSounds[offset + rnd.Next( count )]; - } - - void ReadMetadata( string path ) { - using( StreamReader reader = new StreamReader( path ) ) { - string line; - while( (line = reader.ReadLine()) != null ) { - if( line.Length == 0 || line[0] == '#' ) continue; - string[] parts = line.Split( ',' ); - if( parts.Length < 6 ) continue; - string name = parts[0].ToLower(); - int sampleRate, bitsPerSample, channels; - int offset, length; - - if( !Int32.TryParse( parts[1], out sampleRate ) || - !Int32.TryParse( parts[2], out bitsPerSample ) || - !Int32.TryParse( parts[3], out channels ) || - !Int32.TryParse( parts[4], out offset ) || - !Int32.TryParse( parts[5], out length ) ) - continue; - Sound s = new Sound(); - s.Name = name; s.SampleRate = sampleRate; - s.BitsPerSample = bitsPerSample; s.Channels = channels; - s.Offset = offset; s.Length = length; - rawSounds.Add( s ); - } - } - } - - void GetGroups() { - string last = Group( rawSounds[0].Name ); - int offset = 0, count = 0; - for( int i = 0; i < rawSounds.Count; i++ ) { - string group = Group( rawSounds[i].Name ); - if( group != last ) { - groupFlags[last] = (count << 12) | offset; - offset = i; - last = group; - count = 0; - } - count++; - } - groupFlags[last] = (count << 12) | offset; - } - - string Group( string name ) { - return name.Substring( 0, name.Length - 1 ); - } + List sounds; + if( !allSounds.TryGetValue( name, out sounds ) ) return null; + return sounds[rnd.Next( sounds.Count )]; + } } public class Sound { - public string Name; public int SampleRate, BitsPerSample, Channels; - public int Offset, Length; - public byte Metadata; + public byte[] Data; } }