A game framework written with osu! in mind.
1// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
2// See the LICENCE file in the repository root for full licence text.
3
4using System.Reflection;
5using System.Threading.Tasks;
6using NUnit.Framework;
7using osu.Framework.Audio;
8using osu.Framework.Audio.Sample;
9using osu.Framework.Audio.Track;
10using osu.Framework.IO.Stores;
11using osu.Framework.Threading;
12
13namespace osu.Framework.Tests.Audio
14{
15 [TestFixture]
16 public class AudioComponentTest
17 {
18 private AudioThread thread;
19 private NamespacedResourceStore<byte[]> store;
20 private AudioManager manager;
21
22 [SetUp]
23 public void SetUp()
24 {
25 thread = new AudioThread();
26 store = new NamespacedResourceStore<byte[]>(new DllResourceStore(new AssemblyName("osu.Framework")), @"Resources");
27
28 manager = new AudioManager(thread, store, store);
29
30 thread.Start();
31 }
32
33 [TearDown]
34 public void TearDown()
35 {
36 Assert.IsFalse(thread.Exited);
37
38 thread.Exit();
39
40 manager?.Dispose();
41
42 AudioThreadTest.WaitForOrAssert(() => thread.Exited, "Audio thread did not exit in time");
43 }
44
45 [Test]
46 public void TestNestedStoreAdjustments()
47 {
48 var customStore = manager.GetSampleStore(new ResourceStore<byte[]>());
49
50 checkAggregateVolume(manager.Samples, 1);
51 checkAggregateVolume(customStore, 1);
52
53 manager.Samples.Volume.Value = 0.5;
54
55 waitAudioFrame();
56
57 checkAggregateVolume(manager.Samples, 0.5);
58 checkAggregateVolume(customStore, 0.5);
59
60 customStore.Volume.Value = 0.5;
61
62 waitAudioFrame();
63
64 checkAggregateVolume(manager.Samples, 0.5);
65 checkAggregateVolume(customStore, 0.25);
66 }
67
68 private void checkAggregateVolume(ISampleStore store, double expected)
69 {
70 Assert.AreEqual(expected, store.AggregateVolume.Value);
71 }
72
73 [Test]
74 public void TestVirtualTrack()
75 {
76 var track = manager.Tracks.GetVirtual();
77
78 waitAudioFrame();
79
80 checkTrackCount(1);
81
82 Assert.IsFalse(track.IsRunning);
83 Assert.AreEqual(0, track.CurrentTime);
84
85 track.Start();
86 waitAudioFrame();
87
88 Assert.Greater(track.CurrentTime, 0);
89
90 track.Stop();
91 Assert.IsFalse(track.IsRunning);
92
93 track.Dispose();
94 waitAudioFrame();
95
96 checkTrackCount(0);
97 }
98
99 [Test]
100 public void TestTrackVirtualSeekCurrent()
101 {
102 var trackVirtual = manager.Tracks.GetVirtual();
103 trackVirtual.Start();
104
105 waitAudioFrame();
106
107 Assert.Greater(trackVirtual.CurrentTime, 0);
108
109 trackVirtual.Tempo.Value = 2.0f;
110 trackVirtual.Frequency.Value = 2.0f;
111
112 waitAudioFrame();
113
114 Assert.AreEqual(4.0f, trackVirtual.Rate);
115
116 trackVirtual.Stop();
117 var stoppedTime = trackVirtual.CurrentTime;
118 Assert.Greater(stoppedTime, 0);
119
120 trackVirtual.Seek(stoppedTime);
121
122 Assert.AreEqual(stoppedTime, trackVirtual.CurrentTime);
123 }
124
125 private void checkTrackCount(int expected)
126 => Assert.AreEqual(expected, ((TrackStore)manager.Tracks).Items.Count);
127
128 /// <summary>
129 /// Block for a specified number of audio thread frames.
130 /// </summary>
131 /// <param name="count">The number of frames to wait for. Two frames is generally considered safest.</param>
132 private void waitAudioFrame(int count = 2)
133 {
134 var cts = new TaskCompletionSource<bool>();
135
136 void runScheduled()
137 {
138 thread.Scheduler.Add(() =>
139 {
140 if (count-- > 0)
141 runScheduled();
142 else
143 {
144 cts.SetResult(true);
145 }
146 });
147 }
148
149 runScheduled();
150
151 Task.WaitAll(cts.Task);
152 }
153 }
154}