diff --git a/osu.Game/Storyboards/Drawables/DrawableStoryboardSample.cs b/osu.Game/Storyboards/Drawables/DrawableStoryboardSample.cs new file mode 100644 index 0000000000..cdec8c042f --- /dev/null +++ b/osu.Game/Storyboards/Drawables/DrawableStoryboardSample.cs @@ -0,0 +1,69 @@ +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System.IO; +using osu.Framework.Allocation; +using osu.Framework.Audio.Sample; +using osu.Framework.Graphics; +using osu.Game.Beatmaps; + +namespace osu.Game.Storyboards.Drawables +{ + public class DrawableStoryboardSample : Component + { + /// + /// The amount of time allowable beyond the start time of the sample, for the sample to start. + /// + private const double allowable_late_start = 100; + + private readonly StoryboardSample sample; + private SampleChannel channel; + + public override bool RemoveWhenNotAlive => false; + + public DrawableStoryboardSample(StoryboardSample sample) + { + this.sample = sample; + LifetimeStart = sample.Time; + } + + [BackgroundDependencyLoader] + private void load(IBindableBeatmap beatmap) + { + // Try first with the full name, then attempt with no path + channel = beatmap.Value.Skin.GetSample(sample.Path) ?? beatmap.Value.Skin.GetSample(Path.ChangeExtension(sample.Path, null)); + + if (channel != null) + channel.Volume.Value = sample.Volume / 100; + } + + protected override void Update() + { + base.Update(); + + // TODO: this logic will need to be consolidated with other game samples like hitsounds. + if (Time.Current < sample.Time) + { + // We've rewound before the start time of the sample + channel?.Stop(); + + // In the case that the user fast-forwards to a point far beyond the start time of the sample, + // we want to be able to fall into the if-conditional below (therefore we must not have a life time end) + LifetimeStart = sample.Time; + LifetimeEnd = double.MaxValue; + } + else if (Time.Current - Time.Elapsed < sample.Time) + { + // We've passed the start time of the sample. We only play the sample if we're within an allowable range + // from the sample's start, to reduce layering if we've been fast-forwarded far into the future + if (Time.Current - sample.Time < allowable_late_start) + channel?.Play(); + + // In the case that the user rewinds to a point far behind the start time of the sample, + // we want to be able to fall into the if-conditional above (therefore we must not have a life time start) + LifetimeStart = double.MinValue; + LifetimeEnd = sample.Time; + } + } + } +} diff --git a/osu.Game/Storyboards/StoryboardSample.cs b/osu.Game/Storyboards/StoryboardSample.cs index d0555493a6..c34a39a7bf 100644 --- a/osu.Game/Storyboards/StoryboardSample.cs +++ b/osu.Game/Storyboards/StoryboardSample.cs @@ -2,14 +2,14 @@ // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE using osu.Framework.Graphics; -using System; +using osu.Game.Storyboards.Drawables; namespace osu.Game.Storyboards { public class StoryboardSample : IStoryboardElement { public string Path { get; set; } - public bool IsDrawable => false; + public bool IsDrawable => true; public double Time; public float Volume; @@ -21,9 +21,6 @@ namespace osu.Game.Storyboards Volume = volume; } - public Drawable CreateDrawable() - { - throw new InvalidOperationException(); - } + public Drawable CreateDrawable() => new DrawableStoryboardSample(this); } }