mirror of https://github.com/ppy/osu
428 lines
20 KiB
C#
428 lines
20 KiB
C#
// 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 System.Collections.Generic;
|
|
using System.Linq;
|
|
using NUnit.Framework;
|
|
using osu.Framework.Allocation;
|
|
using osu.Framework.Graphics;
|
|
using osu.Framework.Timing;
|
|
using osu.Game.Audio;
|
|
using osu.Game.Beatmaps;
|
|
using osu.Game.Beatmaps.ControlPoints;
|
|
using osu.Game.Rulesets.Objects;
|
|
using osu.Game.Rulesets.Taiko.Objects;
|
|
using osu.Game.Rulesets.Taiko.Objects.Drawables;
|
|
using osu.Game.Rulesets.Taiko.UI;
|
|
using osu.Game.Rulesets.UI;
|
|
using osu.Game.Rulesets.UI.Scrolling;
|
|
using osu.Game.Screens.Play;
|
|
using osu.Game.Tests.Visual;
|
|
|
|
namespace osu.Game.Rulesets.Taiko.Tests
|
|
{
|
|
public partial class TestSceneDrumSampleTriggerSource : OsuTestScene
|
|
{
|
|
[Cached(typeof(IScrollingInfo))]
|
|
private ScrollingTestContainer.TestScrollingInfo info = new ScrollingTestContainer.TestScrollingInfo
|
|
{
|
|
Direction = { Value = ScrollingDirection.Left },
|
|
TimeRange = { Value = 200 },
|
|
};
|
|
|
|
private ScrollingHitObjectContainer hitObjectContainer = null!;
|
|
private TestDrumSampleTriggerSource triggerSource = null!;
|
|
private readonly ManualClock manualClock = new TestManualClock();
|
|
private GameplayClockContainer gameplayClock = null!;
|
|
|
|
[SetUp]
|
|
public void SetUp() => Schedule(() =>
|
|
{
|
|
gameplayClock = new GameplayClockContainer(manualClock)
|
|
{
|
|
RelativeSizeAxes = Axes.Both,
|
|
Children = new Drawable[]
|
|
{
|
|
hitObjectContainer = new ScrollingHitObjectContainer(),
|
|
triggerSource = new TestDrumSampleTriggerSource(hitObjectContainer)
|
|
}
|
|
};
|
|
gameplayClock.Reset(0);
|
|
|
|
hitObjectContainer.Clock = gameplayClock;
|
|
Child = gameplayClock;
|
|
});
|
|
|
|
[Test]
|
|
public void TestNormalHit()
|
|
{
|
|
AddStep("add hit with normal samples", () =>
|
|
{
|
|
var hit = new Hit
|
|
{
|
|
StartTime = 100,
|
|
Samples = new List<HitSampleInfo>
|
|
{
|
|
new HitSampleInfo(HitSampleInfo.HIT_NORMAL)
|
|
}
|
|
};
|
|
hit.ApplyDefaults(new ControlPointInfo(), new BeatmapDifficulty());
|
|
var drawableHit = new DrawableHit(hit);
|
|
hitObjectContainer.Add(drawableHit);
|
|
});
|
|
|
|
AddAssert("most valid object is hit", () => triggerSource.GetMostValidObject(), Is.InstanceOf<Hit>);
|
|
checkSamples(HitType.Centre, false, HitSampleInfo.HIT_NORMAL, SampleControlPoint.DEFAULT_BANK);
|
|
checkSamples(HitType.Rim, false, HitSampleInfo.HIT_CLAP, SampleControlPoint.DEFAULT_BANK);
|
|
|
|
seekTo(200);
|
|
AddAssert("most valid object is hit", () => triggerSource.GetMostValidObject(), Is.InstanceOf<Hit>);
|
|
checkSamples(HitType.Centre, false, HitSampleInfo.HIT_NORMAL, SampleControlPoint.DEFAULT_BANK);
|
|
checkSamples(HitType.Rim, false, HitSampleInfo.HIT_CLAP, SampleControlPoint.DEFAULT_BANK);
|
|
}
|
|
|
|
[Test]
|
|
public void TestSoftHit()
|
|
{
|
|
AddStep("add hit with soft samples", () =>
|
|
{
|
|
var hit = new Hit
|
|
{
|
|
StartTime = 100,
|
|
Samples = new List<HitSampleInfo>
|
|
{
|
|
new HitSampleInfo(HitSampleInfo.HIT_NORMAL, HitSampleInfo.BANK_SOFT)
|
|
}
|
|
};
|
|
hit.ApplyDefaults(new ControlPointInfo(), new BeatmapDifficulty());
|
|
var drawableHit = new DrawableHit(hit);
|
|
hitObjectContainer.Add(drawableHit);
|
|
});
|
|
|
|
AddAssert("most valid object is hit", () => triggerSource.GetMostValidObject(), Is.InstanceOf<Hit>);
|
|
checkSamples(HitType.Centre, false, HitSampleInfo.HIT_NORMAL, HitSampleInfo.BANK_SOFT);
|
|
checkSamples(HitType.Rim, false, HitSampleInfo.HIT_CLAP, HitSampleInfo.BANK_SOFT);
|
|
|
|
seekTo(200);
|
|
AddAssert("most valid object is hit", () => triggerSource.GetMostValidObject(), Is.InstanceOf<Hit>);
|
|
checkSamples(HitType.Centre, false, HitSampleInfo.HIT_NORMAL, HitSampleInfo.BANK_SOFT);
|
|
checkSamples(HitType.Rim, false, HitSampleInfo.HIT_CLAP, HitSampleInfo.BANK_SOFT);
|
|
}
|
|
|
|
[Test]
|
|
public void TestBetweenHits()
|
|
{
|
|
Hit first = null!, second = null!;
|
|
|
|
AddStep("add hit with normal samples", () =>
|
|
{
|
|
first = new Hit
|
|
{
|
|
StartTime = 100,
|
|
Samples = new List<HitSampleInfo>
|
|
{
|
|
new HitSampleInfo(HitSampleInfo.HIT_NORMAL)
|
|
}
|
|
};
|
|
first.ApplyDefaults(new ControlPointInfo(), new BeatmapDifficulty());
|
|
var drawableHit = new DrawableHit(first);
|
|
hitObjectContainer.Add(drawableHit);
|
|
});
|
|
AddStep("add hit with soft samples", () =>
|
|
{
|
|
second = new Hit
|
|
{
|
|
StartTime = 500,
|
|
Samples = new List<HitSampleInfo>
|
|
{
|
|
new HitSampleInfo(HitSampleInfo.HIT_NORMAL, HitSampleInfo.BANK_SOFT),
|
|
new HitSampleInfo(HitSampleInfo.HIT_CLAP, HitSampleInfo.BANK_SOFT)
|
|
}
|
|
};
|
|
second.ApplyDefaults(new ControlPointInfo(), new BeatmapDifficulty());
|
|
var drawableHit = new DrawableHit(second);
|
|
hitObjectContainer.Add(drawableHit);
|
|
});
|
|
|
|
AddAssert("most valid object is first hit", () => triggerSource.GetMostValidObject(), () => Is.EqualTo(first));
|
|
checkSamples(HitType.Centre, false, HitSampleInfo.HIT_NORMAL, HitSampleInfo.BANK_NORMAL);
|
|
checkSamples(HitType.Rim, false, HitSampleInfo.HIT_CLAP, HitSampleInfo.BANK_NORMAL);
|
|
|
|
seekTo(120);
|
|
AddAssert("most valid object is first hit", () => triggerSource.GetMostValidObject(), () => Is.EqualTo(first));
|
|
checkSamples(HitType.Centre, false, HitSampleInfo.HIT_NORMAL, HitSampleInfo.BANK_NORMAL);
|
|
checkSamples(HitType.Rim, false, HitSampleInfo.HIT_CLAP, HitSampleInfo.BANK_NORMAL);
|
|
|
|
seekTo(480);
|
|
AddAssert("most valid object is second hit", () => triggerSource.GetMostValidObject(), () => Is.EqualTo(second));
|
|
checkSamples(HitType.Centre, false, HitSampleInfo.HIT_NORMAL, HitSampleInfo.BANK_SOFT);
|
|
checkSamples(HitType.Rim, false, HitSampleInfo.HIT_CLAP, HitSampleInfo.BANK_SOFT);
|
|
|
|
seekTo(700);
|
|
AddAssert("most valid object is second hit", () => triggerSource.GetMostValidObject(), () => Is.EqualTo(second));
|
|
checkSamples(HitType.Centre, false, HitSampleInfo.HIT_NORMAL, HitSampleInfo.BANK_SOFT);
|
|
checkSamples(HitType.Rim, false, HitSampleInfo.HIT_CLAP, HitSampleInfo.BANK_SOFT);
|
|
}
|
|
|
|
[Test]
|
|
public void TestDrumStrongHit()
|
|
{
|
|
AddStep("add strong hit with drum samples", () =>
|
|
{
|
|
var hit = new Hit
|
|
{
|
|
StartTime = 100,
|
|
Samples = new List<HitSampleInfo>
|
|
{
|
|
new HitSampleInfo(HitSampleInfo.HIT_NORMAL, HitSampleInfo.BANK_DRUM),
|
|
new HitSampleInfo(HitSampleInfo.HIT_FINISH, HitSampleInfo.BANK_DRUM) // implies strong
|
|
}
|
|
};
|
|
hit.ApplyDefaults(new ControlPointInfo(), new BeatmapDifficulty());
|
|
var drawableHit = new DrawableHit(hit);
|
|
hitObjectContainer.Add(drawableHit);
|
|
});
|
|
|
|
AddAssert("most valid object is nested strong hit", () => triggerSource.GetMostValidObject(), Is.InstanceOf<Hit.StrongNestedHit>);
|
|
checkSamples(HitType.Centre, true, $"{HitSampleInfo.HIT_NORMAL},{HitSampleInfo.HIT_FINISH}", HitSampleInfo.BANK_DRUM);
|
|
checkSamples(HitType.Rim, true, $"{HitSampleInfo.HIT_CLAP},{HitSampleInfo.HIT_WHISTLE}", HitSampleInfo.BANK_DRUM);
|
|
|
|
seekTo(200);
|
|
AddAssert("most valid object is hit", () => triggerSource.GetMostValidObject(), Is.InstanceOf<Hit>);
|
|
checkSamples(HitType.Centre, true, $"{HitSampleInfo.HIT_NORMAL},{HitSampleInfo.HIT_FINISH}", HitSampleInfo.BANK_DRUM);
|
|
checkSamples(HitType.Rim, true, $"{HitSampleInfo.HIT_CLAP},{HitSampleInfo.HIT_WHISTLE}", HitSampleInfo.BANK_DRUM);
|
|
}
|
|
|
|
[Test]
|
|
public void TestNormalDrumRoll()
|
|
{
|
|
AddStep("add drum roll with normal samples", () =>
|
|
{
|
|
var drumRoll = new DrumRoll
|
|
{
|
|
StartTime = 100,
|
|
EndTime = 1100,
|
|
Samples = new List<HitSampleInfo>
|
|
{
|
|
new HitSampleInfo(HitSampleInfo.HIT_NORMAL)
|
|
}
|
|
};
|
|
drumRoll.ApplyDefaults(new ControlPointInfo(), new BeatmapDifficulty());
|
|
var drawableDrumRoll = new DrawableDrumRoll(drumRoll);
|
|
hitObjectContainer.Add(drawableDrumRoll);
|
|
});
|
|
|
|
AddAssert("most valid object is drum roll tick", () => triggerSource.GetMostValidObject(), Is.InstanceOf<DrumRollTick>);
|
|
checkSamples(HitType.Centre, false, HitSampleInfo.HIT_NORMAL, SampleControlPoint.DEFAULT_BANK);
|
|
checkSamples(HitType.Rim, false, HitSampleInfo.HIT_CLAP, SampleControlPoint.DEFAULT_BANK);
|
|
|
|
seekTo(600);
|
|
AddAssert("most valid object is drum roll tick", () => triggerSource.GetMostValidObject(), Is.InstanceOf<DrumRollTick>);
|
|
checkSamples(HitType.Centre, false, HitSampleInfo.HIT_NORMAL, SampleControlPoint.DEFAULT_BANK);
|
|
checkSamples(HitType.Rim, false, HitSampleInfo.HIT_CLAP, SampleControlPoint.DEFAULT_BANK);
|
|
|
|
seekTo(1200);
|
|
AddAssert("most valid object is drum roll", () => triggerSource.GetMostValidObject(), Is.InstanceOf<DrumRoll>);
|
|
checkSamples(HitType.Centre, false, HitSampleInfo.HIT_NORMAL, SampleControlPoint.DEFAULT_BANK);
|
|
checkSamples(HitType.Rim, false, HitSampleInfo.HIT_CLAP, SampleControlPoint.DEFAULT_BANK);
|
|
}
|
|
|
|
[Test]
|
|
public void TestSoftDrumRoll()
|
|
{
|
|
AddStep("add drum roll with soft samples", () =>
|
|
{
|
|
var drumRoll = new DrumRoll
|
|
{
|
|
StartTime = 100,
|
|
EndTime = 1100,
|
|
Samples = new List<HitSampleInfo>
|
|
{
|
|
new HitSampleInfo(HitSampleInfo.HIT_NORMAL, HitSampleInfo.BANK_SOFT)
|
|
}
|
|
};
|
|
drumRoll.ApplyDefaults(new ControlPointInfo(), new BeatmapDifficulty());
|
|
var drawableDrumRoll = new DrawableDrumRoll(drumRoll);
|
|
hitObjectContainer.Add(drawableDrumRoll);
|
|
});
|
|
|
|
AddAssert("most valid object is drum roll tick", () => triggerSource.GetMostValidObject(), Is.InstanceOf<DrumRollTick>);
|
|
checkSamples(HitType.Centre, false, HitSampleInfo.HIT_NORMAL, HitSampleInfo.BANK_SOFT);
|
|
checkSamples(HitType.Rim, false, HitSampleInfo.HIT_CLAP, HitSampleInfo.BANK_SOFT);
|
|
|
|
seekTo(600);
|
|
AddAssert("most valid object is drum roll tick", () => triggerSource.GetMostValidObject(), Is.InstanceOf<DrumRollTick>);
|
|
checkSamples(HitType.Centre, false, HitSampleInfo.HIT_NORMAL, HitSampleInfo.BANK_SOFT);
|
|
checkSamples(HitType.Rim, false, HitSampleInfo.HIT_CLAP, HitSampleInfo.BANK_SOFT);
|
|
|
|
seekTo(1200);
|
|
AddAssert("most valid object is drum roll", () => triggerSource.GetMostValidObject(), Is.InstanceOf<DrumRoll>);
|
|
checkSamples(HitType.Centre, false, HitSampleInfo.HIT_NORMAL, HitSampleInfo.BANK_SOFT);
|
|
checkSamples(HitType.Rim, false, HitSampleInfo.HIT_CLAP, HitSampleInfo.BANK_SOFT);
|
|
}
|
|
|
|
[Test]
|
|
public void TestDrumStrongDrumRoll()
|
|
{
|
|
AddStep("add strong drum roll with drum samples", () =>
|
|
{
|
|
var drumRoll = new DrumRoll
|
|
{
|
|
StartTime = 100,
|
|
EndTime = 1100,
|
|
Samples = new List<HitSampleInfo>
|
|
{
|
|
new HitSampleInfo(HitSampleInfo.HIT_NORMAL, HitSampleInfo.BANK_DRUM),
|
|
new HitSampleInfo(HitSampleInfo.HIT_FINISH, HitSampleInfo.BANK_DRUM) // implies strong
|
|
}
|
|
};
|
|
drumRoll.ApplyDefaults(new ControlPointInfo(), new BeatmapDifficulty());
|
|
var drawableDrumRoll = new DrawableDrumRoll(drumRoll);
|
|
hitObjectContainer.Add(drawableDrumRoll);
|
|
});
|
|
|
|
AddAssert("most valid object is drum roll tick", () => triggerSource.GetMostValidObject(), Is.InstanceOf<DrumRollTick>);
|
|
checkSamples(HitType.Centre, true, $"{HitSampleInfo.HIT_NORMAL},{HitSampleInfo.HIT_FINISH}", HitSampleInfo.BANK_DRUM);
|
|
checkSamples(HitType.Rim, true, $"{HitSampleInfo.HIT_CLAP},{HitSampleInfo.HIT_WHISTLE}", HitSampleInfo.BANK_DRUM);
|
|
|
|
seekTo(600);
|
|
AddAssert("most valid object is drum roll tick", () => triggerSource.GetMostValidObject(), Is.InstanceOf<DrumRollTick>);
|
|
checkSamples(HitType.Centre, true, $"{HitSampleInfo.HIT_NORMAL},{HitSampleInfo.HIT_FINISH}", HitSampleInfo.BANK_DRUM);
|
|
checkSamples(HitType.Rim, true, $"{HitSampleInfo.HIT_CLAP},{HitSampleInfo.HIT_WHISTLE}", HitSampleInfo.BANK_DRUM);
|
|
|
|
seekTo(1200);
|
|
AddAssert("most valid object is drum roll", () => triggerSource.GetMostValidObject(), Is.InstanceOf<DrumRoll>);
|
|
checkSamples(HitType.Centre, true, $"{HitSampleInfo.HIT_NORMAL},{HitSampleInfo.HIT_FINISH}", HitSampleInfo.BANK_DRUM);
|
|
checkSamples(HitType.Rim, true, $"{HitSampleInfo.HIT_CLAP},{HitSampleInfo.HIT_WHISTLE}", HitSampleInfo.BANK_DRUM);
|
|
}
|
|
|
|
[Test]
|
|
public void TestNormalSwell()
|
|
{
|
|
AddStep("add swell with normal samples", () =>
|
|
{
|
|
var swell = new Swell
|
|
{
|
|
StartTime = 100,
|
|
EndTime = 1100,
|
|
Samples = new List<HitSampleInfo>
|
|
{
|
|
new HitSampleInfo(HitSampleInfo.HIT_NORMAL)
|
|
}
|
|
};
|
|
swell.ApplyDefaults(new ControlPointInfo(), new BeatmapDifficulty());
|
|
var drawableSwell = new DrawableSwell(swell);
|
|
hitObjectContainer.Add(drawableSwell);
|
|
});
|
|
|
|
// You might think that this should be a SwellTick since we're before the swell, but SwellTicks get no StartTime (ie. they are zero).
|
|
// This works fine in gameplay because they are judged whenever the user pressed, rather than being timed hits.
|
|
// But for sample playback purposes they can be ignored as noise.
|
|
AddAssert("most valid object is swell", () => triggerSource.GetMostValidObject(), Is.InstanceOf<Swell>);
|
|
checkSamples(HitType.Centre, false, HitSampleInfo.HIT_NORMAL, SampleControlPoint.DEFAULT_BANK);
|
|
checkSamples(HitType.Rim, false, HitSampleInfo.HIT_CLAP, SampleControlPoint.DEFAULT_BANK);
|
|
|
|
seekTo(600);
|
|
AddAssert("most valid object is swell", () => triggerSource.GetMostValidObject(), Is.InstanceOf<Swell>);
|
|
checkSamples(HitType.Centre, false, HitSampleInfo.HIT_NORMAL, SampleControlPoint.DEFAULT_BANK);
|
|
checkSamples(HitType.Rim, false, HitSampleInfo.HIT_CLAP, SampleControlPoint.DEFAULT_BANK);
|
|
|
|
seekTo(1200);
|
|
AddAssert("most valid object is swell", () => triggerSource.GetMostValidObject(), Is.InstanceOf<Swell>);
|
|
checkSamples(HitType.Centre, false, HitSampleInfo.HIT_NORMAL, SampleControlPoint.DEFAULT_BANK);
|
|
checkSamples(HitType.Rim, false, HitSampleInfo.HIT_CLAP, SampleControlPoint.DEFAULT_BANK);
|
|
}
|
|
|
|
[Test]
|
|
public void TestDrumSwell()
|
|
{
|
|
AddStep("add swell with drum samples", () =>
|
|
{
|
|
var swell = new Swell
|
|
{
|
|
StartTime = 100,
|
|
EndTime = 1100,
|
|
Samples = new List<HitSampleInfo>
|
|
{
|
|
new HitSampleInfo(HitSampleInfo.HIT_NORMAL, HitSampleInfo.BANK_DRUM)
|
|
}
|
|
};
|
|
swell.ApplyDefaults(new ControlPointInfo(), new BeatmapDifficulty());
|
|
var drawableSwell = new DrawableSwell(swell);
|
|
hitObjectContainer.Add(drawableSwell);
|
|
});
|
|
|
|
// You might think that this should be a SwellTick since we're before the swell, but SwellTicks get no StartTime (ie. they are zero).
|
|
// This works fine in gameplay because they are judged whenever the user pressed, rather than being timed hits.
|
|
// But for sample playback purposes they can be ignored as noise.
|
|
AddAssert("most valid object is swell", () => triggerSource.GetMostValidObject(), Is.InstanceOf<Swell>);
|
|
checkSamples(HitType.Centre, false, HitSampleInfo.HIT_NORMAL, HitSampleInfo.BANK_DRUM);
|
|
checkSamples(HitType.Rim, false, HitSampleInfo.HIT_CLAP, HitSampleInfo.BANK_DRUM);
|
|
|
|
seekTo(600);
|
|
AddAssert("most valid object is swell", () => triggerSource.GetMostValidObject(), Is.InstanceOf<Swell>);
|
|
checkSamples(HitType.Centre, false, HitSampleInfo.HIT_NORMAL, HitSampleInfo.BANK_DRUM);
|
|
checkSamples(HitType.Rim, false, HitSampleInfo.HIT_CLAP, HitSampleInfo.BANK_DRUM);
|
|
|
|
seekTo(1200);
|
|
AddAssert("most valid object is swell", () => triggerSource.GetMostValidObject(), Is.InstanceOf<Swell>);
|
|
checkSamples(HitType.Centre, false, HitSampleInfo.HIT_NORMAL, HitSampleInfo.BANK_DRUM);
|
|
checkSamples(HitType.Rim, false, HitSampleInfo.HIT_CLAP, HitSampleInfo.BANK_DRUM);
|
|
}
|
|
|
|
private void checkSamples(HitType hitType, bool strong, string expectedSamplesCsv, string expectedBank)
|
|
{
|
|
AddStep($"hit {hitType}", () => triggerSource.Play(hitType, strong));
|
|
AddAssert($"last played sample is {expectedSamplesCsv}", () => string.Join(',', triggerSource.LastPlayedSamples!.OfType<HitSampleInfo>().Select(s => s.Name)),
|
|
() => Is.EqualTo(expectedSamplesCsv));
|
|
AddAssert($"last played sample has {expectedBank} bank", () => triggerSource.LastPlayedSamples!.OfType<HitSampleInfo>().First().Bank, () => Is.EqualTo(expectedBank));
|
|
}
|
|
|
|
private void seekTo(double time) => AddStep($"seek to {time}", () => gameplayClock.Seek(time));
|
|
|
|
private partial class TestDrumSampleTriggerSource : DrumSampleTriggerSource
|
|
{
|
|
public ISampleInfo[]? LastPlayedSamples { get; private set; }
|
|
|
|
public TestDrumSampleTriggerSource(HitObjectContainer hitObjectContainer)
|
|
: base(hitObjectContainer)
|
|
{
|
|
}
|
|
|
|
protected override void PlaySamples(ISampleInfo[] samples)
|
|
{
|
|
base.PlaySamples(samples);
|
|
LastPlayedSamples = samples;
|
|
}
|
|
|
|
public new HitObject? GetMostValidObject() => base.GetMostValidObject();
|
|
}
|
|
|
|
private class TestManualClock : ManualClock, IAdjustableClock
|
|
{
|
|
public TestManualClock()
|
|
{
|
|
IsRunning = true;
|
|
}
|
|
|
|
public void Start() => IsRunning = true;
|
|
|
|
public void Stop() => IsRunning = false;
|
|
|
|
public bool Seek(double position)
|
|
{
|
|
CurrentTime = position;
|
|
return true;
|
|
}
|
|
|
|
public void Reset()
|
|
{
|
|
}
|
|
|
|
public void ResetSpeedAdjustments()
|
|
{
|
|
}
|
|
}
|
|
}
|
|
}
|