using Amphora; using System; using System.Diagnostics; using System.Threading; using UnitTestSharp; namespace Susurrus.XAudio2.UnitTests { public class XAudio2ModuleTests : TestFixture { /// /// Returns whether the engine sends to the device the way the core's pan law says. /// public static bool PlacedAt(XAudio2Module module, Voice voice, float pan, int sourceChannels) { int count = sourceChannels * module.DeviceChannelCount; float[] expected = new float[count]; float[] actual = new float[count]; OutputMatrix.Fill( expected, Pan.Matrix(pan, sourceChannels), sourceChannels, module.DeviceChannelCount); module.GetOutputLevels(voice, actual); return OutputMatrixTests.LevelsAre(actual, expected); } public static XAudio2StreamedSound Streamed(XAudio2Module module, int frameCount, int channelCount = 1) { byte[] file = SilentSounds.Wav(frameCount, channelCount); return (XAudio2StreamedSound)module.LoadStream(new ByteChunks(new ByteView(file))).Result.Value; } public class CtorTests : TestFixture { public void NoMonoVoices_Throws() { CheckThrow(typeof(ArgumentOutOfRangeException)); new XAudio2Module(monoVoiceCount: 0, stereoVoiceCount: 1); } public void NoStereoVoices_Throws() { CheckThrow(typeof(ArgumentOutOfRangeException)); new XAudio2Module(monoVoiceCount: 1, stereoVoiceCount: 0); } public void StartsUnloadedWithNoFailure() { XAudio2Module module = new XAudio2Module(); CheckFalse(module.IsLoaded); CheckTrue(module.LastFailure.IsOk); } public void PrefersItselfToTheSilentBackend() { CheckGreater(new XAudio2Module().Priority, new NullModule().Priority); } } public class IsSupportedTests : TestFixture { public void IsTrueOnceLoaded() { using (TestModule test = TestModule.Open()) { if (test == null) { return; } CheckTrue(test.Module.IsSupported); } } public void ProbingLeavesTheModuleUnloaded() { XAudio2Module module = new XAudio2Module(); bool supported = module.IsSupported; CheckFalse(module.IsLoaded); CheckEqual(supported, module.IsSupported); } } public class LoadTests : TestFixture { public void LoadsTheModule() { using (TestModule test = TestModule.Open()) { if (test == null) { return; } CheckTrue(test.Module.IsLoaded); } } public void WhenLoaded_Throws() { using (TestModule test = TestModule.Open()) { if (test == null) { return; } CheckThrow(typeof(InvalidOperationException)); test.Module.Load(); } } public void WorksAgainAfterUnload() { using (TestModule test = TestModule.Open()) { if (test == null) { return; } test.Module.Unload(); test.Module.Load(); CheckTrue(test.Module.IsLoaded); CheckFalse(test.Module.Play(test.Module.LoadSound(SilentSounds.Long()), volume: 0f).IsNone); } } } public class LimiterEnabledTests : TestFixture { public void IsOnByDefault() { CheckTrue(new XAudio2Module().LimiterEnabled); } public void LoadingLimitsOutputByDefault() { using (TestModule test = TestModule.Open()) { if (test == null) { return; } CheckTrue(test.Module.IsLimiting); } } public void TurningItOffWhileLoaded_StopsLimiting() { using (TestModule test = TestModule.Open()) { if (test == null) { return; } test.Module.LimiterEnabled = false; CheckFalse(test.Module.LimiterEnabled); CheckFalse(test.Module.IsLimiting); } } public void TurningItOffBeforeLoad_LoadsWithoutLimiting() { XAudio2Module module = new XAudio2Module(monoVoiceCount: 1, stereoVoiceCount: 1); if (!module.IsSupported) { return; } module.LimiterEnabled = false; module.Load(); try { CheckFalse(module.IsLimiting); } finally { module.Unload(); } } public void TheChoiceSurvivesAnUnloadAndLoad() { using (TestModule test = TestModule.Open()) { if (test == null) { return; } test.Module.LimiterEnabled = false; test.Module.Unload(); test.Module.Load(); CheckFalse(test.Module.IsLimiting); } } public void IsNotLimitingWhenUnloaded() { CheckFalse(new XAudio2Module().IsLimiting); } } public class UnloadTests : TestFixture { public void WhenNotLoaded_DoesNothing() { XAudio2Module module = new XAudio2Module(); module.Unload(); CheckFalse(module.IsLoaded); } public void ReleasesEveryHold() { using (TestModule test = TestModule.Open()) { if (test == null) { return; } XAudio2Sound sound = (XAudio2Sound)test.Module.LoadSound(SilentSounds.Long()); test.Module.Play(sound, volume: 0f); test.Module.Play(sound, volume: 0f); test.Module.Unload(); CheckEqual(0, sound.HoldCount); CheckFalse(sound.IsPinned); } } public void StopsPlayingVoices() { using (TestModule test = TestModule.Open()) { if (test == null) { return; } Voice voice = test.Module.Play(test.Module.LoadSound(SilentSounds.Long()), volume: 0f); test.Module.Unload(); CheckFalse(test.Module.IsLoaded); CheckFalse(test.Module.IsPlaying(voice)); } } } public class LoadSoundTests : TestFixture { public void WorksBeforeLoad() { XAudio2Module module = new XAudio2Module(); Sound sound = module.LoadSound(SilentSounds.Of(frameCount: 24000, channelCount: 2)); CheckEqual(TimeSpan.FromSeconds(0.5), sound.Duration); CheckEqual(2, sound.ChannelCount); } public void ReturnsASoundThatThisModuleCanPlay() { CheckTrue(new XAudio2Module().LoadSound(SilentSounds.Short()) is XAudio2Sound); } public void Null_Throws() { CheckThrow(typeof(ArgumentNullException)); new XAudio2Module().LoadSound(null); } public void MoreThanTwoChannels_Throws() { CheckThrow(typeof(ArgumentException)); new XAudio2Module().LoadSound(SilentSounds.Short(channelCount: 3)); } public void WavBytes_GiveADoneLoadOfASoundThisModuleCanPlay() { byte[] file = SilentSounds.Wav(frameCount: 3, channelCount: 1); SoundLoad load = new XAudio2Module().LoadSound(new ByteView(file)); CheckTrue(load.IsDone); CheckTrue(load.Result.Value is XAudio2Sound); CheckEqual(1, load.Result.Value.ChannelCount); } public void BytesThatAreNotAudio_GiveADoneLoadThatFailed() { byte[] text = System.Text.Encoding.ASCII.GetBytes("This is just some text, not sound."); SoundLoad load = new XAudio2Module().LoadSound(new ByteView(text)); CheckTrue(load.IsDone); CheckEqual(SoundFailures.UnrecognizedFormat, load.Result.Kind); } public void ASoundWithMoreThanTwoChannels_GivesAFailedLoad() { byte[] file = SilentSounds.Wav(frameCount: 2, channelCount: 3); SoundLoad load = new XAudio2Module().LoadSound(new ByteView(file)); CheckEqual(SoundFailures.TooManyChannels, load.Result.Kind); CheckEqual("Susurrus plays mono and stereo sounds, and this one has 3 channels.", load.Result.Status.Describe()); } } public class LoadStreamTests : TestFixture { public void GivesAStreamedSoundWithTheFilesShape() { XAudio2StreamedSound sound = Streamed(new XAudio2Module(), frameCount: 24000, channelCount: 2); CheckEqual(TimeSpan.FromSeconds(0.5), sound.Duration); CheckEqual(2, sound.ChannelCount); } public void ABrokenFile_FailsAsOpeningItDoes() { SoundLoad load = new XAudio2Module().LoadStream(new ByteChunks(new ByteView(new byte[100]))); CheckEqual(SoundFailures.UnrecognizedFormat, load.Result.Status.Kind); } public void MoreThanTwoChannels_Fail() { byte[] file = SilentSounds.Wav(frameCount: 10, channelCount: 3); SoundLoad load = new XAudio2Module().LoadStream(new ByteChunks(new ByteView(file))); CheckEqual(SoundFailures.TooManyChannels, load.Result.Status.Kind); } public void NullSource_Throws() { CheckThrow(typeof(ArgumentNullException)); new XAudio2Module().LoadStream(null); } } public class PlayTests : TestFixture { public void AStreamedSound_PlaysAndFinishesAndIsFreed() { using (TestModule test = TestModule.Open()) { if (test == null) { return; } XAudio2StreamedSound sound = Streamed(test.Module, frameCount: 24000); Voice voice = test.Module.Play(sound, volume: 0f); CheckFalse(voice.IsNone); CheckTrue(sound.IsHeld); CheckTrue(test.UpdateUntilFinished(voice)); CheckFalse(sound.IsHeld); CheckFalse(sound.IsPinned); } } public void AStreamedSoundLongerThanItsBuffers_PlaysToTheEnd() { using (TestModule test = TestModule.Open()) { if (test == null) { return; } XAudio2StreamedSound sound = Streamed(test.Module, frameCount: 48000 * 2); Voice voice = test.Module.Play(sound, volume: 0f, pitch: 2f); CheckTrue(test.UpdateUntilFinished(voice)); CheckEqual((long)48000 * 2, sound.Stream.Position); } } public void AStereoStreamedSound_Plays() { using (TestModule test = TestModule.Open()) { if (test == null) { return; } XAudio2StreamedSound sound = Streamed(test.Module, frameCount: 48000 * 30, channelCount: 2); CheckTrue(test.Module.IsPlaying(test.Module.Play(sound, volume: 0f))); } } public void AStreamedSoundThatIsAlreadyPlaying_GivesNoVoice() { using (TestModule test = TestModule.Open()) { if (test == null) { return; } XAudio2StreamedSound sound = Streamed(test.Module, frameCount: 48000 * 30); test.Module.Play(sound, volume: 0f); CheckTrue(test.Module.Play(sound, volume: 0f).IsNone); } } public void AStreamedSoundAfterItsVoiceEnded_PlaysAgainFromTheStart() { using (TestModule test = TestModule.Open()) { if (test == null) { return; } XAudio2StreamedSound sound = Streamed(test.Module, frameCount: 4800); test.UpdateUntilFinished(test.Module.Play(sound, volume: 0f)); Voice again = test.Module.Play(sound, volume: 0f); CheckFalse(again.IsNone); CheckTrue(test.UpdateUntilFinished(again)); CheckEqual(4800L, sound.Stream.Position); } } public void AStreamedSoundWhenEveryVoiceOfItsKindIsBusy_GivesNoVoiceAndStaysFree() { using (TestModule test = TestModule.Open(monoVoiceCount: 1, stereoVoiceCount: 1)) { if (test == null) { return; } test.Module.Play(test.Module.LoadSound(SilentSounds.Long()), volume: 0f); XAudio2StreamedSound sound = Streamed(test.Module, frameCount: 48000 * 30); CheckTrue(test.Module.Play(sound, volume: 0f).IsNone); CheckFalse(sound.IsHeld); } } public void ADisposedStreamedSound_Throws() { using (TestModule test = TestModule.Open()) { if (test == null) { return; } XAudio2StreamedSound sound = Streamed(test.Module, frameCount: 4800); sound.Dispose(); CheckThrow(typeof(ObjectDisposedException)); test.Module.Play(sound, volume: 0f); } } public void ALoopingStreamedSound_KeepsPlayingPastItsEndUntilStopped() { using (TestModule test = TestModule.Open()) { if (test == null) { return; } XAudio2StreamedSound sound = Streamed(test.Module, frameCount: 4800); Voice voice = test.Module.Play(sound, volume: 0f, loop: Loop.Whole); Stopwatch clock = Stopwatch.StartNew(); CheckTrue(test.UpdateUntil(() => clock.ElapsedMilliseconds > 400)); CheckTrue(test.Module.IsPlaying(voice)); test.Module.Stop(voice); CheckTrue(test.UpdateUntil(() => !sound.IsHeld)); } } public void AVoiceThatRanDryBecauseUpdateCameLate_KeepsPlaying() { using (TestModule test = TestModule.Open()) { if (test == null) { return; } XAudio2StreamedSound sound = Streamed(test.Module, frameCount: 48000 * 30); Voice voice = test.Module.Play(sound, volume: 0f, pitch: Pitch.Max); System.Threading.Thread.Sleep(1500); test.Module.Update(); CheckTrue(test.Module.IsPlaying(voice)); } } public void AStreamedSoundWhoseReadFails_GivesNoVoiceAtOnceAndFreesItWhenDrained() { using (TestModule test = TestModule.Open()) { if (test == null) { return; } XAudio2StreamedSoundTests.FlakyStream stream = new XAudio2StreamedSoundTests.FlakyStream( XAudio2StreamedSoundTests.OpenWav(frameCount: 48000)); stream.FailReads = true; XAudio2StreamedSound sound = new XAudio2StreamedSound(stream); Voice voice = test.Module.Play(sound, volume: 0f); CheckTrue(voice.IsNone); CheckEqual(SoundFailures.StreamRequestInvalid, test.Module.LastFailure.Kind); CheckTrue(test.UpdateUntil(() => !sound.IsHeld)); } } public void AStreamedSoundWhoseSeekFails_GivesNoVoice() { using (TestModule test = TestModule.Open()) { if (test == null) { return; } XAudio2StreamedSoundTests.FlakyStream stream = new XAudio2StreamedSoundTests.FlakyStream( XAudio2StreamedSoundTests.OpenWav(frameCount: 48000)); stream.Seek(100); stream.FailSeeks = true; XAudio2StreamedSound sound = new XAudio2StreamedSound(stream); CheckTrue(test.Module.Play(sound, volume: 0f).IsNone); CheckTrue(test.UpdateUntil(() => !sound.IsHeld)); } } public void AStreamedSoundIsFreedWhenTheModuleUnloads() { XAudio2StreamedSound sound; using (TestModule test = TestModule.Open()) { if (test == null) { return; } sound = Streamed(test.Module, frameCount: 48000 * 30); test.Module.Play(sound, volume: 0f); test.Module.Unload(); CheckFalse(sound.IsHeld); CheckFalse(sound.IsPinned); } } public void AStoppedStreamedVoice_FreesTheSoundOnceDrained() { using (TestModule test = TestModule.Open()) { if (test == null) { return; } XAudio2StreamedSound sound = Streamed(test.Module, frameCount: 48000 * 30); Voice voice = test.Module.Play(sound, volume: 0f); test.Module.Stop(voice); CheckTrue(test.UpdateUntil(() => !sound.IsHeld)); } } public void WhenNotLoaded_Throws() { XAudio2Module module = new XAudio2Module(); Sound sound = module.LoadSound(SilentSounds.Short()); CheckThrow(typeof(InvalidOperationException)); module.Play(sound, volume: 1f); } public void NullSound_Throws() { using (TestModule test = TestModule.Open()) { if (test == null) { return; } CheckThrow(typeof(ArgumentNullException)); test.Module.Play(null, volume: 1f); } } public void ASoundFromAnotherBackend_Throws() { using (TestModule test = TestModule.Open()) { if (test == null) { return; } Sound foreign = new NullModule().LoadSound(SilentSounds.Short()); CheckThrow(typeof(ArgumentException)); test.Module.Play(foreign, volume: 1f); } } public void ASoundWithMoreThanTwoChannels_Throws() { using (TestModule test = TestModule.Open()) { if (test == null) { return; } XAudio2Sound surround = new XAudio2Sound(SilentSounds.Short(channelCount: 6)); CheckThrow(typeof(ArgumentException)); test.Module.Play(surround, volume: 1f); } } public void NegativeVolume_Throws() { using (TestModule test = TestModule.Open()) { if (test == null) { return; } Sound sound = test.Module.LoadSound(SilentSounds.Short()); CheckThrow(typeof(ArgumentOutOfRangeException)); test.Module.Play(sound, volume: -1f); } } public void StartsAVoiceThatIsPlaying() { using (TestModule test = TestModule.Open()) { if (test == null) { return; } Voice voice = test.Module.Play(test.Module.LoadSound(SilentSounds.Long()), volume: 0f); CheckFalse(voice.IsNone); CheckTrue(test.Module.IsPlaying(voice)); } } public void StereoSounds_PlayOnTheirOwnVoices() { using (TestModule test = TestModule.Open()) { if (test == null) { return; } Voice voice = test.Module.Play(test.Module.LoadSound(SilentSounds.Long(channelCount: 2)), 0f); CheckTrue(test.Module.IsPlaying(voice)); } } public void FloatSounds_PlayOnTheirOwnVoices() { using (TestModule test = TestModule.Open()) { if (test == null) { return; } Sound sound = test.Module.LoadSound(SilentSounds.Of(48000 * 30, format: SampleFormat.Float32)); Voice voice = test.Module.Play(sound, volume: 0f); CheckTrue(test.Module.IsPlaying(voice)); } } public void ASoundAtAnotherRate_Plays() { using (TestModule test = TestModule.Open()) { if (test == null) { return; } Sound sound = test.Module.LoadSound(SilentSounds.Of(22050 * 30, sampleRate: 22050)); Voice voice = test.Module.Play(sound, volume: 0f); CheckTrue(test.Module.IsPlaying(voice)); } } public void ASoundWithNoSamples_PlaysAndFinishes() { using (TestModule test = TestModule.Open()) { if (test == null) { return; } Voice voice = test.Module.Play(test.Module.LoadSound(SilentSounds.Of(frameCount: 0)), volume: 0f); CheckFalse(voice.IsNone); CheckTrue(test.UpdateUntilFinished(voice)); } } public void ALoopingSound_KeepsPlayingPastItsEndUntilStopped() { using (TestModule test = TestModule.Open()) { if (test == null) { return; } Sound sound = test.Module.LoadSound(SilentSounds.Short()); Voice voice = test.Module.Play(sound, volume: 0f, loop: Loop.Whole); Stopwatch clock = Stopwatch.StartNew(); CheckTrue(test.UpdateUntil(() => clock.ElapsedMilliseconds > 150)); CheckTrue(test.Module.IsPlaying(voice)); test.Module.Stop(voice); CheckTrue(test.UpdateUntilFinished(voice)); } } public void ALoopPastTheEndOfTheSound_Throws() { using (TestModule test = TestModule.Open()) { if (test == null) { return; } Sound sound = test.Module.LoadSound(SilentSounds.Short()); CheckThrow(typeof(ArgumentOutOfRangeException)); test.Module.Play(sound, volume: 0f, loop: new Loop(TimeSpan.Zero, TimeSpan.FromSeconds(1))); } } public void WhenEveryVoiceOfItsKindIsBusy_ReturnsNone() { using (TestModule test = TestModule.Open(monoVoiceCount: 2, stereoVoiceCount: 1)) { if (test == null) { return; } Sound sound = test.Module.LoadSound(SilentSounds.Long()); test.Module.Play(sound, volume: 0f); test.Module.Play(sound, volume: 0f); CheckTrue(test.Module.Play(sound, volume: 0f).IsNone); } } public void ABusyKindOfVoice_DoesNotUseUpOtherKinds() { using (TestModule test = TestModule.Open(monoVoiceCount: 1, stereoVoiceCount: 1)) { if (test == null) { return; } Sound mono = test.Module.LoadSound(SilentSounds.Long()); Sound stereo = test.Module.LoadSound(SilentSounds.Long(channelCount: 2)); Sound floats = test.Module.LoadSound(SilentSounds.Of(48000 * 30, format: SampleFormat.Float32)); test.Module.Play(mono, volume: 0f); CheckFalse(test.Module.Play(stereo, volume: 0f).IsNone); CheckFalse(test.Module.Play(floats, volume: 0f).IsNone); } } public void HoldsTheSoundWhileItPlays() { using (TestModule test = TestModule.Open()) { if (test == null) { return; } XAudio2Sound sound = (XAudio2Sound)test.Module.LoadSound(SilentSounds.Long()); test.Module.Play(sound, volume: 0f); CheckEqual(1, sound.HoldCount); CheckTrue(sound.IsPinned); } } public void TwoVoicesOfOneSound_HoldItTwice() { using (TestModule test = TestModule.Open()) { if (test == null) { return; } XAudio2Sound sound = (XAudio2Sound)test.Module.LoadSound(SilentSounds.Long()); test.Module.Play(sound, volume: 0f); test.Module.Play(sound, volume: 0f); CheckEqual(2, sound.HoldCount); } } public void WhenEveryVoiceOfItsKindIsBusy_DoesNotHoldTheSound() { using (TestModule test = TestModule.Open(monoVoiceCount: 1, stereoVoiceCount: 1)) { if (test == null) { return; } XAudio2Sound sound = (XAudio2Sound)test.Module.LoadSound(SilentSounds.Long()); test.Module.Play(sound, volume: 0f); test.Module.Play(sound, volume: 0f); CheckEqual(1, sound.HoldCount); } } public void ARateXAudio2Refuses_ReleasesTheHoldOnceTheVoiceIsDrained() { using (TestModule test = TestModule.Open()) { if (test == null) { return; } XAudio2Sound absurd = (XAudio2Sound)test.Module.LoadSound( SilentSounds.Of(frameCount: 10, sampleRate: 100000000)); test.Module.Play(absurd, volume: 1f); CheckEqual(1, absurd.HoldCount); CheckTrue(test.UpdateUntil(() => absurd.HoldCount == 0)); } } public void ASoundWithNoSamples_ReleasesItsHoldAtUpdate() { using (TestModule test = TestModule.Open()) { if (test == null) { return; } XAudio2Sound empty = (XAudio2Sound)test.Module.LoadSound(SilentSounds.Of(frameCount: 0)); test.Module.Play(empty, volume: 0f); CheckEqual(1, empty.HoldCount); test.Module.Update(); CheckEqual(0, empty.HoldCount); } } public void ARateXAudio2Refuses_ReturnsNoneAndRemembersWhy() { using (TestModule test = TestModule.Open()) { if (test == null) { return; } Sound absurd = test.Module.LoadSound(SilentSounds.Of(frameCount: 10, sampleRate: 100000000)); CheckTrue(test.Module.Play(absurd, volume: 1f).IsNone); CheckEqual(XAudio2Failures.CallFailed, test.Module.LastFailure.Kind); } } public void AfterAFailure_TheVoiceIsUsableAgain() { using (TestModule test = TestModule.Open(monoVoiceCount: 1, stereoVoiceCount: 1)) { if (test == null) { return; } Sound absurd = test.Module.LoadSound(SilentSounds.Of(frameCount: 10, sampleRate: 100000000)); Sound fine = test.Module.LoadSound(SilentSounds.Long()); test.Module.Play(absurd, volume: 1f); CheckFalse(test.UpdateUntilPlayable(fine).IsNone); } } public void WithAPitch_PlaysAtThatPitch() { using (TestModule test = TestModule.Open()) { if (test == null) { return; } Sound sound = test.Module.LoadSound(SilentSounds.Long()); Voice voice = test.Module.Play(sound, volume: 0f, pitch: 2f); CheckTrue(test.UpdateUntilPitch(voice, 2f)); } } public void TheHighestPitchOnASoundWithAHighRate_Plays() { using (TestModule test = TestModule.Open()) { if (test == null) { return; } Sound sound = test.Module.LoadSound(SilentSounds.Of(96000 * 30, sampleRate: 96000)); Voice voice = test.Module.Play(sound, volume: 0f, pitch: Pitch.Max); CheckTrue(test.UpdateUntilPitch(voice, Pitch.Max)); CheckTrue(test.Module.LastFailure.IsOk); } } public void TheLowestPitchOnASoundWithALowRate_Plays() { using (TestModule test = TestModule.Open()) { if (test == null) { return; } Sound sound = test.Module.LoadSound(SilentSounds.Of(8000 * 30, sampleRate: 8000)); Voice voice = test.Module.Play(sound, volume: 0f, pitch: Pitch.Min); CheckTrue(test.UpdateUntilPitch(voice, Pitch.Min)); CheckTrue(test.Module.LastFailure.IsOk); } } public void AMonoSoundWithAPan_IsPlacedByTheCoreLaw() { using (TestModule test = TestModule.Open()) { if (test == null) { return; } Sound sound = test.Module.LoadSound(SilentSounds.Long()); Voice voice = test.Module.Play(sound, volume: 0f, pan: 0.5f); CheckTrue(PlacedAt(test.Module, voice, pan: 0.5f, sourceChannels: 1)); } } public void AStereoSoundWithAPan_IsPlacedByTheCoreLaw() { using (TestModule test = TestModule.Open()) { if (test == null) { return; } Sound sound = test.Module.LoadSound(SilentSounds.Long(channelCount: 2)); Voice voice = test.Module.Play(sound, volume: 0f, pan: -0.5f); CheckTrue(PlacedAt(test.Module, voice, pan: -0.5f, sourceChannels: 2)); } } public void AVoiceTakenBackIntoThePool_StartsAgainAtNaturalPitchAndTheCenter() { using (TestModule test = TestModule.Open(monoVoiceCount: 1)) { if (test == null) { return; } Sound sound = test.Module.LoadSound(SilentSounds.Long()); Voice first = test.Module.Play(sound, volume: 0f, pitch: 4f, pan: 1f); test.Module.Stop(first); Voice second = test.UpdateUntilPlayable(sound); CheckFalse(second.IsNone); CheckTrue(test.UpdateUntilPitch(second, 1f)); CheckTrue(PlacedAt(test.Module, second, pan: 0f, sourceChannels: 1)); } } public void APitchOutOfRange_Throws() { using (TestModule test = TestModule.Open()) { if (test == null) { return; } Sound sound = test.Module.LoadSound(SilentSounds.Long()); CheckThrow(typeof(ArgumentOutOfRangeException)); test.Module.Play(sound, volume: 0f, pitch: 2000f); } } public void APanOutOfRange_Throws() { using (TestModule test = TestModule.Open()) { if (test == null) { return; } Sound sound = test.Module.LoadSound(SilentSounds.Long()); CheckThrow(typeof(ArgumentOutOfRangeException)); test.Module.Play(sound, volume: 0f, pan: 1.5f); } } } public class StopTests : TestFixture { public void EndsPlayingAtOnce() { using (TestModule test = TestModule.Open()) { if (test == null) { return; } Voice voice = test.Module.Play(test.Module.LoadSound(SilentSounds.Long()), volume: 0f); test.Module.Stop(voice); CheckFalse(test.Module.IsPlaying(voice)); } } public void KeepsTheHoldUntilTheFlushHasDrained() { using (TestModule test = TestModule.Open()) { if (test == null) { return; } XAudio2Sound sound = (XAudio2Sound)test.Module.LoadSound(SilentSounds.Long()); Voice voice = test.Module.Play(sound, volume: 0f); test.Module.Stop(voice); CheckEqual(1, sound.HoldCount); CheckTrue(test.UpdateUntil(() => sound.HoldCount == 0)); } } public void FreesTheVoiceForAnotherSoundOnceUpdated() { using (TestModule test = TestModule.Open(monoVoiceCount: 1, stereoVoiceCount: 1)) { if (test == null) { return; } Sound sound = test.Module.LoadSound(SilentSounds.Long()); Voice first = test.Module.Play(sound, volume: 0f); test.Module.Stop(first); Voice second = test.UpdateUntilPlayable(sound); CheckFalse(second.IsNone); CheckFalse(test.Module.IsPlaying(first)); CheckTrue(test.Module.IsPlaying(second)); } } public void ADoneVoice_DoesNothing() { using (TestModule test = TestModule.Open()) { if (test == null) { return; } test.Module.Stop(Voice.None); test.Module.Stop(new Voice(slot: 3, generation: 5)); CheckTrue(test.Module.LastFailure.IsOk); } } public void CanBeCalledTwice() { using (TestModule test = TestModule.Open()) { if (test == null) { return; } Voice voice = test.Module.Play(test.Module.LoadSound(SilentSounds.Long()), volume: 0f); test.Module.Stop(voice); test.Module.Stop(voice); CheckFalse(test.Module.IsPlaying(voice)); CheckTrue(test.Module.LastFailure.IsOk); } } } public class SetVolumeTests : TestFixture { public void ChangesAPlayingVoice() { using (TestModule test = TestModule.Open()) { if (test == null) { return; } Voice voice = test.Module.Play(test.Module.LoadSound(SilentSounds.Long()), volume: 0f); test.Module.SetVolume(voice, 0.5f); CheckTrue(test.Module.IsPlaying(voice)); CheckTrue(test.Module.LastFailure.IsOk); } } public void NegativeVolume_Throws() { using (TestModule test = TestModule.Open()) { if (test == null) { return; } Voice voice = test.Module.Play(test.Module.LoadSound(SilentSounds.Long()), volume: 0f); CheckThrow(typeof(ArgumentOutOfRangeException)); test.Module.SetVolume(voice, -1f); } } public void ADoneVoice_StillChecksTheVolume() { XAudio2Module module = new XAudio2Module(); CheckThrow(typeof(ArgumentOutOfRangeException)); module.SetVolume(Voice.None, float.NaN); } public void ADoneVoice_DoesNothing() { using (TestModule test = TestModule.Open()) { if (test == null) { return; } test.Module.SetVolume(Voice.None, 1f); CheckTrue(test.Module.LastFailure.IsOk); } } } public class SetPitchTests : TestFixture { public void ChangesAPlayingVoice() { using (TestModule test = TestModule.Open()) { if (test == null) { return; } Voice voice = test.Module.Play(test.Module.LoadSound(SilentSounds.Long()), volume: 0f); test.Module.SetPitch(voice, 0.5f); CheckTrue(test.UpdateUntilPitch(voice, 0.5f)); CheckTrue(test.Module.LastFailure.IsOk); } } public void APitchOutOfRange_Throws() { using (TestModule test = TestModule.Open()) { if (test == null) { return; } Voice voice = test.Module.Play(test.Module.LoadSound(SilentSounds.Long()), volume: 0f); CheckThrow(typeof(ArgumentOutOfRangeException)); test.Module.SetPitch(voice, 0f); } } public void ADoneVoice_StillChecksThePitch() { XAudio2Module module = new XAudio2Module(); CheckThrow(typeof(ArgumentOutOfRangeException)); module.SetPitch(Voice.None, float.NaN); } public void ADoneVoice_DoesNothing() { using (TestModule test = TestModule.Open()) { if (test == null) { return; } test.Module.SetPitch(Voice.None, 2f); CheckTrue(test.Module.LastFailure.IsOk); } } } public class SetPanTests : TestFixture { public void MovesAPlayingMonoVoice() { using (TestModule test = TestModule.Open()) { if (test == null) { return; } Voice voice = test.Module.Play(test.Module.LoadSound(SilentSounds.Long()), volume: 0f); test.Module.SetPan(voice, -1f); CheckTrue(PlacedAt(test.Module, voice, pan: -1f, sourceChannels: 1)); CheckTrue(test.Module.LastFailure.IsOk); } } public void MovesAPlayingStereoVoice() { using (TestModule test = TestModule.Open()) { if (test == null) { return; } Sound sound = test.Module.LoadSound(SilentSounds.Long(channelCount: 2)); Voice voice = test.Module.Play(sound, volume: 0f); test.Module.SetPan(voice, 0.5f); CheckTrue(PlacedAt(test.Module, voice, pan: 0.5f, sourceChannels: 2)); } } public void APanOutOfRange_Throws() { using (TestModule test = TestModule.Open()) { if (test == null) { return; } Voice voice = test.Module.Play(test.Module.LoadSound(SilentSounds.Long()), volume: 0f); CheckThrow(typeof(ArgumentOutOfRangeException)); test.Module.SetPan(voice, 2f); } } public void ADoneVoice_StillChecksThePan() { XAudio2Module module = new XAudio2Module(); CheckThrow(typeof(ArgumentOutOfRangeException)); module.SetPan(Voice.None, float.NaN); } public void ADoneVoice_DoesNothing() { using (TestModule test = TestModule.Open()) { if (test == null) { return; } test.Module.SetPan(Voice.None, 0.5f); CheckTrue(test.Module.LastFailure.IsOk); } } } public class PitchOfTests : TestFixture { public void WhenNotLoaded_IsNatural() { CheckEqual(1f, new XAudio2Module().PitchOf(Voice.None)); } public void ForAVoiceThatStopped_IsNatural() { using (TestModule test = TestModule.Open()) { if (test == null) { return; } Voice voice = test.Module.Play(test.Module.LoadSound(SilentSounds.Long()), volume: 0f, pitch: 2f); test.Module.Stop(voice); CheckEqual(1f, test.Module.PitchOf(voice)); } } } public class GetOutputLevelsTests : TestFixture { public void ForAVoiceThatStopped_LeavesTheLevelsAlone() { using (TestModule test = TestModule.Open()) { if (test == null) { return; } Voice voice = test.Module.Play(test.Module.LoadSound(SilentSounds.Long()), volume: 0f); test.Module.Stop(voice); float[] levels = { 9f, 9f }; test.Module.GetOutputLevels(voice, levels); CheckEqual(9f, levels[0]); CheckEqual(9f, levels[1]); } } } public class DeviceChannelCountTests : TestFixture { public void WhenNotLoaded_IsZero() { CheckEqual(0, new XAudio2Module().DeviceChannelCount); } public void WhenLoaded_IsTheDevicesChannelCount() { using (TestModule test = TestModule.Open()) { if (test == null) { return; } CheckGreater(test.Module.DeviceChannelCount, 0); } } } public class SetPausedTests : TestFixture { // The engine acts on a pause at its next mixing pass, so the count settles a moment after the call. public const int SettleMilliseconds = 100; public void HoldsThePositionUntilResumed() { using (TestModule test = TestModule.Open()) { if (test == null) { return; } Voice voice = test.Module.Play(test.Module.LoadSound(SilentSounds.Long()), volume: 0f); CheckTrue(test.UpdateUntil(() => test.Module.FramesPlayedOf(voice) > 0)); test.Module.SetPaused(voice, true); Thread.Sleep(SettleMilliseconds); long held = test.Module.FramesPlayedOf(voice); Thread.Sleep(SettleMilliseconds); CheckEqual(held, test.Module.FramesPlayedOf(voice)); CheckTrue(test.Module.IsPlaying(voice)); CheckTrue(test.Module.LastFailure.IsOk); } } public void Resuming_CarriesOnFromWhereItStopped() { using (TestModule test = TestModule.Open()) { if (test == null) { return; } Voice voice = test.Module.Play(test.Module.LoadSound(SilentSounds.Long()), volume: 0f); CheckTrue(test.UpdateUntil(() => test.Module.FramesPlayedOf(voice) > 0)); test.Module.SetPaused(voice, true); Thread.Sleep(SettleMilliseconds); long held = test.Module.FramesPlayedOf(voice); test.Module.SetPaused(voice, false); CheckTrue(test.UpdateUntil(() => test.Module.FramesPlayedOf(voice) > held)); CheckTrue(test.Module.LastFailure.IsOk); } } public void Stop_EndsAPausedVoice() { using (TestModule test = TestModule.Open()) { if (test == null) { return; } Voice voice = test.Module.Play(test.Module.LoadSound(SilentSounds.Long()), volume: 0f); test.Module.SetPaused(voice, true); test.Module.Stop(voice); CheckTrue(test.UpdateUntilFinished(voice)); } } public void ADoneVoice_DoesNothing() { using (TestModule test = TestModule.Open()) { if (test == null) { return; } test.Module.SetPaused(Voice.None, true); CheckTrue(test.Module.LastFailure.IsOk); } } } public class FramesPlayedOfTests : TestFixture { public void IsZeroForNoVoice() { CheckEqual(0L, new XAudio2Module().FramesPlayedOf(Voice.None)); } } public class IsPlayingTests : TestFixture { public void WhenNotLoaded_IsFalse() { CheckFalse(new XAudio2Module().IsPlaying(new Voice(slot: 0, generation: 1))); } public void NoVoice_IsFalse() { using (TestModule test = TestModule.Open()) { if (test == null) { return; } CheckFalse(test.Module.IsPlaying(Voice.None)); } } public void ASoundThatEnds_StopsAtAnUpdate() { using (TestModule test = TestModule.Open()) { if (test == null) { return; } Voice voice = test.Module.Play(test.Module.LoadSound(SilentSounds.Short()), volume: 0f); CheckTrue(test.UpdateUntilFinished(voice)); } } } public class GetPerformanceDataTests : TestFixture { public void WhenNotLoaded_Throws() { CheckThrow(typeof(InvalidOperationException)); new XAudio2Module().GetPerformanceData(); } public void CountsEveryVoiceThePoolsMade() { using (TestModule test = TestModule.Open(monoVoiceCount: 3, stereoVoiceCount: 2)) { if (test == null) { return; } // Each pool exists once per sample format. CheckEqual(10u, test.Module.GetPerformanceData().TotalSourceVoiceCount); } } } public class UpdateTests : TestFixture { public void WhenNotLoaded_DoesNothing() { new XAudio2Module().Update(); } public void LeavesPlayingVoicesAlone() { using (TestModule test = TestModule.Open()) { if (test == null) { return; } Voice voice = test.Module.Play(test.Module.LoadSound(SilentSounds.Long()), volume: 0f); test.Module.Update(); CheckTrue(test.Module.IsPlaying(voice)); } } public void AFinishedVoice_ReleasesItsHold() { using (TestModule test = TestModule.Open()) { if (test == null) { return; } XAudio2Sound sound = (XAudio2Sound)test.Module.LoadSound(SilentSounds.Short()); test.Module.Play(sound, volume: 0f); CheckTrue(test.UpdateUntil(() => sound.HoldCount == 0)); CheckFalse(sound.IsPinned); } } public void FreesAFinishedVoiceForTheNextSound() { using (TestModule test = TestModule.Open(monoVoiceCount: 1, stereoVoiceCount: 1)) { if (test == null) { return; } Sound sound = test.Module.LoadSound(SilentSounds.Short()); test.Module.Play(sound, volume: 0f); CheckFalse(test.UpdateUntilPlayable(sound).IsNone); } } } } }