From 8d56de80cace5122960af9157aa431e7ad3a0075 Mon Sep 17 00:00:00 2001 From: Pavel Penev Date: Thu, 19 Nov 2015 00:27:22 +0200 Subject: [PATCH] Add ISoundLoader The interface is to be implemented by all sound loaders, just like ISpriteLoader. All loading goes through the interface. This would allow mods to create their own sound loaders outside the engine. Also add a SoundFormats property to mod.yaml, where mods can define what sound loaders they will need. This requires Game.Sound to be initialized after the ModData is loaded. --- OpenRA.Game/Game.cs | 3 ++- OpenRA.Game/Graphics/SpriteLoader.cs | 2 +- OpenRA.Game/Manifest.cs | 7 ++++- OpenRA.Game/ModData.cs | 13 ++++++++++ OpenRA.Game/Sound/Sound.cs | 38 +++++++++++++++------------- 5 files changed, 43 insertions(+), 20 deletions(-) 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); }