diff --git a/osu.Framework.Tests/Audio/AudioManagerWithDeviceLoss.cs b/osu.Framework.Tests/Audio/AudioManagerWithDeviceLoss.cs
index 311468c0b6..7d065f47bf 100644
--- a/osu.Framework.Tests/Audio/AudioManagerWithDeviceLoss.cs
+++ b/osu.Framework.Tests/Audio/AudioManagerWithDeviceLoss.cs
@@ -14,7 +14,7 @@ namespace osu.Framework.Tests.Audio
/// that can simulate the loss of a device.
/// This will NOT work without a physical audio device!
///
- internal class AudioManagerWithDeviceLoss : AudioManager
+ internal class AudioManagerWithDeviceLoss : BassAudioManager
{
public AudioManagerWithDeviceLoss(AudioThread audioThread, ResourceStore trackStore, ResourceStore sampleStore)
: base(audioThread, trackStore, sampleStore)
diff --git a/osu.Framework.Tests/Audio/AudioTestComponents.cs b/osu.Framework.Tests/Audio/AudioTestComponents.cs
new file mode 100644
index 0000000000..39796d2ed1
--- /dev/null
+++ b/osu.Framework.Tests/Audio/AudioTestComponents.cs
@@ -0,0 +1,91 @@
+// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
+// See the LICENCE file in the repository root for full licence text.
+
+using System;
+using osu.Framework.Audio.Mixing;
+using osu.Framework.Audio.Sample;
+using osu.Framework.Audio.Track;
+using osu.Framework.Audio;
+using System.IO;
+using osu.Framework.IO.Stores;
+
+namespace osu.Framework.Tests.Audio
+{
+ public abstract class AudioTestComponents : IDisposable
+ {
+ public enum Type
+ {
+ BASS,
+ SDL3
+ }
+
+ internal readonly AudioMixer Mixer;
+ public readonly DllResourceStore Resources;
+ internal readonly TrackStore TrackStore;
+ internal readonly SampleStore SampleStore;
+
+ protected readonly AudioCollectionManager AllComponents = new AudioCollectionManager();
+ protected readonly AudioCollectionManager MixerComponents = new AudioCollectionManager();
+
+ protected AudioTestComponents(bool init)
+ {
+ Prepare();
+
+ if (init)
+ Init();
+
+ AllComponents.AddItem(MixerComponents);
+
+ Mixer = CreateMixer();
+ Resources = new DllResourceStore(typeof(TrackBassTest).Assembly);
+ TrackStore = new TrackStore(Resources, Mixer, CreateTrack);
+ SampleStore = new SampleStore(Resources, Mixer, CreateSampleFactory);
+
+ Add(TrackStore, SampleStore);
+ }
+
+ protected virtual void Prepare()
+ {
+ }
+
+ internal abstract Track CreateTrack(Stream data, string name);
+
+ internal abstract SampleFactory CreateSampleFactory(Stream stream, string name, AudioMixer mixer, int playbackConcurrency);
+
+ public abstract void Init();
+
+ public virtual void Add(params AudioComponent[] component)
+ {
+ foreach (var c in component)
+ AllComponents.AddItem(c);
+ }
+
+ public abstract AudioMixer CreateMixer();
+
+ public virtual void Update()
+ {
+ RunOnAudioThread(AllComponents.Update);
+ }
+
+ ///
+ /// Runs an on a newly created audio thread, and blocks until it has been run to completion.
+ ///
+ /// The action to run on the audio thread.
+ public virtual void RunOnAudioThread(Action action) => AudioTestHelper.RunOnAudioThread(action);
+
+ internal Track GetTrack() => TrackStore.Get("Resources.Tracks.sample-track.mp3");
+ internal Sample GetSample() => SampleStore.Get("Resources.Tracks.sample-track.mp3");
+
+ public void Dispose() => RunOnAudioThread(() =>
+ {
+ AllComponents.Dispose();
+ AllComponents.Update(); // Actually runs the disposal.
+
+ DisposeInternal();
+ });
+
+ public virtual void DisposeInternal()
+ {
+ }
+ }
+}
diff --git a/osu.Framework.Tests/Audio/BassAudioMixerTest.cs b/osu.Framework.Tests/Audio/BassAudioMixerTest.cs
index 23b5a10a75..775d4e5bdd 100644
--- a/osu.Framework.Tests/Audio/BassAudioMixerTest.cs
+++ b/osu.Framework.Tests/Audio/BassAudioMixerTest.cs
@@ -8,6 +8,7 @@
using ManagedBass;
using ManagedBass.Mix;
using NUnit.Framework;
+using osu.Framework.Audio.Mixing;
using osu.Framework.Audio.Mixing.Bass;
using osu.Framework.Audio.Sample;
using osu.Framework.Audio.Track;
@@ -18,170 +19,233 @@ namespace osu.Framework.Tests.Audio
[TestFixture]
public class BassAudioMixerTest
{
- private BassTestComponents bass;
- private TrackBass track;
- private SampleBass sample;
+ private AudioTestComponents.Type type;
+ private AudioTestComponents audio;
+ private AudioMixer mixer;
+ private Track track;
+ private Sample sample;
- [SetUp]
- public void Setup()
+ [TearDown]
+ public void Teardown()
+ {
+ audio?.Dispose();
+ }
+
+ private void setupBackend(AudioTestComponents.Type id, bool loadTrack = false)
{
- bass = new BassTestComponents();
- track = bass.GetTrack();
- sample = bass.GetSample();
+ type = id;
+
+ if (id == AudioTestComponents.Type.BASS)
+ {
+ audio = new BassTestComponents();
+ track = audio.GetTrack();
+ sample = audio.GetSample();
+ }
+ else if (id == AudioTestComponents.Type.SDL3)
+ {
+ audio = new SDL3AudioTestComponents();
+ track = audio.GetTrack();
+ sample = audio.GetSample();
- bass.Update();
+ if (loadTrack)
+ ((SDL3AudioTestComponents)audio).WaitUntilTrackIsLoaded((TrackSDL3)track);
+ }
+ else
+ {
+ throw new InvalidOperationException("not a supported id");
+ }
+
+ audio.Update();
+ mixer = audio.Mixer;
}
- [TearDown]
- public void Teardown()
+ private void assertThatMixerContainsChannel(AudioMixer mixer, IAudioChannel channel)
{
- bass?.Dispose();
+ if (type == AudioTestComponents.Type.BASS)
+ Assert.That(BassMix.ChannelGetMixer(((IBassAudioChannel)channel).Handle), Is.EqualTo(((BassAudioMixer)mixer).Handle));
+ else
+ Assert.That(channel.Mixer == mixer, Is.True);
}
[Test]
public void TestMixerInitialised()
{
- Assert.That(bass.Mixer.Handle, Is.Not.Zero);
+ setupBackend(AudioTestComponents.Type.BASS);
+
+ Assert.That(((BassAudioMixer)mixer).Handle, Is.Not.Zero);
}
- [Test]
- public void TestAddedToGlobalMixerByDefault()
+ [TestCase(AudioTestComponents.Type.BASS)]
+ [TestCase(AudioTestComponents.Type.SDL3)]
+ public void TestAddedToGlobalMixerByDefault(AudioTestComponents.Type id)
{
- Assert.That(BassMix.ChannelGetMixer(getHandle()), Is.EqualTo(bass.Mixer.Handle));
+ setupBackend(id);
+
+ assertThatMixerContainsChannel(mixer, track);
}
- [Test]
- public void TestCannotBeRemovedFromGlobalMixer()
+ [TestCase(AudioTestComponents.Type.BASS)]
+ [TestCase(AudioTestComponents.Type.SDL3)]
+ public void TestCannotBeRemovedFromGlobalMixerBass(AudioTestComponents.Type id)
{
- bass.Mixer.Remove(track);
- bass.Update();
+ setupBackend(id);
+
+ mixer.Remove(track);
+ audio.Update();
- Assert.That(BassMix.ChannelGetMixer(getHandle()), Is.EqualTo(bass.Mixer.Handle));
+ assertThatMixerContainsChannel(mixer, track);
}
- [Test]
- public void TestTrackIsMovedBetweenMixers()
+ [TestCase(AudioTestComponents.Type.BASS)]
+ [TestCase(AudioTestComponents.Type.SDL3)]
+ public void TestTrackIsMovedBetweenMixers(AudioTestComponents.Type id)
{
- var secondMixer = bass.CreateMixer();
+ setupBackend(id);
+
+ var secondMixer = audio.CreateMixer();
secondMixer.Add(track);
- bass.Update();
+ audio.Update();
- Assert.That(BassMix.ChannelGetMixer(getHandle()), Is.EqualTo(secondMixer.Handle));
+ assertThatMixerContainsChannel(secondMixer, track);
- bass.Mixer.Add(track);
- bass.Update();
+ mixer.Add(track);
+ audio.Update();
- Assert.That(BassMix.ChannelGetMixer(getHandle()), Is.EqualTo(bass.Mixer.Handle));
+ assertThatMixerContainsChannel(mixer, track);
}
- [Test]
- public void TestMovedToGlobalMixerWhenRemovedFromMixer()
+ [TestCase(AudioTestComponents.Type.BASS)]
+ [TestCase(AudioTestComponents.Type.SDL3)]
+ public void TestMovedToGlobalMixerWhenRemovedFromMixer(AudioTestComponents.Type id)
{
- var secondMixer = bass.CreateMixer();
+ setupBackend(id);
+
+ var secondMixer = audio.CreateMixer();
secondMixer.Add(track);
secondMixer.Remove(track);
- bass.Update();
+ audio.Update();
- Assert.That(BassMix.ChannelGetMixer(getHandle()), Is.EqualTo(bass.Mixer.Handle));
+ assertThatMixerContainsChannel(mixer, track);
}
- [Test]
- public void TestVirtualTrackCanBeAddedAndRemoved()
+ [TestCase(AudioTestComponents.Type.BASS)]
+ [TestCase(AudioTestComponents.Type.SDL3)]
+ public void TestVirtualTrackCanBeAddedAndRemoved(AudioTestComponents.Type id)
{
- var secondMixer = bass.CreateMixer();
- var virtualTrack = bass.TrackStore.GetVirtual();
+ setupBackend(id);
+
+ var secondMixer = audio.CreateMixer();
+ var virtualTrack = audio.TrackStore.GetVirtual();
secondMixer.Add(virtualTrack);
- bass.Update();
+ audio.Update();
secondMixer.Remove(virtualTrack);
- bass.Update();
+ audio.Update();
}
- [Test]
- public void TestFreedChannelRemovedFromDefault()
+ [TestCase(AudioTestComponents.Type.BASS)]
+ [TestCase(AudioTestComponents.Type.SDL3)]
+ public void TestFreedChannelRemovedFromDefault(AudioTestComponents.Type id)
{
+ setupBackend(id);
+
track.Dispose();
- bass.Update();
+ audio.Update();
- Assert.That(BassMix.ChannelGetMixer(getHandle()), Is.Zero);
+ if (id == AudioTestComponents.Type.BASS)
+ Assert.That(BassMix.ChannelGetMixer(((IBassAudioChannel)track).Handle), Is.Zero);
+ else
+ Assert.That(((IAudioChannel)track).Mixer, Is.Null);
}
- [Test]
- public void TestChannelMovedToGlobalMixerAfterDispose()
+ [TestCase(AudioTestComponents.Type.BASS)]
+ [TestCase(AudioTestComponents.Type.SDL3)]
+ public void TestChannelMovedToGlobalMixerAfterDispose(AudioTestComponents.Type id)
{
- var secondMixer = bass.CreateMixer();
+ setupBackend(id);
+
+ var secondMixer = audio.CreateMixer();
secondMixer.Add(track);
- bass.Update();
+ audio.Update();
secondMixer.Dispose();
- bass.Update();
+ audio.Update();
- Assert.That(BassMix.ChannelGetMixer(getHandle()), Is.EqualTo(bass.Mixer.Handle));
+ assertThatMixerContainsChannel(mixer, track);
}
- [Test]
- public void TestPlayPauseStop()
+ [TestCase(AudioTestComponents.Type.BASS)]
+ [TestCase(AudioTestComponents.Type.SDL3)]
+ public void TestPlayPauseStop(AudioTestComponents.Type id)
{
+ setupBackend(id, true);
+
Assert.That(!track.IsRunning);
- bass.RunOnAudioThread(() => track.Start());
- bass.Update();
+ audio.RunOnAudioThread(() => track.Start());
+ audio.Update();
Assert.That(track.IsRunning);
- bass.RunOnAudioThread(() => track.Stop());
- bass.Update();
+ audio.RunOnAudioThread(() => track.Stop());
+ audio.Update();
Assert.That(!track.IsRunning);
- bass.RunOnAudioThread(() =>
+ audio.RunOnAudioThread(() =>
{
track.Seek(track.Length - 1000);
track.Start();
});
- bass.Update();
+ audio.Update();
Assert.That(() =>
{
- bass.Update();
+ audio.Update();
return !track.IsRunning;
}, Is.True.After(3000));
}
- [Test]
- public void TestChannelRetainsPlayingStateWhenMovedBetweenMixers()
+ [TestCase(AudioTestComponents.Type.BASS)]
+ [TestCase(AudioTestComponents.Type.SDL3)]
+ public void TestChannelRetainsPlayingStateWhenMovedBetweenMixers(AudioTestComponents.Type id)
{
- var secondMixer = bass.CreateMixer();
+ setupBackend(id);
+
+ var secondMixer = audio.CreateMixer();
secondMixer.Add(track);
- bass.Update();
+ audio.Update();
Assert.That(!track.IsRunning);
- bass.RunOnAudioThread(() => track.Start());
- bass.Update();
+ audio.RunOnAudioThread(() => track.Start());
+ audio.Update();
Assert.That(track.IsRunning);
- bass.Mixer.Add(track);
- bass.Update();
+ mixer.Add(track);
+ audio.Update();
Assert.That(track.IsRunning);
}
- [Test]
- public void TestTrackReferenceLostWhenTrackIsDisposed()
+ [TestCase(AudioTestComponents.Type.SDL3)]
+ [TestCase(AudioTestComponents.Type.BASS)]
+ public void TestTrackReferenceLostWhenTrackIsDisposed(AudioTestComponents.Type id)
{
+ setupBackend(id);
+
var trackReference = testDisposeTrackWithoutReference();
// The first update disposes the track, the second one removes the track from the TrackStore.
- bass.Update();
- bass.Update();
+ audio.Update();
+ audio.Update();
GC.Collect();
GC.WaitForPendingFinalizers();
@@ -189,9 +253,9 @@ public void TestTrackReferenceLostWhenTrackIsDisposed()
Assert.That(!trackReference.TryGetTarget(out _));
}
- private WeakReference testDisposeTrackWithoutReference()
+ private WeakReference