using Amphora; using Azimuth; using System; using UnitTestSharp; namespace Susurrus.UnitTests { public class MixerTests : TestFixture { public class Rig { public readonly NullModule Module; public readonly ScriptedGenerator Random; public readonly Mixer Mixer; public Rig(int maxVoices, int moduleVoices, ulong[] words, float fadeSeconds) { Module = new NullModule(moduleVoices) { EndsVoicesOnUpdate = false }; Random = new ScriptedGenerator(words); Mixer = new Mixer(Module, Random, maxVoices, fadeSeconds); } public Rig(int maxVoices = 32, int moduleVoices = 32, params ulong[] words) : this(maxVoices, moduleVoices, words, fadeSeconds: 0f) { } public int DrawsLeft => Random.Remaining; public static Rig WithFade(float fadeSeconds, int maxVoices = 32, int moduleVoices = 32) { return new Rig(maxVoices, moduleVoices, new ulong[0], fadeSeconds); } public SoundId Define(Bus bus, float baseVolume = 1f, int maxInstances = SoundDefinition.Unlimited, float minRetriggerSeconds = 0f, int priority = 0) { return Mixer.Define(new SoundDefinition(Clip(), bus, baseVolume: baseVolume, priority: priority, maxInstances: maxInstances, minRetriggerSeconds: minRetriggerSeconds)); } public SoundId DefineLooping(Loop loop) { return Mixer.Define(new SoundDefinition(Clip(), Mixer.Master, loop: loop)); } public SoundId DefineFading(float fadeInSeconds, float? fadeOutSeconds = null, Bus bus = null) { return Mixer.Define(new SoundDefinition(Clip(), bus ?? Mixer.Master, fadeInSeconds: fadeInSeconds, fadeOutSeconds: fadeOutSeconds)); } public SoundId DefineSustained(float graceSeconds = 0.5f, float fadeInSeconds = 0f, float? fadeOutSeconds = null, IFalloff falloff = null, int priority = 0, int maxInstances = SoundDefinition.Unlimited, Bus bus = null) { return Mixer.Define(new SoundDefinition(Clip(), bus ?? Mixer.Master, priority: priority, maxInstances: maxInstances, falloff: falloff, loop: Loop.Whole, fadeInSeconds: fadeInSeconds, fadeOutSeconds: fadeOutSeconds, sustainGraceSeconds: graceSeconds)); } public SoundId DefineWith(IFalloff falloff) { return Mixer.Define(new SoundDefinition(Clip(), Mixer.Master, falloff: falloff)); } // Fades out to silence 2 to 10 world units from the middle of a view that reaches 10 units each way. public SoundId DefineFalloff(int priority = 0, float minRetriggerSeconds = 0f) { Mixer.View = new View(Vector.Zero, 10); return Mixer.Define(new SoundDefinition(Clip(), Mixer.Master, priority: priority, minRetriggerSeconds: minRetriggerSeconds, falloff: new Falloff(2, 10))); } } public class FixedFalloff : IFalloff { private readonly float gain; public FixedFalloff(float gain) { this.gain = gain; } public float GainAt(Scalar distance) { return gain; } } public static Sound Clip() { return new Sound(TimeSpan.FromSeconds(1), channelCount: 1); } public static bool Near(float expected, float actual) { return Math.Abs(expected - actual) <= expected * 1e-5f; } public static ulong Index(int index, int count) { return ScriptedGenerator.WordForIndex((ulong)index, (ulong)count); } public class CtorTests : TestFixture { public void ANullModule_Throws() { CheckThrow(typeof(ArgumentNullException)); new Mixer(null, new ScriptedGenerator()); } public void ANullRandom_Throws() { CheckThrow(typeof(ArgumentNullException)); new Mixer(new NullModule(), null); } public void NoRoomForVoices_Throws() { CheckThrow(typeof(ArgumentOutOfRangeException)); new Rig(maxVoices: 0); } public void StartsWithNothingPlayingAndTheDefaultRoom() { Mixer mixer = new Mixer( new NullModule(), new ScriptedGenerator()); CheckEqual(0, mixer.ActiveCount); CheckEqual(0, mixer.FadingCount); CheckEqual(Mixer.DefaultMaxVoices, mixer.MaxVoices); } public void MaxVoices_IsTheRoomGiven() { CheckEqual(5, new Rig(maxVoices: 5).Mixer.MaxVoices); } public void MakesAMasterBus() { CheckEqual("Master", new Rig().Mixer.Master.Name); } } public class AddBusTests : TestFixture { public void ReturnsABusOfThatName() { CheckEqual("Effects", new Rig().Mixer.AddBus("Effects").Name); } public void ADuplicateName_Throws() { Rig rig = new Rig(); rig.Mixer.AddBus("Effects"); CheckThrow(typeof(ArgumentException)); rig.Mixer.AddBus("Effects"); } public void TheNameOfTheMasterBus_Throws() { CheckThrow(typeof(ArgumentException)); new Rig().Mixer.AddBus("Master"); } public void ANullName_Throws() { CheckThrow(typeof(ArgumentException)); new Rig().Mixer.AddBus(null); } } public class DefineTests : TestFixture { public void HandsOutIndexesInOrder() { Rig rig = new Rig(); Bus effects = rig.Mixer.AddBus("Effects"); CheckEqual(new SoundId(0), rig.Define(effects)); CheckEqual(new SoundId(1), rig.Define(effects)); } public void ADefinitionOnTheMasterBus_IsAccepted() { Rig rig = new Rig(); CheckEqual(new SoundId(0), rig.Define(rig.Mixer.Master)); } public void ADefinitionOnAnotherMixersBus_Throws() { Rig rig = new Rig(); Bus foreign = new Rig().Mixer.AddBus("Effects"); CheckThrow(typeof(ArgumentException)); rig.Define(foreign); } public void ADefinitionOnABusMadeOutsideAMixer_Throws() { Rig rig = new Rig(); CheckThrow(typeof(ArgumentException)); rig.Define(new Bus("Loose")); } public void ANullDefinition_Throws() { CheckThrow(typeof(ArgumentNullException)); new Rig().Mixer.Define(null); } } public class PlayTests : TestFixture { public void TheLoopOfTheDefinition_ReachesTheModule() { Rig rig = new Rig(); Loop loop = new Loop(TimeSpan.FromSeconds(0.25), TimeSpan.FromSeconds(0.75)); Voice voice = rig.Mixer.Play(rig.DefineLooping(loop)); CheckEqual(TimeSpan.FromSeconds(0.25), rig.Module.LoopOf(voice).Start); CheckEqual(TimeSpan.FromSeconds(0.75), rig.Module.LoopOf(voice).End.Value); } public void ADefinitionWithNoLoop_PlaysOnce() { Rig rig = new Rig(); Voice voice = rig.Mixer.Play(rig.Define(rig.Mixer.Master)); CheckFalse(rig.Module.LoopOf(voice).IsLooping); } public void ALoopingVoice_KeepsPlayingThroughUpdates() { Rig rig = new Rig(); rig.Module.EndsVoicesOnUpdate = true; Voice voice = rig.Mixer.Play(rig.DefineLooping(Loop.Whole)); rig.Mixer.Update(0.5f); rig.Mixer.Update(0.5f); CheckTrue(rig.Mixer.IsPlaying(voice)); CheckEqual(1, rig.Mixer.ActiveCount); } public void ALoopingVoice_EndsWhenStopped() { Rig rig = new Rig(); rig.Module.EndsVoicesOnUpdate = true; Voice voice = rig.Mixer.Play(rig.DefineLooping(Loop.Whole)); rig.Mixer.Stop(voice); rig.Mixer.Update(0.5f); CheckFalse(rig.Mixer.IsPlaying(voice)); CheckEqual(0, rig.Mixer.ActiveCount); } public void StartsTheDefinitionsClipOnTheModule() { Rig rig = new Rig(); Sound clip = Clip(); SoundId id = rig.Mixer.Define(new SoundDefinition(clip, rig.Mixer.Master)); Voice voice = rig.Mixer.Play(id); CheckFalse(voice.IsNone); Check(clip == rig.Module.SoundOf(voice)); CheckEqual(1, rig.Mixer.ActiveCount); } public void MultipliesTheDefinitionsVolumeByThePlays() { Rig rig = new Rig(); SoundId id = rig.Define(rig.Mixer.Master, baseVolume: 0.5f); Voice voice = rig.Mixer.Play(id, volume: 0.5f); CheckEqual(0.25f, rig.Module.VolumeOf(voice)); } public void ThePlayVolumeAmplifiesAboveOne() { Rig rig = new Rig(); SoundId id = rig.Define(rig.Mixer.Master); CheckEqual(3f, rig.Module.VolumeOf(rig.Mixer.Play(id, volume: 3f))); } public void AppliesTheBusAndTheMasterGain() { Rig rig = new Rig(); Bus effects = rig.Mixer.AddBus("Effects"); effects.Volume = 0.5f; rig.Mixer.Master.Volume = 0.5f; SoundId id = rig.Define(effects); CheckEqual(0.25f, rig.Module.VolumeOf(rig.Mixer.Play(id))); } public void OnTheMasterBus_AppliesTheMasterGainOnce() { Rig rig = new Rig(); rig.Mixer.Master.Volume = 0.5f; SoundId id = rig.Define(rig.Mixer.Master); CheckEqual(0.5f, rig.Module.VolumeOf(rig.Mixer.Play(id))); } public void OnAMutedBus_StartsSilentButStillTakesAVoice() { Rig rig = new Rig(); Bus effects = rig.Mixer.AddBus("Effects"); effects.IsMuted = true; SoundId id = rig.Define(effects); Voice voice = rig.Mixer.Play(id); CheckFalse(voice.IsNone); CheckEqual(0f, rig.Module.VolumeOf(voice)); } public void AnUnknownSound_Throws() { Rig rig = new Rig(); CheckThrow(typeof(ArgumentOutOfRangeException)); rig.Mixer.Play(new SoundId(0)); } public void ANegativeVolume_Throws() { Rig rig = new Rig(); SoundId id = rig.Define(rig.Mixer.Master); CheckThrow(typeof(ArgumentOutOfRangeException)); rig.Mixer.Play(id, volume: -1f); } public void PastTheInstanceCap_IsDropped() { Rig rig = new Rig(); SoundId id = rig.Define(rig.Mixer.Master, maxInstances: 2); rig.Mixer.Play(id); rig.Mixer.Play(id); CheckTrue(rig.Mixer.Play(id).IsNone); CheckEqual(2, rig.Mixer.ActiveCount); } public void TheInstanceCap_IsPerDefinition() { Rig rig = new Rig(); SoundId capped = rig.Define(rig.Mixer.Master, maxInstances: 1); SoundId other = rig.Define(rig.Mixer.Master, maxInstances: 1); rig.Mixer.Play(capped); CheckFalse(rig.Mixer.Play(other).IsNone); } public void AFinishedInstance_FreesRoomUnderTheCap() { Rig rig = new Rig(); SoundId id = rig.Define(rig.Mixer.Master, maxInstances: 1); Voice first = rig.Mixer.Play(id); rig.Module.Finish(first); rig.Mixer.Update(seconds: 0f); CheckFalse(rig.Mixer.Play(id).IsNone); } public void ATooSoonRetrigger_IsDropped() { Rig rig = new Rig(); SoundId id = rig.Define(rig.Mixer.Master, minRetriggerSeconds: 0.5f); rig.Mixer.Play(id); rig.Mixer.Update(seconds: 0.25f); CheckTrue(rig.Mixer.Play(id).IsNone); } public void ARetriggerAtTheIntervalExactly_Plays() { Rig rig = new Rig(); SoundId id = rig.Define(rig.Mixer.Master, minRetriggerSeconds: 0.5f); rig.Mixer.Play(id); rig.Mixer.Update(seconds: 0.25f); rig.Mixer.Update(seconds: 0.25f); CheckFalse(rig.Mixer.Play(id).IsNone); } public void TheRetriggerInterval_IsPerDefinition() { Rig rig = new Rig(); SoundId slow = rig.Define(rig.Mixer.Master, minRetriggerSeconds: 10f); SoundId other = rig.Define(rig.Mixer.Master, minRetriggerSeconds: 10f); rig.Mixer.Play(slow); CheckFalse(rig.Mixer.Play(other).IsNone); } public void ADropForTheCap_DoesNotRestartTheInterval() { Rig rig = new Rig(); SoundId id = rig.Define(rig.Mixer.Master, maxInstances: 1, minRetriggerSeconds: 1f); Voice first = rig.Mixer.Play(id); rig.Mixer.Update(seconds: 0.75f); rig.Mixer.Play(id); rig.Module.Finish(first); rig.Mixer.Update(seconds: 0.25f); CheckFalse(rig.Mixer.Play(id).IsNone); } public void WithEveryVoiceTakenByMoreImportantSounds_IsDropped() { Rig rig = new Rig(maxVoices: 2); SoundId important = rig.Define(rig.Mixer.Master, priority: 1); SoundId minor = rig.Define(rig.Mixer.Master, priority: 0); rig.Mixer.Play(important); rig.Mixer.Play(important); CheckTrue(rig.Mixer.Play(minor).IsNone); CheckEqual(2, rig.Mixer.ActiveCount); } public void WhenTheModuleIsFull_IsDroppedWithoutStartingTheInterval() { Rig rig = new Rig(maxVoices: 4, moduleVoices: 1); SoundId hog = rig.Define(rig.Mixer.Master); SoundId slow = rig.Define(rig.Mixer.Master, minRetriggerSeconds: 10f); Voice taken = rig.Mixer.Play(hog); CheckTrue(rig.Mixer.Play(slow).IsNone); CheckEqual(1, rig.Mixer.ActiveCount); rig.Mixer.Stop(taken); CheckFalse(rig.Mixer.Play(slow).IsNone); } public void OneClip_UsesNoDraw() { Rig rig = new Rig(); SoundId id = rig.Define(rig.Mixer.Master); rig.Mixer.Play(id); CheckEqual(0, rig.DrawsLeft); } public void ManyClips_PlaysTheClipTheDrawNames() { Rig rig = new Rig(32, 32, Index(2, 3), Index(0, 3)); Sound[] clips = { Clip(), Clip(), Clip() }; SoundId id = rig.Mixer.Define(new SoundDefinition(clips, rig.Mixer.Master)); Voice first = rig.Mixer.Play(id); Voice second = rig.Mixer.Play(id); Check(clips[2] == rig.Module.SoundOf(first)); Check(clips[0] == rig.Module.SoundOf(second)); CheckEqual(0, rig.DrawsLeft); } public void WithoutNeverRepeat_AClipMayPlayTwiceInARow() { Rig rig = new Rig(32, 32, Index(1, 3), Index(1, 3)); Sound[] clips = { Clip(), Clip(), Clip() }; SoundId id = rig.Mixer.Define(new SoundDefinition(clips, rig.Mixer.Master)); Voice first = rig.Mixer.Play(id); Voice second = rig.Mixer.Play(id); Check(clips[1] == rig.Module.SoundOf(first)); Check(clips[1] == rig.Module.SoundOf(second)); } public void NeverRepeat_TheFirstPlayDrawsFromEveryClip() { Rig rig = new Rig(32, 32, Index(2, 3)); Sound[] clips = { Clip(), Clip(), Clip() }; SoundId id = rig.Mixer.Define(new SoundDefinition(clips, rig.Mixer.Master, neverRepeatClip: true)); Voice voice = rig.Mixer.Play(id); Check(clips[2] == rig.Module.SoundOf(voice)); CheckEqual(0, rig.DrawsLeft); } public void NeverRepeat_DrawsFromTheOtherClipsBelowTheLastOne() { Rig rig = new Rig(32, 32, Index(1, 3), Index(0, 2)); Sound[] clips = { Clip(), Clip(), Clip() }; SoundId id = rig.Mixer.Define(new SoundDefinition(clips, rig.Mixer.Master, neverRepeatClip: true)); rig.Mixer.Play(id); Voice second = rig.Mixer.Play(id); Check(clips[0] == rig.Module.SoundOf(second)); CheckEqual(0, rig.DrawsLeft); } public void NeverRepeat_SkipsOverTheLastClipForHigherDraws() { Rig rig = new Rig(32, 32, Index(1, 3), Index(1, 2)); Sound[] clips = { Clip(), Clip(), Clip() }; SoundId id = rig.Mixer.Define(new SoundDefinition(clips, rig.Mixer.Master, neverRepeatClip: true)); rig.Mixer.Play(id); Voice second = rig.Mixer.Play(id); Check(clips[2] == rig.Module.SoundOf(second)); } public void NeverRepeat_TracksTheLastClipPerDefinition() { Rig rig = new Rig(32, 32, Index(1, 2), Index(1, 2)); Sound[] clips = { Clip(), Clip() }; SoundId first = rig.Mixer.Define(new SoundDefinition(clips, rig.Mixer.Master, neverRepeatClip: true)); SoundId other = rig.Mixer.Define(new SoundDefinition(clips, rig.Mixer.Master, neverRepeatClip: true)); rig.Mixer.Play(first); Voice voice = rig.Mixer.Play(other); Check(clips[1] == rig.Module.SoundOf(voice)); } public void NeverRepeat_WithOneClip_PlaysItAgain() { Rig rig = new Rig(); Sound clip = Clip(); SoundId id = rig.Mixer.Define( new SoundDefinition(new[] { clip }, rig.Mixer.Master, neverRepeatClip: true)); rig.Mixer.Play(id); Voice second = rig.Mixer.Play(id); Check(clip == rig.Module.SoundOf(second)); CheckEqual(0, rig.DrawsLeft); } public void StartsTheVoiceAtThePitchAndPanGiven() { Rig rig = new Rig(); SoundId id = rig.Define(rig.Mixer.Master); Voice voice = rig.Mixer.Play(id, pitch: 2f, pan: -0.5f); CheckEqual(2f, rig.Module.PitchOf(voice)); CheckEqual(-0.5f, rig.Module.PanOf(voice)); } public void ByDefault_IsAtNaturalPitchInTheCenter() { Rig rig = new Rig(); SoundId id = rig.Define(rig.Mixer.Master); Voice voice = rig.Mixer.Play(id); CheckEqual(Pitch.Natural, rig.Module.PitchOf(voice)); CheckEqual(Pan.Center, rig.Module.PanOf(voice)); } public void APitchOutOfRange_Throws() { Rig rig = new Rig(); SoundId id = rig.Define(rig.Mixer.Master); CheckThrow(typeof(ArgumentOutOfRangeException)); rig.Mixer.Play(id, pitch: 2000f); } public void APanOutOfRange_Throws() { Rig rig = new Rig(); SoundId id = rig.Define(rig.Mixer.Master); CheckThrow(typeof(ArgumentOutOfRangeException)); rig.Mixer.Play(id, pan: 1.5f); } public void WithoutVariation_TakesNoRandomNumbers() { Rig rig = new Rig(32, 32, Index(0, 2)); SoundId id = rig.Define(rig.Mixer.Master); rig.Mixer.Play(id, pitch: 2f); CheckEqual(1, rig.DrawsLeft); } public void VolumeVariation_DrawsADecibelChangeAndMultipliesTheVolume() { Rig rig = new Rig(32, 32, ScriptedGenerator.WordForUnit(0.5)); SoundId id = rig.Mixer.Define(new SoundDefinition( Clip(), rig.Mixer.Master, baseVolume: 0.5f, volumeDecibels: new Variation(-16, 0))); Voice voice = rig.Mixer.Play(id, volume: 2f); CheckEqual(Volume.FromDecibels(-8f), rig.Module.VolumeOf(voice)); CheckEqual(0, rig.DrawsLeft); } public void PitchVariation_DrawsASemitoneChangeAndMultipliesThePitch() { Rig rig = new Rig(32, 32, ScriptedGenerator.WordForUnit(0.5)); SoundId id = rig.Mixer.Define(new SoundDefinition( Clip(), rig.Mixer.Master, pitchSemitones: new Variation(0, 16))); Voice voice = rig.Mixer.Play(id, pitch: 2f); CheckTrue(Near(2f * Pitch.FromSemitones(8f), rig.Module.PitchOf(voice))); CheckEqual(0, rig.DrawsLeft); } public void PitchVariation_PastTheLimit_IsHeldAtTheLimit() { Rig rig = new Rig(32, 32, ScriptedGenerator.WordForUnit(0.0)); SoundId id = rig.Mixer.Define(new SoundDefinition( Clip(), rig.Mixer.Master, pitchSemitones: new Variation(16, 32))); Voice voice = rig.Mixer.Play(id, pitch: Pitch.Max); CheckEqual(Pitch.Max, rig.Module.PitchOf(voice)); } public void PitchVariation_BelowTheLimit_IsHeldAtTheLimit() { Rig rig = new Rig(32, 32, ScriptedGenerator.WordForUnit(0.0)); SoundId id = rig.Mixer.Define(new SoundDefinition( Clip(), rig.Mixer.Master, pitchSemitones: new Variation(-32, -16))); Voice voice = rig.Mixer.Play(id, pitch: Pitch.Min); CheckEqual(Pitch.Min, rig.Module.PitchOf(voice)); } public void VolumeAndPitchVariation_DrawVolumeFirst() { Rig rig = new Rig(32, 32, ScriptedGenerator.WordForUnit(0.0), ScriptedGenerator.WordForUnit(0.5)); SoundId id = rig.Mixer.Define(new SoundDefinition(Clip(), rig.Mixer.Master, volumeDecibels: new Variation(-16, 0), pitchSemitones: new Variation(0, 16))); Voice voice = rig.Mixer.Play(id); CheckEqual(Volume.FromDecibels(-16f), rig.Module.VolumeOf(voice)); CheckTrue(Near(Pitch.FromSemitones(8f), rig.Module.PitchOf(voice))); CheckEqual(0, rig.DrawsLeft); } public void VariationWithNoWidth_AppliesItsOneValueAndTakesNoRandomNumbers() { Rig rig = new Rig(32, 32, Index(0, 2)); SoundId id = rig.Mixer.Define(new SoundDefinition(Clip(), rig.Mixer.Master, volumeDecibels: -3, pitchSemitones: 5)); Voice voice = rig.Mixer.Play(id); CheckEqual(Volume.FromDecibels(-3f), rig.Module.VolumeOf(voice)); CheckEqual(Pitch.FromSemitones(5f), rig.Module.PitchOf(voice)); CheckEqual(1, rig.DrawsLeft); } public void ADroppedPlay_TakesNoVariationDraws() { Rig rig = new Rig(32, 32, ScriptedGenerator.WordForUnit(0.5)); SoundId id = rig.Mixer.Define(new SoundDefinition(Clip(), rig.Mixer.Master, maxInstances: 1, pitchSemitones: new Variation(0, 16))); rig.Mixer.Play(id); CheckTrue(rig.Mixer.Play(id).IsNone); CheckEqual(0, rig.DrawsLeft); } } public class SetPitchTests : TestFixture { public void ReplacesThePitchOfAPlayingVoice() { Rig rig = new Rig(); SoundId id = rig.Define(rig.Mixer.Master); Voice voice = rig.Mixer.Play(id); rig.Mixer.SetPitch(voice, 0.5f); CheckEqual(0.5f, rig.Module.PitchOf(voice)); } public void LeavesOtherVoicesAlone() { Rig rig = new Rig(); SoundId id = rig.Define(rig.Mixer.Master); Voice first = rig.Mixer.Play(id); Voice second = rig.Mixer.Play(id); rig.Mixer.SetPitch(second, 2f); CheckEqual(Pitch.Natural, rig.Module.PitchOf(first)); CheckEqual(2f, rig.Module.PitchOf(second)); } public void ReplacesTheVariationAsWell() { Rig rig = new Rig(32, 32, ScriptedGenerator.WordForUnit(0.5)); SoundId id = rig.Mixer.Define(new SoundDefinition( Clip(), rig.Mixer.Master, pitchSemitones: new Variation(0, 16))); Voice voice = rig.Mixer.Play(id); rig.Mixer.SetPitch(voice, 1f); CheckEqual(1f, rig.Module.PitchOf(voice)); } public void APitchOutOfRange_Throws() { Rig rig = new Rig(); Voice voice = rig.Mixer.Play(rig.Define(rig.Mixer.Master)); CheckThrow(typeof(ArgumentOutOfRangeException)); rig.Mixer.SetPitch(voice, 0f); } public void AVoiceFadingOut_IsLeftAlone() { Rig rig = Rig.WithFade(1f); Voice voice = rig.Mixer.Play(rig.Define(rig.Mixer.Master)); rig.Mixer.Stop(voice); rig.Mixer.SetPitch(voice, 2f); CheckEqual(Pitch.Natural, rig.Module.PitchOf(voice)); } public void AVoiceTheMixerDidNotStart_IsLeftAlone() { Rig rig = new Rig(); Voice outsider = rig.Module.Play(Clip(), volume: 1f); rig.Mixer.SetPitch(outsider, 2f); CheckEqual(Pitch.Natural, rig.Module.PitchOf(outsider)); } public void NoVoice_DoesNothing() { Rig rig = new Rig(); rig.Mixer.SetPitch(Voice.None, 2f); } } public class SetPanTests : TestFixture { public void MovesAPlayingVoice() { Rig rig = new Rig(); Voice voice = rig.Mixer.Play(rig.Define(rig.Mixer.Master)); rig.Mixer.SetPan(voice, 1f); CheckEqual(1f, rig.Module.PanOf(voice)); } public void APanOutOfRange_Throws() { Rig rig = new Rig(); Voice voice = rig.Mixer.Play(rig.Define(rig.Mixer.Master)); CheckThrow(typeof(ArgumentOutOfRangeException)); rig.Mixer.SetPan(voice, 2f); } public void AVoiceFadingOut_IsLeftAlone() { Rig rig = Rig.WithFade(1f); Voice voice = rig.Mixer.Play(rig.Define(rig.Mixer.Master)); rig.Mixer.Stop(voice); rig.Mixer.SetPan(voice, 1f); CheckEqual(Pan.Center, rig.Module.PanOf(voice)); } public void AVoiceTheMixerDidNotStart_IsLeftAlone() { Rig rig = new Rig(); Voice outsider = rig.Module.Play(Clip(), volume: 1f); rig.Mixer.SetPan(outsider, 1f); CheckEqual(Pan.Center, rig.Module.PanOf(outsider)); } public void NoVoice_DoesNothing() { Rig rig = new Rig(); rig.Mixer.SetPan(Voice.None, 1f); } } public class PlayAtPositionTests : TestFixture { public void PansByHowFarRightOfTheViewTheSoundIs() { Rig rig = new Rig(); SoundId id = rig.DefineFalloff(); Voice voice = rig.Mixer.Play(id, new Vector(5, 0)); CheckEqual(0.5f, rig.Module.PanOf(voice)); } public void HeightDoesNotPan() { Rig rig = new Rig(); SoundId id = rig.DefineFalloff(); Voice voice = rig.Mixer.Play(id, new Vector(0, 1)); CheckEqual(Pan.Center, rig.Module.PanOf(voice)); } public void ASoundWithNoFalloff_PlaysAtFullGainWhereverItIs() { Rig rig = new Rig(); SoundId id = rig.Define(rig.Mixer.Master); Voice voice = rig.Mixer.Play(id, new Vector(1000, 0)); CheckEqual(1f, rig.Module.VolumeOf(voice)); CheckEqual(1f, rig.Module.PanOf(voice)); } public void ASoundWithFalloff_IsQuieterTheFartherItIs() { Rig rig = new Rig(); SoundId id = rig.DefineFalloff(); Voice voice = rig.Mixer.Play(id, new Vector(6, 0)); CheckEqual(0.5f, rig.Module.VolumeOf(voice)); } public void Distance_IsFromTheCenterOfTheViewAlongBothAxes() { Rig rig = new Rig(); SoundId id = rig.DefineFalloff(); Voice voice = rig.Mixer.Play(id, new Vector(3, 4)); CheckEqual(0.625f, rig.Module.VolumeOf(voice)); } public void FalloffMultipliesTheVolumeOfThePlay() { Rig rig = new Rig(); SoundId id = rig.DefineFalloff(); Voice voice = rig.Mixer.Play(id, new Vector(6, 0), volume: 0.5f); CheckEqual(0.25f, rig.Module.VolumeOf(voice)); } public void TheViewMovesWhereDistanceIsMeasuredFrom() { Rig rig = new Rig(); SoundId id = rig.DefineFalloff(); rig.Mixer.View = new View(new Vector(100, 0), 10); Voice voice = rig.Mixer.Play(id, new Vector(106, 0)); CheckEqual(0.5f, rig.Module.VolumeOf(voice)); } public void ThePitchGiven_ReachesTheModule() { Rig rig = new Rig(); SoundId id = rig.DefineFalloff(); Voice voice = rig.Mixer.Play(id, Vector.Zero, pitch: 2f); CheckEqual(2f, rig.Module.PitchOf(voice)); } public void ASoundBeyondItsSilentDistance_IsDroppedWithoutPlaying() { Rig rig = new Rig(); SoundId id = rig.DefineFalloff(); CheckTrue(rig.Mixer.Play(id, new Vector(10, 0)).IsNone); CheckEqual(0, rig.Mixer.ActiveCount); } public void ADroppedSound_DoesNotStartTheRetriggerInterval() { Rig rig = new Rig(); SoundId id = rig.DefineFalloff(minRetriggerSeconds: 1f); rig.Mixer.Play(id, new Vector(50, 0)); CheckFalse(rig.Mixer.Play(id, Vector.Zero).IsNone); } public void ADroppedSound_TakesNoVoiceFromAFullMixer() { Rig rig = Rig.WithFade(0.5f, maxVoices: 1); SoundId id = rig.DefineFalloff(); Voice kept = rig.Mixer.Play(id, Vector.Zero); rig.Mixer.Play(id, new Vector(50, 0)); CheckTrue(rig.Mixer.IsPlaying(kept)); CheckEqual(0, rig.Mixer.FadingCount); } public void ACustomFalloff_ShapesTheGain() { Rig rig = new Rig(); SoundId id = rig.DefineWith(new FixedFalloff(0.75f)); Voice voice = rig.Mixer.Play(id, new Vector(1, 0)); CheckEqual(0.75f, rig.Module.VolumeOf(voice)); } public void ACustomFalloffThatReturnsZero_DropsThePlay() { Rig rig = new Rig(); SoundId id = rig.DefineWith(new FixedFalloff(0f)); CheckTrue(rig.Mixer.Play(id, Vector.Zero).IsNone); } public void ACustomFalloffThatReturnsMoreThanOne_Throws() { Rig rig = new Rig(); SoundId id = rig.DefineWith(new FixedFalloff(1.5f)); CheckThrow(typeof(InvalidOperationException)); rig.Mixer.Play(id, Vector.Zero); } public void ACustomFalloffThatReturnsLessThanZero_Throws() { Rig rig = new Rig(); SoundId id = rig.DefineWith(new FixedFalloff(-0.5f)); CheckThrow(typeof(InvalidOperationException)); rig.Mixer.Play(id, Vector.Zero); } public void ACustomFalloffThatReturnsNaN_Throws() { Rig rig = new Rig(); SoundId id = rig.DefineWith(new FixedFalloff(float.NaN)); CheckThrow(typeof(InvalidOperationException)); rig.Mixer.Play(id, Vector.Zero); } public void AnUnknownSound_Throws() { Rig rig = new Rig(); CheckThrow(typeof(ArgumentOutOfRangeException)); rig.Mixer.Play(new SoundId(3), Vector.Zero); } public void APositionThatIsNotANumber_Throws() { Rig rig = new Rig(); SoundId id = rig.DefineFalloff(); CheckThrow(typeof(ArgumentOutOfRangeException)); rig.Mixer.Play(id, Vector.NaN); } public void AVolumeOutOfRange_Throws() { Rig rig = new Rig(); SoundId id = rig.DefineFalloff(); CheckThrow(typeof(ArgumentOutOfRangeException)); rig.Mixer.Play(id, Vector.Zero, volume: -1f); } public void APitchOutOfRange_Throws() { Rig rig = new Rig(); SoundId id = rig.DefineFalloff(); CheckThrow(typeof(ArgumentOutOfRangeException)); rig.Mixer.Play(id, Vector.Zero, pitch: 0f); } } public class SetPositionTests : TestFixture { public void ChangesThePanAndTheGain() { Rig rig = new Rig(); SoundId id = rig.DefineFalloff(); Voice voice = rig.Mixer.Play(id, Vector.Zero); rig.Mixer.SetPosition(voice, new Vector(-6, 0)); CheckEqual(-0.6f, rig.Module.PanOf(voice)); CheckEqual(0.5f, rig.Module.VolumeOf(voice)); } public void MovingBeyondTheSilentDistance_SilencesTheVoiceButKeepsIt() { Rig rig = new Rig(); SoundId id = rig.DefineFalloff(); Voice voice = rig.Mixer.Play(id, Vector.Zero); rig.Mixer.SetPosition(voice, new Vector(50, 0)); CheckEqual(0f, rig.Module.VolumeOf(voice)); CheckTrue(rig.Mixer.IsPlaying(voice)); } public void AVoiceStartedWithoutAPosition_IsLeftAlone() { Rig rig = new Rig(); Voice voice = rig.Mixer.Play(rig.DefineFalloff()); rig.Mixer.SetPosition(voice, new Vector(6, 0)); CheckEqual(Pan.Center, rig.Module.PanOf(voice)); CheckEqual(1f, rig.Module.VolumeOf(voice)); } public void AVoiceFadingOut_IsLeftAlone() { Rig rig = Rig.WithFade(1f); Voice voice = rig.Mixer.Play(rig.DefineFalloff(), Vector.Zero); rig.Mixer.Stop(voice); rig.Mixer.SetPosition(voice, new Vector(6, 0)); CheckEqual(Pan.Center, rig.Module.PanOf(voice)); } public void AVoiceTheMixerDidNotStart_IsLeftAlone() { Rig rig = new Rig(); rig.Mixer.Play(rig.DefineFalloff(), Vector.Zero); Voice outsider = rig.Module.Play(Clip(), volume: 1f); rig.Mixer.SetPosition(outsider, new Vector(6, 0)); CheckEqual(Pan.Center, rig.Module.PanOf(outsider)); } public void NoVoice_DoesNothing() { Rig rig = new Rig(); rig.Mixer.SetPosition(Voice.None, Vector.Zero); } public void APositionThatIsNotANumber_Throws() { Rig rig = new Rig(); CheckThrow(typeof(ArgumentOutOfRangeException)); rig.Mixer.SetPosition(Voice.None, Vector.NaN); } } public class ViewPropertyTests : TestFixture { public void ByDefault_IsCenteredOnTheOriginAndOneUnitWide() { Rig rig = new Rig(); CheckEqual(0.0, rig.Mixer.View.Center.X.Value); CheckEqual(0.0, rig.Mixer.View.Center.Y.Value); CheckEqual(1.0, rig.Mixer.View.HalfWidth.Value); } public void SettingIt_ReturnsWhatWasSet() { Rig rig = new Rig(); rig.Mixer.View = new View(new Vector(7, 8), 9); CheckEqual(9.0, rig.Mixer.View.HalfWidth.Value); } public void ADefaultView_Throws() { Rig rig = new Rig(); CheckThrow(typeof(ArgumentException)); rig.Mixer.View = default(View); } public void APlacedVoiceIsRepannedAtTheNextUpdate() { Rig rig = new Rig(); Voice voice = rig.Mixer.Play(rig.DefineFalloff(), new Vector(5, 0)); rig.Mixer.View = new View(new Vector(10, 0), 10); CheckEqual(0.5f, rig.Module.PanOf(voice)); rig.Mixer.Update(0.01f); CheckEqual(-0.5f, rig.Module.PanOf(voice)); } public void APlacedVoiceChangesGainAtTheNextUpdate() { Rig rig = new Rig(); Voice voice = rig.Mixer.Play(rig.DefineFalloff(), Vector.Zero); rig.Mixer.View = new View(new Vector(-6, 0), 10); rig.Mixer.Update(0.01f); CheckEqual(0.5f, rig.Module.VolumeOf(voice)); } public void AVoiceStartedWithoutAPosition_IsNotRepanned() { Rig rig = new Rig(); Voice voice = rig.Mixer.Play(rig.DefineFalloff()); rig.Mixer.View = new View(new Vector(10, 0), 10); rig.Mixer.Update(0.01f); CheckEqual(Pan.Center, rig.Module.PanOf(voice)); CheckEqual(1f, rig.Module.VolumeOf(voice)); } public void APlacementThatHasNotChanged_IsNotSentAgain() { Rig rig = new Rig(); Voice voice = rig.Mixer.Play(rig.DefineFalloff(), new Vector(5, 0)); rig.Mixer.SetPan(voice, 1f); rig.Mixer.Update(0.01f); CheckEqual(1f, rig.Module.PanOf(voice)); } public void ThePlacementGainMultipliesTheBusGain() { Rig rig = new Rig(); SoundId id = rig.DefineFalloff(); Voice voice = rig.Mixer.Play(id, new Vector(6, 0)); rig.Mixer.Master.Volume = 0.5f; rig.Mixer.Update(0.01f); CheckEqual(0.25f, rig.Module.VolumeOf(voice)); } } public class StopTests : TestFixture { public void StopsTheVoiceOnTheModuleAndFreesItsRoom() { Rig rig = new Rig(); SoundId id = rig.Define(rig.Mixer.Master, maxInstances: 1); Voice voice = rig.Mixer.Play(id); rig.Mixer.Stop(voice); CheckFalse(rig.Module.IsPlaying(voice)); CheckEqual(0, rig.Mixer.ActiveCount); CheckFalse(rig.Mixer.Play(id).IsNone); } public void LeavesOtherVoicesPlaying() { Rig rig = new Rig(); SoundId id = rig.Define(rig.Mixer.Master); Voice first = rig.Mixer.Play(id); Voice second = rig.Mixer.Play(id); rig.Mixer.Stop(first); CheckTrue(rig.Module.IsPlaying(second)); CheckEqual(1, rig.Mixer.ActiveCount); } public void AVoiceAlreadyStopped_DoesNothing() { Rig rig = new Rig(); SoundId id = rig.Define(rig.Mixer.Master); Voice voice = rig.Mixer.Play(id); rig.Mixer.Play(id); rig.Mixer.Stop(voice); rig.Mixer.Stop(voice); CheckEqual(1, rig.Mixer.ActiveCount); } public void NoVoice_DoesNothing() { Rig rig = new Rig(); rig.Mixer.Stop(Voice.None); CheckEqual(0, rig.Mixer.ActiveCount); } public void AVoiceTheMixerDidNotStart_IsLeftPlaying() { Rig rig = new Rig(); Voice outsider = rig.Module.Play(Clip(), volume: 1f); rig.Mixer.Stop(outsider); CheckTrue(rig.Module.IsPlaying(outsider)); } } public class IsPlayingTests : TestFixture { public void IsTrueWhileThePlayLasts() { Rig rig = new Rig(); Voice voice = rig.Mixer.Play(rig.Define(rig.Mixer.Master)); CheckTrue(rig.Mixer.IsPlaying(voice)); } public void IsFalseOnceStopped() { Rig rig = new Rig(); Voice voice = rig.Mixer.Play(rig.Define(rig.Mixer.Master)); rig.Mixer.Stop(voice); CheckFalse(rig.Mixer.IsPlaying(voice)); } public void IsFalseForNoVoice() { CheckFalse(new Rig().Mixer.IsPlaying(Voice.None)); } } public class UpdateTests : TestFixture { public void UpdatesTheModule() { Rig rig = new Rig(); rig.Module.EndsVoicesOnUpdate = true; Voice voice = rig.Mixer.Play(rig.Define(rig.Mixer.Master)); rig.Mixer.Update(seconds: 0f); CheckFalse(rig.Module.IsPlaying(voice)); } public void FreesTheVoicesThatFinished() { Rig rig = new Rig(); SoundId id = rig.Define(rig.Mixer.Master); Voice finished = rig.Mixer.Play(id); rig.Mixer.Play(id); rig.Module.Finish(finished); rig.Mixer.Update(seconds: 0f); CheckEqual(1, rig.Mixer.ActiveCount); } public void FreesEveryVoiceWhenAllFinish() { Rig rig = new Rig(); rig.Module.EndsVoicesOnUpdate = true; SoundId id = rig.Define(rig.Mixer.Master); rig.Mixer.Play(id); rig.Mixer.Play(id); rig.Mixer.Play(id); rig.Mixer.Update(seconds: 0f); CheckEqual(0, rig.Mixer.ActiveCount); } public void ABusVolumeChange_ReachesOnlyThatBusesVoices() { Rig rig = new Rig(); Bus effects = rig.Mixer.AddBus("Effects"); Bus music = rig.Mixer.AddBus("Music"); Voice effect = rig.Mixer.Play(rig.Define(effects, baseVolume: 0.5f)); Voice tune = rig.Mixer.Play(rig.Define(music)); effects.Volume = 0.5f; rig.Mixer.Update(seconds: 0f); CheckEqual(0.25f, rig.Module.VolumeOf(effect)); CheckEqual(1f, rig.Module.VolumeOf(tune)); } public void MutingABus_SilencesItsVoicesAndUnmutingRestoresThem() { Rig rig = new Rig(); Bus effects = rig.Mixer.AddBus("Effects"); Voice voice = rig.Mixer.Play(rig.Define(effects, baseVolume: 0.5f)); effects.IsMuted = true; rig.Mixer.Update(seconds: 0f); CheckEqual(0f, rig.Module.VolumeOf(voice)); effects.IsMuted = false; rig.Mixer.Update(seconds: 0f); CheckEqual(0.5f, rig.Module.VolumeOf(voice)); } public void AMasterChange_ReachesEveryBus() { Rig rig = new Rig(); Bus effects = rig.Mixer.AddBus("Effects"); effects.Volume = 0.5f; Voice effect = rig.Mixer.Play(rig.Define(effects)); Voice direct = rig.Mixer.Play(rig.Define(rig.Mixer.Master)); rig.Mixer.Master.Volume = 0.5f; rig.Mixer.Update(seconds: 0f); CheckEqual(0.25f, rig.Module.VolumeOf(effect)); CheckEqual(0.5f, rig.Module.VolumeOf(direct)); } public void AChange_IsAppliedOnceAndThenLeftAlone() { Rig rig = new Rig(); Bus effects = rig.Mixer.AddBus("Effects"); Voice voice = rig.Mixer.Play(rig.Define(effects)); effects.Volume = 0.5f; rig.Mixer.Update(seconds: 0f); rig.Module.SetVolume(voice, 0.9f); rig.Mixer.Update(seconds: 0f); CheckEqual(0.9f, rig.Module.VolumeOf(voice)); } public void AChangeToABusWithNothingPlaying_IsHarmless() { Rig rig = new Rig(); Bus effects = rig.Mixer.AddBus("Effects"); effects.Volume = 0.5f; rig.Mixer.Update(seconds: 0f); CheckEqual(0, rig.Mixer.ActiveCount); } public void AChangeMadeBeforeAPlay_LeavesThePlaysGainUnchanged() { Rig rig = new Rig(); Bus effects = rig.Mixer.AddBus("Effects"); SoundId id = rig.Define(effects); effects.Volume = 0.5f; Voice voice = rig.Mixer.Play(id); rig.Mixer.Update(seconds: 0f); CheckEqual(0.5f, rig.Module.VolumeOf(voice)); } public void ABusChangeTheSameFrameAVoiceFinishes_LeavesTheFinishedVoiceAlone() { Rig rig = new Rig(); Bus effects = rig.Mixer.AddBus("Effects"); Voice voice = rig.Mixer.Play(rig.Define(effects)); rig.Module.Finish(voice); effects.Volume = 0.5f; rig.Mixer.Update(seconds: 0f); CheckEqual(0, rig.Mixer.ActiveCount); } public void ANegativeTime_Throws() { CheckThrow(typeof(ArgumentOutOfRangeException)); new Rig().Mixer.Update(seconds: -0.1f); } public void ATimeThatIsNotANumber_Throws() { CheckThrow(typeof(ArgumentOutOfRangeException)); new Rig().Mixer.Update(seconds: float.NaN); } public void AnInfiniteTime_Throws() { CheckThrow(typeof(ArgumentOutOfRangeException)); new Rig().Mixer.Update(seconds: float.PositiveInfinity); } } public class FadeSecondsTests : TestFixture { public void IsThirtyMillisecondsByDefault() { Mixer mixer = new Mixer( new NullModule(), new ScriptedGenerator()); CheckEqual(0.03f, mixer.FadeSeconds); } public void IsTheLengthGiven() { CheckEqual(0.5f, Rig.WithFade(0.5f).Mixer.FadeSeconds); } public void ANegativeLength_Throws() { CheckThrow(typeof(ArgumentOutOfRangeException)); Rig.WithFade(-0.1f); } public void ALengthThatIsNotANumber_Throws() { CheckThrow(typeof(ArgumentOutOfRangeException)); Rig.WithFade(float.NaN); } public void AnInfiniteLength_Throws() { CheckThrow(typeof(ArgumentOutOfRangeException)); Rig.WithFade(float.PositiveInfinity); } } public class StealingTests : TestFixture { public void AnEqualPriorityPlay_FadesOutTheQuietestVoiceBeforeTheOldest() { Rig rig = Rig.WithFade(0.5f, maxVoices: 2); SoundId id = rig.DefineFalloff(); Voice nearAndOlder = rig.Mixer.Play(id, Vector.Zero); Voice farAndNewer = rig.Mixer.Play(id, new Vector(6, 0)); Voice stealer = rig.Mixer.Play(id, Vector.Zero); CheckFalse(stealer.IsNone); CheckTrue(rig.Mixer.IsPlaying(nearAndOlder)); CheckFalse(rig.Mixer.IsPlaying(farAndNewer)); } public void AFainterPlay_IsDroppedWhenEveryVoiceOfEqualPriorityIsLouder() { Rig rig = Rig.WithFade(0.5f, maxVoices: 2); SoundId id = rig.DefineFalloff(); Voice unplaced = rig.Mixer.Play(id); Voice nearby = rig.Mixer.Play(id, new Vector(3, 0)); CheckTrue(rig.Mixer.Play(id, new Vector(6, 0)).IsNone); CheckTrue(rig.Mixer.IsPlaying(unplaced)); CheckTrue(rig.Mixer.IsPlaying(nearby)); CheckEqual(0, rig.Mixer.FadingCount); } public void AFaintPlay_StillFadesOutAQuieterVoiceOfLowerPriority() { Rig rig = Rig.WithFade(0.5f, maxVoices: 1); SoundId low = rig.Define(rig.Mixer.Master, priority: 0); SoundId high = rig.DefineFalloff(priority: 1); Voice kept = rig.Mixer.Play(low); Voice stealer = rig.Mixer.Play(high, new Vector(6, 0)); CheckFalse(stealer.IsNone); CheckFalse(rig.Mixer.IsPlaying(kept)); } public void AHigherPriorityPlay_FadesOutTheLowestPriorityVoice() { Rig rig = Rig.WithFade(0.5f, maxVoices: 2); SoundId low = rig.Define(rig.Mixer.Master, priority: 0); SoundId high = rig.Define(rig.Mixer.Master, priority: 1); Voice first = rig.Mixer.Play(low); Voice second = rig.Mixer.Play(low); Voice stealer = rig.Mixer.Play(high); CheckFalse(stealer.IsNone); CheckFalse(rig.Mixer.IsPlaying(first)); CheckTrue(rig.Mixer.IsPlaying(second)); CheckEqual(2, rig.Mixer.ActiveCount); CheckEqual(1, rig.Mixer.FadingCount); } public void ALowerPriorityPlay_IsDroppedAndTakesNothing() { Rig rig = Rig.WithFade(0.5f, maxVoices: 1); SoundId low = rig.Define(rig.Mixer.Master, priority: 0); SoundId high = rig.Define(rig.Mixer.Master, priority: 1); Voice kept = rig.Mixer.Play(high); CheckTrue(rig.Mixer.Play(low).IsNone); CheckTrue(rig.Mixer.IsPlaying(kept)); CheckEqual(0, rig.Mixer.FadingCount); } public void AnEqualPriorityPlay_FadesOutTheOldestVoice() { Rig rig = Rig.WithFade(0.5f, maxVoices: 2); SoundId id = rig.Define(rig.Mixer.Master); Voice oldest = rig.Mixer.Play(id); Voice newer = rig.Mixer.Play(id); Voice stealer = rig.Mixer.Play(id); CheckFalse(stealer.IsNone); CheckFalse(rig.Mixer.IsPlaying(oldest)); CheckTrue(rig.Mixer.IsPlaying(newer)); } public void TheLowestPriorityGoesEvenWhenItIsNewer() { Rig rig = Rig.WithFade(0.5f, maxVoices: 2); SoundId low = rig.Define(rig.Mixer.Master, priority: 0); SoundId high = rig.Define(rig.Mixer.Master, priority: 1); Voice olderHigh = rig.Mixer.Play(high); Voice newerLow = rig.Mixer.Play(low); rig.Mixer.Play(high); CheckTrue(rig.Mixer.IsPlaying(olderHigh)); CheckFalse(rig.Mixer.IsPlaying(newerLow)); } public void AgeIsByStartOrderNotByPositionInTheTable() { Rig rig = Rig.WithFade(0.5f, maxVoices: 3); SoundId id = rig.Define(rig.Mixer.Master); Voice first = rig.Mixer.Play(id); Voice second = rig.Mixer.Play(id); Voice third = rig.Mixer.Play(id); rig.Module.Finish(first); rig.Mixer.Update(seconds: 0f); Voice fourth = rig.Mixer.Play(id); rig.Mixer.Play(id); CheckFalse(rig.Mixer.IsPlaying(second)); CheckTrue(rig.Mixer.IsPlaying(third)); CheckTrue(rig.Mixer.IsPlaying(fourth)); } public void TheVictimStopsCountingAgainstItsDefinitionsCap() { Rig rig = Rig.WithFade(0.5f, maxVoices: 2); SoundId capped = rig.Define(rig.Mixer.Master, maxInstances: 1, priority: 0); SoundId filler = rig.Define(rig.Mixer.Master, priority: 0); SoundId high = rig.Define(rig.Mixer.Master, priority: 1); rig.Mixer.Play(capped); rig.Mixer.Play(filler); rig.Mixer.Play(high); CheckFalse(rig.Mixer.Play(capped).IsNone); } public void APlayDroppedByItsCap_StealsNothing() { Rig rig = Rig.WithFade(0.5f, maxVoices: 2); SoundId capped = rig.Define(rig.Mixer.Master, maxInstances: 1, priority: 5); SoundId low = rig.Define(rig.Mixer.Master, priority: 0); rig.Mixer.Play(low); rig.Mixer.Play(capped); CheckTrue(rig.Mixer.Play(capped).IsNone); CheckEqual(0, rig.Mixer.FadingCount); } public void APlayDroppedByItsRetriggerInterval_StealsNothing() { Rig rig = Rig.WithFade(0.5f, maxVoices: 2); SoundId slow = rig.Define(rig.Mixer.Master, minRetriggerSeconds: 10f, priority: 5); SoundId low = rig.Define(rig.Mixer.Master, priority: 0); rig.Mixer.Play(low); rig.Mixer.Play(slow); CheckTrue(rig.Mixer.Play(slow).IsNone); CheckEqual(0, rig.Mixer.FadingCount); } public void WhenTheModuleHasNoRoomUntilTheFadeEnds_TheStealingPlayIsDropped() { Rig rig = Rig.WithFade(0.5f, maxVoices: 1, moduleVoices: 1); SoundId low = rig.Define(rig.Mixer.Master, priority: 0); SoundId high = rig.Define(rig.Mixer.Master, priority: 1); rig.Mixer.Play(low); CheckTrue(rig.Mixer.Play(high).IsNone); CheckEqual(0, rig.Mixer.ActiveCount); CheckEqual(1, rig.Mixer.FadingCount); rig.Mixer.Update(seconds: 0.5f); CheckFalse(rig.Mixer.Play(high).IsNone); } public void WithoutAFade_TheVictimIsCutAtOnce() { Rig rig = new Rig(maxVoices: 1); SoundId id = rig.Define(rig.Mixer.Master); Voice victim = rig.Mixer.Play(id); rig.Mixer.Play(id); CheckFalse(rig.Module.IsPlaying(victim)); CheckEqual(0, rig.Mixer.FadingCount); } } public class FadeOutTests : TestFixture { public void Stop_FadesTheVoiceInsteadOfCuttingIt() { Rig rig = Rig.WithFade(0.5f); Voice voice = rig.Mixer.Play(rig.Define(rig.Mixer.Master)); rig.Mixer.Stop(voice); CheckFalse(rig.Mixer.IsPlaying(voice)); CheckTrue(rig.Module.IsPlaying(voice)); CheckEqual(0, rig.Mixer.ActiveCount); CheckEqual(1, rig.Mixer.FadingCount); } public void Stop_FreesTheDefinitionsCapAtOnce() { Rig rig = Rig.WithFade(0.5f); SoundId id = rig.Define(rig.Mixer.Master, maxInstances: 1); rig.Mixer.Stop(rig.Mixer.Play(id)); CheckFalse(rig.Mixer.Play(id).IsNone); } public void Update_LowersTheGainInProportionToTheTimeElapsed() { Rig rig = Rig.WithFade(0.5f); Voice voice = rig.Mixer.Play(rig.Define(rig.Mixer.Master)); rig.Mixer.Stop(voice); rig.Mixer.Update(seconds: 0.125f); CheckEqual(0.75f, rig.Module.VolumeOf(voice)); rig.Mixer.Update(seconds: 0.125f); CheckEqual(0.5f, rig.Module.VolumeOf(voice)); } public void Update_StopsTheVoiceOnceTheFadeHasRun() { Rig rig = Rig.WithFade(0.5f); Voice voice = rig.Mixer.Play(rig.Define(rig.Mixer.Master)); rig.Mixer.Stop(voice); rig.Mixer.Update(seconds: 0.25f); rig.Mixer.Update(seconds: 0.25f); CheckFalse(rig.Module.IsPlaying(voice)); CheckEqual(0, rig.Mixer.FadingCount); } public void Update_StopsAtOnceWhenTheFrameOutlastsTheFade() { Rig rig = Rig.WithFade(0.5f); Voice voice = rig.Mixer.Play(rig.Define(rig.Mixer.Master)); rig.Mixer.Stop(voice); rig.Mixer.Update(seconds: 2f); CheckFalse(rig.Module.IsPlaying(voice)); CheckEqual(0, rig.Mixer.FadingCount); } public void Update_DropsAFadingVoiceTheModuleSaysHasEnded() { Rig rig = Rig.WithFade(0.5f); Voice voice = rig.Mixer.Play(rig.Define(rig.Mixer.Master)); rig.Mixer.Stop(voice); rig.Module.Finish(voice); rig.Mixer.Update(seconds: 0.125f); CheckEqual(0, rig.Mixer.FadingCount); } public void ABusChangeMidFade_DoesNotDisturbTheFade() { Rig rig = Rig.WithFade(0.5f); Bus effects = rig.Mixer.AddBus("Effects"); Voice voice = rig.Mixer.Play(rig.Define(effects)); rig.Mixer.Stop(voice); effects.Volume = 0.5f; rig.Mixer.Update(seconds: 0.125f); CheckEqual(0.75f, rig.Module.VolumeOf(voice)); } public void Stop_OnAVoiceAlreadyFading_DoesNothing() { Rig rig = Rig.WithFade(0.5f); Voice voice = rig.Mixer.Play(rig.Define(rig.Mixer.Master)); rig.Mixer.Stop(voice); rig.Mixer.Stop(voice); CheckEqual(1, rig.Mixer.FadingCount); } public void WithNoRoomToFade_TheVoiceIsCut() { Rig rig = Rig.WithFade(0.5f, maxVoices: 1); SoundId id = rig.Define(rig.Mixer.Master); rig.Mixer.Stop(rig.Mixer.Play(id)); Voice cut = rig.Mixer.Play(id); rig.Mixer.Stop(cut); CheckFalse(rig.Module.IsPlaying(cut)); CheckEqual(1, rig.Mixer.FadingCount); } public void IsPlayingIsFalseForAFadingVoiceEvenBeforeUpdate() { Rig rig = Rig.WithFade(0.5f); Voice voice = rig.Mixer.Play(rig.Define(rig.Mixer.Master)); rig.Mixer.Stop(voice); CheckFalse(rig.Mixer.IsPlaying(voice)); } public void Stop_UsesTheDefinitionsFadeOutInPlaceOfTheMixers() { Rig rig = Rig.WithFade(0.5f); Voice voice = rig.Mixer.Play(rig.DefineFading(fadeInSeconds: 0f, fadeOutSeconds: 2f)); rig.Mixer.Stop(voice); rig.Mixer.Update(seconds: 0.5f); CheckEqual(0.75f, rig.Module.VolumeOf(voice)); CheckTrue(rig.Module.IsPlaying(voice)); } public void Stop_WithADefinitionFadeOfZero_CutsAtOnce() { Rig rig = Rig.WithFade(0.5f); Voice voice = rig.Mixer.Play(rig.DefineFading(fadeInSeconds: 0f, fadeOutSeconds: 0f)); rig.Mixer.Stop(voice); CheckFalse(rig.Module.IsPlaying(voice)); CheckEqual(0, rig.Mixer.FadingCount); } public void Stop_WithoutADefinitionFade_UsesTheMixers() { Rig rig = Rig.WithFade(0.5f); Voice voice = rig.Mixer.Play(rig.DefineFading(fadeInSeconds: 1f)); rig.Mixer.Update(seconds: 1f); rig.Mixer.Stop(voice); rig.Mixer.Update(seconds: 0.25f); CheckEqual(0.5f, rig.Module.VolumeOf(voice)); } public void AStolenVoice_FadesOverTheMixersFadeNotItsDefinitions() { Rig rig = Rig.WithFade(0.5f, maxVoices: 1); Voice slow = rig.Mixer.Play(rig.DefineFading(fadeInSeconds: 0f, fadeOutSeconds: 10f)); rig.Mixer.Play(rig.Define(rig.Mixer.Master)); rig.Mixer.Update(seconds: 0.25f); CheckEqual(0.5f, rig.Module.VolumeOf(slow)); } } public class StopWithFadeTests : TestFixture { public void FadesOverTheGivenTimeWhateverTheDefinitionSays() { Rig rig = Rig.WithFade(0.5f); Voice voice = rig.Mixer.Play(rig.DefineFading(fadeInSeconds: 0f, fadeOutSeconds: 10f)); rig.Mixer.Stop(voice, fadeSeconds: 4f); rig.Mixer.Update(seconds: 1f); CheckEqual(0.75f, rig.Module.VolumeOf(voice)); } public void ZeroCutsAtOnce() { Rig rig = Rig.WithFade(0.5f); Voice voice = rig.Mixer.Play(rig.Define(rig.Mixer.Master)); rig.Mixer.Stop(voice, fadeSeconds: 0f); CheckFalse(rig.Module.IsPlaying(voice)); CheckEqual(0, rig.Mixer.FadingCount); } public void OnAVoiceAlreadyFading_DoesNothing() { Rig rig = Rig.WithFade(0.5f); Voice voice = rig.Mixer.Play(rig.Define(rig.Mixer.Master)); rig.Mixer.Stop(voice); rig.Mixer.Stop(voice, fadeSeconds: 0f); CheckTrue(rig.Module.IsPlaying(voice)); CheckEqual(1, rig.Mixer.FadingCount); } public void OnAVoiceTheMixerDidNotStart_LeavesTheOthersPlaying() { Rig rig = Rig.WithFade(0.5f); Voice playing = rig.Mixer.Play(rig.Define(rig.Mixer.Master)); rig.Mixer.Stop(Voice.None, fadeSeconds: 0f); CheckTrue(rig.Mixer.IsPlaying(playing)); CheckEqual(1, rig.Mixer.ActiveCount); } public void ANegativeTime_Throws() { Rig rig = Rig.WithFade(0.5f); CheckThrow(typeof(ArgumentOutOfRangeException)); rig.Mixer.Stop(Voice.None, fadeSeconds: -1f); } public void ANonFiniteTime_Throws() { Rig rig = Rig.WithFade(0.5f); CheckThrow(typeof(ArgumentOutOfRangeException)); rig.Mixer.Stop(Voice.None, fadeSeconds: float.NaN); } } public class PauseTests : TestFixture { public void APausedBus_PausesItsVoicesAtTheNextUpdate() { Rig rig = Rig.WithFade(0.5f); Bus music = rig.Mixer.AddBus("Music"); Voice voice = rig.Mixer.Play(rig.Define(music)); music.IsPaused = true; CheckFalse(rig.Module.PausedOf(voice)); rig.Mixer.Update(seconds: 0.1f); CheckTrue(rig.Module.PausedOf(voice)); CheckTrue(rig.Mixer.IsPlaying(voice)); } public void APausedBus_LeavesTheOtherBusesPlaying() { Rig rig = Rig.WithFade(0.5f); Bus music = rig.Mixer.AddBus("Music"); Bus effects = rig.Mixer.AddBus("Effects"); Voice effect = rig.Mixer.Play(rig.Define(effects)); rig.Mixer.Play(rig.Define(music)); music.IsPaused = true; rig.Mixer.Update(seconds: 0.1f); CheckFalse(rig.Module.PausedOf(effect)); } public void APausedMaster_PausesEveryBus() { Rig rig = Rig.WithFade(0.5f); Bus music = rig.Mixer.AddBus("Music"); Voice onMusic = rig.Mixer.Play(rig.Define(music)); Voice onMaster = rig.Mixer.Play(rig.Define(rig.Mixer.Master)); rig.Mixer.Master.IsPaused = true; rig.Mixer.Update(seconds: 0.1f); CheckTrue(rig.Module.PausedOf(onMusic)); CheckTrue(rig.Module.PausedOf(onMaster)); } public void Resuming_LetsTheVoiceGoAgain() { Rig rig = Rig.WithFade(0.5f); Bus music = rig.Mixer.AddBus("Music"); Voice voice = rig.Mixer.Play(rig.Define(music)); music.IsPaused = true; rig.Mixer.Update(seconds: 0.1f); music.IsPaused = false; rig.Mixer.Update(seconds: 0.1f); CheckFalse(rig.Module.PausedOf(voice)); } public void Resuming_OneOfTwoPausedBuses_LeavesTheOtherPaused() { Rig rig = Rig.WithFade(0.5f); Bus music = rig.Mixer.AddBus("Music"); Bus ambience = rig.Mixer.AddBus("Ambience"); Voice onMusic = rig.Mixer.Play(rig.Define(music)); Voice onAmbience = rig.Mixer.Play(rig.Define(ambience)); music.IsPaused = true; ambience.IsPaused = true; rig.Mixer.Update(seconds: 0.1f); music.IsPaused = false; rig.Mixer.Update(seconds: 0.1f); CheckFalse(rig.Module.PausedOf(onMusic)); CheckTrue(rig.Module.PausedOf(onAmbience)); } public void Resuming_AMasterThatWasPausedAsWellAsItsBus_KeepsTheBusVoicePaused() { Rig rig = Rig.WithFade(0.5f); Bus music = rig.Mixer.AddBus("Music"); Voice voice = rig.Mixer.Play(rig.Define(music)); music.IsPaused = true; rig.Mixer.Master.IsPaused = true; rig.Mixer.Update(seconds: 0.1f); rig.Mixer.Master.IsPaused = false; rig.Mixer.Update(seconds: 0.1f); CheckTrue(rig.Module.PausedOf(voice)); } public void APlayOnAPausedBus_StartsPausedAndSilent() { Rig rig = Rig.WithFade(0.5f); Bus music = rig.Mixer.AddBus("Music"); music.IsPaused = true; Voice voice = rig.Mixer.Play(rig.Define(music)); CheckTrue(rig.Module.PausedOf(voice)); CheckEqual(0f, rig.Module.VolumeOf(voice)); } public void APlayOnAPausedBus_SoundsAtItsOwnGainOnceResumed() { Rig rig = Rig.WithFade(0.5f); Bus music = rig.Mixer.AddBus("Music"); music.Volume = 0.5f; music.IsPaused = true; Voice voice = rig.Mixer.Play(rig.Define(music), volume: 0.5f); music.IsPaused = false; rig.Mixer.Update(seconds: 0.1f); CheckEqual(0.25f, rig.Module.VolumeOf(voice)); CheckFalse(rig.Module.PausedOf(voice)); } public void APausedVoice_KeepsItsDefinitionsCount() { Rig rig = Rig.WithFade(0.5f); Bus music = rig.Mixer.AddBus("Music"); SoundId tune = rig.Define(music, maxInstances: 1); rig.Mixer.Play(tune); music.IsPaused = true; rig.Mixer.Update(seconds: 0.1f); CheckTrue(rig.Mixer.Play(tune).IsNone); } public void AFadeIn_WaitsWhileTheVoiceIsPaused() { Rig rig = Rig.WithFade(0.5f); Bus music = rig.Mixer.AddBus("Music"); Voice voice = rig.Mixer.Play(rig.DefineFading(fadeInSeconds: 2f, bus: music)); rig.Mixer.Update(seconds: 1f); music.IsPaused = true; rig.Mixer.Update(seconds: 5f); music.IsPaused = false; rig.Mixer.Update(seconds: 0.5f); CheckEqual(0.75f, rig.Module.VolumeOf(voice)); } public void Stop_OnAPausedVoice_StillFadesItOut() { Rig rig = Rig.WithFade(0.5f); Bus music = rig.Mixer.AddBus("Music"); Voice voice = rig.Mixer.Play(rig.Define(music)); music.IsPaused = true; rig.Mixer.Update(seconds: 0.1f); rig.Mixer.Stop(voice); rig.Mixer.Update(seconds: 0.5f); CheckFalse(rig.Module.IsPlaying(voice)); CheckEqual(0, rig.Mixer.FadingCount); } } public class BackgroundTests : TestFixture { public void ByDefault_TheGameIsInTheForegroundAndTheBackgroundMutes() { Mixer mixer = new Rig().Mixer; CheckFalse(mixer.IsInBackground); CheckEqual(BackgroundPolicy.Mute, mixer.BackgroundPolicy); CheckEqual(0.25f, mixer.DuckGain); CheckEqual(0.1f, mixer.BackgroundFadeSeconds); } public void Mute_LowersTheGainInProportionToTheTimeElapsed() { Rig rig = new Rig(); Voice voice = rig.Mixer.Play(rig.Define(rig.Mixer.Master)); rig.Mixer.IsInBackground = true; rig.Mixer.Update(seconds: 0.05f); CheckEqual(0.5f, rig.Module.VolumeOf(voice)); rig.Mixer.Update(seconds: 0.05f); CheckEqual(0f, rig.Module.VolumeOf(voice)); } public void Mute_StopsAtSilenceWhenTheFrameOutlastsTheFade() { Rig rig = new Rig(); Voice voice = rig.Mixer.Play(rig.Define(rig.Mixer.Master)); rig.Mixer.IsInBackground = true; rig.Mixer.Update(seconds: 5f); CheckEqual(0f, rig.Module.VolumeOf(voice)); } public void Mute_LeavesTheVoiceGoingUnpaused() { Rig rig = new Rig(); Voice voice = rig.Mixer.Play(rig.Define(rig.Mixer.Master)); rig.Mixer.IsInBackground = true; rig.Mixer.Update(seconds: 1f); CheckTrue(rig.Mixer.IsPlaying(voice)); CheckFalse(rig.Module.PausedOf(voice)); } public void ComingBack_RaisesTheGainAgain() { Rig rig = new Rig(); Voice voice = rig.Mixer.Play(rig.Define(rig.Mixer.Master)); rig.Mixer.IsInBackground = true; rig.Mixer.Update(seconds: 1f); rig.Mixer.IsInBackground = false; rig.Mixer.Update(seconds: 0.05f); CheckEqual(0.5f, rig.Module.VolumeOf(voice)); rig.Mixer.Update(seconds: 0.05f); CheckEqual(1f, rig.Module.VolumeOf(voice)); } public void Duck_LowersTheGainToTheDuckGain() { Rig rig = new Rig(); rig.Mixer.BackgroundPolicy = BackgroundPolicy.Duck; Voice voice = rig.Mixer.Play(rig.Define(rig.Mixer.Master)); rig.Mixer.IsInBackground = true; rig.Mixer.Update(seconds: 5f); CheckEqual(0.25f, rig.Module.VolumeOf(voice)); } public void Duck_UsesTheDuckGainSet() { Rig rig = new Rig(); rig.Mixer.BackgroundPolicy = BackgroundPolicy.Duck; rig.Mixer.DuckGain = 0.5f; Voice voice = rig.Mixer.Play(rig.Define(rig.Mixer.Master)); rig.Mixer.IsInBackground = true; rig.Mixer.Update(seconds: 5f); CheckEqual(0.5f, rig.Module.VolumeOf(voice)); } public void Duck_MovesAtThePaceOfAFullSwing() { Rig rig = new Rig(); rig.Mixer.BackgroundPolicy = BackgroundPolicy.Duck; rig.Mixer.DuckGain = 0.5f; Voice voice = rig.Mixer.Play(rig.Define(rig.Mixer.Master)); rig.Mixer.IsInBackground = true; rig.Mixer.Update(seconds: 0.025f); CheckEqual(0.75f, rig.Module.VolumeOf(voice)); } public void Duck_ScalesTheBusAndDefinitionGains() { Rig rig = new Rig(); rig.Mixer.BackgroundPolicy = BackgroundPolicy.Duck; Bus music = rig.Mixer.AddBus("Music"); music.Volume = 0.5f; Voice voice = rig.Mixer.Play(rig.Define(music), volume: 0.5f); rig.Mixer.IsInBackground = true; rig.Mixer.Update(seconds: 5f); CheckEqual(0.0625f, rig.Module.VolumeOf(voice)); } public void Continue_LeavesTheGainAlone() { Rig rig = new Rig(); rig.Mixer.BackgroundPolicy = BackgroundPolicy.Continue; Voice voice = rig.Mixer.Play(rig.Define(rig.Mixer.Master)); rig.Mixer.IsInBackground = true; rig.Mixer.Update(seconds: 5f); CheckEqual(1f, rig.Module.VolumeOf(voice)); CheckFalse(rig.Module.PausedOf(voice)); } public void Pause_FreezesTheVoicesAtOnceAndKeepsTheirGain() { Rig rig = new Rig(); rig.Mixer.BackgroundPolicy = BackgroundPolicy.Pause; Voice voice = rig.Mixer.Play(rig.Define(rig.Mixer.Master)); rig.Mixer.IsInBackground = true; CheckTrue(rig.Module.PausedOf(voice)); rig.Mixer.Update(seconds: 5f); CheckEqual(1f, rig.Module.VolumeOf(voice)); CheckTrue(rig.Mixer.IsPlaying(voice)); } public void Pause_ResumesTheVoicesWhenTheGameComesBack() { Rig rig = new Rig(); rig.Mixer.BackgroundPolicy = BackgroundPolicy.Pause; Voice voice = rig.Mixer.Play(rig.Define(rig.Mixer.Master)); rig.Mixer.IsInBackground = true; rig.Mixer.IsInBackground = false; CheckFalse(rig.Module.PausedOf(voice)); } public void Pause_LeavesAVoiceOfAPausedBusPausedWhenTheGameComesBack() { Rig rig = new Rig(); rig.Mixer.BackgroundPolicy = BackgroundPolicy.Pause; Bus music = rig.Mixer.AddBus("Music"); Voice voice = rig.Mixer.Play(rig.Define(music)); music.IsPaused = true; rig.Mixer.Update(seconds: 0.1f); rig.Mixer.IsInBackground = true; rig.Mixer.IsInBackground = false; CheckTrue(rig.Module.PausedOf(voice)); } public void Pause_StartsAPlayInTheBackgroundPaused() { Rig rig = new Rig(); rig.Mixer.BackgroundPolicy = BackgroundPolicy.Pause; rig.Mixer.IsInBackground = true; Voice voice = rig.Mixer.Play(rig.Define(rig.Mixer.Master)); CheckTrue(rig.Module.PausedOf(voice)); } public void Mute_StartsAPlayInTheBackgroundSilent() { Rig rig = new Rig(); rig.Mixer.IsInBackground = true; rig.Mixer.Update(seconds: 1f); Voice voice = rig.Mixer.Play(rig.Define(rig.Mixer.Master)); CheckEqual(0f, rig.Module.VolumeOf(voice)); } public void ChangingThePolicyInTheBackground_AppliesTheNewOne() { Rig rig = new Rig(); Voice voice = rig.Mixer.Play(rig.Define(rig.Mixer.Master)); rig.Mixer.IsInBackground = true; rig.Mixer.Update(seconds: 1f); rig.Mixer.BackgroundPolicy = BackgroundPolicy.Pause; rig.Mixer.Update(seconds: 1f); CheckTrue(rig.Module.PausedOf(voice)); CheckEqual(1f, rig.Module.VolumeOf(voice)); } public void AZeroFade_ChangesTheGainAtTheNextUpdate() { Rig rig = new Rig(); rig.Mixer.BackgroundFadeSeconds = 0f; Voice voice = rig.Mixer.Play(rig.Define(rig.Mixer.Master)); rig.Mixer.IsInBackground = true; rig.Mixer.Update(seconds: 0f); CheckEqual(0f, rig.Module.VolumeOf(voice)); } public void ABusChangeInTheBackground_KeepsTheBackgroundScale() { Rig rig = new Rig(); rig.Mixer.BackgroundPolicy = BackgroundPolicy.Duck; Bus music = rig.Mixer.AddBus("Music"); Voice voice = rig.Mixer.Play(rig.Define(music)); rig.Mixer.IsInBackground = true; rig.Mixer.Update(seconds: 5f); music.Volume = 0.5f; rig.Mixer.Update(seconds: 0.1f); CheckEqual(0.125f, rig.Module.VolumeOf(voice)); } public void ADuckGainBelowZero_Throws() { CheckThrow(typeof(ArgumentOutOfRangeException)); new Rig().Mixer.DuckGain = -0.1f; } public void ADuckGainAboveOne_Throws() { CheckThrow(typeof(ArgumentOutOfRangeException)); new Rig().Mixer.DuckGain = 1.5f; } public void ANegativeBackgroundFade_Throws() { CheckThrow(typeof(ArgumentOutOfRangeException)); new Rig().Mixer.BackgroundFadeSeconds = -1f; } public void APolicyThatIsNotOneOfTheFour_Throws() { CheckThrow(typeof(ArgumentOutOfRangeException)); new Rig().Mixer.BackgroundPolicy = (BackgroundPolicy)99; } } public class FadeInTests : TestFixture { public void Play_StartsTheVoiceSilent() { Rig rig = Rig.WithFade(0.5f); Voice voice = rig.Mixer.Play(rig.DefineFading(fadeInSeconds: 2f)); CheckEqual(0f, rig.Module.VolumeOf(voice)); } public void Play_WithoutAFadeIn_StartsAtFullVolume() { Rig rig = Rig.WithFade(0.5f); Voice voice = rig.Mixer.Play(rig.DefineFading(fadeInSeconds: 0f)); CheckEqual(1f, rig.Module.VolumeOf(voice)); } public void Update_RaisesTheGainInProportionToTheTimeElapsed() { Rig rig = Rig.WithFade(0.5f); Voice voice = rig.Mixer.Play(rig.DefineFading(fadeInSeconds: 2f)); rig.Mixer.Update(seconds: 0.5f); CheckEqual(0.25f, rig.Module.VolumeOf(voice)); rig.Mixer.Update(seconds: 0.5f); CheckEqual(0.5f, rig.Module.VolumeOf(voice)); } public void Update_EndsAtExactlyFullVolumeWhenTheFrameOutlastsTheFade() { Rig rig = Rig.WithFade(0.5f); Voice voice = rig.Mixer.Play(rig.DefineFading(fadeInSeconds: 2f)); rig.Mixer.Update(seconds: 5f); CheckEqual(1f, rig.Module.VolumeOf(voice)); } public void Update_SendsNothingMoreOnceTheFadeHasEnded() { Rig rig = Rig.WithFade(0.5f); Voice voice = rig.Mixer.Play(rig.DefineFading(fadeInSeconds: 1f)); rig.Mixer.Update(seconds: 1f); rig.Module.SetVolume(voice, 0.5f); rig.Mixer.Update(seconds: 1f); CheckEqual(0.5f, rig.Module.VolumeOf(voice)); } public void TheRampMultipliesTheBusAndTheDefinitionsVolume() { Rig rig = Rig.WithFade(0.5f); Bus music = rig.Mixer.AddBus("Music"); music.Volume = 0.5f; Voice voice = rig.Mixer.Play(rig.DefineFading(fadeInSeconds: 2f, bus: music), volume: 0.5f); rig.Mixer.Update(seconds: 1f); CheckEqual(0.125f, rig.Module.VolumeOf(voice)); } public void ABusChangeMidFade_KeepsTheRamp() { Rig rig = Rig.WithFade(0.5f); Bus music = rig.Mixer.AddBus("Music"); Voice voice = rig.Mixer.Play(rig.DefineFading(fadeInSeconds: 2f, bus: music)); rig.Mixer.Update(seconds: 1f); music.Volume = 0.5f; rig.Mixer.Update(seconds: 0.5f); CheckEqual(0.375f, rig.Module.VolumeOf(voice)); } public void APlacedVoice_RampsItsAttenuatedGain() { Rig rig = Rig.WithFade(0.5f); rig.Mixer.View = new View(Vector.Zero, 10); SoundId id = rig.Mixer.Define(new SoundDefinition(Clip(), rig.Mixer.Master, falloff: new FixedFalloff(0.5f), fadeInSeconds: 2f)); Voice voice = rig.Mixer.Play(id, new Vector(1, 0)); rig.Mixer.Update(seconds: 1f); CheckEqual(0.25f, rig.Module.VolumeOf(voice)); } public void Stop_MidFadeIn_FadesOutFromTheCurrentLevel() { Rig rig = Rig.WithFade(1f); Voice voice = rig.Mixer.Play(rig.DefineFading(fadeInSeconds: 2f, fadeOutSeconds: 1f)); rig.Mixer.Update(seconds: 1f); rig.Mixer.Stop(voice); rig.Mixer.Update(seconds: 0.5f); CheckEqual(0.25f, rig.Module.VolumeOf(voice)); } public void AFadingInVoice_IsPlaying() { Rig rig = Rig.WithFade(0.5f); Voice voice = rig.Mixer.Play(rig.DefineFading(fadeInSeconds: 2f)); CheckTrue(rig.Mixer.IsPlaying(voice)); } public void ACrossfade_RaisesTheNewTrackAsTheOldOneFades() { Rig rig = Rig.WithFade(0.5f); SoundId tune = rig.DefineFading(fadeInSeconds: 2f, fadeOutSeconds: 2f); Voice old = rig.Mixer.Play(tune); rig.Mixer.Update(seconds: 2f); rig.Mixer.Stop(old); Voice next = rig.Mixer.Play(tune); rig.Mixer.Update(seconds: 1f); CheckEqual(0.5f, rig.Module.VolumeOf(old)); CheckEqual(0.5f, rig.Module.VolumeOf(next)); } } public class SustainTests : TestFixture { public void TheFirstReport_StartsALoopingVoiceAtTheNextUpdate() { Rig rig = new Rig(); SoundId id = rig.DefineSustained(); rig.Mixer.Sustain(id); CheckEqual(Voice.None, rig.Mixer.SustainedVoiceOf(id)); rig.Mixer.Update(seconds: 0f); Voice voice = rig.Mixer.SustainedVoiceOf(id); CheckFalse(voice.IsNone); CheckTrue(rig.Module.LoopOf(voice).IsLooping); CheckEqual(1f, rig.Module.VolumeOf(voice)); } public void ManyReportsInAFrame_PlayOneVoice() { Rig rig = new Rig(); SoundId id = rig.DefineSustained(); rig.Mixer.Sustain(id); rig.Mixer.Sustain(id); rig.Mixer.Sustain(id); rig.Mixer.Update(seconds: 0f); CheckEqual(1, rig.Mixer.ActiveCount); } public void ReportsOfTwoDefinitions_PlayTwoVoices() { Rig rig = new Rig(); SoundId first = rig.DefineSustained(); SoundId second = rig.DefineSustained(); rig.Mixer.Sustain(first); rig.Mixer.Sustain(second); rig.Mixer.Update(seconds: 0f); CheckEqual(2, rig.Mixer.ActiveCount); } public void TwoReports_CombineAsPowers() { Rig rig = new Rig(); SoundId id = rig.DefineSustained(); rig.Mixer.Sustain(id, volume: 0.5f); rig.Mixer.Sustain(id, volume: 0.5f); rig.Mixer.Update(seconds: 0f); CheckTrue(Near(0.70710678f, rig.Module.VolumeOf(rig.Mixer.SustainedVoiceOf(id)))); } public void ManyLoudReports_AreHeldToFullVolume() { Rig rig = new Rig(); SoundId id = rig.DefineSustained(); for (int i = 0; i < 4; ++i) { rig.Mixer.Sustain(id); } rig.Mixer.Update(seconds: 0f); CheckEqual(1f, rig.Module.VolumeOf(rig.Mixer.SustainedVoiceOf(id))); } public void AmplifyingReports_AreHeldToTheLoudest() { Rig rig = new Rig(); SoundId id = rig.DefineSustained(); rig.Mixer.Sustain(id, volume: 2f); rig.Mixer.Sustain(id, volume: 2f); rig.Mixer.Update(seconds: 0f); CheckEqual(2f, rig.Module.VolumeOf(rig.Mixer.SustainedVoiceOf(id))); } public void TheBaseVolumeAndBusApplyToTheCombinedLoudness() { Rig rig = new Rig(); Bus music = rig.Mixer.AddBus("Music"); music.Volume = 0.5f; SoundId id = rig.DefineSustained(bus: music); rig.Mixer.Sustain(id, volume: 0.5f); rig.Mixer.Update(seconds: 0f); CheckEqual(0.25f, rig.Module.VolumeOf(rig.Mixer.SustainedVoiceOf(id))); } public void ThePan_IsAverageWeightedByLoudness() { Rig rig = new Rig(); SoundId id = rig.DefineSustained(); rig.Mixer.Sustain(id, volume: 1f, pan: 1f); rig.Mixer.Sustain(id, volume: 0.5f, pan: -1f); rig.Mixer.Update(seconds: 0f); CheckTrue(Near(1f / 3f, rig.Module.PanOf(rig.Mixer.SustainedVoiceOf(id)))); } public void ThePitch_IsTheLoudestReports() { Rig rig = new Rig(); SoundId id = rig.DefineSustained(); rig.Mixer.Sustain(id, volume: 0.5f, pitch: 2f); rig.Mixer.Sustain(id, volume: 1f, pitch: 0.5f); rig.Mixer.Sustain(id, volume: 0.25f, pitch: 4f); rig.Mixer.Update(seconds: 0f); CheckEqual(0.5f, rig.Module.PitchOf(rig.Mixer.SustainedVoiceOf(id))); } public void ReportsOfEqualLoudness_TakeTheFirstPitch() { Rig rig = new Rig(); SoundId id = rig.DefineSustained(); rig.Mixer.Sustain(id, pitch: 2f); rig.Mixer.Sustain(id, pitch: 4f); rig.Mixer.Update(seconds: 0f); CheckEqual(2f, rig.Module.PitchOf(rig.Mixer.SustainedVoiceOf(id))); } public void ASilentUnplacedReport_StartsNothing() { Rig rig = new Rig(); SoundId id = rig.DefineSustained(); rig.Mixer.Sustain(id, volume: 0f); rig.Mixer.Update(seconds: 0f); CheckEqual(0, rig.Mixer.ActiveCount); } public void ASilentReport_KeepsAPlayingVoiceAndSilencesIt() { Rig rig = new Rig(); SoundId id = rig.DefineSustained(); rig.Mixer.Sustain(id); rig.Mixer.Update(seconds: 0f); Voice voice = rig.Mixer.SustainedVoiceOf(id); rig.Mixer.Sustain(id, volume: 0f); rig.Mixer.Update(seconds: 0f); CheckEqual(voice, rig.Mixer.SustainedVoiceOf(id)); CheckEqual(0f, rig.Module.VolumeOf(voice)); } public void ALoudnessChange_ReachesTheVoice() { Rig rig = new Rig(); SoundId id = rig.DefineSustained(); rig.Mixer.Sustain(id); rig.Mixer.Update(seconds: 0f); rig.Mixer.Sustain(id, volume: 0.5f); rig.Mixer.Update(seconds: 0f); CheckEqual(0.5f, rig.Module.VolumeOf(rig.Mixer.SustainedVoiceOf(id))); } public void APanAndPitchChange_ReachTheVoice() { Rig rig = new Rig(); SoundId id = rig.DefineSustained(); rig.Mixer.Sustain(id); rig.Mixer.Update(seconds: 0f); rig.Mixer.Sustain(id, pitch: 2f, pan: -0.5f); rig.Mixer.Update(seconds: 0f); Voice voice = rig.Mixer.SustainedVoiceOf(id); CheckEqual(2f, rig.Module.PitchOf(voice)); CheckEqual(-0.5f, rig.Module.PanOf(voice)); } public void TheDefinitionsVariation_TakesNoRandomNumbers() { Rig rig = new Rig(32, 32, 1UL); SoundId id = rig.Mixer.Define(new SoundDefinition(Clip(), rig.Mixer.Master, volumeDecibels: new Variation(-9, 0), pitchSemitones: Variation.PlusOrMinus(3), loop: Loop.Whole)); rig.Mixer.Sustain(id); rig.Mixer.Update(seconds: 0f); Voice voice = rig.Mixer.SustainedVoiceOf(id); CheckEqual(1, rig.DrawsLeft); CheckEqual(1f, rig.Module.VolumeOf(voice)); CheckEqual(1f, rig.Module.PitchOf(voice)); } public void TheDefinitionsFadeIn_RampsTheVoice() { Rig rig = new Rig(); SoundId id = rig.DefineSustained(fadeInSeconds: 0.5f); rig.Mixer.Sustain(id); rig.Mixer.Update(seconds: 0f); Voice voice = rig.Mixer.SustainedVoiceOf(id); CheckEqual(0f, rig.Module.VolumeOf(voice)); rig.Mixer.Sustain(id); rig.Mixer.Update(seconds: 0.25f); CheckEqual(0.5f, rig.Module.VolumeOf(voice)); } public void AVoiceOnAPausedBus_StartsPaused() { Rig rig = new Rig(); Bus music = rig.Mixer.AddBus("Music"); music.IsPaused = true; SoundId id = rig.DefineSustained(bus: music); rig.Mixer.Sustain(id); rig.Mixer.Update(seconds: 0f); CheckTrue(rig.Module.PausedOf(rig.Mixer.SustainedVoiceOf(id))); } public void TheVoice_CountsAgainstTheInstanceCap() { Rig rig = new Rig(); SoundId id = rig.DefineSustained(maxInstances: 1); rig.Mixer.Sustain(id); rig.Mixer.Update(seconds: 0f); CheckEqual(Voice.None, rig.Mixer.Play(id)); } public void ACapTakenByAPlay_LeavesTheReportsUnvoiced() { Rig rig = new Rig(); SoundId id = rig.DefineSustained(maxInstances: 1); Voice taken = rig.Mixer.Play(id); rig.Mixer.Sustain(id); rig.Mixer.Update(seconds: 0f); CheckEqual(Voice.None, rig.Mixer.SustainedVoiceOf(id)); CheckEqual(1, rig.Mixer.ActiveCount); CheckTrue(rig.Mixer.IsPlaying(taken)); } public void WithoutReports_TheVoiceKeepsPlayingThroughTheGrace() { Rig rig = new Rig(); SoundId id = rig.DefineSustained(graceSeconds: 0.5f); rig.Mixer.Sustain(id); rig.Mixer.Update(seconds: 0f); Voice voice = rig.Mixer.SustainedVoiceOf(id); rig.Mixer.Update(seconds: 0.25f); CheckTrue(rig.Mixer.IsPlaying(voice)); CheckEqual(voice, rig.Mixer.SustainedVoiceOf(id)); } public void WithoutReports_TheVoiceKeepsItsLoudnessThroughTheGrace() { Rig rig = new Rig(); SoundId id = rig.DefineSustained(graceSeconds: 0.5f); rig.Mixer.Sustain(id, volume: 0.5f); rig.Mixer.Update(seconds: 0f); rig.Mixer.Update(seconds: 0.25f); CheckEqual(0.5f, rig.Module.VolumeOf(rig.Mixer.SustainedVoiceOf(id))); } public void WithoutReports_TheVoiceStopsWhenTheGraceIsUsedUp() { Rig rig = new Rig(); SoundId id = rig.DefineSustained(graceSeconds: 0.5f); rig.Mixer.Sustain(id); rig.Mixer.Update(seconds: 0f); Voice voice = rig.Mixer.SustainedVoiceOf(id); rig.Mixer.Update(seconds: 0.25f); rig.Mixer.Update(seconds: 0.25f); CheckFalse(rig.Mixer.IsPlaying(voice)); CheckEqual(Voice.None, rig.Mixer.SustainedVoiceOf(id)); CheckEqual(0, rig.Mixer.ActiveCount); } public void AReportInsideTheGrace_RestartsTheClockAndKeepsTheVoice() { Rig rig = new Rig(); SoundId id = rig.DefineSustained(graceSeconds: 0.5f); rig.Mixer.Sustain(id); rig.Mixer.Update(seconds: 0f); Voice voice = rig.Mixer.SustainedVoiceOf(id); rig.Mixer.Update(seconds: 0.25f); rig.Mixer.Sustain(id); rig.Mixer.Update(seconds: 0.25f); rig.Mixer.Update(seconds: 0.25f); CheckEqual(voice, rig.Mixer.SustainedVoiceOf(id)); CheckTrue(rig.Mixer.IsPlaying(voice)); } public void WithNoGrace_TheVoiceStopsAtTheFirstFrameWithoutReports() { Rig rig = new Rig(); SoundId id = rig.DefineSustained(graceSeconds: 0f); rig.Mixer.Sustain(id); rig.Mixer.Update(seconds: 0f); Voice voice = rig.Mixer.SustainedVoiceOf(id); rig.Mixer.Update(seconds: 0f); CheckFalse(rig.Mixer.IsPlaying(voice)); } public void AfterTheVoiceStops_ANewReportStartsAnother() { Rig rig = new Rig(); SoundId id = rig.DefineSustained(graceSeconds: 0f); rig.Mixer.Sustain(id); rig.Mixer.Update(seconds: 0f); Voice first = rig.Mixer.SustainedVoiceOf(id); rig.Mixer.Update(seconds: 0f); rig.Mixer.Sustain(id); rig.Mixer.Update(seconds: 0f); Voice second = rig.Mixer.SustainedVoiceOf(id); CheckFalse(second.IsNone); CheckNotEqual(first, second); } public void TheStop_FadesOutOverTheDefinitionsFadeOut() { Rig rig = new Rig(); SoundId id = rig.DefineSustained(graceSeconds: 0f, fadeOutSeconds: 1f); rig.Mixer.Sustain(id); rig.Mixer.Update(seconds: 0f); Voice voice = rig.Mixer.SustainedVoiceOf(id); rig.Mixer.Update(seconds: 0f); CheckEqual(1, rig.Mixer.FadingCount); CheckEqual(0, rig.Mixer.ActiveCount); CheckTrue(rig.Module.IsPlaying(voice)); } public void AVoiceThatFinished_IsReplacedByTheNextReport() { Rig rig = new Rig(); SoundId id = rig.DefineSustained(); rig.Mixer.Sustain(id); rig.Mixer.Update(seconds: 0f); Voice first = rig.Mixer.SustainedVoiceOf(id); rig.Module.Finish(first); rig.Mixer.Sustain(id); rig.Mixer.Update(seconds: 0f); Voice second = rig.Mixer.SustainedVoiceOf(id); CheckFalse(second.IsNone); CheckNotEqual(first, second); CheckEqual(1, rig.Mixer.ActiveCount); } public void AStolenVoice_StartsAgainWhenARoomIsFree() { Rig rig = new Rig(maxVoices: 1); SoundId id = rig.DefineSustained(); SoundId important = rig.Define(rig.Mixer.Master, priority: 5); rig.Mixer.Sustain(id); rig.Mixer.Update(seconds: 0f); Voice stolen = rig.Mixer.SustainedVoiceOf(id); Voice thief = rig.Mixer.Play(important); rig.Mixer.Stop(thief); rig.Mixer.Sustain(id); rig.Mixer.Update(seconds: 0f); Voice replacement = rig.Mixer.SustainedVoiceOf(id); CheckFalse(rig.Mixer.IsPlaying(stolen)); CheckFalse(replacement.IsNone); CheckNotEqual(stolen, replacement); } public void AStolenVoice_WithNoMoreReports_IsForgotten() { Rig rig = new Rig(maxVoices: 1); SoundId id = rig.DefineSustained(); SoundId important = rig.Define(rig.Mixer.Master, priority: 5); rig.Mixer.Sustain(id); rig.Mixer.Update(seconds: 0f); rig.Mixer.Play(important); rig.Mixer.Update(seconds: 0f); CheckEqual(Voice.None, rig.Mixer.SustainedVoiceOf(id)); } public void ASustainedVoice_IsStolenByALowerPriorityLast() { Rig rig = new Rig(maxVoices: 1); SoundId id = rig.DefineSustained(priority: 5); SoundId minor = rig.Define(rig.Mixer.Master, priority: 0); rig.Mixer.Sustain(id); rig.Mixer.Update(seconds: 0f); Voice dropped = rig.Mixer.Play(minor); CheckEqual(Voice.None, dropped); CheckFalse(rig.Mixer.SustainedVoiceOf(id).IsNone); } public void ANonLoopingDefinition_Throws() { Rig rig = new Rig(); SoundId id = rig.Define(rig.Mixer.Master); CheckThrow(typeof(ArgumentException)); rig.Mixer.Sustain(id); } public void AnUnknownSound_Throws() { Rig rig = new Rig(); CheckThrow(typeof(ArgumentOutOfRangeException)); rig.Mixer.Sustain(new SoundId(3)); } public void ANegativeVolume_Throws() { Rig rig = new Rig(); SoundId id = rig.DefineSustained(); CheckThrow(typeof(ArgumentOutOfRangeException)); rig.Mixer.Sustain(id, volume: -1f); } public void APitchOutOfRange_Throws() { Rig rig = new Rig(); SoundId id = rig.DefineSustained(); CheckThrow(typeof(ArgumentOutOfRangeException)); rig.Mixer.Sustain(id, pitch: 0f); } public void APanOutOfRange_Throws() { Rig rig = new Rig(); SoundId id = rig.DefineSustained(); CheckThrow(typeof(ArgumentOutOfRangeException)); rig.Mixer.Sustain(id, pan: 2f); } } public class SustainAtPositionTests : TestFixture { public void ThePanComesFromThePosition() { Rig rig = new Rig(); rig.Mixer.View = new View(Vector.Zero, 10); SoundId id = rig.DefineSustained(); rig.Mixer.Sustain(id, new Vector(5, 0)); rig.Mixer.Update(seconds: 0f); CheckEqual(0.5f, rig.Module.PanOf(rig.Mixer.SustainedVoiceOf(id))); } public void TheLoudnessComesFromTheFalloff() { Rig rig = new Rig(); rig.Mixer.View = new View(Vector.Zero, 10); SoundId id = rig.DefineSustained(falloff: new Falloff(2, 10)); rig.Mixer.Sustain(id, new Vector(6, 0)); rig.Mixer.Update(seconds: 0f); CheckEqual(0.5f, rig.Module.VolumeOf(rig.Mixer.SustainedVoiceOf(id))); } public void TwoSources_CombineAsPowers() { Rig rig = new Rig(); SoundId id = rig.DefineSustained(falloff: new FixedFalloff(0.5f)); rig.Mixer.Sustain(id, new Vector(1, 0)); rig.Mixer.Sustain(id, new Vector(-1, 0)); rig.Mixer.Update(seconds: 0f); CheckTrue(Near(0.70710678f, rig.Module.VolumeOf(rig.Mixer.SustainedVoiceOf(id)))); } public void ASourceBeyondTheFalloff_CountsAsNoReport() { Rig rig = new Rig(); rig.Mixer.View = new View(Vector.Zero, 10); SoundId id = rig.DefineSustained(falloff: new Falloff(2, 10)); rig.Mixer.Sustain(id, new Vector(20, 0)); rig.Mixer.Update(seconds: 0f); CheckEqual(0, rig.Mixer.ActiveCount); } public void ASourceThatMovesBeyondTheFalloff_LetsTheVoiceGoAfterTheGrace() { Rig rig = new Rig(); rig.Mixer.View = new View(Vector.Zero, 10); SoundId id = rig.DefineSustained(graceSeconds: 0.5f, falloff: new Falloff(2, 10)); rig.Mixer.Sustain(id, new Vector(1, 0)); rig.Mixer.Update(seconds: 0f); Voice voice = rig.Mixer.SustainedVoiceOf(id); rig.Mixer.Sustain(id, new Vector(20, 0)); rig.Mixer.Update(seconds: 0.5f); CheckFalse(rig.Mixer.IsPlaying(voice)); } public void TheVolumeOfTheReport_MultipliesTheFalloff() { Rig rig = new Rig(); rig.Mixer.View = new View(Vector.Zero, 10); SoundId id = rig.DefineSustained(falloff: new Falloff(2, 10)); rig.Mixer.Sustain(id, new Vector(6, 0), volume: 0.5f); rig.Mixer.Update(seconds: 0f); CheckEqual(0.25f, rig.Module.VolumeOf(rig.Mixer.SustainedVoiceOf(id))); } public void ANonLoopingDefinition_Throws() { Rig rig = new Rig(); SoundId id = rig.Define(rig.Mixer.Master); CheckThrow(typeof(ArgumentException)); rig.Mixer.Sustain(id, new Vector(1, 0)); } public void APositionThatIsNotANumber_Throws() { Rig rig = new Rig(); SoundId id = rig.DefineSustained(); CheckThrow(typeof(ArgumentOutOfRangeException)); rig.Mixer.Sustain(id, new Vector(double.NaN, 0)); } public void ANegativeVolume_Throws() { Rig rig = new Rig(); SoundId id = rig.DefineSustained(); CheckThrow(typeof(ArgumentOutOfRangeException)); rig.Mixer.Sustain(id, new Vector(1, 0), volume: -1f); } public void APitchOutOfRange_Throws() { Rig rig = new Rig(); SoundId id = rig.DefineSustained(); CheckThrow(typeof(ArgumentOutOfRangeException)); rig.Mixer.Sustain(id, new Vector(1, 0), pitch: 0f); } } public class SustainedVoiceOfTests : TestFixture { public void ADefinitionNeverSustained_HasNoVoice() { Rig rig = new Rig(); SoundId id = rig.DefineSustained(); CheckEqual(Voice.None, rig.Mixer.SustainedVoiceOf(id)); } public void AnUnknownSound_Throws() { Rig rig = new Rig(); CheckThrow(typeof(ArgumentOutOfRangeException)); rig.Mixer.SustainedVoiceOf(new SoundId(0)); } } } }