Decompiled source of ForgeAudio v0.1.0
ForgeAudio.dll
Decompiled 2 days ago
The result has been truncated due to the large size, download it to view full contents!
using System; using System.Buffers.Binary; using System.Collections.Concurrent; using System.Collections.Generic; using System.Diagnostics; using System.Globalization; using System.IO; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Runtime.Versioning; using System.Security; using System.Security.Cryptography; using System.Security.Permissions; using System.Text; using System.Text.Json; using System.Text.RegularExpressions; using System.Threading; using System.Threading.Tasks; using AIGraph; using AK; using Agents; using BepInEx; using BepInEx.Configuration; using BepInEx.Unity.IL2CPP; using Feedback; using ForgeCore; using Gear; using HarmonyLib; using Il2CppInterop.Runtime; using Il2CppInterop.Runtime.InteropTypes; using Il2CppInterop.Runtime.InteropTypes.Arrays; using Il2CppInterop.Runtime.Runtime; using Il2CppSystem.Collections.Generic; using Microsoft.CodeAnalysis; using NVorbis; using NVorbis.Contracts; using NVorbis.Ogg; using Player; using SNetwork; using UnityEngine; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: TargetFramework(".NETCoreApp,Version=v6.0", FrameworkDisplayName = ".NET 6.0")] [assembly: AssemblyCompany("ForgeAudio")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("0.1.0.0")] [assembly: AssemblyInformationalVersion("0.1.0+66ec6b43e1a1603bac84a4b9a226674300b2fc7d")] [assembly: AssemblyProduct("ForgeAudio")] [assembly: AssemblyTitle("ForgeAudio")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("0.1.0.0")] [module: UnverifiableCode] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)] internal sealed class NullableAttribute : Attribute { public readonly byte[] NullableFlags; public NullableAttribute(byte P_0) { NullableFlags = new byte[1] { P_0 }; } public NullableAttribute(byte[] P_0) { NullableFlags = P_0; } } [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)] internal sealed class NullableContextAttribute : Attribute { public readonly byte Flag; public NullableContextAttribute(byte P_0) { Flag = P_0; } } [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)] internal sealed class RefSafetyRulesAttribute : Attribute { public readonly int Version; public RefSafetyRulesAttribute(int P_0) { Version = P_0; } } } namespace ForgeAudio { internal sealed class Catalog { internal readonly Dictionary<string, CueDefinition> Cues = new Dictionary<string, CueDefinition>(StringComparer.OrdinalIgnoreCase); internal readonly Dictionary<string, BusDefinition> Buses = new Dictionary<string, BusDefinition>(StringComparer.OrdinalIgnoreCase); internal readonly Dictionary<string, AudioFailure> Failures = new Dictionary<string, AudioFailure>(StringComparer.OrdinalIgnoreCase); internal AudioFailure? Error; internal string[] Files => Cues.Values.SelectMany((CueDefinition cue) => cue.Clips).Distinct<string>(StringComparer.OrdinalIgnoreCase).OrderBy<string, string>((string file) => file, StringComparer.OrdinalIgnoreCase) .ToArray(); internal HashSet<string> DirectFiles => new HashSet<string>(Cues.Values.Where((CueDefinition cue) => cue.IsDirect).SelectMany((CueDefinition cue) => cue.Clips), StringComparer.OrdinalIgnoreCase); internal HashSet<string> MixedFiles => new HashSet<string>(Cues.Values.Where((CueDefinition cue) => !cue.IsDirect).SelectMany((CueDefinition cue) => cue.Clips), StringComparer.OrdinalIgnoreCase); } internal static class CueCatalog { internal const string SettingsFile = "cues.json"; internal const int MaxSettingsBytes = 1048576; private static readonly string[] OtherAudio = new string[9] { ".wav", ".mp3", ".flac", ".m4a", ".aac", ".opus", ".wma", ".aif", ".aiff" }; private static readonly string[] CueFields = CueSchema.Names(CueSchema.Cue.All); private static readonly string[] LayerFields = CueSchema.Names(CueSchema.Layer.All); private static readonly string[] SpaceFields = CueSchema.Names(CueSchema.Space.All); private static readonly string[] BusFields = CueSchema.Names(CueSchema.Bus.All); internal static string CueName(string relativeFile) { string text = PosixPath.ToPosix(relativeFile); return text.Substring(0, text.Length - Path.GetExtension(text).Length); } internal static bool IsOgg(string file) { return string.Equals(Path.GetExtension(file), ".ogg", StringComparison.OrdinalIgnoreCase); } internal static Catalog Build(IEnumerable<string> files, string? settings) { Catalog catalog = new Catalog(); Dictionary<string, string> dictionary = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase); List<string> list = new List<string>(); foreach (string file in files) { string text = PosixPath.ToPosix(file); if (IsOgg(text)) { dictionary[text] = text; } else if (Array.IndexOf(OtherAudio, Path.GetExtension(text).ToLowerInvariant()) >= 0) { list.Add(text); } } List<(string, JsonElement)> list2 = new List<(string, JsonElement)>(); JsonDocument jsonDocument = null; try { if (settings == null) { throw new FormatException("the folder has no cues.json: every cue plays on a bus and every bus names its category (" + AudioCategories.Known + "), so the cues must be listed there."); } if (settings.Length > 1048576) { throw new FormatException("cues.json exceeds 1 MiB."); } jsonDocument = JsonDocument.Parse(settings, new JsonDocumentOptions { MaxDepth = 12, CommentHandling = JsonCommentHandling.Skip, AllowTrailingCommas = true }); if (jsonDocument.RootElement.ValueKind != JsonValueKind.Object) { throw new FormatException("cues.json must be an object with \"buses\" and \"cues\"."); } foreach (JsonProperty item in jsonDocument.RootElement.EnumerateObject()) { string name = item.Name; if ((!(name == "buses") && !(name == "cues")) || 1 == 0) { throw new FormatException("cues.json has unknown section '" + item.Name + "' (known: buses, cues)."); } if (item.Value.ValueKind != JsonValueKind.Object) { throw new FormatException("cues.json section '" + item.Name + "' must be an object."); } HashSet<string> hashSet = new HashSet<string>(StringComparer.OrdinalIgnoreCase); foreach (JsonProperty item2 in item.Value.EnumerateObject()) { if (string.IsNullOrWhiteSpace(item2.Name)) { throw new FormatException("cues.json has an empty " + item.Name + " name."); } if (!hashSet.Add(item2.Name)) { throw new FormatException("cues.json names '" + item2.Name + "' twice in " + item.Name + "."); } if (item.Name == "buses") { catalog.Buses[item2.Name] = ParseBus(item2.Name, item2.Value); } else { list2.Add((item2.Name, item2.Value)); } } } HashSet<string> hashSet2 = new HashSet<string>(StringComparer.OrdinalIgnoreCase); foreach (var (text2, value) in list2) { try { CueDefinition cueDefinition = ParseCue(text2, value, dictionary, catalog.Buses); foreach (string clip in cueDefinition.Clips) { if (!string.Equals(CueName(clip), text2, StringComparison.OrdinalIgnoreCase)) { hashSet2.Add(clip); } } catalog.Cues[text2] = cueDefinition; catalog.Failures.Remove(text2); } catch (Exception ex) when (((ex is FormatException || ex is InvalidOperationException || ex is KeyNotFoundException) ? 1 : 0) != 0) { catalog.Failures[text2] = new AudioFailure(AudioError.InvalidSettings, "cues.json cue '" + text2 + "': " + ex.Message); } } foreach (string key2 in dictionary.Keys) { string text3 = CueName(key2); if (!hashSet2.Contains(key2) && !catalog.Cues.ContainsKey(text3) && !catalog.Failures.ContainsKey(text3)) { catalog.Failures[text3] = new AudioFailure(AudioError.InvalidSettings, key2 + " is not a cue in cues.json and no cue's layer uses it; name it there with its bus (\"" + text3 + "\": {\"bus\": \"...\"})."); } } foreach (string item3 in list) { string key = CueName(item3); if (!catalog.Cues.ContainsKey(key) && !catalog.Failures.ContainsKey(key)) { catalog.Failures[key] = new AudioFailure(AudioError.UnsupportedCodec, item3 + " is not Ogg Vorbis. Convert the folder: python convert_audio.py convert <source folder> <audio folder>"); } } } catch (Exception ex2) when (((ex2 is JsonException || ex2 is FormatException) ? 1 : 0) != 0) { catalog.Cues.Clear(); catalog.Error = new AudioFailure(AudioError.InvalidSettings, "cues.json: " + ex2.Message); } finally { jsonDocument?.Dispose(); } return catalog; } private static void CheckFields(JsonElement value, string[] known, string what) { if (value.ValueKind != JsonValueKind.Object) { throw new FormatException(what + " must be an object."); } HashSet<string> hashSet = new HashSet<string>(StringComparer.Ordinal); foreach (JsonProperty item in value.EnumerateObject()) { if (Array.IndexOf(known, item.Name) < 0) { throw new FormatException("unknown " + what + " setting '" + item.Name + "' (known: " + string.Join(", ", known) + ")."); } if (!hashSet.Add(item.Name)) { throw new FormatException(what + " setting '" + item.Name + "' appears twice."); } } } private static BusDefinition ParseBus(string name, JsonElement value) { try { CheckFields(value, BusFields, "bus"); BusDefinition busDefinition = new BusDefinition { Name = name, Category = CategoryOf(value, CueSchema.Bus.Category.Name, "category"), Gain = GainOf(value, CueSchema.Bus.Gain, CueSchema.Bus.GainDb, 1f), Seats = Integer(value, CueSchema.Bus.Seats, 0) }; if (value.TryGetProperty(CueSchema.Bus.Duck.Name, out var value2)) { CheckFields(value2, new string[2] { "categories", "db" }, "duck"); Field duckCategories = CueSchema.Bus.DuckCategories; if (!value2.TryGetProperty("categories", out var value3) || value3.ValueKind != JsonValueKind.Array || value3.GetArrayLength() < duckCategories.MinI || value3.GetArrayLength() > duckCategories.MaxI) { throw new FormatException("duck.categories must list " + duckCategories.MinI + " to " + duckCategories.MaxI + " categories (" + AudioCategories.Known + ")."); } busDefinition.Duck = (from t in value3.EnumerateArray() select CategoryOf(t, "duck.categories")).Distinct().ToArray(); AudioCategory[] duck = busDefinition.Duck; foreach (AudioCategory category in duck) { busDefinition.DuckMask |= AudioCategories.Bit(category); } busDefinition.DuckDb = Number(value2, CueSchema.Bus.DuckDb, busDefinition.DuckDb); } return busDefinition; } catch (FormatException ex) { throw new FormatException("bus '" + name + "': " + ex.Message); } } private static CueDefinition ParseCue(string name, JsonElement value, Dictionary<string, string> oggs, Dictionary<string, BusDefinition> buses) { CheckFields(value, CueFields, "cue"); CueDefinition cueDefinition = new CueDefinition { Name = name }; if (value.TryGetProperty("layers", out var value2)) { Field layers = CueSchema.Cue.Layers; if (value2.ValueKind != JsonValueKind.Array || value2.GetArrayLength() < layers.MinI || value2.GetArrayLength() > layers.MaxI) { throw new FormatException("layers must list " + layers.MinI + " to " + layers.MaxI + " layers."); } cueDefinition.Layers = (from layer in value2.EnumerateArray() select ParseLayer(layer, oggs)).ToArray(); } else { string text = oggs.Keys.FirstOrDefault((string file) => string.Equals(CueName(file), name, StringComparison.OrdinalIgnoreCase)) ?? throw new FormatException("there is no file " + name + ".ogg and no layers list."); cueDefinition.Layers = new LayerDefinition[1] { new LayerDefinition { Clips = new string[1] { text } } }; } (cueDefinition.GainMin, cueDefinition.Gain) = GainRange(value); if (value.TryGetProperty("pitch", out var value3)) { (cueDefinition.PitchMin, cueDefinition.PitchMax) = PitchRange(value3); } cueDefinition.Loop = Flag(value, CueSchema.Cue.Loop.Key, cueDefinition.Loop); cueDefinition.Priority = Integer(value, CueSchema.Cue.Priority, cueDefinition.Priority); cueDefinition.MaxVoices = Integer(value, CueSchema.Cue.MaxVoices, cueDefinition.MaxVoices); cueDefinition.MaxPerEmitter = Integer(value, CueSchema.Cue.MaxPerEmitter, cueDefinition.MaxVoices, cueDefinition.MaxVoices); cueDefinition.MinInterval = Number(value, CueSchema.Cue.MinInterval, cueDefinition.MinInterval); cueDefinition.Environment = Flag(value, CueSchema.Cue.Environment.Key, cueDefinition.Environment); cueDefinition.MaxSeconds = Number(value, CueSchema.Cue.MaxSeconds, 0f); if (!value.TryGetProperty(CueSchema.Cue.BusName.Key, out var value4)) { throw new FormatException("a cue needs a bus (the bus names its category: " + AudioCategories.Known + ")."); } string text2 = ((value4.ValueKind == JsonValueKind.String) ? value4.GetString().Trim() : ""); if (!buses.ContainsKey(text2)) { throw new FormatException("bus '" + text2 + "' is not defined in \"buses\"."); } cueDefinition.Bus = buses[text2].Name; if (value.TryGetProperty("place", out var value5)) { if (value5.ValueKind == JsonValueKind.String) { cueDefinition.SelfPlace = (cueDefinition.OtherPlace = PlaceOf(value5)); } else { CheckFields(value5, new string[2] { "self", "other" }, "place"); if (value5.TryGetProperty("self", out var value6)) { cueDefinition.SelfPlace = PlaceOf(value6); } if (value5.TryGetProperty("other", out var value7)) { cueDefinition.OtherPlace = PlaceOf(value7); } } } if (value.TryGetProperty("space", out var value8)) { cueDefinition.Space = ParseSpace(value8); } if (cueDefinition.Loop) { if (cueDefinition.Layers.Length != 1 || cueDefinition.Layers[0].OffsetMs != 0f || cueDefinition.Layers[0].RangeStart != 0f || cueDefinition.Layers[0].RangeEnd >= 0f || cueDefinition.Layers[0].FadeInMs != 0f || cueDefinition.Layers[0].FadeOutMs != 0f || cueDefinition.Layers[0].PitchMin != 1f || cueDefinition.Layers[0].PitchMax != 1f || cueDefinition.Layers[0].DistanceMin != 0f || !float.IsPositiveInfinity(cueDefinition.Layers[0].DistanceMax) || cueDefinition.Layers[0].LowpassHz != 0f) { throw new FormatException("a loop has exactly one layer with only clips and gain; its sample-exact loop point cannot be shaped."); } if (cueDefinition.MaxSeconds != 0f) { throw new FormatException("a loop has no maxSeconds."); } } return cueDefinition; } private static LayerDefinition ParseLayer(JsonElement value, Dictionary<string, string> oggs) { CheckFields(value, LayerFields, "layer"); LayerDefinition layerDefinition = new LayerDefinition(); Field clips = CueSchema.Layer.Clips; if (!value.TryGetProperty(clips.Key, out var value2) || value2.ValueKind != JsonValueKind.Array || value2.GetArrayLength() < clips.MinI || value2.GetArrayLength() > clips.MaxI) { throw new FormatException("a layer's clips must list " + clips.MinI + " to " + clips.MaxI + " .ogg files."); } List<string> list = new List<string>(); foreach (JsonElement item in value2.EnumerateArray()) { string text = ((item.ValueKind == JsonValueKind.String) ? item.GetString() : null); if (text == null) { throw new FormatException("clips entries must be file names."); } string text2 = PosixPath.ToPosix(text); if (!IsOgg(text2) || Path.IsPathRooted(text2) || ((ReadOnlySpan<string>)text2.Split('/')).Contains("..")) { throw new FormatException("clip '" + text2 + "' must be a .ogg file inside the audio folder."); } if (!oggs.TryGetValue(text2, out string value3)) { throw new FormatException("clip file '" + text2 + "' was not found."); } if (list.Contains<string>(value3, StringComparer.OrdinalIgnoreCase)) { throw new FormatException("clip '" + text2 + "' is listed twice."); } list.Add(value3); } layerDefinition.Clips = list.ToArray(); layerDefinition.OffsetMs = Number(value, CueSchema.Layer.OffsetMs, 0f); layerDefinition.Gain = GainOf(value, CueSchema.Layer.Gain, CueSchema.Layer.GainDb, 1f); if (value.TryGetProperty(CueSchema.Layer.Range.Key, out var value4)) { Field range = CueSchema.Layer.Range; if (value4.ValueKind != JsonValueKind.Array || value4.GetArrayLength() != 2 || value4[0].ValueKind != JsonValueKind.Number || value4[1].ValueKind != JsonValueKind.Number) { throw new FormatException("range must be [startSeconds, endSeconds]."); } layerDefinition.RangeStart = Range(value4[0].GetSingle(), range.MinF, range.MaxF, "range start"); layerDefinition.RangeEnd = Range(value4[1].GetSingle(), layerDefinition.RangeStart + 0.001f, range.MaxF, "range end"); } layerDefinition.FadeInMs = Number(value, CueSchema.Layer.FadeInMs, 0f); layerDefinition.FadeOutMs = Number(value, CueSchema.Layer.FadeOutMs, 0f); if (value.TryGetProperty(CueSchema.Layer.Pitch.Key, out var value5)) { (layerDefinition.PitchMin, layerDefinition.PitchMax) = PitchRange(value5); } if (value.TryGetProperty(CueSchema.Layer.Distance.Key, out var value6)) { CheckFields(value6, new string[2] { "min", "max" }, "distance"); layerDefinition.DistanceMin = Number(value6, CueSchema.Layer.DistanceMin, 0f); layerDefinition.DistanceMax = Number(value6, "max", layerDefinition.DistanceMin + 0.01f, CueSchema.Layer.DistanceMax.MaxF, float.PositiveInfinity); } layerDefinition.LowpassHz = Number(value, CueSchema.Layer.LowpassHz, 0f); return layerDefinition; } private static SpaceDefinition ParseSpace(JsonElement value) { CheckFields(value, SpaceFields, "space"); SpaceDefinition spaceDefinition = new SpaceDefinition { Near = Number(value, CueSchema.Space.Near, 2f), Far = Number(value, CueSchema.Space.Far, 30f), Blend = Number(value, CueSchema.Space.Blend, 1f), Spread = Number(value, CueSchema.Space.Spread, 0f), Doppler = Number(value, CueSchema.Space.Doppler, 0f), Cull = Flag(value, CueSchema.Space.Cull.Key, fallback: true) }; if (spaceDefinition.Far <= spaceDefinition.Near) { throw new FormatException("far must be greater than near."); } if (value.TryGetProperty(CueSchema.Space.Curve.Key, out var value2)) { if (value2.ValueKind != JsonValueKind.String) { if (value2.ValueKind == JsonValueKind.Array) { int arrayLength = value2.GetArrayLength(); if (arrayLength >= 2 && arrayLength <= 32) { List<float> list = new List<float>(); List<float> list2 = new List<float>(); foreach (JsonElement item in value2.EnumerateArray()) { if (item.ValueKind != JsonValueKind.Array || item.GetArrayLength() != 2 || item[0].ValueKind != JsonValueKind.Number || item[1].ValueKind != JsonValueKind.Number) { throw new FormatException("curve points must be [metres, gain] pairs."); } float num = Range(item[0].GetSingle(), 0f, 10000f, "curve metres"); if (list.Count > 0) { if (num <= list[list.Count - 1]) { throw new FormatException("curve metres must increase."); } } list.Add(num); list2.Add(Range(item[1].GetSingle(), 0f, 4f, "curve gain")); } spaceDefinition.Curve = new DistanceCurve { Shape = DistanceCurve.Kind.Points, Distances = list.ToArray(), Gains = list2.ToArray() }; goto IL_0298; } } throw new FormatException("curve must be \"linear\", \"log\" or 2 to 32 [metres, gain] points."); } DistanceCurve curve = spaceDefinition.Curve; string text = value2.GetString(); DistanceCurve.Kind shape; if (!(text == "linear")) { if (!(text == "log")) { throw new FormatException("curve must be \"linear\", \"log\" or a list of [metres, gain] points."); } shape = DistanceCurve.Kind.Log; } else { shape = DistanceCurve.Kind.Linear; } curve.Shape = shape; } goto IL_0298; IL_0298: return spaceDefinition; } private static (float, float) PitchRange(JsonElement pitch) { float minF = CueSchema.Cue.Pitch.MinF; float maxF = CueSchema.Cue.Pitch.MaxF; float min = minF; if (pitch.ValueKind == JsonValueKind.Number) { float num = Range(pitch.GetSingle(), min, maxF, "pitch"); return (num, num); } if (pitch.ValueKind == JsonValueKind.Array && pitch.GetArrayLength() == 2) { float num2 = Range(pitch[0].GetSingle(), min, maxF, "pitch"); return (num2, Range(pitch[1].GetSingle(), num2, maxF, "pitch")); } throw new FormatException("pitch must be a number or [min, max]."); } private static AudioCategory CategoryOf(JsonElement owner, string key, string what) { if (!owner.TryGetProperty(key, out var value)) { throw new FormatException("needs a " + what + " (" + AudioCategories.Known + ")."); } return CategoryOf(value, what); } private static AudioCategory CategoryOf(JsonElement value, string what) { if (value.ValueKind != JsonValueKind.String || !AudioCategories.TryParse(value.GetString(), out var category)) { throw new FormatException(what + " must be one of " + AudioCategories.Known + "."); } return category; } private static Place PlaceOf(JsonElement e) { if (e.ValueKind == JsonValueKind.String) { return e.GetString() switch { "auto" => Place.Auto, "2d" => Place.TwoD, "world" => Place.World, "follow" => Place.Follow, _ => throw new FormatException("place must be \"2d\", \"world\", \"follow\" or \"auto\"."), }; } throw new FormatException("place must be \"2d\", \"world\", \"follow\" or \"auto\"."); } private static float GainOf(JsonElement e, Field linear, Field decibels, float fallback) { JsonElement value; bool num = e.TryGetProperty(linear.Key, out value); bool flag = e.TryGetProperty(decibels.Key, out value); if (num && flag) { throw new FormatException(linear.Key + " and " + decibels.Key + " cannot both be set."); } if (!flag) { return Number(e, linear, fallback); } return MathF.Pow(10f, Number(e, decibels, 0f) / 20f); } private static (float Min, float Max) GainRange(JsonElement e) { JsonElement value; bool num = e.TryGetProperty("gain", out value); JsonElement value2; bool hasDb = e.TryGetProperty("gainDb", out value2); if (num && hasDb) { throw new FormatException("gain and gainDb cannot both be set."); } if (!num && !hasDb) { return (Min: 1f, Max: 1f); } Field obj = (hasDb ? CueSchema.Cue.GainDb : CueSchema.Cue.Gain); JsonElement jsonElement = (hasDb ? value2 : value); string key = obj.Key; float minF = obj.MinF; float maxF = obj.MaxF; float min = minF; string text = key; JsonElement jsonElement2 = jsonElement; if (jsonElement2.ValueKind == JsonValueKind.Number) { float num2 = From(Range(jsonElement2.GetSingle(), min, maxF, text)); return (Min: num2, Max: num2); } if (jsonElement2.ValueKind == JsonValueKind.Array && jsonElement2.GetArrayLength() == 2 && jsonElement2[0].ValueKind == JsonValueKind.Number && jsonElement2[1].ValueKind == JsonValueKind.Number) { float num3 = Range(jsonElement2[0].GetSingle(), min, maxF, text); return (Min: From(num3), Max: From(Range(jsonElement2[1].GetSingle(), num3, maxF, text))); } throw new FormatException(text + " must be a number or [min, max]."); float From(float v) { if (!hasDb) { return v; } return MathF.Pow(10f, v / 20f); } } private static bool Flag(JsonElement e, string key, bool fallback) { if (!e.TryGetProperty(key, out var value)) { return fallback; } return value.ValueKind switch { JsonValueKind.True => true, JsonValueKind.False => false, _ => throw new FormatException(key + " must be true or false."), }; } private static int Integer(JsonElement e, Field field, int fallback) { return Integer(e, field.Key, field.MinI, field.MaxI, fallback); } private static int Integer(JsonElement e, Field field, int max, int fallback) { return Integer(e, field.Key, field.MinI, max, fallback); } private static float Number(JsonElement e, Field field, float fallback) { return Number(e, field.Key, field.MinF, field.MaxF, fallback); } private static int Integer(JsonElement e, string key, int min, int max, int fallback) { if (!e.TryGetProperty(key, out var value)) { return fallback; } if (value.ValueKind != JsonValueKind.Number || !value.TryGetInt32(out var value2) || value2 < min || value2 > max) { throw new FormatException(key + " must be a whole number from " + min + " to " + max + "."); } return value2; } private static float Number(JsonElement e, string key, float min, float max, float fallback) { if (!e.TryGetProperty(key, out var value)) { return fallback; } if (value.ValueKind != JsonValueKind.Number) { throw new FormatException(key + " must be a number."); } return Range(value.GetSingle(), min, max, key); } private static float Range(float n, float min, float max, string key) { if (!float.IsFinite(n) || !(n >= min) || !(n <= max)) { throw new FormatException(key + " must be from " + min + " to " + max + "."); } return n; } } internal readonly record struct OggInfo(int Channels, int SampleRate, long Frames) { internal long DecodedBytes => Frames * Channels * 4; [CompilerGenerated] private bool PrintMembers(StringBuilder builder) { builder.Append("Channels = "); builder.Append(Channels.ToString()); builder.Append(", SampleRate = "); builder.Append(SampleRate.ToString()); builder.Append(", Frames = "); builder.Append(Frames.ToString()); return true; } } internal static class AssetValidation { internal const int MaxFileBytes = 67108864; internal const long MaxDecodedBytes = 536870912L; internal const long MaxCompressedCache = 268435456L; internal static Result<OggInfo> Ogg(byte[] bytes) { if (bytes.Length > 67108864) { return Result<OggInfo>.Fail(AudioError.AssetLimit, "file exceeds 64 MiB."); } if (bytes.Length < 58 || bytes[0] != 79 || bytes[1] != 103 || bytes[2] != 103 || bytes[3] != 83 || bytes[4] != 0 || bytes[26] < 1) { return Result<OggInfo>.Fail(AudioError.UnsupportedCodec, "not an Ogg file (convert it with convert_audio.py)."); } int num = 27 + bytes[26]; if (num + 30 > bytes.Length || bytes[num] != 1 || Encoding.ASCII.GetString(bytes, num + 1, 6) != "vorbis") { return Result<OggInfo>.Fail(AudioError.UnsupportedCodec, "Ogg but not Vorbis (Opus is not supported)."); } byte b = bytes[num + 11]; uint num2 = BinaryPrimitives.ReadUInt32LittleEndian(bytes.AsSpan(num + 12)); bool flag = ((b < 1 || b > 2) ? true : false); bool flag2 = flag; if (!flag2) { bool flag3 = ((num2 < 8000 || num2 > 192000) ? true : false); flag2 = flag3; } if (flag2 || BinaryPrimitives.ReadUInt32LittleEndian(bytes.AsSpan(num + 7)) != 0 || bytes[num + 29] != 1) { return Result<OggInfo>.Fail(AudioError.UnsupportedCodec, "expected mono or stereo Vorbis at 8 to 192 kHz."); } uint num3 = BinaryPrimitives.ReadUInt32LittleEndian(bytes.AsSpan(14)); int num4 = 0; uint num5 = 0u; ulong num6 = 0uL; bool flag4 = false; while (num4 < bytes.Length) { if (flag4 || bytes.Length - num4 < 27 || bytes[num4] != 79 || bytes[num4 + 1] != 103 || bytes[num4 + 2] != 103 || bytes[num4 + 3] != 83 || bytes[num4 + 4] != 0 || BinaryPrimitives.ReadUInt32LittleEndian(bytes.AsSpan(num4 + 14)) != num3 || BinaryPrimitives.ReadUInt32LittleEndian(bytes.AsSpan(num4 + 18)) != num5++) { return Result<OggInfo>.Fail(AudioError.UnsupportedCodec, "invalid or chained Ogg pages (one Vorbis stream is required)."); } byte b2 = bytes[num4 + 5]; if ((num4 == 0 && b2 != 2 && b2 != 6) || (num4 != 0 && (b2 & 2) != 0)) { return Result<OggInfo>.Fail(AudioError.UnsupportedCodec, "invalid Ogg beginning-of-stream flag."); } byte b3 = bytes[num4 + 26]; int num7 = num4 + 27 + b3; if (num7 > bytes.Length) { return Result<OggInfo>.Fail(AudioError.UnsupportedCodec, "truncated Ogg segment table."); } int num8 = 0; for (int i = 0; i < b3; i++) { num8 += bytes[num4 + 27 + i]; } if (num8 > bytes.Length - num7 || (num4 == 0 && num8 < 30)) { return Result<OggInfo>.Fail(AudioError.UnsupportedCodec, "truncated Ogg page."); } ulong num9 = BinaryPrimitives.ReadUInt64LittleEndian(bytes.AsSpan(num4 + 6)); if (num9 != ulong.MaxValue) { if (num9 < num6) { return Result<OggInfo>.Fail(AudioError.UnsupportedCodec, "non-monotonic Vorbis sample positions."); } num6 = num9; if (num6 > (ulong)((long)num2 * 3600L) || num6 > 536870912uL / (ulong)b / 4) { return Result<OggInfo>.Fail(AudioError.AssetLimit, "longer than one hour or over 512 MiB of decoded audio."); } } flag4 = (b2 & 4) != 0; num4 = num7 + num8; } if (!flag4 || num6 == 0L) { return Result<OggInfo>.Fail(AudioError.UnsupportedCodec, "Ogg stream is truncated (no final page)."); } return Result<OggInfo>.Ok(new OggInfo(b, (int)num2, (long)num6)); } } internal sealed class ClaimBook<T> where T : class { internal sealed class Entry { internal readonly T Owner; internal readonly int Kinds; internal readonly int Priority; internal readonly long Order; internal IntPtr[] Pointers = Array.Empty<IntPtr>(); internal bool Live; internal Entry(T owner, int kinds, int priority, long order) { Owner = owner; Kinds = kinds; Priority = priority; Order = order; } } private readonly Dictionary<IntPtr, List<Entry>> _byPointer = new Dictionary<IntPtr, List<Entry>>(); private readonly List<Entry> _live = new List<Entry>(); private long _order; internal int Count => _live.Count; internal Entry Add(T owner, IntPtr[] pointers, int kinds, int priority) { Entry entry = new Entry(owner, kinds, priority, ++_order) { Live = true }; _live.Add(entry); Index(entry, pointers); return entry; } internal void Reset(Entry entry, IntPtr[] pointers) { if (entry.Live) { Unindex(entry); Index(entry, pointers); } } internal bool Remove(Entry entry) { if (!entry.Live) { return false; } entry.Live = false; Unindex(entry); _live.Remove(entry); return true; } internal void Clear() { foreach (Entry item in _live) { item.Live = false; item.Pointers = Array.Empty<IntPtr>(); } _live.Clear(); _byPointer.Clear(); } internal T? Find(IntPtr pointer, int kind) { if (_live.Count == 0 || pointer == IntPtr.Zero || !_byPointer.TryGetValue(pointer, out List<Entry> value)) { return null; } for (int i = 0; i < value.Count; i++) { if ((value[i].Kinds & kind) != 0) { return value[i].Owner; } } return null; } private void Index(Entry entry, IntPtr[] pointers) { entry.Pointers = pointers; foreach (IntPtr intPtr in pointers) { if (intPtr == IntPtr.Zero) { continue; } if (!_byPointer.TryGetValue(intPtr, out List<Entry> value)) { value = (_byPointer[intPtr] = new List<Entry>(1)); } if (!value.Contains(entry)) { int j; for (j = 0; j < value.Count && (value[j].Priority > entry.Priority || (value[j].Priority == entry.Priority && value[j].Order > entry.Order)); j++) { } value.Insert(j, entry); } } } private void Unindex(Entry entry) { IntPtr[] pointers = entry.Pointers; foreach (IntPtr key in pointers) { if (_byPointer.TryGetValue(key, out List<Entry> value)) { value.Remove(entry); if (value.Count == 0) { _byPointer.Remove(key); } } } entry.Pointers = Array.Empty<IntPtr>(); } } internal enum ControlRoute { Run, Queue, Drop } internal static class ControlRouting { internal static ControlRoute Decide(bool ready, bool onMainThread) { if (ready) { if (!onMainThread) { return ControlRoute.Queue; } return ControlRoute.Run; } return ControlRoute.Drop; } } internal sealed record MixedAudio(float[] Samples, int Channels, int SampleRate) { internal int Frames => Samples.Length / Channels; internal double Seconds => (double)Frames / (double)SampleRate; [CompilerGenerated] private bool PrintMembers(StringBuilder builder) { RuntimeHelpers.EnsureSufficientExecutionStack(); builder.Append("Samples = "); builder.Append(Samples); builder.Append(", Channels = "); builder.Append(Channels.ToString()); builder.Append(", SampleRate = "); builder.Append(SampleRate.ToString()); return true; } } internal readonly record struct MixProfile(float Min, float Max, int Mask) { internal bool Silent => Mask == 0; [CompilerGenerated] private bool PrintMembers(StringBuilder builder) { builder.Append("Min = "); builder.Append(Min.ToString()); builder.Append(", Max = "); builder.Append(Max.ToString()); builder.Append(", Mask = "); builder.Append(Mask.ToString()); return true; } } internal static class CueMix { internal const int MaxVariants = 16; internal const double MaxMixSeconds = 60.0; internal const float CapFadeRatio = 0.12f; internal const float CapFadeMin = 0.04f; internal const float CapFadeMax = 0.15f; internal static MixProfile[] Profiles(CueDefinition cue) { SortedSet<float> sortedSet = new SortedSet<float> { 0f }; LayerDefinition[] layers = cue.Layers; foreach (LayerDefinition layerDefinition in layers) { if (layerDefinition.DistanceMin > 0f) { sortedSet.Add(layerDefinition.DistanceMin); } if (float.IsFinite(layerDefinition.DistanceMax)) { sortedSet.Add(layerDefinition.DistanceMax); } } float[] array = sortedSet.ToArray(); List<MixProfile> list = new List<MixProfile>(); for (int j = 0; j < array.Length; j++) { int num = 0; for (int k = 0; k < cue.Layers.Length; k++) { if (cue.Layers[k].DistanceMin <= array[j] && array[j] < cue.Layers[k].DistanceMax) { num |= 1 << k; } } float max = ((j + 1 < array.Length) ? array[j + 1] : float.PositiveInfinity); if (list.Count > 0) { if (list[list.Count - 1].Mask == num) { list[list.Count - 1] = list[list.Count - 1]with { Max = max }; continue; } } list.Add(new MixProfile(array[j], max, num)); } return list.ToArray(); } internal static int ProfileAt(MixProfile[] profiles, float distance) { for (int i = 0; i < profiles.Length; i++) { if (distance < profiles[i].Max) { return i; } } return profiles.Length - 1; } internal static int Variants(CueDefinition cue) { return Math.Min(16, cue.Layers.Max((LayerDefinition layer) => layer.Clips.Length)); } internal static float LayerPitch(CueDefinition cue, LayerDefinition layer, int variant, int layerIndex) { if (layer.PitchMin != layer.PitchMax) { return layer.PitchMin + (layer.PitchMax - layer.PitchMin) * Unit(cue.Name, variant, layerIndex); } return layer.PitchMin; } private static float Unit(string name, int variant, int layer) { uint num = 2166136261u; foreach (char c in name) { num = (num ^ c) * 16777619; } num = (num ^ (uint)variant) * 16777619; num = (num ^ (uint)layer) * 16777619; num ^= num >> 15; num *= 2246822519u; num ^= num >> 13; return (float)(num & 0xFFFFFF) / 16777216f; } internal static MixedAudio? Render(CueDefinition cue, MixProfile profile, int variant, Func<string, DecodedAudio> source) { if (profile.Silent) { return null; } int sampleRate = source(cue.Layers[0].Clips[0]).SampleRate; int channels = ((!cue.Clips.Any((string file) => source(file).Channels > 1)) ? 1 : 2); List<(int, float[])> list = new List<(int, float[])>(); long frames = 0L; for (int num = 0; num < cue.Layers.Length; num++) { if ((profile.Mask & (1 << num)) != 0) { LayerDefinition layerDefinition = cue.Layers[num]; float[] array = RenderLayer(cue, layerDefinition, num, variant, source(layerDefinition.Clips[variant % layerDefinition.Clips.Length]), sampleRate, channels); int num2 = (int)Math.Round((double)(layerDefinition.OffsetMs * (float)sampleRate) / 1000.0); list.Add((num2, array)); frames = Math.Max(frames, num2 + (long)array.Length / (long)channels); } } if (cue.MaxSeconds > 0f) { frames = Math.Min(frames, (long)Math.Round(cue.MaxSeconds * (float)sampleRate)); } if (frames <= 0 || (double)frames > 60.0 * (double)sampleRate) { throw new InvalidDataException("the mix is empty or longer than " + 60.0 + " seconds."); } float[] array2 = new float[frames * channels]; foreach (var item3 in list) { int item = item3.Item1; float[] item2 = item3.Item2; int num3 = (int)Math.Min(item2.Length, (frames - item) * channels); for (int num4 = 0; num4 < num3; num4++) { array2[item * channels + num4] += item2[num4]; } } if (cue.MaxSeconds > 0f && list.Any<(int, float[])>(((int Offset, float[] Samples) part) => part.Offset + (long)part.Samples.Length / (long)channels > frames)) { int num5 = (int)Math.Min(frames / 2, Math.Round(Math.Clamp(cue.MaxSeconds * 0.12f, 0.04f, 0.15f) * (float)sampleRate)); for (int num6 = 0; num6 < num5; num6++) { float num7 = (float)(num5 - 1 - num6) / (float)Math.Max(1, num5 - 1); for (int num8 = 0; num8 < channels; num8++) { array2[(frames - num5 + num6) * channels + num8] *= num7; } } } for (int num9 = 0; num9 < array2.Length; num9++) { array2[num9] = Math.Clamp(array2[num9], -1f, 1f); } return new MixedAudio(array2, channels, sampleRate); } private static float[] RenderLayer(CueDefinition cue, LayerDefinition layer, int index, int variant, DecodedAudio clip, int rate, int channels) { int frames = clip.Frames; int num = Math.Min(frames, (int)Math.Round(layer.RangeStart * (float)clip.SampleRate)); int num2 = ((layer.RangeEnd < 0f) ? frames : Math.Min(frames, (int)Math.Round(layer.RangeEnd * (float)clip.SampleRate))); if (num >= num2) { throw new InvalidDataException("layer range is outside the clip (" + ((double)frames / (double)clip.SampleRate).ToString("F3", CultureInfo.InvariantCulture) + " s long)."); } double num3 = (double)clip.SampleRate / (double)rate * (double)LayerPitch(cue, layer, variant, index); int num4 = (int)Math.Floor((double)(num2 - num - 1) / num3) + 1; float[] array = new float[num4 * channels]; for (int i = 0; i < num4; i++) { double num5 = (double)num + (double)i * num3; int num6 = (int)num5; float num7 = (float)(num5 - (double)num6); int num8 = Math.Min(num6 + 1, num2 - 1); for (int j = 0; j < channels; j++) { int num9 = ((clip.Channels != 1) ? j : 0); float num10 = clip.Samples[num6 * clip.Channels + num9]; float num11 = clip.Samples[num8 * clip.Channels + num9]; array[i * channels + j] = num10 + (num11 - num10) * num7; } } if (layer.LowpassHz > 0f) { float num12 = (float)Math.Exp(Math.PI * -2.0 * (double)layer.LowpassHz / (double)rate); for (int k = 0; k < channels; k++) { float num13 = 0f; float num14 = 0f; for (int l = 0; l < num4; l++) { num13 = array[l * channels + k] * (1f - num12) + num13 * num12; num14 = (array[l * channels + k] = num13 * (1f - num12) + num14 * num12); } } } int num15 = Math.Min(num4, (int)Math.Round((double)(layer.FadeInMs * (float)rate) / 1000.0)); int num16 = Math.Min(num4, (int)Math.Round((double)(layer.FadeOutMs * (float)rate) / 1000.0)); for (int m = 0; m < num4; m++) { float num17 = layer.Gain; if (m < num15) { num17 *= (float)m / (float)Math.Max(1, num15); } int num18 = num4 - 1 - m; if (num18 < num16) { num17 *= (float)num18 / (float)Math.Max(1, num16); } if (num17 != 1f) { for (int n = 0; n < channels; n++) { array[m * channels + n] *= num17; } } } return array; } internal static float Loudness(CueDefinition cue, float busGain, float distance) { return cue.Gain * busGain * cue.Space.Attenuation(distance); } } internal enum AudioCategory { Ui, Weapon, Explosion, Debris, Ambience, Voice, Vehicle, Device, Music } internal static class AudioCategories { internal const int Count = 9; internal static readonly string[] Names = new string[9] { "ui", "weapon", "explosion", "debris", "ambience", "voice", "vehicle", "device", "music" }; internal static readonly int[] Limits = new int[9] { 8, 16, 10, 12, 8, 4, 48, 10, 2 }; internal static readonly string[] Meanings = new string[9] { "menus, prompts, confirmations and warnings", "gunfire, reloads and weapon handling", "blasts and impacts", "falling debris, shrapnel and settling rubble", "beds, hums and room tones", "speech and radio voices", "suits, drop pods and other vehicles: steps, servos, engines", "machines in the world: beacons, turrets, supply pods, terminals", "music" }; internal static string Known => string.Join(", ", Names); internal static string Name(AudioCategory category) { return Names[(int)category]; } internal static int Bit(AudioCategory category) { return 1 << (int)category; } internal static bool TryParse(string? name, out AudioCategory category) { int num = ((name == null) ? (-1) : Array.IndexOf<string>(Names, name)); category = ((num >= 0) ? ((AudioCategory)num) : AudioCategory.Ui); return num >= 0; } } internal enum Place { Auto, TwoD, World, Follow } internal sealed class BusDefinition { internal string Name = ""; internal AudioCategory Category; internal float Gain = 1f; internal int Seats; internal AudioCategory[] Duck = Array.Empty<AudioCategory>(); internal float DuckDb = 6f; internal int DuckMask; } internal sealed class DistanceCurve { internal enum Kind { Linear, Log, Points } internal Kind Shape; internal float[] Distances = Array.Empty<float>(); internal float[] Gains = Array.Empty<float>(); internal float Evaluate(float distance, float near, float far, bool cull) { if (cull && distance >= far) { return 0f; } if (distance <= near) { if (Shape != Kind.Points) { return 1f; } return PointGain(distance); } switch (Shape) { case Kind.Log: { float num = near / far; return Math.Clamp((near / distance - num) / (1f - num), 0f, 1f); } case Kind.Points: return PointGain(distance); default: if (!(distance >= far)) { return 1f - (distance - near) / (far - near); } return 0f; } } private float PointGain(float distance) { if (distance <= Distances[0]) { return Gains[0]; } for (int i = 1; i < Distances.Length; i++) { if (!(distance > Distances[i])) { float num = (distance - Distances[i - 1]) / (Distances[i] - Distances[i - 1]); return Gains[i - 1] + (Gains[i] - Gains[i - 1]) * num; } } return Gains[^1]; } } internal sealed class SpaceDefinition { internal float Near = 2f; internal float Far = 30f; internal DistanceCurve Curve = new DistanceCurve(); internal float Blend = 1f; internal float Spread; internal float Doppler; internal bool Cull = true; internal float Attenuation(float distance) { float num = Curve.Evaluate(distance, Near, Far, Cull); if (Cull && distance >= Far) { return 0f; } return 1f + (num - 1f) * Blend; } } internal sealed class LayerDefinition { internal string[] Clips = Array.Empty<string>(); internal float OffsetMs; internal float Gain = 1f; internal float RangeStart; internal float RangeEnd = -1f; internal float FadeInMs; internal float FadeOutMs; internal float PitchMin = 1f; internal float PitchMax = 1f; internal float DistanceMin; internal float DistanceMax = float.PositiveInfinity; internal float LowpassHz; internal bool IsPlain { get { if (OffsetMs == 0f && Gain == 1f && RangeStart == 0f && RangeEnd < 0f && FadeInMs == 0f && FadeOutMs == 0f && PitchMin == 1f && PitchMax == 1f && DistanceMin == 0f && float.IsPositiveInfinity(DistanceMax)) { return LowpassHz == 0f; } return false; } } } internal sealed class CueDefinition { internal string Name = ""; internal LayerDefinition[] Layers = Array.Empty<LayerDefinition>(); internal float Gain = 1f; internal float GainMin = 1f; internal float PitchMin = 1f; internal float PitchMax = 1f; internal bool Loop; internal int Priority = 100; internal int MaxVoices = 8; internal int MaxPerEmitter = 8; internal float MinInterval; internal bool Environment = true; internal string Bus = ""; internal float MaxSeconds; internal Place SelfPlace; internal Place OtherPlace; internal SpaceDefinition Space = new SpaceDefinition(); internal IEnumerable<string> Clips => Layers.SelectMany((LayerDefinition layer) => layer.Clips).Distinct<string>(StringComparer.OrdinalIgnoreCase); internal bool IsDirect { get { if (Layers.Length == 1 && Layers[0].IsPlain) { return MaxSeconds == 0f; } return false; } } } internal enum FieldKind { Integer, Number, Range, Flag, Choice, Name, List, Group } internal sealed record Field(string Name, FieldKind Kind, string Summary, double Min = 0.0, double Max = 0.0, object? Default = null, string[]? Values = null, string Unit = "", bool Required = false) { internal string Key => Name.Substring(Name.LastIndexOf('.') + 1); internal float MinF => (float)Min; internal float MaxF => (float)Max; internal int MinI => (int)Min; internal int MaxI => (int)Max; [CompilerGenerated] private bool PrintMembers(StringBuilder builder) { RuntimeHelpers.EnsureSufficientExecutionStack(); builder.Append("Name = "); builder.Append((object?)Name); builder.Append(", Kind = "); builder.Append(Kind.ToString()); builder.Append(", Summary = "); builder.Append((object?)Summary); builder.Append(", Min = "); builder.Append(Min.ToString()); builder.Append(", Max = "); builder.Append(Max.ToString()); builder.Append(", Default = "); builder.Append(Default); builder.Append(", Values = "); builder.Append(Values); builder.Append(", Unit = "); builder.Append((object?)Unit); builder.Append(", Required = "); builder.Append(Required.ToString()); return true; } } internal static class CueSchema { internal static class Bus { internal static readonly Field Category = new Field("category", FieldKind.Choice, "The global category the bus belongs to; all mods share each category's voice limit.", 0.0, 0.0, null, AudioCategories.Names, "", Required: true); internal static readonly Field Gain = new Field("gain", FieldKind.Number, "Volume of every cue on the bus, linear. Use either gain or gainDb.", 0.0, 4.0, 1f); internal static readonly Field GainDb = new Field("gainDb", FieldKind.Number, "Volume of every cue on the bus, in decibels. Use either gain or gainDb.", -60.0, 12.0, null, null, "dB"); internal static readonly Field Seats = new Field("seats", FieldKind.Integer, "How many sounds of the bus may play at once from one emitter; the weakest are faded out for a stronger newcomer.", 1.0, 64.0, 0, null, "sounds"); internal static readonly Field Duck = new Field("duck", FieldKind.Group, "Lowers other categories while a sound of this bus plays."); internal static readonly Field DuckCategories = new Field("duck.categories", FieldKind.List, "The categories to lower.", 1.0, 9.0, null, AudioCategories.Names, "", Required: true); internal static readonly Field DuckDb = new Field("duck.db", FieldKind.Number, "How far to lower them.", 0.5, 48.0, 6f, null, "dB"); internal static readonly Field[] All = new Field[7] { Category, Gain, GainDb, Seats, Duck, DuckCategories, DuckDb }; } internal static class Cue { internal static readonly Field BusName = new Field("bus", FieldKind.Name, "The bus the cue plays on; it must be defined in \"buses\". The bus decides the category.", 0.0, 0.0, null, null, "", Required: true); internal static readonly Field Layers = new Field("layers", FieldKind.List, "The layers mixed into the cue. Without it the cue plays the .ogg file of the same name.", 1.0, 8.0); internal static readonly Field Gain = new Field("gain", FieldKind.Range, "Volume, linear; a [min, max] pair lets each play pick between them. Use either gain or gainDb.", 0.0, 4.0, 1f); internal static readonly Field GainDb = new Field("gainDb", FieldKind.Range, "Volume in decibels; a [min, max] pair lets each play pick between them.", -60.0, 12.0, null, null, "dB"); internal static readonly Field Pitch = new Field("pitch", FieldKind.Range, "Playback speed as a factor; a [min, max] pair lets each play pick between them.", 0.1, 3.0, 1f); internal static readonly Field Loop = new Field("loop", FieldKind.Flag, "Repeats seamlessly. A loop has exactly one layer with only clips and gain.", 0.0, 0.0, false); internal static readonly Field Priority = new Field("priority", FieldKind.Integer, "Higher priority wins when voices run out; 200 and up counts as a warning.", 0.0, 255.0, 100); internal static readonly Field MaxVoices = new Field("maxVoices", FieldKind.Integer, "How many plays of the cue may sound at once.", 1.0, 256.0, 8); internal static readonly Field MaxPerEmitter = new Field("maxPerEmitter", FieldKind.Integer, "How many plays of the cue may sound at once from one emitter; at most maxVoices.", 1.0, 256.0, 8); internal static readonly Field MinInterval = new Field("minInterval", FieldKind.Number, "The shortest gap between two plays of the cue.", 0.0, 60.0, 0f, null, "s"); internal static readonly Field Environment = new Field("environment", FieldKind.Flag, "Whether room acoustics (occlusion, room tone) apply.", 0.0, 0.0, true); internal static readonly Field MaxSeconds = new Field("maxSeconds", FieldKind.Number, "The longest the cue may last; the mix is cut there and fades out. 0 means no cap.", 0.05, 60.0, 0f, null, "s"); internal static readonly Field Place = new Field("place", FieldKind.Choice, "Where the sound sits: in the listener's head (2d), at a world point (world) or following an object (follow); auto keeps what the caller supplied. A { \"self\", \"other\" } pair sets the local player's own sounds and everyone else's separately.", 0.0, 0.0, "auto", PlaceValues); internal static readonly Field Space = new Field("space", FieldKind.Group, "How the cue is heard in space; see the space settings."); internal static readonly Field[] All = new Field[14] { BusName, Layers, Gain, GainDb, Pitch, Loop, Priority, MaxVoices, MaxPerEmitter, MinInterval, Environment, MaxSeconds, Place, Space }; } internal static class Layer { internal static readonly Field Clips = new Field("clips", FieldKind.List, "The .ogg files of the layer; each play picks one.", 1.0, 32.0, null, null, "", Required: true); internal static readonly Field OffsetMs = new Field("offsetMs", FieldKind.Number, "Delay before the layer starts.", 0.0, 60000.0, 0f, null, "ms"); internal static readonly Field Gain = new Field("gain", FieldKind.Number, "Layer volume, linear. Use either gain or gainDb.", 0.0, 4.0, 1f); internal static readonly Field GainDb = new Field("gainDb", FieldKind.Number, "Layer volume in decibels. Use either gain or gainDb.", -60.0, 12.0, null, null, "dB"); internal static readonly Field Range = new Field("range", FieldKind.List, "[start, end] seconds of the clip to use.", 0.0, 600.0, null, null, "s"); internal static readonly Field FadeInMs = new Field("fadeInMs", FieldKind.Number, "Fade-in length.", 0.0, 10000.0, 0f, null, "ms"); internal static readonly Field FadeOutMs = new Field("fadeOutMs", FieldKind.Number, "Fade-out length.", 0.0, 10000.0, 0f, null, "ms"); internal static readonly Field Pitch = new Field("pitch", FieldKind.Range, "Layer playback speed as a factor.", 0.1, 3.0, 1f); internal static readonly Field Distance = new Field("distance", FieldKind.Group, "The listener distances inside which the layer plays, so a far layer can replace the near one."); internal static readonly Field DistanceMin = new Field("distance.min", FieldKind.Number, "Nearest distance.", 0.0, 100000.0, 0f, null, "m"); internal static readonly Field DistanceMax = new Field("distance.max", FieldKind.Number, "Farthest distance; unbounded when omitted.", 0.0, 100000.0, null, null, "m"); internal static readonly Field LowpassHz = new Field("lowpassHz", FieldKind.Number, "Low-pass cutoff; 0 leaves the layer unfiltered.", 50.0, 20000.0, 0f, null, "Hz"); internal static readonly Field[] All = new Field[12] { Clips, OffsetMs, Gain, GainDb, Range, FadeInMs, FadeOutMs, Pitch, Distance, DistanceMin, DistanceMax, LowpassHz }; } internal static class Space { internal static readonly Field Near = new Field("near", FieldKind.Number, "Distance up to which the sound is at full volume.", 0.01, 10000.0, 2f, null, "m"); internal static readonly Field Far = new Field("far", FieldKind.Number, "Distance beyond which the sound is silent (when cull is on); greater than near.", 0.01, 10000.0, 30f, null, "m"); internal static readonly Field Curve = new Field("curve", FieldKind.Choice, "Loudness by distance: \"linear\", \"log\", or 2 to 32 [metres, gain] points with increasing metres.", 0.0, 0.0, "linear", CurveShapes); internal static readonly Field Blend = new Field("blend", FieldKind.Number, "0 hears the sound flat (no distance loss, no panning), 1 fully positioned.", 0.0, 1.0, 1f); internal static readonly Field Spread = new Field("spread", FieldKind.Number, "How much the sound widens, which reduces panning.", 0.0, 180.0, 0f, null, "degrees"); internal static readonly Field Doppler = new Field("doppler", FieldKind.Number, "How strongly relative motion shifts the pitch.", 0.0, 5.0, 0f); internal static readonly Field Cull = new Field("cull", FieldKind.Flag, "Beyond far the sound is silent and takes no voice.", 0.0, 0.0, true); internal static readonly Field[] All = new Field[7] { Near, Far, Curve, Blend, Spread, Doppler, Cull }; } internal const int MaxClips = 32; internal const int MaxLayers = 8; internal const float CurveMetresMax = 10000f; internal const float CurveGainMax = 4f; internal static readonly string[] PlaceValues = new string[4] { "auto", "2d", "world", "follow" }; internal static readonly string[] CurveShapes = new string[2] { "linear", "log" }; internal static string[] Names(Field[] fields) { return (from field in fields where !field.Name.Contains('.') select field.Name).ToArray(); } } internal static class GainPick { internal static float At(float min, float max, double unit) { if (min >= max) { return max; } float num = (float)Math.Clamp(unit, 0.0, 1.0); if (!(min <= 0f)) { return min * MathF.Pow(max / min, num); } return min + (max - min) * num; } } internal static class CategoryVolume { internal static float Map(AudioCategory category, float master, float sfx, float music, float dialogue, bool muteBackground, bool focused, float kitMaster = 1f, float kitCategory = 1f) { if (muteBackground && !focused) { return 0f; } return Unit(master) * Unit(category switch { AudioCategory.Music => music, AudioCategory.Voice => dialogue, _ => sfx, }) * Unit(kitMaster) * Unit(kitCategory); } private static float Unit(float n) { if (!float.IsFinite(n)) { return 0f; } return Math.Clamp(n, 0f, 1f); } } internal enum ListenerDecision { Unavailable, ReuseExternal, KeepOwned, CreateOwned } internal static class ListenerPolicy { internal static ListenerDecision Decide(bool cameraActive, bool ownedOnCamera, int otherActive) { if (otherActive <= 0) { if (cameraActive) { if (!ownedOnCamera) { return ListenerDecision.CreateOwned; } return ListenerDecision.KeepOwned; } return ListenerDecision.Unavailable; } return ListenerDecision.ReuseExternal; } } internal enum OneShotState { Playing, Completed, Stalled } internal struct OneShotProgress { private double _phase; private double _lastDsp; private double _lastPosition; private double _lastProgress; private double _started; internal OneShotProgress(double position, double dsp, double now) { _phase = (_lastPosition = position); _lastDsp = dsp; _lastProgress = (_started = now); } internal OneShotState Update(double position, bool playing, double duration, float pitch, double dsp, double now) { double num = Math.Max(0.0, dsp - _lastDsp); _lastDsp = dsp; _phase = Math.Max(position, _phase + num * (double)pitch); if (num > 0.0 || position > _lastPosition) { _lastProgress = now; } _lastPosition = position; if (position >= duration || _phase >= duration + 0.25 || (!playing && now - _started >= 0.1)) { return OneShotState.Completed; } if (!(now - _lastProgress >= 2.0)) { return OneShotState.Playing; } return OneShotState.Stalled; } } internal enum VoiceGroup { General, Dialogue, Warning, EnvironmentLoop } internal readonly record struct VoiceRank(int Priority, float Audibility, double Started, VoiceGroup Group = VoiceGroup.General); internal static class VoicePolicy { internal const int Capacity = 64; internal const int LogicalCapacity = 256; internal static bool CanSteal(VoiceRank incoming, VoiceRank current, double now) { if (now - current.Started >= 0.1) { if (incoming.Priority <= current.Priority) { if (incoming.Priority == current.Priority) { return incoming.Audibility > current.Audibility + 0.05f; } return false; } return true; } return false; } internal static bool Less(VoiceRank a, VoiceRank b) { if (a.Priority >= b.Priority) { if (a.Priority == b.Priority) { if (!(a.Audibility < b.Audibility)) { if (a.Audibility == b.Audibility) { return a.Started < b.Started; } return false; } return true; } return false; } return true; } internal static int Select(ReadOnlySpan<VoiceRank?> active, VoiceRank incoming, double now) { Span<int> span = stackalloc int[4]; span.Clear(); for (int i = 0; i < active.Length; i++) { VoiceRank? voiceRank = active[i]; if (!voiceRank.HasValue) { return i; } span[(int)voiceRank.GetValueOrDefault().Group]++; } bool flag = span[(int)incoming.Group] < Reserved(incoming.Group); int num = -1; int num2 = -1; for (int j = 0; j < active.Length; j++) { VoiceRank value = active[j].Value; if (value.Group == incoming.Group || span[(int)value.Group] > Reserved(value.Group)) { if (flag && value.Group != incoming.Group && now - value.Started >= 0.1 && (num2 < 0 || Less(value, active[num2].Value))) { num2 = j; } if (CanSteal(incoming, value, now) && (num < 0 || Less(value, active[num].Value))) { num = j; } } } if (num2 < 0) { return num; } return num2; } internal static int Reserved(VoiceGroup group) { if (group != VoiceGroup.General) { return 8; } return 0; } internal static VoiceGroup Group(AudioCategory category, bool loop, int priority) { if (category == AudioCategory.Voice) { return VoiceGroup.Dialogue; } VoiceGroup result; if (priority >= 200) { result = VoiceGroup.Warning; } else { bool flag = loop; if (flag) { bool flag2 = ((category == AudioCategory.Ambience || category == AudioCategory.Music) ? true : false); flag = flag2; } result = (flag ? VoiceGroup.EnvironmentLoop : VoiceGroup.General); } return result; } } internal readonly record struct BusMember(int Priority, float Audibility, double Started); internal static class BusPolicy { internal const float ReplaceFade = 0.06f; internal static int Evict(ReadOnlySpan<BusMember> holders, int seats, int incomingPriority, float incomingAudibility, Span<int> victims) { int num = holders.Length - seats + 1; if (num <= 0) { return 0; } for (int i = 0; i < holders.Length; i++) { victims[i] = i; } for (int j = 1; j < holders.Length; j++) { int num2 = victims[j]; int num3 = j - 1; while (num3 >= 0 && Weaker(holders[num2], holders[victims[num3]])) { victims[num3 + 1] = victims[num3]; num3--; } victims[num3 + 1] = num2; } for (int k = 0; k < num; k++) { BusMember busMember = holders[victims[k]]; if (busMember.Priority > incomingPriority || (busMember.Priority == incomingPriority && incomingAudibility < busMember.Audibility * 0.5f)) { return -1; } } return num; } internal static bool Weaker(BusMember a, BusMember b) { if (a.Priority == b.Priority) { if (a.Audibility == b.Audibility) { return a.Started < b.Started; } return a.Audibility < b.Audibility; } return a.Priority < b.Priority; } } internal enum CategoryAdmission { Room, PushOut, Refuse } internal static class CategoryPolicy { internal static CategoryAdmission Admit(ReadOnlySpan<BusMember> holders, int limit, int incomingPriority, float incomingAudibility, out int victim) { victim = -1; if (holders.Length < limit) { return CategoryAdmission.Room; } int num = 0; for (int i = 1; i < holders.Length; i++) { if (BusPolicy.Weaker(holders[i], holders[num])) { num = i; } } BusMember busMember = holders[num]; if (incomingPriority > busMember.Priority || (incomingPriority == busMember.Priority && incomingAudibility > busMember.Audibility)) { victim = num; return CategoryAdmission.PushOut; } return CategoryAdmission.Refuse; } } internal readonly record struct DuckSource(int Index, int Mask, float Factor); internal static class DuckPolicy { internal const float DownRate = 25f; internal const float UpRate = 6f; internal static float Factor(float decibels) { return MathF.Pow(10f, (0f - decibels) / 20f); } internal static float Target(ReadOnlySpan<DuckSource> duckers, int index, AudioCategory category) { int num = AudioCategories.Bit(category); float num2 = 1f; ReadOnlySpan<DuckSource> readOnlySpan = duckers; for (int i = 0; i < readOnlySpan.Length; i++) { DuckSource duckSource = readOnlySpan[i]; if (duckSource.Index != index && (duckSource.Mask & num) != 0 && duckSource.Factor < num2) { num2 = duckSource.Factor; } } return num2; } internal static float Step(float current, float target, float dt) { return current + (target - current) * (1f - MathF.Exp((0f - dt) * ((target < current) ? 25f : 6f))); } } internal sealed class OcclusionSchedule { internal const int RaysPerFrame = 4; internal const double Interval = 0.2; private readonly double[] _due = new double[64]; private int _cursor; private int _frame = -1; private int _used; internal void Reset(int slot) { _due[slot] = 0.0; } internal int Next(int frame, double now, bool[] eligible) { if (frame != _frame) { _frame = frame; _used = 0; } if (_used == 4) { return -1; } for (int i = 0; i < _due.Length; i++) { int cursor = _cursor; _cursor = (_cursor + 1) % _due.Length; if (eligible[cursor] && !(now < _due[cursor])) { _due[cursor] = now + 0.2; _used++; return cursor; } } return -1; } } internal sealed class EndGate { internal bool Ended { get; private set; } internal bool End() { if (Ended) { return false; } Ended = true; return true; } } internal enum PrewarmStep { Started, Waiting, Refused } internal sealed class PrewarmProgress { internal const int MaxRefused = 600; private readonly string _identity; private readonly string[] _cues; private int _next; private int _refused; internal bool Done { get; private set; } internal string? Current { get { if (!Done) { return _cues[_next]; } return null; } } internal PrewarmProgress(string identity, string[] cues) { _identity = identity; _cues = cues; Done = cues.Length == 0; } internal static string[] OneShots(IEnumerable<CueDefinition> cues) { List<string> list = new List<string>(); foreach (CueDefinition cue in cues) { if (!cue.Loop) { list.Add(cue.Name); } } return list.ToArray(); } internal string? Report(PrewarmStep step, string detail = "") { if (Done) { return null; } switch (step) { case PrewarmStep.Waiting: return null; case PrewarmStep.Refused: if (++_refused < 600) { return null; } Done = true; return _identity + " sounds could not be prewarmed: " + _cues[_next] + " was refused " + _refused + " times (" + detail + ")."; default: _refused = 0; if (++_next >= _cues.Length) { Done = true; } return null; } } } internal static class CueRequirement { internal static string? Missing(Func<string, bool> has, IEnumerable<string> cues) { List<string> list = null; foreach (string cue in cues) { if (!has(cue)) { (list ?? (list = new List<string>())).Add(cue); } } if (list != null) { return string.Join(", ", list); } return null; } } public enum AudioError { None, InvalidSettings, InvalidSource, PathOutsideRoot, UnsupportedCodec, DecodeFailed, AssetLimit, InvalidIdentity, DuplicateLibrary, NotReady, LibraryDisposed, CueNotFound, WrongThread, InvalidOptions, ListenerUnavailable, TargetDestroyed, VoiceLimit, EnvironmentUnavailable, CallbackFailed, DeviceChanged, Cancelled, VoiceFailed } public readonly struct AudioFailure { public AudioError Code { get; } public string Detail { get; } internal AudioFailure(AudioError code, string detail) { Code = code; Detail = detail; } public override string ToString() { return Code.ToString() + ": " + Detail; } } internal readonly struct Result<T> { private readonly T _value; private readonly AudioFailure _error; public bool Success { get; } public AudioFailure Error { get { if (Success || _error.Code != AudioError.None) { return _error; } return new AudioFailure(AudioError.NotReady, "Uninitialized result."); } } public T Value { get { if (!Success) { throw new InvalidOperationException(Error.ToString()); } return _value; } } private Result(T value) { _value = value; Success = true; _error = default(AudioFailure); } private Result(AudioFailure error) { _value = default(T); Success = false; _error = error; } internal static Result<T> Ok(T value) { return new Result<T>(value); } internal static Result<T> Fail(AudioError code, string detail) { return new Result<T>(new AudioFailure(code, detail)); } internal static Result<T> Fail(AudioFailure failure) { return new Result<T>(failure); } } internal static class AudioLog { internal static Action<string, AudioFailure> Sink = delegate(string key, AudioFailure error) { Console.Error.WriteLine("ForgeAudio " + error); }; internal static void Report(string key, AudioFailure error) { Sink(key, error); } internal static void Report(string key, AudioError code, string detail) { Report(key, new AudioFailure(code, detail)); } internal static void Report(AudioFailure error) { Sink(error.ToString(), error); } internal static void Report(AudioError code, string detail) { Report(new AudioFailure(code, detail)); } } [Flags] public enum BodySound { Footstep = 1, Movement = 2 } [Flags] public enum WeaponAction { ReloadStart = 1, ReloadDone = 2, DryFire = 4 } [StructLayout(LayoutKind.Sequential, Pack = 1)] internal struct SyncedPlay { internal ulong Library; internal ulong Cue; internal float X; internal float Y; internal float Z; internal float Gain; internal float CueGain; internal float Pitch; internal float StartSeconds; internal int Dimension; internal byte Variant; internal byte Place; } internal enum SyncPlace : byte { TwoD, World } internal readonly record struct SyncRequest(ulong Library, ulong Cue, SyncPlace Place, float X, float Y, float Z, float Gain, float CueGain, float Pitch, float StartSeconds, int Dimension, int Variant); internal static class SyncCodec { internal const int MaxVariant = 254; internal static SyncedPlay Encode(ulong library, ulong cue, SyncPlace place, float x, float y, float z, float gain, float cueGain, float pitch, float startSeconds, int dimension, int variant) { return new SyncedPlay { Library = library, Cue = cue, Place = (byte)place, X = x, Y = y, Z = z, Gain = gain, CueGain = cueGain, Pitch = pitch, StartSeconds = startSeconds, Dimension = ((place == SyncPlace.TwoD) ? (-1) : dimension), Variant = (byte)Math.Clamp(variant, 0, 254) }; } internal static bool TryDecode(in SyncedPlay message, out SyncRequest request, out string reason) { request = default(SyncRequest); reason = ""; if (message.Place > 1) { reason = "unknown placement " + message.Place; return false; } SyncPlace place = (SyncPlace)message.Place; if (message.Library == 0L || message.Cue == 0L) { reason = "no library or cue"; return false; } if (!In(message.Gain, 0f, 4f) || !In(message.CueGain, 0f, 4f) || !In(message.Pitch, 0.1f, 3f) || !In(message.StartSeconds, 0f, 3600f)) { reason = "gain, cue volume, pitch or start out of range"; return false; } if (place == SyncPlace.World && (!float.IsFinite(message.X) || !float.IsFinite(message.Y) || !float.IsFinite(message.Z) || message.Dimension < 0)) { reason = "a world sound needs a finite position and a dimension"; return false; } if (message.Variant > 254) { reason = "variant out of range"; return false; } request = new SyncRequest(message.Library, message.Cue, place, message.X, message.Y, message.Z, message.Gain, message.CueGain, message.Pitch, message.StartSeconds, (place == SyncPlace.TwoD) ? (-1) : message.Dimension, message.Variant); return true; } private static bool In(float value, float min, float max) { if (float.IsFinite(value) && value >= min) { return value <= max; } return false; } } internal static class SyncKey { internal static ulong Of(string name) { ulong num = 14695981039346656037uL; for (int i = 0; i < name.Length; i++) { char c = char.ToLowerInvariant(name[i]); num = Fnv.Mix64(Fnv.Mix64(num, (byte)c), (byte)((int)c >> 8)); } if (num != 0L) { return num; } return 1uL; } } internal static class VariantPick { internal static int Next(int count, ref int last, Random random) { if (count < 2) { last = 0; return 0; } int num = random.Next(count - 1); if (num >= last) { num++; } return last = num; } } internal sealed record DecodedAudio(float[] Samples, int Channels, int SampleRate) { internal int Frames => Samples.Length / Channels; [CompilerGenerated] private bool PrintMembers(StringBuilder builder) { RuntimeHelpers.EnsureSufficientExecutionStack(); builder.Append("Samples = "); builder.Append(Samples); builder.Append(", Channels = "); builder.Append(Channels.ToString()); builder.Append(", SampleRate = "); builder.Append(SampleRate.ToString()); return true; } } internal static class VorbisDecode { private sealed class BoundedPackets : IPacketProvider { private readonly IPacketProvider _inner; private readonly CancellationToken _cancellation; private readonly int _limit; private int _served; public bool CanSeek => false; public int StreamSerial => _inner.StreamSerial; internal BoundedPackets(IPacketProvider inner, int limit, CancellationToken cancellation) { _inner = inner; _limit = limit; _cancellation = cancellation; } public IPacket? GetNextPacket() { _cancellation.ThrowIfCancellationRequested(); if (++_served > _limit) { throw new InvalidDataException("Vorbis stream yields more packets than the file can hold."); } IPacket nextPacket = _inner.GetNextPacket(); if (nextPacket != null) { return (IPacket?)(object)new Packet(nextPacket); } return null; } public IPacket? PeekNextPacket() { IPacket val = _inner.PeekNextPacket(); if (val != null) { return (IPacket?)(object)new Packet(val); } return null; } public long SeekTo(long granulePos, int preRoll, GetPacketGranuleCount getPacketGranuleCount) { throw new NotSupportedException("Kit decodes whole clips without seeking."); } public long GetGranuleCount() { return _inner.GetGranuleCount(); } } private sealed class Packet : IPacket { private readonly IPacket _inner; public bool IsResync => _inner.IsResync; public bool IsShort => _inner.IsShort; public long? GranulePosition => _inner.GranulePosition; public bool IsEndOfStream => false; public int BitsRead => _inner.BitsRead; public int BitsRemaining => _inner.BitsRemaining; public int ContainerOverheadBits => _inner.ContainerOverheadBits; internal Packet(IPacket inner) { _inner = inner; } public ulong TryPeekBits(int count, out int bitsRead) { return _inner.TryPeekBits(count, ref bitsRead); } public void SkipBits(int count) { _inner.SkipBits(count); } public ulong ReadBits(int count) { return _inner.ReadBits(count); } public void Done() { _inner.Done(); } public void Reset() { _inner.Reset(); } } internal const int ToleranceFrames = 16384; private const int ChunkFrames = 16384; internal static DecodedAudio Decode(byte[] bytes, OggInfo info, CancellationToken cancellation) { cancellation.ThrowIfCancellationRequested(); try { return DecodeChecked(bytes, info, cancellation); } catch (Exception ex) when ((!(ex is InvalidDataException) && !(ex is OperationCanceledException)) || 1 == 0) { throw new InvalidDataException("Vorbis decoder rejected the stream: " + ex.GetType().Name + ": " + ex.Message, ex); } } private static DecodedAudio DecodeChecked(byte[] bytes, OggInfo info, CancellationToken cancellation) { //IL_000e: Unknown result type (might be due to invalid IL or missing references) //IL_0014: Expected O, but got Unknown //IL_0023: Unknown result type (might be due to invalid IL or missing references) //IL_002d: Expected O, but got Unknown //IL_005a: Unknown result type (might be due to invalid IL or missing references) //IL_005f: Unknown result type (might be due to invalid IL or missing references) //IL_0067: Expected O, but got Unknown ContainerReader val = new ContainerReader((Stream)new MemoryStream(bytes, writable: false), true); try { IPacketProvider stream = null; val.NewStreamCallback = (NewStreamHandler)delegate(IPacketProvider provider) { if (stream == null) { stream = provider; } return true; }; if (!val.TryInit() || stream == null) { throw new InvalidDataException("No Vorbis stream in the Ogg container."); } StreamDecoder val2 = new StreamDecoder((IPacketProvider)(object)new BoundedPackets(stream, bytes.Length + 16, cancellation)) { ClipSamples = true }; try { if (val2.Channels != info.Channels || val2.SampleRate != info.SampleRate) { throw new InvalidDataException("Vorbis header disagrees with the Ogg identification page."); } int channels = info.Channels; int num = checked((int)((info.Frames + 16384) * channels)); float[] array = new float[num]; int num2 = 0; while (true) { cancellation.ThrowIfCancellationRequested(); int num3 = Math.Min(16384 * channels, num - num2); if (num3 == 0) { if (val2.Read((Span<float>)new float[channels], 0, channels) == 0) { break; } throw new InvalidDataException("Vorbis audio runs more than " + 16384 + " frames past its declared end."); } int num4 = val2.Read((Span<float>)array, num2, num3); if (num4 == 0) { break; } num2 += num4; } int num5 = num2 / channels; if (num5 <= 0 || info.Frames - num5 > 16384) { throw new InvalidDataException("Vorbis audio ends " + (info.Frames - num5) + " frames before its declared end."); } Array.Resize(ref array, (int)Math.Min(num5, info.Frames) * channels); for (int num6 = 0; num6 < array.Length; num6++) { if (!float.IsFinite(array[num6])) { throw new InvalidDataException("Vorbis decoder produced non-finite samples."); } } return new DecodedAudio(array, channels, info.SampleRate); } finally { ((IDisposable)val2)?.Dispose(); } } finally { ((IDisposable)val)?.Dispose(); } } } public enum PlayStatus { Failed, Played, Virtualized, DistanceCulled, BudgetRejected, Throttled } public enum PlaybackState { Playing, Virtualized, Fading, Ended } public enum EndReason { Completed, Stopped, Stolen, TargetDestroyed, LevelExit, CheckpointReload, LibraryDisposed, DeviceChanged, Failed, PluginUnloaded, Replaced, PushedOut } public sealed class PlayOptions { private static readonly Dictionary<float, PlayOptions> Gains = new Dictionary<float, PlayOptions>(); public float Gain { get; init; } = 1f; public float? Pitch { get; init; } public int? Variant { get; init; } public float StartSeconds { get; init; } public bool IsSelf { get; init; } public int? Dimension { get; init; } public object? Emitter { get; init; } public static PlayOptions OfGain(float gain) { if (!Gains.TryGetValue(gain, out PlayOptions value)) { Dictionary<float, PlayOptions> gains = Gains; PlayOptions obj = new PlayOptions { Gain = gain }; value = obj; gains.Add(gain, obj); } return value; } } public readonly struct PlayResult { private readonly AudioFailure _error; public PlayStatus Status { get; } public AudioHandle? Handle { get; } public AudioFailure Error { get { if (Status != PlayStatus.Failed || _error.Code != AudioError.None) { return _error; } return new AudioFailure(AudioError.NotReady, "Uninitialized play result."); } } public bool Accepted { get { PlayStatus status = Status; if ((uint)(status - 1) <= 1u) { return true; } return false; } } internal PlayResult(PlayStatus status, AudioHandle? handle = null, AudioFailure error = default(AudioFailure)) { Status = status; Handle = handle; _error = error; } } public sealed class AudioHandle { internal readonly EndGate Gate = new EndGate(); internal Playback? Playback; public long Generation { get; } public PlaybackState State => Playback?.State ?? PlaybackState.Ended; public EndReason? Reason { get; private set; } public event Action<EndReason>? Ended; internal AudioHandle(long generation) { Generation = generation; } public bool Stop(float fadeSeconds = 0f) { return Engine.Control(this, fadeSeconds, ControlKind.Stop); } public bool SetGain(float gain) { return Engine.Control(this, gain, ControlKind.Gain); } internal void Finish(EndReason reason) { if (Gate.End()) { Playback = null; Reason = reason; Engine.NotifyEnded(this); } } internal void DispatchEnded() { Action<EndReason> action = this.Ended; this.Ended = null; if (action == null) { return; } Delegate[] invocationList = action.GetInvocationList(); for (int i = 0; i < invocationList.Length; i++) { Action<EndReason> action2 = (Action<EndReason>)invocationList[i]; try { action2(Reason.Value); } catch (Exception ex) { AudioLog.Report(AudioError.CallbackFailed, ex.ToString()); } } } } public sealed class AudioLibrary : IDisposable { internal Catalog Catalog = new Catalog(); internal readonly Dictionary<string, CueHandle> Cues = new Dictionary<string, CueHandle>(StringComparer.OrdinalIgnoreCase); internal readonly List<ClipLease> Assets = new List<ClipLease>(); internal readonly ulong Key; internal readonly Dictionary<ulong, CueHandle> CueKeys = new Dictionary<ulong, CueHandle>(); private static readonly PlayOptions Silent = new PlayOptions { Gain = 0f }; private PrewarmProgress? _prewarm; public string Identity { get; } public string Folder { get; } public bool IsLoaded { get; internal set; } public bool IsDisposed { get; private set; } public AudioFailure? Error { get; internal set; } internal AudioLibrary(string identity, string folder) { Identity = identity; Folder = folder; Key = SyncKey.Of(identity); } public AudioCue Cue(string cue) { return new AudioCue(this, cue ?? throw new ArgumentNullException("cue")); } public bool Has(string cue) { if (cue != null) { return Cues.ContainsKey(cue); } return false; } public PlayResult Play(string cue, PlayOptions? options = null) { //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_000b: Unknown result type (might be due to invalid IL or missing references) return Engine.Play(this, cue, EmitterKind.TwoD, default(Vector3), null, options); } public PlayResult Play(string cue, Vector3 position, PlayOptions? options = null) { //IL_0003: Unknown result type (might be due to invalid IL or missing references) return Engine.Play(this, cue, EmitterKind.Positioned, position, null, options); } public PlayResult Play(string cue, Transform follow, PlayOptions? options = null) { //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_000b: Unknown result type (might be due to invalid IL or missing references) return Engine.Play(this, cue, EmitterKind.Attached, default(Vector3), follow, options); } public void StopAll(float fadeSeconds = 0f) { Engine.StopLibrary(this, fadeSeconds); } public void Dispose() { if (!IsDisposed) { if (!AudioKit.Ready) { IsDisposed = true; return; } if (Environment.CurrentManagedThreadId != AudioKit.MainThread) { Engine.QueueDispose(this); return; } IsDisposed = true; Engine.DisposeLibrary(this); } } public AudioSustain Sustain(string cue, Transform owner, PlayOptions? options = null, Func<bool>? keepWhile = null) { if ((Object)(object)owner == (Object)null) { throw new ArgumentNullException("owner"); } AudioSustain audioSustain = new AudioSustain(this, cue, owner, options, keepWhile); if (Emitter.Gone((Object?)(object)owner) || IsDisposed) { audioSustain.IsActive = false; return audioSustain; } audioSustain.Voice = Play(cue, owner, options).Handle; Sustains.Add(audioSustain); return audioSustain; } public AudioVoice Voice(Transform anchor) { return new AudioVoice(this, anchor ?? throw new ArgumentNullException("anchor")); } public void Require(params string[] cues) { AudioFailure? error = Error; if (error.HasValue) { AudioFailure valueOrDefault = error.GetValueOrDefault(); throw new InvalidDataException(Identity + " sounds cannot load: " + valueOrDefault); } if (!IsLoaded) { throw new InvalidOperationException(Identity + " sounds have not finished loading."); } string text = CueRequirement.Missing(Has, cues); if (text != null) { throw new InvalidDataException(Identity + " sounds missing from the audio folder or its cues.json: " + text); } } public bool Prewarm() { //IL_0085: Unknown result type (might be due to invalid IL or missing references) if (IsDisposed || Error.HasValue) { return true; } if (!IsLoaded) { return false; } if (_prewarm == null) { _prewarm = new PrewarmProgress(Identity, PrewarmProgress.OneShots(Cues.Values.Select((CueHandle cue) => cue.Definition))); } string current = _prewarm.Current; if (current == null) { return true; } PlayResult playResult = Play(current, GameContext.Ear, Silent); PrewarmStep step = ((playResult.Status == PlayStatus.Failed) ? ((playResult.Error.Code == AudioError.ListenerUnavailable) ? PrewarmStep.Waiting : PrewarmStep.Refused) : PrewarmStep.Started); string text = _prewarm.Report(step, playResult.Error.Detail); if (text != null) { AudioLog.Report(AudioError.NotReady, text); } return _prewarm.Done; } internal void ResetPrewarm() { _prewarm = null; } public PlayResult Play(string cue, GameObject follow, PlayOptions? options = null) { if (!Emitter.Gone((Object?)(object)follow)) { return Play(cue, follow.transform, options); } return Engine.TargetGone(this, cue); } public AudioSustain Sustain(string cue, GameObject owner, PlayOptions? options = null, Func<bool>? keepWhile = null) { if (!Emitter.Gone((Object?)(object)owner)) { return Sustain(cue, owner.transform, options, keepWhile); } return new AudioSustain(this, cue, null, options, keepWhile) { IsActive = false }; } } public interface IWeaponActionHandler { void Play(WeaponAction action); } public sealed class WeaponActionClaim : IDisposable { internal readonly IWeaponActionHandler Handler; internal ClaimBook<WeaponActionClaim>.Entry? Entry; internal bool HandlerFailed; public string ModId { get; } public Item Weapon { get; } public WeaponAction Kinds { get; } public int Priority { get; } public bool Active => Entry?.Live ?? false; internal WeaponActionClaim(string modId, Item weapon, WeaponAction kinds, int priority, IWeaponActionHandler handler) { ModId = modId; Weapon = weapon; Kinds = kinds; Priority = priority; Handler = handler; } public void Dispose() { if (Entry != null) { WeaponActions.Book.Remove(Entry); } } } public static class AudioKit { public const string PluginId = "NAinfini.ForgeAudio"; public const string FolderName = "audio"; internal static int MainThread; internal static bool Ready; private static readonly Dictionary<string, uint> GameEventIds = new Dictionary<string, uint>(StringComparer.OrdinalIgnoreCase); internal static bool OnMainThread { get { if (Ready) { return Environment.CurrentManagedThreadId == MainThread; } return false; } } [MethodImpl(MethodImplOptions.NoInlining)] public static AudioLibrary Load(string identity, string? folder = null) { if (folder == null) { string location = Assembly.GetCallingAssembly().Location; folder = (string.IsNullOrEmpty(location) ? "" : Path.Combine(Path.GetDirectoryName(location), "audio")); } return Assets.Load(identity, folder); } internal static bool ValidIdentity(string? identity) { if (identity != null && identity.Length <= 100) { return Regex.IsMatch(identity, "^[A-Za-z0-9][A-Za-z0-9_.-]*$"); } return false; } public static BodySoundClaim? ClaimBodySounds(string modId, GameObject root, BodySound kinds, IBodySoundHandler handler, int priority = 0) { return BodySounds.Claim(modId, root, kinds, handler, priority); } public static WeaponActionClaim? ClaimWeaponActions(string modId, Item weapon, WeaponAction kinds, IWeaponActionHandler handler, int priority = 0) { return WeaponActions.Claim(modId, weapon, kinds, handler, priority); } public static bool PostGameEvent(uint eventId, Vector3 position) { //IL_0028: Unknown result type (might be due to invalid IL or missing references) if (eventId == 0) { AudioLog.Report(AudioError.InvalidOptions, "A game sound event needs a non-zero event ID."); return false; } if (!OnMainThread) { AudioLog.Report(AudioError.WrongThread, "PostGameEvent must be called on the Unity main thread after the kit started."); return false; } try { CellSound.Post(eventId, position); return true; } catch (Exception ex) { AudioLog.Report(AudioError.VoiceFailed, "Game sound event " + eventId + " could not be posted: " + ex); return false; } } public static bool PostGameEvent(string eventName, Vector3 position) { //IL_00d8: Unknown result type (might be due to invalid IL or missing references) if (string.IsNullOrEmpty(eventName)) { AudioLog.Report(AudioError.InvalidOptions, "A game sound event needs a name."); return false; } if (!OnMainThread) { AudioLog.Report(AudioError.WrongThread, "PostGameEvent must be called on the Unity main thread after the kit started."); return false; } if (!GameEventIds.TryGetValue(eventName, out var value)) { try { Type typeFromHandle = typeof(EVENTS); object obj = typeFromHandle.GetProperty(eventName, BindingFlags.IgnoreCase | BindingFlags.Static | BindingFlags.Public)?.GetValue(null) ?? typeFromHandle.GetField(eventName, BindingFlags.IgnoreCase | BindingFlags.Static | BindingFlags.Public)?.GetValue(null); if (obj == null) { AudioLog.Report(AudioError.CueNotFound, "The game has no sound event named '" + eventName + "' (AK.EVENTS)."); return false; } value = Convert.ToUInt32(obj); } catch (Exception ex) { AudioLog.Report(AudioError.CueNotFound, "The game sound event '" + eventName + "' could not be read: " + ex); return false; } GameEventIds[eventName] = value; } return PostGameEvent(value, position); } public static int DimensionAt(Vector3 position, bool preferLocalPlayer = false) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) return GameContext.DimensionAt(position, preferLocalPlayer); } } public interface IBodySoundHandler { bool Play(BodySound kind, Vector3 position); } public sealed class BodySoundClaim : IDisposable { internal readonly IBodySoundHandler Handler; internal readonly Transform RootTransform; internal ClaimBook<BodySoundClaim>.Entry? Entry; internal bool HandlerFailed; public string ModId { get; } public GameObject Root { get; } public BodySound Kinds { get; } public int Priority { get; } public bool Active => Entry?.Live ?? false; internal BodySoundClaim(string modId, GameObject root, BodySound kinds, int priority, IBodySoundHandler handler) { ModId = modId; Root = root; Kinds = kinds; Priority = priority; Handler = handler; RootTransform = root.transform; } public void Refresh() { if (Active) { if ((Object)(object)Root == (Object)null || ((Il2CppObjectBase)Root).WasCollected) { Dispose(); } else { BodySounds.Book.Reset(Entry, BodySounds.Collect(Root)); } } } public void Dispose() { if (Entry != null) { BodySounds.Book.Remove(Entry); } } } internal sealed class ClipLease { internal readonly string Key; internal readonly AudioClip Clip; internal readonly long CompressedBytes; internal readonly long DecodedBytes; internal int References; internal ClipLease(string key, AudioClip clip, long compressed, long decoded) { Key = key; Clip = clip; CompressedBytes = compressed; DecodedBytes = decoded; Assets.Keep<AudioClip>(clip); } } internal sealed class CueHandle { internal readonly AudioLibrary Library; internal readonly CueDefinition Definition; internal readonly BusDefinition Bus; internal readonly MixProfile[] Profiles; internal readonly ClipLease[]?[] Clips; internal readonly int Variants; internal double NextAllowed; internal int LastVariant = -1; internal CueHandle(AudioLibrary library, CueDefinition definition, BusDefinition bus, MixProfile[] profiles, ClipLease[]?[] clips) { Library = library; Definition = definition; Bus = bus; Profiles = profiles; Clips = clips; Variants = clips.First((ClipLease[] stretch) => stretch != null).Length; } } internal static class Assets { private sealed class Prepared { internal byte[] Data = Array.Empty<byte>(); internal OggInfo Info; internal string Hash = ""; internal AudioFailure? Failure; } private sealed class Loading { internal readonly AudioLibrary Library; internal readonly string[] Files; internal readonly CancellationTokenSource Cancel = new CancellationTokenSource(); internal readonly Dictionary<string, ClipLease> Clips = new Dictionary<string, ClipLease>(StringComparer.OrdinalIgnoreCase); internal readonly Dictionary<string, AudioFailure> Failed = new Dictionary<string, AudioFailure>(StringComparer.OrdinalIgnoreCase); internal readonly Dictionary<string, DecodedAudio> Pcm = new Dictionary<string, DecodedAudio>(StringComparer.OrdinalIgnoreCase); internal readonly Dictionary<string, (MixProfile[] Profiles, ClipLease[]?[] Clips)> Mixed = new Dictionary<string, (MixProfile[], ClipLease[][])>(StringComparer.OrdinalIgnoreCase); internal readonly HashSet<string> DirectFiles; internal readonly HashSet<string> MixedFiles; internal Queue<CueDefinition>? Todo; internal int Index; internal Task<Prepared>? Preparing; internal Prepared? Ready; internal Task<DecodedAudio>? Decoding; internal string File => Files[Index]; internal Loading(AudioLibrary library, string[] files) { Library = library; Files = files; DirectFiles = library.Catalog.DirectFiles; MixedFiles = library.Catalog.MixedFiles; } } private static readonly Queue<Loading> Pending = new Queue<Loading>(); private static readonly Dictionary<string, ClipLease> Cache = new Dictionary<string, ClipLease>(StringComparer.Ordinal); private static readonly List<AudioLibrary> Libraries = new List<AudioLibrary>(); private static Loading? _active; internal static long CompressedBytes; internal static long DecodedBytes; internal static IReadOnlyList<AudioLibrary> Loaded => Libraries; internal static AudioLibrary Load(string identity, string folder) { AudioLibrary audioLibrary = new AudioLibrary(identity ?? "", folder); AudioFailure? audioFailure = null; if (!AudioKit.Ready) { audioFailure = new AudioFailure(AudioError.NotReady, "ForgeAudio is not loaded; add [BepInDependency(AudioKit.PluginId)] to your plugin."); } else if (!AudioKit.OnMainThread) { audioFailure = new AudioFailure(AudioError.WrongThread, "AudioKit.Load must run on the Unity main thread (your plugin's Load)."); } else { AudioFailure? failure = ClipUpload.Failure; if (failure.HasValue) { AudioFailure valueOrDefault = failure.GetValueOrDefault(); audioFailure = valueOrDefault; } else if (!AudioKit.ValidIdentity(identity)) { audioFailure = new AudioFailure(AudioError.InvalidIdentity, "identity '" + identity + "' must be letters, digits, '.', '_' or '-' (at most 100)."); } else if (Libraries.Any((AudioLibrary other) => string.Equals(other.Identity, identity, StringComparison.OrdinalIgnoreCase))) { audioFailure = new AudioFailure(AudioError.DuplicateLibrary, "a library named '" + identity + "' is already loaded."); } else if (string.IsNullOrEmpty(folder) || !Path.IsPathFullyQualified(folder)) { audioFailure = new AudioFailure(AudioError.PathOutsideRoot, "audio folder '" + folder + "' is not an absolute path; pass the folder to AudioKit.Load."); } else if (!Directory.Exists(folder)) { audioFailure = new AudioFailure(AudioError.InvalidSource, "audio folder not found: " + folder); } } if (!audioFailure.HasValue) { try { string root = Path.GetFullPath(folder); string[] files = (from file in Directory.EnumerateFiles(root, "*", SearchOption.AllDirectories) select Path.GetRelativePath(root, file)).ToArray(); string path = Path.Combine(root, "cues.json"); audioLibrary.Catalog = CueCatalog.Build(files, File.Exists(path) ? File.ReadAllText(path) : null); audioFailure = audioLibrary.Catalog.Error; } catch (Exception ex) when (((ex is IOException || ex is UnauthorizedAccessException) ? 1 : 0) != 0) { audioFailure = new AudioFailure(AudioError.InvalidSource, "audio folder unreadable: " + ex); } } if (audioFailure.HasValue) { AudioFailure valueOrDefault2 = audioFailure.GetValueOrDefault(); audioLibrary.Error = new AudioFailure(valueOrDefault2.Code, identity + ": " + valueOrDefault2.Detail + " No sounds will play."); audioLibrary.IsLoaded = true; AudioLog.Report(audioLibrary.Error.Value); return audioLibrary; } foreach (KeyValuePair<string, AudioFailure> failure2 in audioLibrary.Catalog.Failures) { AudioLog.Report(failure2.Value.Code, identity + ": " + failure2.Value.Detail); } Libraries.Add(audioLibrary); SyncPlays.Register(audioLibrary); Loading item = new Loading(audioLibrary, audioLibrary.Catalog.Files); Pending.Enqueue(item); return audioLibrary; } internal static void Tick() { if (_active == null && Pending.Count != 0) { _active = Pending.Dequeue(); } Loading r = _active; if (r == null) { return; } if (r.Library.IsDisposed) { Abort(r); return; } if (r.Index == r.Files.Length) { if (BuildNext(r)) { Complete(r); } return; } string file = r.File; try { bool flag = r.MixedFiles.Contains(file); bool flag2 = r.DirectFiles.Contains(file); if (r.Ready == null) { Loading loading = r; if (loading.Preparing == null) { loading.Preparing = Task.Run(() => Prepare(Path.Combine(r.Library.Folder, file), r.Cancel.Token)); } if (!r.Preparing.IsCompleted) { return; } r.Ready = r.Preparing.GetAwaiter().GetResult(); r.Preparing = null; AudioFailure? failure = r.Ready.Failure; if (failure.HasValue) { AudioFailure valueOrDefault = failure.GetValueOrDefault(); Fail(r, valueOrDefault); return; } if (flag2 && !flag && Cache.TryGetValue(r.Ready.Hash, out ClipLease value)) { value.References++; Accept(r, value); return; } OggInfo info = r.Ready.Info; if (DecodedBytes + info.DecodedBytes > 536870912 || CompressedBytes + r.Ready.Data.Length > 268435456) { Fail(r, new AudioFailure(AudioError.AssetLimit, "the audio cache is full (512 MiB decoded / 256 MiB compressed in total).")); return; } } if (r.Decoding == null) { Prepared prepared = r.Ready; CancellationToken token = r.Cancel.Token; r.Decoding = Task.Run(() => VorbisDecode.Decode(prepared.Data, prepared.Info, token)); } else if (r.Decoding.IsCompleted) { DecodedAudio result = r.Decoding.GetAwaiter().GetResult(); r.Decoding = null; if (flag) { r.Pcm[file] = result; } if (!flag2) { Next(r); return; } if (Cache.TryGetValue(r.Ready.Hash, out ClipLease value2)) { value2.References++; Accept(r, value2); return; } AudioClip clip = MakeClip(r.Library.Identity + "/" + CueCatalog.CueName(file), result); ClipLease clipLease = new ClipLease(r.Ready.Hash, clip, r.Ready.Data.Length, (long)result.Frames * (long)result.Channels * 4) { References = 1 };
NVorbis.dll
Decompiled 2 days ago
The result has been truncated due to the large size, download it to view full contents!
using System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.Globalization; using System.IO; using System.Linq; using System.Reflection; using System.Resources; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using System.Text; using System.Threading; using NVorbis.Contracts; using NVorbis.Contracts.Ogg; using NVorbis.Ogg; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: TargetFramework(".NETStandard,Version=v2.0", FrameworkDisplayName = "")] [assembly: AssemblyCompany("Andrew Ward")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyCopyright("Copyright © Andrew Ward 2021")] [assembly: AssemblyDescription("A fully managed implementation of a Xiph.org Foundation Ogg Vorbis decoder.")] [assembly: AssemblyFileVersion("0.10.5.0")] [assembly: AssemblyInformationalVersion("0.10.5")] [assembly: AssemblyProduct("NVorbis")] [assembly: AssemblyTitle("NVorbis")] [assembly: AssemblyMetadata("RepositoryUrl", "https://github.com/NVorbis/NVorbis")] [assembly: NeutralResourcesLanguage("en")] [assembly: AssemblyVersion("0.10.5.0")] namespace NVorbis { internal class Codebook : ICodebook { private class FastRange : IReadOnlyList<int>, IEnumerable<int>, IEnumerable, IReadOnlyCollection<int> { [ThreadStatic] private static FastRange _cachedRange; private int _start; private int _count; public int this[int index] { get { if (index > _count) { throw new ArgumentOutOfRangeException(); } return _start + index; } } public int Count => _count; internal static FastRange Get(int start, int count) { FastRange obj = _cachedRange ?? (_cachedRange = new FastRange()); obj._start = start; obj._count = count; return obj; } private FastRange() { } public IEnumerator<int> GetEnumerator() { throw new NotSupportedException(); } IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); } } private int[] _lengths; private float[] _lookupTable; private IReadOnlyList<HuffmanListNode> _overflowList; private IReadOnlyList<HuffmanListNode> _prefixList; private int _prefixBitLength; private int _maxBits; public float this[int entry, int dim] => _lookupTable[entry * Dimensions + dim]; public int Dimensions { get; private set; } public int Entries { get; private set; } public int MapType { get; private set; } public void Init(IPacket packet, IHuffman huffman) { if (packet.ReadBits(24) != 5653314) { throw new InvalidDataException("Book header had invalid signature!"); } Dimensions = (int)packet.ReadBits(16); Entries = (int)packet.ReadBits(24); _lengths = new int[Entries]; InitTree(packet, huffman); InitLookupTable(packet); } private void InitTree(IPacket packet, IHuffman huffman) { int num = 0; bool flag; int num5; if (packet.ReadBit()) { int num2 = (int)packet.ReadBits(5) + 1; int num3 = 0; while (num3 < Entries) { int num4 = (int)packet.ReadBits(Utils.ilog(Entries - num3)); while (--num4 >= 0) { _lengths[num3++] = num2; } num2++; } num = 0; flag = false; num5 = num2; } else { num5 = -1; flag = packet.ReadBit(); for (int i = 0; i < Entries; i++) { if (!flag || packet.ReadBit()) { _lengths[i] = (int)packet.ReadBits(5) + 1; num++; } else { _lengths[i] = -1; } if (_lengths[i] > num5) { num5 = _lengths[i]; } } } if ((_maxBits = num5) > -1) { int[] array = null; if (flag && num >= Entries >> 2) { array = new int[Entries]; Array.Copy(_lengths, array, Entries); flag = false; } int num6 = (flag ? num : 0); int[] array2 = null; int[] array3 = null; if (!flag) { array3 = new int[Entries]; } else if (num6 != 0) { array = new int[num6]; array3 = new int[num6]; array2 = new int[num6]; } if (!ComputeCodewords(flag, array3, array, _lengths, Entries, array2)) { throw new InvalidDataException(); } IReadOnlyList<int> readOnlyList = array2; IReadOnlyList<int> value = readOnlyList ?? FastRange.Get(0, array3.Length); huffman.GenerateTable(value, array ?? _lengths, array3); _prefixList = huffman.PrefixTree; _prefixBitLength = huffman.TableBits; _overflowList = huffman.OverflowList; } } private bool ComputeCodewords(bool sparse, int[] codewords, int[] codewordLengths, int[] len, int n, int[] values) { int num = 0; uint[] array = new uint[32]; int i; for (i = 0; i < n && len[i] <= 0; i++) { } if (i == n) { return true; } AddEntry(sparse, codewords, codewordLengths, 0u, i, num++, len[i], values); for (int j = 1; j <= len[i]; j++) { array[j] = (uint)(1 << 32 - j); } for (int j = i + 1; j < n; j++) { int num2 = len[j]; if (num2 <= 0) { continue; } while (num2 > 0 && array[num2] == 0) { num2--; } if (num2 == 0) { return false; } uint num3 = array[num2]; array[num2] = 0u; AddEntry(sparse, codewords, codewordLengths, Utils.BitReverse(num3), j, num++, len[j], values); if (num2 != len[j]) { for (int num4 = len[j]; num4 > num2; num4--) { array[num4] = num3 + (uint)(1 << 32 - num4); } } } return true; } private void AddEntry(bool sparse, int[] codewords, int[] codewordLengths, uint huffCode, int symbol, int count, int len, int[] values) { if (sparse) { codewords[count] = (int)huffCode; codewordLengths[count] = len; values[count] = symbol; } else { codewords[symbol] = (int)huffCode; } } private void InitLookupTable(IPacket packet) { MapType = (int)packet.ReadBits(4); if (MapType == 0) { return; } float num = Utils.ConvertFromVorbisFloat32((uint)packet.ReadBits(32)); float num2 = Utils.ConvertFromVorbisFloat32((uint)packet.ReadBits(32)); int count = (int)packet.ReadBits(4) + 1; bool flag = packet.ReadBit(); int num3 = Entries * Dimensions; float[] array = new float[num3]; if (MapType == 1) { num3 = lookup1_values(); } uint[] array2 = new uint[num3]; for (int i = 0; i < num3; i++) { array2[i] = (uint)packet.ReadBits(count); } if (MapType == 1) { for (int j = 0; j < Entries; j++) { double num4 = 0.0; int num5 = 1; for (int k = 0; k < Dimensions; k++) { int num6 = j / num5 % num3; double num7 = (double)((float)array2[num6] * num2 + num) + num4; array[j * Dimensions + k] = (float)num7; if (flag) { num4 = num7; } num5 *= num3; } } } else { for (int l = 0; l < Entries; l++) { double num8 = 0.0; int num9 = l * Dimensions; for (int m = 0; m < Dimensions; m++) { double num10 = (double)((float)array2[num9] * num2 + num) + num8; array[l * Dimensions + m] = (float)num10; if (flag) { num8 = num10; } num9++; } } } _lookupTable = array; } private int lookup1_values() { int num = (int)Math.Floor(Math.Exp(Math.Log(Entries) / (double)Dimensions)); if (Math.Floor(Math.Pow(num + 1, Dimensions)) <= (double)Entries) { num++; } return num; } public int DecodeScalar(IPacket packet) { int index = (int)packet.TryPeekBits(_prefixBitLength, out var bitsRead); if (bitsRead == 0) { return -1; } HuffmanListNode huffmanListNode = _prefixList[index]; if (huffmanListNode != null) { packet.SkipBits(huffmanListNode.Length); return huffmanListNode.Value; } index = (int)packet.TryPeekBits(_maxBits, out var _); for (int i = 0; i < _overflowList.Count; i++) { huffmanListNode = _overflowList[i]; if (huffmanListNode.Bits == (index & huffmanListNode.Mask)) { packet.SkipBits(huffmanListNode.Length); return huffmanListNode.Value; } } return -1; } } public abstract class DataPacket : IPacket { [Flags] protected enum PacketFlags : byte { IsResync = 1, IsEndOfStream = 2, IsShort = 4, User0 = 8, User1 = 0x10, User2 = 0x20, User3 = 0x40, User4 = 0x80 } private ulong _bitBucket; private int _bitCount; private byte _overflowBits; private PacketFlags _packetFlags; private int _readBits; public int ContainerOverheadBits { get; set; } public long? GranulePosition { get; set; } public bool IsResync { get { return GetFlag(PacketFlags.IsResync); } set { SetFlag(PacketFlags.IsResync, value); } } public bool IsShort { get { return GetFlag(PacketFlags.IsShort); } private set { SetFlag(PacketFlags.IsShort, value); } } public bool IsEndOfStream { get { return GetFlag(PacketFlags.IsEndOfStream); } set { SetFlag(PacketFlags.IsEndOfStream, value); } } public int BitsRead => _readBits; public int BitsRemaining => TotalBits - _readBits; protected abstract int TotalBits { get; } private bool GetFlag(PacketFlags flag) { return _packetFlags.HasFlag(flag); } private void SetFlag(PacketFlags flag, bool value) { if (value) { _packetFlags |= flag; } else { _packetFlags &= (PacketFlags)(byte)(~(int)flag); } } protected abstract int ReadNextByte(); public virtual void Done() { } public virtual void Reset() { _bitBucket = 0uL; _bitCount = 0; _overflowBits = 0; _readBits = 0; } ulong IPacket.ReadBits(int count) { if (count == 0) { return 0uL; } int bitsRead; ulong result = TryPeekBits(count, out bitsRead); SkipBits(count); return result; } public ulong TryPeekBits(int count, out int bitsRead) { switch (count) { default: throw new ArgumentOutOfRangeException("count"); case 0: bitsRead = 0; return 0uL; case 1: case 2: case 3: case 4: case 5: case 6: case 7: case 8: case 9: case 10: case 11: case 12: case 13: case 14: case 15: case 16: case 17: case 18: case 19: case 20: case 21: case 22: case 23: case 24: case 25: case 26: case 27: case 28: case 29: case 30: case 31: case 32: case 33: case 34: case 35: case 36: case 37: case 38: case 39: case 40: case 41: case 42: case 43: case 44: case 45: case 46: case 47: case 48: case 49: case 50: case 51: case 52: case 53: case 54: case 55: case 56: case 57: case 58: case 59: case 60: case 61: case 62: case 63: case 64: break; } while (_bitCount < count) { int num = ReadNextByte(); if (num == -1) { bitsRead = _bitCount; return _bitBucket; } _bitBucket = (ulong)((long)(num & 0xFF) << _bitCount) | _bitBucket; _bitCount += 8; if (_bitCount > 64) { _overflowBits = (byte)(num >> 72 - _bitCount); } } ulong num2 = _bitBucket; if (count < 64) { num2 &= (ulong)((1L << count) - 1); } bitsRead = count; return num2; } public void SkipBits(int count) { if (count <= 0) { return; } if (_bitCount > count) { if (count > 63) { _bitBucket = 0uL; } else { _bitBucket >>= count; } if (_bitCount > 64) { int num = _bitCount - 64; _bitBucket |= (ulong)_overflowBits << _bitCount - count - num; if (num > count) { _overflowBits = (byte)(_overflowBits >> count); } } _bitCount -= count; _readBits += count; return; } if (_bitCount == count) { _bitBucket = 0uL; _bitCount = 0; _readBits += count; return; } count -= _bitCount; _readBits += _bitCount; _bitCount = 0; _bitBucket = 0uL; while (count > 8) { if (ReadNextByte() == -1) { count = 0; IsShort = true; break; } count -= 8; _readBits += 8; } if (count > 0) { int num2 = ReadNextByte(); if (num2 == -1) { IsShort = true; return; } _bitBucket = (ulong)(num2 >> count); _bitCount = 8 - count; _readBits += count; } } } public static class Extensions { public static int Read(this IPacket packet, byte[] buffer, int index, int count) { if (index < 0 || index >= buffer.Length) { throw new ArgumentOutOfRangeException("index"); } if (count < 0 || index + count > buffer.Length) { throw new ArgumentOutOfRangeException("count"); } for (int i = 0; i < count; i++) { int bitsRead; byte b = (byte)packet.TryPeekBits(8, out bitsRead); if (bitsRead == 0) { return i; } buffer[index++] = b; packet.SkipBits(8); } return count; } public static byte[] ReadBytes(this IPacket packet, int count) { byte[] array = new byte[count]; int num = packet.Read(array, 0, count); if (num < count) { byte[] array2 = new byte[num]; Buffer.BlockCopy(array, 0, array2, 0, num); return array2; } return array; } public static bool ReadBit(this IPacket packet) { return packet.ReadBits(1) == 1; } public static byte PeekByte(this IPacket packet) { int bitsRead; return (byte)packet.TryPeekBits(8, out bitsRead); } public static byte ReadByte(this IPacket packet) { return (byte)packet.ReadBits(8); } public static short ReadInt16(this IPacket packet) { return (short)packet.ReadBits(16); } public static int ReadInt32(this IPacket packet) { return (int)packet.ReadBits(32); } public static long ReadInt64(this IPacket packet) { return (long)packet.ReadBits(64); } public static ushort ReadUInt16(this IPacket packet) { return (ushort)packet.ReadBits(16); } public static uint ReadUInt32(this IPacket packet) { return (uint)packet.ReadBits(32); } public static ulong ReadUInt64(this IPacket packet) { return packet.ReadBits(64); } public static void SkipBytes(this IPacket packet, int count) { packet.SkipBits(count * 8); } } internal class Factory : IFactory { public IHuffman CreateHuffman() { return new Huffman(); } public IMdct CreateMdct() { return new Mdct(); } public ICodebook CreateCodebook() { return new Codebook(); } public IFloor CreateFloor(IPacket packet) { return (int)packet.ReadBits(16) switch { 0 => new Floor0(), 1 => new Floor1(), _ => throw new InvalidDataException("Invalid floor type!"), }; } public IMapping CreateMapping(IPacket packet) { if (packet.ReadBits(16) != 0L) { throw new InvalidDataException("Invalid mapping type!"); } return new Mapping(); } public IMode CreateMode() { return new Mode(); } public IResidue CreateResidue(IPacket packet) { return (int)packet.ReadBits(16) switch { 0 => new Residue0(), 1 => new Residue1(), 2 => new Residue2(), _ => throw new InvalidDataException("Invalid residue type!"), }; } } internal class Floor0 : IFloor { private class Data : IFloorData { internal float[] Coeff; internal float Amp; public bool ExecuteChannel { get { if (ForceEnergy || Amp > 0f) { return !ForceNoEnergy; } return false; } } public bool ForceEnergy { get; set; } public bool ForceNoEnergy { get; set; } } private int _order; private int _rate; private int _bark_map_size; private int _ampBits; private int _ampOfs; private int _ampDiv; private ICodebook[] _books; private int _bookBits; private Dictionary<int, float[]> _wMap; private Dictionary<int, int[]> _barkMaps; public void Init(IPacket packet, int channels, int block0Size, int block1Size, ICodebook[] codebooks) { _order = (int)packet.ReadBits(8); _rate = (int)packet.ReadBits(16); _bark_map_size = (int)packet.ReadBits(16); _ampBits = (int)packet.ReadBits(6); _ampOfs = (int)packet.ReadBits(8); _books = new ICodebook[(int)packet.ReadBits(4) + 1]; if (_order < 1 || _rate < 1 || _bark_map_size < 1 || _books.Length == 0) { throw new InvalidDataException(); } _ampDiv = (1 << _ampBits) - 1; for (int i = 0; i < _books.Length; i++) { int num = (int)packet.ReadBits(8); if (num < 0 || num >= codebooks.Length) { throw new InvalidDataException(); } ICodebook codebook = codebooks[num]; if (codebook.MapType == 0 || codebook.Dimensions < 1) { throw new InvalidDataException(); } _books[i] = codebook; } _bookBits = Utils.ilog(_books.Length); _barkMaps = new Dictionary<int, int[]> { [block0Size] = SynthesizeBarkCurve(block0Size / 2), [block1Size] = SynthesizeBarkCurve(block1Size / 2) }; _wMap = new Dictionary<int, float[]> { [block0Size] = SynthesizeWDelMap(block0Size / 2), [block1Size] = SynthesizeWDelMap(block1Size / 2) }; } private int[] SynthesizeBarkCurve(int n) { float num = (float)_bark_map_size / toBARK(_rate / 2); int[] array = new int[n + 1]; for (int i = 0; i < n - 1; i++) { array[i] = Math.Min(_bark_map_size - 1, (int)Math.Floor(toBARK((float)_rate / 2f / (float)n * (float)i) * num)); } array[n] = -1; return array; } private static float toBARK(double lsp) { return (float)(13.1 * Math.Atan(0.00074 * lsp) + 2.24 * Math.Atan(1.85E-08 * lsp * lsp) + 0.0001 * lsp); } private float[] SynthesizeWDelMap(int n) { float num = (float)(Math.PI / (double)_bark_map_size); float[] array = new float[n]; for (int i = 0; i < n; i++) { array[i] = 2f * (float)Math.Cos(num * (float)i); } return array; } public IFloorData Unpack(IPacket packet, int blockSize, int channel) { Data data = new Data { Coeff = new float[_order + 1] }; data.Amp = packet.ReadBits(_ampBits); if (data.Amp > 0f) { Array.Clear(data.Coeff, 0, data.Coeff.Length); data.Amp = data.Amp / (float)_ampDiv * (float)_ampOfs; uint num = (uint)packet.ReadBits(_bookBits); if (num >= _books.Length) { data.Amp = 0f; return data; } ICodebook codebook = _books[num]; int i = 0; while (i < _order) { int num2 = codebook.DecodeScalar(packet); if (num2 == -1) { data.Amp = 0f; return data; } int num3 = 0; for (; i < _order; i++) { if (num3 >= codebook.Dimensions) { break; } data.Coeff[i] = codebook[num2, num3]; num3++; } } float num4 = 0f; int num5 = 0; while (num5 < _order) { int num6 = 0; while (num5 < _order && num6 < codebook.Dimensions) { data.Coeff[num5] += num4; num5++; num6++; } num4 = data.Coeff[num5 - 1]; } } return data; } public void Apply(IFloorData floorData, int blockSize, float[] residue) { if (!(floorData is Data data)) { throw new ArgumentException("Incorrect packet data!"); } int num = blockSize / 2; if (data.Amp > 0f) { int[] array = _barkMaps[blockSize]; float[] array2 = _wMap[blockSize]; int num2 = 0; for (num2 = 0; num2 < _order; num2++) { data.Coeff[num2] = 2f * (float)Math.Cos(data.Coeff[num2]); } num2 = 0; while (num2 < num) { int num3 = array[num2]; float num4 = 0.5f; float num5 = 0.5f; float num6 = array2[num3]; int i; for (i = 1; i < _order; i += 2) { num5 *= num6 - data.Coeff[i - 1]; num4 *= num6 - data.Coeff[i]; } if (i == _order) { num5 *= num6 - data.Coeff[i - 1]; num4 *= num4 * (4f - num6 * num6); num5 *= num5; } else { num4 *= num4 * (2f - num6); num5 *= num5 * (2f + num6); } num5 = data.Amp / (float)Math.Sqrt(num4 + num5) - (float)_ampOfs; num5 = (float)Math.Exp(num5 * 0.11512925f); residue[num2] *= num5; while (array[++num2] == num3) { residue[num2] *= num5; } } } else { Array.Clear(residue, 0, num); } } } internal class Floor1 : IFloor { private class Data : IFloorData { internal int[] Posts = new int[64]; internal int PostCount; public bool ExecuteChannel { get { if (ForceEnergy || PostCount > 0) { return !ForceNoEnergy; } return false; } } public bool ForceEnergy { get; set; } public bool ForceNoEnergy { get; set; } } private int[] _partitionClass; private int[] _classDimensions; private int[] _classSubclasses; private int[] _xList; private int[] _classMasterBookIndex; private int[] _hNeigh; private int[] _lNeigh; private int[] _sortIdx; private int _multiplier; private int _range; private int _yBits; private ICodebook[] _classMasterbooks; private ICodebook[][] _subclassBooks; private int[][] _subclassBookIndex; private static readonly int[] _rangeLookup = new int[4] { 256, 128, 86, 64 }; private static readonly int[] _yBitsLookup = new int[4] { 8, 7, 7, 6 }; private static readonly float[] inverse_dB_table = new float[256] { 1.0649863E-07f, 1.1341951E-07f, 1.2079015E-07f, 1.2863978E-07f, 1.369995E-07f, 1.459025E-07f, 1.5538409E-07f, 1.6548181E-07f, 1.7623574E-07f, 1.8768856E-07f, 1.998856E-07f, 2.128753E-07f, 2.2670913E-07f, 2.4144197E-07f, 2.5713223E-07f, 2.7384212E-07f, 2.9163792E-07f, 3.1059022E-07f, 3.307741E-07f, 3.5226967E-07f, 3.7516213E-07f, 3.995423E-07f, 4.255068E-07f, 4.5315863E-07f, 4.8260745E-07f, 5.1397E-07f, 5.4737063E-07f, 5.829419E-07f, 6.208247E-07f, 6.611694E-07f, 7.041359E-07f, 7.4989464E-07f, 7.98627E-07f, 8.505263E-07f, 9.057983E-07f, 9.646621E-07f, 1.0273513E-06f, 1.0941144E-06f, 1.1652161E-06f, 1.2409384E-06f, 1.3215816E-06f, 1.4074654E-06f, 1.4989305E-06f, 1.5963394E-06f, 1.7000785E-06f, 1.8105592E-06f, 1.9282195E-06f, 2.053526E-06f, 2.1869757E-06f, 2.3290977E-06f, 2.4804558E-06f, 2.6416496E-06f, 2.813319E-06f, 2.9961443E-06f, 3.1908505E-06f, 3.39821E-06f, 3.619045E-06f, 3.8542307E-06f, 4.1047006E-06f, 4.371447E-06f, 4.6555283E-06f, 4.958071E-06f, 5.280274E-06f, 5.623416E-06f, 5.988857E-06f, 6.3780467E-06f, 6.7925284E-06f, 7.2339453E-06f, 7.704048E-06f, 8.2047E-06f, 8.737888E-06f, 9.305725E-06f, 9.910464E-06f, 1.0554501E-05f, 1.1240392E-05f, 1.1970856E-05f, 1.2748789E-05f, 1.3577278E-05f, 1.4459606E-05f, 1.5399271E-05f, 1.6400005E-05f, 1.7465769E-05f, 1.8600793E-05f, 1.9809577E-05f, 2.1096914E-05f, 2.2467912E-05f, 2.3928002E-05f, 2.5482977E-05f, 2.7139005E-05f, 2.890265E-05f, 3.078091E-05f, 3.2781227E-05f, 3.4911533E-05f, 3.718028E-05f, 3.9596467E-05f, 4.2169668E-05f, 4.491009E-05f, 4.7828602E-05f, 5.0936775E-05f, 5.424693E-05f, 5.7772202E-05f, 6.152657E-05f, 6.552491E-05f, 6.9783084E-05f, 7.4317984E-05f, 7.914758E-05f, 8.429104E-05f, 8.976875E-05f, 9.560242E-05f, 0.00010181521f, 0.00010843174f, 0.00011547824f, 0.00012298267f, 0.00013097477f, 0.00013948625f, 0.00014855085f, 0.00015820454f, 0.00016848555f, 0.00017943469f, 0.00019109536f, 0.00020351382f, 0.0002167393f, 0.00023082423f, 0.00024582449f, 0.00026179955f, 0.00027881275f, 0.00029693157f, 0.00031622787f, 0.00033677815f, 0.00035866388f, 0.00038197188f, 0.00040679457f, 0.00043323037f, 0.0004613841f, 0.0004913675f, 0.00052329927f, 0.0005573062f, 0.0005935231f, 0.0006320936f, 0.0006731706f, 0.000716917f, 0.0007635063f, 0.00081312325f, 0.00086596457f, 0.00092223985f, 0.0009821722f, 0.0010459992f, 0.0011139743f, 0.0011863665f, 0.0012634633f, 0.0013455702f, 0.0014330129f, 0.0015261382f, 0.0016253153f, 0.0017309374f, 0.0018434235f, 0.0019632196f, 0.0020908006f, 0.0022266726f, 0.0023713743f, 0.0025254795f, 0.0026895993f, 0.0028643848f, 0.0030505287f, 0.003248769f, 0.0034598925f, 0.0036847359f, 0.0039241905f, 0.0041792067f, 0.004450795f, 0.004740033f, 0.005048067f, 0.0053761187f, 0.005725489f, 0.0060975635f, 0.0064938175f, 0.0069158226f, 0.0073652514f, 0.007843887f, 0.008353627f, 0.008896492f, 0.009474637f, 0.010090352f, 0.01074608f, 0.011444421f, 0.012188144f, 0.012980198f, 0.013823725f, 0.014722068f, 0.015678791f, 0.016697686f, 0.017782796f, 0.018938422f, 0.020169148f, 0.021479854f, 0.022875736f, 0.02436233f, 0.025945531f, 0.027631618f, 0.029427277f, 0.031339627f, 0.03337625f, 0.035545226f, 0.037855156f, 0.0403152f, 0.042935107f, 0.045725275f, 0.048696756f, 0.05186135f, 0.05523159f, 0.05882085f, 0.062643364f, 0.06671428f, 0.07104975f, 0.075666964f, 0.08058423f, 0.08582105f, 0.09139818f, 0.097337745f, 0.1036633f, 0.11039993f, 0.11757434f, 0.12521498f, 0.13335215f, 0.14201812f, 0.15124726f, 0.16107617f, 0.1715438f, 0.18269168f, 0.19456401f, 0.20720787f, 0.22067343f, 0.23501402f, 0.25028655f, 0.26655158f, 0.28387362f, 0.3023213f, 0.32196787f, 0.34289113f, 0.36517414f, 0.3889052f, 0.41417846f, 0.44109413f, 0.4697589f, 0.50028646f, 0.53279793f, 0.5674221f, 0.6042964f, 0.64356697f, 0.6853896f, 0.72993004f, 0.777365f, 0.8278826f, 0.88168305f, 0.9389798f, 1f }; public void Init(IPacket packet, int channels, int block0Size, int block1Size, ICodebook[] codebooks) { int num = -1; _partitionClass = new int[(uint)packet.ReadBits(5)]; for (int i = 0; i < _partitionClass.Length; i++) { _partitionClass[i] = (int)packet.ReadBits(4); if (_partitionClass[i] > num) { num = _partitionClass[i]; } } _classDimensions = new int[++num]; _classSubclasses = new int[num]; _classMasterbooks = new ICodebook[num]; _classMasterBookIndex = new int[num]; _subclassBooks = new ICodebook[num][]; _subclassBookIndex = new int[num][]; for (int j = 0; j < num; j++) { _classDimensions[j] = (int)packet.ReadBits(3) + 1; _classSubclasses[j] = (int)packet.ReadBits(2); if (_classSubclasses[j] > 0) { _classMasterBookIndex[j] = (int)packet.ReadBits(8); _classMasterbooks[j] = codebooks[_classMasterBookIndex[j]]; } _subclassBooks[j] = new ICodebook[1 << _classSubclasses[j]]; _subclassBookIndex[j] = new int[_subclassBooks[j].Length]; for (int k = 0; k < _subclassBooks[j].Length; k++) { int num2 = (int)packet.ReadBits(8) - 1; if (num2 >= 0) { _subclassBooks[j][k] = codebooks[num2]; } _subclassBookIndex[j][k] = num2; } } _multiplier = (int)packet.ReadBits(2); _range = _rangeLookup[_multiplier]; _yBits = _yBitsLookup[_multiplier]; _multiplier++; int num3 = (int)packet.ReadBits(4); List<int> list = new List<int>(); list.Add(0); list.Add(1 << num3); for (int l = 0; l < _partitionClass.Length; l++) { int num4 = _partitionClass[l]; for (int m = 0; m < _classDimensions[num4]; m++) { list.Add((int)packet.ReadBits(num3)); } } _xList = list.ToArray(); _lNeigh = new int[list.Count]; _hNeigh = new int[list.Count]; _sortIdx = new int[list.Count]; _sortIdx[0] = 0; _sortIdx[1] = 1; for (int n = 2; n < _lNeigh.Length; n++) { _lNeigh[n] = 0; _hNeigh[n] = 1; _sortIdx[n] = n; for (int num5 = 2; num5 < n; num5++) { int num6 = _xList[num5]; if (num6 < _xList[n]) { if (num6 > _xList[_lNeigh[n]]) { _lNeigh[n] = num5; } } else if (num6 < _xList[_hNeigh[n]]) { _hNeigh[n] = num5; } } } for (int num7 = 0; num7 < _sortIdx.Length - 1; num7++) { for (int num8 = num7 + 1; num8 < _sortIdx.Length; num8++) { if (_xList[num7] == _xList[num8]) { throw new InvalidDataException(); } if (_xList[_sortIdx[num7]] > _xList[_sortIdx[num8]]) { int num9 = _sortIdx[num7]; _sortIdx[num7] = _sortIdx[num8]; _sortIdx[num8] = num9; } } } } public IFloorData Unpack(IPacket packet, int blockSize, int channel) { Data data = new Data(); if (packet.ReadBit()) { int num = 2; data.Posts[0] = (int)packet.ReadBits(_yBits); data.Posts[1] = (int)packet.ReadBits(_yBits); for (int i = 0; i < _partitionClass.Length; i++) { int num2 = _partitionClass[i]; int num3 = _classDimensions[num2]; int num4 = _classSubclasses[num2]; int num5 = (1 << num4) - 1; uint num6 = 0u; if (num4 > 0 && (num6 = (uint)_classMasterbooks[num2].DecodeScalar(packet)) == uint.MaxValue) { num = 0; break; } for (int j = 0; j < num3; j++) { ICodebook codebook = _subclassBooks[num2][num6 & num5]; num6 >>= num4; if (codebook != null && (data.Posts[num] = codebook.DecodeScalar(packet)) == -1) { num = 0; i = _partitionClass.Length; break; } num++; } } data.PostCount = num; } return data; } public void Apply(IFloorData floorData, int blockSize, float[] residue) { if (!(floorData is Data data)) { throw new ArgumentException("Incorrect packet data!", "packetData"); } int num = blockSize / 2; if (data.PostCount > 0) { bool[] array = UnwrapPosts(data); int num2 = 0; int num3 = data.Posts[0] * _multiplier; for (int i = 1; i < data.PostCount; i++) { int num4 = _sortIdx[i]; if (array[num4]) { int num5 = _xList[num4]; int num6 = data.Posts[num4] * _multiplier; if (num2 < num) { RenderLineMulti(num2, num3, Math.Min(num5, num), num6, residue); } num2 = num5; num3 = num6; } if (num2 >= num) { break; } } if (num2 < num) { RenderLineMulti(num2, num3, num, num3, residue); } } else { Array.Clear(residue, 0, num); } } private bool[] UnwrapPosts(Data data) { bool[] array = new bool[64]; array[0] = true; array[1] = true; int[] array2 = new int[64]; array2[0] = data.Posts[0]; array2[1] = data.Posts[1]; for (int i = 2; i < data.PostCount; i++) { int num = _lNeigh[i]; int num2 = _hNeigh[i]; int num3 = RenderPoint(_xList[num], array2[num], _xList[num2], array2[num2], _xList[i]); int num4 = data.Posts[i]; int num5 = _range - num3; int num6 = num3; int num7 = ((num5 >= num6) ? (num6 * 2) : (num5 * 2)); if (num4 != 0) { array[num] = true; array[num2] = true; array[i] = true; if (num4 >= num7) { if (num5 > num6) { array2[i] = num4 - num6 + num3; } else { array2[i] = num3 - num4 + num5 - 1; } } else if (num4 % 2 == 1) { array2[i] = num3 - (num4 + 1) / 2; } else { array2[i] = num3 + num4 / 2; } } else { array[i] = false; array2[i] = num3; } } for (int j = 0; j < data.PostCount; j++) { data.Posts[j] = array2[j]; } return array; } private int RenderPoint(int x0, int y0, int x1, int y1, int X) { int num = y1 - y0; int num2 = x1 - x0; int num3 = Math.Abs(num) * (X - x0) / num2; if (num < 0) { return y0 - num3; } return y0 + num3; } private void RenderLineMulti(int x0, int y0, int x1, int y1, float[] v) { int num = y1 - y0; int num2 = x1 - x0; int num3 = Math.Abs(num); int num4 = 1 - ((num >> 31) & 1) * 2; int num5 = num / num2; int num6 = x0; int num7 = y0; int num8 = -num2; v[x0] *= inverse_dB_table[y0]; num3 -= Math.Abs(num5) * num2; while (++num6 < x1) { num7 += num5; num8 += num3; if (num8 >= 0) { num8 -= num2; num7 += num4; } v[num6] *= inverse_dB_table[num7]; } } } internal class Huffman : IHuffman, IComparer<HuffmanListNode> { private const int MAX_TABLE_BITS = 10; public int TableBits { get; private set; } public IReadOnlyList<HuffmanListNode> PrefixTree { get; private set; } public IReadOnlyList<HuffmanListNode> OverflowList { get; private set; } public void GenerateTable(IReadOnlyList<int> values, int[] lengthList, int[] codeList) { HuffmanListNode[] array = new HuffmanListNode[lengthList.Length]; int num = 0; for (int i = 0; i < array.Length; i++) { array[i] = new HuffmanListNode { Value = values[i], Length = ((lengthList[i] <= 0) ? 99999 : lengthList[i]), Bits = codeList[i], Mask = (1 << lengthList[i]) - 1 }; if (lengthList[i] > 0 && num < lengthList[i]) { num = lengthList[i]; } } Array.Sort(array, 0, array.Length, this); int num2 = ((num > 10) ? 10 : num); List<HuffmanListNode> list = new List<HuffmanListNode>(1 << num2); List<HuffmanListNode> list2 = null; for (int j = 0; j < array.Length && array[j].Length < 99999; j++) { int length = array[j].Length; if (length > num2) { list2 = new List<HuffmanListNode>(array.Length - j); for (; j < array.Length && array[j].Length < 99999; j++) { list2.Add(array[j]); } continue; } int num3 = 1 << num2 - length; HuffmanListNode huffmanListNode = array[j]; for (int k = 0; k < num3; k++) { int num4 = (k << length) | huffmanListNode.Bits; while (list.Count <= num4) { list.Add(null); } list[num4] = huffmanListNode; } } while (list.Count < 1 << num2) { list.Add(null); } TableBits = num2; PrefixTree = list; OverflowList = list2; } int IComparer<HuffmanListNode>.Compare(HuffmanListNode x, HuffmanListNode y) { int num = x.Length - y.Length; if (num == 0) { return x.Bits - y.Bits; } return num; } } [Obsolete("Moved to NVorbis.Contracts.IContainerReader", true)] public interface IContainerReader : NVorbis.Contracts.IContainerReader, IDisposable { [Obsolete("Use Streams.Select(s => s.StreamSerial).ToArray() instead.", true)] int[] StreamSerials { get; } [Obsolete("No longer supported.", true)] int PagesRead { get; } [Obsolete("Moved to NewStreamCallback.", true)] event EventHandler<NewStreamEventArgs> NewStream; [Obsolete("Renamed to TryInit().", true)] bool Init(); [Obsolete("No longer supported.", true)] int GetTotalPageCount(); } [Obsolete("Moved to NVorbis.Contracts.IPacketProvider", true)] public interface IPacketProvider : NVorbis.Contracts.IPacketProvider { [Obsolete("Moved to per-stream IStreamStats instance on IStreamDecoder.Stats or VorbisReader.Stats.", true)] long ContainerBits { get; } [Obsolete("No longer supported.", true)] event EventHandler ParameterChange; [Obsolete("No longer supported.", true)] int GetTotalPageCount(); [Obsolete("Getting a packet by index is no longer supported.", true)] DataPacket GetPacket(int packetIndex); [Obsolete("Moved to long SeekTo(long, int, GetPacketGranuleCount)", true)] DataPacket FindPacket(long granulePos, Func<DataPacket, DataPacket, int> packetGranuleCountCallback); [Obsolete("Seeking to a specified packet is no longer supported. See SeekTo(...) instead.", true)] void SeekToPacket(DataPacket packet, int preRoll); } [Obsolete("Moved to NVorbis.Contracts.IStreamStats", true)] public interface IVorbisStreamStatus : IStreamStats { [Obsolete("No longer supported.", true)] TimeSpan PageLatency { get; } [Obsolete("No longer supported.", true)] TimeSpan PacketLatency { get; } [Obsolete("No longer supported.", true)] TimeSpan SecondLatency { get; } [Obsolete("No longer supported.", true)] int PagesRead { get; } [Obsolete("No longer supported.", true)] int TotalPages { get; } [Obsolete("Use IStreamDecoder.HasClipped instead. VorbisReader.HasClipped will return the same value for the stream it is handling.", true)] bool Clipped { get; } } internal class Mapping : IMapping { private IMdct _mdct; private int[] _couplingAngle; private int[] _couplingMangitude; private IFloor[] _submapFloor; private IResidue[] _submapResidue; private IFloor[] _channelFloor; private IResidue[] _channelResidue; public void Init(IPacket packet, int channels, IFloor[] floors, IResidue[] residues, IMdct mdct) { int num = 1; if (packet.ReadBit()) { num += (int)packet.ReadBits(4); } int num2 = 0; if (packet.ReadBit()) { num2 = (int)packet.ReadBits(8) + 1; } int count = Utils.ilog(channels - 1); _couplingAngle = new int[num2]; _couplingMangitude = new int[num2]; for (int i = 0; i < num2; i++) { int num3 = (int)packet.ReadBits(count); int num4 = (int)packet.ReadBits(count); if (num3 == num4 || num3 > channels - 1 || num4 > channels - 1) { throw new InvalidDataException("Invalid magnitude or angle in mapping header!"); } _couplingAngle[i] = num4; _couplingMangitude[i] = num3; } if (packet.ReadBits(2) != 0L) { throw new InvalidDataException("Reserved bits not 0 in mapping header."); } int[] array = new int[channels]; if (num > 1) { for (int j = 0; j < channels; j++) { array[j] = (int)packet.ReadBits(4); if (array[j] > num) { throw new InvalidDataException("Invalid channel mux submap index in mapping header!"); } } } _submapFloor = new IFloor[num]; _submapResidue = new IResidue[num]; for (int k = 0; k < num; k++) { packet.SkipBits(8); int num5 = (int)packet.ReadBits(8); if (num5 >= floors.Length) { throw new InvalidDataException("Invalid floor number in mapping header!"); } int num6 = (int)packet.ReadBits(8); if (num6 >= residues.Length) { throw new InvalidDataException("Invalid residue number in mapping header!"); } _submapFloor[k] = floors[num5]; _submapResidue[k] = residues[num6]; } _channelFloor = new IFloor[channels]; _channelResidue = new IResidue[channels]; for (int l = 0; l < channels; l++) { _channelFloor[l] = _submapFloor[array[l]]; _channelResidue[l] = _submapResidue[array[l]]; } _mdct = mdct; } public void DecodePacket(IPacket packet, int blockSize, int channels, float[][] buffer) { int num = blockSize >> 1; IFloorData[] array = new IFloorData[_channelFloor.Length]; bool[] array2 = new bool[_channelFloor.Length]; for (int i = 0; i < _channelFloor.Length; i++) { array[i] = _channelFloor[i].Unpack(packet, blockSize, i); array2[i] = !array[i].ExecuteChannel; Array.Clear(buffer[i], 0, num); } for (int j = 0; j < _couplingAngle.Length; j++) { if (array[_couplingAngle[j]].ExecuteChannel || array[_couplingMangitude[j]].ExecuteChannel) { array[_couplingAngle[j]].ForceEnergy = true; array[_couplingMangitude[j]].ForceEnergy = true; } } for (int k = 0; k < _submapFloor.Length; k++) { for (int l = 0; l < _channelFloor.Length; l++) { if (_submapFloor[k] != _channelFloor[l] || _submapResidue[k] != _channelResidue[l]) { array[l].ForceNoEnergy = true; } } _submapResidue[k].Decode(packet, array2, blockSize, buffer); } for (int num2 = _couplingAngle.Length - 1; num2 >= 0; num2--) { if (array[_couplingAngle[num2]].ExecuteChannel || array[_couplingMangitude[num2]].ExecuteChannel) { float[] array3 = buffer[_couplingMangitude[num2]]; float[] array4 = buffer[_couplingAngle[num2]]; for (int m = 0; m < num; m++) { float num3 = array3[m]; float num4 = array4[m]; float num5; float num6; if (num3 > 0f) { if (num4 > 0f) { num5 = num3; num6 = num3 - num4; } else { num6 = num3; num5 = num3 + num4; } } else if (num4 > 0f) { num5 = num3; num6 = num3 + num4; } else { num6 = num3; num5 = num3 - num4; } array3[m] = num5; array4[m] = num6; } } } for (int n = 0; n < _channelFloor.Length; n++) { if (array[n].ExecuteChannel) { _channelFloor[n].Apply(array[n], blockSize, buffer[n]); _mdct.Reverse(buffer[n], blockSize); } else { Array.Clear(buffer[n], num, num); } } } } internal class Mdct : IMdct { private class MdctImpl { private readonly int _n; private readonly int _n2; private readonly int _n4; private readonly int _n8; private readonly int _ld; private readonly float[] _a; private readonly float[] _b; private readonly float[] _c; private readonly ushort[] _bitrev; public MdctImpl(int n) { _n = n; _n2 = n >> 1; _n4 = _n2 >> 1; _n8 = _n4 >> 1; _ld = Utils.ilog(n) - 1; _a = new float[_n2]; _b = new float[_n2]; _c = new float[_n4]; int num; int num2 = (num = 0); while (num2 < _n4) { _a[num] = (float)Math.Cos((float)(4 * num2) * MathF.PI / (float)n); _a[num + 1] = (float)(0.0 - Math.Sin((float)(4 * num2) * MathF.PI / (float)n)); _b[num] = (float)Math.Cos((float)(num + 1) * MathF.PI / (float)n / 2f) * 0.5f; _b[num + 1] = (float)Math.Sin((float)(num + 1) * MathF.PI / (float)n / 2f) * 0.5f; num2++; num += 2; } num2 = (num = 0); while (num2 < _n8) { _c[num] = (float)Math.Cos((float)(2 * (num + 1)) * MathF.PI / (float)n); _c[num + 1] = (float)(0.0 - Math.Sin((float)(2 * (num + 1)) * MathF.PI / (float)n)); num2++; num += 2; } _bitrev = new ushort[_n8]; for (int i = 0; i < _n8; i++) { _bitrev[i] = (ushort)(Utils.BitReverse((uint)i, _ld - 3) << 2); } } internal void CalcReverse(float[] buffer) { float[] array = new float[_n2]; int num = _n2 - 2; int num2 = 0; int i = 0; for (int n = _n2; i != n; i += 4) { array[num + 1] = buffer[i] * _a[num2] - buffer[i + 2] * _a[num2 + 1]; array[num] = buffer[i] * _a[num2 + 1] + buffer[i + 2] * _a[num2]; num -= 2; num2 += 2; } i = _n2 - 3; while (num >= 0) { array[num + 1] = (0f - buffer[i + 2]) * _a[num2] - (0f - buffer[i]) * _a[num2 + 1]; array[num] = (0f - buffer[i + 2]) * _a[num2 + 1] + (0f - buffer[i]) * _a[num2]; num -= 2; num2 += 2; i -= 4; } float[] array2 = array; int num3 = _n2 - 8; int num4 = _n4; int num5 = 0; int num6 = _n4; int num7 = 0; while (num3 >= 0) { float num8 = array2[num4 + 1] - array2[num5 + 1]; float num9 = array2[num4] - array2[num5]; buffer[num6 + 1] = array2[num4 + 1] + array2[num5 + 1]; buffer[num6] = array2[num4] + array2[num5]; buffer[num7 + 1] = num8 * _a[num3 + 4] - num9 * _a[num3 + 5]; buffer[num7] = num9 * _a[num3 + 4] + num8 * _a[num3 + 5]; num8 = array2[num4 + 3] - array2[num5 + 3]; num9 = array2[num4 + 2] - array2[num5 + 2]; buffer[num6 + 3] = array2[num4 + 3] + array2[num5 + 3]; buffer[num6 + 2] = array2[num4 + 2] + array2[num5 + 2]; buffer[num7 + 3] = num8 * _a[num3] - num9 * _a[num3 + 1]; buffer[num7 + 2] = num9 * _a[num3] + num8 * _a[num3 + 1]; num3 -= 8; num6 += 4; num7 += 4; num4 += 4; num5 += 4; } int n2 = _n >> 4; int num10 = _n2 - 1; _ = _n4; step3_iter0_loop(n2, buffer, num10 - 0, -_n8); step3_iter0_loop(_n >> 4, buffer, _n2 - 1 - _n4, -_n8); int lim = _n >> 5; int num11 = _n2 - 1; _ = _n8; step3_inner_r_loop(lim, buffer, num11 - 0, -(_n >> 4), 16); step3_inner_r_loop(_n >> 5, buffer, _n2 - 1 - _n8, -(_n >> 4), 16); step3_inner_r_loop(_n >> 5, buffer, _n2 - 1 - _n8 * 2, -(_n >> 4), 16); step3_inner_r_loop(_n >> 5, buffer, _n2 - 1 - _n8 * 3, -(_n >> 4), 16); int j; for (j = 2; j < _ld - 3 >> 1; j++) { int num12 = _n >> j + 2; int num13 = num12 >> 1; int num14 = 1 << j + 1; for (int k = 0; k < num14; k++) { step3_inner_r_loop(_n >> j + 4, buffer, _n2 - 1 - num12 * k, -num13, 1 << j + 3); } } for (; j < _ld - 6; j++) { int num15 = _n >> j + 2; int num16 = 1 << j + 3; int num17 = num15 >> 1; int num18 = _n >> j + 6; int n3 = 1 << j + 1; int num19 = _n2 - 1; int num20 = 0; for (int num21 = num18; num21 > 0; num21--) { step3_inner_s_loop(n3, buffer, num19, -num17, num20, num16, num15); num20 += num16 * 4; num19 -= 8; } } step3_inner_s_loop_ld654(_n >> 5, buffer, _n2 - 1, _n); int num22 = 0; int num23 = _n4 - 4; int num24 = _n2 - 4; while (num23 >= 0) { int num25 = _bitrev[num22]; array2[num24 + 3] = buffer[num25]; array2[num24 + 2] = buffer[num25 + 1]; array2[num23 + 3] = buffer[num25 + 2]; array2[num23 + 2] = buffer[num25 + 3]; num25 = _bitrev[num22 + 1]; array2[num24 + 1] = buffer[num25]; array2[num24] = buffer[num25 + 1]; array2[num23 + 1] = buffer[num25 + 2]; array2[num23] = buffer[num25 + 3]; num23 -= 4; num24 -= 4; num22 += 2; } int num26 = 0; int num27 = 0; int num28 = _n2 - 4; while (num27 < num28) { float num29 = array2[num27] - array2[num28 + 2]; float num30 = array2[num27 + 1] + array2[num28 + 3]; float num31 = _c[num26 + 1] * num29 + _c[num26] * num30; float num32 = _c[num26 + 1] * num30 - _c[num26] * num29; float num33 = array2[num27] + array2[num28 + 2]; float num34 = array2[num27 + 1] - array2[num28 + 3]; array2[num27] = num33 + num31; array2[num27 + 1] = num34 + num32; array2[num28 + 2] = num33 - num31; array2[num28 + 3] = num32 - num34; num29 = array2[num27 + 2] - array2[num28]; num30 = array2[num27 + 3] + array2[num28 + 1]; num31 = _c[num26 + 3] * num29 + _c[num26 + 2] * num30; num32 = _c[num26 + 3] * num30 - _c[num26 + 2] * num29; num33 = array2[num27 + 2] + array2[num28]; num34 = array2[num27 + 3] - array2[num28 + 1]; array2[num27 + 2] = num33 + num31; array2[num27 + 3] = num34 + num32; array2[num28] = num33 - num31; array2[num28 + 1] = num32 - num34; num26 += 4; num27 += 4; num28 -= 4; } int num35 = _n2 - 8; int num36 = _n2 - 8; int num37 = 0; int num38 = _n2 - 4; int num39 = _n2; int num40 = _n - 4; while (num36 >= 0) { float num41 = array[num36 + 6] * _b[num35 + 7] - array[num36 + 7] * _b[num35 + 6]; float num42 = (0f - array[num36 + 6]) * _b[num35 + 6] - array[num36 + 7] * _b[num35 + 7]; buffer[num37] = num41; buffer[num38 + 3] = 0f - num41; buffer[num39] = num42; buffer[num40 + 3] = num42; float num43 = array[num36 + 4] * _b[num35 + 5] - array[num36 + 5] * _b[num35 + 4]; float num44 = (0f - array[num36 + 4]) * _b[num35 + 4] - array[num36 + 5] * _b[num35 + 5]; buffer[num37 + 1] = num43; buffer[num38 + 2] = 0f - num43; buffer[num39 + 1] = num44; buffer[num40 + 2] = num44; num41 = array[num36 + 2] * _b[num35 + 3] - array[num36 + 3] * _b[num35 + 2]; num42 = (0f - array[num36 + 2]) * _b[num35 + 2] - array[num36 + 3] * _b[num35 + 3]; buffer[num37 + 2] = num41; buffer[num38 + 1] = 0f - num41; buffer[num39 + 2] = num42; buffer[num40 + 1] = num42; num43 = array[num36] * _b[num35 + 1] - array[num36 + 1] * _b[num35]; num44 = (0f - array[num36]) * _b[num35] - array[num36 + 1] * _b[num35 + 1]; buffer[num37 + 3] = num43; buffer[num38] = 0f - num43; buffer[num39 + 3] = num44; buffer[num40] = num44; num35 -= 8; num36 -= 8; num37 += 4; num39 += 4; num38 -= 4; num40 -= 4; } } private void step3_iter0_loop(int n, float[] e, int i_off, int k_off) { int num = i_off; int num2 = num + k_off; int num3 = 0; for (int num4 = n >> 2; num4 > 0; num4--) { float num5 = e[num] - e[num2]; float num6 = e[num - 1] - e[num2 - 1]; e[num] += e[num2]; e[num - 1] += e[num2 - 1]; e[num2] = num5 * _a[num3] - num6 * _a[num3 + 1]; e[num2 - 1] = num6 * _a[num3] + num5 * _a[num3 + 1]; num3 += 8; num5 = e[num - 2] - e[num2 - 2]; num6 = e[num - 3] - e[num2 - 3]; e[num - 2] += e[num2 - 2]; e[num - 3] += e[num2 - 3]; e[num2 - 2] = num5 * _a[num3] - num6 * _a[num3 + 1]; e[num2 - 3] = num6 * _a[num3] + num5 * _a[num3 + 1]; num3 += 8; num5 = e[num - 4] - e[num2 - 4]; num6 = e[num - 5] - e[num2 - 5]; e[num - 4] += e[num2 - 4]; e[num - 5] += e[num2 - 5]; e[num2 - 4] = num5 * _a[num3] - num6 * _a[num3 + 1]; e[num2 - 5] = num6 * _a[num3] + num5 * _a[num3 + 1]; num3 += 8; num5 = e[num - 6] - e[num2 - 6]; num6 = e[num - 7] - e[num2 - 7]; e[num - 6] += e[num2 - 6]; e[num - 7] += e[num2 - 7]; e[num2 - 6] = num5 * _a[num3] - num6 * _a[num3 + 1]; e[num2 - 7] = num6 * _a[num3] + num5 * _a[num3 + 1]; num3 += 8; num -= 8; num2 -= 8; } } private void step3_inner_r_loop(int lim, float[] e, int d0, int k_off, int k1) { int num = d0; int num2 = num + k_off; int num3 = 0; for (int num4 = lim >> 2; num4 > 0; num4--) { float num5 = e[num] - e[num2]; float num6 = e[num - 1] - e[num2 - 1]; e[num] += e[num2]; e[num - 1] += e[num2 - 1]; e[num2] = num5 * _a[num3] - num6 * _a[num3 + 1]; e[num2 - 1] = num6 * _a[num3] + num5 * _a[num3 + 1]; num3 += k1; num5 = e[num - 2] - e[num2 - 2]; num6 = e[num - 3] - e[num2 - 3]; e[num - 2] += e[num2 - 2]; e[num - 3] += e[num2 - 3]; e[num2 - 2] = num5 * _a[num3] - num6 * _a[num3 + 1]; e[num2 - 3] = num6 * _a[num3] + num5 * _a[num3 + 1]; num3 += k1; num5 = e[num - 4] - e[num2 - 4]; num6 = e[num - 5] - e[num2 - 5]; e[num - 4] += e[num2 - 4]; e[num - 5] += e[num2 - 5]; e[num2 - 4] = num5 * _a[num3] - num6 * _a[num3 + 1]; e[num2 - 5] = num6 * _a[num3] + num5 * _a[num3 + 1]; num3 += k1; num5 = e[num - 6] - e[num2 - 6]; num6 = e[num - 7] - e[num2 - 7]; e[num - 6] += e[num2 - 6]; e[num - 7] += e[num2 - 7]; e[num2 - 6] = num5 * _a[num3] - num6 * _a[num3 + 1]; e[num2 - 7] = num6 * _a[num3] + num5 * _a[num3 + 1]; num3 += k1; num -= 8; num2 -= 8; } } private void step3_inner_s_loop(int n, float[] e, int i_off, int k_off, int a, int a_off, int k0) { float num = _a[a]; float num2 = _a[a + 1]; float num3 = _a[a + a_off]; float num4 = _a[a + a_off + 1]; float num5 = _a[a + a_off * 2]; float num6 = _a[a + a_off * 2 + 1]; float num7 = _a[a + a_off * 3]; float num8 = _a[a + a_off * 3 + 1]; int num9 = i_off; int num10 = num9 + k_off; for (int num11 = n; num11 > 0; num11--) { float num12 = e[num9] - e[num10]; float num13 = e[num9 - 1] - e[num10 - 1]; e[num9] += e[num10]; e[num9 - 1] += e[num10 - 1]; e[num10] = num12 * num - num13 * num2; e[num10 - 1] = num13 * num + num12 * num2; num12 = e[num9 - 2] - e[num10 - 2]; num13 = e[num9 - 3] - e[num10 - 3]; e[num9 - 2] += e[num10 - 2]; e[num9 - 3] += e[num10 - 3]; e[num10 - 2] = num12 * num3 - num13 * num4; e[num10 - 3] = num13 * num3 + num12 * num4; num12 = e[num9 - 4] - e[num10 - 4]; num13 = e[num9 - 5] - e[num10 - 5]; e[num9 - 4] += e[num10 - 4]; e[num9 - 5] += e[num10 - 5]; e[num10 - 4] = num12 * num5 - num13 * num6; e[num10 - 5] = num13 * num5 + num12 * num6; num12 = e[num9 - 6] - e[num10 - 6]; num13 = e[num9 - 7] - e[num10 - 7]; e[num9 - 6] += e[num10 - 6]; e[num9 - 7] += e[num10 - 7]; e[num10 - 6] = num12 * num7 - num13 * num8; e[num10 - 7] = num13 * num7 + num12 * num8; num9 -= k0; num10 -= k0; } } private void step3_inner_s_loop_ld654(int n, float[] e, int i_off, int base_n) { int num = base_n >> 3; float num2 = _a[num]; int num3 = i_off; int num4 = num3 - 16 * n; while (num3 > num4) { float num5 = e[num3] - e[num3 - 8]; float num6 = e[num3 - 1] - e[num3 - 9]; e[num3] += e[num3 - 8]; e[num3 - 1] += e[num3 - 9]; e[num3 - 8] = num5; e[num3 - 9] = num6; num5 = e[num3 - 2] - e[num3 - 10]; num6 = e[num3 - 3] - e[num3 - 11]; e[num3 - 2] += e[num3 - 10]; e[num3 - 3] += e[num3 - 11]; e[num3 - 10] = (num5 + num6) * num2; e[num3 - 11] = (num6 - num5) * num2; num5 = e[num3 - 12] - e[num3 - 4]; num6 = e[num3 - 5] - e[num3 - 13]; e[num3 - 4] += e[num3 - 12]; e[num3 - 5] += e[num3 - 13]; e[num3 - 12] = num6; e[num3 - 13] = num5; num5 = e[num3 - 14] - e[num3 - 6]; num6 = e[num3 - 7] - e[num3 - 15]; e[num3 - 6] += e[num3 - 14]; e[num3 - 7] += e[num3 - 15]; e[num3 - 14] = (num5 + num6) * num2; e[num3 - 15] = (num5 - num6) * num2; iter_54(e, num3); iter_54(e, num3 - 8); num3 -= 16; } } private void iter_54(float[] e, int z) { float num = e[z] - e[z - 4]; float num2 = e[z] + e[z - 4]; float num3 = e[z - 2] + e[z - 6]; float num4 = e[z - 2] - e[z - 6]; e[z] = num2 + num3; e[z - 2] = num2 - num3; float num5 = e[z - 3] - e[z - 7]; e[z - 4] = num + num5; e[z - 6] = num - num5; float num6 = e[z - 1] - e[z - 5]; float num7 = e[z - 1] + e[z - 5]; float num8 = e[z - 3] + e[z - 7]; e[z - 1] = num7 + num8; e[z - 3] = num7 - num8; e[z - 5] = num6 - num4; e[z - 7] = num6 + num4; } } private const float M_PI = MathF.PI; private Dictionary<int, MdctImpl> _setupCache = new Dictionary<int, MdctImpl>(); public void Reverse(float[] samples, int sampleCount) { if (!_setupCache.TryGetValue(sampleCount, out var value)) { value = new MdctImpl(sampleCount); _setupCache[sampleCount] = value; } value.CalcReverse(samples); } } internal class Mode : IMode { private struct OverlapInfo { public int PacketStartIndex; public int PacketTotalLength; public int PacketValidLength; } private const float M_PI2 = MathF.PI / 2f; private int _channels; private bool _blockFlag; private int _blockSize; private IMapping _mapping; private float[][] _windows; private OverlapInfo[] _overlapInfo; public void Init(IPacket packet, int channels, int block0Size, int block1Size, IMapping[] mappings) { _channels = channels; _blockFlag = packet.ReadBit(); if (packet.ReadBits(32) != 0L) { throw new InvalidDataException("Mode header had invalid window or transform type!"); } int num = (int)packet.ReadBits(8); if (num >= mappings.Length) { throw new InvalidDataException("Mode header had invalid mapping index!"); } _mapping = mappings[num]; if (_blockFlag) { _blockSize = block1Size; _windows = new float[4][] { CalcWindow(block0Size, block1Size, block0Size), CalcWindow(block1Size, block1Size, block0Size), CalcWindow(block0Size, block1Size, block1Size), CalcWindow(block1Size, block1Size, block1Size) }; _overlapInfo = new OverlapInfo[4] { CalcOverlap(block0Size, block1Size, block0Size), CalcOverlap(block1Size, block1Size, block0Size), CalcOverlap(block0Size, block1Size, block1Size), CalcOverlap(block1Size, block1Size, block1Size) }; } else { _blockSize = block0Size; _windows = new float[1][] { CalcWindow(block0Size, block0Size, block0Size) }; } } private static float[] CalcWindow(int prevBlockSize, int blockSize, int nextBlockSize) { float[] array = new float[blockSize]; int num = prevBlockSize / 2; int num2 = nextBlockSize / 2; int num3 = blockSize / 4 - num / 2; int num4 = blockSize - blockSize / 4 - num2 / 2; for (int i = 0; i < num; i++) { float num5 = (float)Math.Sin(((double)i + 0.5) / (double)num * 1.5707963705062866); num5 *= num5; array[num3 + i] = (float)Math.Sin(num5 * (MathF.PI / 2f)); } for (int j = num3 + num; j < num4; j++) { array[j] = 1f; } for (int k = 0; k < num2; k++) { float num6 = (float)Math.Sin(((double)(num2 - k) - 0.5) / (double)num2 * 1.5707963705062866); num6 *= num6; array[num4 + k] = (float)Math.Sin(num6 * (MathF.PI / 2f)); } return array; } private static OverlapInfo CalcOverlap(int prevBlockSize, int blockSize, int nextBlockSize) { int num = prevBlockSize / 4; int num2 = nextBlockSize / 4; int packetStartIndex = blockSize / 4 - num; int num3 = blockSize / 4 * 3 + num2; int packetValidLength = num3 - num2 * 2; OverlapInfo result = default(OverlapInfo); result.PacketStartIndex = packetStartIndex; result.PacketValidLength = packetValidLength; result.PacketTotalLength = num3; return result; } private bool GetPacketInfo(IPacket packet, out int windowIndex, out int packetStartIndex, out int packetValidLength, out int packetTotalLength) { if (packet.IsShort) { windowIndex = 0; packetStartIndex = 0; packetValidLength = 0; packetTotalLength = 0; return false; } if (_blockFlag) { bool flag = packet.ReadBit(); bool flag2 = packet.ReadBit(); windowIndex = (flag ? 1 : 0) + (flag2 ? 2 : 0); OverlapInfo overlapInfo = _overlapInfo[windowIndex]; packetStartIndex = overlapInfo.PacketStartIndex; packetValidLength = overlapInfo.PacketValidLength; packetTotalLength = overlapInfo.PacketTotalLength; } else { windowIndex = 0; packetStartIndex = 0; packetValidLength = _blockSize / 2; packetTotalLength = _blockSize; } return true; } public bool Decode(IPacket packet, float[][] buffer, out int packetStartindex, out int packetValidLength, out int packetTotalLength) { if (GetPacketInfo(packet, out var windowIndex, out packetStartindex, out packetValidLength, out packetTotalLength)) { _mapping.DecodePacket(packet, _blockSize, _channels, buffer); float[] array = _windows[windowIndex]; for (int i = 0; i < _blockSize; i++) { for (int j = 0; j < _channels; j++) { buffer[j][i] *= array[i]; } } return true; } return false; } public int GetPacketSampleCount(IPacket packet) { GetPacketInfo(packet, out var _, out var packetStartIndex, out var packetValidLength, out var _); return packetValidLength - packetStartIndex; } } [Serializable] public class NewStreamEventArgs : EventArgs { public IStreamDecoder StreamDecoder { get; } public bool IgnoreStream { get; set; } public NewStreamEventArgs(IStreamDecoder streamDecoder) { StreamDecoder = streamDecoder ?? throw new ArgumentNullException("streamDecoder"); } } internal class Residue0 : IResidue { private int _channels; private int _begin; private int _end; private int _partitionSize; private int _classifications; private int _maxStages; private ICodebook[][] _books; private ICodebook _classBook; private int[] _cascade; private int[][] _decodeMap; private static int icount(int v) { int num = 0; while (v != 0) { num += v & 1; v >>= 1; } return num; } public virtual void Init(IPacket packet, int channels, ICodebook[] codebooks) { _begin = (int)packet.ReadBits(24); _end = (int)packet.ReadBits(24); _partitionSize = (int)packet.ReadBits(24) + 1; _classifications = (int)packet.ReadBits(6) + 1; _classBook = codebooks[(uint)packet.ReadBits(8)]; _cascade = new int[_classifications]; int num = 0; for (int i = 0; i < _classifications; i++) { int num2 = (int)packet.ReadBits(3); if (packet.ReadBit()) { _cascade[i] = ((int)packet.ReadBits(5) << 3) | num2; } else { _cascade[i] = num2; } num += icount(_cascade[i]); } int[] array = new int[num]; for (int j = 0; j < num; j++) { array[j] = (int)packet.ReadBits(8); if (codebooks[array[j]].MapType == 0) { throw new InvalidDataException(); } } int entries = _classBook.Entries; int num3 = _classBook.Dimensions; int num4 = 1; while (num3 > 0) { num4 *= _classifications; if (num4 > entries) { throw new InvalidDataException(); } num3--; } _books = new ICodebook[_classifications][]; num = 0; int num5 = 0; for (int k = 0; k < _classifications; k++) { int num6 = Utils.ilog(_cascade[k]); _books[k] = new ICodebook[num6]; if (num6 <= 0) { continue; } num5 = Math.Max(num5, num6); for (int l = 0; l < num6; l++) { if ((_cascade[k] & (1 << l)) > 0) { _books[k][l] = codebooks[array[num++]]; } } } _maxStages = num5; _decodeMap = new int[num4][]; for (int m = 0; m < num4; m++) { int num7 = m; int num8 = num4 / _classifications; _decodeMap[m] = new int[_classBook.Dimensions]; for (int n = 0; n < _classBook.Dimensions; n++) { int num9 = num7 / num8; num7 -= num9 * num8; num8 /= _classifications; _decodeMap[m][n] = num9; } } _channels = channels; } public virtual void Decode(IPacket packet, bool[] doNotDecodeChannel, int blockSize, float[][] buffer) { int num = ((_end < blockSize / 2) ? _end : (blockSize / 2)) - _begin; if (num <= 0 || Array.IndexOf(doNotDecodeChannel, value: false) == -1) { return; } int num2 = num / _partitionSize; int num3 = (num2 + _classBook.Dimensions - 1) / _classBook.Dimensions; int[,][] array = new int[_channels, num3][]; for (int i = 0; i < _maxStages; i++) { int j = 0; int num4 = 0; while (j < num2) { if (i == 0) { for (int k = 0; k < _channels; k++) { int num5 = _classBook.DecodeScalar(packet); if (num5 >= 0 && num5 < _decodeMap.Length) { array[k, num4] = _decodeMap[num5]; continue; } j = num2; i = _maxStages; break; } } int num6 = 0; for (; j < num2; j++) { if (num6 >= _classBook.Dimensions) { break; } int offset = _begin + j * _partitionSize; for (int l = 0; l < _channels; l++) { int num7 = array[l, num4][num6]; if ((_cascade[num7] & (1 << i)) != 0) { ICodebook codebook = _books[num7][i]; if (codebook != null && WriteVectors(codebook, packet, buffer, l, offset, _partitionSize)) { j = num2; i = _maxStages; break; } } } num6++; } num4++; } } } protected virtual bool WriteVectors(ICodebook codebook, IPacket packet, float[][] residue, int channel, int offset, int partitionSize) { float[] array = residue[channel]; int num = partitionSize / codebook.Dimensions; int[] array2 = new int[num]; for (int i = 0; i < num; i++) { if ((array2[i] = codebook.DecodeScalar(packet)) == -1) { return true; } } for (int j = 0; j < codebook.Dimensions; j++) { int num2 = 0; while (num2 < num) { array[offset] += codebook[array2[num2], j]; num2++; offset++; } } return false; } } internal class Residue1 : Residue0 { protected override bool WriteVectors(ICodebook codebook, IPacket packet, float[][] residue, int channel, int offset, int partitionSize) { float[] array = residue[channel]; int num = 0; while (num < partitionSize) { int num2 = codebook.DecodeScalar(packet); if (num2 == -1) { return true; } for (int i = 0; i < codebook.Dimensions; i++) { array[offset + num] += codebook[num2, i]; num++; } } return false; } } internal class Residue2 : Residue0 { private int _channels; public override void Init(IPacket packet, int channels, ICodebook[] codebooks) { _channels = channels; base.Init(packet, 1, codebooks); } public override void Decode(IPacket packet, bool[] doNotDecodeChannel, int blockSize, float[][] buffer) { base.Decode(packet, doNotDecodeChannel, blockSize * _channels, buffer); } protected override bool WriteVectors(ICodebook codebook, IPacket packet, float[][] residue, int channel, int offset, int partitionSize) { int num = 0; offset /= _channels; int num2 = 0; while (num2 < partitionSize) { int num3 = codebook.DecodeScalar(packet); if (num3 == -1) { return true; } int num4 = 0; while (num4 < codebook.Dimensions) { residue[num][offset] += codebook[num3, num4]; if (++num == _channels) { num = 0; offset++; } num4++; num2++; } } return false; } } public sealed class StreamDecoder : IStreamDecoder, IDisposable { private NVorbis.Contracts.IPacketProvider _packetProvider; private IFactory _factory; private StreamStats _stats; private byte _channels; private int _sampleRate; private int _block0Size; private int _block1Size; private IMode[] _modes; private int _modeFieldBits; private string _vendor; private string[] _comments; private ITagData _tags; private long _currentPosition; private bool _hasClipped; private bool _hasPosition; private bool _eosFound; private float[][] _nextPacketBuf; private float[][] _prevPacketBuf; private int _prevPacketStart; private int _prevPacketEnd; private int _prevPacketStop; private static readonly byte[] PacketSignatureStream = new byte[11] { 1, 118, 111, 114, 98, 105, 115, 0, 0, 0, 0 }; private static readonly byte[] PacketSignatureComments = new byte[7] { 3, 118, 111, 114, 98, 105, 115 }; private static readonly byte[] PacketSignatureBooks = new byte[7] { 5, 118, 111, 114, 98, 105, 115 }; internal static Func<IFactory> CreateFactory { get; set; } = () => new Factory(); public int Channels => _channels; public int SampleRate => _sampleRate; public int UpperBitrate { get; private set; } public int NominalBitrate { get; private set; } public int LowerBitrate { get; private set; } public ITagData Tags => _tags ?? (_tags = new TagData(_vendor, _comments)); public TimeSpan TotalTime => TimeSpan.FromSeconds((double)TotalSamples / (double)_sampleRate); public long TotalSamples => (_packetProvider ?? throw new ObjectDisposedException("StreamDecoder")).GetGranuleCount(); public TimeSpan TimePosition { get { return TimeSpan.FromSeconds((double)_currentPosition / (double)_sampleRate); } set { SeekTo(value); } } public long SamplePosition { get { return _currentPosition; } set { SeekTo(value); } } public bool ClipSamples { get; set; } public bool HasClipped => _hasClipped; public bool IsEndOfStream { get { if (_eosFound) { return _prevPacketBuf == null; } return false; } } public IStreamStats Stats => _stats; public StreamDecoder(NVorbis.Contracts.IPacketProvider packetProvider) : this(packetProvider, new Factory()) { } internal StreamDecoder(NVorbis.Contracts.IPacketProvider packetProvider, IFactory factory) { _packetProvider = packetProvider ?? throw new ArgumentNullException("packetProvider"); _factory = factory ?? throw new ArgumentNullException("factory"); _stats = new StreamStats(); _currentPosition = 0L; ClipSamples = true; IPacket packet = _packetProvider.PeekNextPacket(); if (!ProcessHeaderPackets(packet)) { _packetProvider = null; packet.Reset(); throw GetInvalidStreamException(packet); } } private static Exception GetInvalidStreamException(IPacket packet) { try { ulong num = packet.ReadBits(64); if (num == 7233173838382854223L) { return new ArgumentException("Found OPUS bitstream."); } if ((num & 0xFF) == 127) { return new ArgumentException("Found FLAC bitstream."); } switch (num) { case 2314885909937746003uL: return new ArgumentException("Found Speex bitstream."); case 28254585843050854uL: return new ArgumentException("Found Skeleton metadata bitstream."); default: if ((num & 0xFFFFFFFFFFFF00L) == 27428895509214208L) { return new ArgumentException("Found Theora bitsream."); } return new ArgumentException("Could not find Vorbis data to decode."); } } finally { packet.Reset(); } } private bool ProcessHeaderPackets(IPacket packet) { if (!ProcessHeaderPacket(packet, LoadStreamHeader, delegate { _packetProvider.GetNextPacket().Done(); })) { return false; } if (!ProcessHeaderPacket(_packetProvider.GetNextPacket(), LoadComments, delegate(IPacket pkt) { pkt.Done(); })) { return false; } if (!ProcessHeaderPacket(_packetProvider.GetNextPacket(), LoadBooks, delegate(IPacket pkt) { pkt.Done(); })) { return false; } _currentPosition = 0L; ResetDecoder(); return true; } private static bool ProcessHeaderPacket(IPacket packet, Func<IPacket, bool> processAction, Action<IPacket> doneAction) { if (packet != null) { try { return processAction(packet); } finally { doneAction(packet); } } return false; } private static bool ValidateHeader(IPacket packet, byte[] expected) { for (int i = 0; i < expected.Length; i++) { if (expected[i] != packet.ReadBits(8)) { return false; } } return true; } private static string ReadString(IPacket packet) { int num = (int)packet.ReadBits(32); if (num == 0) { return string.Empty; } byte[] array = new byte[num]; if (packet.Read(array, 0, num) < num) { throw new InvalidDataException("Could not read full string!"); } return Encoding.UTF8.GetString(array); } private bool LoadStreamHeader(IPacket packet) { if (!ValidateHeader(packet, PacketSignatureStream)) { return false; } _channels = (byte)packet.ReadBits(8); _sampleRate = (int)packet.ReadBits(32); UpperBitrate = (int)packet.ReadBits(32); NominalBitrate = (int)packet.ReadBits(32); LowerBitrate = (int)packet.ReadBits(32); _block0Size = 1 << (int)packet.ReadBits(4); _block1Size = 1 << (int)packet.ReadBits(4); if (NominalBitrate == 0 && UpperBitrate > 0 && LowerBitrate > 0) { NominalBitrate = (UpperBitrate + LowerBitrate) / 2; } _stats.SetSampleRate(_sampleRate); _stats.AddPacket(-1, packet.BitsRead, packet.BitsRemaining, packet.ContainerOverheadBits); return true; } private bool LoadComments(IPacket packet) { if (!ValidateHeader(packet, PacketSignatureComments)) { return false; } _vendor = ReadString(packet); _comments = new string[packet.ReadBits(32)]; for (int i = 0; i < _comments.Length; i++) { _comments[i] = ReadString(packet); } _stats.AddPacket(-1, packet.BitsRead, packet.BitsRemaining, packet.ContainerOverheadBits); return true; } private bool LoadBooks(IPacket packet) { if (!ValidateHeader(packet, PacketSignatureBooks)) { return false; } IMdct mdct = _factory.CreateMdct(); IHuffman huffman = _factory.CreateHuffman(); ICodebook[] array = new ICodebook[packet.ReadBits(8) + 1]; for (int i = 0; i < array.Length; i++) { array[i] = _factory.CreateCodebook(); array[i].Init(packet, huffman); } int num = (int)packet.ReadBits(6) + 1; packet.SkipBits(16 * num); IFloor[] array2 = new IFloor[packet.ReadBits(6) + 1]; for (int j = 0; j < array2.Length; j++) { array2[j] = _factory.CreateFloor(packet); array2[j].Init(packet, _channels, _block0Size, _block1Size, array); } IResidue[] array3 = new IResidue[packet.ReadBits(6) + 1]; for (int k = 0; k < array3.Length; k++) { array3[k] = _factory.CreateResidue(packet); array3[k].Init(packet, _channels, array); } IMapping[] array4 = new IMapping[packet.ReadBits(6) + 1]; for (int l = 0; l < array4.Length; l++) { array4[l] = _factory.CreateMapping(packet); array4[l].Init(packet, _channels, array2, array3, mdct); } _modes = new IMode[packet.ReadBits(6) + 1]; for (int m = 0; m < _modes.Length; m++) { _modes[m] = _factory.CreateMode(); _modes[m].Init(packet, _channels, _block0Size, _block1Size, array4); } if (!packet.ReadBit()) { throw new InvalidDataException("Book packet did not end on correct bit!"); } _modeFieldBits = Utils.ilog(_modes.Length - 1); _stats.AddPacket(-1, packet.BitsRead, packet.BitsRemaining, packet.ContainerOverheadBits); return true; } private void ResetDecoder() { _prevPacketBuf = null; _prevPacketStart = 0; _prevPacketEnd = 0; _prevPacketStop = 0; _nextPacketBuf = null; _eosFound = false; _hasClipped = false; _hasPosition = false; } public int Read(Span<float> buffer, int offset, int count) { if (buffer == null) { throw new ArgumentNullException("buffer"); } if (offset < 0 || offset + count > buffer.Length) { throw new ArgumentOutOfRangeException("offset"); } if (count % _channels != 0) { throw new ArgumentOutOfRangeException("count", "Must be a multiple of Channels!"); } if (_packetProvider == null) { throw new ObjectDisposedException("StreamDecoder"); } if (count == 0) { return 0; } int num = offset; int num2 = offset + count; while (num < num2) { if (_prevPacketStart == _prevPacketEnd) { if (_eosFound) { _nextPacketBuf = null; _prevPacketBuf = null; break; } if (!ReadNextPacket((num - offset) / _channels, out var samplePosition)) { _prevPacketEnd = _prevPacketStop; } if (samplePosition.HasValue && !_hasPosition) { _hasPosition = true; _currentPosition = samplePosition.Value - (_prevPacketEnd - _prevPacketStart) - (num - offset) / _channels; } } int num3 = Math.Min((num2 - num) / _channels, _prevPacketEnd - _prevPacketStart); if (num3 > 0) { num = ((!ClipSamples) ? (num + CopyBuffer(buffer, num, num3)) : (num + ClippingCopyBuffer(buffer, num, num3))); } } count = num - offset; _currentPosition += count / _channels; return count; } private int ClippingCopyBuffer(Span<float> target, int targetIndex, int count) { int num = targetIndex; while (count > 0) { for (int i = 0; i < _channels; i++) { target[num++] = Utils.ClipValue(_prevPacketBuf[i][_prevPacketStart], ref _hasClipped); } _prevPacketStart++; count--; } return num - targetIndex; } private int CopyBuffer(Span<float> target, int targetIndex, int count) { int num = targetIndex; while (count > 0) { for (int i = 0; i < _channels; i++) { target[num++] = _prevPacketBuf[i][_prevPacketStart]; } _prevPacketStart++; count--; } return num - targetIndex; } private bool ReadNextPacket(int bufferedSamples, out long? samplePosition) { int packetStartindex; int packetValidLength; int packetTotalLength; bool isEndOfStream; int bitsRead; int bitsRemaining; int containerOverheadBits; float[][] array = DecodeNextPacket(out packetStartindex, out packetValidLength, out packetTotalLength, out isEndOfStream, out samplePosition, out bitsRead, out bitsRemaining, out containerOverheadBits); _eosFound |= isEndOfStream; if (array == null) { _stats.AddPacket(0, bitsRead, bitsRemaining, containerOverheadBits); return false; } if (samplePosition.HasValue && isEndOfStream) { long num = _currentPosition + bufferedSamples + packetValidLength - packetStartindex; int num2 = (int)(samplePosition.Value - num); if (num2 < 0) { packetValidLength += num2; } } if (_prevPacketEnd > 0) { OverlapBuffers(_prevPacketBuf, array, _prevPacketStart, _prevPacketStop, packetStartindex, _channels); _prevPacketStart = packetStartindex; } else if (_prevPacketBuf == null) { _prevPacketStart = packetValidLength; } _stats.AddPacket(packetValidLength - _prevPacketStart, bitsRead, bitsRemaining, containerOverheadBits); _nextPacketBuf = _prevPacketBuf; _prevPacketEnd = packetValidLength; _prevPacketStop = packetTotalLength; _prevPacketBuf = array; return true; } private float[][] DecodeNextPacket(out int packetStartindex, out int packetValidLength, out int packetTotalLength, out bool isEndOfStream, out long? samplePosition, out int bitsRead, out int bitsRemaining, out int containerOverheadBits) { IPacket packet = null; try { if ((packet = _packetProvider.GetNextPacket()) == null) { isEndOfStream = true; } else { isEndOfStream = packet.IsEndOfStream; if (packet.IsResync) { _hasPosition = false; } containerOverheadBits = packet.ContainerOverheadBits; if (packet.ReadBit()) { bitsRemaining = packet.BitsRemaining + 1; } else { IMode mode = _modes[(uint)packet.ReadBits(_modeFieldBits)]; if (_nextPacketBuf == null) { _nextPacketBuf = new float[_channels][]; for (int i = 0; i < _channels; i++) { _nextPacketBuf[i] = new float[_block1Size]; } } if (mode.Decode(packet, _nextPacketBuf, out packetStartindex, out packetValidLength, out packetTotalLength)) { samplePosition = packet.GranulePosition; bitsRead = packet.BitsRead; bitsRemaining = packet.BitsRemaining; return _nextPacketBuf; } bitsRemaining = packet.BitsRead + packet.BitsRemaining; } } packetStartindex = 0; packetValidLength = 0; packetTotalLength = 0; samplePosition = null; bitsRead = 0; bitsRemaining = 0; containerOverheadBits = 0; return null; } finally { packet?.Done(); } } private static void OverlapBuffers(float[][] previous, float[][] next, int prevStart, int prevLen, int nextStart, int channels) { while (prevStart < prevLen) { for (int i = 0; i < channels; i++) { next[i][nextStart] += previous[i][prevStart]; } prevStart++; nextStart++; } } public void SeekTo(TimeSpan timePosition, SeekOrigin seekOrigin = SeekOrigin.Begin) { SeekTo((long)((double)SampleRate * timePosition.TotalSeconds), seekOrigin); } public void SeekTo(long samplePosition, SeekOrigin seekOrigin = SeekOrigin.Begin) { if (_packetProvider == null) { throw new ObjectDisposedException("StreamDecoder"); } if (!_packetProvider.CanSeek) { throw new InvalidOperationException("Seek is not supported by the Contracts.IPacketProvider instance."); } switch (seekOrigin) { case SeekOrigin.Current: samplePosition = SamplePosition - samplePosition; break; case SeekOrigin.End: samplePosition = TotalSamples - samplePosition; break; default: throw new ArgumentOutOfRangeException("seekOrigin"); case SeekOrigin.Begin: break; } if (samplePosition < 0) { throw new ArgumentOutOfRangeException("samplePosition"); } int num; if (samplePosition == 0L) { _packetProvider.SeekTo(0L, 0, GetPacketGranules); num = 0; } else { long num2 = _packetProvider.SeekTo(samplePosition, 1, GetPacketGranules); num = (int)(samplePosition - num2); } ResetDecoder(); _hasPosition = true; if (!ReadNextPacket(0, out var samplePosition2)) { _eosFound = true; if (_packetProvider.GetGranuleCount() != samplePosition) { throw new InvalidOperationException("Could not read pre-roll packet! Try seeking again prior to reading more samples."); } _prevPacketStart = _prevPacketStop; _currentPosition = samplePosition; return; } if (!ReadNextPacket(0, out samplePosition2)) { ResetDecoder(); _eosFound = true; throw new InvalidOperationException("Could not read pre-roll packet! Try seeking again prior to reading more samples."); } _prevPacketStart += num; _currentPosition = samplePosition; } private int GetPacketGranules(IPacket curPacket) { if (curPacket.IsResync) { return 0; } if (curPacket.ReadBit()) { return 0; } int num = (int)curPacket.ReadBits(_modeFieldBits); if (num < 0 || num >= _modes.Length) { return 0; } return _modes[num].GetPacketSampleCount(curPacket); } public void Dispose() { (_packetProvider as IDisposable)?.Dispose(); _packetProvider = null; } } internal class StreamStats : IStreamStats { private int _sampleRate; private readonly int[] _packetBits = new int[2]; private readonly int[] _packetSamples = new int[2]; private int _packetIndex; private long _totalSamples; private long _audioBits; private long _headerBits; private long _containerBits; private long _wasteBits; private object _lock = new object(); private int _packetCount; public int EffectiveBitRate { get { long totalSamples; long num; lock (_lock) { totalSamples = _totalSamples; num = _audioBits + _headerBits + _containerBits + _wasteBits; } if (totalSamples > 0) { return (int)((double)num / (double)totalSamples * (double)_sampleRate); } return 0; } } public int InstantBitRate { get { int num; int num2; lock (_lock) { num = _packetBits[0] + _packetBits[1]; num2 = _packetSamples[0] + _packetSamples[1]; } if (num2 > 0) { return (int)((double)num / (double)num2 * (double)_sampleRate); } return 0; } } public long ContainerBits => _containerBits; public long OverheadBits => _headerBits; public long AudioBits => _audioBits; public long WasteBits => _wasteBits; public int PacketCount => _packetCount; public void ResetStats() { lock (_lock) { _packetBits[0] = (_packetBits[1] = 0); _packetSamples[0] = (_packetSamples[1] = 0); _packetIndex = 0; _packetCount = 0; _audioBits = 0L; _totalSamples = 0L; _headerBits = 0L; _containerBits = 0L; _wasteBits = 0L; } } internal void SetSampleRate(int sampleRate) { lock (_lock) { _sampleRate = sampleRate; ResetStats(); } } internal void AddPacket(int samples, int bits, int waste, int container) { lock (_lock) { if (samples >= 0) { _audioBits += bits; _wasteBits += waste; _containerBits += container; _totalSamples += samples; _packetBits[_packetIndex] = bits + waste; _packetSamples[_packetIndex] = samples; if (++_packetIndex == 2) { _packetIndex = 0; } } else { _headerBits += bits; _wasteBits += waste; _containerBits += container; } } } } internal class TagData : ITagData { private static IReadOnlyList<string> s_emptyList = new List<string>(); private Dictionary<string, IReadOnlyList<string>> _tags; public IReadOnlyDictionary<string, IReadOnlyList<string>> All => _tags; public string EncoderVendor { get; } public string Title => GetTagSingle("TITLE"); public string Version => GetTagSingle("VERSION"); public string Album => GetTagSingle("ALBUM"); public string TrackNumber => GetTagSingle("TRACKNUMBER"); public string Artist => GetTagSingle("ARTIST"); public IReadOnlyList<string> Performers => GetTagMulti("PERFORMER"); public string Copyright => GetTagSingle("COPYRIGHT"); public string License => GetTagSingle("LICENSE"); public string Organization => GetTagSingle("ORGANIZATION"); public string Description => GetTagSingle("DESCRIPTION"); public IReadOnlyList<string> Genres => GetTagMulti("GENRE"); public IReadOnlyList<string> Dates => GetTagMulti("DATE"); public IReadOnlyList<string> Locations => GetTagMulti("LOCATION"); public string Contact => GetTagSingle("CONTACT"); public string Isrc => GetTagSingle("ISRC"); public TagData(string vendor, string[] comments) { EncoderVendor = vendor; Dictionary<string, IReadOnlyList<string>> dictionary = new Dictionary<string, IReadOnlyList<string>>(); for (int i = 0; i < comments.Length; i++) { string[] array = comments[i].Split(new char[1] { '=' }); if (array.Length == 1) { array = new string[2] { array[0], string.Empty }; } int num = array[0].IndexOf('['); if (num > -1) { array[1] = array[0].Substring(num + 1, array[0].Length - num - 2).ToUpper(CultureInfo.CurrentCulture) + ": " + array[1]; array[0] = array[0].Substring(0, num); } if (dictionary.TryGetValue(array[0].ToUpperInvariant(), out var value)) { ((List<string>)value).Add(array[1]); continue; } dictionary.Add(array[0].ToUpperInvariant(), new List<string> { array[1] }); } _tags = dictionary; } public string GetTagSingle(string key, bool concatenate = false) { IReadOnlyList<string> tagMulti = GetTagMulti(key); if (tagMulti.Count > 0) { if (concatenate) { return string.Join(Environment.NewLine, tagMulti.ToArray()); } return tagMulti[tagMulti.Count - 1]; } return string.Empty; } public IReadOnlyList<string> GetTagMulti(string key) { if (_tags.TryGetValue(key.ToUpperInvariant(), out var value)) { return value; } return s_emptyList; } } internal static class Utils { internal static int ilog(int x) { int num = 0; while (x > 0) { num++; x >>= 1; } return num; } internal static uint BitReverse(uint n) { return BitReverse(n, 32); } internal static uint BitReverse(uint n, int bits) { n = ((n & 0xAAAAAAAAu) >> 1) | ((n & 0x55555555) << 1); n = ((n & 0xCCCCCCCCu) >> 2) | ((n & 0x33333333) << 2); n = ((n & 0xF0F0F0F0u) >> 4) | ((n & 0xF0F0F0F) << 4); n = ((n & 0xFF00FF00u) >> 8) | ((n & 0xFF00FF) << 8); return ((n >> 16) | (n << 16)) >> 32 - bits; } internal static float ClipValue(float value, ref bool clipped) { if (value > MathF.PI * 113f / 355f) { clipped = true; return MathF.PI * 113f / 355f; } if (value < MathF.PI * -113f / 355f) { clipped = true; return MathF.PI * -113f / 355f; } return value; } internal static float ConvertFromVorbisFloat32(uint bits) { int num = (int)bits >> 31; double y = (int)(((bits & 0x7FE00000) >> 21) - 788); return (float)(((bits & 0x1FFFFF) ^ num) + (num & 1)) * (float)Math.Pow(2.0, y); } } public sealed class VorbisReader : IVorbisReader, IDisposable { private readonly List<IStreamDecoder> _decoders; private readonly NVorbis.Contracts.IContainerReader _containerReader; private readonly bool _closeOnDispose; private IStreamDecoder _streamDecoder; internal static Func<Stream, bool, NVorbis.Contracts.IContainerReader> CreateContainerReader { get; set; } = (Stream s, bool cod) => new ContainerReader(s, cod); internal static Func<NVorbis.Contracts.IPacketProvider, IStreamDecoder> CreateStreamDecoder { get; set; } = (NVorbis.Contracts.IPacketProvider pp) => new StreamDecoder(pp, new Factory()); public IReadOnlyList<IStreamDecoder> Streams => _decoders; public int Channels => _streamDecoder.Channels; public int SampleRate => _streamDecoder.SampleRate; public int UpperBitrate => _streamDecoder.UpperBitrate; public int NominalBitrate => _streamDecoder.NominalBitrate; public int LowerBitrate => _streamDecoder.LowerBitrate; public ITagData Tags => _streamDecoder.Tags; [Obsolete("Use .Tags.EncoderVendor instead.")] public string Vendor => _streamDecoder.Tags.EncoderVendor; [Obsolete("Use .Tags.All instead.")] public string[] Comments => _streamDecoder.Tags.All.SelectMany((KeyValuePair<string, IReadOnlyList<string>> k) => k.Value, (KeyValuePair<string, IReadOnlyList<string>> kvp, string Item) => kvp.Key + "=" + Item).ToArray(); [Obsolete("No longer supported. Will receive a new stream when parameters change.", true)] public bool IsParameterChange { get { throw new NotSupportedException(); } } public long ContainerOverheadBits => _containerReader?.ContainerBits ?? 0; public long ContainerWasteBits => _containerReader?.WasteBits ?? 0; public int StreamIndex => _decoders.IndexOf(_streamDecoder); [Obsolete("Use .Streams.Count instead.")] public int StreamCount => _decoders.Count; [Obsolete("Use VorbisReader.TimePosition instead.")] public TimeSpan DecodedTime { get { return _streamDecoder.TimePosition; } set { TimePosition = value; } } [Obsolete("Use VorbisReader.SamplePosition instead.")] public long DecodedPosition { get { return _streamDecoder.SamplePosition; } set { SamplePosition = value; } } public TimeSpan TotalTime => _streamDecoder.TotalTime; public long TotalSamples => _streamDecoder.TotalSamples; public TimeSpan TimePosition { get { return _streamDecoder.TimePosition; } set { _streamDecoder.TimePosition = value; } } public long SamplePosition { get { return _streamDecoder.SamplePosition; } set { _streamDecoder.SamplePosition = value; } } public bool IsEndOfStream => _streamDecoder.IsEndOfStream; public bool ClipSamples { get { return _streamDecoder.ClipSamples; } set { _streamDecoder.ClipSamples = value; } } public bool HasClipped => _streamDecoder.HasClipped; public IStreamStats StreamStats => _streamDecoder.Stats; [Obsolete("Use Streams[*].Stats instead.", true)] public IVorbisStreamStatus[] Stats { get { throw new NotSupportedException(); } } public event EventHandler<NewStreamEventArgs> NewStream; public VorbisReader(string fileName) : this(File.OpenRead(fileName)) { } public VorbisReader(Stream stream, bool closeOnDispose = true) { _decoders = new List<IStreamDecoder>(); NVorbis.Contracts.IContainerReader containerReader = CreateContainerReader(stream, closeOnDispose); containerReader.NewStreamCallback = ProcessNewStream; if (!containerReader.TryInit() || _decoders.Count == 0) { containerReader.NewStreamCallback = null; containerReader.Dispose(); if (closeOnDispose) { stream.Dispose(); } throw new ArgumentException("Could not load the specified container!", "containerReader"); } _closeOnDispose = closeOnDispose; _containerReader = containerReader; _streamDecoder = _decoders[0]; } [Obsolete("Use \"new StreamDecoder(Contracts.IPacketProvider)\" and the container's NewStreamCallback or Streams property instead.", true)] public VorbisReader(NVorbis.Contracts.IContainerReader containerReader) { throw new NotSupportedException(); } [Obsolete("Use \"new StreamDecoder(Contracts.IPacketProvider)\" instead.", true)] public VorbisReader(NVorbis.Contracts.IPacketProvider packetProvider) { throw new NotSupportedException(); } private bool ProcessNewStream(NVorbis.Contracts.IPacketProvider packetProvider) { IStreamDecoder streamDecoder = CreateStreamDecoder(packetProvider); streamDecoder.ClipSamples = true; NewStreamEventArgs newStreamEventArgs = new NewStreamEventArgs(streamDecoder); this.NewStream?.Invoke(this, newStreamEventArgs); if (!newStreamEventArgs.IgnoreStream) { _decoders.Add(streamDecoder); return true; } return false; } public void Dispose() { if (_decoders != null) { foreach (IStreamDecoder decoder in _decoders) { decoder.Dispose(); } _decoders.Clear(); } if (_containerReader != null) { _containerReader.NewStreamCallback = null; if (_closeOnDispose) { _containerReader.Dispose(); } } } public bool FindNextStream() { if (_containerReader == null) { return false; } return _containerReader.FindNextStream(); } public bool SwitchStreams(int index) { if (index < 0 || index >= _decoders.Count) { throw new ArgumentOutOfRangeException("index"); } IStreamDecoder streamDecoder = _decoders[index]; IStreamDecoder streamDecoder2 = _streamDecoder; if (streamDecoder == streamDecoder2) { return false; } streamDecoder.ClipSamples = streamDecoder2.ClipSamples; _streamDecoder = streamDecoder; if (streamDecoder.Channels == streamDecoder2.Channels) { return streamDecoder.SampleRate != streamDecoder2.SampleRate; } return true; } public void SeekTo(TimeSpan timePosition, SeekOrigin seekOrigin = SeekOrigin.Begin) { _streamDecoder.SeekTo(timePosition, seekOrigin); } public void SeekTo(long samplePosition, SeekOrigin seekOrigin = SeekOrigin.Begin) { _streamDecoder.SeekTo(samplePosition, seekOrigin); } public int ReadSamples(float[] buffer, int offset, int count) { count -= count % _streamDecoder.Channels; if (count > 0) { return _streamDecoder.Read(buffer, offset, count); } return 0; } public int ReadSamples(Span<float> buffer) { int count = buffer.Length - buffer.Length % _streamDecoder.Channels; if (!buffer.IsEmpty) { return _streamDecoder.Read(buffer, 0, count); } return 0; } [Obsolete("No longer needed.", true)] public void ClearParameterChange() { throw new NotSupportedException(); } } } namespace NVorbis.Ogg { public sealed class ContainerReader : NVorbis.Contracts.IContainerReader, IDisposable { private IPageReader _reader; private List<WeakReference<NVorbis.Contracts.IPacketProvider>> _packetProviders; private bool _foundStream; internal static Func<Stream, bool, Func<NVorbis.Contracts.IPacketProvider, bool>, IPageReader> CreatePageReader { get; set; } = (Stream s, bool cod, Func<NVorbis.Contracts.IPacketProvider, bool> cb) => new PageReader(s, cod, cb); internal static Func<Stream, bool, Func<NVorbis.Contracts.IPacketProvider, bool>, IPageReader> CreateForwardOnlyPageReader { get; set; } = (Stream s, bool cod, Func<NVorbis.Contracts.IPacketProvider, bool> cb) => new ForwardOnlyPageReader(s, cod, cb); public NewStreamHandler NewStreamCallback { get; set; } public bool CanSeek { get; } public long WasteBits => _reader.WasteBits; public long ContainerBits => _reader.ContainerBits; public IReadOnlyList<NVorbis.Contracts.IPacketProvider> GetStreams() { List<NVorbis.Contracts.IPacketProvider> list = new List<NVorbis.Contracts.IPacketProvider>(_packetProviders.Count); for (int i = 0; i < _packetProviders.Count; i++) { if (_packetProviders[i].TryGetTarget(out var target)) { list.Add(target); continue; } list.RemoveAt(i); i--; } return list; } public ContainerReader(Stream stream, bool closeOnDispose) { if (stream == null) { throw new ArgumentNullException("stream"); } _packetProviders = new List<WeakReference<NVorbis.Contracts.IPacketProvider>>(); if (stream.CanSeek) { _reader = CreatePageReader(stream, closeOnDispose, ProcessNewStream); CanSeek = true; } else { _reader = CreateForwardOnlyPageReader(stream, closeOnDispose, ProcessNewStream); } } public bool TryInit() { return FindNextStream(); } public bool FindNextStream() { _reader.Lock(); try { _foundStream = false; while (_reader.ReadNextPage()) { if (_foundStream) { return true; } } return false; } finally { _reader.Release(); } } private bool ProcessNewStream(NVorbis.Contracts.IPacketProvider packetProvider) { bool flag = _reader.Release(); try { NewStreamHandler newStreamCallback = NewStreamCallback; if (newStreamCallback == null || newStreamCallback(packetProvider)) { _packetProviders.Add(new WeakReference<NVorbis.Contracts.IPacketProvider>(packetProvider)); _foundStream = true; return true; } return false; } finally { if (flag) { _reader.Lock(); } } } public void Dispose() { _reader?.Dispose(); _reader = null; } } internal class Crc : ICrc { private const uint CRC32_POLY = 79764919u; private static readonly uint[] s_crcTable; private uint _crc; static Crc() { s_crcTable = new uint[256]; for (uint num = 0u; num < 256; num++) { uint num2 = num << 24; for (int i = 0; i < 8; i++) { num2 = (num2 << 1) ^ ((num2 >= 2147483648u) ? 79764919u : 0u); } s_crcTable[num] = num2; } } public Crc() { Reset(); } public void Reset() { _crc = 0u; } public void Update(int nextVal) { _crc = (_crc << 8) ^ s_crcTable[nextVal ^ (_crc >> 24)]; } public bool Test(uint checkCrc) { return _crc == checkCrc; } } internal class ForwardOnlyPacketProvider : DataPacket, IForwardOnlyPacketProvider, NVorbis.Contracts.IPacketProvider { private int _lastSeqNo; private readonly Queue<(byte[] buf, bool isResync)> _pageQueue = new Queue<(byte[], bool)>(); private readonly IPageReader _reader; private byte[] _pageBuf; private int _packetIndex; private bool _isEndOfStream; private int _dataStart; private bool _lastWasPeek; private Memory<byte> _packetBuf; private int _dataIndex; public bool CanSeek => false; public int StreamSerial { get; } protected override int TotalBits => _packetBuf.Length * 8; public ForwardOnlyPacketProvider(IPageReader reader, int streamSerial) { _reader = reader; StreamSerial = streamSerial; _packetIndex = int.MaxValue; } public bool AddPage(byte[] buf, bool isResync) { if ((buf[5] & 2u) != 0) { if (_isEndOfStream) { return false; } isResync = true; _lastSeqNo = BitConverter.ToInt32(buf, 18); } else { int num = BitConverter.ToInt32(buf, 18); isResync |= num != _lastSeqNo + 1; _lastSeqNo = num; } int num2 = 0; for (int i = 0; i < buf[26]; i++) { num2 += buf[27 + i]; } if (num2 == 0) { return false; } _pageQueue.Enqueue((buf, isResync)); return true; } public void SetEndOfStream() { _isEndOfStream = true; } public IPacket GetNextPacket() { if (_packetBuf.Length > 0) { if (!_lastWasPeek) { throw new InvalidOperationException("Must call Done() on previous packet first."); } _lastWasPeek = false; return this; } _lastWasPeek = false; if (GetPacket()) { return this; } return null; } public IPacket PeekNextPacket() { if (_packetBuf.Length > 0) { if (!_lastWasPeek) { throw new InvalidOperationException("Must call Done() on previous packet first."); } return this; } _lastWasPeek = true; if (GetPacket()) { return this; } return null; } private bool GetPacket() { byte[] pageBuf; bool isResync; int packetIndex; bool isContinuation; bool isContinued; int dataStart; if (_pageBuf != null && _packetIndex < 27 + _pageBuf[26]) { pageBuf = _pageBuf; isResync = false; dataStart = _dataStart; packetIndex = _packetIndex; isContinuation = false; isContinued = pageBuf[26 + pageBuf[26]] == byte.MaxValue; } else if (!ReadNextPage(out pageBuf, out isResync, out dataStart, out packetIndex, out isContinuation, out isContinued)) { return false; } int num = dataStart; bool flag = packetIndex == 27; if (isContinuation && flag) { isResync = true; num += GetPacketLength(pageBuf, ref packetIndex); if (packetIndex == 27 + pageBuf[26]) { return GetPacket(); } } if (!flag) { num = 0; } int packetLength = GetPacketLength(pageBuf, ref packetIndex); Memory<byte> memory = new Memory<byte>(pageBuf, dataStart, packetLength); dataStart += packetLength; bool flag2 = packetIndex == 27 + pageBuf[26]; if (isContinued) { if (flag2) { flag2 = false; } else { int packetIndex2 = packetIndex; GetPacketLength(pageBuf, ref packetIndex2); flag2 = packetIndex2 == 27 + pageBuf[26]; } } bool flag3 = false; long? granulePosition = null; if (flag2) { granulePosition = BitConverter.ToInt64(pageBuf, 6); if ((pageBuf[5] & 4u) != 0 || (_isEndOfStream && _pageQueue.Count == 0)) { flag3 = true; } } else { while (isContinued && packetIndex == 27 + pageBuf[26] && ReadNextPage(out pageBuf, out isResync, out dataStart, out packetIndex, out isContinuation, out isContinued) && !isResync && isContinuation) { num += 27 + pageBuf[26]; Memory<byte> memory2 = memory; int packetLength2 = GetPacketLength(pageBuf, ref packetIndex); memory = new Memory<byte>(new byte[memory2.Length + packetLength2]); memory2.CopyTo(memory); new Memory<byte>(pageBuf, dataStart, packetLength2).CopyTo(memory.Slice(memory2.Length)); dataStart += packetLength2; } } base.IsResync = isResync; base.GranulePosition = granulePosition; base.IsEndOfStream = flag3; base.ContainerOverheadBits = num * 8; _pageBuf = pageBuf; _dataStart = dataStart; _packetIndex = packetIndex; _packetBuf = memory; _isEndOfStream |= flag3; Reset(); return true; } private bool ReadNextPage(out byte[] pageBuf, out bool isResync, out int dataStart, out int packetIndex, out bool isContinuation, out bool isContinued) { while (_pageQueue.Count == 0) { if (_isEndOfStream || !_reader.ReadNextPage()) { pageBuf = null; isResync = false; dataStart = 0; packetIndex = 0; isContinuation = false; isContinued = false; return false; } } (byte[], bool) tuple = _pageQueue.Dequeue(); pageBuf = tuple.Item1; isResync = tuple.Item2; dataStart = pageBuf[26] + 27; packetIndex = 27; isContinuation = (pageBuf[5] & 1) != 0; isContinued = pageBuf[26 + pageBuf[26]] == byte.MaxValue; return true; } private int GetPacketLength(byte[] pageBuf, ref int packetIndex) { int num = 0;