diff --git a/osu.Game/Screens/Edit/Components/FormSampleSet.cs b/osu.Game/Screens/Edit/Components/FormSampleSet.cs
index a96b255b5c..2d3404a513 100644
--- a/osu.Game/Screens/Edit/Components/FormSampleSet.cs
+++ b/osu.Game/Screens/Edit/Components/FormSampleSet.cs
@@ -289,18 +289,7 @@ namespace osu.Game.Screens.Edit.Components
AddInternal(hoverSounds = (ActualFilename.Value == null ? new HoverClickSounds(HoverSampleSet.Button) : new HoverSounds(HoverSampleSet.Button)));
- if (ActualFilename.Value != null)
- {
- // to cover all bases, invalidate the extensionless filename (which gameplay is most likely to use)
- // as well as the filename with extension (which we are using here).
- editorBeatmap?.BeatmapSkin?.Skin.Samples?.Invalidate(ExpectedFilename.Value);
- editorBeatmap?.BeatmapSkin?.Skin.Samples?.Invalidate(ActualFilename.Value);
- sample = editorBeatmap?.BeatmapSkin?.Skin.Samples?.Get(ActualFilename.Value);
- }
- else
- {
- sample = null;
- }
+ sample = ActualFilename.Value != null ? editorBeatmap?.BeatmapSkin?.Skin.Samples?.Get(ActualFilename.Value) : null;
});
protected override bool OnHover(HoverEvent e)
diff --git a/osu.Game/Screens/Edit/EditorBeatmapSkin.cs b/osu.Game/Screens/Edit/EditorBeatmapSkin.cs
index f47a6b020e..a88330f772 100644
--- a/osu.Game/Screens/Edit/EditorBeatmapSkin.cs
+++ b/osu.Game/Screens/Edit/EditorBeatmapSkin.cs
@@ -61,7 +61,13 @@ namespace osu.Game.Screens.Edit
ComboColours.BindCollectionChanged((_, _) => updateColours());
if (skin.BeatmapSetResources != null)
- skin.BeatmapSetResources.CacheInvalidated += InvokeSkinChanged;
+ skin.BeatmapSetResources.CacheInvalidated += beatmapResourcesInvalidated;
+ }
+
+ private void beatmapResourcesInvalidated()
+ {
+ Skin.RecycleSamples();
+ InvokeSkinChanged();
}
public void InvokeSkinChanged() => BeatmapSkinChanged?.Invoke();
@@ -149,7 +155,7 @@ namespace osu.Game.Screens.Edit
public void Dispose()
{
if (Skin.BeatmapSetResources != null)
- Skin.BeatmapSetResources.CacheInvalidated -= InvokeSkinChanged;
+ Skin.BeatmapSetResources.CacheInvalidated -= beatmapResourcesInvalidated;
Skin.Dispose();
}
diff --git a/osu.Game/Skinning/Skin.cs b/osu.Game/Skinning/Skin.cs
index 3affd5afa6..d50fc2e3d0 100644
--- a/osu.Game/Skinning/Skin.cs
+++ b/osu.Game/Skinning/Skin.cs
@@ -38,7 +38,7 @@ namespace osu.Game.Skinning
///
/// A sample store which can be used to perform user file lookups for this skin.
///
- protected internal ISampleStore? Samples { get; }
+ protected internal ISampleStore? Samples { get; private set; }
public readonly Live SkinInfo;
@@ -84,18 +84,7 @@ namespace osu.Game.Skinning
store.AddStore(new RealmBackedResourceStore(SkinInfo, resources.Files, resources.RealmAccess));
- var samples = resources.AudioManager?.GetSampleStore(store);
-
- if (samples != null)
- {
- samples.PlaybackConcurrency = OsuGameBase.SAMPLE_CONCURRENCY;
-
- // osu-stable performs audio lookups in order of wav -> mp3 -> ogg.
- // The GetSampleStore() call above internally adds wav and mp3, so ogg is added at the end to ensure expected ordering.
- samples.AddExtension(@"ogg");
- }
-
- Samples = samples;
+ RecycleSamples();
Textures = new TextureStore(resources.Renderer, CreateTextureLoaderStore(resources, store));
}
else
@@ -153,6 +142,30 @@ namespace osu.Game.Skinning
}
}
+ ///
+ /// Recreates .
+ /// All users of samples from the skin are expected to manually re-retrieve their samples from this skin after this is called.
+ /// Exposed as public for the purpose of e.g. editing flows where the skin's set of available samples changes.
+ /// In such a scenario a full recycle of the store is required to avoid accidentally retrieving stale samples that don't exist in the skin anymore.
+ ///
+ public void RecycleSamples()
+ {
+ Samples?.Dispose();
+
+ var samples = resources?.AudioManager?.GetSampleStore(store);
+
+ if (samples != null)
+ {
+ samples.PlaybackConcurrency = OsuGameBase.SAMPLE_CONCURRENCY;
+
+ // osu-stable performs audio lookups in order of wav -> mp3 -> ogg.
+ // The GetSampleStore() call above internally adds wav and mp3, so ogg is added at the end to ensure expected ordering.
+ samples.AddExtension(@"ogg");
+ }
+
+ Samples = samples;
+ }
+
protected virtual IResourceStore CreateTextureLoaderStore(IStorageResourceProvider resources, IResourceStore storage)
=> new MaxDimensionLimitedTextureLoaderStore(resources.CreateTextureLoaderStore(storage));