Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improve IBeatSyncProvider interface and reduce beatmap track dependence #19548

Merged
merged 6 commits into from
Aug 3, 2022
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 18 additions & 0 deletions osu.Game/Beatmaps/BeatSyncProviderExtensions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
// Copyright (c) ppy Pty Ltd <[email protected]>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.

namespace osu.Game.Beatmaps
{
public static class BeatSyncProviderExtensions
{
/// <summary>
/// Check whether beat sync is currently available.
/// </summary>
public static bool CheckBeatSyncAvailable(this IBeatSyncProvider provider) => provider.Clock != null;

/// <summary>
/// Whether the beat sync provider is currently in a kiai section. Should make everything more epic.
/// </summary>
public static bool CheckIsKiaiTime(this IBeatSyncProvider provider) => provider.Clock != null && provider.ControlPoints?.EffectPointAt(provider.Clock.CurrentTime).KiaiMode == true;
}
}
12 changes: 8 additions & 4 deletions osu.Game/Beatmaps/IBeatSyncProvider.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
// See the LICENCE file in the repository root for full licence text.

using osu.Framework.Allocation;
using osu.Framework.Audio.Track;
using osu.Framework.Audio;
using osu.Framework.Timing;
using osu.Game.Beatmaps.ControlPoints;
using osu.Game.Graphics.Containers;
Expand All @@ -14,12 +14,16 @@ namespace osu.Game.Beatmaps
/// Primarily intended for use with <see cref="BeatSyncedContainer"/>.
/// </summary>
[Cached]
public interface IBeatSyncProvider
public interface IBeatSyncProvider : IHasAmplitudes
{
/// <summary>
/// Access any available control points from a beatmap providing beat sync. If <c>null</c>, no current provider is available.
/// </summary>
ControlPointInfo? ControlPoints { get; }

/// <summary>
/// Access a clock currently responsible for providing beat sync. If <c>null</c>, no current provider is available.
/// </summary>
IClock? Clock { get; }

ChannelAmplitudes? Amplitudes { get; }
}
}
31 changes: 15 additions & 16 deletions osu.Game/Graphics/Containers/BeatSyncedContainer.cs
Original file line number Diff line number Diff line change
@@ -1,22 +1,19 @@
// Copyright (c) ppy Pty Ltd <[email protected]>. 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 osu.Framework.Allocation;
using osu.Framework.Audio.Track;
using osu.Framework.Graphics.Containers;
using osu.Game.Beatmaps;
using osu.Game.Beatmaps.ControlPoints;
using osu.Game.Screens.Play;

