diff --git a/osu.Framework/Audio/AudioDecoder.cs b/osu.Framework/Audio/AudioDecoder.cs
index 1b29966f54..6de197ac49 100644
--- a/osu.Framework/Audio/AudioDecoder.cs
+++ b/osu.Framework/Audio/AudioDecoder.cs
@@ -118,8 +118,6 @@ public AudioDecoder(SDL.SDL_AudioSpec spec)
private readonly CancellationTokenSource tokenSource = new CancellationTokenSource();
- private bool disposedValue;
-
///
/// Start decoding in the decoding thread.
///
@@ -369,6 +367,8 @@ private int loadFromStream(AudioDecoderData job)
return 0;
}
+ private bool disposedValue;
+
protected virtual void Dispose(bool disposing)
{
if (!disposedValue)
@@ -395,12 +395,12 @@ protected virtual void Dispose(bool disposing)
~AudioDecoder()
{
- Dispose(disposing: false);
+ Dispose(false);
}
public void Dispose()
{
- Dispose(disposing: true);
+ Dispose(true);
GC.SuppressFinalize(this);
}
}
diff --git a/osu.Framework/Audio/Mixing/SDL2/SDL2AudioMixer.cs b/osu.Framework/Audio/Mixing/SDL2/SDL2AudioMixer.cs
index 7d5746d6de..a7cc7e4ddc 100644
--- a/osu.Framework/Audio/Mixing/SDL2/SDL2AudioMixer.cs
+++ b/osu.Framework/Audio/Mixing/SDL2/SDL2AudioMixer.cs
@@ -86,15 +86,13 @@ public void MixChannelsInto(float[] data)
{
lock (syncRoot)
{
- int sampleCount = data.Length;
- if (ret == null || sampleCount != ret.Length)
- ret = new float[sampleCount];
+ int sampleCount = data.Length;
+ if (ret == null || sampleCount != ret.Length)
+ ret = new float[sampleCount];
- bool useFilters = AudioFilters.Count > 0;
- float[] put = useFilters ? new float[sampleCount] : data;
+ bool useFilters = audioFilters.Count > 0;
+ float[] put = useFilters ? new float[sampleCount] : data;
- lock (activeChannels)
- {
var node = activeChannels.First;
while (node != null)
@@ -123,26 +121,22 @@ public void MixChannelsInto(float[] data)
}
channelCount = activeChannels.Count;
- }
- if (useFilters)
- {
- lock (AudioFilters)
+ if (useFilters)
{
for (int i = 0; i < sampleCount; i++)
{
- foreach (var filter in AudioFilters)
+ foreach (var filter in audioFilters)
{
if (filter.BiQuadFilter != null)
put[i] = filter.BiQuadFilter.Transform(put[i]);
}
}
- }
- mixAudio(data, put, sampleCount, SDL.SDL_MIX_MAXVOLUME);
+ mixAudio(data, put, sampleCount, SDL.SDL_MIX_MAXVOLUME);
+ }
}
}
- }
private void adjustBalance(double balance, float[] audio, int size)
{
@@ -157,7 +151,7 @@ private void adjustBalance(double balance, float[] audio, int size)
}
}
- internal readonly List AudioFilters = new List();
+ private readonly List audioFilters = new List();
private void onEffectsChanged(object? sender, NotifyCollectionChangedEventArgs e) => EnqueueAction(() =>
{
@@ -169,15 +163,15 @@ private void onEffectsChanged(object? sender, NotifyCollectionChangedEventArgs e
{
Debug.Assert(e.NewItems != null);
int startIndex = Math.Max(0, e.NewStartingIndex);
- AudioFilters.InsertRange(startIndex, e.NewItems.OfType().Select(eff => new EffectBox(eff)));
+ audioFilters.InsertRange(startIndex, e.NewItems.OfType().Select(eff => new EffectBox(eff)));
break;
}
case NotifyCollectionChangedAction.Move:
{
- EffectBox effect = AudioFilters[e.OldStartingIndex];
- AudioFilters.RemoveAt(e.OldStartingIndex);
- AudioFilters.Insert(e.NewStartingIndex, effect);
+ EffectBox effect = audioFilters[e.OldStartingIndex];
+ audioFilters.RemoveAt(e.OldStartingIndex);
+ audioFilters.Insert(e.NewStartingIndex, effect);
break;
}
@@ -185,7 +179,7 @@ private void onEffectsChanged(object? sender, NotifyCollectionChangedEventArgs e
{
Debug.Assert(e.OldItems != null);
- AudioFilters.RemoveRange(e.OldStartingIndex, e.OldItems.Count);
+ audioFilters.RemoveRange(e.OldStartingIndex, e.OldItems.Count);
break;
}
@@ -194,13 +188,13 @@ private void onEffectsChanged(object? sender, NotifyCollectionChangedEventArgs e
Debug.Assert(e.NewItems != null);
EffectBox newFilter = new EffectBox((IEffectParameter)e.NewItems[0].AsNonNull());
- AudioFilters[e.NewStartingIndex] = newFilter;
+ audioFilters[e.NewStartingIndex] = newFilter;
break;
}
case NotifyCollectionChangedAction.Reset:
{
- AudioFilters.Clear();
+ audioFilters.Clear();
break;
}
}
diff --git a/osu.Framework/Audio/ResamplingPlayer.cs b/osu.Framework/Audio/ResamplingPlayer.cs
index cac9ef8c88..225eb17b2e 100644
--- a/osu.Framework/Audio/ResamplingPlayer.cs
+++ b/osu.Framework/Audio/ResamplingPlayer.cs
@@ -10,7 +10,7 @@ namespace osu.Framework.Audio
/// Abstract class that's meant to be used with a real player implementation.
/// This class provides resampling on the fly for players.
///
- internal abstract class ResamplingPlayer : AudioComponent
+ internal abstract class ResamplingPlayer
{
///
/// Represents current relative rate. Use to set this variable.
@@ -19,8 +19,8 @@ internal abstract class ResamplingPlayer : AudioComponent
private WdlResampler? resampler;
- private readonly int srcRate;
- private readonly byte srcChannels;
+ protected readonly int SrcRate;
+ protected readonly byte SrcChannels;
///
/// Creates a new .
@@ -29,8 +29,8 @@ internal abstract class ResamplingPlayer : AudioComponent
/// Channels of audio that's given from or
protected ResamplingPlayer(int srcRate, byte srcChannels)
{
- this.srcRate = srcRate;
- this.srcChannels = srcChannels;
+ SrcRate = srcRate;
+ SrcChannels = srcChannels;
}
///
@@ -56,7 +56,7 @@ public void SetRate(double relativeRate)
resampler.SetFeedMode(false);
}
- resampler.SetRates(srcRate, srcRate / relativeRate);
+ resampler.SetRates(SrcRate, SrcRate / relativeRate);
RelativeRate = relativeRate;
}
@@ -65,11 +65,9 @@ protected double GetResampleLatency()
if (resampler == null || RelativeRate == 1)
return 0;
- return latencyCache;
+ return resampler.GetCurrentLatency() * 1000.0d;
}
- private double latencyCache;
-
///
/// Returns rate adjusted audio samples. It calls a parent method if is 1.
///
@@ -83,15 +81,14 @@ public virtual int GetRemainingSamples(float[] data)
if (resampler == null || RelativeRate == 1)
return GetRemainingRawFloats(data, 0, data.Length);
- int requested = data.Length / srcChannels;
- int needed = resampler.ResamplePrepare(requested, srcChannels, out float[] inBuffer, out int inBufferOffset);
- int rawGot = GetRemainingRawFloats(inBuffer, inBufferOffset, needed * srcChannels);
+ int requested = data.Length / SrcChannels;
+ int needed = resampler.ResamplePrepare(requested, SrcChannels, out float[] inBuffer, out int inBufferOffset);
+ int rawGot = GetRemainingRawFloats(inBuffer, inBufferOffset, needed * SrcChannels);
if (rawGot > 0)
{
- int got = resampler.ResampleOut(data, 0, rawGot / srcChannels, requested, srcChannels);
- latencyCache = resampler.GetCurrentLatency() * 1000.0d;
- return got * srcChannels;
+ int got = resampler.ResampleOut(data, 0, rawGot / SrcChannels, requested, SrcChannels);
+ return got * SrcChannels;
}
return 0;
diff --git a/osu.Framework/Audio/SDL2AudioManager.cs b/osu.Framework/Audio/SDL2AudioManager.cs
index e24183d240..a9507af629 100644
--- a/osu.Framework/Audio/SDL2AudioManager.cs
+++ b/osu.Framework/Audio/SDL2AudioManager.cs
@@ -26,8 +26,6 @@ public class SDL2AudioManager : AudioManager
private SDL.SDL_AudioSpec spec;
- public SDL.SDL_AudioSpec Spec => spec;
-
private readonly AudioDecoder decoder;
///
@@ -148,11 +146,11 @@ protected override bool SetAudioDevice(string deviceName = null)
Logger.Log($@"🔈 SDL Audio initialised
Driver: {SDL.SDL_GetCurrentAudioDriver()}
Device Name: {currentDeviceName}
- Frequency: {Spec.freq} hz
- Channels: {Spec.channels}
- Format: {(SDL.SDL_AUDIO_ISSIGNED(Spec.format) ? "" : "un")}signed {SDL.SDL_AUDIO_BITSIZE(Spec.format)} bits{(SDL.SDL_AUDIO_ISFLOAT(Spec.format) ? " (float)" : "")}
- Samples: {Spec.samples} samples
- Buffer size: {Spec.size} bytes");
+ Frequency: {spec.freq} hz
+ Channels: {spec.channels}
+ Format: {(SDL.SDL_AUDIO_ISSIGNED(spec.format) ? "" : "un")}signed {SDL.SDL_AUDIO_BITSIZE(spec.format)} bits{(SDL.SDL_AUDIO_ISFLOAT(spec.format) ? " (float)" : "")}
+ Samples: {spec.samples} samples
+ Buffer size: {spec.size} bytes");
return true;
}
@@ -169,13 +167,13 @@ protected override bool SetAudioDevice(int deviceIndex)
internal override Track.Track GetNewTrack(Stream data, string name)
{
- TrackSDL2 track = new TrackSDL2(name, Spec.freq, Spec.channels, Spec.samples);
+ TrackSDL2 track = new TrackSDL2(name, spec.freq, spec.channels, spec.samples);
decoder.StartDecodingAsync(data, track.AddToQueue, null);
return track;
}
internal override SampleFactory GetSampleFactory(Stream data, string name, AudioMixer mixer, int playbackConcurrency)
- => new SampleSDL2Factory(data, name, (SDL2AudioMixer)mixer, playbackConcurrency, Spec, decoder);
+ => new SampleSDL2Factory(data, name, (SDL2AudioMixer)mixer, playbackConcurrency, spec, decoder);
protected override void Dispose(bool disposing)
{
diff --git a/osu.Framework/Audio/Sample/SampleChannelSDL2.cs b/osu.Framework/Audio/Sample/SampleChannelSDL2.cs
index bcfce97f88..c3dd99fbfd 100644
--- a/osu.Framework/Audio/Sample/SampleChannelSDL2.cs
+++ b/osu.Framework/Audio/Sample/SampleChannelSDL2.cs
@@ -38,17 +38,6 @@ public SampleChannelSDL2(SampleSDL2 sample)
this.sample = sample;
}
- protected override void UpdateChildren()
- {
- base.UpdateChildren();
-
- if (player != null)
- {
- lock (syncRoot)
- player.Update();
- }
- }
-
public override void Play()
{
enqueuedPlaybackStart = true;
@@ -107,7 +96,7 @@ int ISDL2AudioChannel.GetRemainingSamples(float[] data)
ret = player.GetRemainingSamples(data);
- if (player.IsDone())
+ if (player.Done)
playing = false;
}
@@ -134,12 +123,10 @@ protected override void Dispose(bool disposing)
lock (syncRoot)
{
- player?.Dispose();
+ playing = false;
player = null;
}
- playing = false;
-
base.Dispose(disposing);
}
}
diff --git a/osu.Framework/Audio/Sample/SampleSDL2AudioPlayer.cs b/osu.Framework/Audio/Sample/SampleSDL2AudioPlayer.cs
index 3855147723..ab184e7fde 100644
--- a/osu.Framework/Audio/Sample/SampleSDL2AudioPlayer.cs
+++ b/osu.Framework/Audio/Sample/SampleSDL2AudioPlayer.cs
@@ -7,10 +7,10 @@ namespace osu.Framework.Audio.Sample
{
internal class SampleSDL2AudioPlayer : ResamplingPlayer
{
- public override bool IsLoaded => true;
-
private int position;
- private bool done;
+
+ private volatile bool done;
+ public bool Done => done;
private readonly float[] audioData;
@@ -67,10 +67,5 @@ public void Reset(bool resetIndex = true)
if (resetIndex)
position = 0;
}
-
- public bool IsDone()
- {
- return done;
- }
}
}
diff --git a/osu.Framework/Audio/Track/TempoSDL2AudioPlayer.cs b/osu.Framework/Audio/Track/TempoSDL2AudioPlayer.cs
index 89a37d0f24..0c76e29784 100644
--- a/osu.Framework/Audio/Track/TempoSDL2AudioPlayer.cs
+++ b/osu.Framework/Audio/Track/TempoSDL2AudioPlayer.cs
@@ -20,6 +20,8 @@ internal class TempoSDL2AudioPlayer : TrackSDL2AudioPlayer
private bool doneFilling;
private bool donePlaying;
+ public override bool Done => base.Done && (soundTouch == null || donePlaying);
+
///
/// Creates a new .
///
@@ -32,9 +34,8 @@ public TempoSDL2AudioPlayer(int rate, byte channels, int samples)
samplesize = samples;
}
- protected override void UpdateState()
+ public void FillRequiredSamples()
{
- base.UpdateState();
fillSamples(samplesize);
}
@@ -47,18 +48,18 @@ private void fillSamples(int samples)
if (soundTouch == null)
return;
- while (!base.IsDone() && soundTouch.AvailableSamples < samples)
+ while (!base.Done && soundTouch.AvailableSamples < samples)
{
- int getSamples = (int)Math.Ceiling((samples - soundTouch.AvailableSamples) * Tempo) * Channels;
+ int getSamples = (int)Math.Ceiling((samples - soundTouch.AvailableSamples) * Tempo) * SrcChannels;
float[] src = new float[getSamples];
getSamples = base.GetRemainingSamples(src);
if (getSamples <= 0)
break;
- soundTouch.PutSamples(src, getSamples / Channels);
+ soundTouch.PutSamples(src, getSamples / SrcChannels);
}
- if (!doneFilling && base.IsDone())
+ if (!doneFilling && base.Done)
{
soundTouch.Flush();
doneFilling = true;
@@ -78,8 +79,8 @@ public void SetTempo(double tempo)
{
soundTouch = new SoundTouchProcessor
{
- SampleRate = Rate,
- Channels = Channels
+ SampleRate = SrcRate,
+ Channels = SrcChannels
};
soundTouch.SetSetting(SettingId.UseQuickSeek, 1);
soundTouch.SetSetting(SettingId.OverlapDurationMs, 4);
@@ -92,7 +93,7 @@ public void SetTempo(double tempo)
if (Tempo == 1.0f)
{
- int latency = GetTempoLatency() * 4 * Channels;
+ int latency = GetTempoLatency() * 4 * SrcChannels;
StreamPosition = !ReversePlayback ? StreamPosition - latency : StreamPosition + latency;
Reset(false);
soundTouch = null;
@@ -118,7 +119,7 @@ public override int GetRemainingSamples(float[] ret)
if (RelativeRate == 0)
return 0;
- int expected = ret.Length / Channels;
+ int expected = ret.Length / SrcChannels;
if (!doneFilling && soundTouch.AvailableSamples < expected)
{
@@ -130,9 +131,7 @@ public override int GetRemainingSamples(float[] ret)
if (got == 0 && doneFilling)
donePlaying = true;
- timeCache = getCurrentTime();
-
- return got * Channels;
+ return got * SrcChannels;
}
public override void Reset(bool resetPosition = true)
@@ -151,12 +150,13 @@ protected int GetTempoLatency()
return (int)(soundTouch.UnprocessedSampleCount + soundTouch.AvailableSamples * Tempo);
}
- private double timeCache;
-
- private double getCurrentTime()
+ public override double GetCurrentTime()
{
+ if (soundTouch == null)
+ return base.GetCurrentTime();
+
double baseTime = base.GetCurrentTime();
- double latency = (double)GetTempoLatency() / Rate * 1000.0d;
+ double latency = (double)GetTempoLatency() / SrcRate * 1000.0d;
// GetCurrentTime is from TrackAudioPlayer, and fillSample retrieves samples from TrackPlayer.
// fillSample already moved the position of TrackPlayer past the actual playing position, so subtract passed sample count to get actual current time.
@@ -164,24 +164,11 @@ private double getCurrentTime()
return !ReversePlayback ? baseTime - latency : baseTime + latency;
}
- public override double GetCurrentTime()
- {
- if (soundTouch == null)
- return base.GetCurrentTime();
-
- return timeCache;
- }
-
public override void Seek(double seek)
{
base.Seek(seek);
if (soundTouch != null)
fillSamples(samplesize);
}
-
- public override bool IsDone()
- {
- return base.IsDone() && (soundTouch == null || donePlaying);
- }
}
}
diff --git a/osu.Framework/Audio/Track/TrackSDL2.cs b/osu.Framework/Audio/Track/TrackSDL2.cs
index 035f72b2e7..fdc6b44162 100644
--- a/osu.Framework/Audio/Track/TrackSDL2.cs
+++ b/osu.Framework/Audio/Track/TrackSDL2.cs
@@ -12,30 +12,23 @@ namespace osu.Framework.Audio.Track
{
public sealed class TrackSDL2 : Track, ISDL2AudioChannel
{
- public bool Preview { get; private set; }
-
private readonly TempoSDL2AudioPlayer player;
- private volatile bool isLoaded;
-
public override bool IsDummyDevice => false;
+ private volatile bool isLoaded;
public override bool IsLoaded => isLoaded;
private double currentTime;
-
public override double CurrentTime => currentTime;
private volatile bool isRunning;
-
public override bool IsRunning => isRunning;
private volatile bool hasCompleted;
-
public override bool HasCompleted => hasCompleted;
private volatile int bitrate;
-
public override int? Bitrate => bitrate;
public TrackSDL2(string name, int rate, byte channels, int samples)
@@ -49,7 +42,6 @@ public TrackSDL2(string name, int rate, byte channels, int samples)
throw new ArgumentException($"{nameof(TrackSDL2)} does not support {nameof(Tempo)} specifications below {tempo_minimum_supported}. Use {nameof(Frequency)} instead.");
};
- Preview = false;
player = new TempoSDL2AudioPlayer(rate, channels, samples);
}
@@ -80,17 +72,14 @@ protected override void UpdateState()
{
base.UpdateState();
- if (decodeData != null)
+ if (decodeData != null && !isLoaded)
{
- if (!isLoaded)
- {
- Length = decodeData.Length;
- bitrate = decodeData.Bitrate;
- isLoaded = true;
- }
+ Length = decodeData.Length;
+ bitrate = decodeData.Bitrate;
+ isLoaded = true;
}
- if (player.IsDone() && isRunning)
+ if (player.Done && isRunning)
{
if (Looping)
{
@@ -104,16 +93,7 @@ protected override void UpdateState()
}
}
- lock (syncRoot)
- Interlocked.Exchange(ref currentTime, player.GetCurrentTime());
- }
-
- protected override void UpdateChildren()
- {
- base.UpdateChildren();
-
- lock (syncRoot)
- player.Update();
+ // may need to fill soundtouch buffer
}
internal override void OnStateChanged()
@@ -144,7 +124,7 @@ public override async Task SeekAsync(double seek)
return conservativeClamped == seek;
}
- private void seekInternal(double seek)
+ private void seekInternal(double seek) => EnqueueAction(() =>
{
lock (syncRoot)
{
@@ -158,7 +138,7 @@ private void seekInternal(double seek)
Interlocked.Exchange(ref currentTime, player.GetCurrentTime());
}
- }
+ });
public override void Start()
{
@@ -177,10 +157,7 @@ public override Task StartAsync() => EnqueueAction(() =>
hasCompleted = false;
});
- public override void Stop()
- {
- StopAsync().WaitSafely();
- }
+ public override void Stop() => StopAsync().WaitSafely();
public override Task StopAsync() => EnqueueAction(() =>
{
@@ -196,6 +173,8 @@ int ISDL2AudioChannel.GetRemainingSamples(float[] data)
lock (syncRoot)
ret = player.GetRemainingSamples(data);
+ Interlocked.Exchange(ref currentTime, player.GetCurrentTime());
+
if (ret < 0)
{
EnqueueAction(RaiseFailed);
@@ -205,7 +184,7 @@ int ISDL2AudioChannel.GetRemainingSamples(float[] data)
return ret;
}
- bool ISDL2AudioChannel.Playing => isRunning && player.IsDone() == false;
+ bool ISDL2AudioChannel.Playing => isRunning && !player.Done;
int ISDL2AudioChannel.Volume => (int)(AggregateVolume.Value * SDL.SDL_MIX_MAXVOLUME);
diff --git a/osu.Framework/Audio/Track/TrackSDL2AudioPlayer.cs b/osu.Framework/Audio/Track/TrackSDL2AudioPlayer.cs
index 0933ebd9d7..2be64e37f4 100644
--- a/osu.Framework/Audio/Track/TrackSDL2AudioPlayer.cs
+++ b/osu.Framework/Audio/Track/TrackSDL2AudioPlayer.cs
@@ -10,38 +10,30 @@ namespace osu.Framework.Audio.Track
///
/// Mainly returns audio data to .
///
- internal class TrackSDL2AudioPlayer : ResamplingPlayer
+ internal class TrackSDL2AudioPlayer : ResamplingPlayer, IDisposable
{
private volatile bool isLoaded;
- public override bool IsLoaded => isLoaded;
+ public bool IsLoaded => isLoaded;
private volatile bool isLoading;
public bool IsLoading => isLoading;
private volatile bool done;
-
- protected int Rate { get; set; }
- protected int Channels { get; set; }
+ public virtual bool Done => done;
///
/// Returns a byte position converted into milliseconds with configuration set for this player.
///
/// A byte position to convert
///
- public double GetMsFromBytes(long bytePos)
- {
- return bytePos * 1000.0d / Rate / Channels / 4;
- }
+ public double GetMsFromBytes(long bytePos) => bytePos * 1000.0d / SrcRate / SrcChannels / 4;
///
/// Returns a position in milliseconds converted from a byte position with configuration set for this player.
///
/// A position in milliseconds to convert
///
- public long GetBytesFromMs(double seconds)
- {
- return (long)(seconds / 1000.0d * Rate) * Channels * 4;
- }
+ public long GetBytesFromMs(double seconds) => (long)(seconds / 1000.0d * SrcRate) * SrcChannels * 4;
///
/// Stores raw audio data.
@@ -107,9 +99,6 @@ public bool ReversePlayback
public TrackSDL2AudioPlayer(int rate, byte channels)
: base(rate, channels)
{
- Rate = rate;
- Channels = channels;
-
isLoading = false;
isLoaded = false;
}
@@ -204,7 +193,7 @@ protected override int GetRemainingRawBytes(byte[] data)
{
if (ReversePlayback)
{
- int frameSize = Channels * 4;
+ int frameSize = SrcChannels * 4;
byte[] temp = new byte[put];
AudioData.Position -= put;
@@ -252,10 +241,7 @@ public virtual void Reset(bool resetPosition = true)
///
/// Returns current position converted into milliseconds.
///
- public virtual double GetCurrentTime()
- {
- return !ReversePlayback ? GetMsFromBytes(StreamPosition) - GetResampleLatency() : GetMsFromBytes(StreamPosition) + GetResampleLatency();
- }
+ public virtual double GetCurrentTime() => !ReversePlayback ? GetMsFromBytes(StreamPosition) - GetResampleLatency() : GetMsFromBytes(StreamPosition) + GetResampleLatency();
protected long SaveSeek;
@@ -280,25 +266,26 @@ public virtual void Seek(double seek)
}
}
- public virtual bool IsDone()
- {
- return done;
- }
+ private bool disposedValue;
- ~TrackSDL2AudioPlayer()
+ protected virtual void Dispose(bool disposing)
{
- Dispose(false);
+ if (!disposedValue)
+ {
+ if (disposing)
+ {
+ AudioData?.Dispose();
+ AudioData = null;
+ }
+
+ disposedValue = true;
+ }
}
- protected override void Dispose(bool disposing)
+ public void Dispose()
{
- if (IsDisposed)
- return;
-
- AudioData?.Dispose();
- AudioData = null;
-
- base.Dispose(disposing);
+ Dispose(true);
+ GC.SuppressFinalize(this);
}
}
}