mirror of
https://github.com/ppy/osu
synced 2024-12-28 18:02:53 +00:00
Merge branch 'master' into gameplay/argon-key-counter_le-retour
This commit is contained in:
commit
0c71fa1bbd
@ -1,9 +1,8 @@
|
||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
#nullable disable
|
||||
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using NUnit.Framework;
|
||||
@ -161,6 +160,21 @@ namespace osu.Game.Tests.Beatmaps.Formats
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestDecodeImageSpecifiedAsVideo()
|
||||
{
|
||||
var decoder = new LegacyBeatmapDecoder { ApplyOffsets = false };
|
||||
|
||||
using (var resStream = TestResources.OpenResource("image-specified-as-video.osb"))
|
||||
using (var stream = new LineBufferedReader(resStream))
|
||||
{
|
||||
var beatmap = decoder.Decode(stream);
|
||||
var metadata = beatmap.Metadata;
|
||||
|
||||
Assert.AreEqual("BG.jpg", metadata.BackgroundFile);
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestDecodeBeatmapTimingPoints()
|
||||
{
|
||||
@ -320,6 +334,8 @@ namespace osu.Game.Tests.Beatmaps.Formats
|
||||
{
|
||||
var comboColors = decoder.Decode(stream).ComboColours;
|
||||
|
||||
Debug.Assert(comboColors != null);
|
||||
|
||||
Color4[] expectedColors =
|
||||
{
|
||||
new Color4(142, 199, 255, 255),
|
||||
@ -330,7 +346,7 @@ namespace osu.Game.Tests.Beatmaps.Formats
|
||||
new Color4(255, 177, 140, 255),
|
||||
new Color4(100, 100, 100, 255), // alpha is specified as 100, but should be ignored.
|
||||
};
|
||||
Assert.AreEqual(expectedColors.Length, comboColors?.Count);
|
||||
Assert.AreEqual(expectedColors.Length, comboColors.Count);
|
||||
for (int i = 0; i < expectedColors.Length; i++)
|
||||
Assert.AreEqual(expectedColors[i], comboColors[i]);
|
||||
}
|
||||
@ -415,14 +431,14 @@ namespace osu.Game.Tests.Beatmaps.Formats
|
||||
|
||||
Assert.IsNotNull(positionData);
|
||||
Assert.IsNotNull(curveData);
|
||||
Assert.AreEqual(new Vector2(192, 168), positionData.Position);
|
||||
Assert.AreEqual(new Vector2(192, 168), positionData!.Position);
|
||||
Assert.AreEqual(956, hitObjects[0].StartTime);
|
||||
Assert.IsTrue(hitObjects[0].Samples.Any(s => s.Name == HitSampleInfo.HIT_NORMAL));
|
||||
|
||||
positionData = hitObjects[1] as IHasPosition;
|
||||
|
||||
Assert.IsNotNull(positionData);
|
||||
Assert.AreEqual(new Vector2(304, 56), positionData.Position);
|
||||
Assert.AreEqual(new Vector2(304, 56), positionData!.Position);
|
||||
Assert.AreEqual(1285, hitObjects[1].StartTime);
|
||||
Assert.IsTrue(hitObjects[1].Samples.Any(s => s.Name == HitSampleInfo.HIT_CLAP));
|
||||
}
|
||||
@ -578,8 +594,8 @@ namespace osu.Game.Tests.Beatmaps.Formats
|
||||
[Test]
|
||||
public void TestFallbackDecoderForCorruptedHeader()
|
||||
{
|
||||
Decoder<Beatmap> decoder = null;
|
||||
Beatmap beatmap = null;
|
||||
Decoder<Beatmap> decoder = null!;
|
||||
Beatmap beatmap = null!;
|
||||
|
||||
using (var resStream = TestResources.OpenResource("corrupted-header.osu"))
|
||||
using (var stream = new LineBufferedReader(resStream))
|
||||
@ -596,8 +612,8 @@ namespace osu.Game.Tests.Beatmaps.Formats
|
||||
[Test]
|
||||
public void TestFallbackDecoderForMissingHeader()
|
||||
{
|
||||
Decoder<Beatmap> decoder = null;
|
||||
Beatmap beatmap = null;
|
||||
Decoder<Beatmap> decoder = null!;
|
||||
Beatmap beatmap = null!;
|
||||
|
||||
using (var resStream = TestResources.OpenResource("missing-header.osu"))
|
||||
using (var stream = new LineBufferedReader(resStream))
|
||||
@ -614,8 +630,8 @@ namespace osu.Game.Tests.Beatmaps.Formats
|
||||
[Test]
|
||||
public void TestDecodeFileWithEmptyLinesAtStart()
|
||||
{
|
||||
Decoder<Beatmap> decoder = null;
|
||||
Beatmap beatmap = null;
|
||||
Decoder<Beatmap> decoder = null!;
|
||||
Beatmap beatmap = null!;
|
||||
|
||||
using (var resStream = TestResources.OpenResource("empty-lines-at-start.osu"))
|
||||
using (var stream = new LineBufferedReader(resStream))
|
||||
@ -632,8 +648,8 @@ namespace osu.Game.Tests.Beatmaps.Formats
|
||||
[Test]
|
||||
public void TestDecodeFileWithEmptyLinesAndNoHeader()
|
||||
{
|
||||
Decoder<Beatmap> decoder = null;
|
||||
Beatmap beatmap = null;
|
||||
Decoder<Beatmap> decoder = null!;
|
||||
Beatmap beatmap = null!;
|
||||
|
||||
using (var resStream = TestResources.OpenResource("empty-line-instead-of-header.osu"))
|
||||
using (var stream = new LineBufferedReader(resStream))
|
||||
@ -650,8 +666,8 @@ namespace osu.Game.Tests.Beatmaps.Formats
|
||||
[Test]
|
||||
public void TestDecodeFileWithContentImmediatelyAfterHeader()
|
||||
{
|
||||
Decoder<Beatmap> decoder = null;
|
||||
Beatmap beatmap = null;
|
||||
Decoder<Beatmap> decoder = null!;
|
||||
Beatmap beatmap = null!;
|
||||
|
||||
using (var resStream = TestResources.OpenResource("no-empty-line-after-header.osu"))
|
||||
using (var stream = new LineBufferedReader(resStream))
|
||||
@ -678,7 +694,7 @@ namespace osu.Game.Tests.Beatmaps.Formats
|
||||
[Test]
|
||||
public void TestAllowFallbackDecoderOverwrite()
|
||||
{
|
||||
Decoder<Beatmap> decoder = null;
|
||||
Decoder<Beatmap> decoder = null!;
|
||||
|
||||
using (var resStream = TestResources.OpenResource("corrupted-header.osu"))
|
||||
using (var stream = new LineBufferedReader(resStream))
|
||||
|
@ -1,8 +1,6 @@
|
||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
#nullable disable
|
||||
|
||||
using System.Linq;
|
||||
using NUnit.Framework;
|
||||
using osuTK;
|
||||
@ -30,35 +28,35 @@ namespace osu.Game.Tests.Beatmaps.Formats
|
||||
Assert.IsTrue(storyboard.HasDrawable);
|
||||
Assert.AreEqual(6, storyboard.Layers.Count());
|
||||
|
||||
StoryboardLayer background = storyboard.Layers.FirstOrDefault(l => l.Depth == 3);
|
||||
StoryboardLayer background = storyboard.Layers.Single(l => l.Depth == 3);
|
||||
Assert.IsNotNull(background);
|
||||
Assert.AreEqual(16, background.Elements.Count);
|
||||
Assert.IsTrue(background.VisibleWhenFailing);
|
||||
Assert.IsTrue(background.VisibleWhenPassing);
|
||||
Assert.AreEqual("Background", background.Name);
|
||||
|
||||
StoryboardLayer fail = storyboard.Layers.FirstOrDefault(l => l.Depth == 2);
|
||||
StoryboardLayer fail = storyboard.Layers.Single(l => l.Depth == 2);
|
||||
Assert.IsNotNull(fail);
|
||||
Assert.AreEqual(0, fail.Elements.Count);
|
||||
Assert.IsTrue(fail.VisibleWhenFailing);
|
||||
Assert.IsFalse(fail.VisibleWhenPassing);
|
||||
Assert.AreEqual("Fail", fail.Name);
|
||||
|
||||
StoryboardLayer pass = storyboard.Layers.FirstOrDefault(l => l.Depth == 1);
|
||||
StoryboardLayer pass = storyboard.Layers.Single(l => l.Depth == 1);
|
||||
Assert.IsNotNull(pass);
|
||||
Assert.AreEqual(0, pass.Elements.Count);
|
||||
Assert.IsFalse(pass.VisibleWhenFailing);
|
||||
Assert.IsTrue(pass.VisibleWhenPassing);
|
||||
Assert.AreEqual("Pass", pass.Name);
|
||||
|
||||
StoryboardLayer foreground = storyboard.Layers.FirstOrDefault(l => l.Depth == 0);
|
||||
StoryboardLayer foreground = storyboard.Layers.Single(l => l.Depth == 0);
|
||||
Assert.IsNotNull(foreground);
|
||||
Assert.AreEqual(151, foreground.Elements.Count);
|
||||
Assert.IsTrue(foreground.VisibleWhenFailing);
|
||||
Assert.IsTrue(foreground.VisibleWhenPassing);
|
||||
Assert.AreEqual("Foreground", foreground.Name);
|
||||
|
||||
StoryboardLayer overlay = storyboard.Layers.FirstOrDefault(l => l.Depth == int.MinValue);
|
||||
StoryboardLayer overlay = storyboard.Layers.Single(l => l.Depth == int.MinValue);
|
||||
Assert.IsNotNull(overlay);
|
||||
Assert.IsEmpty(overlay.Elements);
|
||||
Assert.IsTrue(overlay.VisibleWhenFailing);
|
||||
@ -76,7 +74,7 @@ namespace osu.Game.Tests.Beatmaps.Formats
|
||||
|
||||
var sprite = background.Elements.ElementAt(0) as StoryboardSprite;
|
||||
Assert.NotNull(sprite);
|
||||
Assert.IsTrue(sprite.HasCommands);
|
||||
Assert.IsTrue(sprite!.HasCommands);
|
||||
Assert.AreEqual(new Vector2(320, 240), sprite.InitialPosition);
|
||||
Assert.IsTrue(sprite.IsDrawable);
|
||||
Assert.AreEqual(Anchor.Centre, sprite.Origin);
|
||||
@ -171,6 +169,21 @@ namespace osu.Game.Tests.Beatmaps.Formats
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestDecodeImageSpecifiedAsVideo()
|
||||
{
|
||||
var decoder = new LegacyStoryboardDecoder();
|
||||
|
||||
using (var resStream = TestResources.OpenResource("image-specified-as-video.osb"))
|
||||
using (var stream = new LineBufferedReader(resStream))
|
||||
{
|
||||
var storyboard = decoder.Decode(stream);
|
||||
|
||||
StoryboardLayer foreground = storyboard.Layers.Single(l => l.Name == "Video");
|
||||
Assert.That(foreground.Elements.Count, Is.Zero);
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestDecodeOutOfRangeLoopAnimationType()
|
||||
{
|
||||
|
4
osu.Game.Tests/Resources/image-specified-as-video.osb
Normal file
4
osu.Game.Tests/Resources/image-specified-as-video.osb
Normal file
@ -0,0 +1,4 @@
|
||||
osu file format v14
|
||||
|
||||
[Events]
|
||||
Video,0,"BG.jpg",0,0
|
@ -45,6 +45,9 @@ namespace osu.Game.Tests.Visual.Gameplay
|
||||
[Resolved]
|
||||
private SessionStatics sessionStatics { get; set; }
|
||||
|
||||
[Resolved]
|
||||
private OsuConfigManager config { get; set; }
|
||||
|
||||
[Cached(typeof(INotificationOverlay))]
|
||||
private readonly NotificationOverlay notificationOverlay;
|
||||
|
||||
@ -317,6 +320,7 @@ namespace osu.Game.Tests.Visual.Gameplay
|
||||
saveVolumes();
|
||||
setFullVolume();
|
||||
|
||||
AddStep("enable storyboards", () => config.SetValue(OsuSetting.ShowStoryboard, true));
|
||||
AddStep("change epilepsy warning", () => epilepsyWarning = warning);
|
||||
AddStep("load dummy beatmap", () => resetPlayer(false));
|
||||
|
||||
@ -333,12 +337,30 @@ namespace osu.Game.Tests.Visual.Gameplay
|
||||
restoreVolumes();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestEpilepsyWarningWithDisabledStoryboard()
|
||||
{
|
||||
saveVolumes();
|
||||
setFullVolume();
|
||||
|
||||
AddStep("disable storyboards", () => config.SetValue(OsuSetting.ShowStoryboard, false));
|
||||
AddStep("change epilepsy warning", () => epilepsyWarning = true);
|
||||
AddStep("load dummy beatmap", () => resetPlayer(false));
|
||||
|
||||
AddUntilStep("wait for current", () => loader.IsCurrentScreen());
|
||||
|
||||
AddUntilStep("epilepsy warning absent", () => getWarning() == null);
|
||||
|
||||
restoreVolumes();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestEpilepsyWarningEarlyExit()
|
||||
{
|
||||
saveVolumes();
|
||||
setFullVolume();
|
||||
|
||||
AddStep("enable storyboards", () => config.SetValue(OsuSetting.ShowStoryboard, true));
|
||||
AddStep("set epilepsy warning", () => epilepsyWarning = true);
|
||||
AddStep("load dummy beatmap", () => resetPlayer(false));
|
||||
|
||||
@ -449,7 +471,7 @@ namespace osu.Game.Tests.Visual.Gameplay
|
||||
AddStep("click notification", () => notification.TriggerClick());
|
||||
}
|
||||
|
||||
private EpilepsyWarning getWarning() => loader.ChildrenOfType<EpilepsyWarning>().SingleOrDefault();
|
||||
private EpilepsyWarning getWarning() => loader.ChildrenOfType<EpilepsyWarning>().SingleOrDefault(w => w.IsAlive);
|
||||
|
||||
private partial class TestPlayerLoader : PlayerLoader
|
||||
{
|
||||
|
@ -1,8 +1,6 @@
|
||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
#nullable disable
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
@ -22,8 +20,10 @@ namespace osu.Game.Tests.Visual.Gameplay
|
||||
{
|
||||
public partial class TestSceneSkinnableSound : OsuTestScene
|
||||
{
|
||||
private TestSkinSourceContainer skinSource;
|
||||
private PausableSkinnableSound skinnableSound;
|
||||
private TestSkinSourceContainer skinSource = null!;
|
||||
private PausableSkinnableSound skinnableSound = null!;
|
||||
|
||||
private const string sample_lookup = "Gameplay/normal-sliderslide";
|
||||
|
||||
[SetUpSteps]
|
||||
public void SetUpSteps()
|
||||
@ -36,7 +36,7 @@ namespace osu.Game.Tests.Visual.Gameplay
|
||||
};
|
||||
|
||||
// has to be added after the hierarchy above else the `ISkinSource` dependency won't be cached.
|
||||
skinSource.Add(skinnableSound = new PausableSkinnableSound(new SampleInfo("Gameplay/normal-sliderslide")));
|
||||
skinSource.Add(skinnableSound = new PausableSkinnableSound(new SampleInfo(sample_lookup)));
|
||||
});
|
||||
}
|
||||
|
||||
@ -99,10 +99,28 @@ namespace osu.Game.Tests.Visual.Gameplay
|
||||
AddAssert("sample not playing", () => !skinnableSound.IsPlaying);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestSampleUpdatedBeforePlaybackWhenNotPresent()
|
||||
{
|
||||
AddStep("make sample non-present", () => skinnableSound.Hide());
|
||||
AddUntilStep("ensure not present", () => skinnableSound.IsPresent, () => Is.False);
|
||||
|
||||
AddUntilStep("ensure sample loaded", () => skinnableSound.ChildrenOfType<DrawableSample>().Single().Name, () => Is.EqualTo(sample_lookup));
|
||||
|
||||
AddStep("change source", () =>
|
||||
{
|
||||
skinSource.OverridingSample = new SampleVirtual("new skin");
|
||||
skinSource.TriggerSourceChanged();
|
||||
});
|
||||
|
||||
AddStep("start sample", () => skinnableSound.Play());
|
||||
AddUntilStep("sample updated", () => skinnableSound.ChildrenOfType<DrawableSample>().Single().Name, () => Is.EqualTo("new skin"));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestSkinChangeDoesntPlayOnPause()
|
||||
{
|
||||
DrawableSample sample = null;
|
||||
DrawableSample? sample = null;
|
||||
AddStep("start sample", () =>
|
||||
{
|
||||
skinnableSound.Play();
|
||||
@ -118,7 +136,7 @@ namespace osu.Game.Tests.Visual.Gameplay
|
||||
|
||||
AddAssert("retrieve and ensure current sample is different", () =>
|
||||
{
|
||||
DrawableSample oldSample = sample;
|
||||
DrawableSample? oldSample = sample;
|
||||
sample = skinnableSound.ChildrenOfType<DrawableSample>().Single();
|
||||
return sample != oldSample;
|
||||
});
|
||||
@ -134,20 +152,29 @@ namespace osu.Game.Tests.Visual.Gameplay
|
||||
private partial class TestSkinSourceContainer : Container, ISkinSource, ISamplePlaybackDisabler
|
||||
{
|
||||
[Resolved]
|
||||
private ISkinSource source { get; set; }
|
||||
private ISkinSource source { get; set; } = null!;
|
||||
|
||||
public event Action SourceChanged;
|
||||
public event Action? SourceChanged;
|
||||
|
||||
public Bindable<bool> SamplePlaybackDisabled { get; } = new Bindable<bool>();
|
||||
|
||||
public ISample? OverridingSample;
|
||||
|
||||
IBindable<bool> ISamplePlaybackDisabler.SamplePlaybackDisabled => SamplePlaybackDisabled;
|
||||
|
||||
public Drawable GetDrawableComponent(ISkinComponentLookup lookup) => source?.GetDrawableComponent(lookup);
|
||||
public Texture GetTexture(string componentName, WrapMode wrapModeS, WrapMode wrapModeT) => source?.GetTexture(componentName, wrapModeS, wrapModeT);
|
||||
public ISample GetSample(ISampleInfo sampleInfo) => source?.GetSample(sampleInfo);
|
||||
public IBindable<TValue> GetConfig<TLookup, TValue>(TLookup lookup) => source?.GetConfig<TLookup, TValue>(lookup);
|
||||
public ISkin FindProvider(Func<ISkin, bool> lookupFunction) => lookupFunction(this) ? this : source?.FindProvider(lookupFunction);
|
||||
public IEnumerable<ISkin> AllSources => new[] { this }.Concat(source?.AllSources ?? Enumerable.Empty<ISkin>());
|
||||
public Drawable? GetDrawableComponent(ISkinComponentLookup lookup) => source.GetDrawableComponent(lookup);
|
||||
public Texture? GetTexture(string componentName, WrapMode wrapModeS, WrapMode wrapModeT) => source.GetTexture(componentName, wrapModeS, wrapModeT);
|
||||
public ISample? GetSample(ISampleInfo sampleInfo) => OverridingSample ?? source.GetSample(sampleInfo);
|
||||
|
||||
public IBindable<TValue>? GetConfig<TLookup, TValue>(TLookup lookup)
|
||||
where TLookup : notnull
|
||||
where TValue : notnull
|
||||
{
|
||||
return source.GetConfig<TLookup, TValue>(lookup);
|
||||
}
|
||||
|
||||
public ISkin? FindProvider(Func<ISkin, bool> lookupFunction) => lookupFunction(this) ? this : source.FindProvider(lookupFunction);
|
||||
public IEnumerable<ISkin> AllSources => new[] { this }.Concat(source.AllSources);
|
||||
|
||||
public void TriggerSourceChanged()
|
||||
{
|
||||
|
@ -363,6 +363,19 @@ namespace osu.Game.Beatmaps.Formats
|
||||
beatmap.BeatmapInfo.Metadata.BackgroundFile = CleanFilename(split[3]);
|
||||
break;
|
||||
|
||||
case LegacyEventType.Video:
|
||||
string filename = CleanFilename(split[2]);
|
||||
|
||||
// Some very old beatmaps had incorrect type specifications for their backgrounds (ie. using 1 for VIDEO
|
||||
// instead of 0 for BACKGROUND). To handle this gracefully, check the file extension against known supported
|
||||
// video extensions and handle similar to a background if it doesn't match.
|
||||
if (!OsuGameBase.VIDEO_EXTENSIONS.Contains(Path.GetExtension(filename)))
|
||||
{
|
||||
beatmap.BeatmapInfo.Metadata.BackgroundFile = filename;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case LegacyEventType.Background:
|
||||
beatmap.BeatmapInfo.Metadata.BackgroundFile = CleanFilename(split[2]);
|
||||
break;
|
||||
|
@ -109,6 +109,14 @@ namespace osu.Game.Beatmaps.Formats
|
||||
int offset = Parsing.ParseInt(split[1]);
|
||||
string path = CleanFilename(split[2]);
|
||||
|
||||
// See handling in LegacyBeatmapDecoder for the special case where a video type is used but
|
||||
// the file extension is not a valid video.
|
||||
//
|
||||
// This avoids potential weird crashes when ffmpeg attempts to parse an image file as a video
|
||||
// (see https://github.com/ppy/osu/issues/22829#issuecomment-1465552451).
|
||||
if (!OsuGameBase.VIDEO_EXTENSIONS.Contains(Path.GetExtension(path)))
|
||||
break;
|
||||
|
||||
storyboard.GetLayer("Video").Add(new StoryboardVideo(path, offset));
|
||||
break;
|
||||
}
|
||||
@ -276,7 +284,8 @@ namespace osu.Game.Beatmaps.Formats
|
||||
switch (type)
|
||||
{
|
||||
case "A":
|
||||
timelineGroup?.BlendingParameters.Add(easing, startTime, endTime, BlendingParameters.Additive, startTime == endTime ? BlendingParameters.Additive : BlendingParameters.Inherit);
|
||||
timelineGroup?.BlendingParameters.Add(easing, startTime, endTime, BlendingParameters.Additive,
|
||||
startTime == endTime ? BlendingParameters.Additive : BlendingParameters.Inherit);
|
||||
break;
|
||||
|
||||
case "H":
|
||||
|
@ -71,7 +71,7 @@ namespace osu.Game
|
||||
[Cached(typeof(OsuGameBase))]
|
||||
public partial class OsuGameBase : Framework.Game, ICanAcceptFiles, IBeatSyncProvider
|
||||
{
|
||||
public static readonly string[] VIDEO_EXTENSIONS = { ".mp4", ".mov", ".avi", ".flv" };
|
||||
public static readonly string[] VIDEO_EXTENSIONS = { ".mp4", ".mov", ".avi", ".flv", ".mpg", ".wmv", ".m4v" };
|
||||
|
||||
public const string OSU_PROTOCOL = "osu://";
|
||||
|
||||
|
@ -98,7 +98,7 @@ namespace osu.Game.Rulesets.UI
|
||||
|
||||
isDisposed = true;
|
||||
|
||||
if (ShaderManager.IsNotNull()) SampleStore.Dispose();
|
||||
if (SampleStore.IsNotNull()) SampleStore.Dispose();
|
||||
if (TextureStore.IsNotNull()) TextureStore.Dispose();
|
||||
if (ShaderManager.IsNotNull()) ShaderManager.Dispose();
|
||||
}
|
||||
|
@ -1,15 +1,13 @@
|
||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using osu.Framework.Audio;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Game.Rulesets.UI;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using ManagedBass.Fx;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Audio.Sample;
|
||||
using osu.Framework.Audio;
|
||||
using osu.Framework.Audio.Track;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Framework.Extensions.Color4Extensions;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
@ -21,6 +19,7 @@ using osu.Game.Beatmaps;
|
||||
using osu.Game.Configuration;
|
||||
using osu.Game.Graphics;
|
||||
using osu.Game.Rulesets.Objects.Drawables;
|
||||
using osu.Game.Rulesets.UI;
|
||||
using osu.Game.Skinning;
|
||||
using osuTK;
|
||||
using osuTK.Graphics;
|
||||
@ -50,8 +49,7 @@ namespace osu.Game.Screens.Play
|
||||
|
||||
private const float duration = 2500;
|
||||
|
||||
private ISample? failSample;
|
||||
private SampleChannel? failSampleChannel;
|
||||
private SkinnableSound failSample = null!;
|
||||
|
||||
[Resolved]
|
||||
private OsuConfigManager config { get; set; } = null!;
|
||||
@ -76,10 +74,10 @@ namespace osu.Game.Screens.Play
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(AudioManager audio, ISkinSource skin, IBindable<WorkingBeatmap> beatmap)
|
||||
private void load(AudioManager audio, IBindable<WorkingBeatmap> beatmap)
|
||||
{
|
||||
track = beatmap.Value.Track;
|
||||
failSample = skin.GetSample(new SampleInfo(@"Gameplay/failsound"));
|
||||
AddInternal(failSample = new SkinnableSound(new SampleInfo("Gameplay/failsound")));
|
||||
|
||||
AddRangeInternal(new Drawable[]
|
||||
{
|
||||
@ -126,7 +124,7 @@ namespace osu.Game.Screens.Play
|
||||
|
||||
failHighPassFilter.CutoffTo(300);
|
||||
failLowPassFilter.CutoffTo(300, duration, Easing.OutCubic);
|
||||
failSampleChannel = failSample?.Play();
|
||||
failSample.Play();
|
||||
|
||||
track.AddAdjustment(AdjustableProperty.Frequency, trackFreq);
|
||||
track.AddAdjustment(AdjustableProperty.Volume, volumeAdjustment);
|
||||
@ -159,7 +157,7 @@ namespace osu.Game.Screens.Play
|
||||
/// </summary>
|
||||
public void Stop()
|
||||
{
|
||||
failSampleChannel?.Stop();
|
||||
failSample.Stop();
|
||||
removeFilters();
|
||||
}
|
||||
|
||||
|
@ -67,6 +67,8 @@ namespace osu.Game.Screens.Play
|
||||
|
||||
private OsuScrollContainer settingsScroll = null!;
|
||||
|
||||
private Bindable<bool> showStoryboards = null!;
|
||||
|
||||
private bool backgroundBrightnessReduction;
|
||||
|
||||
private readonly BindableDouble volumeAdjustment = new BindableDouble(1);
|
||||
@ -149,10 +151,11 @@ namespace osu.Game.Screens.Play
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(SessionStatics sessionStatics, AudioManager audio)
|
||||
private void load(SessionStatics sessionStatics, AudioManager audio, OsuConfigManager config)
|
||||
{
|
||||
muteWarningShownOnce = sessionStatics.GetBindable<bool>(Static.MutedAudioNotificationShownOnce);
|
||||
batteryWarningShownOnce = sessionStatics.GetBindable<bool>(Static.LowBatteryNotificationShownOnce);
|
||||
showStoryboards = config.GetBindable<bool>(OsuSetting.ShowStoryboard);
|
||||
|
||||
const float padding = 25;
|
||||
|
||||
@ -463,7 +466,10 @@ namespace osu.Game.Screens.Play
|
||||
|
||||
// only show if the warning was created (i.e. the beatmap needs it)
|
||||
// and this is not a restart of the map (the warning expires after first load).
|
||||
if (epilepsyWarning?.IsAlive == true)
|
||||
//
|
||||
// note the late check of storyboard enable as the user may have just changed it
|
||||
// from the settings on the loader screen.
|
||||
if (epilepsyWarning?.IsAlive == true && showStoryboards.Value)
|
||||
{
|
||||
const double epilepsy_display_length = 3000;
|
||||
|
||||
@ -483,6 +489,7 @@ namespace osu.Game.Screens.Play
|
||||
{
|
||||
// This goes hand-in-hand with the restoration of low pass filter in contentOut().
|
||||
this.TransformBindableTo(volumeAdjustment, 0, CONTENT_OUT_DURATION, Easing.OutCubic);
|
||||
epilepsyWarning?.Expire();
|
||||
}
|
||||
|
||||
pushSequence.Schedule(() =>
|
||||
|
@ -9,7 +9,6 @@ using JetBrains.Annotations;
|
||||
using osu.Framework.Audio;
|
||||
using osu.Framework.Audio.Sample;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Framework.Extensions.ObjectExtensions;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Audio;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
@ -70,20 +69,6 @@ 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)
|
||||
{
|
||||
base.SkinChanged(skin);
|
||||
@ -109,6 +94,8 @@ namespace osu.Game.Skinning
|
||||
|
||||
private void updateSample()
|
||||
{
|
||||
clearPreviousSamples();
|
||||
|
||||
if (sampleInfo == null)
|
||||
return;
|
||||
|
||||
@ -129,6 +116,8 @@ namespace osu.Game.Skinning
|
||||
/// </summary>
|
||||
public void Play()
|
||||
{
|
||||
FlushPendingSkinChanges();
|
||||
|
||||
if (Sample == null)
|
||||
return;
|
||||
|
||||
@ -172,14 +161,6 @@ namespace osu.Game.Skinning
|
||||
}
|
||||
}
|
||||
|
||||
protected override void Dispose(bool isDisposing)
|
||||
{
|
||||
base.Dispose(isDisposing);
|
||||
|
||||
if (CurrentSkin.IsNotNull())
|
||||
CurrentSkin.SourceChanged -= skinChangedImmediate;
|
||||
}
|
||||
|
||||
#region Re-expose AudioContainer
|
||||
|
||||
public BindableNumber<double> Volume => sampleContainer.Volume;
|
||||
|
@ -5,6 +5,7 @@ using System;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Extensions.ObjectExtensions;
|
||||
using osu.Framework.Graphics.Pooling;
|
||||
using osu.Framework.Threading;
|
||||
|
||||
namespace osu.Game.Skinning
|
||||
{
|
||||
@ -14,6 +15,8 @@ namespace osu.Game.Skinning
|
||||
/// </summary>
|
||||
public abstract partial class SkinReloadableDrawable : PoolableDrawable
|
||||
{
|
||||
private ScheduledDelegate? pendingSkinChange;
|
||||
|
||||
/// <summary>
|
||||
/// Invoked when <see cref="CurrentSkin"/> has changed.
|
||||
/// </summary>
|
||||
@ -31,21 +34,30 @@ namespace osu.Game.Skinning
|
||||
CurrentSkin.SourceChanged += onChange;
|
||||
}
|
||||
|
||||
private void onChange() =>
|
||||
// schedule required to avoid calls after disposed.
|
||||
// note that this has the side-effect of components only performing a skin change when they are alive.
|
||||
Scheduler.AddOnce(skinChanged);
|
||||
|
||||
protected override void LoadAsyncComplete()
|
||||
{
|
||||
base.LoadAsyncComplete();
|
||||
skinChanged();
|
||||
}
|
||||
|
||||
private void skinChanged()
|
||||
/// <summary>
|
||||
/// Force any pending <see cref="SkinChanged"/> calls to be performed immediately.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// When a skin change occurs, the handling provided by this class is scheduled.
|
||||
/// In some cases, such a sample playback, this can result in the sample being played
|
||||
/// just before it is updated to a potentially different sample.
|
||||
///
|
||||
/// Calling this method will ensure any pending update operations are run immediately.
|
||||
/// It is recommended to call this before consuming the result of skin changes for anything non-drawable.
|
||||
/// </remarks>
|
||||
protected void FlushPendingSkinChanges()
|
||||
{
|
||||
SkinChanged(CurrentSkin);
|
||||
OnSkinChanged?.Invoke();
|
||||
if (pendingSkinChange == null)
|
||||
return;
|
||||
|
||||
pendingSkinChange.RunTask();
|
||||
pendingSkinChange = null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -56,6 +68,22 @@ namespace osu.Game.Skinning
|
||||
{
|
||||
}
|
||||
|
||||
private void onChange()
|
||||
{
|
||||
// schedule required to avoid calls after disposed.
|
||||
// note that this has the side-effect of components only performing a skin change when they are alive.
|
||||
pendingSkinChange?.Cancel();
|
||||
pendingSkinChange = Scheduler.Add(skinChanged);
|
||||
}
|
||||
|
||||
private void skinChanged()
|
||||
{
|
||||
SkinChanged(CurrentSkin);
|
||||
OnSkinChanged?.Invoke();
|
||||
|
||||
pendingSkinChange = null;
|
||||
}
|
||||
|
||||
protected override void Dispose(bool isDisposing)
|
||||
{
|
||||
base.Dispose(isDisposing);
|
||||
|
@ -115,6 +115,8 @@ namespace osu.Game.Skinning
|
||||
/// </summary>
|
||||
public virtual void Play()
|
||||
{
|
||||
FlushPendingSkinChanges();
|
||||
|
||||
samplesContainer.ForEach(c =>
|
||||
{
|
||||
if (PlayWhenZeroVolume || c.AggregateVolume.Value > 0)
|
||||
|
Loading…
Reference in New Issue
Block a user