namespace osu.Game.Graphics.Containers
{
/// <summary>
/// A container which fires a callback when a new beat is reached.
/// Consumes a parent <see cref="GameplayClock"/> or <see cref="Beatmap"/> (whichever is first available).
/// Consumes a parent <see cref="IBeatSyncProvider"/>.
/// </summary>
/// <remarks>
/// This container does not set its own clock to the source used for beat matching.
Expand All @@ -28,8 +25,10 @@ namespace osu.Game.Graphics.Containers
public class BeatSyncedContainer : Container
{
private int lastBeat;
protected TimingControlPoint LastTimingPoint { get; private set; }
protected EffectControlPoint LastEffectPoint { get; private set; }

private TimingControlPoint? lastTimingPoint { get; set; }

protected bool IsKiaiTime { get; private set; }

/// <summary>
/// The amount of time before a beat we should fire <see cref="OnNewBeat(int, TimingControlPoint, EffectControlPoint, ChannelAmplitudes)"/>.
Expand Down Expand Up @@ -71,12 +70,12 @@ public class BeatSyncedContainer : Container
public double MinimumBeatLength { get; set; }

/// <summary>
/// Whether this container is currently tracking a beatmap's timing data.
/// Whether this container is currently tracking a beat sync provider.
/// </summary>
protected bool IsBeatSyncedWithTrack { get; private set; }

[Resolved]
protected IBeatSyncProvider BeatSyncSource { get; private set; }
protected IBeatSyncProvider BeatSyncSource { get; private set; } = null!;

protected virtual void OnNewBeat(int beatIndex, TimingControlPoint timingPoint, EffectControlPoint effectPoint, ChannelAmplitudes amplitudes)
{
Expand All @@ -87,19 +86,18 @@ protected override void Update()
TimingControlPoint timingPoint;
EffectControlPoint effectPoint;

IsBeatSyncedWithTrack = BeatSyncSource.Clock?.IsRunning == true && BeatSyncSource.ControlPoints != null;
IsBeatSyncedWithTrack = BeatSyncSource.CheckBeatSyncAvailable() && BeatSyncSource.Clock?.IsRunning == true;

double currentTrackTime;

if (IsBeatSyncedWithTrack)
{
Debug.Assert(BeatSyncSource.ControlPoints != null);
Debug.Assert(BeatSyncSource.Clock != null);

currentTrackTime = BeatSyncSource.Clock.CurrentTime + EarlyActivationMilliseconds;

timingPoint = BeatSyncSource.ControlPoints.TimingPointAt(currentTrackTime);
effectPoint = BeatSyncSource.ControlPoints.EffectPointAt(currentTrackTime);
timingPoint = BeatSyncSource.ControlPoints?.TimingPointAt(currentTrackTime) ?? TimingControlPoint.DEFAULT;
effectPoint = BeatSyncSource.ControlPoints?.EffectPointAt(currentTrackTime) ?? EffectControlPoint.DEFAULT;
}
else
{
Expand Down Expand Up @@ -128,20 +126,21 @@ protected override void Update()

TimeSinceLastBeat = beatLength - TimeUntilNextBeat;

if (ReferenceEquals(timingPoint, LastTimingPoint) && beatIndex == lastBeat)
if (ReferenceEquals(timingPoint, lastTimingPoint) && beatIndex == lastBeat)
return;

// as this event is sometimes used for sound triggers where `BeginDelayedSequence` has no effect, avoid firing it if too far away from the beat.
// this can happen after a seek operation.
if (AllowMistimedEventFiring || Math.Abs(TimeSinceLastBeat) < MISTIMED_ALLOWANCE)
{
using (BeginDelayedSequence(-TimeSinceLastBeat))
OnNewBeat(beatIndex, timingPoint, effectPoint, BeatSyncSource.Amplitudes ?? ChannelAmplitudes.Empty);
OnNewBeat(beatIndex, timingPoint, effectPoint, BeatSyncSource.CurrentAmplitudes);
}

lastBeat = beatIndex;
LastTimingPoint = timingPoint;
LastEffectPoint = effectPoint;
lastTimingPoint = timingPoint;

IsKiaiTime = effectPoint.KiaiMode;
}
}
}
2 changes: 1 addition & 1 deletion osu.Game/OsuGameBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -588,6 +588,6 @@ protected override void Dispose(bool isDisposing)

ControlPointInfo IBeatSyncProvider.ControlPoints => Beatmap.Value.BeatmapLoaded ? Beatmap.Value.Beatmap.ControlPointInfo : null;
IClock IBeatSyncProvider.Clock => Beatmap.Value.TrackLoaded ? Beatmap.Value.Track : (IClock)null;
ChannelAmplitudes? IBeatSyncProvider.Amplitudes => Beatmap.Value.TrackLoaded ? Beatmap.Value.Track.CurrentAmplitudes : null;
ChannelAmplitudes IHasAmplitudes.CurrentAmplitudes => Beatmap.Value.TrackLoaded ? Beatmap.Value.Track.CurrentAmplitudes : ChannelAmplitudes.Empty;
}
}
3 changes: 2 additions & 1 deletion osu.Game/Screens/Edit/Editor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
using JetBrains.Annotations;
using osu.Framework;
using osu.Framework.Allocation;
using osu.Framework.Audio;
using osu.Framework.Audio.Track;
using osu.Framework.Bindables;
using osu.Framework.Graphics;
Expand Down Expand Up @@ -949,7 +950,7 @@ private void cancelExit()

ControlPointInfo IBeatSyncProvider.ControlPoints => editorBeatmap.ControlPointInfo;
IClock IBeatSyncProvider.Clock => clock;
ChannelAmplitudes? IBeatSyncProvider.Amplitudes => Beatmap.Value.TrackLoaded ? Beatmap.Value.Track.CurrentAmplitudes : null;
ChannelAmplitudes IHasAmplitudes.CurrentAmplitudes => Beatmap.Value.TrackLoaded ? Beatmap.Value.Track.CurrentAmplitudes : ChannelAmplitudes.Empty;

