From 8a7eb2e711d64f5ef9e153dc7001ad9ff874d368 Mon Sep 17 00:00:00 2001 From: Jesse Freeman Date: Thu, 4 Feb 2021 19:22:08 -0500 Subject: [PATCH] Cleaning up sound chip to better support wav samples. --- PixelVision8.CoreDesktop.csproj | 2 - SDK/Editor/Editors/GameEditor.cs | 6 +- SDK/Editor/Exporters/SongExporter.cs | 2 +- SDK/Editor/Exporters/SystemExporter.cs | 4 +- SDK/Editor/Services/GameDataExportService.cs | 2 +- SDK/Player/Chips/Audio/IChannel.cs | 45 -------- SDK/Player/Chips/Audio/ISoundChip.cs | 63 ---------- SDK/Player/Chips/Audio/MusicChip.cs | 12 +- SDK/Player/Chips/Audio/Sfxr/SfxrSoundChip.cs | 10 +- .../Chips/Audio/Sfxr/SfxrSynthChannel.cs | 29 ++--- SDK/Player/Chips/Audio/SoundChannel.cs | 67 +++++------ SDK/Player/Chips/Audio/SoundChip.cs | 109 +++++++++--------- SDK/Player/Chips/Game/GameChip.Sound.cs | 2 +- SDK/Runner/Parsers/SystemParser.cs | 4 +- SDK/Runner/Parsers/WavParser.cs | 30 ++--- SDK/Runner/Services/LoadService.cs | 6 +- 16 files changed, 127 insertions(+), 266 deletions(-) delete mode 100755 SDK/Player/Chips/Audio/IChannel.cs delete mode 100755 SDK/Player/Chips/Audio/ISoundChip.cs diff --git a/PixelVision8.CoreDesktop.csproj b/PixelVision8.CoreDesktop.csproj index 89a9cd92..90c0cd81 100755 --- a/PixelVision8.CoreDesktop.csproj +++ b/PixelVision8.CoreDesktop.csproj @@ -72,8 +72,6 @@ - - diff --git a/SDK/Editor/Editors/GameEditor.cs b/SDK/Editor/Editors/GameEditor.cs index b2fb24a9..70fd23d7 100755 --- a/SDK/Editor/Editors/GameEditor.cs +++ b/SDK/Editor/Editors/GameEditor.cs @@ -1486,9 +1486,9 @@ public int TotalSounds(int? total = null) /// public int TotalChannels(int? total = null) { - if (total.HasValue) soundChip.totalChannels = total.Value; + if (total.HasValue) soundChip.TotalChannels = total.Value; - return soundChip.totalChannels; + return soundChip.TotalChannels; } /// @@ -1569,7 +1569,7 @@ public string SoundLabel(int index, string value = null) { soundChip.UpdateLabel(index, value); - soundChip.RefreshSamples(); + // soundChip.RefreshSamples(); } return soundChip.ReadLabel(index); diff --git a/SDK/Editor/Exporters/SongExporter.cs b/SDK/Editor/Exporters/SongExporter.cs index ae78ccb0..945ff643 100755 --- a/SDK/Editor/Exporters/SongExporter.cs +++ b/SDK/Editor/Exporters/SongExporter.cs @@ -45,7 +45,7 @@ public float // int songdataCurrentPos = 0; - public SongExporter(string path, MusicChip musicChip, ISoundChip soundChip, int[] patterns) : base(path) + public SongExporter(string path, MusicChip musicChip, SoundChip soundChip, int[] patterns) : base(path) { // Rebuild the path by adding the active song name and wav extension fileName = path; diff --git a/SDK/Editor/Exporters/SystemExporter.cs b/SDK/Editor/Exporters/SystemExporter.cs index d0210ddc..69d56a2f 100755 --- a/SDK/Editor/Exporters/SystemExporter.cs +++ b/SDK/Editor/Exporters/SystemExporter.cs @@ -336,7 +336,7 @@ private void SerializeSoundChip(SfxrSoundChip soundChip) JsonUtil.GetLineBreak(sb, 1); sb.Append("\"totalChannels\":"); - sb.Append(soundChip.totalChannels); + sb.Append(soundChip.TotalChannels); sb.Append(","); JsonUtil.GetLineBreak(sb, 1); @@ -347,7 +347,7 @@ private void SerializeSoundChip(SfxrSoundChip soundChip) sb.Append("\"channelTypes\":["); - var total = soundChip.totalChannels; + var total = soundChip.TotalChannels; for (var i = 0; i < total; i++) { diff --git a/SDK/Editor/Services/GameDataExportService.cs b/SDK/Editor/Services/GameDataExportService.cs index c2cffd13..e35429be 100755 --- a/SDK/Editor/Services/GameDataExportService.cs +++ b/SDK/Editor/Services/GameDataExportService.cs @@ -110,7 +110,7 @@ public void ExportGame(string path, PixelVision engine, FileFlags fileFlags, boo StartExport(useSteps); } - public void ExportSong(string path, MusicChip musicChip, ISoundChip soundChip, int[] patterns) + public void ExportSong(string path, MusicChip musicChip, SoundChip soundChip, int[] patterns) { Restart(); diff --git a/SDK/Player/Chips/Audio/IChannel.cs b/SDK/Player/Chips/Audio/IChannel.cs deleted file mode 100755 index 6ec3e190..00000000 --- a/SDK/Player/Chips/Audio/IChannel.cs +++ /dev/null @@ -1,45 +0,0 @@ -// -// Copyright (c) Jesse Freeman, Pixel Vision 8. All rights reserved. -// -// Licensed under the Microsoft Public License (MS-PL) except for a few -// portions of the code. See LICENSE file in the project root for full -// license information. Third-party libraries used by Pixel Vision 8 are -// under their own licenses. Please refer to those libraries for details -// on the license they use. -// -// Contributors -// -------------------------------------------------------- -// This is the official list of Pixel Vision 8 contributors: -// -// Jesse Freeman - @JesseFreeman -// Christina-Antoinette Neofotistou @CastPixel -// Christer Kaitila - @McFunkypants -// Pedro Medeiros - @saint11 -// Shawn Rakowski - @shwany -// - -namespace PixelVision8.Player.Audio -{ - /// - /// The ISoundData interface represents a basic API for working - /// with sound objects in the PixelVisionSDK. Implement this - /// Interface with access to sound data to use it inside of - /// games and the MusicChip. - /// - public interface IChannel - { - bool Playing { get; } - - /// - /// Plays the sound at a specific frequency. - /// - /// - /// - void Play(SoundData soundData, float? frequency = null); - - /// - /// Stops the current sound from playing - /// - void Stop(); - } -} \ No newline at end of file diff --git a/SDK/Player/Chips/Audio/ISoundChip.cs b/SDK/Player/Chips/Audio/ISoundChip.cs deleted file mode 100755 index 6c8118eb..00000000 --- a/SDK/Player/Chips/Audio/ISoundChip.cs +++ /dev/null @@ -1,63 +0,0 @@ -// -// Copyright (c) Jesse Freeman, Pixel Vision 8. All rights reserved. -// -// Licensed under the Microsoft Public License (MS-PL) except for a few -// portions of the code. See LICENSE file in the project root for full -// license information. Third-party libraries used by Pixel Vision 8 are -// under their own licenses. Please refer to those libraries for details -// on the license they use. -// -// Contributors -// -------------------------------------------------------- -// This is the official list of Pixel Vision 8 contributors: -// -// Jesse Freeman - @JesseFreeman -// Christina-Antoinette Neofotistou @CastPixel -// Christer Kaitila - @McFunkypants -// Pedro Medeiros - @saint11 -// Shawn Rakowski - @shwany -// - -namespace PixelVision8.Player -{ - public interface ISoundChip - { - /// - /// The total number of available for playing - /// back sounds. - /// - int totalChannels { get; set; } - - /// - /// The total number of in the collection. - /// - int TotalSounds { get; set; } - - /// - /// This method plays back a sound on a specific channel. The - /// SoundChip has a limit of active - /// so playing a sound effect while another was is playing on the same - /// will cancel it out and replace with the - /// new sound. - /// - /// - /// The ID of the sound in the SoundCollection. - /// - /// - /// The channel the sound should play back on. - /// - void PlaySound(int index, int channelID = 0, float? frequency = null); - - void PlaySound(string name, int channelID = 0, float? frequency = null); - - bool IsChannelPlaying(int channelID); - void StopSound(int channel); - void Shutdown(); - void AddSample(string name, byte[] bytes); - - /// - /// Goes through the sounds and the sound bank and adds the sample byte data - /// - void RefreshSamples(); - } -} \ No newline at end of file diff --git a/SDK/Player/Chips/Audio/MusicChip.cs b/SDK/Player/Chips/Audio/MusicChip.cs index ed0339db..796fc271 100755 --- a/SDK/Player/Chips/Audio/MusicChip.cs +++ b/SDK/Player/Chips/Audio/MusicChip.cs @@ -24,10 +24,10 @@ namespace PixelVision8.Player { - public partial interface IPlayerChips - { - public MusicChip MusicChip { get; set; } - } + // public partial interface IPlayerChips + // { + // public MusicChip MusicChip { get; set; } + // } /// /// The MusicChpip is a sequencer for playing back ISoundData. It @@ -172,7 +172,7 @@ public int totalNotes } } - public int totalTracks => SoundChip.totalChannels; + public int totalTracks => SoundChip.TotalChannels; // { // get => _totalTracks; // set @@ -200,7 +200,7 @@ public TrackerData ActiveTrackerData } } - protected ISoundChip SoundChip => Player.SoundChip; + protected SoundChip SoundChip => Player.SoundChip; /// /// Updates the sequencer if it is in playback mode. This will diff --git a/SDK/Player/Chips/Audio/Sfxr/SfxrSoundChip.cs b/SDK/Player/Chips/Audio/Sfxr/SfxrSoundChip.cs index dc106747..f86eb86c 100755 --- a/SDK/Player/Chips/Audio/Sfxr/SfxrSoundChip.cs +++ b/SDK/Player/Chips/Audio/Sfxr/SfxrSoundChip.cs @@ -48,12 +48,12 @@ public virtual void UpdateSound(int index, string param) /// create new sound instances that implement the ISoundData interface. /// /// - public override IChannel CreateSoundChannel() + public override SoundChannel CreateSoundChannel() { return new SfxrSynthChannel(); } - public override SoundData CreateSoundData(string name) + public override SoundData CreateSoundData(string name, byte[] bytes = null) { return new SfxSoundData(name); } @@ -71,6 +71,12 @@ public override SoundData CreateSoundData(string name) /// public SfxSoundData ReadSound(int id) { + if (id < 0 || id >= TotalSounds) + return null; + + if (Sounds[id] == null) + Sounds[id] = CreateSoundData("Untitled" + id.ToString("D2")); + return Sounds[id] as SfxSoundData; } diff --git a/SDK/Player/Chips/Audio/Sfxr/SfxrSynthChannel.cs b/SDK/Player/Chips/Audio/Sfxr/SfxrSynthChannel.cs index 8b82c3c7..d13c8d46 100755 --- a/SDK/Player/Chips/Audio/Sfxr/SfxrSynthChannel.cs +++ b/SDK/Player/Chips/Audio/Sfxr/SfxrSynthChannel.cs @@ -35,8 +35,8 @@ public class SfxrSynthChannel : SoundChannel // private int _overtones; // Minimum frequency before stopping - private readonly Dictionary wavCache = - new Dictionary(); + // private readonly Dictionary wavCache = + // new Dictionary(); private float _changeAmount; // Amount to change the note by @@ -171,6 +171,8 @@ public override void Play(SoundData soundData, float? frequency = null) // Stop any playing sound Stop(); + // TODO this logic isn't working correctly. Need to double check the cache + // Clear the last sound instance _soundInstance = null; @@ -411,13 +413,12 @@ public void CacheSound() { Stop(); - var paramKey = parameters.GetSettingsString(); - if (wavCache.ContainsKey(paramKey)) + if (SoundInstanceCache.ContainsKey(paramKey)) { - _soundInstance = wavCache[paramKey]; + _soundInstance = SoundInstanceCache[paramKey]; } else { @@ -442,7 +443,7 @@ public void CacheSound() _soundInstance = soundEffect.CreateInstance(); } - wavCache[paramKey] = _soundInstance; + SoundInstanceCache[paramKey] = _soundInstance; } } @@ -881,21 +882,5 @@ private bool SynthWave(float[] __buffer, int __bufferPos, uint __length) return false; } - /// - /// Returns a random value: 0 <= n < 1 - /// - /// - // private float parameters.GetRandom() - // { - // // We can't use Unity's Random.value because it cannot be called from a separate thread - // // (We get the error "get_value can only be called from the main thread" when this is called to generate the sound data) - // return (float) (_random.NextDouble() % 1); - // } - public void Dispose() - { - _soundInstance?.Dispose(); - - foreach (var wav in wavCache) wav.Value?.Dispose(); - } } } \ No newline at end of file diff --git a/SDK/Player/Chips/Audio/SoundChannel.cs b/SDK/Player/Chips/Audio/SoundChannel.cs index a5f38e13..acfeaf79 100755 --- a/SDK/Player/Chips/Audio/SoundChannel.cs +++ b/SDK/Player/Chips/Audio/SoundChannel.cs @@ -18,25 +18,18 @@ // Shawn Rakowski - @shwany // -using Microsoft.Xna.Framework.Audio; using System.Collections.Generic; +using Microsoft.Xna.Framework.Audio; using System.IO; namespace PixelVision8.Player.Audio { - public class SoundChannel : IChannel + public class SoundChannel { - private readonly Dictionary wavCache = - new Dictionary(); - - // private float amp; // Used in other calculations + private SoundEffectInstance _soundInstance; - - /// - /// Sound parameters - /// - // public SfxrParams parameters { get; } = new SfxrParams(); - + protected readonly Dictionary SoundInstanceCache = new Dictionary(); + public bool Playing { get @@ -57,34 +50,32 @@ public virtual void Play(SoundData soundData, float? frequency = null) // Stop any playing sound Stop(); - // Clear the last sound instance - _soundInstance = null; - - // See if this is a wav - if (soundData.bytes != null) + if (SoundInstanceCache.ContainsKey(soundData.name)) + _soundInstance = SoundInstanceCache[soundData.name]; + else { - // if (waveLock == WaveType.Sample || waveLock == WaveType.None) - using (var stream = new MemoryStream(soundData.bytes)) + // Clear the last sound instance + _soundInstance = null; + + // See if this is a wav + if (soundData.bytes != null) { - var soundEffect = SoundEffect.FromStream(stream); + // if (waveLock == WaveType.Sample || waveLock == WaveType.None) + using (var stream = new MemoryStream(soundData.bytes)) + { + var soundEffect = SoundEffect.FromStream(stream); + + _soundInstance = soundEffect.CreateInstance(); - // var param = new SfxrParams(); - // param.SetSettingsString(soundData.param); + // TODO need to make sure this is correct and we can use the frequency to manipulate the pitch + if (frequency.HasValue) + _soundInstance.Pitch = frequency.Value; - // TODO This should be cached? - _soundInstance = soundEffect.CreateInstance(); - // TODO need to set the volume? - // _soundInstance.Volume = param.masterVolume; + } } - } - // else - // { - // parameters.SetSettingsString(soundData.param); - // - // if (frequency.HasValue) parameters.startFrequency = frequency.Value; - // - // if (parameters.invalid) CacheSound(); - // } + + SoundInstanceCache[soundData.name] = _soundInstance; + } // Only play if there is a sound instance _soundInstance?.Play(); @@ -98,11 +89,5 @@ public virtual void Stop() _soundInstance?.Stop(); } - public void Dispose() - { - _soundInstance?.Dispose(); - - foreach (var wav in wavCache) wav.Value?.Dispose(); - } } } \ No newline at end of file diff --git a/SDK/Player/Chips/Audio/SoundChip.cs b/SDK/Player/Chips/Audio/SoundChip.cs index cff3fcde..e5de6c9f 100755 --- a/SDK/Player/Chips/Audio/SoundChip.cs +++ b/SDK/Player/Chips/Audio/SoundChip.cs @@ -26,36 +26,30 @@ namespace PixelVision8.Player { - public partial interface IPlayerChips - { - /// - /// The Sound Chip stores and manages playback of sound effects in the - /// game engine. This property offers direct access to it. - /// - ISoundChip SoundChip { get; set; } - } - + /// /// The is responsible for playing back sound /// effects in the engine. It's powered by SFxr. /// - public class SoundChip : AbstractChip, ISoundChip + public class SoundChip : AbstractChip { - protected readonly Dictionary soundBank = new Dictionary(); - protected IChannel[] Channels = new IChannel[0]; + // private readonly Dictionary _soundBank = new Dictionary(); + protected SoundChannel[] Channels = new SoundChannel[0]; protected SoundData[] Sounds; /// /// The total number of available for playing /// back sounds. /// - public int totalChannels + public int TotalChannels { get => Channels.Length; set { value = MathHelper.Clamp(value, 1, 5); Array.Resize(ref Channels, value); + + // There should never be an empty sound channel so loop through them and make sure one is created for (var i = 0; i < value; i++) if (Channels[i] == null) Channels[i] = CreateSoundChannel(); @@ -75,15 +69,15 @@ public int TotalSounds Array.Resize(ref Sounds, value); - for (var i = 0; i < value; i++) - if (Sounds[i] == null) - Sounds[i] = CreateSoundData("Untitled" + i.ToString("D2")); + // for (var i = 0; i < value; i++) + // if (Sounds[i] == null) + // Sounds[i] = CreateSoundData("Untitled" + i.ToString("D2")); } } - public virtual SoundData CreateSoundData(string name) + public virtual SoundData CreateSoundData(string name, byte[] bytes = null) { - return new SoundData(name); + return new SoundData(name, bytes); } /// @@ -91,7 +85,7 @@ public virtual SoundData CreateSoundData(string name) /// create new sound instances that implement the ISoundData interface. /// /// - public virtual IChannel CreateSoundChannel() + public virtual SoundChannel CreateSoundChannel() { return new SoundChannel(); } @@ -105,7 +99,7 @@ protected override void Configure() { Player.SoundChip = this; TotalSounds = 16; - totalChannels = 5; + TotalChannels = 5; } /// @@ -121,35 +115,49 @@ protected override void Configure() /// /// The channel the sound should play back on. /// - public void PlaySound(int index, int channelID = 0, float? frequency = null) + /// + public void PlaySound(int index, int channelId = 0, float? frequency = null) { - if (index > Sounds.Length) return; + if (index < 0 || index >= Sounds.Length || Sounds[index] == null) + return; + + channelId = MathHelper.Clamp(channelId, 0, TotalChannels - 1); - channelID = MathHelper.Clamp(channelID, 0, totalChannels - 1); + Channels[channelId].Play(Sounds[index], frequency); - var channel = Channels[channelID]; + } - channel?.Stop(); + protected int FindNextEmptySound() + { - // channel = sounds[index]; + for (int i = 0; i < TotalSounds; i++) + { + if (Sounds[i] == null) + return i; + } + + return -1; - channel.Play(Sounds[index], frequency); } - public void PlaySound(string name, int channelID = 0, float? frequency = null) + protected int FindSoundId(string name) { for (int i = 0; i < TotalSounds; i++) { - if (Sounds[i].name == name) + if (Sounds[i] != null && Sounds[i].name == name) { - PlaySound(i, channelID, frequency); + return i; } } + + return - 1; } - public bool IsChannelPlaying(int channelID) + public void PlaySound(string name, int channelID = 0, float? frequency = null) => PlaySound(FindSoundId(name), channelID, frequency); + + public bool IsChannelPlaying(int channelId) { - return Channels[channelID] != null && Channels[channelID].Playing; + return Channels[channelId] != null && Channels[channelId].Playing; } public void StopSound(int channel) @@ -157,6 +165,21 @@ public void StopSound(int channel) if (Channels[channel] != null) Channels[channel].Stop(); } + public void AddSample(string name, byte[] bytes) + { + + var id = FindSoundId(name); + + if (id == -1) + id = FindNextEmptySound(); + + if(id == -1) + return; + + Sounds[id] = CreateSoundData(name, bytes); + + } + public override void Shutdown() { foreach (var channel in Channels) @@ -166,26 +189,6 @@ public override void Shutdown() base.Shutdown(); } - public void AddSample(string name, byte[] bytes) - { - // Add the wav sample to the sound bank - if (soundBank.ContainsKey(name)) - soundBank[name] = bytes; - else - soundBank.Add(name, bytes); - } - - /// - /// Goes through the sounds and the sound bank and adds the sample byte data - /// - public void RefreshSamples() - { - for (var i = 0; i < TotalSounds; i++) - { - var name = Sounds[i].name; - Sounds[i].bytes = soundBank.ContainsKey(name) ? soundBank[name] : null; - } - } } } @@ -193,6 +196,6 @@ namespace PixelVision8.Player { public partial class PixelVision { - public ISoundChip SoundChip { get; set; } + public SoundChip SoundChip { get; set; } } } \ No newline at end of file diff --git a/SDK/Player/Chips/Game/GameChip.Sound.cs b/SDK/Player/Chips/Game/GameChip.Sound.cs index 960778df..826ae726 100644 --- a/SDK/Player/Chips/Game/GameChip.Sound.cs +++ b/SDK/Player/Chips/Game/GameChip.Sound.cs @@ -22,7 +22,7 @@ namespace PixelVision8.Player { public partial class GameChip { - protected ISoundChip SoundChip => Player.SoundChip; + protected SoundChip SoundChip => Player.SoundChip; #region Sound diff --git a/SDK/Runner/Parsers/SystemParser.cs b/SDK/Runner/Parsers/SystemParser.cs index 8d77f897..49fdd364 100755 --- a/SDK/Runner/Parsers/SystemParser.cs +++ b/SDK/Runner/Parsers/SystemParser.cs @@ -415,7 +415,7 @@ public void ConfigureSoundChip(Dictionary data) // Flag chip to export //soundChip.export = true; - if (data.ContainsKey("totalChannels")) soundChip.totalChannels = (int) (long) data["totalChannels"]; + if (data.ContainsKey("totalChannels")) soundChip.TotalChannels = (int) (long) data["totalChannels"]; if (data.ContainsKey("totalSounds")) soundChip.TotalSounds = (int) (long) data["totalSounds"]; @@ -425,7 +425,7 @@ public void ConfigureSoundChip(Dictionary data) for (var i = 0; i < types.Count; i++) // Make sure we are only changing channels that exist - if (i < soundChip.totalChannels) + if (i < soundChip.TotalChannels) soundChip.ChannelType(i, (WaveType) Convert.ToInt32(types[i])); } diff --git a/SDK/Runner/Parsers/WavParser.cs b/SDK/Runner/Parsers/WavParser.cs index 6e134759..4414bb91 100755 --- a/SDK/Runner/Parsers/WavParser.cs +++ b/SDK/Runner/Parsers/WavParser.cs @@ -23,49 +23,39 @@ namespace PixelVision8.Runner { public class WavParser : AbstractParser { - public string[] files; - public ISoundChip soundChip; - private IFileLoader _fileLoadHelper; + private readonly SoundChip _soundChip; + private readonly IFileLoader _fileLoadHelper; - public WavParser(string[] files, IFileLoader fileLoadHelper, PixelVision engine) + public WavParser(string file, IFileLoader fileLoadHelper, SoundChip soundChip) { _fileLoadHelper = fileLoadHelper; - soundChip = engine.SoundChip; - this.files = files; + _soundChip = soundChip; + SourcePath = file; } public override void CalculateSteps() { base.CalculateSteps(); Steps.Add(ParseWavData); - Steps.Add(ConfigureSamples); } public void ParseWavData() { - foreach (var file in files) - { - var name = _fileLoadHelper.GetFileName(file).Replace(".wav", ""); - soundChip.AddSample(name, _fileLoadHelper.ReadAllBytes(file)); - } + + var name = _fileLoadHelper.GetFileName(SourcePath).Replace(".wav", ""); + _soundChip.AddSample(name, _fileLoadHelper.ReadAllBytes(SourcePath)); CurrentStep++; } - public void ConfigureSamples() - { - soundChip.RefreshSamples(); - - CurrentStep++; - } } public partial class Loader { [FileParser(".wav", FileFlags.Sounds)] - public void ParseWave(string[] file, PixelVision engine) + public void ParseWave(string file, PixelVision engine) { - AddParser(new WavParser(file, _fileLoadHelper, engine)); + AddParser(new WavParser(file, _fileLoadHelper, engine.SoundChip)); } } } \ No newline at end of file diff --git a/SDK/Runner/Services/LoadService.cs b/SDK/Runner/Services/LoadService.cs index 5b20a3f1..70b1b07d 100755 --- a/SDK/Runner/Services/LoadService.cs +++ b/SDK/Runner/Services/LoadService.cs @@ -150,8 +150,10 @@ public virtual void ParseFiles(string[] files, PixelVision engine, FileFlags fil // Get all of the wav files var wavFiles = files.Where(x => x.EndsWith(".wav")).ToArray(); - if (wavFiles.Length > 0) - _loader.AddParser(new WavParser(wavFiles, _fileLoadHelper, targetEngine)); + for (int i = 0; i < wavFiles.Length; i++) + { + _loader.AddParser(new WavParser(wavFiles[i], _fileLoadHelper, targetEngine.SoundChip)); + } } // Step 10 (optional). Look for meta data and override the game