From f87ba1508987535d8e67678981624beaad3deb21 Mon Sep 17 00:00:00 2001 From: Leo Bottaro Date: Fri, 5 Jul 2024 06:06:47 -0300 Subject: [PATCH 1/5] Add mixer Volume control of all channel it owns --- osu.Framework/Audio/AudioManager.cs | 9 +++++---- osu.Framework/Audio/Mixing/AudioMixer.cs | 4 ++++ osu.Framework/Audio/Sample/SampleChannel.cs | 13 ++++++++++++- osu.Framework/Audio/Sample/SampleChannelBass.cs | 2 +- osu.Framework/Audio/Track/TrackBass.cs | 9 +++++++-- 5 files changed, 29 insertions(+), 8 deletions(-) diff --git a/osu.Framework/Audio/AudioManager.cs b/osu.Framework/Audio/AudioManager.cs index f6c366271d..8e934cf255 100644 --- a/osu.Framework/Audio/AudioManager.cs +++ b/osu.Framework/Audio/AudioManager.cs @@ -176,14 +176,16 @@ public AudioManager(AudioThread audioThread, ResourceStore trackStore, R usingGlobalMixer.Value = handle.NewValue.HasValue; }; - AddItem(TrackMixer = createAudioMixer(null, nameof(TrackMixer))); - AddItem(SampleMixer = createAudioMixer(null, nameof(SampleMixer))); + TrackMixer = createAudioMixer(null, nameof(TrackMixer)); + SampleMixer = createAudioMixer(null, nameof(SampleMixer)); + + TrackMixer.Volume.BindTo(VolumeTrack); + SampleMixer.Volume.BindTo(VolumeSample); globalTrackStore = new Lazy(() => { var store = new TrackStore(trackStore, TrackMixer); AddItem(store); - store.AddAdjustment(AdjustableProperty.Volume, VolumeTrack); return store; }); @@ -191,7 +193,6 @@ public AudioManager(AudioThread audioThread, ResourceStore trackStore, R { var store = new SampleStore(sampleStore, SampleMixer); AddItem(store); - store.AddAdjustment(AdjustableProperty.Volume, VolumeSample); return store; }); diff --git a/osu.Framework/Audio/Mixing/AudioMixer.cs b/osu.Framework/Audio/Mixing/AudioMixer.cs index 4cb612e0d0..81a622dedc 100644 --- a/osu.Framework/Audio/Mixing/AudioMixer.cs +++ b/osu.Framework/Audio/Mixing/AudioMixer.cs @@ -2,6 +2,8 @@ // See the LICENCE file in the repository root for full licence text. using ManagedBass; + +using osu.Framework.Bindables; using osu.Framework.Extensions.ObjectExtensions; namespace osu.Framework.Audio.Mixing @@ -13,6 +15,8 @@ public abstract class AudioMixer : AudioComponent, IAudioMixer { public readonly string Identifier; + public readonly Bindable Volume = new BindableDouble(1); + private readonly AudioMixer? fallbackMixer; /// diff --git a/osu.Framework/Audio/Sample/SampleChannel.cs b/osu.Framework/Audio/Sample/SampleChannel.cs index 4659004b37..5eb208ffba 100644 --- a/osu.Framework/Audio/Sample/SampleChannel.cs +++ b/osu.Framework/Audio/Sample/SampleChannel.cs @@ -55,9 +55,20 @@ protected override void UpdateState() AudioMixer? IAudioChannel.Mixer { get => Mixer; - set => Mixer = value; + set + { + if (Mixer != null) + Mixer.Volume.ValueChanged -= mixerVolumeChanged; + + Mixer = value; + + if (Mixer != null) + Mixer.Volume.ValueChanged += mixerVolumeChanged; + } } + private void mixerVolumeChanged(Bindables.ValueChangedEvent obj) => OnStateChanged(); + Task IAudioChannel.EnqueueAction(Action action) => EnqueueAction(action); #endregion diff --git a/osu.Framework/Audio/Sample/SampleChannelBass.cs b/osu.Framework/Audio/Sample/SampleChannelBass.cs index ee9bc84c7b..46d6274cb1 100644 --- a/osu.Framework/Audio/Sample/SampleChannelBass.cs +++ b/osu.Framework/Audio/Sample/SampleChannelBass.cs @@ -143,7 +143,7 @@ internal override void OnStateChanged() if (!hasChannel) return; - Bass.ChannelSetAttribute(channel, ChannelAttribute.Volume, AggregateVolume.Value); + Bass.ChannelSetAttribute(channel, ChannelAttribute.Volume, AggregateVolume.Value * bassMixer.Volume.Value); Bass.ChannelSetAttribute(channel, ChannelAttribute.Pan, AggregateBalance.Value); relativeFrequencyHandler.SetFrequency(AggregateFrequency.Value); } diff --git a/osu.Framework/Audio/Track/TrackBass.cs b/osu.Framework/Audio/Track/TrackBass.cs index 55088f81d8..a71e2b49ce 100644 --- a/osu.Framework/Audio/Track/TrackBass.cs +++ b/osu.Framework/Audio/Track/TrackBass.cs @@ -355,8 +355,7 @@ internal override void OnStateChanged() return; setDirection(AggregateFrequency.Value < 0); - - Bass.ChannelSetAttribute(activeStream, ChannelAttribute.Volume, AggregateVolume.Value); + Bass.ChannelSetAttribute(activeStream, ChannelAttribute.Volume, AggregateVolume.Value * bassMixer.Volume.Value); Bass.ChannelSetAttribute(activeStream, ChannelAttribute.Pan, AggregateBalance.Value); relativeFrequencyHandler.SetFrequency(AggregateFrequency.Value); @@ -423,7 +422,10 @@ protected override AudioMixer? Mixer { // While BASS cleans up syncs automatically on mixer change, ManagedBass internally tracks the sync procedures via ChannelReferences, so clean up eagerly for safety. if (Mixer != null) + { + Mixer.Volume.ValueChanged -= mixerVolumeChanged; cleanUpSyncs(); + } base.Mixer = value; @@ -432,6 +434,7 @@ protected override AudioMixer? Mixer { // Tracks are always active until they're disposed, so they need to be added to the mix prematurely for operations like Seek() to work immediately. bassMixer.AddChannelToBassMix(this); + Mixer.Volume.ValueChanged += mixerVolumeChanged; // Syncs are not automatically moved on mixer change, so restore them on the new mixer manually. initializeSyncs(); @@ -439,6 +442,8 @@ protected override AudioMixer? Mixer } } + private void mixerVolumeChanged(Bindables.ValueChangedEvent obj) => OnStateChanged(); + private BassAudioMixer bassMixer => (BassAudioMixer)Mixer.AsNonNull(); bool IBassAudioChannel.IsActive => !IsDisposed; From 11d5a51f005f5f3b6c9eed1653f97026059b8769 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 5 Jul 2024 19:16:57 +0900 Subject: [PATCH 2/5] Apply volume changes in a much nicer way --- osu.Framework/Audio/AudioManager.cs | 6 +++--- osu.Framework/Audio/Mixing/AudioMixer.cs | 12 +++++++----- osu.Framework/Audio/Sample/SampleChannel.cs | 13 +------------ osu.Framework/Audio/Sample/SampleChannelBass.cs | 2 +- osu.Framework/Audio/Track/TrackBass.cs | 9 ++------- 5 files changed, 14 insertions(+), 28 deletions(-) diff --git a/osu.Framework/Audio/AudioManager.cs b/osu.Framework/Audio/AudioManager.cs index 8e934cf255..308fc3f681 100644 --- a/osu.Framework/Audio/AudioManager.cs +++ b/osu.Framework/Audio/AudioManager.cs @@ -176,10 +176,10 @@ public AudioManager(AudioThread audioThread, ResourceStore trackStore, R usingGlobalMixer.Value = handle.NewValue.HasValue; }; - TrackMixer = createAudioMixer(null, nameof(TrackMixer)); - SampleMixer = createAudioMixer(null, nameof(SampleMixer)); - + AddItem(TrackMixer = createAudioMixer(null, nameof(TrackMixer))); TrackMixer.Volume.BindTo(VolumeTrack); + + AddItem(SampleMixer = createAudioMixer(null, nameof(SampleMixer))); SampleMixer.Volume.BindTo(VolumeSample); globalTrackStore = new Lazy(() => diff --git a/osu.Framework/Audio/Mixing/AudioMixer.cs b/osu.Framework/Audio/Mixing/AudioMixer.cs index 81a622dedc..9ab652b2cc 100644 --- a/osu.Framework/Audio/Mixing/AudioMixer.cs +++ b/osu.Framework/Audio/Mixing/AudioMixer.cs @@ -2,8 +2,6 @@ // See the LICENCE file in the repository root for full licence text. using ManagedBass; - -using osu.Framework.Bindables; using osu.Framework.Extensions.ObjectExtensions; namespace osu.Framework.Audio.Mixing @@ -11,12 +9,10 @@ namespace osu.Framework.Audio.Mixing /// /// Mixes together multiple s into one output. /// - public abstract class AudioMixer : AudioComponent, IAudioMixer + public abstract class AudioMixer : AdjustableAudioComponent, IAudioMixer { public readonly string Identifier; - public readonly Bindable Volume = new BindableDouble(1); - private readonly AudioMixer? fallbackMixer; /// @@ -33,6 +29,9 @@ protected AudioMixer(AudioMixer? fallbackMixer, string identifier) public void Add(IAudioChannel channel) { + if (channel is IAdjustableAudioComponent adj) + adj.BindAdjustments(this); + channel.EnqueueAction(() => { if (channel.Mixer == this) @@ -74,6 +73,9 @@ protected void Remove(IAudioChannel channel, bool returnToDefault) RemoveInternal(channel); channel.Mixer = null; + if (channel is IAdjustableAudioComponent adj) + adj.UnbindAdjustments(this); + // Add the channel back to the default mixer so audio can always be played. if (returnToDefault) fallbackMixer.AsNonNull().Add(channel); diff --git a/osu.Framework/Audio/Sample/SampleChannel.cs b/osu.Framework/Audio/Sample/SampleChannel.cs index 5eb208ffba..4659004b37 100644 --- a/osu.Framework/Audio/Sample/SampleChannel.cs +++ b/osu.Framework/Audio/Sample/SampleChannel.cs @@ -55,20 +55,9 @@ protected override void UpdateState() AudioMixer? IAudioChannel.Mixer { get => Mixer; - set - { - if (Mixer != null) - Mixer.Volume.ValueChanged -= mixerVolumeChanged; - - Mixer = value; - - if (Mixer != null) - Mixer.Volume.ValueChanged += mixerVolumeChanged; - } + set => Mixer = value; } - private void mixerVolumeChanged(Bindables.ValueChangedEvent obj) => OnStateChanged(); - Task IAudioChannel.EnqueueAction(Action action) => EnqueueAction(action); #endregion diff --git a/osu.Framework/Audio/Sample/SampleChannelBass.cs b/osu.Framework/Audio/Sample/SampleChannelBass.cs index 46d6274cb1..ee9bc84c7b 100644 --- a/osu.Framework/Audio/Sample/SampleChannelBass.cs +++ b/osu.Framework/Audio/Sample/SampleChannelBass.cs @@ -143,7 +143,7 @@ internal override void OnStateChanged() if (!hasChannel) return; - Bass.ChannelSetAttribute(channel, ChannelAttribute.Volume, AggregateVolume.Value * bassMixer.Volume.Value); + Bass.ChannelSetAttribute(channel, ChannelAttribute.Volume, AggregateVolume.Value); Bass.ChannelSetAttribute(channel, ChannelAttribute.Pan, AggregateBalance.Value); relativeFrequencyHandler.SetFrequency(AggregateFrequency.Value); } diff --git a/osu.Framework/Audio/Track/TrackBass.cs b/osu.Framework/Audio/Track/TrackBass.cs index a71e2b49ce..55088f81d8 100644 --- a/osu.Framework/Audio/Track/TrackBass.cs +++ b/osu.Framework/Audio/Track/TrackBass.cs @@ -355,7 +355,8 @@ internal override void OnStateChanged() return; setDirection(AggregateFrequency.Value < 0); - Bass.ChannelSetAttribute(activeStream, ChannelAttribute.Volume, AggregateVolume.Value * bassMixer.Volume.Value); + + Bass.ChannelSetAttribute(activeStream, ChannelAttribute.Volume, AggregateVolume.Value); Bass.ChannelSetAttribute(activeStream, ChannelAttribute.Pan, AggregateBalance.Value); relativeFrequencyHandler.SetFrequency(AggregateFrequency.Value); @@ -422,10 +423,7 @@ protected override AudioMixer? Mixer { // While BASS cleans up syncs automatically on mixer change, ManagedBass internally tracks the sync procedures via ChannelReferences, so clean up eagerly for safety. if (Mixer != null) - { - Mixer.Volume.ValueChanged -= mixerVolumeChanged; cleanUpSyncs(); - } base.Mixer = value; @@ -434,7 +432,6 @@ protected override AudioMixer? Mixer { // Tracks are always active until they're disposed, so they need to be added to the mix prematurely for operations like Seek() to work immediately. bassMixer.AddChannelToBassMix(this); - Mixer.Volume.ValueChanged += mixerVolumeChanged; // Syncs are not automatically moved on mixer change, so restore them on the new mixer manually. initializeSyncs(); @@ -442,8 +439,6 @@ protected override AudioMixer? Mixer } } - private void mixerVolumeChanged(Bindables.ValueChangedEvent obj) => OnStateChanged(); - private BassAudioMixer bassMixer => (BassAudioMixer)Mixer.AsNonNull(); bool IBassAudioChannel.IsActive => !IsDisposed; From 67afbb50e72deb5975f7c6951eab070f469be1e1 Mon Sep 17 00:00:00 2001 From: Leo Bottaro Date: Sun, 7 Jul 2024 03:35:47 -0300 Subject: [PATCH 3/5] Fix AudioMixer being added to active mixers collection before a handle was assigned --- osu.Framework/Audio/AudioManager.cs | 6 ++---- osu.Framework/Audio/Mixing/Bass/BassAudioMixer.cs | 2 ++ 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Framework/Audio/AudioManager.cs b/osu.Framework/Audio/AudioManager.cs index 308fc3f681..89e27b8627 100644 --- a/osu.Framework/Audio/AudioManager.cs +++ b/osu.Framework/Audio/AudioManager.cs @@ -271,11 +271,9 @@ private AudioMixer createAudioMixer(AudioMixer fallbackMixer, string identifier) return mixer; } - protected override void ItemAdded(AudioComponent item) + internal void AddActiveMixer(AudioMixer mixer) { - base.ItemAdded(item); - if (item is AudioMixer mixer) - activeMixers.Add(mixer); + activeMixers.Add(mixer); } protected override void ItemRemoved(AudioComponent item) diff --git a/osu.Framework/Audio/Mixing/Bass/BassAudioMixer.cs b/osu.Framework/Audio/Mixing/Bass/BassAudioMixer.cs index a564a6b20a..74968dc73c 100644 --- a/osu.Framework/Audio/Mixing/Bass/BassAudioMixer.cs +++ b/osu.Framework/Audio/Mixing/Bass/BassAudioMixer.cs @@ -322,6 +322,8 @@ private void createMixer() BassMix.MixerAddChannel(manager.GlobalMixerHandle.Value.Value, Handle, BassFlags.MixerChanBuffer | BassFlags.MixerChanNoRampin); ManagedBass.Bass.ChannelPlay(Handle); + + manager?.AddActiveMixer(this); } /// From 6d09be0a751de56fea961c45bcefb73d73db6c14 Mon Sep 17 00:00:00 2001 From: Leo Bottaro Date: Sun, 7 Jul 2024 03:36:31 -0300 Subject: [PATCH 4/5] Improre Mixer Visualiser responsiveness and add Peak bar --- .../Audio/AudioChannelDisplay.cs | 114 +++++++++++++----- .../Audio/AudioMixerVisualiser.cs | 2 +- .../Visualisation/Audio/MixerDisplay.cs | 5 +- 3 files changed, 85 insertions(+), 36 deletions(-) diff --git a/osu.Framework/Graphics/Visualisation/Audio/AudioChannelDisplay.cs b/osu.Framework/Graphics/Visualisation/Audio/AudioChannelDisplay.cs index f2e7a75c0f..ca956ae4ca 100644 --- a/osu.Framework/Graphics/Visualisation/Audio/AudioChannelDisplay.cs +++ b/osu.Framework/Graphics/Visualisation/Audio/AudioChannelDisplay.cs @@ -17,17 +17,21 @@ namespace osu.Framework.Graphics.Visualisation.Audio public partial class AudioChannelDisplay : CompositeDrawable { private const int sample_window = 30; - private const int peak_hold_time = 3000; + private const int peak_hold_time = 1000; public readonly int ChannelHandle; private readonly Drawable volBarL; private readonly Drawable volBarR; + private readonly Drawable peakBarL; + private readonly Drawable peakBarR; private readonly SpriteText peakText; private readonly SpriteText maxPeakText; private readonly SpriteText mixerLabel; - private float maxPeak = float.MinValue; + private float peakLevelLR = float.MinValue; + private float peakLevelL = float.MinValue; + private float peakLevelR = float.MinValue; private double lastMaxPeakTime; private readonly bool isOutputChannel; private IBindable usingGlobalMixer = null!; @@ -55,35 +59,72 @@ public AudioChannelDisplay(int channelHandle, bool isOutputChannel = false) }, Content = new[] { - new Drawable[] - { + [ new FillFlowContainer { RelativeSizeAxes = Axes.Y, AutoSizeAxes = Axes.X, Direction = FillDirection.Horizontal, - Spacing = new Vector2(5), - Children = new[] - { - volBarL = new Box + Spacing = new Vector2(3), + Children = + [ + new Container { - Anchor = Anchor.BottomLeft, - Origin = Anchor.BottomLeft, RelativeSizeAxes = Axes.Y, - Width = 30, - Colour = isOutputChannel ? FrameworkColour.YellowGreen : FrameworkColour.Green + AutoSizeAxes = Axes.X, + Height = 1, + Children = + [ + volBarL = new Box + { + Anchor = Anchor.BottomLeft, + Origin = Anchor.BottomLeft, + RelativeSizeAxes = Axes.Y, + Width = 31, + Height = 0, + Colour = isOutputChannel ? FrameworkColour.YellowGreen : FrameworkColour.Green, + }, + peakBarL = new Box + { + Anchor = Anchor.BottomLeft, + Origin = Anchor.TopLeft, + RelativePositionAxes = Axes.Y, + Width = 31, + Height = 5f, + Colour = isOutputChannel ? FrameworkColour.YellowGreen : FrameworkColour.Green, + }, + ] }, - volBarR = new Box + new Container { - Anchor = Anchor.BottomLeft, - Origin = Anchor.BottomLeft, RelativeSizeAxes = Axes.Y, - Width = 30, - Colour = isOutputChannel ? FrameworkColour.YellowGreen : FrameworkColour.Green + AutoSizeAxes = Axes.X, + Height = 1, + Children = + [ + volBarR = new Box + { + Anchor = Anchor.BottomLeft, + Origin = Anchor.BottomLeft, + RelativeSizeAxes = Axes.Y, + Width = 31, + Height = 0, + Colour = isOutputChannel ? FrameworkColour.YellowGreen : FrameworkColour.Green, + }, + peakBarR = new Box + { + Anchor = Anchor.BottomLeft, + Origin = Anchor.TopLeft, + RelativePositionAxes = Axes.Y, + Width = 31, + Height = 5f, + Colour = isOutputChannel ? FrameworkColour.YellowGreen : FrameworkColour.Green, + }, + ] } - } + ] } - }, + ], new Drawable[] { new FillFlowContainer @@ -120,27 +161,34 @@ protected override void Update() else BassMix.ChannelGetLevel(ChannelHandle, levels, 1 / 1000f * sample_window, LevelRetrievalFlags.Stereo); - float curPeakL = levels[0]; - float curPeakR = levels[1]; - float curPeak = (curPeakL + curPeakR) / 2f; + float curLevelL = levels[0]; + float curLevelR = levels[1]; + float curLevelLR = (curLevelL + curLevelR) / 2f; - if (curPeak > maxPeak || Clock.CurrentTime - lastMaxPeakTime > peak_hold_time) + if (Clock.CurrentTime - lastMaxPeakTime > peak_hold_time) { lastMaxPeakTime = Clock.CurrentTime; - maxPeak = float.MinValue; + peakLevelL = 0; + peakLevelR = 0; + peakLevelLR = 0; } - maxPeak = Math.Max(maxPeak, curPeak); + peakLevelL = Math.Max(peakLevelL, curLevelL); + peakLevelR = Math.Max(peakLevelR, curLevelR); + peakLevelLR = Math.Max(peakLevelL, peakLevelR); + + peakBarL.MoveToY(-peakLevelL, peakBarL.Y >= -peakLevelL ? 0 : sample_window * 4); + peakBarR.MoveToY(-peakLevelR, peakBarR.Y >= -peakLevelR ? 0 : sample_window * 4); - volBarL.ResizeHeightTo(curPeakL, sample_window * 4); - volBarR.ResizeHeightTo(curPeakR, sample_window * 4); + volBarL.ResizeHeightTo(curLevelL, volBarL.Height <= curLevelL ? 0 : sample_window * 4); + volBarR.ResizeHeightTo(curLevelR, volBarR.Height <= curLevelR ? 0 : sample_window * 4); - string peakDisplay = curPeak == 0 ? "-∞ " : $"{BassUtils.LevelToDb(curPeak):F}"; - string maxPeakDisplay = maxPeak == 0 ? "-∞ " : $"{BassUtils.LevelToDb(maxPeak):F}"; - peakText.Text = $"curr: {peakDisplay}dB"; - maxPeakText.Text = $"peak: {maxPeakDisplay}dB"; - peakText.Colour = BassUtils.LevelToDb(curPeak) > 0 ? Colour4.Red : Colour4.White; - maxPeakText.Colour = BassUtils.LevelToDb(maxPeak) > 0 ? Colour4.Red : Colour4.White; + string curDisplay = curLevelLR == 0 ? "-∞ " : $"{BassUtils.LevelToDb(curLevelLR):F}"; + string peakDisplay = peakLevelLR == 0 ? "-∞ " : $"{BassUtils.LevelToDb(peakLevelLR):F}"; + peakText.Text = $"curr: {curDisplay}dB"; + maxPeakText.Text = $"peak: {peakDisplay}dB"; + peakText.Colour = BassUtils.LevelToDb(Math.Max(curLevelL, curLevelR)) > 0 ? Colour4.Red : Colour4.White; + maxPeakText.Colour = BassUtils.LevelToDb(peakLevelLR) > 0 ? Colour4.Red : Colour4.White; mixerLabel.Text = isOutputChannel ? "MIXER OUT" : " "; } } diff --git a/osu.Framework/Graphics/Visualisation/Audio/AudioMixerVisualiser.cs b/osu.Framework/Graphics/Visualisation/Audio/AudioMixerVisualiser.cs index ab2eb04131..3cd233c80f 100644 --- a/osu.Framework/Graphics/Visualisation/Audio/AudioMixerVisualiser.cs +++ b/osu.Framework/Graphics/Visualisation/Audio/AudioMixerVisualiser.cs @@ -30,7 +30,7 @@ public AudioMixerVisualiser() MainHorizontalContent.Add(new BasicScrollContainer(Direction.Horizontal) { RelativeSizeAxes = Axes.Y, - Width = WIDTH, + Width = WIDTH * 1.5f, Children = new[] { mixerFlow = new FillFlowContainer diff --git a/osu.Framework/Graphics/Visualisation/Audio/MixerDisplay.cs b/osu.Framework/Graphics/Visualisation/Audio/MixerDisplay.cs index be15983b4a..01ed2ca2e9 100644 --- a/osu.Framework/Graphics/Visualisation/Audio/MixerDisplay.cs +++ b/osu.Framework/Graphics/Visualisation/Audio/MixerDisplay.cs @@ -42,12 +42,13 @@ public MixerDisplay(AudioMixer mixer) }, new SpriteText { - Anchor = Anchor.BottomCentre, - Origin = Anchor.BottomCentre, + Anchor = Anchor.BottomLeft, + Origin = Anchor.BottomLeft, Text = mixer.Identifier, Font = FrameworkFont.Condensed.With(size: 14), Colour = FrameworkColour.Yellow, Padding = new MarginPadding(2), + Margin = new MarginPadding(10) }, new FillFlowContainer { From d5067bff2f1ab7cf6de8a719485da75b50dc5638 Mon Sep 17 00:00:00 2001 From: Leo Bottaro Date: Mon, 22 Jul 2024 00:54:49 -0300 Subject: [PATCH 5/5] Enqueue mixer adjustment binding --- osu.Framework/Audio/Mixing/AudioMixer.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Framework/Audio/Mixing/AudioMixer.cs b/osu.Framework/Audio/Mixing/AudioMixer.cs index 9ab652b2cc..da8a3c7655 100644 --- a/osu.Framework/Audio/Mixing/AudioMixer.cs +++ b/osu.Framework/Audio/Mixing/AudioMixer.cs @@ -29,9 +29,6 @@ protected AudioMixer(AudioMixer? fallbackMixer, string identifier) public void Add(IAudioChannel channel) { - if (channel is IAdjustableAudioComponent adj) - adj.BindAdjustments(this); - channel.EnqueueAction(() => { if (channel.Mixer == this) @@ -40,6 +37,9 @@ public void Add(IAudioChannel channel) // Ensure the channel is removed from its current mixer. channel.Mixer?.Remove(channel, false); + if (channel is IAdjustableAudioComponent adj) + adj.BindAdjustments(this); + AddInternal(channel); channel.Mixer = this;