using System; using System.Diagnostics; using System.Threading; using UnitTestSharp; namespace Susurrus.XAudio2.UnitTests { public class SourceVoiceTests : TestFixture { public const int OneTenthSecond = TestDevice.SampleRate / 10; public static bool WaitUntilNothingIsQueued(SourceVoice voice) { Stopwatch clock = Stopwatch.StartNew(); while (voice.GetBuffersQueued() != 0) { if (clock.ElapsedMilliseconds > 5000) { return false; } Thread.Sleep(1); } return true; } /// /// Waits for the mixing thread to take up a frequency ratio, which it does a moment after the call. /// public static bool WaitForRatio(SourceVoice voice, float expected) { Stopwatch clock = Stopwatch.StartNew(); while (voice.GetFrequencyRatio() != expected) { if (clock.ElapsedMilliseconds > 5000) { return false; } Thread.Sleep(1); } return true; } public class StartTests : TestFixture { public void Succeeds() { using (TestDevice device = TestDevice.Open()) { if (device == null) { return; } SourceVoice voice = device.MonoVoice(); CheckTrue(voice.Start().IsOk); voice.Destroy(); } } public void AfterDestroy_Throws() { using (TestDevice device = TestDevice.Open()) { if (device == null) { return; } SourceVoice voice = device.MonoVoice(); voice.Destroy(); CheckThrow(typeof(ObjectDisposedException)); voice.Start(); } } } public class StopTests : TestFixture { public void Succeeds() { using (TestDevice device = TestDevice.Open()) { if (device == null) { return; } SourceVoice voice = device.MonoVoice(); voice.Start(); CheckTrue(voice.Stop().IsOk); voice.Destroy(); } } public void AfterDestroy_Throws() { using (TestDevice device = TestDevice.Open()) { if (device == null) { return; } SourceVoice voice = device.MonoVoice(); voice.Destroy(); CheckThrow(typeof(ObjectDisposedException)); voice.Stop(); } } } public class SubmitTests : TestFixture { public void QueuesTheBuffer() { using (TestDevice device = TestDevice.Open()) using (PinnedSilence silence = new PinnedSilence(OneTenthSecond)) { if (device == null) { return; } SourceVoice voice = device.MonoVoice(); CheckTrue(voice.Submit(silence.Buffer()).IsOk); CheckEqual(1, voice.GetBuffersQueued()); voice.Destroy(); } } public void ABufferOfNoBytes_FailsWithAStatus() { using (TestDevice device = TestDevice.Open()) { if (device == null) { return; } SourceVoice voice = device.MonoVoice(); Amphora.Status status = voice.Submit(new XAudio2Buffer()); CheckEqual(XAudio2Failures.CallFailed, status.Kind); voice.Destroy(); } } public void AfterDestroy_Throws() { using (TestDevice device = TestDevice.Open()) using (PinnedSilence silence = new PinnedSilence(OneTenthSecond)) { if (device == null) { return; } SourceVoice voice = device.MonoVoice(); voice.Destroy(); CheckThrow(typeof(ObjectDisposedException)); voice.Submit(silence.Buffer()); } } } public class FlushTests : TestFixture { public void DropsEveryQueuedBuffer() { using (TestDevice device = TestDevice.Open()) using (PinnedSilence silence = new PinnedSilence(OneTenthSecond)) { if (device == null) { return; } SourceVoice voice = device.MonoVoice(); voice.Submit(silence.Buffer()); voice.Submit(silence.Buffer()); CheckTrue(voice.Flush().IsOk); CheckTrue(WaitUntilNothingIsQueued(voice)); voice.Destroy(); } } public void DropsTheBufferThatIsPlaying() { using (TestDevice device = TestDevice.Open()) using (PinnedSilence silence = new PinnedSilence(TestDevice.SampleRate * 10)) { if (device == null) { return; } SourceVoice voice = device.MonoVoice(); voice.Submit(silence.Buffer()); voice.Start(); voice.Flush(); CheckTrue(WaitUntilNothingIsQueued(voice)); voice.Destroy(); } } public void AfterDestroy_Throws() { using (TestDevice device = TestDevice.Open()) { if (device == null) { return; } SourceVoice voice = device.MonoVoice(); voice.Destroy(); CheckThrow(typeof(ObjectDisposedException)); voice.Flush(); } } } public class GetStateTests : TestFixture { public void BeforeAnythingPlays_HasNoSamplesPlayed() { using (TestDevice device = TestDevice.Open()) { if (device == null) { return; } SourceVoice voice = device.MonoVoice(); CheckEqual(0ul, voice.GetState().SamplesPlayed); voice.Destroy(); } } public void WhilePlaying_CountsSamplesPlayed() { using (TestDevice device = TestDevice.Open()) using (PinnedSilence silence = new PinnedSilence(TestDevice.SampleRate * 10)) { if (device == null) { return; } SourceVoice voice = device.MonoVoice(); voice.Submit(silence.Buffer()); voice.Start(); Stopwatch clock = Stopwatch.StartNew(); while (voice.GetState().SamplesPlayed == 0 && clock.ElapsedMilliseconds < 5000) { Thread.Sleep(1); } CheckGreater(voice.GetState().SamplesPlayed, 0ul); voice.Destroy(); } } public void AfterDestroy_Throws() { using (TestDevice device = TestDevice.Open()) { if (device == null) { return; } SourceVoice voice = device.MonoVoice(); voice.Destroy(); CheckThrow(typeof(ObjectDisposedException)); voice.GetState(); } } } public class GetBuffersQueuedTests : TestFixture { public void ReachesZeroOnceTheBufferHasPlayed() { using (TestDevice device = TestDevice.Open()) using (PinnedSilence silence = new PinnedSilence(OneTenthSecond)) { if (device == null) { return; } SourceVoice voice = device.MonoVoice(); voice.Submit(silence.Buffer()); voice.Start(); CheckTrue(WaitUntilNothingIsQueued(voice)); voice.Destroy(); } } public void AfterDestroy_Throws() { using (TestDevice device = TestDevice.Open()) { if (device == null) { return; } SourceVoice voice = device.MonoVoice(); voice.Destroy(); CheckThrow(typeof(ObjectDisposedException)); voice.GetBuffersQueued(); } } } public class SetVolumeTests : TestFixture { public void Succeeds() { using (TestDevice device = TestDevice.Open()) { if (device == null) { return; } SourceVoice voice = device.MonoVoice(); CheckTrue(voice.SetVolume(0.5f).IsOk); voice.Destroy(); } } public void NegativeVolume_Throws() { using (TestDevice device = TestDevice.Open()) { if (device == null) { return; } SourceVoice voice = device.MonoVoice(); CheckThrow(typeof(ArgumentOutOfRangeException)); voice.SetVolume(-1f); } } public void AfterDestroy_Throws() { using (TestDevice device = TestDevice.Open()) { if (device == null) { return; } SourceVoice voice = device.MonoVoice(); voice.Destroy(); CheckThrow(typeof(ObjectDisposedException)); voice.SetVolume(1f); } } } public class SetSampleRateTests : TestFixture { public void ARateWithinTheVoicesRange_Succeeds() { using (TestDevice device = TestDevice.Open()) { if (device == null) { return; } SourceVoice voice = device.MonoVoice(); CheckTrue(voice.SetSampleRate(TestDevice.SampleRate * 2).IsOk); voice.Destroy(); } } public void ARateFarBeyondTheVoicesRange_FailsWithAStatus() { using (TestDevice device = TestDevice.Open()) { if (device == null) { return; } SourceVoice voice = device.MonoVoice(); CheckEqual(XAudio2Failures.CallFailed, voice.SetSampleRate(TestDevice.SampleRate * 2000).Kind); voice.Destroy(); } } public void ZeroRate_Throws() { using (TestDevice device = TestDevice.Open()) { if (device == null) { return; } SourceVoice voice = device.MonoVoice(); CheckThrow(typeof(ArgumentOutOfRangeException)); voice.SetSampleRate(0); } } public void AfterDestroy_Throws() { using (TestDevice device = TestDevice.Open()) { if (device == null) { return; } SourceVoice voice = device.MonoVoice(); voice.Destroy(); CheckThrow(typeof(ObjectDisposedException)); voice.SetSampleRate(TestDevice.SampleRate); } } } public class SetFrequencyRatioTests : TestFixture { public void ARatioWithinTheVoicesRange_IsReadBack() { using (TestDevice device = TestDevice.Open()) { if (device == null) { return; } SourceVoice voice = device.MonoVoice(); CheckTrue(voice.SetFrequencyRatio(1.5f).IsOk); CheckTrue(WaitForRatio(voice, 1.5f)); voice.Destroy(); } } public void TheWholePitchRange_IsAllowedOnAVoiceMadeForIt() { using (TestDevice device = TestDevice.Open()) { if (device == null) { return; } SourceVoice voice = device.MonoVoice(maxFrequencyRatio: Pitch.Max); CheckTrue(voice.SetFrequencyRatio(Pitch.Max).IsOk); CheckTrue(WaitForRatio(voice, Pitch.Max)); CheckTrue(voice.SetFrequencyRatio(Pitch.Min).IsOk); CheckTrue(WaitForRatio(voice, Pitch.Min)); voice.Destroy(); } } public void ARatioOnAVoiceWithAnotherSampleRate_StillGetsTheWholeRange() { using (TestDevice device = TestDevice.Open()) { if (device == null) { return; } SourceVoice voice = device.MonoVoice(maxFrequencyRatio: Pitch.Max); voice.SetSampleRate(TestDevice.SampleRate * 2); CheckTrue(voice.SetFrequencyRatio(Pitch.Max).IsOk); CheckTrue(WaitForRatio(voice, Pitch.Max)); voice.Destroy(); } } public void ARatioBeyondWhatTheVoiceWasMadeFor_IsHeldToTheVoicesMaximum() { using (TestDevice device = TestDevice.Open()) { if (device == null) { return; } SourceVoice voice = device.MonoVoice(maxFrequencyRatio: 2f); voice.SetFrequencyRatio(4f); CheckTrue(WaitForRatio(voice, 2f)); voice.Destroy(); } } public void ARatioOutOfRange_Throws() { using (TestDevice device = TestDevice.Open()) { if (device == null) { return; } SourceVoice voice = device.MonoVoice(); CheckThrow(typeof(ArgumentOutOfRangeException)); voice.SetFrequencyRatio(0f); } } public void AfterDestroy_Throws() { using (TestDevice device = TestDevice.Open()) { if (device == null) { return; } SourceVoice voice = device.MonoVoice(); voice.Destroy(); CheckThrow(typeof(ObjectDisposedException)); voice.SetFrequencyRatio(1f); } } } public class GetFrequencyRatioTests : TestFixture { public void ANewVoice_PlaysAtItsOwnPitch() { using (TestDevice device = TestDevice.Open()) { if (device == null) { return; } SourceVoice voice = device.MonoVoice(); CheckEqual(1f, voice.GetFrequencyRatio()); voice.Destroy(); } } public void AfterDestroy_Throws() { using (TestDevice device = TestDevice.Open()) { if (device == null) { return; } SourceVoice voice = device.MonoVoice(); voice.Destroy(); CheckThrow(typeof(ObjectDisposedException)); voice.GetFrequencyRatio(); } } } public class SetOutputMatrixTests : TestFixture { public void LevelsAreReadBack() { using (TestDevice device = TestDevice.Open()) { if (device == null) { return; } SourceVoice voice = device.MonoVoice(); int channels = device.Master.GetChannelCount(); float[] set = new float[channels]; float[] got = new float[channels]; for (int i = 0; i < channels; ++i) { set[i] = 0.125f * (i + 1); } CheckTrue(voice.SetOutputMatrix(1, channels, set).IsOk); voice.GetOutputMatrix(1, channels, got); for (int i = 0; i < channels; ++i) { CheckEqual(set[i], got[i]); } voice.Destroy(); } } public void LevelsLongerThanTheMatrix_AreAllowed() { using (TestDevice device = TestDevice.Open()) { if (device == null) { return; } SourceVoice voice = device.MonoVoice(); int channels = device.Master.GetChannelCount(); CheckTrue(voice.SetOutputMatrix(1, channels, new float[channels + 3]).IsOk); voice.Destroy(); } } public void ADestinationChannelCountThatIsntTheDevices_FailsWithAStatus() { using (TestDevice device = TestDevice.Open()) { if (device == null) { return; } SourceVoice voice = device.MonoVoice(); int wrong = device.Master.GetChannelCount() + 1; CheckEqual(XAudio2Failures.CallFailed, voice.SetOutputMatrix(1, wrong, new float[wrong]).Kind); voice.Destroy(); } } public void LevelsThatAreTooShort_Throw() { using (TestDevice device = TestDevice.Open()) { if (device == null) { return; } SourceVoice voice = device.MonoVoice(); CheckThrow(typeof(ArgumentException)); voice.SetOutputMatrix(1, 2, new float[1]); } } public void NullLevels_Throws() { using (TestDevice device = TestDevice.Open()) { if (device == null) { return; } SourceVoice voice = device.MonoVoice(); CheckThrow(typeof(ArgumentNullException)); voice.SetOutputMatrix(1, 2, null); } } public void NoChannels_Throws() { using (TestDevice device = TestDevice.Open()) { if (device == null) { return; } SourceVoice voice = device.MonoVoice(); CheckThrow(typeof(ArgumentException)); voice.SetOutputMatrix(0, 2, new float[2]); } } public void AfterDestroy_Throws() { using (TestDevice device = TestDevice.Open()) { if (device == null) { return; } SourceVoice voice = device.MonoVoice(); voice.Destroy(); CheckThrow(typeof(ObjectDisposedException)); voice.SetOutputMatrix(1, 2, new float[2]); } } } public class GetOutputMatrixTests : TestFixture { public void ANewMonoVoice_SendsItsChannelToTheDevice() { using (TestDevice device = TestDevice.Open()) { if (device == null) { return; } SourceVoice voice = device.MonoVoice(); int channels = device.Master.GetChannelCount(); float[] levels = new float[channels]; voice.GetOutputMatrix(1, channels, levels); CheckGreater(Math.Abs(levels[0]), 0f); voice.Destroy(); } } public void LevelsThatAreTooShort_Throw() { using (TestDevice device = TestDevice.Open()) { if (device == null) { return; } SourceVoice voice = device.MonoVoice(); CheckThrow(typeof(ArgumentException)); voice.GetOutputMatrix(1, 2, new float[1]); } } public void AfterDestroy_Throws() { using (TestDevice device = TestDevice.Open()) { if (device == null) { return; } SourceVoice voice = device.MonoVoice(); voice.Destroy(); CheckThrow(typeof(ObjectDisposedException)); voice.GetOutputMatrix(1, 2, new float[2]); } } } public class DestroyTests : TestFixture { public void MarksTheVoiceDestroyed() { using (TestDevice device = TestDevice.Open()) { if (device == null) { return; } SourceVoice voice = device.MonoVoice(); CheckFalse(voice.IsDestroyed); voice.Destroy(); CheckTrue(voice.IsDestroyed); } } public void CanBeCalledTwice() { using (TestDevice device = TestDevice.Open()) { if (device == null) { return; } SourceVoice voice = device.MonoVoice(); voice.Destroy(); voice.Destroy(); } } } } }