From bd9f24f59a2178de68d64c74471587eaae4b6635 Mon Sep 17 00:00:00 2001 From: DV666 Date: Sat, 11 Apr 2026 14:38:23 +0200 Subject: [PATCH 1/8] Move EffectPreset in class (EffetPreset.cs) --- Assembly-CSharp/Assembly-CSharp.csproj | 1 + .../Sound/SaXAudio/AudioEffectManager.cs | 261 +++------------- .../Global/Sound/SaXAudio/EffectPreset.cs | 286 ++++++++++++++++++ .../Sound/SaXAudio/SdLibAPIWithSaXAudio.cs | 4 +- Assembly-CSharp/SoundDebugRoom/SoundView.cs | 2 +- 5 files changed, 325 insertions(+), 229 deletions(-) create mode 100644 Assembly-CSharp/Global/Sound/SaXAudio/EffectPreset.cs diff --git a/Assembly-CSharp/Assembly-CSharp.csproj b/Assembly-CSharp/Assembly-CSharp.csproj index f894cebd1..e17b7f0e0 100644 --- a/Assembly-CSharp/Assembly-CSharp.csproj +++ b/Assembly-CSharp/Assembly-CSharp.csproj @@ -265,6 +265,7 @@ + diff --git a/Assembly-CSharp/Global/Sound/SaXAudio/AudioEffectManager.cs b/Assembly-CSharp/Global/Sound/SaXAudio/AudioEffectManager.cs index 6e29833fe..d1bfd9884 100644 --- a/Assembly-CSharp/Global/Sound/SaXAudio/AudioEffectManager.cs +++ b/Assembly-CSharp/Global/Sound/SaXAudio/AudioEffectManager.cs @@ -13,7 +13,7 @@ namespace Global.Sound.SaXAudio { public class AudioEffectManager { - private const String FILENAME = "AudioEffects.txt"; + private const String FILENAME = "StreamingAssets/Data/Voices/AudioEffects.csv"; private static Dictionary fieldIDPresets = new Dictionary(); private static Dictionary battleIDPresets = new Dictionary(); @@ -21,7 +21,7 @@ public class AudioEffectManager private static Dictionary resourceIDPresets = new Dictionary(); private static List conditionalPresets = new List(); private static Dictionary unlistedPresets = new Dictionary(); - private static EffectPreset? currentPreset = null; + private static EffectPreset currentPreset = null; private static Boolean initialized = false; private static FileSystemWatcher watcher = null; @@ -48,7 +48,7 @@ public static void ApplyFieldEffects(Int32 fieldID) if (!initialized) Init(); } - if (fieldIDPresets.TryGetValue(fieldID, out EffectPreset preset) && ApplyPreset(ref preset)) + if (fieldIDPresets.TryGetValue(fieldID, out EffectPreset preset) && ApplyPreset(preset)) { currentPreset = preset; Log.Message($"[AudioEffectManager] Applied preset '{preset.Name}' to field {fieldID}"); @@ -66,14 +66,14 @@ public static void ApplyBattleEffects(Int32 battleID, Int32 battleBgID) if (!initialized) Init(); } - if (battleIDPresets.TryGetValue(battleID, out EffectPreset preset) && ApplyPreset(ref preset)) + if (battleIDPresets.TryGetValue(battleID, out EffectPreset preset) && ApplyPreset(preset)) { currentPreset = preset; Log.Message($"[AudioEffectManager] Applied preset '{preset.Name}' to battle {battleID}"); return; } - if (battleBgIDPresets.TryGetValue(battleBgID, out preset) && ApplyPreset(ref preset)) + if (battleBgIDPresets.TryGetValue(battleBgID, out preset) && ApplyPreset(preset)) { currentPreset = preset; Log.Message($"[AudioEffectManager] Applied preset '{preset.Name}' to battle background {battleBgID}"); @@ -82,42 +82,42 @@ public static void ApplyBattleEffects(Int32 battleID, Int32 battleBgID) ResetEffects(); } - public static EffectPreset? GetPreset(SoundProfile profile, Int32 bus) + public static EffectPreset GetPreset(SoundProfile profile, Int32 bus) { if (!IsSaXAudio || !initialized) return null; - EffectPreset? preset = null; + EffectPreset preset = null; if (resourceIDPresets.ContainsKey(profile.ResourceID)) preset = resourceIDPresets[profile.ResourceID]; // resourceIDs gets priority - if (preset != null && (String.IsNullOrEmpty(preset.Value.Condition) || EvaluatePresetCondition(profile, preset.Value.Condition, preset.Value.Name))) + if (preset != null && (String.IsNullOrEmpty(preset.Condition) || EvaluatePresetCondition(profile, preset.Condition, preset.Name))) return preset; // then conditional for (Int32 i = 0; i < conditionalPresets.Count; i++) { preset = conditionalPresets[i]; - if ((preset.Value.Layers == EffectPreset.Layer.None || preset.Value.IsBusInLayers(bus)) && EvaluatePresetCondition(profile, preset.Value.Condition, preset.Value.Name)) + if ((preset.Layers == EffectPreset.Layer.None || preset.IsBusInLayers(bus)) && EvaluatePresetCondition(profile, preset.Condition, preset.Name)) return preset; } // then filter current preset preset = currentPreset; - if (preset != null && preset.Value.IsBusInLayers(bus) && !String.IsNullOrEmpty(preset.Value.Condition) && !EvaluatePresetCondition(profile, preset.Value.Condition, preset.Value.Name)) + if (preset != null && preset.IsBusInLayers(bus) && !String.IsNullOrEmpty(preset.Condition) && !EvaluatePresetCondition(profile, preset.Condition, preset.Name)) return new EffectPreset(); // empty preset because we want to exclude that particular sound return null; } - public static EffectPreset? GetUnlistedPreset(String presetName) + public static EffectPreset GetUnlistedPreset(String presetName) { if (!IsSaXAudio || !initialized || !unlistedPresets.ContainsKey(presetName)) return null; return unlistedPresets[presetName]; } - public static EffectPreset? FindPreset(String presetName) + public static EffectPreset FindPreset(String presetName) { if (!IsSaXAudio || !initialized) return null; @@ -309,19 +309,19 @@ private static void Init() initialized = true; } - private static Boolean ApplyPreset(ref EffectPreset preset) + private static Boolean ApplyPreset(EffectPreset preset) { SdLibAPIWithSaXAudio saXAudio = ISdLibAPIProxy.Instance as SdLibAPIWithSaXAudio; - if ((preset.Layers & EffectPreset.Layer.Music) != 0) ApplyPresetOnBus(ref preset, saXAudio.BusMusic); - if ((preset.Layers & EffectPreset.Layer.Ambient) != 0) ApplyPresetOnBus(ref preset, saXAudio.BusAmbient); - if ((preset.Layers & EffectPreset.Layer.SoundEffect) != 0) ApplyPresetOnBus(ref preset, saXAudio.BusSoundEffect); - if ((preset.Layers & EffectPreset.Layer.Voice) != 0) ApplyPresetOnBus(ref preset, saXAudio.BusVoice); + if ((preset.Layers & EffectPreset.Layer.Music) != 0) ApplyPresetOnBus(preset, saXAudio.BusMusic); + if ((preset.Layers & EffectPreset.Layer.Ambient) != 0) ApplyPresetOnBus(preset, saXAudio.BusAmbient); + if ((preset.Layers & EffectPreset.Layer.SoundEffect) != 0) ApplyPresetOnBus(preset, saXAudio.BusSoundEffect); + if ((preset.Layers & EffectPreset.Layer.Voice) != 0) ApplyPresetOnBus(preset, saXAudio.BusVoice); return true; } - private static void ApplyPresetOnBus(ref EffectPreset preset, Int32 bus) + private static void ApplyPresetOnBus(EffectPreset preset, Int32 bus) { Boolean reverb = (preset.Effects & EffectPreset.Effect.Reverb) != 0; Boolean eq = (preset.Effects & EffectPreset.Effect.Eq) != 0; @@ -338,208 +338,6 @@ private static void ApplyPresetOnBus(ref EffectPreset preset, Int32 bus) else SaXAudio.SetVolume(bus, 1f, 0, true); } - public struct EffectPreset - { - public enum Effect : Byte - { - None = 0, - Reverb = 1, - Eq = 1 << 1, - Echo = 1 << 2, - Volume = 1 << 3, - All = Reverb | Eq | Echo | Volume - } - - public enum Layer : Byte - { - None = 0, - Music = 1, - Ambient = 1 << 1, - SoundEffect = 1 << 2, - Voice = 1 << 3, - All = Music | Ambient | SoundEffect | Voice - } - - public Boolean IsBusInLayers(Int32 bus) - { - if (!IsSaXAudio || Layers == Layer.None) - return false; - - SdLibAPIWithSaXAudio saXAudio = ISdLibAPIProxy.Instance as SdLibAPIWithSaXAudio; - - if ((Layers & Layer.Music) != 0 && bus == saXAudio.BusMusic) - return true; - if ((Layers & Layer.Ambient) != 0 && bus == saXAudio.BusAmbient) - return true; - if ((Layers & Layer.SoundEffect) != 0 && bus == saXAudio.BusSoundEffect) - return true; - if ((Layers & Layer.Voice) != 0 && bus == saXAudio.BusVoice) - return true; - return false; - } - - public String Name = ""; - public Effect Effects = Effect.None; - public SaXAudio.ReverbParameters Reverb = new SaXAudio.ReverbParameters(); - public SaXAudio.EqParameters Eq = new SaXAudio.EqParameters(); - public SaXAudio.EchoParameters Echo = new SaXAudio.EchoParameters(); - public Single Volume = 1f; - - public Layer Layers = Layer.None; - public HashSet FieldIDs = new HashSet(); - public HashSet ResourceIDs = new HashSet(); - public HashSet BattleIDs = new HashSet(); - public HashSet BattleBgIDs = new HashSet(); - - public String Condition = ""; - - public EffectPreset() { } - public EffectPreset(String str) - { - String[] tokens = str.Split(';'); - Int32 i = 0; - Name = tokens[i++]; - Effects = (Effect)Byte.Parse(tokens[i++]); - - FieldInfo[] fields = typeof(SaXAudio.ReverbParameters).GetFields(); - object reverb = Reverb; - foreach (FieldInfo field in fields) - { - field.SetValue(reverb, Convert.ChangeType(tokens[i++], field.FieldType, CultureInfo.InvariantCulture)); - } - Reverb = (SaXAudio.ReverbParameters)reverb; - - fields = typeof(SaXAudio.EqParameters).GetFields(); - object eq = Eq; - foreach (FieldInfo field in fields) - { - field.SetValue(eq, Convert.ChangeType(tokens[i++], field.FieldType, CultureInfo.InvariantCulture)); - } - Eq = (SaXAudio.EqParameters)eq; - - fields = typeof(SaXAudio.EchoParameters).GetFields(); - object echo = Echo; - foreach (FieldInfo field in fields) - { - field.SetValue(echo, Convert.ChangeType(tokens[i++], field.FieldType, CultureInfo.InvariantCulture)); - } - Echo = (SaXAudio.EchoParameters)echo; - - Volume = Single.Parse(tokens[i++]); - - if (i == tokens.Length) return; - - Layers = (Layer)Byte.Parse(tokens[i++]); - - String[] ids = tokens[i++].Split('|'); - if (ids.Length > 0) - { - FieldIDs.Clear(); - foreach (String id in ids) - { - if (id.Length > 0) - FieldIDs.Add(Int32.Parse(id)); - } - } - - ids = tokens[i++].Split('|'); - if (ids.Length > 0) - { - BattleIDs.Clear(); - foreach (String id in ids) - { - if (id.Length > 0) - BattleIDs.Add(Int32.Parse(id)); - } - } - - ids = tokens[i++].Split('|'); - if (ids.Length > 0) - { - BattleBgIDs.Clear(); - foreach (String id in ids) - { - if (id.Length > 0) - BattleBgIDs.Add(Int32.Parse(id)); - } - } - - ids = tokens[i++].Split('|'); - if (ids.Length > 0) - { - ResourceIDs.Clear(); - foreach (String id in ids) - { - if (id.Length > 0) - ResourceIDs.Add(id); - } - } - - Condition = tokens[i++].Replace('\x0A', ';'); - } - - public override readonly String ToString() - { - StringBuilder builder = new StringBuilder(); - const Char separator = ';'; - builder.Append(Name); - builder.Append(separator); - builder.Append((Byte)Effects); - - FieldInfo[] fields = typeof(SaXAudio.ReverbParameters).GetFields(); - foreach (FieldInfo field in fields) - { - builder.Append(separator); - builder.Append((String)Convert.ChangeType(field.GetValue(Reverb), typeof(String), CultureInfo.InvariantCulture)); - } - - fields = typeof(SaXAudio.EqParameters).GetFields(); - foreach (FieldInfo field in fields) - { - builder.Append(separator); - builder.Append((String)Convert.ChangeType(field.GetValue(Eq), typeof(String), CultureInfo.InvariantCulture)); - } - - fields = typeof(SaXAudio.EchoParameters).GetFields(); - foreach (FieldInfo field in fields) - { - builder.Append(separator); - builder.Append((String)Convert.ChangeType(field.GetValue(Echo), typeof(String), CultureInfo.InvariantCulture)); - } - - builder.Append(separator); - builder.Append(Volume); - - builder.Append(separator); - builder.Append((Byte)Layers); - - builder.Append(separator); - builder.Append(String.Join("|", FieldIDs.Select(x => x.ToString()).ToArray())); - - builder.Append(separator); - builder.Append(String.Join("|", BattleIDs.Select(x => x.ToString()).ToArray())); - - builder.Append(separator); - builder.Append(String.Join("|", BattleBgIDs.Select(x => x.ToString()).ToArray())); - - builder.Append(separator); - builder.Append(String.Join("|", ResourceIDs.ToArray())); - - builder.Append(separator); - builder.Append(Condition.Trim().Replace(';', '\x0A')); - - return builder.ToString(); - } - - public void RemoveIDs() - { - FieldIDs = null; - BattleIDs = null; - BattleBgIDs = null; - ResourceIDs = null; - } - } - public static SortedDictionary LoadPresets(String modLocation, Boolean backup = false) { SortedDictionary presets = new SortedDictionary(); @@ -554,10 +352,16 @@ public static SortedDictionary LoadPresets(String modLocat { if (line.Length == 0 || line.StartsWith("#")) continue; - EffectPreset preset = new EffectPreset(line); - presets.Add(preset.Name, preset); + + EffectPreset preset = new EffectPreset(); + preset.ParseEntry(line.Split(';'), null); + + if (!string.IsNullOrEmpty(preset.Name)) + { + presets[preset.Name] = preset; + } } - Log.Message($"[AudioEffectManager] Loaded {mod} presets"); + Log.Message($"[AudioEffectManager] Loaded {mod} presets from CSV"); } catch (Exception e) { @@ -573,13 +377,18 @@ public static void SavePresets(SortedDictionary presets, S { String mod = Path.GetDirectoryName(modLocation); List lines = new List(); + + lines.Add("# ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------"); + lines.Add("# Name;Effects;Reverb.WetDryMix;Reverb.ReflectionsDelay;Reverb.ReverbDelay;Reverb.RearDelay;Reverb.SideDelay;Reverb.PositionLeft;Reverb.PositionRight;Reverb.PositionMatrixLeft;Reverb.PositionMatrixRight;Reverb.EarlyDiffusion;Reverb.LateDiffusion;Reverb.LowEQGain;Reverb.LowEQCutoff;Reverb.HighEQGain;Reverb.HighEQCutoff;Reverb.RoomFilterFreq;Reverb.RoomFilterMain;Reverb.RoomFilterHF;Reverb.ReflectionsGain;Reverb.ReverbGain;Reverb.DecayTime;Reverb.Density;Reverb.RoomSize;Reverb.DisableLateField;Eq.FrequencyCenter0;Eq.Gain0;Eq.Bandwidth0;Eq.FrequencyCenter1;Eq.Gain1;Eq.Bandwidth1;Eq.FrequencyCenter2;Eq.Gain2;Eq.Bandwidth2;Eq.FrequencyCenter3;Eq.Gain3;Eq.Bandwidth3;Echo.WetDryMix;Echo.Feedback;Echo.Delay;Volume;Layers;FieldIDs;BattleIDs;BattleBgIDs;ResourceIDs;NCalcCondition"); + lines.Add("# String;Byte;Single;UInt32;Byte;Byte;Byte;Byte;Byte;Byte;Byte;Byte;Byte;Byte;Byte;Byte;Byte;Single;Single;Single;Single;Single;Single;Single;Single;Boolean;Single;Single;Single;Single;Single;Single;Single;Single;Single;Single;Single;Single;Single;Single;Single;Single;Byte;String;String;String;String"); + lines.Add("# ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------"); + foreach (EffectPreset effectPreset in presets.Values) - { lines.Add(effectPreset.ToString()); - } + String path = Path.Combine(modLocation, $"{FILENAME}{(backup ? ".bak" : "")}"); File.WriteAllLines(path, lines.ToArray()); - Log.Message($"[AudioEffectManager] Saved {mod} presets"); + Log.Message($"[AudioEffectManager] Saved {mod} presets to CSV"); } catch (Exception e) { diff --git a/Assembly-CSharp/Global/Sound/SaXAudio/EffectPreset.cs b/Assembly-CSharp/Global/Sound/SaXAudio/EffectPreset.cs new file mode 100644 index 000000000..ecf9ba7e4 --- /dev/null +++ b/Assembly-CSharp/Global/Sound/SaXAudio/EffectPreset.cs @@ -0,0 +1,286 @@ +using Memoria.Prime.CSV; +using System; +using System.Collections.Generic; +using System.Globalization; +using System.Linq; + +namespace Global.Sound.SaXAudio +{ + public sealed class EffectPreset : ICsvEntry + { + public enum Effect : Byte + { + None = 0, + Reverb = 1, + Eq = 1 << 1, + Echo = 1 << 2, + Volume = 1 << 3, + All = Reverb | Eq | Echo | Volume + } + + public enum Layer : Byte + { + None = 0, + Music = 1, + Ambient = 1 << 1, + SoundEffect = 1 << 2, + Voice = 1 << 3, + All = Music | Ambient | SoundEffect | Voice + } + + public String Name = ""; + public Effect Effects = Effect.None; + public SaXAudio.ReverbParameters Reverb = new SaXAudio.ReverbParameters(); + public SaXAudio.EqParameters Eq = new SaXAudio.EqParameters(); + public SaXAudio.EchoParameters Echo = new SaXAudio.EchoParameters(); + public Single Volume = 1f; + + public Layer Layers = Layer.None; + public HashSet FieldIDs = new HashSet(); + public HashSet BattleIDs = new HashSet(); + public HashSet BattleBgIDs = new HashSet(); + public HashSet ResourceIDs = new HashSet(); + public String Condition = ""; + + public EffectPreset() { } + + public Boolean IsBusInLayers(Int32 bus) + { + if (!AudioEffectManager.IsSaXAudio || Layers == Layer.None) + return false; + + SdLibAPIWithSaXAudio saXAudio = ISdLibAPIProxy.Instance as SdLibAPIWithSaXAudio; + if (saXAudio == null) return false; + + if ((Layers & Layer.Music) != 0 && bus == saXAudio.BusMusic) + return true; + if ((Layers & Layer.Ambient) != 0 && bus == saXAudio.BusAmbient) + return true; + if ((Layers & Layer.SoundEffect) != 0 && bus == saXAudio.BusSoundEffect) + return true; + if ((Layers & Layer.Voice) != 0 && bus == saXAudio.BusVoice) + return true; + return false; + } + + public void RemoveIDs() + { + FieldIDs = null; + BattleIDs = null; + BattleBgIDs = null; + ResourceIDs = null; + } + + public void ParseEntry(String[] raw, CsvMetaData metadata) + { + if (raw.Length < 47) return; + + Name = raw[0]; + Effects = (Effect)Byte.Parse(raw[1]); + + Reverb.WetDryMix = Single.Parse(raw[2], CultureInfo.InvariantCulture); + Reverb.ReflectionsDelay = UInt32.Parse(raw[3], CultureInfo.InvariantCulture); + Reverb.ReverbDelay = Byte.Parse(raw[4], CultureInfo.InvariantCulture); + Reverb.RearDelay = Byte.Parse(raw[5], CultureInfo.InvariantCulture); + Reverb.SideDelay = Byte.Parse(raw[6], CultureInfo.InvariantCulture); + Reverb.PositionLeft = Byte.Parse(raw[7], CultureInfo.InvariantCulture); + Reverb.PositionRight = Byte.Parse(raw[8], CultureInfo.InvariantCulture); + Reverb.PositionMatrixLeft = Byte.Parse(raw[9], CultureInfo.InvariantCulture); + Reverb.PositionMatrixRight = Byte.Parse(raw[10], CultureInfo.InvariantCulture); + Reverb.EarlyDiffusion = Byte.Parse(raw[11], CultureInfo.InvariantCulture); + Reverb.LateDiffusion = Byte.Parse(raw[12], CultureInfo.InvariantCulture); + Reverb.LowEQGain = Byte.Parse(raw[13], CultureInfo.InvariantCulture); + Reverb.LowEQCutoff = Byte.Parse(raw[14], CultureInfo.InvariantCulture); + Reverb.HighEQGain = Byte.Parse(raw[15], CultureInfo.InvariantCulture); + Reverb.HighEQCutoff = Byte.Parse(raw[16], CultureInfo.InvariantCulture); + Reverb.RoomFilterFreq = Single.Parse(raw[17], CultureInfo.InvariantCulture); + Reverb.RoomFilterMain = Single.Parse(raw[18], CultureInfo.InvariantCulture); + Reverb.RoomFilterHF = Single.Parse(raw[19], CultureInfo.InvariantCulture); + Reverb.ReflectionsGain = Single.Parse(raw[20], CultureInfo.InvariantCulture); + Reverb.ReverbGain = Single.Parse(raw[21], CultureInfo.InvariantCulture); + Reverb.DecayTime = Single.Parse(raw[22], CultureInfo.InvariantCulture); + Reverb.Density = Single.Parse(raw[23], CultureInfo.InvariantCulture); + Reverb.RoomSize = Single.Parse(raw[24], CultureInfo.InvariantCulture); + Reverb.DisableLateField = Boolean.Parse(raw[25]); + + Eq.FrequencyCenter0 = Single.Parse(raw[26], CultureInfo.InvariantCulture); + Eq.Gain0 = Single.Parse(raw[27], CultureInfo.InvariantCulture); + Eq.Bandwidth0 = Single.Parse(raw[28], CultureInfo.InvariantCulture); + Eq.FrequencyCenter1 = Single.Parse(raw[29], CultureInfo.InvariantCulture); + Eq.Gain1 = Single.Parse(raw[30], CultureInfo.InvariantCulture); + Eq.Bandwidth1 = Single.Parse(raw[31], CultureInfo.InvariantCulture); + Eq.FrequencyCenter2 = Single.Parse(raw[32], CultureInfo.InvariantCulture); + Eq.Gain2 = Single.Parse(raw[33], CultureInfo.InvariantCulture); + Eq.Bandwidth2 = Single.Parse(raw[34], CultureInfo.InvariantCulture); + Eq.FrequencyCenter3 = Single.Parse(raw[35], CultureInfo.InvariantCulture); + Eq.Gain3 = Single.Parse(raw[36], CultureInfo.InvariantCulture); + Eq.Bandwidth3 = Single.Parse(raw[37], CultureInfo.InvariantCulture); + + Echo.WetDryMix = Single.Parse(raw[38], CultureInfo.InvariantCulture); + Echo.Feedback = Single.Parse(raw[39], CultureInfo.InvariantCulture); + Echo.Delay = Single.Parse(raw[40], CultureInfo.InvariantCulture); + + Volume = Single.Parse(raw[41], CultureInfo.InvariantCulture); + Layers = (Layer)Byte.Parse(raw[42]); + + String[] ids; + + ids = raw[43].Split('|'); + if (ids.Length > 0) + { + FieldIDs.Clear(); + foreach (String id in ids) + if (id.Length > 0) FieldIDs.Add(Int32.Parse(id)); + } + + ids = raw[44].Split('|'); + if (ids.Length > 0) + { + BattleIDs.Clear(); + foreach (String id in ids) + if (id.Length > 0) BattleIDs.Add(Int32.Parse(id)); + } + + ids = raw[45].Split('|'); + if (ids.Length > 0) + { + BattleBgIDs.Clear(); + foreach (String id in ids) + if (id.Length > 0) BattleBgIDs.Add(Int32.Parse(id)); + } + + ids = raw[46].Split('|'); + if (ids.Length > 0) + { + ResourceIDs.Clear(); + foreach (String id in ids) + if (id.Length > 0) ResourceIDs.Add(id); + } + + Condition = raw[47]; + } + + public void WriteEntry(CsvWriter writer, CsvMetaData metadata) + { + writer.String(Name); + writer.Byte((Byte)Effects); + + // Reverb + writer.String(Reverb.WetDryMix.ToString(CultureInfo.InvariantCulture)); + writer.String(Reverb.ReflectionsDelay.ToString(CultureInfo.InvariantCulture)); + writer.Byte(Reverb.ReverbDelay); + writer.Byte(Reverb.RearDelay); + writer.Byte(Reverb.SideDelay); + writer.Byte(Reverb.PositionLeft); + writer.Byte(Reverb.PositionRight); + writer.Byte(Reverb.PositionMatrixLeft); + writer.Byte(Reverb.PositionMatrixRight); + writer.Byte(Reverb.EarlyDiffusion); + writer.Byte(Reverb.LateDiffusion); + writer.Byte(Reverb.LowEQGain); + writer.Byte(Reverb.LowEQCutoff); + writer.Byte(Reverb.HighEQGain); + writer.Byte(Reverb.HighEQCutoff); + writer.String(Reverb.RoomFilterFreq.ToString(CultureInfo.InvariantCulture)); + writer.String(Reverb.RoomFilterMain.ToString(CultureInfo.InvariantCulture)); + writer.String(Reverb.RoomFilterHF.ToString(CultureInfo.InvariantCulture)); + writer.String(Reverb.ReflectionsGain.ToString(CultureInfo.InvariantCulture)); + writer.String(Reverb.ReverbGain.ToString(CultureInfo.InvariantCulture)); + writer.String(Reverb.DecayTime.ToString(CultureInfo.InvariantCulture)); + writer.String(Reverb.Density.ToString(CultureInfo.InvariantCulture)); + writer.String(Reverb.RoomSize.ToString(CultureInfo.InvariantCulture)); + writer.Boolean(Reverb.DisableLateField); + + // EQ + writer.String(Eq.FrequencyCenter0.ToString(CultureInfo.InvariantCulture)); + writer.String(Eq.Gain0.ToString(CultureInfo.InvariantCulture)); + writer.String(Eq.Bandwidth0.ToString(CultureInfo.InvariantCulture)); + writer.String(Eq.FrequencyCenter1.ToString(CultureInfo.InvariantCulture)); + writer.String(Eq.Gain1.ToString(CultureInfo.InvariantCulture)); + writer.String(Eq.Bandwidth1.ToString(CultureInfo.InvariantCulture)); + writer.String(Eq.FrequencyCenter2.ToString(CultureInfo.InvariantCulture)); + writer.String(Eq.Gain2.ToString(CultureInfo.InvariantCulture)); + writer.String(Eq.Bandwidth2.ToString(CultureInfo.InvariantCulture)); + writer.String(Eq.FrequencyCenter3.ToString(CultureInfo.InvariantCulture)); + writer.String(Eq.Gain3.ToString(CultureInfo.InvariantCulture)); + writer.String(Eq.Bandwidth3.ToString(CultureInfo.InvariantCulture)); + + // Echo + writer.String(Echo.WetDryMix.ToString(CultureInfo.InvariantCulture)); + writer.String(Echo.Feedback.ToString(CultureInfo.InvariantCulture)); + writer.String(Echo.Delay.ToString(CultureInfo.InvariantCulture)); + + // Volume & Layers + writer.String(Volume.ToString(CultureInfo.InvariantCulture)); + writer.Byte((Byte)Layers); + + writer.String(String.Join("|", FieldIDs.Select(x => x.ToString()).ToArray())); + writer.String(String.Join("|", BattleIDs.Select(x => x.ToString()).ToArray())); + writer.String(String.Join("|", BattleBgIDs.Select(x => x.ToString()).ToArray())); + writer.String(String.Join("|", ResourceIDs.ToArray())); + + writer.String(""); + } + + public override string ToString() + { + List raw = new List(); + + raw.Add(Name); + raw.Add(((Byte)Effects).ToString()); + + raw.Add(Reverb.WetDryMix.ToString(CultureInfo.InvariantCulture)); + raw.Add(Reverb.ReflectionsDelay.ToString(CultureInfo.InvariantCulture)); + raw.Add(Reverb.ReverbDelay.ToString()); + raw.Add(Reverb.RearDelay.ToString()); + raw.Add(Reverb.SideDelay.ToString()); + raw.Add(Reverb.PositionLeft.ToString()); + raw.Add(Reverb.PositionRight.ToString()); + raw.Add(Reverb.PositionMatrixLeft.ToString()); + raw.Add(Reverb.PositionMatrixRight.ToString()); + raw.Add(Reverb.EarlyDiffusion.ToString()); + raw.Add(Reverb.LateDiffusion.ToString()); + raw.Add(Reverb.LowEQGain.ToString()); + raw.Add(Reverb.LowEQCutoff.ToString()); + raw.Add(Reverb.HighEQGain.ToString()); + raw.Add(Reverb.HighEQCutoff.ToString()); + raw.Add(Reverb.RoomFilterFreq.ToString(CultureInfo.InvariantCulture)); + raw.Add(Reverb.RoomFilterMain.ToString(CultureInfo.InvariantCulture)); + raw.Add(Reverb.RoomFilterHF.ToString(CultureInfo.InvariantCulture)); + raw.Add(Reverb.ReflectionsGain.ToString(CultureInfo.InvariantCulture)); + raw.Add(Reverb.ReverbGain.ToString(CultureInfo.InvariantCulture)); + raw.Add(Reverb.DecayTime.ToString(CultureInfo.InvariantCulture)); + raw.Add(Reverb.Density.ToString(CultureInfo.InvariantCulture)); + raw.Add(Reverb.RoomSize.ToString(CultureInfo.InvariantCulture)); + raw.Add(Reverb.DisableLateField.ToString()); + + raw.Add(Eq.FrequencyCenter0.ToString(CultureInfo.InvariantCulture)); + raw.Add(Eq.Gain0.ToString(CultureInfo.InvariantCulture)); + raw.Add(Eq.Bandwidth0.ToString(CultureInfo.InvariantCulture)); + raw.Add(Eq.FrequencyCenter1.ToString(CultureInfo.InvariantCulture)); + raw.Add(Eq.Gain1.ToString(CultureInfo.InvariantCulture)); + raw.Add(Eq.Bandwidth1.ToString(CultureInfo.InvariantCulture)); + raw.Add(Eq.FrequencyCenter2.ToString(CultureInfo.InvariantCulture)); + raw.Add(Eq.Gain2.ToString(CultureInfo.InvariantCulture)); + raw.Add(Eq.Bandwidth2.ToString(CultureInfo.InvariantCulture)); + raw.Add(Eq.FrequencyCenter3.ToString(CultureInfo.InvariantCulture)); + raw.Add(Eq.Gain3.ToString(CultureInfo.InvariantCulture)); + raw.Add(Eq.Bandwidth3.ToString(CultureInfo.InvariantCulture)); + + raw.Add(Echo.WetDryMix.ToString(CultureInfo.InvariantCulture)); + raw.Add(Echo.Feedback.ToString(CultureInfo.InvariantCulture)); + raw.Add(Echo.Delay.ToString(CultureInfo.InvariantCulture)); + + raw.Add(Volume.ToString(CultureInfo.InvariantCulture)); + raw.Add(((Byte)Layers).ToString()); + + raw.Add(String.Join("|", FieldIDs.Select(x => x.ToString()).ToArray())); + raw.Add(String.Join("|", BattleIDs.Select(x => x.ToString()).ToArray())); + raw.Add(String.Join("|", BattleBgIDs.Select(x => x.ToString()).ToArray())); + raw.Add(String.Join("|", ResourceIDs.ToArray())); + raw.Add(Condition); + + return String.Join(";", raw.ToArray()); + } + } +} diff --git a/Assembly-CSharp/Global/Sound/SaXAudio/SdLibAPIWithSaXAudio.cs b/Assembly-CSharp/Global/Sound/SaXAudio/SdLibAPIWithSaXAudio.cs index cf1801560..09176b461 100644 --- a/Assembly-CSharp/Global/Sound/SaXAudio/SdLibAPIWithSaXAudio.cs +++ b/Assembly-CSharp/Global/Sound/SaXAudio/SdLibAPIWithSaXAudio.cs @@ -158,7 +158,7 @@ public override Int32 SdSoundSystem_CreateSound(Int32 bankID) break; } - AudioEffectManager.EffectPreset? preset = AudioEffectManager.GetPreset(data.Profile, busID); + EffectPreset preset = AudioEffectManager.GetPreset(data.Profile, busID); if (preset != null && busID != 0) { Log.Message($"[AudioEffectManager] Filtered '{data.Profile.ResourceID}' from bus {busID}"); @@ -178,7 +178,7 @@ public override Int32 SdSoundSystem_CreateSound(Int32 bankID) } if (preset != null) - AudioEffectManager.ApplyPresetOnSound(preset.Value, soundID, data.Profile.Name); + AudioEffectManager.ApplyPresetOnSound(preset, soundID, data.Profile.Name); if (soundID > 0) sounds[soundID] = bankID; diff --git a/Assembly-CSharp/SoundDebugRoom/SoundView.cs b/Assembly-CSharp/SoundDebugRoom/SoundView.cs index 0f6998155..3f7ee29ce 100644 --- a/Assembly-CSharp/SoundDebugRoom/SoundView.cs +++ b/Assembly-CSharp/SoundDebugRoom/SoundView.cs @@ -1094,7 +1094,7 @@ private void BuildPresetManager(Single width) if (GUILayout.Button("Enable All")) { presetChanged = true; - soundViewController.CurrentEffect.Effects = AudioEffectManager.EffectPreset.Effect.All; + soundViewController.CurrentEffect.Effects = EffectPreset.Effect.All; soundViewController.SetReverb(); soundViewController.SetEq(); From d1d9fcd6f3fd8b5e0c4dfdd440e212e8b0e9a413 Mon Sep 17 00:00:00 2001 From: DV666 Date: Sat, 11 Apr 2026 17:03:49 +0200 Subject: [PATCH 2/8] state.BuffersQueued condition in AudioVoice::Start --- SaXAudio/AudioVoice.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/SaXAudio/AudioVoice.cpp b/SaXAudio/AudioVoice.cpp index 7c258d266..10cfbfa11 100644 --- a/SaXAudio/AudioVoice.cpp +++ b/SaXAudio/AudioVoice.cpp @@ -30,9 +30,11 @@ namespace SaXAudio if (!SourceVoice || !BankData) return false; Log(BankID, VoiceID, "[Start] at: " + to_string(atSample) + (Looping ? " loop start: " + to_string(LoopStart) + " loop end: " + to_string(LoopEnd) : "")); - // Update position offset + XAUDIO2_VOICE_STATE state; + SourceVoice->GetState(&state); + m_positionOffset = atSample; - if (IsPlaying) + if (IsPlaying || state.BuffersQueued > 0) { if (flush) { @@ -40,8 +42,6 @@ namespace SaXAudio SourceVoice->Stop(); SourceVoice->FlushSourceBuffers(); } - XAUDIO2_VOICE_STATE state; - SourceVoice->GetState(&state); m_positionOffset -= state.SamplesPlayed; } From 956f705877749a36c5d5ab91ee7f03d0637587cf Mon Sep 17 00:00:00 2001 From: DV666 Date: Sat, 11 Apr 2026 17:12:14 +0200 Subject: [PATCH 3/8] Stop Fader when calling new sound in AudioVoice::Start --- SaXAudio/AudioVoice.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/SaXAudio/AudioVoice.cpp b/SaXAudio/AudioVoice.cpp index 10cfbfa11..97d0f37ef 100644 --- a/SaXAudio/AudioVoice.cpp +++ b/SaXAudio/AudioVoice.cpp @@ -45,6 +45,9 @@ namespace SaXAudio m_positionOffset -= state.SamplesPlayed; } + Fader::Instance.StopFade(m_volumeFadeID); + m_volumeFadeID = 0; + // Set up buffer Buffer.PlayBegin = atSample; Buffer.PlayLength = 0; // Plays until the end From 3ea4133d17f83933183c135527bab76faaf6ad26 Mon Sep 17 00:00:00 2001 From: DV666 Date: Sun, 12 Apr 2026 13:05:07 +0200 Subject: [PATCH 4/8] Fix the FolderPath when loading preset --- Assembly-CSharp/Global/Sound/SaXAudio/AudioEffectManager.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Assembly-CSharp/Global/Sound/SaXAudio/AudioEffectManager.cs b/Assembly-CSharp/Global/Sound/SaXAudio/AudioEffectManager.cs index d1bfd9884..fe8ff36c7 100644 --- a/Assembly-CSharp/Global/Sound/SaXAudio/AudioEffectManager.cs +++ b/Assembly-CSharp/Global/Sound/SaXAudio/AudioEffectManager.cs @@ -254,7 +254,7 @@ private static void Init() { if (folder.TryFindAssetInModOnDisc(FILENAME, out String fullPath)) { - var presets = LoadPresets($"{Path.GetDirectoryName(fullPath)}\\"); + var presets = LoadPresets(folder.FolderPath); foreach (var preset in presets.Values) { Boolean listed = false; From 37c49da476f34380ed4c2f31438756bced62406b Mon Sep 17 00:00:00 2001 From: DV666 Date: Sun, 12 Apr 2026 13:05:28 +0200 Subject: [PATCH 5/8] - --- Assembly-CSharp/Global/Sound/SaXAudio/AudioEffectManager.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Assembly-CSharp/Global/Sound/SaXAudio/AudioEffectManager.cs b/Assembly-CSharp/Global/Sound/SaXAudio/AudioEffectManager.cs index fe8ff36c7..055b7c82c 100644 --- a/Assembly-CSharp/Global/Sound/SaXAudio/AudioEffectManager.cs +++ b/Assembly-CSharp/Global/Sound/SaXAudio/AudioEffectManager.cs @@ -380,7 +380,7 @@ public static void SavePresets(SortedDictionary presets, S lines.Add("# ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------"); lines.Add("# Name;Effects;Reverb.WetDryMix;Reverb.ReflectionsDelay;Reverb.ReverbDelay;Reverb.RearDelay;Reverb.SideDelay;Reverb.PositionLeft;Reverb.PositionRight;Reverb.PositionMatrixLeft;Reverb.PositionMatrixRight;Reverb.EarlyDiffusion;Reverb.LateDiffusion;Reverb.LowEQGain;Reverb.LowEQCutoff;Reverb.HighEQGain;Reverb.HighEQCutoff;Reverb.RoomFilterFreq;Reverb.RoomFilterMain;Reverb.RoomFilterHF;Reverb.ReflectionsGain;Reverb.ReverbGain;Reverb.DecayTime;Reverb.Density;Reverb.RoomSize;Reverb.DisableLateField;Eq.FrequencyCenter0;Eq.Gain0;Eq.Bandwidth0;Eq.FrequencyCenter1;Eq.Gain1;Eq.Bandwidth1;Eq.FrequencyCenter2;Eq.Gain2;Eq.Bandwidth2;Eq.FrequencyCenter3;Eq.Gain3;Eq.Bandwidth3;Echo.WetDryMix;Echo.Feedback;Echo.Delay;Volume;Layers;FieldIDs;BattleIDs;BattleBgIDs;ResourceIDs;NCalcCondition"); - lines.Add("# String;Byte;Single;UInt32;Byte;Byte;Byte;Byte;Byte;Byte;Byte;Byte;Byte;Byte;Byte;Byte;Byte;Single;Single;Single;Single;Single;Single;Single;Single;Boolean;Single;Single;Single;Single;Single;Single;Single;Single;Single;Single;Single;Single;Single;Single;Single;Single;Byte;String;String;String;String"); + lines.Add("# String;Byte;Single;UInt32;Byte;Byte;Byte;Byte;Byte;Byte;Byte;Byte;Byte;Byte;Byte;Byte;Byte;Single;Single;Single;Single;Single;Single;Single;Single;Boolean;Single;Single;Single;Single;Single;Single;Single;Single;Single;Single;Single;Single;Single;Single;Single;Single;Byte;String;String;String;String;String"); lines.Add("# ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------"); foreach (EffectPreset effectPreset in presets.Values) From 07c9e6830ec2d1c8054c7b5dbdebfeb0f6492e41 Mon Sep 17 00:00:00 2001 From: DV666 Date: Sun, 12 Apr 2026 13:28:11 +0200 Subject: [PATCH 6/8] Cached expression for NCalc condition in EffectPreset.cs --- .../Sound/SaXAudio/AudioEffectManager.cs | 34 ++++++++++--------- .../Global/Sound/SaXAudio/EffectPreset.cs | 23 +++++++++++++ 2 files changed, 41 insertions(+), 16 deletions(-) diff --git a/Assembly-CSharp/Global/Sound/SaXAudio/AudioEffectManager.cs b/Assembly-CSharp/Global/Sound/SaXAudio/AudioEffectManager.cs index 055b7c82c..f24818f49 100644 --- a/Assembly-CSharp/Global/Sound/SaXAudio/AudioEffectManager.cs +++ b/Assembly-CSharp/Global/Sound/SaXAudio/AudioEffectManager.cs @@ -88,25 +88,29 @@ public static EffectPreset GetPreset(SoundProfile profile, Int32 bus) return null; EffectPreset preset = null; - if (resourceIDPresets.ContainsKey(profile.ResourceID)) - preset = resourceIDPresets[profile.ResourceID]; - // resourceIDs gets priority - if (preset != null && (String.IsNullOrEmpty(preset.Condition) || EvaluatePresetCondition(profile, preset.Condition, preset.Name))) - return preset; + // 1. resourceIDs gets priority + if (resourceIDPresets.TryGetValue(profile.ResourceID, out preset)) + if (String.IsNullOrEmpty(preset.Condition) || EvaluatePresetCondition(profile, preset.CompiledCondition, preset.Name)) + return preset; - // then conditional + // 2. then conditional for (Int32 i = 0; i < conditionalPresets.Count; i++) { preset = conditionalPresets[i]; - if ((preset.Layers == EffectPreset.Layer.None || preset.IsBusInLayers(bus)) && EvaluatePresetCondition(profile, preset.Condition, preset.Name)) + + Boolean isBusValid = preset.Layers == EffectPreset.Layer.None || preset.IsBusInLayers(bus); + if (isBusValid && EvaluatePresetCondition(profile, preset.CompiledCondition, preset.Name)) return preset; } - // then filter current preset + // 3. then filter current preset preset = currentPreset; - if (preset != null && preset.IsBusInLayers(bus) && !String.IsNullOrEmpty(preset.Condition) && !EvaluatePresetCondition(profile, preset.Condition, preset.Name)) - return new EffectPreset(); // empty preset because we want to exclude that particular sound + if (preset != null && preset.IsBusInLayers(bus)) + { + if (!String.IsNullOrEmpty(preset.Condition) && !EvaluatePresetCondition(profile, preset.CompiledCondition, preset.Name)) + return new EffectPreset(); // empty preset because we want to exclude that particular sound + } return null; } @@ -157,12 +161,10 @@ public static EffectPreset FindPreset(String presetName) return null; } - public static Boolean EvaluatePresetCondition(SoundProfile profile, String condition, String presetName) + public static Boolean EvaluatePresetCondition(SoundProfile profile, Expression c, String presetName) { - Expression c = new Expression(condition); - c.EvaluateFunction += NCalcUtility.commonNCalcFunctions; - c.EvaluateParameter += NCalcUtility.commonNCalcParameters; - c.EvaluateParameter += NCalcUtility.worldNCalcParameters; + if (c == null) return false; + c.Parameters["SoundIndex"] = profile.SoundIndex; c.Parameters["ResourceID"] = profile.ResourceID; c.Parameters["SoundProfileType"] = profile.SoundProfileType; @@ -181,7 +183,7 @@ public static Boolean EvaluatePresetCondition(SoundProfile profile, String condi } catch (Exception e) { - Log.Error($"[AudioEffectManager] Couldn't evaluate condition: '{condition.Trim()}' in preset '{presetName}'"); + Log.Error($"[AudioEffectManager] Couldn't evaluate condition in preset '{presetName}'"); Log.Error(e); } diff --git a/Assembly-CSharp/Global/Sound/SaXAudio/EffectPreset.cs b/Assembly-CSharp/Global/Sound/SaXAudio/EffectPreset.cs index ecf9ba7e4..cb25b1181 100644 --- a/Assembly-CSharp/Global/Sound/SaXAudio/EffectPreset.cs +++ b/Assembly-CSharp/Global/Sound/SaXAudio/EffectPreset.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using System.Globalization; using System.Linq; +using NCalc; namespace Global.Sound.SaXAudio { @@ -42,6 +43,8 @@ public enum Layer : Byte public HashSet ResourceIDs = new HashSet(); public String Condition = ""; + public Expression CompiledCondition { get; private set; } = null; + public EffectPreset() { } public Boolean IsBusInLayers(Int32 bus) @@ -71,6 +74,15 @@ public void RemoveIDs() ResourceIDs = null; } + public static Expression PrepareExpression(String code) + { + Expression e = new Expression(code); + e.EvaluateFunction += NCalcUtility.commonNCalcFunctions; + e.EvaluateParameter += NCalcUtility.commonNCalcParameters; + e.EvaluateParameter += NCalcUtility.worldNCalcParameters; + return e; + } + public void ParseEntry(String[] raw, CsvMetaData metadata) { if (raw.Length < 47) return; @@ -158,6 +170,17 @@ public void ParseEntry(String[] raw, CsvMetaData metadata) } Condition = raw[47]; + if (!String.IsNullOrEmpty(Condition)) + { + try + { + CompiledCondition = PrepareExpression(Condition); + } + catch (Exception e) + { + Memoria.Prime.Log.Warning($"[EffectPreset] Failed to compile condition '{Condition}' for preset '{Name}': {e.Message}"); + } + } } public void WriteEntry(CsvWriter writer, CsvMetaData metadata) From 316b2416d392beea5b12f88ad7975ebddee8c83c Mon Sep 17 00:00:00 2001 From: DV666 Date: Sun, 12 Apr 2026 14:02:48 +0200 Subject: [PATCH 7/8] Research bar for all sound type in Debug Menu. --- Assembly-CSharp/SoundDebugRoom/SoundView.cs | 79 ++++++-- .../SoundDebugRoom/SoundViewController.cs | 182 ++++++------------ 2 files changed, 126 insertions(+), 135 deletions(-) diff --git a/Assembly-CSharp/SoundDebugRoom/SoundView.cs b/Assembly-CSharp/SoundDebugRoom/SoundView.cs index 3f7ee29ce..790a8ad88 100644 --- a/Assembly-CSharp/SoundDebugRoom/SoundView.cs +++ b/Assembly-CSharp/SoundDebugRoom/SoundView.cs @@ -1,6 +1,7 @@ using Assets.Scripts.Common; using Assets.Sources.Scripts.Common; using Global.Sound.SaXAudio; +using Memoria.Data; using Memoria.Prime; using System; using System.Collections.Generic; @@ -359,6 +360,7 @@ private void BuildSelectorManager(Single width) if (GUILayout.Button("Sound Effect")) { soundViewController.SetActiveSoundType(SoundProfileType.SoundEffect); + searchString = ""; soundSelectorScrollPosition = Vector2.zero; } if (GUILayout.Button("BGM")) @@ -367,21 +369,25 @@ private void BuildSelectorManager(Single width) soundViewController.SetPanning(PanningPosition); soundViewController.SetPitch(PitchPosition); soundViewController.SetVolume(SoundVolume); + searchString = ""; soundSelectorScrollPosition = Vector2.zero; } if (GUILayout.Button("Song")) { soundViewController.SetActiveSoundType(SoundProfileType.Song); + searchString = ""; soundSelectorScrollPosition = Vector2.zero; } if (GUILayout.Button("Sfx Sound")) { soundViewController.SetActiveSoundType(SoundProfileType.Sfx); + searchString = ""; soundSelectorScrollPosition = Vector2.zero; } if (GUILayout.Button("Movie Audio")) { soundViewController.SetActiveSoundType(SoundProfileType.MovieAudio); + searchString = ""; soundSelectorScrollPosition = Vector2.zero; } if (soundViewController.ModVoiceDictionary.Count > 0) @@ -389,6 +395,7 @@ private void BuildSelectorManager(Single width) if (GUILayout.Button("Mod Voices")) { soundViewController.SetActiveSoundType(SoundProfileType.Voice); + searchString = ""; soundSelectorScrollPosition = Vector2.zero; } } @@ -402,12 +409,14 @@ private void BuildSelectorManager(Single width) if (GUILayout.Button("<")) { soundViewController.PreviousPlayList(); + searchString = ""; soundSelectorScrollPosition = Vector2.zero; } GUILayout.Label(soundViewController.PlaylistInfo, GUILayout.Width(width / 3f)); if (GUILayout.Button(">")) { soundViewController.NextPlayList(); + searchString = ""; soundSelectorScrollPosition = Vector2.zero; } } @@ -961,17 +970,52 @@ private Single BuildEffectParam(String name, Single value, Single min, Single ma private void BuildSfxSoundSelector() { + String search = GUILayout.TextField(searchString); + if (search != searchString) + { + searchString = search; + } + + if (sfxSoundUIState == 1) + { + if (GUILayout.Button("Back")) + { + sfxSoundUIState = 0; + searchString = ""; + } + } + soundSelectorScrollPosition = GUILayout.BeginScrollView(soundSelectorScrollPosition, GUILayout.ExpandHeight(true)); + if (sfxSoundUIState == 0) { foreach (Int32 num in soundViewController.AllSfxGroupSongIndex) { + String sfxName; + if (Enum.IsDefined(typeof(SpecialEffect), num)) + { + sfxName = ((SpecialEffect)num).ToString().Replace("__", " - ").Replace("_", " "); + sfxName = $"{sfxName} ({num})"; + } + else + { + sfxName = "EFX ID: " + num; + } + + if (!String.IsNullOrEmpty(searchString) && + !sfxName.ToLower().Contains(searchString.ToLower()) && + !num.ToString().Contains(searchString)) + { + continue; + } + GUILayout.BeginHorizontal(); { - if (GUILayout.Button("Load EFX ID: " + num)) + if (GUILayout.Button(sfxName)) { CurrentSpecialEffectID = num; sfxSoundUIState = 1; + searchString = ""; soundViewController.LoadSfxSoundGroup(num); } } @@ -983,17 +1027,20 @@ private void BuildSfxSoundSelector() List sfxSoundPlaylist = soundViewController.GetSfxSoundPlaylist(CurrentSpecialEffectID); GUILayout.BeginVertical(); { - if (GUILayout.Button("Back")) - { - sfxSoundUIState = 0; - } for (Int32 i = 0; i < sfxSoundPlaylist.Count; i++) { - if (i == SoundLib.GetResidentSfxSoundCount()) + if (i == SoundLib.GetResidentSfxSoundCount() && String.IsNullOrEmpty(searchString)) { GUILayout.Label("---- ---- ---- ----"); } + String text = sfxSoundPlaylist[i]; + + if (!String.IsNullOrEmpty(searchString) && !text.ToLower().Contains(searchString.ToLower())) + { + continue; + } + GUILayout.BeginHorizontal(); { if (GUILayout.Button(text)) @@ -1015,8 +1062,7 @@ private void BuildSfxSoundSelector() private void BuildVoiceSoundSelector() { - List voices = soundViewController.GetPlaylist(); - if (voices == null || voices.Count == 0) + if (!soundViewController.HasActiveVoiceMod) { soundSelectorScrollPosition = GUILayout.BeginScrollView(soundSelectorScrollPosition); foreach (String mod in soundViewController.ModVoiceDictionary.Keys) @@ -1026,24 +1072,28 @@ private void BuildVoiceSoundSelector() if (GUILayout.Button(mod)) { soundViewController.GenerateVoiceList(mod); + searchString = ""; } } GUILayout.EndHorizontal(); } + GUILayout.EndScrollView(); } else { String search = GUILayout.TextField(searchString); if (search != searchString) { - soundViewController.FilterVoiceList(search); + searchString = search; + soundViewController.FilterPlaylist(search); } - searchString = search; if (GUILayout.Button("Back")) { soundViewController.GenerateVoiceList(""); + searchString = ""; } + soundSelectorScrollPosition = GUILayout.BeginScrollView(soundSelectorScrollPosition); List playlist = soundViewController.GetPlaylist(); if (playlist != null) @@ -1058,12 +1108,19 @@ private void BuildVoiceSoundSelector() GUILayout.EndHorizontal(); } } + GUILayout.EndScrollView(); } - GUILayout.EndScrollView(); } private void BuildSoundSelector() { + String search = GUILayout.TextField(searchString); + if (search != searchString) + { + searchString = search; + soundViewController.FilterPlaylist(search); + } + soundSelectorScrollPosition = GUILayout.BeginScrollView(soundSelectorScrollPosition); List playlist = soundViewController.GetPlaylist(); if (playlist != null) diff --git a/Assembly-CSharp/SoundDebugRoom/SoundViewController.cs b/Assembly-CSharp/SoundDebugRoom/SoundViewController.cs index 9de672c6b..e5df6b8f5 100644 --- a/Assembly-CSharp/SoundDebugRoom/SoundViewController.cs +++ b/Assembly-CSharp/SoundDebugRoom/SoundViewController.cs @@ -45,6 +45,7 @@ public void SetActiveSoundType(SoundProfileType type) return; } currentPlaylist = 0; + currentFilter = ""; PlaylistInfo = ""; PlaylistDetail = ""; filteredPlaylist = null; @@ -411,6 +412,7 @@ private List SplitIntoNaturalParts(string input) public void GenerateVoiceList(String modLocation) { currentVoiceMod = modLocation; + currentFilter = ""; PlaylistInfo = ""; PlaylistDetail = ""; currentPlaylist = 0; @@ -418,21 +420,11 @@ public void GenerateVoiceList(String modLocation) GeneratePlaylistData(); } - public void FilterVoiceList(String filter) + public void FilterPlaylist(String filter) { - voiceFilter = filter; + currentFilter = filter; currentPlaylist = 0; - - if (!ModVoiceDictionary.ContainsKey(currentVoiceMod)) - return; - - filteredPlaylist = ModVoiceDictionary[currentVoiceMod]; - if (voiceFilter.Length > 0) - { - filteredPlaylist = filteredPlaylist.Where((p) => p.ResourceID.ToLower().Contains(voiceFilter.ToLower())).ToList(); - } - if (filteredPlaylist.Count == 0) - filteredPlaylist = ModVoiceDictionary[currentVoiceMod]; + filteredPlaylist = null; GeneratePlaylistData(); } @@ -491,34 +483,10 @@ public void ApplyEffectPreset(String name) public void NextPlayList() { - Int32 count = 0; - if (activeType == SoundProfileType.Music) - { - count = allMusicIndex.Count; - } - else if (activeType == SoundProfileType.SoundEffect) - { - count = allSoundEffectIndex.Count; - } - else if (activeType == SoundProfileType.Song) - { - count = allSongIndex.Count; - } - else if (activeType == SoundProfileType.Sfx) - { - SoundLib.LogWarning("Does not support SoundProfileType.Sfx"); - } - else if (activeType == SoundProfileType.MovieAudio) - { - count = allMovieAudioIndex.Count; - } - else if (activeType == SoundProfileType.Voice && filteredPlaylist != null) - { - count = filteredPlaylist.Count; - } - Int32 num = (Int32)((count % 100 != 0) ? 1 : 0); - Int32 num2 = count / 100 + num; - if (currentPlaylist < num2 - 1) + if (activeType == SoundProfileType.Sfx) return; + Int32 count = filteredPlaylist != null ? filteredPlaylist.Count : 0; + Int32 maxPage = Math.Max(0, (count - 1) / 100); + if (currentPlaylist < maxPage) { currentPlaylist++; GeneratePlaylistData(); @@ -591,96 +559,61 @@ private void GeneratePlaylistData() private List GetActiveTypePlaylist() { - List list = null; - if (activeType == SoundProfileType.Music) - { - list = allMusicIndex; - } - else if (activeType == SoundProfileType.SoundEffect) - { - list = allSoundEffectIndex; - } - else if (activeType == SoundProfileType.Song) - { - list = allSongIndex; - } - else if (activeType == SoundProfileType.Sfx) - { - SoundLib.LogWarning("GetPlaylist does not support SoundProfileType.Sfx"); - } - else if (activeType == SoundProfileType.MovieAudio) - { - list = allMovieAudioIndex; - } - else if (activeType == SoundProfileType.Voice && ModVoiceDictionary.ContainsKey(currentVoiceMod)) - { - if (filteredPlaylist == null) - FilterVoiceList(voiceFilter); + if (activeType == SoundProfileType.Sfx) return null; - Int32 index = Math.Min(currentPlaylist * 100, filteredPlaylist.Count - 1); - Int32 count = (100 + index) > filteredPlaylist.Count ? filteredPlaylist.Count - index : 100; - return filteredPlaylist.GetRange(index, count); - } - if (list == null) - { - return null; - } - if (currentPlaylist < 0) + if (filteredPlaylist == null) { - return null; - } - if (currentPlaylist > list.Count) - { - return null; - } - Int32 num = currentPlaylist * 100; - List list2 = new List(); - Int32 num2 = num; - while (num2 < num + 100 && num2 < list.Count) - { - list2.Add(SoundMetaData.GetSoundProfile(list[num2], activeType)); - num2++; + filteredPlaylist = new List(); + if (activeType == SoundProfileType.Voice) + { + if (ModVoiceDictionary.ContainsKey(currentVoiceMod)) + { + var voices = ModVoiceDictionary[currentVoiceMod]; + if (String.IsNullOrEmpty(currentFilter)) + filteredPlaylist.AddRange(voices); + else + filteredPlaylist.AddRange(voices.Where(p => p.ResourceID.ToLower().Contains(currentFilter.ToLower()))); + } + } + else + { + List list = null; + if (activeType == SoundProfileType.Music) list = allMusicIndex; + else if (activeType == SoundProfileType.SoundEffect) list = allSoundEffectIndex; + else if (activeType == SoundProfileType.Song) list = allSongIndex; + else if (activeType == SoundProfileType.MovieAudio) list = allMovieAudioIndex; + + if (list != null) + { + foreach (Int32 id in list) + { + SoundProfile p = SoundMetaData.GetSoundProfile(id, activeType); + if (String.IsNullOrEmpty(currentFilter) || + (p.Name != null && p.Name.ToLower().Contains(currentFilter.ToLower())) || + p.SoundIndex.ToString().Contains(currentFilter)) + { + filteredPlaylist.Add(p); + } + } + } + } } - return list2; + + if (filteredPlaylist.Count == 0) return new List(); + Int32 index = Math.Min(currentPlaylist * 100, Math.Max(0, filteredPlaylist.Count - 1)); + Int32 count = Math.Min(100, filteredPlaylist.Count - index); + return filteredPlaylist.GetRange(index, count); } private void SetPlaylistInfo(List allSoundProfile) { - Int32 count = 0; - if (activeType == SoundProfileType.Music) - { - count = allMusicIndex.Count; - } - else if (activeType == SoundProfileType.SoundEffect) - { - count = allSoundEffectIndex.Count; - } - else if (activeType == SoundProfileType.Song) - { - count = allSongIndex.Count; - } - else if (activeType == SoundProfileType.Sfx) - { - SoundLib.LogWarning("SetPlaylistInfo does not support SoundProfileType.Sfx"); - } - else if (activeType == SoundProfileType.MovieAudio) - { - count = allMovieAudioIndex.Count; - } - else if (activeType == SoundProfileType.Voice) - { - count = ModVoiceDictionary[currentVoiceMod].Where((p) => p.ResourceID.Contains(voiceFilter)).Count(); - } - PlaylistInfo = currentPlaylist + 1 + "/" + (count / 100 + 1); + if (activeType == SoundProfileType.Sfx) return; + + Int32 count = filteredPlaylist != null ? filteredPlaylist.Count : 0; + Int32 maxPage = Math.Max(1, (count - 1) / 100 + 1); + PlaylistInfo = (currentPlaylist + 1) + "/" + maxPage; Int32 num = currentPlaylist * 100; - PlaylistDetail = String.Concat(new Object[] - { - num + 1, - "-", - num + allSoundProfile.Count, - " of ", - count - }); + PlaylistDetail = $"{num + (count > 0 ? 1 : 0)}-{num + allSoundProfile.Count} of {count}"; } public List GetSfxSoundPlaylist(Int32 specialEffectID) @@ -783,8 +716,9 @@ public Boolean IsEffectVolumeEnabled public Dictionary> ModVoiceDictionary = new Dictionary>(); private String currentVoiceMod = ""; + public Boolean HasActiveVoiceMod => !String.IsNullOrEmpty(currentVoiceMod); - private String voiceFilter = ""; + public String currentFilter = ""; private SoundProfileType activeType = SoundProfileType.SoundEffect; From 341fd66addbcd67b7a08849cf3f0ebd1d1d07fab Mon Sep 17 00:00:00 2001 From: DV666 Date: Sun, 12 Apr 2026 15:26:29 +0200 Subject: [PATCH 8/8] Fix a critical issue on AudioEffectManager watcher (from Unity Debug) --- Assembly-CSharp/Global/Sound/SaXAudio/AudioEffectManager.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Assembly-CSharp/Global/Sound/SaXAudio/AudioEffectManager.cs b/Assembly-CSharp/Global/Sound/SaXAudio/AudioEffectManager.cs index f24818f49..093d0c796 100644 --- a/Assembly-CSharp/Global/Sound/SaXAudio/AudioEffectManager.cs +++ b/Assembly-CSharp/Global/Sound/SaXAudio/AudioEffectManager.cs @@ -297,7 +297,7 @@ private static void Init() if (watcher == null) { - watcher = new FileSystemWatcher("./", $"*{FILENAME}"); + watcher = new FileSystemWatcher("./", Path.GetFileName(FILENAME)); watcher.IncludeSubdirectories = true; watcher.NotifyFilter = NotifyFilters.LastWrite; watcher.Changed += (sender, e) =>