diff --git a/Assets/Script/Audio/Bass/BassAudioManager.cs b/Assets/Script/Audio/Bass/BassAudioManager.cs index 64c6c40175..2f0f058c5a 100644 --- a/Assets/Script/Audio/Bass/BassAudioManager.cs +++ b/Assets/Script/Audio/Bass/BassAudioManager.cs @@ -292,35 +292,26 @@ public void LoadMogg(byte[] moggArray, List<(SongStem, int[], float[])> stemMaps for (var i = 0; i < stemMaps.Count; i++) { - int[] channelStreams = new int[stemMaps[i].Item2.Length]; - var matrixes = new List(); - for (int j = 0; j < channelStreams.Length; ++j) + for (int j = 0; j < stemMaps[i].Item2.Length; ++j) { - int channelIndex = stemMaps[i].Item2[j]; - channelMap[0] = channelIndex; + channelMap[0] = stemMaps[i].Item2[j]; int splitHandle = BassMix.CreateSplitStream(moggStreamHandle, BassFlags.Decode | BassFlags.SplitPosition, channelMap); if (splitHandle == 0) { throw new Exception($"Failed to create MOGG stream handle: {Bass.LastError}"); } - channelStreams[j] = splitHandle; - var matrix = new float[2]; - matrix[0] = stemMaps[i].Item3[2 * j]; - matrix[1] = stemMaps[i].Item3[2 * j + 1]; - matrixes.Add(matrix); - } - - var channel = new BassMoggStemChannel(this, stemMaps[i].Item1, channelStreams, matrixes); - if (channel.Load(speed) < 0) - { - throw new Exception($"Failed to load MOGG stem channel: {Bass.LastError}"); - } + var channel = new BassMoggStemChannel(this, stemMaps[i].Item1, splitHandle, stemMaps[i].Item3[2 * j], stemMaps[i].Item3[2 * j + 1]); + if (channel.Load(speed) < 0) + { + throw new Exception($"Failed to load MOGG stem channel: {Bass.LastError}"); + } - int code = mixer.AddChannel(channel); - if (code != 0) - { - throw new Exception($"Failed to add MOGG stem channel to mixer: {Bass.LastError}"); + int code = mixer.AddChannel(channel); + if (code != 0) + { + throw new Exception($"Failed to add MOGG stem channel to mixer: {Bass.LastError}"); + } } } @@ -353,7 +344,7 @@ public void LoadCustomAudioFile(string audioPath, float speed) return; } - if (_mixer.GetChannel(SongStem.Song) != null) + if (_mixer.GetChannels(SongStem.Song) != null) { Debug.LogError($"Stem already loaded! {audioPath}"); return; @@ -394,17 +385,7 @@ private void Play(bool fadeIn) return; } - foreach (var channel in _mixer.Channels.Values) - { - if (fadeIn) - { - channel.SetVolume(0); - } - else - { - channel.SetVolume(channel.Volume); - } - } + _mixer.SetPlayVolume(fadeIn); if (_mixer.Play() != 0) { @@ -432,10 +413,8 @@ public void Pause() public void FadeIn(float maxVolume) { Play(true); - if (IsPlaying) - { - _mixer?.FadeIn(maxVolume); - } + if (IsPlaying && _mixer != null) + _mixer.FadeIn(maxVolume); } public async UniTask FadeOut(CancellationToken token = default) @@ -463,9 +442,12 @@ public void PlaySoundEffect(SfxSample sample) public void SetStemVolume(SongStem stem, double volume) { - var channel = _mixer?.GetChannel(stem); + if (_mixer == null) + return; - channel?.SetVolume(volume); + var stemChannels = _mixer?.GetChannels(stem); + for (int i = 0; i < stemChannels.Length; ++i) + stemChannels[i].SetVolume(volume); } public void SetAllStemsVolume(double volume) @@ -475,10 +457,9 @@ public void SetAllStemsVolume(double volume) return; } - foreach (var (_, channel) in _mixer.Channels) - { - channel.SetVolume(volume); - } + foreach (var (_, stem) in _mixer.Channels) + for (int i = 0; i < stem.Count; ++i) + stem[i].SetVolume(volume); } public void UpdateVolumeSetting(SongStem stem, double volume) @@ -509,14 +490,19 @@ public double GetVolumeSetting(SongStem stem) }; } - public void ApplyReverb(SongStem stem, bool reverb) => _mixer?.GetChannel(stem)?.SetReverb(reverb); - + public void ApplyReverb(SongStem stem, bool reverb) + { + foreach (var channel in _mixer.GetChannels(stem)) + channel.SetReverb(reverb); + } + public void SetWhammyPitch(SongStem stem, float percent) { - if (!AudioHelpers.PitchBendAllowedStems.Contains(stem)) + if (_mixer == null || !AudioHelpers.PitchBendAllowedStems.Contains(stem)) return; - _mixer?.GetChannel(stem)?.SetWhammyPitch(percent); + foreach (var channel in _mixer.GetChannels(stem)) + channel.SetWhammyPitch(percent); } public double GetPosition(bool desyncCompensation = true) diff --git a/Assets/Script/Audio/Bass/BassMoggStemChannel.cs b/Assets/Script/Audio/Bass/BassMoggStemChannel.cs index 6fc6c077e3..2a3f9bd053 100644 --- a/Assets/Script/Audio/Bass/BassMoggStemChannel.cs +++ b/Assets/Script/Audio/Bass/BassMoggStemChannel.cs @@ -1,190 +1,18 @@ -using System; +using System; using System.Collections.Generic; using Cysharp.Threading.Tasks; namespace YARG.Audio.BASS { - public class BassMoggStemChannel : IStemChannel + public class BassMoggStemChannel : BassStemChannel { - public SongStem Stem { get; } - public double LengthD { get; private set; } - public double Volume { get; private set; } + public readonly float left; + public readonly float right; - public event Action ChannelEnd + public BassMoggStemChannel(IAudioManager manager, SongStem stem, int splitStreams, float left, float right) : base(manager, stem, splitStreams) { - add - { - if (_leadChannel is null) - { - throw new InvalidOperationException("No song is currently loaded!"); - } - - _leadChannel.ChannelEnd += value; - } - remove - { - if (_leadChannel is null) - { - throw new InvalidOperationException("No song is currently loaded!"); - } - - _leadChannel.ChannelEnd -= value; - } - } - - private readonly IAudioManager _manager; - - private IStemChannel _leadChannel; - private List _channels = new(); - public IReadOnlyList Channels => _channels; - - private List _matrixes; - public IReadOnlyList Matrixes => _matrixes; - - private bool _isLoaded; - private bool _disposed; - - public BassMoggStemChannel(IAudioManager manager, SongStem stem, int[] splitStreams, List matrixes) - { - _manager = manager; - Stem = stem; - - foreach (int stream in splitStreams) - { - _channels.Add(new BassStemChannel(_manager, Stem, stream)); - } - - _matrixes = matrixes; - - Volume = 1; - } - - ~BassMoggStemChannel() - { - Dispose(false); - } - - public int Load(float speed) - { - if (_disposed) - { - return -1; - } - - if (_isLoaded) - { - return 0; - } - - foreach (var channel in _channels) - { - channel.Load(speed); - - // Get longest channel as part of this stem - double length = channel.LengthD; - if (length > LengthD) - { - _leadChannel = channel; - LengthD = length; - } - } - - _isLoaded = true; - - return 0; - } - - public void FadeIn(float maxVolume) - { - foreach (var channel in _channels) - { - channel.FadeIn(maxVolume); - } - } - - public UniTask FadeOut() - { - var fadeOuts = new List(); - foreach (var channel in _channels) - { - fadeOuts.Add(channel.FadeOut()); - } - - return UniTask.WhenAll(fadeOuts); - } - - public void SetVolume(double newVolume) - { - if (!_isLoaded) - { - return; - } - - Volume = newVolume; - - foreach (var channel in _channels) - { - channel.SetVolume(newVolume); - } - } - - public void SetReverb(bool reverb) - { - foreach (var channel in _channels) - { - channel.SetReverb(reverb); - } - } - - public void SetWhammyPitch(float percent) - { - foreach (var channel in _channels) - { - channel.SetWhammyPitch(percent); - } - } - - public double GetPosition(bool desyncCompensation = true) - { - return _leadChannel.GetPosition(desyncCompensation); - } - - public void SetPosition(double position, bool desyncCompensation = true) - { - foreach (var channel in _channels) - { - channel.SetPosition(position, desyncCompensation); - } - } - - public double GetLengthInSeconds() - { - return _leadChannel.GetLengthInSeconds(); - } - - public void Dispose() - { - Dispose(true); - GC.SuppressFinalize(this); - } - - private void Dispose(bool disposing) - { - if (!_disposed) - { - // Free managed resources here - if (disposing) - { - foreach (var channel in _channels) - { - channel.Dispose(); - } - - _channels = null; - } - - _disposed = true; - } + this.left = left; + this.right = right; } } } \ No newline at end of file diff --git a/Assets/Script/Audio/Bass/BassMoggStemMixer.cs b/Assets/Script/Audio/Bass/BassMoggStemMixer.cs index 820e9e8eef..7a03d984dc 100644 --- a/Assets/Script/Audio/Bass/BassMoggStemMixer.cs +++ b/Assets/Script/Audio/Bass/BassMoggStemMixer.cs @@ -1,4 +1,4 @@ -using System; +using System; using ManagedBass; using ManagedBass.Mix; using UnityEngine; @@ -16,54 +16,41 @@ public BassMoggStemMixer(IAudioManager manager, int moggStreamHandle) : base(man public override int AddChannel(IStemChannel channel) { - if (channel is not BassMoggStemChannel moggStem) + if (channel is not BassMoggStemChannel moggChannel) { throw new ArgumentException("Channel must be of type BassMoggStemChannel"); } - if (_channels.ContainsKey(channel.Stem)) - { - return 0; - } - var matrixes = moggStem.Matrixes; - for (var i = 0; i < moggStem.Channels.Count; i++) + if (!BassMix.MixerAddChannel(_mixerHandle, moggChannel.StreamHandle, BassFlags.MixerChanMatrix | BassFlags.MixerChanDownMix) || + !BassMix.MixerAddChannel(_mixerHandle, moggChannel.ReverbStreamHandle, BassFlags.MixerChanMatrix | BassFlags.MixerChanDownMix)) { - var moggChannel = (BassStemChannel) moggStem.Channels[i]; - if (!BassMix.MixerAddChannel(_mixerHandle, moggChannel.StreamHandle, BassFlags.MixerChanMatrix)) - { - return (int) Bass.LastError; - } - - if (!BassMix.MixerAddChannel(_mixerHandle, moggChannel.ReverbStreamHandle, BassFlags.MixerChanMatrix)) - { - return (int) Bass.LastError; - } + return (int) Bass.LastError; + } - moggChannel.IsMixed = true; + moggChannel.IsMixed = true; - float[,] channelPanVol = + float[,] channelPanVol = + { { - { - matrixes[i][0] - }, - { - matrixes[i][1] - } - }; - - if (!BassMix.ChannelSetMatrix(moggChannel.StreamHandle, channelPanVol)) + moggChannel.left + }, { - return (int) Bass.LastError; + moggChannel.right } + }; - if (!BassMix.ChannelSetMatrix(moggChannel.ReverbStreamHandle, channelPanVol)) - { - return (int) Bass.LastError; - } + if (!BassMix.ChannelSetMatrix(moggChannel.StreamHandle, channelPanVol) || + !BassMix.ChannelSetMatrix(moggChannel.ReverbStreamHandle, channelPanVol)) + { + return (int) Bass.LastError; } - _channels.Add(channel.Stem, channel); + if (_channels.TryGetValue(channel.Stem, out var list)) + list.Add(channel); + else + _channels.Add(channel.Stem, new() { channel }); + StemsLoaded++; if (channel.LengthD > LeadChannel?.LengthD || LeadChannel is null) diff --git a/Assets/Script/Audio/Bass/BassStemMixer.cs b/Assets/Script/Audio/Bass/BassStemMixer.cs index 0348557428..da03d08664 100644 --- a/Assets/Script/Audio/Bass/BassStemMixer.cs +++ b/Assets/Script/Audio/Bass/BassStemMixer.cs @@ -15,7 +15,7 @@ public class BassStemMixer : IStemMixer public bool IsPlaying { get; protected set; } - public IReadOnlyDictionary Channels => _channels; + public IReadOnlyDictionary> Channels => _channels; public IStemChannel LeadChannel { get; protected set; } @@ -42,7 +42,7 @@ public event Action SongEnd } protected readonly IAudioManager _manager; - protected readonly Dictionary _channels; + protected readonly Dictionary> _channels; protected int _mixerHandle; @@ -51,7 +51,7 @@ public event Action SongEnd public BassStemMixer(IAudioManager manager) { _manager = manager; - _channels = new Dictionary(); + _channels = new Dictionary>(); StemsLoaded = 0; IsPlaying = false; @@ -102,15 +102,18 @@ public int Play(bool restart = false) public void FadeIn(float maxVolume) { - foreach (var channel in Channels.Values) - { - channel.FadeIn(maxVolume); - } + foreach (var stem in Channels.Values) + for (int i = 0; i < stem.Count; i++) + stem[i].FadeIn(maxVolume); } public UniTask FadeOut(CancellationToken token = default) { - var fadeOuts = Enumerable.Select(Channels.Values, channel => channel.FadeOut()).ToList(); + List stemChannels = new(); + foreach (var stem in Channels.Values) + stemChannels.AddRange(stem); + + var fadeOuts = Enumerable.Select(stemChannels, channel => channel.FadeOut()).ToList(); return UniTask.WhenAll(fadeOuts).AttachExternalCancellation(token); } @@ -149,10 +152,16 @@ public void SetPosition(double position, bool desyncCompensation = true) return; } + foreach (var stem in Channels.Values) + for (int i = 0; i < stem.Count; i++) + stem[i].SetPosition(position, desyncCompensation); + } + + public void SetPlayVolume(bool fadeIn) + { foreach (var channel in Channels.Values) - { - channel.SetPosition(position, desyncCompensation); - } + for (int i = 0; i < channel.Count; i++) + channel[i].SetVolume(fadeIn ? 0 : channel[i].Volume); } public virtual int AddChannel(IStemChannel channel) @@ -179,7 +188,7 @@ public virtual int AddChannel(IStemChannel channel) bassChannel.IsMixed = true; - _channels.Add(channel.Stem, channel); + _channels.Add(channel.Stem, new() { channel }); StemsLoaded++; if (channel.LengthD > LeadChannel?.LengthD || LeadChannel is null) @@ -215,11 +224,14 @@ public bool RemoveChannel(IStemChannel channel) if (channel == LeadChannel) { // Update lead channel - foreach (var c in _channels.Values) + foreach (var stem in Channels.Values) { - if (c.LengthD > LeadChannel?.LengthD) + for (int i = 0; i < stem.Count; i++) { - LeadChannel = c; + if (LeadChannel is null || stem[i].LengthD > LeadChannel.LengthD) + { + LeadChannel = stem[i]; + } } } } @@ -227,9 +239,9 @@ public bool RemoveChannel(IStemChannel channel) return true; } - public IStemChannel GetChannel(SongStem stem) + public IStemChannel[] GetChannels(SongStem stem) { - return !_channels.ContainsKey(stem) ? null : _channels[stem]; + return !_channels.ContainsKey(stem) ? Array.Empty() : _channels[stem].ToArray(); } public void Dispose() @@ -255,10 +267,9 @@ private void Dispose(bool disposing) protected virtual void ReleaseManagedResources() { // Free managed resources here - foreach (var channel in _channels.Values) - { - channel?.Dispose(); - } + foreach (var stem in Channels.Values) + for (int i = 0; i < stem.Count; i++) + stem[i].Dispose(); _channels.Clear(); } diff --git a/Assets/Script/Audio/Interfaces/IStemMixer.cs b/Assets/Script/Audio/Interfaces/IStemMixer.cs index a58f9b83a9..899b48f827 100644 --- a/Assets/Script/Audio/Interfaces/IStemMixer.cs +++ b/Assets/Script/Audio/Interfaces/IStemMixer.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.Threading; using Cysharp.Threading.Tasks; @@ -13,7 +13,7 @@ public interface IStemMixer : IDisposable public event Action SongEnd; - public IReadOnlyDictionary Channels { get; } + public IReadOnlyDictionary> Channels { get; } public IStemChannel LeadChannel { get; } @@ -30,10 +30,12 @@ public interface IStemMixer : IDisposable public void SetPosition(double position, bool desyncCompensation = true); + public void SetPlayVolume(bool fadeIn); + public int AddChannel(IStemChannel channel); public bool RemoveChannel(IStemChannel channel); - public IStemChannel GetChannel(SongStem stem); + public IStemChannel[] GetChannels(SongStem stem); } } \ No newline at end of file diff --git a/Assets/Script/Song/Types/ExConSongEntry.cs b/Assets/Script/Song/Types/ExConSongEntry.cs index 18689e0843..08fbec5390 100644 --- a/Assets/Script/Song/Types/ExConSongEntry.cs +++ b/Assets/Script/Song/Types/ExConSongEntry.cs @@ -789,18 +789,18 @@ public override void LoadAudio(IAudioManager manager, float speed, params SongSt break; //drum (0 1 2): mono kick, stereo snare/kit --> (0) (1 2) case 3: - stemMaps.Add(new(SongStem.Drums1, new[] { DrumIndices[0] }, DrumStemValues[0..2])); + stemMaps.Add(new(SongStem.Drums1, DrumIndices[0..1], DrumStemValues[0..2])); stemMaps.Add(new(SongStem.Drums2, DrumIndices[1..3], DrumStemValues[2..6])); break; //drum (0 1 2 3): mono kick, mono snare, stereo kit --> (0) (1) (2 3) case 4: - stemMaps.Add(new(SongStem.Drums1, new[] { DrumIndices[0] }, DrumStemValues[0..2])); - stemMaps.Add(new(SongStem.Drums2, new[] { DrumIndices[1] }, DrumStemValues[2..4])); + stemMaps.Add(new(SongStem.Drums1, DrumIndices[0..1], DrumStemValues[0..2])); + stemMaps.Add(new(SongStem.Drums2, DrumIndices[1..2], DrumStemValues[2..4])); stemMaps.Add(new(SongStem.Drums3, DrumIndices[2..4], DrumStemValues[4..8])); break; //drum (0 1 2 3 4): mono kick, stereo snare, stereo kit --> (0) (1 2) (3 4) case 5: - stemMaps.Add(new(SongStem.Drums1, new[] { DrumIndices[0] }, DrumStemValues[0..2])); + stemMaps.Add(new(SongStem.Drums1, DrumIndices[0..1], DrumStemValues[0..2])); stemMaps.Add(new(SongStem.Drums2, DrumIndices[1..3], DrumStemValues[2..6])); stemMaps.Add(new(SongStem.Drums3, DrumIndices[3..5], DrumStemValues[6..10])); break;