1
0
mirror of https://github.com/ppy/osu.git synced 2025-01-13 07:22:54 +08:00

Add SkinnableSound class

Tidy things up, move logic out of SampleInfo.
This commit is contained in:
Dean Herbert 2018-02-23 20:34:08 +09:00
parent a312fb365a
commit 768e0a4e2a
8 changed files with 184 additions and 96 deletions

View File

@ -2,10 +2,9 @@
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System.Collections.Generic;
using osu.Framework.Audio;
using osu.Framework.Audio.Sample;
using osu.Game.Audio;
using osu.Game.Beatmaps.ControlPoints;
using osu.Game.Skinning;
namespace osu.Game.Rulesets.Taiko.Audio
{
@ -14,7 +13,9 @@ namespace osu.Game.Rulesets.Taiko.Audio
private readonly ControlPointInfo controlPoints;
private readonly Dictionary<double, DrumSample> mappings = new Dictionary<double, DrumSample>();
public DrumSampleMapping(ControlPointInfo controlPoints, AudioManager audio)
public readonly List<SkinnableSound> Drawables = new List<SkinnableSound>();
public DrumSampleMapping(ControlPointInfo controlPoints)
{
this.controlPoints = controlPoints;
@ -27,20 +28,34 @@ namespace osu.Game.Rulesets.Taiko.Audio
foreach (var s in samplePoints)
{
var centre = s.GetSampleInfo();
var rim = s.GetSampleInfo(SampleInfo.HIT_CLAP);
// todo: this is ugly
centre.Namespace = "taiko";
rim.Namespace = "taiko";
mappings[s.Time] = new DrumSample
{
Centre = s.GetSampleInfo().GetChannel(audio.Sample.Get, "Taiko"),
Rim = s.GetSampleInfo(SampleInfo.HIT_CLAP).GetChannel(audio.Sample.Get, "Taiko")
Centre = addDrawableSound(centre),
Rim = addDrawableSound(rim)
};
}
}
private SkinnableSound addDrawableSound(SampleInfo rim)
{
var drawable = new SkinnableSound(rim);
Drawables.Add(drawable);
return drawable;
}
public DrumSample SampleAt(double time) => mappings[controlPoints.SamplePointAt(time).Time];
public class DrumSample
{
public SampleChannel Centre;
public SampleChannel Rim;
public SkinnableSound Centre;
public SkinnableSound Rim;
}
}
}

View File

@ -4,7 +4,6 @@
using System;
using OpenTK;
using osu.Framework.Allocation;
using osu.Framework.Audio;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Sprites;
@ -34,9 +33,9 @@ namespace osu.Game.Rulesets.Taiko.UI
}
[BackgroundDependencyLoader]
private void load(AudioManager audio)
private void load()
{
var sampleMappings = new DrumSampleMapping(controlPoints, audio);
var sampleMappings = new DrumSampleMapping(controlPoints);
Children = new Drawable[]
{
@ -63,6 +62,8 @@ namespace osu.Game.Rulesets.Taiko.UI
CentreAction = TaikoAction.RightCentre
}
};
AddRangeInternal(sampleMappings.Drawables);
}
/// <summary>

View File

