Merge pull request #10373 from peppy/fix-hitobject-samples-cut-off

Don't stop non-looping samples immediately when pausing
This commit is contained in:
Dan Balasescu
2020-10-05 17:13:11 +09:00
committed by GitHub
3 changed files with 29 additions and 19 deletions

View File

@ -19,12 +19,14 @@ namespace osu.Game.Tests.Visual.Editing
public void TestSlidingSampleStopsOnSeek() public void TestSlidingSampleStopsOnSeek()
{ {
DrawableSlider slider = null; DrawableSlider slider = null;
DrawableSample[] samples = null; DrawableSample[] loopingSamples = null;
DrawableSample[] onceOffSamples = null;
AddStep("get first slider", () => AddStep("get first slider", () =>
{ {
slider = Editor.ChildrenOfType<DrawableSlider>().OrderBy(s => s.HitObject.StartTime).First(); slider = Editor.ChildrenOfType<DrawableSlider>().OrderBy(s => s.HitObject.StartTime).First();
samples = slider.ChildrenOfType<DrawableSample>().ToArray(); onceOffSamples = slider.ChildrenOfType<DrawableSample>().Where(s => !s.Looping).ToArray();
loopingSamples = slider.ChildrenOfType<DrawableSample>().Where(s => s.Looping).ToArray();
}); });
AddStep("start playback", () => EditorClock.Start()); AddStep("start playback", () => EditorClock.Start());
@ -34,14 +36,15 @@ namespace osu.Game.Tests.Visual.Editing
if (!slider.Tracking.Value) if (!slider.Tracking.Value)
return false; return false;
if (!samples.Any(s => s.Playing)) if (!loopingSamples.Any(s => s.Playing))
return false; return false;
EditorClock.Seek(20000); EditorClock.Seek(20000);
return true; return true;
}); });
AddAssert("slider samples are not playing", () => samples.Length == 5 && samples.All(s => s.Played && !s.Playing)); AddAssert("non-looping samples are playing", () => onceOffSamples.Length == 4 && loopingSamples.All(s => s.Played || s.Playing));
AddAssert("looping samples are not playing", () => loopingSamples.Length == 1 && loopingSamples.All(s => s.Played && !s.Playing));
} }
} }
} }

View File

@ -385,9 +385,14 @@ namespace osu.Game.Rulesets.Objects.Drawables
} }
/// <summary> /// <summary>
/// Stops playback of all samples. Automatically called when <see cref="DrawableHitObject{TObject}"/>'s lifetime has been exceeded. /// Stops playback of all relevant samples. Generally only looping samples should be stopped by this, and the rest let to play out.
/// Automatically called when <see cref="DrawableHitObject{TObject}"/>'s lifetime has been exceeded.
/// </summary> /// </summary>
public virtual void StopAllSamples() => Samples?.Stop(); public virtual void StopAllSamples()
{
if (Samples?.Looping == true)
Samples.Stop();
}
protected override void Update() protected override void Update()
{ {
@ -457,6 +462,8 @@ namespace osu.Game.Rulesets.Objects.Drawables
foreach (var nested in NestedHitObjects) foreach (var nested in NestedHitObjects)
nested.OnKilled(); nested.OnKilled();
// failsafe to ensure looping samples don't get stuck in a playing state.
// this could occur in a non-frame-stable context where DrawableHitObjects get killed before a SkinnableSound has the chance to be stopped.
StopAllSamples(); StopAllSamples();
UpdateResult(false); UpdateResult(false);

View File

@ -34,21 +34,21 @@ namespace osu.Game.Skinning
samplePlaybackDisabled.BindTo(samplePlaybackDisabler.SamplePlaybackDisabled); samplePlaybackDisabled.BindTo(samplePlaybackDisabler.SamplePlaybackDisabled);
samplePlaybackDisabled.BindValueChanged(disabled => samplePlaybackDisabled.BindValueChanged(disabled =>
{ {
if (RequestedPlaying) if (!RequestedPlaying) return;
// let non-looping samples that have already been started play out to completion (sounds better than abruptly cutting off).
if (!Looping) return;
if (disabled.NewValue)
base.Stop();
else
{ {
if (disabled.NewValue) // schedule so we don't start playing a sample which is no longer alive.
base.Stop(); Schedule(() =>
// it's not easy to know if a sample has finished playing (to end).
// to keep things simple only resume playing looping samples.
else if (Looping)
{ {
// schedule so we don't start playing a sample which is no longer alive. if (RequestedPlaying)
Schedule(() => base.Play();
{ });
if (RequestedPlaying)
base.Play();
});
}
} }
}); });
} }