diff --git a/osu.Game/Skinning/PoolableSkinnableSample.cs b/osu.Game/Skinning/PoolableSkinnableSample.cs index b04158a58f..9acc1f1a9d 100644 --- a/osu.Game/Skinning/PoolableSkinnableSample.cs +++ b/osu.Game/Skinning/PoolableSkinnableSample.cs @@ -2,6 +2,7 @@ // See the LICENCE file in the repository root for full licence text. using System; +using System.Linq; using JetBrains.Annotations; using osu.Framework.Allocation; using osu.Framework.Audio; @@ -70,22 +71,48 @@ namespace osu.Game.Skinning updateSample(); } + protected override void LoadComplete() + { + base.LoadComplete(); + + CurrentSkin.SourceChanged += skinChangedImmediate; + } + + private void skinChangedImmediate() + { + // Clean up the previous sample immediately on a source change. + // This avoids a potential call to Play() of an already disposed sample (samples are disposed along with the skin, but SkinChanged is scheduled). + clearPreviousSamples(); + } + protected override void SkinChanged(ISkinSource skin, bool allowFallback) { base.SkinChanged(skin, allowFallback); updateSample(); } + /// + /// Whether this sample was playing before a skin source change. + /// + private bool wasPlaying; + + private void clearPreviousSamples() + { + // only run if the samples aren't already cleared. + // this ensures the "wasPlaying" state is stored correctly even if multiple clear calls are executed. + if (!sampleContainer.Any()) return; + + wasPlaying = Playing; + + sampleContainer.Clear(); + Sample = null; + } + private void updateSample() { if (sampleInfo == null) return; - bool wasPlaying = Playing; - - sampleContainer.Clear(); - Sample = null; - var sample = CurrentSkin.GetSample(sampleInfo); if (sample == null && AllowDefaultFallback) @@ -155,6 +182,14 @@ namespace osu.Game.Skinning } } + protected override void Dispose(bool isDisposing) + { + base.Dispose(isDisposing); + + if (CurrentSkin != null) + CurrentSkin.SourceChanged -= skinChangedImmediate; + } + #region Re-expose AudioContainer public BindableNumber Volume => sampleContainer.Volume;