private class BeatmapEditorToast : Toast
{
Expand Down
116 changes: 53 additions & 63 deletions osu.Game/Screens/Menu/LogoVisualisation.cs
Original file line number Diff line number Diff line change
@@ -1,27 +1,23 @@
// Copyright (c) ppy Pty Ltd <[email protected]>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.

#nullable disable

using osuTK;
using osuTK.Graphics;
using System;
using System.Collections.Generic;
using osu.Framework.Allocation;
using osu.Framework.Audio;
using osu.Framework.Audio.Track;
using osu.Framework.Extensions.Color4Extensions;
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;
using System;
using System.Collections.Generic;
using JetBrains.Annotations;
using osu.Framework.Allocation;
using osu.Framework.Audio;
using osu.Framework.Audio.Track;
using osu.Framework.Bindables;
using osu.Framework.Utils;
using osu.Framework.Extensions.Color4Extensions;
using osu.Game.Beatmaps;
using osuTK;
using osuTK.Graphics;

namespace osu.Game.Screens.Menu
{
Expand All @@ -30,8 +26,6 @@ namespace osu.Game.Screens.Menu
/// </summary>
public class LogoVisualisation : Drawable
{
private readonly IBindable<WorkingBeatmap> beatmap = new Bindable<WorkingBeatmap>();

/// <summary>
/// The number of bars to jump each update iteration.
/// </summary>
Expand Down Expand Up @@ -76,7 +70,8 @@ public class LogoVisualisation : Drawable

private readonly float[] frequencyAmplitudes = new float[256];

private IShader shader;
private IShader shader = null!;

private readonly Texture texture;

public LogoVisualisation()
Expand All @@ -93,32 +88,30 @@ public void AddAmplitudeSource(IHasAmplitudes amplitudeSource)
}

[BackgroundDependencyLoader]
private void load(ShaderManager shaders, IBindable<WorkingBeatmap> beatmap)
private void load(ShaderManager shaders)
{
this.beatmap.BindTo(beatmap);
shader = shaders.Load(VertexShaderDescriptor.TEXTURE_2, FragmentShaderDescriptor.TEXTURE_ROUNDED);
}

private readonly float[] temporalAmplitudes = new float[ChannelAmplitudes.AMPLITUDES_SIZE];

[Resolved]
private IBeatSyncProvider beatSyncProvider { get; set; } = null!;

private void updateAmplitudes()
{
var effect = beatmap.Value.BeatmapLoaded && beatmap.Value.TrackLoaded
? beatmap.Value.Beatmap?.ControlPointInfo.EffectPointAt(beatmap.Value.Track.CurrentTime)
: null;

for (int i = 0; i < temporalAmplitudes.Length; i++)
temporalAmplitudes[i] = 0;

if (beatmap.Value.TrackLoaded)
addAmplitudesFromSource(beatmap.Value.Track);
if (beatSyncProvider.Clock != null)
addAmplitudesFromSource(beatSyncProvider);

foreach (var source in amplitudeSources)
addAmplitudesFromSource(source);

for (int i = 0; i < bars_per_visualiser; i++)
{
float targetAmplitude = (temporalAmplitudes[(i + indexOffset) % bars_per_visualiser]) * (effect?.KiaiMode == true ? 1 : 0.5f);
float targetAmplitude = (temporalAmplitudes[(i + indexOffset) % bars_per_visualiser]) * (beatSyncProvider.CheckIsKiaiTime() ? 1 : 0.5f);
if (targetAmplitude > frequencyAmplitudes[i])
frequencyAmplitudes[i] = targetAmplitude;
}
Expand Down Expand Up @@ -153,7 +146,7 @@ protected override void Update()

protected override DrawNode CreateDrawNode() => new VisualisationDrawNode(this);

private void addAmplitudesFromSource([NotNull] IHasAmplitudes source)
private void addAmplitudesFromSource(IHasAmplitudes source)
{
if (source == null) throw new ArgumentNullException(nameof(source));

Expand All @@ -170,8 +163,8 @@ private class VisualisationDrawNode : DrawNode
{
protected new LogoVisualisation Source => (LogoVisualisation)base.Source;

private IShader shader;
private Texture texture;
private IShader shader = null!;
private Texture texture = null!;

// Assuming the logo is a circle, we don't need a second dimension.
private float size;
Expand Down Expand Up @@ -209,43 +202,40 @@ public override void Draw(Action<TexturedVertex2D> vertexAction)
ColourInfo colourInfo = DrawColourInfo.Colour;
colourInfo.ApplyChild(transparent_white);

if (audioData != null)
frenzibyte marked this conversation as resolved.
Show resolved Hide resolved
for (int j = 0; j < visualiser_rounds; j++)
{
for (int j = 0; j < visualiser_rounds; j++)
for (int i = 0; i < bars_per_visualiser; i++)
{
for (int i = 0; i < bars_per_visualiser; i++)
{
if (audioData[i] < amplitude_dead_zone)
continue;

float rotation = MathUtils.DegreesToRadians(i / (float)bars_per_visualiser * 360 + j * 360 / visualiser_rounds);
float rotationCos = MathF.Cos(rotation);
float rotationSin = MathF.Sin(rotation);
// taking the cos and sin to the 0..1 range
var barPosition = new Vector2(rotationCos / 2 + 0.5f, rotationSin / 2 + 0.5f) * size;

var barSize = new Vector2(size * MathF.Sqrt(2 * (1 - MathF.Cos(MathUtils.DegreesToRadians(360f / bars_per_visualiser)))) / 2f, bar_length * audioData[i]);
// The distance between the position and the sides of the bar.
var bottomOffset = new Vector2(-rotationSin * barSize.X / 2, rotationCos * barSize.X / 2);
// The distance between the bottom side of the bar and the top side.
var amplitudeOffset = new Vector2(rotationCos * barSize.Y, rotationSin * barSize.Y);

var rectangle = new Quad(
Vector2Extensions.Transform(barPosition - bottomOffset, DrawInfo.Matrix),
Vector2Extensions.Transform(barPosition - bottomOffset + amplitudeOffset, DrawInfo.Matrix),
Vector2Extensions.Transform(barPosition + bottomOffset, DrawInfo.Matrix),
Vector2Extensions.Transform(barPosition + bottomOffset + amplitudeOffset, DrawInfo.Matrix)
);

DrawQuad(
texture,
rectangle,
colourInfo,
null,
vertexBatch.AddAction,
// barSize by itself will make it smooth more in the X axis than in the Y axis, this reverts that.
Vector2.Divide(inflation, barSize.Yx));
}
if (audioData[i] < amplitude_dead_zone)
continue;

float rotation = MathUtils.DegreesToRadians(i / (float)bars_per_visualiser * 360 + j * 360 / visualiser_rounds);
float rotationCos = MathF.Cos(rotation);
float rotationSin = MathF.Sin(rotation);
// taking the cos and sin to the 0..1 range
var barPosition = new Vector2(rotationCos / 2 + 0.5f, rotationSin / 2 + 0.5f) * size;

var barSize = new Vector2(size * MathF.Sqrt(2 * (1 - MathF.Cos(MathUtils.DegreesToRadians(360f / bars_per_visualiser)))) / 2f, bar_length * audioData[i]);
// The distance between the position and the sides of the bar.
var bottomOffset = new Vector2(-rotationSin * barSize.X / 2, rotationCos * barSize.X / 2);
// The distance between the bottom side of the bar and the top side.
var amplitudeOffset = new Vector2(rotationCos * barSize.Y, rotationSin * barSize.Y);

var rectangle = new Quad(
Vector2Extensions.Transform(barPosition - bottomOffset, DrawInfo.Matrix),
Vector2Extensions.Transform(barPosition - bottomOffset + amplitudeOffset, DrawInfo.Matrix),
Vector2Extensions.Transform(barPosition + bottomOffset, DrawInfo.Matrix),
Vector2Extensions.Transform(barPosition + bottomOffset + amplitudeOffset, DrawInfo.Matrix)
);

DrawQuad(
texture,
rectangle,
colourInfo,
null,
vertexBatch.AddAction,
// barSize by itself will make it smooth more in the X axis than in the Y axis, this reverts that.
Vector2.Divide(inflation, barSize.Yx));
}
}

Expand Down
2 changes: 1 addition & 1 deletion osu.Game/Screens/Menu/OsuLogo.cs
Original file line number Diff line number Diff line change
Expand Up @@ -353,7 +353,7 @@ protected override void Update()
float maxAmplitude = lastBeatIndex >= 0 ? musicController.CurrentTrack.CurrentAmplitudes.Maximum : 0;
logoAmplitudeContainer.Scale = new Vector2((float)Interpolation.Damp(logoAmplitudeContainer.Scale.X, 1 - Math.Max(0, maxAmplitude - scale_adjust_cutoff) * 0.04f, 0.9f, Time.Elapsed));

triangles.Velocity = (float)Interpolation.Damp(triangles.Velocity, triangles_paused_velocity * (LastEffectPoint.KiaiMode ? 4 : 2), 0.995f, Time.Elapsed);
triangles.Velocity = (float)Interpolation.Damp(triangles.Velocity, triangles_paused_velocity * (IsKiaiTime ? 4 : 2), 0.995f, Time.Elapsed);
}
else
{
Expand Down
Loading