diff --git a/OpenRA.Game/Game.cs b/OpenRA.Game/Game.cs index 9d17c53d1a..61b998c275 100644 --- a/OpenRA.Game/Game.cs +++ b/OpenRA.Game/Game.cs @@ -293,10 +293,11 @@ namespace OpenRA Settings.Game.Mod = mod; Sound.StopVideo(); - Sound.Initialize(); ModData = new ModData(mod, !Settings.Server.Dedicated); + Sound.Initialize(); + using (new PerfTimer("LoadMaps")) ModData.MapCache.LoadMaps(); diff --git a/OpenRA.Game/Graphics/SpriteLoader.cs b/OpenRA.Game/Graphics/SpriteLoader.cs index 57abfdc7de..beffa6501d 100644 --- a/OpenRA.Game/Graphics/SpriteLoader.cs +++ b/OpenRA.Game/Graphics/SpriteLoader.cs @@ -72,7 +72,7 @@ namespace OpenRA.Graphics if (loader.TryParseSprite(stream, out frames)) return frames; - throw new InvalidDataException(filename + " is not a valid sprite file"); + throw new InvalidDataException(filename + " is not a valid sprite file!"); } } } diff --git a/OpenRA.Game/Manifest.cs b/OpenRA.Game/Manifest.cs index 5cf464f4bb..ee962e4e82 100644 --- a/OpenRA.Game/Manifest.cs +++ b/OpenRA.Game/Manifest.cs @@ -49,12 +49,14 @@ namespace OpenRA public readonly Dictionary RequiresMods; public readonly Dictionary> Fonts; + public readonly string[] SoundFormats = { }; public readonly string[] SpriteFormats = { }; readonly string[] reservedModuleNames = { "Metadata", "Folders", "MapFolders", "Packages", "Rules", "Sequences", "VoxelSequences", "Cursors", "Chrome", "Assemblies", "ChromeLayout", "Weapons", "Voices", "Notifications", "Music", "Translations", "TileSets", "ChromeMetrics", "Missions", - "ServerTraits", "LoadScreen", "LobbyDefaults", "Fonts", "SupportsMapsFrom", "SpriteFormats", "RequiresMods" }; + "ServerTraits", "LoadScreen", "LobbyDefaults", "Fonts", "SupportsMapsFrom", "SoundFormats", "SpriteFormats", + "RequiresMods" }; readonly TypeDictionary modules = new TypeDictionary(); readonly Dictionary yaml; @@ -113,6 +115,9 @@ namespace OpenRA MapCompatibility = compat.ToArray(); + if (yaml.ContainsKey("SoundFormats")) + SoundFormats = FieldLoader.GetValue("SoundFormats", yaml["SoundFormats"].Value); + if (yaml.ContainsKey("SpriteFormats")) SpriteFormats = FieldLoader.GetValue("SpriteFormats", yaml["SpriteFormats"].Value); } diff --git a/OpenRA.Game/ModData.cs b/OpenRA.Game/ModData.cs index 60ea3d44c0..2998c86790 100644 --- a/OpenRA.Game/ModData.cs +++ b/OpenRA.Game/ModData.cs @@ -25,6 +25,7 @@ namespace OpenRA public readonly ObjectCreator ObjectCreator; public readonly WidgetLoader WidgetLoader; public readonly MapCache MapCache; + public readonly ISoundLoader[] SoundLoaders; public readonly ISpriteLoader[] SpriteLoaders; public readonly ISpriteSequenceLoader SpriteSequenceLoader; public readonly RulesetCache RulesetCache; @@ -60,6 +61,18 @@ namespace OpenRA RulesetCache.LoadingProgress += HandleLoadingProgress; MapCache = new MapCache(this); + var soundLoaders = new List(); + foreach (var format in Manifest.SoundFormats) + { + var loader = ObjectCreator.FindType(format + "Loader"); + if (loader == null || !loader.GetInterfaces().Contains(typeof(ISoundLoader))) + throw new InvalidOperationException("Unable to find a sound loader for type '{0}'.".F(format)); + + soundLoaders.Add((ISoundLoader)ObjectCreator.CreateBasic(loader)); + } + + SoundLoaders = soundLoaders.ToArray(); + var spriteLoaders = new List(); foreach (var format in Manifest.SpriteFormats) { diff --git a/OpenRA.Game/Sound/Sound.cs b/OpenRA.Game/Sound/Sound.cs index b6beb531bd..97ccc6db43 100644 --- a/OpenRA.Game/Sound/Sound.cs +++ b/OpenRA.Game/Sound/Sound.cs @@ -9,14 +9,19 @@ #endregion using System; +using System.IO; using System.Linq; using System.Reflection; -using OpenRA.FileFormats; using OpenRA.GameRules; using OpenRA.Primitives; namespace OpenRA { + public interface ISoundLoader + { + bool TryParseSound(Stream stream, string fileName, out byte[] rawData, out int channels, out int sampleBits, out int sampleRate); + } + public sealed class Sound : IDisposable { readonly ISoundEngine soundEngine; @@ -51,22 +56,21 @@ namespace OpenRA return null; } - if (filename.ToLowerInvariant().EndsWith("wav")) - using (var s = Game.ModData.ModFiles.Open(filename)) - return LoadWave(new WavLoader(s)); + using (var stream = Game.ModData.ModFiles.Open(filename)) + { + byte[] rawData; + int channels; + int sampleBits; + int sampleRate; + foreach (var loader in Game.ModData.SoundLoaders) + { + stream.Position = 0; + if (loader.TryParseSound(stream, filename, out rawData, out channels, out sampleBits, out sampleRate)) + return soundEngine.AddSoundSourceFromMemory(rawData, channels, sampleBits, sampleRate); + } - using (var s = Game.ModData.ModFiles.Open(filename)) - return LoadSoundRaw(AudLoader.LoadSound(s), 1, 16, 22050); - } - - ISoundSource LoadWave(WavLoader wave) - { - return soundEngine.AddSoundSourceFromMemory(wave.RawOutput, wave.Channels, wave.BitsPerSample, wave.SampleRate); - } - - ISoundSource LoadSoundRaw(byte[] rawData, int channels, int sampleBits, int sampleRate) - { - return soundEngine.AddSoundSourceFromMemory(rawData, channels, sampleBits, sampleRate); + throw new InvalidDataException(filename + " is not a valid sound file!"); + } } public void Initialize() @@ -121,7 +125,7 @@ namespace OpenRA public void PlayVideo(byte[] raw, int channels, int sampleBits, int sampleRate) { - rawSource = LoadSoundRaw(raw, channels, sampleBits, sampleRate); + rawSource = soundEngine.AddSoundSourceFromMemory(raw, channels, sampleBits, sampleRate); video = soundEngine.Play2D(rawSource, false, true, WPos.Zero, InternalSoundVolume, false); }