mirror of
https://github.com/ppy/osu.git
synced 2025-02-13 01:33:10 +08:00
Merge branch 'master' into fix-taiko-proxies
This commit is contained in:
commit
479eac8aa8
@ -19,7 +19,7 @@ namespace osu.Game.Tests.Beatmaps.IO
|
||||
[TestFixture]
|
||||
public class ImportBeatmapTest
|
||||
{
|
||||
private const string osz_path = @"../../../../osu-resources/osu.Game.Resources/Beatmaps/241526 Soleily - Renatus.osz";
|
||||
public const string TEST_OSZ_PATH = @"../../../../osu-resources/osu.Game.Resources/Beatmaps/241526 Soleily - Renatus.osz";
|
||||
|
||||
[Test]
|
||||
public void TestImportWhenClosed()
|
||||
@ -265,7 +265,7 @@ namespace osu.Game.Tests.Beatmaps.IO
|
||||
private string createTemporaryBeatmap()
|
||||
{
|
||||
var temp = Path.GetTempFileName() + ".osz";
|
||||
File.Copy(osz_path, temp, true);
|
||||
File.Copy(TEST_OSZ_PATH, temp, true);
|
||||
Assert.IsTrue(File.Exists(temp));
|
||||
return temp;
|
||||
}
|
||||
|
@ -4,28 +4,42 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using NUnit.Framework;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Configuration;
|
||||
using OpenTK;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Game.Overlays;
|
||||
using osu.Framework.Graphics.Shapes;
|
||||
using osu.Framework.Graphics.UserInterface;
|
||||
using osu.Framework.Timing;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Screens.Edit.Screens.Compose.Timeline;
|
||||
using OpenTK.Graphics;
|
||||
|
||||
namespace osu.Game.Tests.Visual
|
||||
{
|
||||
[TestFixture]
|
||||
public class TestCaseEditorComposeTimeline : OsuTestCase
|
||||
public class TestCaseEditorComposeTimeline : EditorClockTestCase
|
||||
{
|
||||
public override IReadOnlyList<Type> RequiredTypes => new[] { typeof(TimelineArea), typeof(Timeline), typeof(TimelineButton) };
|
||||
|
||||
public TestCaseEditorComposeTimeline()
|
||||
[BackgroundDependencyLoader]
|
||||
private void load()
|
||||
{
|
||||
Beatmap.Value = new WaveformTestBeatmap();
|
||||
|
||||
Children = new Drawable[]
|
||||
{
|
||||
new MusicController
|
||||
new FillFlowContainer
|
||||
{
|
||||
Anchor = Anchor.TopCentre,
|
||||
Origin = Anchor.TopCentre,
|
||||
State = Visibility.Visible
|
||||
AutoSizeAxes = Axes.Both,
|
||||
Direction = FillDirection.Vertical,
|
||||
Spacing = new Vector2(0, 5),
|
||||
Children = new Drawable[]
|
||||
{
|
||||
new StartStopButton(),
|
||||
new AudioVisualiser(),
|
||||
}
|
||||
},
|
||||
new TimelineArea
|
||||
{
|
||||
@ -36,5 +50,85 @@ namespace osu.Game.Tests.Visual
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private class AudioVisualiser : CompositeDrawable
|
||||
{
|
||||
private readonly Drawable marker;
|
||||
|
||||
private readonly IBindable<WorkingBeatmap> beatmap = new Bindable<WorkingBeatmap>();
|
||||
private IAdjustableClock adjustableClock;
|
||||
|
||||
public AudioVisualiser()
|
||||
{
|
||||
Size = new Vector2(250, 25);
|
||||
|
||||
InternalChildren = new[]
|
||||
{
|
||||
new Box
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Alpha = 0.25f,
|
||||
},
|
||||
marker = new Box
|
||||
{
|
||||
RelativePositionAxes = Axes.X,
|
||||
RelativeSizeAxes = Axes.Y,
|
||||
Width = 2,
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(IAdjustableClock adjustableClock, IBindableBeatmap beatmap)
|
||||
{
|
||||
this.adjustableClock = adjustableClock;
|
||||
this.beatmap.BindTo(beatmap);
|
||||
}
|
||||
|
||||
protected override void Update()
|
||||
{
|
||||
base.Update();
|
||||
|
||||
if (beatmap.Value.Track.IsLoaded)
|
||||
marker.X = (float)(adjustableClock.CurrentTime / beatmap.Value.Track.Length);
|
||||
}
|
||||
}
|
||||
|
||||
private class StartStopButton : Button
|
||||
{
|
||||
private IAdjustableClock adjustableClock;
|
||||
private bool started;
|
||||
|
||||
public StartStopButton()
|
||||
{
|
||||
BackgroundColour = Color4.SlateGray;
|
||||
Size = new Vector2(100, 50);
|
||||
Text = "Start";
|
||||
|
||||
Action = onClick;
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(IAdjustableClock adjustableClock)
|
||||
{
|
||||
this.adjustableClock = adjustableClock;
|
||||
}
|
||||
|
||||
private void onClick()
|
||||
{
|
||||
if (started)
|
||||
{
|
||||
adjustableClock.Stop();
|
||||
Text = "Start";
|
||||
}
|
||||
else
|
||||
{
|
||||
adjustableClock.Start();
|
||||
Text = "Stop";
|
||||
}
|
||||
|
||||
started = !started;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -10,7 +10,6 @@ using osu.Framework.Graphics.Audio;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Shapes;
|
||||
using osu.Game.Graphics.Sprites;
|
||||
using osu.Game.Overlays;
|
||||
|
||||
namespace osu.Game.Tests.Visual
|
||||
{
|
||||
@ -20,22 +19,14 @@ namespace osu.Game.Tests.Visual
|
||||
[BackgroundDependencyLoader]
|
||||
private void load()
|
||||
{
|
||||
Beatmap.Value = new WaveformTestBeatmap();
|
||||
|
||||
FillFlowContainer flow;
|
||||
Child = flow = new FillFlowContainer
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Direction = FillDirection.Vertical,
|
||||
Spacing = new Vector2(0, 10),
|
||||
Children = new Drawable[]
|
||||
{
|
||||
new MusicController
|
||||
{
|
||||
Anchor = Anchor.TopCentre,
|
||||
Origin = Anchor.TopCentre,
|
||||
Y = 100,
|
||||
State = Visibility.Visible
|
||||
},
|
||||
}
|
||||
};
|
||||
|
||||
for (int i = 1; i <= 16; i *= 2)
|
||||
@ -44,10 +35,9 @@ namespace osu.Game.Tests.Visual
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Resolution = 1f / i,
|
||||
Waveform = Beatmap.Value.Waveform,
|
||||
};
|
||||
|
||||
Beatmap.ValueChanged += b => newDisplay.Waveform = b.Waveform;
|
||||
|
||||
flow.Add(new Container
|
||||
{
|
||||
RelativeSizeAxes = Axes.X,
|
||||
|
55
osu.Game.Tests/WaveformTestBeatmap.cs
Normal file
55
osu.Game.Tests/WaveformTestBeatmap.cs
Normal file
@ -0,0 +1,55 @@
|
||||
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using osu.Framework.Audio.Track;
|
||||
using osu.Framework.Graphics.Textures;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Beatmaps.Formats;
|
||||
using osu.Game.IO.Archives;
|
||||
using osu.Game.Tests.Beatmaps.IO;
|
||||
|
||||
namespace osu.Game.Tests
|
||||
{
|
||||
/// <summary>
|
||||
/// A <see cref="WorkingBeatmap"/> that is used for testcases that include waveforms.
|
||||
/// </summary>
|
||||
public class WaveformTestBeatmap : WorkingBeatmap
|
||||
{
|
||||
private readonly ZipArchiveReader reader;
|
||||
private readonly FileStream stream;
|
||||
|
||||
public WaveformTestBeatmap()
|
||||
: base(new BeatmapInfo())
|
||||
{
|
||||
stream = File.OpenRead(ImportBeatmapTest.TEST_OSZ_PATH);
|
||||
reader = new ZipArchiveReader(stream);
|
||||
}
|
||||
|
||||
public override void Dispose()
|
||||
{
|
||||
base.Dispose();
|
||||
stream?.Dispose();
|
||||
reader?.Dispose();
|
||||
}
|
||||
|
||||
protected override IBeatmap GetBeatmap() => createTestBeatmap();
|
||||
|
||||
protected override Texture GetBackground() => null;
|
||||
|
||||
protected override Waveform GetWaveform() => new Waveform(getAudioStream());
|
||||
|
||||
protected override Track GetTrack() => new TrackBass(getAudioStream());
|
||||
|
||||
private Stream getAudioStream() => reader.GetStream(reader.Filenames.First(f => f.EndsWith(".mp3")));
|
||||
private Stream getBeatmapStream() => reader.GetStream(reader.Filenames.First(f => f.EndsWith(".osu")));
|
||||
|
||||
private Beatmap createTestBeatmap()
|
||||
{
|
||||
using (var beatmapStream = getBeatmapStream())
|
||||
using (var beatmapReader = new StreamReader(beatmapStream))
|
||||
return Decoder.GetDecoder<Beatmap>(beatmapReader).Decode(beatmapReader);
|
||||
}
|
||||
}
|
||||
}
|
@ -2,9 +2,12 @@
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Audio.Track;
|
||||
using osu.Framework.Configuration;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Audio;
|
||||
using osu.Framework.Input;
|
||||
using osu.Framework.Timing;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Graphics;
|
||||
|
||||
@ -15,6 +18,8 @@ namespace osu.Game.Screens.Edit.Screens.Compose.Timeline
|
||||
public readonly Bindable<bool> WaveformVisible = new Bindable<bool>();
|
||||
public readonly IBindable<WorkingBeatmap> Beatmap = new Bindable<WorkingBeatmap>();
|
||||
|
||||
private IAdjustableClock adjustableClock;
|
||||
|
||||
public Timeline()
|
||||
{
|
||||
ZoomDuration = 200;
|
||||
@ -25,8 +30,10 @@ namespace osu.Game.Screens.Edit.Screens.Compose.Timeline
|
||||
private WaveformGraph waveform;
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(IBindableBeatmap beatmap)
|
||||
private void load(IBindableBeatmap beatmap, IAdjustableClock adjustableClock)
|
||||
{
|
||||
this.adjustableClock = adjustableClock;
|
||||
|
||||
Child = waveform = new WaveformGraph
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
@ -46,12 +53,132 @@ namespace osu.Game.Screens.Edit.Screens.Compose.Timeline
|
||||
waveform.Waveform = Beatmap.Value.Waveform;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The track's time in the previous frame.
|
||||
/// </summary>
|
||||
private double lastTrackTime;
|
||||
|
||||
/// <summary>
|
||||
/// Whether the user is currently dragging the timeline.
|
||||
/// </summary>
|
||||
private bool handlingDragInput;
|
||||
|
||||
/// <summary>
|
||||
/// Whether the track was playing before a user drag event.
|
||||
/// </summary>
|
||||
private bool trackWasPlaying;
|
||||
|
||||
protected override void Update()
|
||||
{
|
||||
base.Update();
|
||||
|
||||
// We want time = 0 to be at the centre of the container when scrolled to the start
|
||||
// The extrema of track time should be positioned at the centre of the container when scrolled to the start or end
|
||||
Content.Margin = new MarginPadding { Horizontal = DrawWidth / 2 };
|
||||
|
||||
if (handlingDragInput)
|
||||
{
|
||||
// The user is dragging - the track should always follow the timeline
|
||||
seekTrackToCurrent();
|
||||
}
|
||||
else if (adjustableClock.IsRunning)
|
||||
{
|
||||
// If the user hasn't provided mouse input but the track is running, always follow the track
|
||||
scrollToTrackTime();
|
||||
}
|
||||
else
|
||||
{
|
||||
// The track isn't playing, so we want to smooth-scroll once more, and re-enable wheel scrolling
|
||||
// There are two cases we have to be wary of:
|
||||
// 1) The user scrolls on this timeline: We want the track to follow us
|
||||
// 2) The user changes the track time through some other means (scrolling in the editor or overview timeline): We want to follow the track time
|
||||
|
||||
// The simplest way to cover both cases is by checking that inter-frame track times are identical
|
||||
if (adjustableClock.CurrentTime == lastTrackTime)
|
||||
{
|
||||
// The track hasn't been seeked externally
|
||||
seekTrackToCurrent();
|
||||
}
|
||||
else
|
||||
{
|
||||
// The track has been seeked externally
|
||||
scrollToTrackTime();
|
||||
}
|
||||
}
|
||||
|
||||
lastTrackTime = adjustableClock.CurrentTime;
|
||||
|
||||
void seekTrackToCurrent()
|
||||
{
|
||||
if (!(Beatmap.Value.Track is TrackVirtual))
|
||||
adjustableClock.Seek(Current / Content.DrawWidth * Beatmap.Value.Track.Length);
|
||||
}
|
||||
|
||||
void scrollToTrackTime()
|
||||
{
|
||||
if (!(Beatmap.Value.Track is TrackVirtual))
|
||||
ScrollTo((float)(adjustableClock.CurrentTime / Beatmap.Value.Track.Length) * Content.DrawWidth, false);
|
||||
}
|
||||
}
|
||||
|
||||
protected override bool OnMouseDown(InputState state, MouseDownEventArgs args)
|
||||
{
|
||||
if (base.OnMouseDown(state, args))
|
||||
{
|
||||
beginUserDrag();
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
protected override bool OnMouseUp(InputState state, MouseUpEventArgs args)
|
||||
{
|
||||
endUserDrag();
|
||||
return base.OnMouseUp(state, args);
|
||||
}
|
||||
|
||||
private void beginUserDrag()
|
||||
{
|
||||
handlingDragInput = true;
|
||||
trackWasPlaying = adjustableClock.IsRunning;
|
||||
adjustableClock.Stop();
|
||||
}
|
||||
|
||||
private void endUserDrag()
|
||||
{
|
||||
handlingDragInput = false;
|
||||
if (trackWasPlaying)
|
||||
adjustableClock.Start();
|
||||
}
|
||||
|
||||
protected override ScrollbarContainer CreateScrollbar(Direction direction) => new TimelineScrollbar(this, direction);
|
||||
|
||||
private class TimelineScrollbar : ScrollbarContainer
|
||||
{
|
||||
private readonly Timeline timeline;
|
||||
|
||||
public TimelineScrollbar(Timeline timeline, Direction scrollDir)
|
||||
: base(scrollDir)
|
||||
{
|
||||
this.timeline = timeline;
|
||||
}
|
||||
|
||||
protected override bool OnMouseDown(InputState state, MouseDownEventArgs args)
|
||||
{
|
||||
if (base.OnMouseDown(state, args))
|
||||
{
|
||||
timeline.beginUserDrag();
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
protected override bool OnMouseUp(InputState state, MouseUpEventArgs args)
|
||||
{
|
||||
timeline.endUserDrag();
|
||||
return base.OnMouseUp(state, args);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -25,13 +25,20 @@ namespace osu.Game.Tests.Visual
|
||||
Clock = new EditorClock(new ControlPointInfo(), 5000, BeatDivisor) { IsCoupled = false };
|
||||
}
|
||||
|
||||
protected override IReadOnlyDependencyContainer CreateLocalDependencies(IReadOnlyDependencyContainer parent)
|
||||
{
|
||||
var dependencies = new DependencyContainer(base.CreateLocalDependencies(parent));
|
||||
|
||||
dependencies.Cache(BeatDivisor);
|
||||
dependencies.CacheAs<IFrameBasedClock>(Clock);
|
||||
dependencies.CacheAs<IAdjustableClock>(Clock);
|
||||
|
||||
return dependencies;
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load()
|
||||
{
|
||||
Dependencies.Cache(BeatDivisor);
|
||||
Dependencies.CacheAs<IFrameBasedClock>(Clock);
|
||||
Dependencies.CacheAs<IAdjustableClock>(Clock);
|
||||
|
||||
Beatmap.BindValueChanged(beatmapChanged, true);
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user