From f3f634e969607be08ba6e592d4b7454a2e53692f Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 8 Jun 2021 15:05:14 +0900 Subject: [PATCH 1/2] Clean up previous sample immediately on skin source change to avoid `Play` after disposal This seems to be the simplest way to avoid calls to `Play` after the underlying sample may have been disposed. As per the issue thread, a local workaround is acceptable here. Closes #13223. --- osu.Game/Skinning/PoolableSkinnableSample.cs | 44 +++++++++++++++++--- 1 file changed, 39 insertions(+), 5 deletions(-) diff --git a/osu.Game/Skinning/PoolableSkinnableSample.cs b/osu.Game/Skinning/PoolableSkinnableSample.cs index b04158a58f..7565417b7f 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,13 @@ namespace osu.Game.Skinning } } + protected override void Dispose(bool isDisposing) + { + base.Dispose(isDisposing); + + CurrentSkin.SourceChanged -= skinChangedImmediate; + } + #region Re-expose AudioContainer public BindableNumber Volume => sampleContainer.Volume; From 67135ce3dbaa2ea8a55abba016fd525d16f1ad2a Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Tue, 8 Jun 2021 16:15:17 +0900 Subject: [PATCH 2/2] Add null check --- osu.Game/Skinning/PoolableSkinnableSample.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/osu.Game/Skinning/PoolableSkinnableSample.cs b/osu.Game/Skinning/PoolableSkinnableSample.cs index 7565417b7f..9acc1f1a9d 100644 --- a/osu.Game/Skinning/PoolableSkinnableSample.cs +++ b/osu.Game/Skinning/PoolableSkinnableSample.cs @@ -186,7 +186,8 @@ namespace osu.Game.Skinning { base.Dispose(isDisposing); - CurrentSkin.SourceChanged -= skinChangedImmediate; + if (CurrentSkin != null) + CurrentSkin.SourceChanged -= skinChangedImmediate; } #region Re-expose AudioContainer