diff --git a/osu.Framework/Audio/AudioManager.cs b/osu.Framework/Audio/AudioManager.cs index f6c366271d..89e27b8627 100644 --- a/osu.Framework/Audio/AudioManager.cs +++ b/osu.Framework/Audio/AudioManager.cs @@ -177,13 +177,15 @@ public AudioManager(AudioThread audioThread, ResourceStore trackStore, R }; AddItem(TrackMixer = createAudioMixer(null, nameof(TrackMixer))); + TrackMixer.Volume.BindTo(VolumeTrack); + AddItem(SampleMixer = createAudioMixer(null, nameof(SampleMixer))); + 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; }); @@ -270,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/AudioMixer.cs b/osu.Framework/Audio/Mixing/AudioMixer.cs index 4cb612e0d0..da8a3c7655 100644 --- a/osu.Framework/Audio/Mixing/AudioMixer.cs +++ b/osu.Framework/Audio/Mixing/AudioMixer.cs @@ -9,7 +9,7 @@ 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; @@ -37,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; @@ -70,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/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); } /// 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 {