A game framework written with osu! in mind.
1// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
2// See the LICENCE file in the repository root for full licence text.
3
4using osu.Framework.Bindables;
5
6namespace osu.Framework.Audio
7{
8 /// <summary>
9 /// An audio component which allows for basic bindable adjustments to be applied.
10 /// </summary>
11 public class AdjustableAudioComponent : AudioComponent, IAdjustableAudioComponent
12 {
13 private readonly AudioAdjustments adjustments = new AudioAdjustments();
14
15 /// <summary>
16 /// The volume of this component.
17 /// </summary>
18 public BindableNumber<double> Volume => adjustments.Volume;
19
20 /// <summary>
21 /// The playback balance of this sample (-1 .. 1 where 0 is centered)
22 /// </summary>
23 public BindableNumber<double> Balance => adjustments.Balance;
24
25 /// <summary>
26 /// Rate at which the component is played back (affects pitch). 1 is 100% playback speed, or default frequency.
27 /// </summary>
28 public BindableNumber<double> Frequency => adjustments.Frequency;
29
30 /// <summary>
31 /// Rate at which the component is played back (does not affect pitch). 1 is 100% playback speed.
32 /// </summary>
33 public BindableNumber<double> Tempo => adjustments.Tempo;
34
35 protected AdjustableAudioComponent()
36 {
37 AggregateVolume.ValueChanged += InvalidateState;
38 AggregateBalance.ValueChanged += InvalidateState;
39 AggregateFrequency.ValueChanged += InvalidateState;
40 AggregateTempo.ValueChanged += InvalidateState;
41 }
42
43 public void AddAdjustment(AdjustableProperty type, IBindable<double> adjustBindable) =>
44 adjustments.AddAdjustment(type, adjustBindable);
45
46 public void RemoveAdjustment(AdjustableProperty type, IBindable<double> adjustBindable) =>
47 adjustments.RemoveAdjustment(type, adjustBindable);
48
49 public void RemoveAllAdjustments(AdjustableProperty type) => adjustments.RemoveAllAdjustments(type);
50
51 private bool invalidationPending;
52
53 internal void InvalidateState(ValueChangedEvent<double> valueChangedEvent = null)
54 {
55 if (CanPerformInline)
56 OnStateChanged();
57 else
58 invalidationPending = true;
59 }
60
61 internal virtual void OnStateChanged()
62 {
63 }
64
65 protected override void UpdateState()
66 {
67 base.UpdateState();
68
69 if (invalidationPending)
70 {
71 invalidationPending = false;
72 OnStateChanged();
73 }
74 }
75
76 public void BindAdjustments(IAggregateAudioAdjustment component) => adjustments.BindAdjustments(component);
77
78 public void UnbindAdjustments(IAggregateAudioAdjustment component) => adjustments.UnbindAdjustments(component);
79
80 public IBindable<double> AggregateVolume => adjustments.AggregateVolume;
81
82 public IBindable<double> AggregateBalance => adjustments.AggregateBalance;
83
84 public IBindable<double> AggregateFrequency => adjustments.AggregateFrequency;
85
86 public IBindable<double> AggregateTempo => adjustments.AggregateTempo;
87
88 protected override void Dispose(bool disposing)
89 {
90 base.Dispose(disposing);
91
92 AggregateVolume.UnbindAll();
93 AggregateBalance.UnbindAll();
94 AggregateFrequency.UnbindAll();
95 AggregateTempo.UnbindAll();
96 }
97 }
98
99 public enum AdjustableProperty
100 {
101 Volume,
102 Balance,
103 Frequency,
104 Tempo
105 }
106}