@ -2,7 +2,6 @@
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System;
using osu.Framework.Audio.Sample;
namespace osu.Game.Audio
{
@ -14,22 +13,10 @@ namespace osu.Game.Audio
public const string HIT_NORMAL = @"hitnormal";
public const string HIT_CLAP = @"hitclap";
public SampleChannel GetChannel(Func<string, SampleChannel> getChannel, string resourceNamespace = null)
{
SampleChannel channel = null;
if (resourceNamespace != null)
channel = getChannel($"Gameplay/{resourceNamespace}/{Bank}-{Name}");
// try without namespace as a fallback.
if (channel == null)
channel = getChannel($"Gameplay/{Bank}-{Name}");
if (channel != null)
channel.Volume.Value = Volume / 100.0;
return channel;
}
/// <summary>
/// An optional ruleset namespace.
/// </summary>
public string Namespace;
/// <summary>
/// The bank to load the sample from.

View File

@ -3,21 +3,19 @@
using System;
using System.Collections.Generic;
using osu.Framework.Allocation;
using osu.Framework.Audio;
using osu.Framework.Audio.Sample;
using osu.Game.Rulesets.Judgements;
using Container = osu.Framework.Graphics.Containers.Container;
using osu.Game.Rulesets.Objects.Types;
using OpenTK.Graphics;
using osu.Game.Audio;
using System.Linq;
using osu.Game.Graphics;
using osu.Framework.Allocation;
using osu.Framework.Configuration;
using OpenTK;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Primitives;
using osu.Game.Audio;
using osu.Game.Graphics;
using osu.Game.Rulesets.Judgements;
using osu.Game.Rulesets.Objects.Types;
using osu.Game.Rulesets.Scoring;
using osu.Game.Skinning;
using OpenTK;
using OpenTK.Graphics;
namespace osu.Game.Rulesets.Objects.Drawables
{
@ -33,8 +31,12 @@ namespace osu.Game.Rulesets.Objects.Drawables
// Todo: Rulesets should be overriding the resources instead, but we need to figure out where/when to apply overrides first
protected virtual string SampleNamespace => null;
protected List<SampleChannel> Samples = new List<SampleChannel>();
protected virtual IEnumerable<SampleInfo> GetSamples() => HitObject.Samples;
protected SkinnableSound Samples;
protected virtual IEnumerable<SampleInfo> GetSamples()
{
return HitObject.Samples;
}
private List<DrawableHitObject> nestedHitObjects;
public IReadOnlyList<DrawableHitObject> NestedHitObjects => nestedHitObjects;
@ -83,41 +85,23 @@ namespace osu.Game.Rulesets.Objects.Drawables
HitObject = hitObject;
}
private readonly Bindable<Skin> skin = new Bindable<Skin>();
[BackgroundDependencyLoader]
private void load(AudioManager audio, SkinManager skins)
private void load()
{
var samples = GetSamples();
var samples = GetSamples().ToArray();
if (samples.Any())
{
if (HitObject.SampleControlPoint == null)
throw new ArgumentNullException(nameof(HitObject.SampleControlPoint), $"{nameof(HitObject)}s must always have an attached {nameof(HitObject.SampleControlPoint)}."
+ $" This is an indication that {nameof(HitObject.ApplyDefaults)} has not been invoked on {this}.");
void loadSamples(Skin skin)
AddInternal(Samples = new SkinnableSound(samples.Select(s => new SampleInfo
{
Samples.Clear();
foreach (SampleInfo s in samples)
{
SampleInfo localSampleInfo = new SampleInfo
{
Bank = s.Bank ?? HitObject.SampleControlPoint.SampleBank,
Name = s.Name,
Volume = s.Volume > 0 ? s.Volume : HitObject.SampleControlPoint.SampleVolume
};
SampleChannel channel = localSampleInfo.GetChannel(skin.GetSample, SampleNamespace) ?? localSampleInfo.GetChannel(audio.Sample.Get, SampleNamespace);
if (channel == null) return;
Samples.Add(channel);
}
}
skin.ValueChanged += loadSamples;
skin.BindTo(skins.CurrentSkin);
Bank = s.Bank ?? HitObject.SampleControlPoint.SampleBank,
Name = s.Name,
Volume = s.Volume > 0 ? s.Volume : HitObject.SampleControlPoint.SampleVolume,
Namespace = SampleNamespace
}).ToArray()));
}
}
@ -149,7 +133,7 @@ namespace osu.Game.Rulesets.Objects.Drawables
/// <summary>
/// Plays all the hitsounds for this <see cref="DrawableHitObject"/>.
/// </summary>
public void PlaySamples() => Samples.ForEach(s => s?.Play());
public void PlaySamples() => Samples?.Play();
protected override void Update()
{
@ -231,10 +215,8 @@ namespace osu.Game.Rulesets.Objects.Drawables
return false;
if (NestedHitObjects != null)
{
foreach (var d in NestedHitObjects)
judgementOccurred |= d.UpdateJudgement(userTriggered);
}
if (!ProvidesJudgement || judgementFinalized || judgementOccurred)
return judgementOccurred;

View File

@ -0,0 +1,53 @@
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using osu.Framework.Allocation;
using osu.Framework.Configuration;
using osu.Framework.Graphics.Containers;
namespace osu.Game.Skinning
{
/// <summary>
/// A drawable which has a callback when the skin changes.
/// </summary>
public abstract class SkinReloadableDrawable : CompositeDrawable
{
private Bindable<Skin> skin;
/// <summary>
/// Whether fallback to default skin should be allowed if the custom skin is missing this resource.
/// </summary>
private readonly bool allowDefaultFallback;
/// <summary>
/// Create a new
/// </summary>
/// <param name="fallback">Whether fallback to default skin should be allowed if the custom skin is missing this resource.</param>
protected SkinReloadableDrawable(bool fallback = true)
{
allowDefaultFallback = fallback;
}
[BackgroundDependencyLoader]
private void load(SkinManager skinManager)
{
skin = skinManager.CurrentSkin.GetBoundCopy();
skin.ValueChanged += skin => SkinChanged(skin, allowDefaultFallback || skin.SkinInfo == SkinInfo.Default);
}
protected override void LoadAsyncComplete()
{
base.LoadAsyncComplete();
skin.TriggerChange();
}
/// <summary>
/// Called when a change is made to the skin.
/// </summary>
/// <param name="skin">The new skin.</param>
/// <param name="allowFallback">Whether fallback to default skin should be allowed if the custom skin is missing this resource.</param>
protected virtual void SkinChanged(Skin skin, bool allowFallback)
{
}
}
}

View File

@ -2,10 +2,7 @@
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System;
using osu.Framework.Allocation;
using osu.Framework.Configuration;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
namespace osu.Game.Skinning
{
@ -14,40 +11,29 @@ namespace osu.Game.Skinning
public SkinnableDrawable(string name, Func<string, Drawable> defaultImplementation, bool fallback = true)
: base(name, defaultImplementation, fallback)
{
RelativeSizeAxes = Axes.Both;
}
}
public class SkinnableDrawable<T> : CompositeDrawable
public class SkinnableDrawable<T> : SkinReloadableDrawable
where T : Drawable
{
private Bindable<Skin> skin;
protected Func<string, T> CreateDefault;
private readonly Func<string, T> createDefault;
public readonly string ComponentName;
private readonly string componentName;
public readonly bool DefaultFallback;
public SkinnableDrawable(string name, Func<string, T> defaultImplementation, bool fallback = true)
public SkinnableDrawable(string name, Func<string, T> defaultImplementation, bool fallback = true) : base(fallback)
{
DefaultFallback = fallback;
ComponentName = name;
CreateDefault = defaultImplementation;
componentName = name;
createDefault = defaultImplementation;
RelativeSizeAxes = Axes.Both;
}
[BackgroundDependencyLoader]
private void load(SkinManager skinManager)
protected override void SkinChanged(Skin skin, bool allowFallback)
{
skin = skinManager.CurrentSkin.GetBoundCopy();
skin.ValueChanged += updateComponent;
skin.TriggerChange();
}
private void updateComponent(Skin skin)
{
var drawable = skin.GetDrawableComponent(ComponentName);
if (drawable == null && (DefaultFallback || skin.SkinInfo == SkinInfo.Default))
drawable = CreateDefault(ComponentName);
var drawable = skin.GetDrawableComponent(componentName);
if (drawable == null && allowFallback)
drawable = createDefault(componentName);
if (drawable != null)
InternalChild = drawable;

View File

@ -0,0 +1,62 @@
// 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;
using System.Linq;
using osu.Framework.Allocation;
using osu.Framework.Audio;
using osu.Framework.Audio.Sample;
using osu.Framework.Extensions.IEnumerableExtensions;
using osu.Game.Audio;
namespace osu.Game.Skinning
{
public class SkinnableSound : SkinReloadableDrawable
{
private readonly SampleInfo[] samples;
private SampleChannel[] channels;
private AudioManager audio;
public SkinnableSound(params SampleInfo[] samples)
{
this.samples = samples;
}
[BackgroundDependencyLoader]
private void load(AudioManager audio)
{
this.audio = audio;
}
public void Play() => channels?.ForEach(c => c.Play());
protected override void SkinChanged(Skin skin, bool allowFallback)
{
channels = samples.Select(s =>
{
var ch = loadChannel(s, skin.GetSample);
if (ch == null && allowFallback)
ch = loadChannel(s, audio.Sample.Get);
return ch;
}).ToArray();
}
private SampleChannel loadChannel(SampleInfo info, Func<string, SampleChannel> getSampleFunction)
{
SampleChannel ch = null;
if (info.Namespace != null)
ch = getSampleFunction($"Gameplay/{info.Namespace}/{info.Bank}-{info.Name}");
// try without namespace as a fallback.
if (ch == null)
ch = getSampleFunction($"Gameplay/{info.Bank}-{info.Name}");
if (ch != null)
ch.Volume.Value = info.Volume / 100.0;
return ch;
}
}
}

View File

@ -1,4 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003" ToolsVersion="14.0">
<Import Project="..\osu.Game.props" />
<PropertyGroup>
@ -861,6 +861,8 @@
<Compile Include="Skinning\SkinInfo.cs" />
<Compile Include="Skinning\SkinManager.cs" />
<Compile Include="Skinning\SkinnableDrawable.cs" />
<Compile Include="Skinning\SkinnableSound.cs" />
<Compile Include="Skinning\SkinReloadableDrawable.cs" />
<Compile Include="Skinning\SkinStore.cs" />
<Compile Include="Storyboards\CommandLoop.cs" />
<Compile Include="Storyboards\CommandTimeline.cs" />