// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. using System.Reflection; using System.Threading.Tasks; using NUnit.Framework; using osu.Framework.Audio; using osu.Framework.Audio.Sample; using osu.Framework.Audio.Track; using osu.Framework.IO.Stores; using osu.Framework.Threading; namespace osu.Framework.Tests.Audio { [TestFixture] public class AudioComponentTest { private AudioThread thread; private NamespacedResourceStore store; private AudioManager manager; [SetUp] public void SetUp() { thread = new AudioThread(); store = new NamespacedResourceStore(new DllResourceStore(new AssemblyName("osu.Framework")), @"Resources"); manager = new AudioManager(thread, store, store); thread.Start(); } [TearDown] public void TearDown() { Assert.IsFalse(thread.Exited); thread.Exit(); manager?.Dispose(); AudioThreadTest.WaitForOrAssert(() => thread.Exited, "Audio thread did not exit in time"); } [Test] public void TestNestedStoreAdjustments() { var customStore = manager.GetSampleStore(new ResourceStore()); checkAggregateVolume(manager.Samples, 1); checkAggregateVolume(customStore, 1); manager.Samples.Volume.Value = 0.5; waitAudioFrame(); checkAggregateVolume(manager.Samples, 0.5); checkAggregateVolume(customStore, 0.5); customStore.Volume.Value = 0.5; waitAudioFrame(); checkAggregateVolume(manager.Samples, 0.5); checkAggregateVolume(customStore, 0.25); } private void checkAggregateVolume(ISampleStore store, double expected) { Assert.AreEqual(expected, store.AggregateVolume.Value); } [Test] public void TestVirtualTrack() { var track = manager.Tracks.GetVirtual(); waitAudioFrame(); checkTrackCount(1); Assert.IsFalse(track.IsRunning); Assert.AreEqual(0, track.CurrentTime); track.Start(); waitAudioFrame(); Assert.Greater(track.CurrentTime, 0); track.Stop(); Assert.IsFalse(track.IsRunning); track.Dispose(); waitAudioFrame(); checkTrackCount(0); } [Test] public void TestTrackVirtualSeekCurrent() { var trackVirtual = manager.Tracks.GetVirtual(); trackVirtual.Start(); waitAudioFrame(); Assert.Greater(trackVirtual.CurrentTime, 0); trackVirtual.Tempo.Value = 2.0f; trackVirtual.Frequency.Value = 2.0f; waitAudioFrame(); Assert.AreEqual(4.0f, trackVirtual.Rate); trackVirtual.Stop(); var stoppedTime = trackVirtual.CurrentTime; Assert.Greater(stoppedTime, 0); trackVirtual.Seek(stoppedTime); Assert.AreEqual(stoppedTime, trackVirtual.CurrentTime); } private void checkTrackCount(int expected) => Assert.AreEqual(expected, ((TrackStore)manager.Tracks).Items.Count); /// /// Block for a specified number of audio thread frames. /// /// The number of frames to wait for. Two frames is generally considered safest. private void waitAudioFrame(int count = 2) { var cts = new TaskCompletionSource(); void runScheduled() { thread.Scheduler.Add(() => { if (count-- > 0) runScheduled(); else { cts.SetResult(true); } }); } runScheduled(); Task.WaitAll(cts.Task); } } }