From 1a7e3fa09e4d51db6a30dfe7975538fc1277b824 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 3 Oct 2017 19:21:08 +0900 Subject: [PATCH 01/15] Initial implementation of a test case which showcases waveforms --- osu.Game/Tests/Visual/TestCaseWaveform.cs | 149 ++++++++++++++++++++++ osu.Game/osu.Game.csproj | 1 + 2 files changed, 150 insertions(+) create mode 100644 osu.Game/Tests/Visual/TestCaseWaveform.cs diff --git a/osu.Game/Tests/Visual/TestCaseWaveform.cs b/osu.Game/Tests/Visual/TestCaseWaveform.cs new file mode 100644 index 0000000000..5f4e86fb92 --- /dev/null +++ b/osu.Game/Tests/Visual/TestCaseWaveform.cs @@ -0,0 +1,149 @@ +// Copyright (c) 2007-2017 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System; +using System.Collections.Generic; +using OpenTK; +using OpenTK.Graphics; +using osu.Framework.Allocation; +using osu.Framework.Audio.Track; +using osu.Framework.Configuration; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Shapes; +using osu.Game.Beatmaps; +using osu.Game.Graphics.Sprites; +using osu.Game.Overlays; + +namespace osu.Game.Tests.Visual +{ + internal class TestCaseWaveform : OsuTestCase + { + private readonly Bindable beatmapBacking = new Bindable(); + + private readonly List displays = new List(); + + public TestCaseWaveform() + { + MusicController mc; + FillFlowContainer flow; + Child = flow = new FillFlowContainer + { + RelativeSizeAxes = Axes.Both, + Direction = FillDirection.Vertical, + Spacing = new Vector2(0, 10), + Children = new Drawable[] + { + mc = new MusicController + { + Anchor = Anchor.TopCentre, + Origin = Anchor.TopCentre, + Y = 100, + State = Visibility.Visible + }, + } + }; + + for (int i = 1; i <= 16; i *= 2) + { + var newDisplay = new WaveformDisplay(i) { RelativeSizeAxes = Axes.Both }; + + displays.Add(newDisplay); + + flow.Add(new Container + { + RelativeSizeAxes = Axes.X, + Height = 100, + Children = new Drawable[] + { + newDisplay, + new Container + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + AutoSizeAxes = Axes.Both, + Children = new Drawable[] + { + new Box + { + RelativeSizeAxes = Axes.Both, + Colour = Color4.Black, + Alpha = 0.75f + }, + new OsuSpriteText + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Text = $"Resolution: {(1f / i).ToString("0.00")}" + } + } + } + } + }); + } + } + + [BackgroundDependencyLoader] + private void load(OsuGameBase osuGame) + { + beatmapBacking.BindTo(osuGame.Beatmap); + beatmapBacking.ValueChanged += b => b.Track.QueryWaveform(processWaveform); + } + + private void processWaveform(Waveform waveform) => Schedule(() => displays.ForEach(d => d.Display(waveform))); + + private class WaveformDisplay : CompositeDrawable + { + private readonly int resolution; + + public WaveformDisplay(int resolution) + { + this.resolution = resolution; + } + + public void Display(Waveform waveform) + { + ClearInternal(); + + var generated = waveform.Generate((int)MathHelper.Clamp(Math.Ceiling(DrawWidth), 0, waveform.TotalPoints) / resolution); + + for (int i = 0; i < generated.Count; i++) + { + var point = generated[i]; + + // Left channel + AddInternal(new NonInputBox + { + Anchor = Anchor.CentreLeft, + Origin = Anchor.BottomLeft, + RelativePositionAxes = Axes.X, + RelativeSizeAxes = Axes.Both, + X = 1f / generated.Count * i, + Size = new Vector2(1f / generated.Count, point.Amplitude[0] / 2), + Colour = Color4.Red + }); + + if (waveform.Channels >= 2) + { + // Right channel + AddInternal(new NonInputBox + { + Anchor = Anchor.CentreLeft, + Origin = Anchor.TopLeft, + RelativePositionAxes = Axes.X, + RelativeSizeAxes = Axes.Both, + X = 1f / generated.Count * i, + Size = new Vector2(1f / generated.Count, point.Amplitude[1] / 2), + Colour = Color4.Green + }); + } + } + } + + private class NonInputBox : Box + { + public override bool HandleInput => false; + } + } + } +} diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index 9a8536bbc2..56490b6119 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -777,6 +777,7 @@ + From 319649f4465a45bc93c615d57ceb11ac36cd8460 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 3 Oct 2017 21:19:38 +0900 Subject: [PATCH 02/15] Make TestCaseWaveform use a custom drawnode instead of boxes --- osu.Game/Tests/Visual/TestCaseWaveform.cs | 128 +++++++++++++++++----- 1 file changed, 101 insertions(+), 27 deletions(-) diff --git a/osu.Game/Tests/Visual/TestCaseWaveform.cs b/osu.Game/Tests/Visual/TestCaseWaveform.cs index 5f4e86fb92..3f71e74a8d 100644 --- a/osu.Game/Tests/Visual/TestCaseWaveform.cs +++ b/osu.Game/Tests/Visual/TestCaseWaveform.cs @@ -5,12 +5,19 @@ using System; using System.Collections.Generic; using OpenTK; using OpenTK.Graphics; +using OpenTK.Graphics.ES30; using osu.Framework.Allocation; using osu.Framework.Audio.Track; using osu.Framework.Configuration; using osu.Framework.Graphics; +using osu.Framework.Graphics.Batches; +using osu.Framework.Graphics.Colour; using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.OpenGL.Vertices; +using osu.Framework.Graphics.Primitives; +using osu.Framework.Graphics.Shaders; using osu.Framework.Graphics.Shapes; +using osu.Framework.Graphics.Textures; using osu.Game.Beatmaps; using osu.Game.Graphics.Sprites; using osu.Game.Overlays; @@ -92,51 +99,118 @@ namespace osu.Game.Tests.Visual private void processWaveform(Waveform waveform) => Schedule(() => displays.ForEach(d => d.Display(waveform))); - private class WaveformDisplay : CompositeDrawable + private class WaveformDisplay : Drawable { + private List points; + private int channels; + + private Shader shader; + private readonly Texture texture; + private readonly int resolution; public WaveformDisplay(int resolution) { this.resolution = resolution; + + texture = Texture.WhitePixel; + } + + [BackgroundDependencyLoader] + private void load(ShaderManager shaders) + { + shader = shaders?.Load(VertexShaderDescriptor.TEXTURE_2, FragmentShaderDescriptor.TEXTURE_ROUNDED); } public void Display(Waveform waveform) { - ClearInternal(); + points = waveform.Generate((int)MathHelper.Clamp(Math.Ceiling(DrawWidth), 0, waveform.TotalPoints) / resolution); + channels = waveform.Channels; + Invalidate(Invalidation.DrawNode); + } - var generated = waveform.Generate((int)MathHelper.Clamp(Math.Ceiling(DrawWidth), 0, waveform.TotalPoints) / resolution); + protected override DrawNode CreateDrawNode() => new WaveformDrawNode(); - for (int i = 0; i < generated.Count; i++) + private readonly WaveformDrawNodeSharedData sharedData = new WaveformDrawNodeSharedData(); + protected override void ApplyDrawNode(DrawNode node) + { + var n = (WaveformDrawNode)node; + + n.Shader = shader; + n.Texture = texture; + n.Points = points; + n.Channels = channels; + n.Size = DrawSize; + n.Shared = sharedData; + + + base.ApplyDrawNode(node); + } + + private class WaveformDrawNodeSharedData + { + public readonly QuadBatch VertexBatch = new QuadBatch(1000, 10); + } + + private class WaveformDrawNode : DrawNode + { + public Shader Shader; + public Texture Texture; + + public WaveformDrawNodeSharedData Shared; + + public List Points; + public Vector2 Size; + public int Channels; + + public override void Draw(Action vertexAction) { - var point = generated[i]; + base.Draw(vertexAction); - // Left channel - AddInternal(new NonInputBox - { - Anchor = Anchor.CentreLeft, - Origin = Anchor.BottomLeft, - RelativePositionAxes = Axes.X, - RelativeSizeAxes = Axes.Both, - X = 1f / generated.Count * i, - Size = new Vector2(1f / generated.Count, point.Amplitude[0] / 2), - Colour = Color4.Red - }); + if (Points == null || Points.Count == 0) + return; - if (waveform.Channels >= 2) + Shader.Bind(); + Texture.TextureGL.Bind(); + + float separation = Size.X / (Points.Count - 1); + Vector2 localInflationAmount = new Vector2(0, 1) * DrawInfo.MatrixInverse.ExtractScale().Xy; + + for (int i = 0; i < Points.Count - 1; i++) { - // Right channel - AddInternal(new NonInputBox + ColourInfo colour = DrawInfo.Colour; + Quad quadToDraw; + + switch (Channels) { - Anchor = Anchor.CentreLeft, - Origin = Anchor.TopLeft, - RelativePositionAxes = Axes.X, - RelativeSizeAxes = Axes.Both, - X = 1f / generated.Count * i, - Size = new Vector2(1f / generated.Count, point.Amplitude[1] / 2), - Colour = Color4.Green - }); + default: + case 2: + { + float height = Size.Y / 2; + quadToDraw = new Quad( + new Vector2(i * separation, height - Points[i].Amplitude[0] * height), + new Vector2((i + 1) * separation, height - Points[i + 1].Amplitude[0] * height), + new Vector2(i * separation, height + Points[i].Amplitude[1] * height), + new Vector2((i + 1) * separation, height + Points[i + 1].Amplitude[1] * height) + ); + } + break; + case 1: + { + quadToDraw = new Quad( + new Vector2(i * separation, Size.Y - Points[i].Amplitude[0] * Size.Y), + new Vector2((i + 1) * separation, Size.Y - Points[i + 1].Amplitude[0] * Size.Y), + new Vector2(i * separation, Size.Y), + new Vector2((i + 1) * separation, Size.Y) + ); + break; + } + } + + Texture.DrawQuad(quadToDraw * DrawInfo.Matrix, colour, null, Shared.VertexBatch.Add, Vector2.Divide(localInflationAmount, quadToDraw.Size)); } + + Shader.Unbind(); } } From 3eeb36cbd42f3cfa4b7b448e9c4170af4a346601 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 3 Oct 2017 23:23:20 +0900 Subject: [PATCH 03/15] Remove now unused class --- osu.Game/Tests/Visual/TestCaseWaveform.cs | 6 ------ 1 file changed, 6 deletions(-) diff --git a/osu.Game/Tests/Visual/TestCaseWaveform.cs b/osu.Game/Tests/Visual/TestCaseWaveform.cs index 3f71e74a8d..2966728fb5 100644 --- a/osu.Game/Tests/Visual/TestCaseWaveform.cs +++ b/osu.Game/Tests/Visual/TestCaseWaveform.cs @@ -143,7 +143,6 @@ namespace osu.Game.Tests.Visual n.Size = DrawSize; n.Shared = sharedData; - base.ApplyDrawNode(node); } @@ -213,11 +212,6 @@ namespace osu.Game.Tests.Visual Shader.Unbind(); } } - - private class NonInputBox : Box - { - public override bool HandleInput => false; - } } } } From a37b10d51220b5f0df864cd8e503696eb669a0b6 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Wed, 4 Oct 2017 14:42:22 +0900 Subject: [PATCH 04/15] Make TestCaseWaveform use invalidations + remove some of the crud --- osu.Game/Tests/Visual/TestCaseWaveform.cs | 69 +++++++++++++++-------- 1 file changed, 44 insertions(+), 25 deletions(-) diff --git a/osu.Game/Tests/Visual/TestCaseWaveform.cs b/osu.Game/Tests/Visual/TestCaseWaveform.cs index 2966728fb5..4df7ffe367 100644 --- a/osu.Game/Tests/Visual/TestCaseWaveform.cs +++ b/osu.Game/Tests/Visual/TestCaseWaveform.cs @@ -28,8 +28,6 @@ namespace osu.Game.Tests.Visual { private readonly Bindable beatmapBacking = new Bindable(); - private readonly List displays = new List(); - public TestCaseWaveform() { MusicController mc; @@ -53,9 +51,13 @@ namespace osu.Game.Tests.Visual for (int i = 1; i <= 16; i *= 2) { - var newDisplay = new WaveformDisplay(i) { RelativeSizeAxes = Axes.Both }; + var newDisplay = new WaveformDisplay + { + RelativeSizeAxes = Axes.Both, + Resolution = 1f / i + }; - displays.Add(newDisplay); + newDisplay.Beatmap.BindTo(beatmapBacking); flow.Add(new Container { @@ -91,29 +93,21 @@ namespace osu.Game.Tests.Visual } [BackgroundDependencyLoader] - private void load(OsuGameBase osuGame) - { - beatmapBacking.BindTo(osuGame.Beatmap); - beatmapBacking.ValueChanged += b => b.Track.QueryWaveform(processWaveform); - } - - private void processWaveform(Waveform waveform) => Schedule(() => displays.ForEach(d => d.Display(waveform))); + private void load(OsuGameBase osuGame) => beatmapBacking.BindTo(osuGame.Beatmap); private class WaveformDisplay : Drawable { - private List points; - private int channels; + public readonly Bindable Beatmap = new Bindable(); + + private Waveform waveform; private Shader shader; private readonly Texture texture; - private readonly int resolution; - - public WaveformDisplay(int resolution) + public WaveformDisplay() { - this.resolution = resolution; - texture = Texture.WhitePixel; + Beatmap.ValueChanged += generateWaveform; } [BackgroundDependencyLoader] @@ -122,26 +116,51 @@ namespace osu.Game.Tests.Visual shader = shaders?.Load(VertexShaderDescriptor.TEXTURE_2, FragmentShaderDescriptor.TEXTURE_ROUNDED); } - public void Display(Waveform waveform) + private float resolution = 1; + public float Resolution { - points = waveform.Generate((int)MathHelper.Clamp(Math.Ceiling(DrawWidth), 0, waveform.TotalPoints) / resolution); - channels = waveform.Channels; - Invalidate(Invalidation.DrawNode); + get { return resolution; } + set + { + if (resolution == value) + return; + resolution = value; + + Invalidate(Invalidation.DrawNode); + } } - protected override DrawNode CreateDrawNode() => new WaveformDrawNode(); + private Track lastQueriedTrack; + + private void generateWaveform(WorkingBeatmap beatmap) + { + // Cancel the old query so we don't saturate the audio thread + lastQueriedTrack?.CancelWaveformQuery(); + + beatmap.Track.QueryWaveform(w => + { + if (Beatmap.Value == beatmap) + { + waveform = w; + Invalidate(Invalidation.DrawNode); + } + }); + + lastQueriedTrack = beatmap.Track; + } private readonly WaveformDrawNodeSharedData sharedData = new WaveformDrawNodeSharedData(); + protected override DrawNode CreateDrawNode() => new WaveformDrawNode(); protected override void ApplyDrawNode(DrawNode node) { var n = (WaveformDrawNode)node; n.Shader = shader; n.Texture = texture; - n.Points = points; - n.Channels = channels; n.Size = DrawSize; n.Shared = sharedData; + n.Points = waveform.Generate((int)(MathHelper.Clamp(Math.Ceiling(DrawWidth), 0, waveform.TotalPoints) * resolution)); + n.Channels = waveform.Channels; base.ApplyDrawNode(node); } From 01c839eda7dfeb6f68edf6b6a442e956f6715c60 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Wed, 4 Oct 2017 14:55:17 +0900 Subject: [PATCH 05/15] Move WaveformDisplay into separate class and add some commenting --- .../Edit/Screens/Compose/WaveformDisplay.cs | 168 ++++++++++++++++++ osu.Game/Tests/Visual/TestCaseWaveform.cs | 149 +--------------- osu.Game/osu.Game.csproj | 1 + 3 files changed, 170 insertions(+), 148 deletions(-) create mode 100644 osu.Game/Screens/Edit/Screens/Compose/WaveformDisplay.cs diff --git a/osu.Game/Screens/Edit/Screens/Compose/WaveformDisplay.cs b/osu.Game/Screens/Edit/Screens/Compose/WaveformDisplay.cs new file mode 100644 index 0000000000..0f0c32ea0d --- /dev/null +++ b/osu.Game/Screens/Edit/Screens/Compose/WaveformDisplay.cs @@ -0,0 +1,168 @@ +// Copyright (c) 2007-2017 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System; +using System.Collections.Generic; +using OpenTK; +using osu.Framework.Allocation; +using osu.Framework.Audio.Track; +using osu.Framework.Configuration; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Batches; +using osu.Framework.Graphics.Colour; +using osu.Framework.Graphics.OpenGL.Vertices; +using osu.Framework.Graphics.Primitives; +using osu.Framework.Graphics.Shaders; +using osu.Framework.Graphics.Textures; +using osu.Game.Beatmaps; + +namespace osu.Game.Screens.Edit.Screens.Compose +{ + public class WaveformDisplay : Drawable + { + /// + /// The beatmap which the audio waveform should be displayed for. + /// + /// + public readonly Bindable Beatmap = new Bindable(); + + private Shader shader; + private readonly Texture texture; + + public WaveformDisplay() + { + texture = Texture.WhitePixel; + Beatmap.ValueChanged += generateWaveform; + } + + [BackgroundDependencyLoader] + private void load(ShaderManager shaders) + { + shader = shaders?.Load(VertexShaderDescriptor.TEXTURE_2, FragmentShaderDescriptor.TEXTURE_ROUNDED); + } + + private float resolution = 1; + /// + /// Controls the amount of interpolation of the waveform into the width of this . + /// Points in the waveform are interpolated between 1 / pixels of this . + /// + /// + public float Resolution + { + get { return resolution; } + set + { + if (value < 0 || value > 1) + throw new ArgumentOutOfRangeException(nameof(value)); + + if (resolution == value) + return; + resolution = value; + + Invalidate(Invalidation.DrawNode); + } + } + + private Waveform waveform; + private Track lastQueriedTrack; + private void generateWaveform(WorkingBeatmap beatmap) + { + // Cancel the old query so we don't saturate the audio thread + lastQueriedTrack?.CancelWaveformQuery(); + + beatmap.Track.QueryWaveform(w => + { + if (Beatmap.Value == beatmap) + { + waveform = w; + Invalidate(Invalidation.DrawNode); + } + }); + + lastQueriedTrack = beatmap.Track; + } + + private readonly WaveformDrawNodeSharedData sharedData = new WaveformDrawNodeSharedData(); + protected override DrawNode CreateDrawNode() => new WaveformDrawNode(); + protected override void ApplyDrawNode(DrawNode node) + { + var n = (WaveformDrawNode)node; + + n.Shader = shader; + n.Texture = texture; + n.Size = DrawSize; + n.Shared = sharedData; + n.Points = waveform.Generate((int)(MathHelper.Clamp(Math.Ceiling(DrawWidth), 0, waveform.TotalPoints) * resolution)); + n.Channels = waveform.Channels; + + base.ApplyDrawNode(node); + } + + private class WaveformDrawNodeSharedData + { + public readonly QuadBatch VertexBatch = new QuadBatch(1000, 10); + } + + private class WaveformDrawNode : DrawNode + { + public Shader Shader; + public Texture Texture; + + public WaveformDrawNodeSharedData Shared; + + public List Points; + public Vector2 Size; + public int Channels; + + public override void Draw(Action vertexAction) + { + base.Draw(vertexAction); + + if (Points == null || Points.Count == 0) + return; + + Shader.Bind(); + Texture.TextureGL.Bind(); + + float separation = Size.X / (Points.Count - 1); + Vector2 localInflationAmount = new Vector2(0, 1) * DrawInfo.MatrixInverse.ExtractScale().Xy; + + for (int i = 0; i < Points.Count - 1; i++) + { + ColourInfo colour = DrawInfo.Colour; + Quad quadToDraw; + + switch (Channels) + { + default: + case 2: + { + float height = Size.Y / 2; + quadToDraw = new Quad( + new Vector2(i * separation, height - Points[i].Amplitude[0] * height), + new Vector2((i + 1) * separation, height - Points[i + 1].Amplitude[0] * height), + new Vector2(i * separation, height + Points[i].Amplitude[1] * height), + new Vector2((i + 1) * separation, height + Points[i + 1].Amplitude[1] * height) + ); + } + break; + case 1: + { + quadToDraw = new Quad( + new Vector2(i * separation, Size.Y - Points[i].Amplitude[0] * Size.Y), + new Vector2((i + 1) * separation, Size.Y - Points[i + 1].Amplitude[0] * Size.Y), + new Vector2(i * separation, Size.Y), + new Vector2((i + 1) * separation, Size.Y) + ); + break; + } + } + + Texture.DrawQuad(quadToDraw * DrawInfo.Matrix, colour, null, Shared.VertexBatch.Add, Vector2.Divide(localInflationAmount, quadToDraw.Size)); + } + + Shader.Unbind(); + } + } + } +} diff --git a/osu.Game/Tests/Visual/TestCaseWaveform.cs b/osu.Game/Tests/Visual/TestCaseWaveform.cs index 4df7ffe367..0430185bf7 100644 --- a/osu.Game/Tests/Visual/TestCaseWaveform.cs +++ b/osu.Game/Tests/Visual/TestCaseWaveform.cs @@ -1,26 +1,17 @@ // Copyright (c) 2007-2017 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE -using System; -using System.Collections.Generic; using OpenTK; using OpenTK.Graphics; -using OpenTK.Graphics.ES30; using osu.Framework.Allocation; -using osu.Framework.Audio.Track; using osu.Framework.Configuration; using osu.Framework.Graphics; -using osu.Framework.Graphics.Batches; -using osu.Framework.Graphics.Colour; using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics.OpenGL.Vertices; -using osu.Framework.Graphics.Primitives; -using osu.Framework.Graphics.Shaders; using osu.Framework.Graphics.Shapes; -using osu.Framework.Graphics.Textures; using osu.Game.Beatmaps; using osu.Game.Graphics.Sprites; using osu.Game.Overlays; +using osu.Game.Screens.Edit.Screens.Compose; namespace osu.Game.Tests.Visual { @@ -94,143 +85,5 @@ namespace osu.Game.Tests.Visual [BackgroundDependencyLoader] private void load(OsuGameBase osuGame) => beatmapBacking.BindTo(osuGame.Beatmap); - - private class WaveformDisplay : Drawable - { - public readonly Bindable Beatmap = new Bindable(); - - private Waveform waveform; - - private Shader shader; - private readonly Texture texture; - - public WaveformDisplay() - { - texture = Texture.WhitePixel; - Beatmap.ValueChanged += generateWaveform; - } - - [BackgroundDependencyLoader] - private void load(ShaderManager shaders) - { - shader = shaders?.Load(VertexShaderDescriptor.TEXTURE_2, FragmentShaderDescriptor.TEXTURE_ROUNDED); - } - - private float resolution = 1; - public float Resolution - { - get { return resolution; } - set - { - if (resolution == value) - return; - resolution = value; - - Invalidate(Invalidation.DrawNode); - } - } - - private Track lastQueriedTrack; - - private void generateWaveform(WorkingBeatmap beatmap) - { - // Cancel the old query so we don't saturate the audio thread - lastQueriedTrack?.CancelWaveformQuery(); - - beatmap.Track.QueryWaveform(w => - { - if (Beatmap.Value == beatmap) - { - waveform = w; - Invalidate(Invalidation.DrawNode); - } - }); - - lastQueriedTrack = beatmap.Track; - } - - private readonly WaveformDrawNodeSharedData sharedData = new WaveformDrawNodeSharedData(); - protected override DrawNode CreateDrawNode() => new WaveformDrawNode(); - protected override void ApplyDrawNode(DrawNode node) - { - var n = (WaveformDrawNode)node; - - n.Shader = shader; - n.Texture = texture; - n.Size = DrawSize; - n.Shared = sharedData; - n.Points = waveform.Generate((int)(MathHelper.Clamp(Math.Ceiling(DrawWidth), 0, waveform.TotalPoints) * resolution)); - n.Channels = waveform.Channels; - - base.ApplyDrawNode(node); - } - - private class WaveformDrawNodeSharedData - { - public readonly QuadBatch VertexBatch = new QuadBatch(1000, 10); - } - - private class WaveformDrawNode : DrawNode - { - public Shader Shader; - public Texture Texture; - - public WaveformDrawNodeSharedData Shared; - - public List Points; - public Vector2 Size; - public int Channels; - - public override void Draw(Action vertexAction) - { - base.Draw(vertexAction); - - if (Points == null || Points.Count == 0) - return; - - Shader.Bind(); - Texture.TextureGL.Bind(); - - float separation = Size.X / (Points.Count - 1); - Vector2 localInflationAmount = new Vector2(0, 1) * DrawInfo.MatrixInverse.ExtractScale().Xy; - - for (int i = 0; i < Points.Count - 1; i++) - { - ColourInfo colour = DrawInfo.Colour; - Quad quadToDraw; - - switch (Channels) - { - default: - case 2: - { - float height = Size.Y / 2; - quadToDraw = new Quad( - new Vector2(i * separation, height - Points[i].Amplitude[0] * height), - new Vector2((i + 1) * separation, height - Points[i + 1].Amplitude[0] * height), - new Vector2(i * separation, height + Points[i].Amplitude[1] * height), - new Vector2((i + 1) * separation, height + Points[i + 1].Amplitude[1] * height) - ); - } - break; - case 1: - { - quadToDraw = new Quad( - new Vector2(i * separation, Size.Y - Points[i].Amplitude[0] * Size.Y), - new Vector2((i + 1) * separation, Size.Y - Points[i + 1].Amplitude[0] * Size.Y), - new Vector2(i * separation, Size.Y), - new Vector2((i + 1) * separation, Size.Y) - ); - break; - } - } - - Texture.DrawQuad(quadToDraw * DrawInfo.Matrix, colour, null, Shared.VertexBatch.Add, Vector2.Divide(localInflationAmount, quadToDraw.Size)); - } - - Shader.Unbind(); - } - } - } } } diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index 56490b6119..f89810dfc1 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -620,6 +620,7 @@ + From 80e984f72dea9361b6b2d53cb9bdbe42614412d2 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Wed, 4 Oct 2017 18:55:38 +0900 Subject: [PATCH 06/15] Update in-line with framework --- osu.Game/Screens/Edit/Screens/Compose/WaveformDisplay.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Screens/Edit/Screens/Compose/WaveformDisplay.cs b/osu.Game/Screens/Edit/Screens/Compose/WaveformDisplay.cs index 0f0c32ea0d..a781e63fd9 100644 --- a/osu.Game/Screens/Edit/Screens/Compose/WaveformDisplay.cs +++ b/osu.Game/Screens/Edit/Screens/Compose/WaveformDisplay.cs @@ -92,7 +92,7 @@ namespace osu.Game.Screens.Edit.Screens.Compose n.Texture = texture; n.Size = DrawSize; n.Shared = sharedData; - n.Points = waveform.Generate((int)(MathHelper.Clamp(Math.Ceiling(DrawWidth), 0, waveform.TotalPoints) * resolution)); + n.Points = waveform.Generate((int)(MathHelper.Clamp(Math.Ceiling(DrawWidth), 0, waveform.MaximumPoints) * resolution)); n.Channels = waveform.Channels; base.ApplyDrawNode(node); From 81960c7b487d2a66b134ad9554d0a639153a2483 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Wed, 4 Oct 2017 18:55:45 +0900 Subject: [PATCH 07/15] CI fixes --- osu.Game/Screens/Edit/Screens/Compose/WaveformDisplay.cs | 2 -- osu.Game/Tests/Visual/TestCaseWaveform.cs | 5 ++--- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/osu.Game/Screens/Edit/Screens/Compose/WaveformDisplay.cs b/osu.Game/Screens/Edit/Screens/Compose/WaveformDisplay.cs index a781e63fd9..236f6b6d47 100644 --- a/osu.Game/Screens/Edit/Screens/Compose/WaveformDisplay.cs +++ b/osu.Game/Screens/Edit/Screens/Compose/WaveformDisplay.cs @@ -23,7 +23,6 @@ namespace osu.Game.Screens.Edit.Screens.Compose /// /// The beatmap which the audio waveform should be displayed for. /// - /// public readonly Bindable Beatmap = new Bindable(); private Shader shader; @@ -46,7 +45,6 @@ namespace osu.Game.Screens.Edit.Screens.Compose /// Controls the amount of interpolation of the waveform into the width of this . /// Points in the waveform are interpolated between 1 / pixels of this . /// - /// public float Resolution { get { return resolution; } diff --git a/osu.Game/Tests/Visual/TestCaseWaveform.cs b/osu.Game/Tests/Visual/TestCaseWaveform.cs index 0430185bf7..eff4f5cdad 100644 --- a/osu.Game/Tests/Visual/TestCaseWaveform.cs +++ b/osu.Game/Tests/Visual/TestCaseWaveform.cs @@ -21,7 +21,6 @@ namespace osu.Game.Tests.Visual public TestCaseWaveform() { - MusicController mc; FillFlowContainer flow; Child = flow = new FillFlowContainer { @@ -30,7 +29,7 @@ namespace osu.Game.Tests.Visual Spacing = new Vector2(0, 10), Children = new Drawable[] { - mc = new MusicController + new MusicController { Anchor = Anchor.TopCentre, Origin = Anchor.TopCentre, @@ -74,7 +73,7 @@ namespace osu.Game.Tests.Visual { Anchor = Anchor.Centre, Origin = Anchor.Centre, - Text = $"Resolution: {(1f / i).ToString("0.00")}" + Text = $"Resolution: {(1f / i):0.00}" } } } From 5ca4a2d2c81600178816afb37778e40ce480f47c Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Wed, 4 Oct 2017 19:09:39 +0900 Subject: [PATCH 08/15] Add some nullchecks to WaveformDisplay --- osu.Game/Screens/Edit/Screens/Compose/WaveformDisplay.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Screens/Edit/Screens/Compose/WaveformDisplay.cs b/osu.Game/Screens/Edit/Screens/Compose/WaveformDisplay.cs index 236f6b6d47..d1550117df 100644 --- a/osu.Game/Screens/Edit/Screens/Compose/WaveformDisplay.cs +++ b/osu.Game/Screens/Edit/Screens/Compose/WaveformDisplay.cs @@ -90,8 +90,8 @@ namespace osu.Game.Screens.Edit.Screens.Compose n.Texture = texture; n.Size = DrawSize; n.Shared = sharedData; - n.Points = waveform.Generate((int)(MathHelper.Clamp(Math.Ceiling(DrawWidth), 0, waveform.MaximumPoints) * resolution)); - n.Channels = waveform.Channels; + n.Points = waveform?.Generate((int)(MathHelper.Clamp(Math.Ceiling(DrawWidth), 0, waveform.MaximumPoints) * resolution)); + n.Channels = waveform?.Channels ?? 0; base.ApplyDrawNode(node); } From 1377f73b460b90f7fd4037b51536cb36a7fff92f Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Wed, 4 Oct 2017 21:57:29 +0900 Subject: [PATCH 09/15] Multiply resolution before clamping --- osu.Game/Screens/Edit/Screens/Compose/WaveformDisplay.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Screens/Edit/Screens/Compose/WaveformDisplay.cs b/osu.Game/Screens/Edit/Screens/Compose/WaveformDisplay.cs index d1550117df..e11cd667e6 100644 --- a/osu.Game/Screens/Edit/Screens/Compose/WaveformDisplay.cs +++ b/osu.Game/Screens/Edit/Screens/Compose/WaveformDisplay.cs @@ -90,7 +90,7 @@ namespace osu.Game.Screens.Edit.Screens.Compose n.Texture = texture; n.Size = DrawSize; n.Shared = sharedData; - n.Points = waveform?.Generate((int)(MathHelper.Clamp(Math.Ceiling(DrawWidth), 0, waveform.MaximumPoints) * resolution)); + n.Points = waveform?.Generate((int)(MathHelper.Clamp(Math.Ceiling(DrawWidth) * Resolution, 0, waveform.MaximumPoints))); n.Channels = waveform?.Channels ?? 0; base.ApplyDrawNode(node); From ea4545299386900ba257dadcb25f7a0960901884 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Wed, 4 Oct 2017 21:57:46 +0900 Subject: [PATCH 10/15] Allow resolution > 1 --- osu.Game/Screens/Edit/Screens/Compose/WaveformDisplay.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Screens/Edit/Screens/Compose/WaveformDisplay.cs b/osu.Game/Screens/Edit/Screens/Compose/WaveformDisplay.cs index e11cd667e6..3856558a32 100644 --- a/osu.Game/Screens/Edit/Screens/Compose/WaveformDisplay.cs +++ b/osu.Game/Screens/Edit/Screens/Compose/WaveformDisplay.cs @@ -50,7 +50,7 @@ namespace osu.Game.Screens.Edit.Screens.Compose get { return resolution; } set { - if (value < 0 || value > 1) + if (value < 0) throw new ArgumentOutOfRangeException(nameof(value)); if (resolution == value) From e9bc5dbd0f5e18cb0520596303cf021acc3f6c5e Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Wed, 4 Oct 2017 23:35:20 +0900 Subject: [PATCH 11/15] Include framework commits --- osu-framework | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu-framework b/osu-framework index 9d142a8e00..bcd92492a9 160000 --- a/osu-framework +++ b/osu-framework @@ -1 +1 @@ -Subproject commit 9d142a8e009794dfee828392e36025d08577131d +Subproject commit bcd92492a9f01178b83c2360cb9b87536435adde From 0a9d23b4bad1da2081f720a3227319d10bbd11df Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 5 Oct 2017 14:33:39 +0900 Subject: [PATCH 12/15] Update with framework changes (removal of WaveformDisplay) --- osu-framework | 2 +- .../Screens/Compose/BeatmapWaveformGraph.cs | 33 ++++ .../Edit/Screens/Compose/WaveformDisplay.cs | 166 ------------------ osu.Game/Tests/Visual/TestCaseWaveform.cs | 2 +- osu.Game/osu.Game.csproj | 2 +- 5 files changed, 36 insertions(+), 169 deletions(-) create mode 100644 osu.Game/Screens/Edit/Screens/Compose/BeatmapWaveformGraph.cs delete mode 100644 osu.Game/Screens/Edit/Screens/Compose/WaveformDisplay.cs diff --git a/osu-framework b/osu-framework index bcd92492a9..4019939ba9 160000 --- a/osu-framework +++ b/osu-framework @@ -1 +1 @@ -Subproject commit bcd92492a9f01178b83c2360cb9b87536435adde +Subproject commit 4019939ba9f0a407880e00422280ba573ffddb24 diff --git a/osu.Game/Screens/Edit/Screens/Compose/BeatmapWaveformGraph.cs b/osu.Game/Screens/Edit/Screens/Compose/BeatmapWaveformGraph.cs new file mode 100644 index 0000000000..ceabda5b16 --- /dev/null +++ b/osu.Game/Screens/Edit/Screens/Compose/BeatmapWaveformGraph.cs @@ -0,0 +1,33 @@ +// Copyright (c) 2007-2017 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Framework.Configuration; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Audio; +using osu.Framework.Graphics.Containers; +using osu.Game.Beatmaps; + +namespace osu.Game.Screens.Edit.Screens.Compose +{ + public class BeatmapWaveformGraph : CompositeDrawable + { + public readonly Bindable Beatmap = new Bindable(); + + private readonly WaveformGraph graph; + + public BeatmapWaveformGraph() + { + InternalChild = graph = new WaveformGraph { RelativeSizeAxes = Axes.Both }; + Beatmap.ValueChanged += b => graph.Track = b.Track; + } + + /// + /// Gets or sets the . + /// + public float Resolution + { + get { return graph.Resolution; } + set { graph.Resolution = value; } + } + } +} diff --git a/osu.Game/Screens/Edit/Screens/Compose/WaveformDisplay.cs b/osu.Game/Screens/Edit/Screens/Compose/WaveformDisplay.cs deleted file mode 100644 index 3856558a32..0000000000 --- a/osu.Game/Screens/Edit/Screens/Compose/WaveformDisplay.cs +++ /dev/null @@ -1,166 +0,0 @@ -// Copyright (c) 2007-2017 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using System; -using System.Collections.Generic; -using OpenTK; -using osu.Framework.Allocation; -using osu.Framework.Audio.Track; -using osu.Framework.Configuration; -using osu.Framework.Graphics; -using osu.Framework.Graphics.Batches; -using osu.Framework.Graphics.Colour; -using osu.Framework.Graphics.OpenGL.Vertices; -using osu.Framework.Graphics.Primitives; -using osu.Framework.Graphics.Shaders; -using osu.Framework.Graphics.Textures; -using osu.Game.Beatmaps; - -namespace osu.Game.Screens.Edit.Screens.Compose -{ - public class WaveformDisplay : Drawable - { - /// - /// The beatmap which the audio waveform should be displayed for. - /// - public readonly Bindable Beatmap = new Bindable(); - - private Shader shader; - private readonly Texture texture; - - public WaveformDisplay() - { - texture = Texture.WhitePixel; - Beatmap.ValueChanged += generateWaveform; - } - - [BackgroundDependencyLoader] - private void load(ShaderManager shaders) - { - shader = shaders?.Load(VertexShaderDescriptor.TEXTURE_2, FragmentShaderDescriptor.TEXTURE_ROUNDED); - } - - private float resolution = 1; - /// - /// Controls the amount of interpolation of the waveform into the width of this . - /// Points in the waveform are interpolated between 1 / pixels of this . - /// - public float Resolution - { - get { return resolution; } - set - { - if (value < 0) - throw new ArgumentOutOfRangeException(nameof(value)); - - if (resolution == value) - return; - resolution = value; - - Invalidate(Invalidation.DrawNode); - } - } - - private Waveform waveform; - private Track lastQueriedTrack; - private void generateWaveform(WorkingBeatmap beatmap) - { - // Cancel the old query so we don't saturate the audio thread - lastQueriedTrack?.CancelWaveformQuery(); - - beatmap.Track.QueryWaveform(w => - { - if (Beatmap.Value == beatmap) - { - waveform = w; - Invalidate(Invalidation.DrawNode); - } - }); - - lastQueriedTrack = beatmap.Track; - } - - private readonly WaveformDrawNodeSharedData sharedData = new WaveformDrawNodeSharedData(); - protected override DrawNode CreateDrawNode() => new WaveformDrawNode(); - protected override void ApplyDrawNode(DrawNode node) - { - var n = (WaveformDrawNode)node; - - n.Shader = shader; - n.Texture = texture; - n.Size = DrawSize; - n.Shared = sharedData; - n.Points = waveform?.Generate((int)(MathHelper.Clamp(Math.Ceiling(DrawWidth) * Resolution, 0, waveform.MaximumPoints))); - n.Channels = waveform?.Channels ?? 0; - - base.ApplyDrawNode(node); - } - - private class WaveformDrawNodeSharedData - { - public readonly QuadBatch VertexBatch = new QuadBatch(1000, 10); - } - - private class WaveformDrawNode : DrawNode - { - public Shader Shader; - public Texture Texture; - - public WaveformDrawNodeSharedData Shared; - - public List Points; - public Vector2 Size; - public int Channels; - - public override void Draw(Action vertexAction) - { - base.Draw(vertexAction); - - if (Points == null || Points.Count == 0) - return; - - Shader.Bind(); - Texture.TextureGL.Bind(); - - float separation = Size.X / (Points.Count - 1); - Vector2 localInflationAmount = new Vector2(0, 1) * DrawInfo.MatrixInverse.ExtractScale().Xy; - - for (int i = 0; i < Points.Count - 1; i++) - { - ColourInfo colour = DrawInfo.Colour; - Quad quadToDraw; - - switch (Channels) - { - default: - case 2: - { - float height = Size.Y / 2; - quadToDraw = new Quad( - new Vector2(i * separation, height - Points[i].Amplitude[0] * height), - new Vector2((i + 1) * separation, height - Points[i + 1].Amplitude[0] * height), - new Vector2(i * separation, height + Points[i].Amplitude[1] * height), - new Vector2((i + 1) * separation, height + Points[i + 1].Amplitude[1] * height) - ); - } - break; - case 1: - { - quadToDraw = new Quad( - new Vector2(i * separation, Size.Y - Points[i].Amplitude[0] * Size.Y), - new Vector2((i + 1) * separation, Size.Y - Points[i + 1].Amplitude[0] * Size.Y), - new Vector2(i * separation, Size.Y), - new Vector2((i + 1) * separation, Size.Y) - ); - break; - } - } - - Texture.DrawQuad(quadToDraw * DrawInfo.Matrix, colour, null, Shared.VertexBatch.Add, Vector2.Divide(localInflationAmount, quadToDraw.Size)); - } - - Shader.Unbind(); - } - } - } -} diff --git a/osu.Game/Tests/Visual/TestCaseWaveform.cs b/osu.Game/Tests/Visual/TestCaseWaveform.cs index eff4f5cdad..531005af10 100644 --- a/osu.Game/Tests/Visual/TestCaseWaveform.cs +++ b/osu.Game/Tests/Visual/TestCaseWaveform.cs @@ -41,7 +41,7 @@ namespace osu.Game.Tests.Visual for (int i = 1; i <= 16; i *= 2) { - var newDisplay = new WaveformDisplay + var newDisplay = new BeatmapWaveformGraph { RelativeSizeAxes = Axes.Both, Resolution = 1f / i diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index f89810dfc1..106e4bfc3b 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -266,6 +266,7 @@ + @@ -620,7 +621,6 @@ - From 7926757898ea4b12579f6fae8abce00e89a754ca Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 5 Oct 2017 14:33:49 +0900 Subject: [PATCH 13/15] Remove unneeded parens --- osu.Game/Tests/Visual/TestCaseWaveform.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Tests/Visual/TestCaseWaveform.cs b/osu.Game/Tests/Visual/TestCaseWaveform.cs index 531005af10..4a6e93c7bf 100644 --- a/osu.Game/Tests/Visual/TestCaseWaveform.cs +++ b/osu.Game/Tests/Visual/TestCaseWaveform.cs @@ -73,7 +73,7 @@ namespace osu.Game.Tests.Visual { Anchor = Anchor.Centre, Origin = Anchor.Centre, - Text = $"Resolution: {(1f / i):0.00}" + Text = $"Resolution: {1f / i:0.00}" } } } From 19b38983dffa148f09441a20185dd0f8b6f70521 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 9 Oct 2017 17:18:11 +0900 Subject: [PATCH 14/15] Update in-line with framework --- osu-framework | 2 +- osu.Game/Beatmaps/BeatmapManager.cs | 10 ++++++++++ osu.Game/Beatmaps/WorkingBeatmap.cs | 14 ++++++++++++++ .../Edit/Screens/Compose/BeatmapWaveformGraph.cs | 2 +- 4 files changed, 26 insertions(+), 2 deletions(-) diff --git a/osu-framework b/osu-framework index 4019939ba9..38cf96c899 160000 --- a/osu-framework +++ b/osu-framework @@ -1 +1 @@ -Subproject commit 4019939ba9f0a407880e00422280ba573ffddb24 +Subproject commit 38cf96c8994ad41ae6aa86de6bfe09fd5fa9e1b6 diff --git a/osu.Game/Beatmaps/BeatmapManager.cs b/osu.Game/Beatmaps/BeatmapManager.cs index a1b678392b..5fcdab11f8 100644 --- a/osu.Game/Beatmaps/BeatmapManager.cs +++ b/osu.Game/Beatmaps/BeatmapManager.cs @@ -573,6 +573,16 @@ namespace osu.Game.Beatmaps } catch { return new TrackVirtual(); } } + + protected override Waveform GetWaveform() + { + try + { + var trackData = store.GetStream(getPathForFile(Metadata.AudioFile)); + return trackData == null ? new Waveform() : new Waveform(trackData); + } + catch { return new Waveform(); } + } } /// diff --git a/osu.Game/Beatmaps/WorkingBeatmap.cs b/osu.Game/Beatmaps/WorkingBeatmap.cs index 277846ee80..959e71d48d 100644 --- a/osu.Game/Beatmaps/WorkingBeatmap.cs +++ b/osu.Game/Beatmaps/WorkingBeatmap.cs @@ -43,6 +43,7 @@ namespace osu.Game.Beatmaps protected abstract Beatmap GetBeatmap(); protected abstract Texture GetBackground(); protected abstract Track GetTrack(); + protected virtual Waveform GetWaveform() => new Waveform(); private Beatmap beatmap; private readonly object beatmapLock = new object(); @@ -96,6 +97,17 @@ namespace osu.Game.Beatmaps } } + private Waveform waveform; + private readonly object waveformLock = new object(); + public Waveform Waveform + { + get + { + lock (waveformLock) + return waveform ?? (waveform = GetWaveform()); + } + } + public bool TrackLoaded => track != null; public void TransferTo(WorkingBeatmap other) @@ -114,6 +126,8 @@ namespace osu.Game.Beatmaps { background?.Dispose(); background = null; + + waveform?.Dispose(); } public void DisposeTrack() diff --git a/osu.Game/Screens/Edit/Screens/Compose/BeatmapWaveformGraph.cs b/osu.Game/Screens/Edit/Screens/Compose/BeatmapWaveformGraph.cs index ceabda5b16..f204c8c525 100644 --- a/osu.Game/Screens/Edit/Screens/Compose/BeatmapWaveformGraph.cs +++ b/osu.Game/Screens/Edit/Screens/Compose/BeatmapWaveformGraph.cs @@ -18,7 +18,7 @@ namespace osu.Game.Screens.Edit.Screens.Compose public BeatmapWaveformGraph() { InternalChild = graph = new WaveformGraph { RelativeSizeAxes = Axes.Both }; - Beatmap.ValueChanged += b => graph.Track = b.Track; + Beatmap.ValueChanged += b => graph.Waveform = b.Waveform; } /// From fb8d8ec5524861f2ad7d96edd936a6b497c24bff Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 12 Oct 2017 21:26:25 +0900 Subject: [PATCH 15/15] Update framework --- osu-framework | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu-framework b/osu-framework index b43aecdce9..255b94f5dd 160000 --- a/osu-framework +++ b/osu-framework @@ -1 +1 @@ -Subproject commit b43aecdce95f59912aa0b7211dde3aba1ed9afb1 +Subproject commit 255b94f5dde0188db1a5b37daf9649659e2b8366