using Amphora; using System; using UnitTestSharp; namespace Susurrus.XAudio2.UnitTests { public class XAudio2StreamedSoundTests : TestFixture { // A stream that fails on request, around a real one. public class FlakyStream : ISoundStream { private readonly ISoundStream inner; public FlakyStream(ISoundStream inner) { this.inner = inner; } public bool FailReads { get; set; } public bool FailSeeks { get; set; } public int SampleRate => inner.SampleRate; public int ChannelCount => inner.ChannelCount; public SampleFormat Format => inner.Format; public long FrameCount => inner.FrameCount; public long Position => inner.Position; public StatusOr Read(ByteView destination) { if (FailReads) { return SoundFailures.StreamRequestInvalid.Fail("Reads fail."); } return inner.Read(destination); } public Status Seek(long frame) { if (FailSeeks) { return SoundFailures.StreamRequestInvalid.Fail("Seeks fail."); } return inner.Seek(frame); } public void Dispose() { inner.Dispose(); } } public static ISoundStream OpenWav(int frameCount, int channelCount = 1) { byte[] file = SilentSounds.Wav(frameCount, channelCount, TestDevice.SampleRate); return SoundStreams.Open(new ByteChunks(new ByteView(file))).Value; } public static XAudio2StreamedSound SoundOf(int frameCount) { return new XAudio2StreamedSound(OpenWav(frameCount)); } public class CtorTests : TestFixture { public void BuffersHoldAQuarterOfASecond() { CheckEqual(11025, SoundOf(frameCount: 100).BufferFrames); } public void ABuffersFrameCountIsAtLeastOne() { byte[] file = SilentSounds.Wav(frameCount: 10, channelCount: 1, sampleRate: 3); ISoundStream stream = SoundStreams.Open(new ByteChunks(new ByteView(file))).Value; CheckEqual(1, new XAudio2StreamedSound(stream).BufferFrames); } public void StartsUnpinnedAndNotFed() { XAudio2StreamedSound sound = SoundOf(frameCount: 100); CheckFalse(sound.IsPinned); CheckFalse(sound.IsFed); CheckFalse(sound.IsHeld); } } public class BeginTests : TestFixture { public void HoldsAndPinsTheSound() { XAudio2StreamedSound sound = SoundOf(frameCount: 100); CheckTrue(sound.Begin(Loop.None).IsOk); CheckTrue(sound.IsHeld); CheckTrue(sound.IsPinned); sound.End(); } public void WhileHeld_Throws() { XAudio2StreamedSound sound = SoundOf(frameCount: 100); sound.Begin(Loop.None); CheckThrow(typeof(InvalidOperationException)); sound.Begin(Loop.None); } public void MovesTheStreamBackToItsStart() { XAudio2StreamedSound sound = SoundOf(frameCount: 100); sound.Stream.Seek(40); sound.Begin(Loop.None); CheckEqual(0L, sound.Stream.Position); sound.End(); } public void ASeekThatFails_FailsAndEndsTheFeeding() { FlakyStream stream = new FlakyStream(OpenWav(frameCount: 100)); stream.Seek(40); stream.FailSeeks = true; XAudio2StreamedSound sound = new XAudio2StreamedSound(stream); Status status = sound.Begin(Loop.None); CheckEqual(SoundFailures.StreamRequestInvalid, status.Kind); CheckTrue(sound.IsFed); CheckTrue(sound.IsHeld); sound.End(); } } public class RefillTests : TestFixture { public void QueuesEveryBufferOfALongSound() { using (TestDevice device = TestDevice.Open()) { if (device == null) { return; } SourceVoice voice = device.MonoVoice(); XAudio2StreamedSound sound = SoundOf(frameCount: TestDevice.SampleRate * 3); sound.Begin(Loop.None); CheckTrue(sound.Refill(voice).IsOk); CheckEqual(XAudio2StreamedSound.BufferCount, voice.GetBuffersQueued()); CheckFalse(sound.IsFed); voice.Destroy(); sound.End(); } } public void ASoundShorterThanABuffer_QueuesItAndIsFed() { using (TestDevice device = TestDevice.Open()) { if (device == null) { return; } SourceVoice voice = device.MonoVoice(); XAudio2StreamedSound sound = SoundOf(frameCount: 5000); sound.Begin(Loop.None); sound.Refill(voice); CheckEqual(1, voice.GetBuffersQueued()); CheckTrue(sound.IsFed); voice.Destroy(); sound.End(); } } public void ASoundOfExactlyOneBuffer_QueuesItThenFindsTheEnd() { using (TestDevice device = TestDevice.Open()) { if (device == null) { return; } SourceVoice voice = device.MonoVoice(); XAudio2StreamedSound sound = SoundOf(frameCount: 11025); sound.Begin(Loop.None); sound.Refill(voice); CheckEqual(1, voice.GetBuffersQueued()); CheckTrue(sound.IsFed); voice.Destroy(); sound.End(); } } public void ASoundWithNoFrames_QueuesNothingAndIsFed() { using (TestDevice device = TestDevice.Open()) { if (device == null) { return; } SourceVoice voice = device.MonoVoice(); XAudio2StreamedSound sound = SoundOf(frameCount: 0); sound.Begin(Loop.None); sound.Refill(voice); CheckEqual(0, voice.GetBuffersQueued()); CheckTrue(sound.IsFed); voice.Destroy(); sound.End(); } } public void WithEveryBufferQueued_ReadsNothingMore() { using (TestDevice device = TestDevice.Open()) { if (device == null) { return; } SourceVoice voice = device.MonoVoice(); XAudio2StreamedSound sound = SoundOf(frameCount: TestDevice.SampleRate * 3); sound.Begin(Loop.None); sound.Refill(voice); long position = sound.Stream.Position; sound.Refill(voice); CheckEqual(position, sound.Stream.Position); voice.Destroy(); sound.End(); } } public void ALoopingSound_NeverRunsOut() { using (TestDevice device = TestDevice.Open()) { if (device == null) { return; } SourceVoice voice = device.MonoVoice(); XAudio2StreamedSound sound = SoundOf(frameCount: 5000); sound.Begin(Loop.Whole); sound.Refill(voice); CheckEqual(XAudio2StreamedSound.BufferCount, voice.GetBuffersQueued()); CheckFalse(sound.IsFed); voice.Destroy(); sound.End(); } } public void AReadThatFails_FailsAndEndsTheFeeding() { using (TestDevice device = TestDevice.Open()) { if (device == null) { return; } SourceVoice voice = device.MonoVoice(); FlakyStream stream = new FlakyStream(OpenWav(frameCount: 5000)); XAudio2StreamedSound sound = new XAudio2StreamedSound(stream); sound.Begin(Loop.None); stream.FailReads = true; Status status = sound.Refill(voice); CheckEqual(SoundFailures.StreamRequestInvalid, status.Kind); CheckTrue(sound.IsFed); voice.Destroy(); sound.End(); } } } public class EndTests : TestFixture { public void UnpinsAndFreesTheSound() { XAudio2StreamedSound sound = SoundOf(frameCount: 100); sound.Begin(Loop.None); sound.End(); CheckFalse(sound.IsPinned); CheckFalse(sound.IsHeld); } public void AllowsABeginAgain() { XAudio2StreamedSound sound = SoundOf(frameCount: 100); sound.Begin(Loop.None); sound.End(); CheckTrue(sound.Begin(Loop.None).IsOk); sound.End(); } } } }