RUMBLE does not support other mod managers. If you want to use a manager, you must use the RUMBLE Mod Manager, a manager specifically designed for this game.
Decompiled source of ReplayMod v1.2.0
Mods/ReplayMod.dll
Decompiled a month 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.ComponentModel.DataAnnotations; using System.Diagnostics; using System.Globalization; using System.IO; using System.IO.Compression; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using System.Text; using System.Text.RegularExpressions; using System.Threading.Tasks; using Concentus; using Concentus.Enums; using Concentus.Oggfile; using HarmonyLib; using Il2Cpp; using Il2CppInterop.Runtime.InteropTypes; using Il2CppInterop.Runtime.InteropTypes.Arrays; using Il2CppPhoton.Pun; using Il2CppPhoton.Realtime; using Il2CppPhoton.Voice; using Il2CppPhoton.Voice.PUN; using Il2CppPhoton.Voice.Unity; using Il2CppRUMBLE.Audio; using Il2CppRUMBLE.Combat.ShiftStones; using Il2CppRUMBLE.Environment; using Il2CppRUMBLE.Environment.MatchFlow; using Il2CppRUMBLE.Input; using Il2CppRUMBLE.Interactions.InteractionBase; using Il2CppRUMBLE.Managers; using Il2CppRUMBLE.MoveSystem; using Il2CppRUMBLE.Networking; using Il2CppRUMBLE.Networking.MatchFlow; using Il2CppRUMBLE.Players; using Il2CppRUMBLE.Players.Scaling; using Il2CppRUMBLE.Players.Subsystems; using Il2CppRUMBLE.Pools; using Il2CppRUMBLE.Poses; using Il2CppRUMBLE.Recording.LCK; using Il2CppRUMBLE.Slabs.Forms; using Il2CppRUMBLE.Social; using Il2CppRUMBLE.Social.Phone; using Il2CppRUMBLE.Utilities; using Il2CppSystem; using Il2CppSystem.Collections.Generic; using Il2CppSystem.IO; using Il2CppTMPro; using MelonLoader; using MelonLoader.Preferences; using MelonLoader.Utils; using Newtonsoft.Json; using ReplayMod.Core; using ReplayMod.Replay; using ReplayMod.Replay.Files; using ReplayMod.Replay.Serialization; using ReplayMod.Replay.UI; using RumbleModdingAPI.RMAPI; using UIFramework; using UnityEngine; using UnityEngine.Events; using UnityEngine.InputSystem.XR; using UnityEngine.Rendering; using UnityEngine.SceneManagement; using UnityEngine.UI; using UnityEngine.VFX; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)] [assembly: MelonInfo(typeof(Main), "ReplayMod", "1.2.0", "ERROR", null)] [assembly: MelonGame("Buckethead Entertainment", "RUMBLE")] [assembly: MelonColor(255, 255, 0, 0)] [assembly: MelonAuthorColor(255, 255, 0, 0)] [assembly: MelonAdditionalDependencies(new string[] { "RumbleModdingAPI", "UIFramework" })] [assembly: TargetFramework(".NETCoreApp,Version=v6.0", FrameworkDisplayName = "")] [assembly: AssemblyCompany("ReplayMod")] [assembly: AssemblyConfiguration("Debug")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: AssemblyInformationalVersion("1.0.0")] [assembly: AssemblyProduct("ReplayMod")] [assembly: AssemblyTitle("ReplayMod")] [assembly: AssemblyVersion("1.0.0.0")] namespace System.Runtime.CompilerServices { [AttributeUsage(AttributeTargets.All)] public sealed class NullableAttribute : Attribute { public NullableAttribute(byte flag) { } public NullableAttribute(byte[] flags) { } } [AttributeUsage(AttributeTargets.All)] public sealed class NullableContextAttribute : Attribute { public NullableContextAttribute(byte flag) { } } } namespace ReplayMod { public static class BinaryExtensions { public static void Write(this BinaryWriter bw, Vector3 v) { //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_000f: Unknown result type (might be due to invalid IL or missing references) //IL_001c: Unknown result type (might be due to invalid IL or missing references) bw.Write(v.x); bw.Write(v.y); bw.Write(v.z); } public static void Write(this BinaryWriter bw, Quaternion q) { int num = 0; float num2 = Mathf.Abs(((Quaternion)(ref q))[0]); for (int i = 1; i < 4; i++) { float num3 = Mathf.Abs(((Quaternion)(ref q))[i]); if (num3 > num2) { num = i; num2 = num3; } } float num4 = ((((Quaternion)(ref q))[num] < 0f) ? (-1f) : 1f); for (int j = 0; j < 4; j++) { if (j != num) { ushort value = (ushort)(Mathf.Clamp01(((Quaternion)(ref q))[j] * num4 * 0.5f + 0.5f) * 65535f); bw.Write(value); } } bw.Write((byte)num); } public static Vector3 ReadVector3(this BinaryReader br) { //IL_0013: Unknown result type (might be due to invalid IL or missing references) //IL_0018: Unknown result type (might be due to invalid IL or missing references) //IL_001b: Unknown result type (might be due to invalid IL or missing references) return new Vector3(br.ReadSingle(), br.ReadSingle(), br.ReadSingle()); } public static Quaternion ReadQuaternion(this BinaryReader br) { //IL_0101: Unknown result type (might be due to invalid IL or missing references) //IL_0106: Unknown result type (might be due to invalid IL or missing references) //IL_010a: Unknown result type (might be due to invalid IL or missing references) float[] array = new float[4]; ushort num = br.ReadUInt16(); ushort num2 = br.ReadUInt16(); ushort num3 = br.ReadUInt16(); byte b = br.ReadByte(); float[] array2 = new float[3] { ((float)(int)num / 65535f - 0.5f) * 2f, ((float)(int)num2 / 65535f - 0.5f) * 2f, ((float)(int)num3 / 65535f - 0.5f) * 2f }; int num4 = 0; for (int i = 0; i < 4; i++) { if (i != b) { array[i] = array2[num4++]; } } float num5 = 0f; for (int j = 0; j < 4; j++) { if (j != b) { num5 += array[j] * array[j]; } } array[b] = Mathf.Sqrt(1f - num5); return new Quaternion(array[0], array[1], array[2], array[3]); } public static void Write<TField>(this BinaryWriter bw, TField field, Vector3 v) where TField : Enum { //IL_001d: Unknown result type (might be due to invalid IL or missing references) bw.Write(Convert.ToByte(field)); bw.Write((ushort)12); bw.Write(v); } public static void Write<TField>(this BinaryWriter bw, TField field, Quaternion q) where TField : Enum { //IL_001c: Unknown result type (might be due to invalid IL or missing references) bw.Write(Convert.ToByte(field)); bw.Write((ushort)7); bw.Write(q); } public static void Write<TField>(this BinaryWriter bw, TField field, short v) where TField : Enum { bw.Write(Convert.ToByte(field)); bw.Write((ushort)2); bw.Write(v); } public static void Write<TField>(this BinaryWriter bw, TField field, bool v) where TField : Enum { bw.Write(Convert.ToByte(field)); bw.Write((ushort)1); bw.Write(v); } public static void Write<TField>(this BinaryWriter bw, TField field, float f) where TField : Enum { bw.Write(Convert.ToByte(field)); bw.Write((ushort)4); bw.Write(f); } public static void Write<TField>(this BinaryWriter bw, TField field, string s) where TField : Enum { byte[] bytes = Encoding.UTF8.GetBytes(s); bw.Write(Convert.ToByte(field)); bw.Write((ushort)bytes.Length); bw.Write(bytes); } public static void Write<TField>(this BinaryWriter bw, TField field, int v) where TField : Enum { bw.Write(Convert.ToByte(field)); bw.Write((ushort)4); bw.Write(v); } public static void Write<TField>(this BinaryWriter bw, TField field, byte v) where TField : Enum { bw.Write(Convert.ToByte(field)); bw.Write((ushort)1); bw.Write(v); } public static void Write<TField>(this BinaryWriter bw, TField field, long v) where TField : Enum { bw.Write(Convert.ToByte(field)); bw.Write((ushort)8); bw.Write(v); } public static void Write<TField>(this BinaryWriter bw, TField field, double v) where TField : Enum { bw.Write(Convert.ToByte(field)); bw.Write((ushort)8); bw.Write(v); } public static void Write<TField>(this BinaryWriter bw, TField field, Vector2 v) where TField : Enum { //IL_001c: Unknown result type (might be due to invalid IL or missing references) //IL_0029: Unknown result type (might be due to invalid IL or missing references) bw.Write(Convert.ToByte(field)); bw.Write((ushort)8); bw.Write(v.x); bw.Write(v.y); } public static void Write<TField>(this BinaryWriter bw, TField field, Color32 c) where TField : Enum { //IL_001c: Unknown result type (might be due to invalid IL or missing references) //IL_0029: Unknown result type (might be due to invalid IL or missing references) //IL_0036: Unknown result type (might be due to invalid IL or missing references) //IL_0043: Unknown result type (might be due to invalid IL or missing references) bw.Write(Convert.ToByte(field)); bw.Write((ushort)4); bw.Write(c.r); bw.Write(c.g); bw.Write(c.b); bw.Write(c.a); } public static void Write<TField, TEnum>(this BinaryWriter bw, TField field, TEnum value) where TField : Enum where TEnum : Enum { bw.Write(Convert.ToByte(field)); bw.Write((ushort)4); bw.Write(Convert.ToInt32(value)); } } public class ReplayCodec { public static byte[] Compress(byte[] data) { using MemoryStream memoryStream = new MemoryStream(); using (BrotliStream brotliStream = new BrotliStream(memoryStream, CompressionLevel.Optimal, leaveOpen: true)) { brotliStream.Write(data, 0, data.Length); } return memoryStream.ToArray(); } public static byte[] Decompress(byte[] compressed) { using MemoryStream stream = new MemoryStream(compressed); using BrotliStream brotliStream = new BrotliStream(stream, CompressionMode.Decompress); using MemoryStream memoryStream = new MemoryStream(); brotliStream.CopyTo(memoryStream); return memoryStream.ToArray(); } } } namespace ReplayMod.Replay { public static class ReplayAPI { public sealed class FrameExtensionWriter { private readonly BinaryWriter _entriesWriter; private readonly int _extensionId; private readonly Action _incrementEntryCount; internal FrameExtensionWriter(BinaryWriter entriesWriter, int extensionId, Action incrementEntryCount) { _entriesWriter = entriesWriter; _extensionId = extensionId; _incrementEntryCount = incrementEntryCount; } public void WriteChunk(int subIndex, Action<BinaryWriter> write) { using MemoryStream memoryStream = new MemoryStream(); using BinaryWriter obj = new BinaryWriter(memoryStream); write(obj); if (memoryStream.Length != 0) { _entriesWriter.Write((byte)250); _entriesWriter.Write(_extensionId); _entriesWriter.Write(subIndex); _entriesWriter.Write((int)memoryStream.Length); _entriesWriter.Write(memoryStream.ToArray()); _incrementEntryCount?.Invoke(); } } } public sealed class ArchiveBuilder { private readonly ZipArchive _zip; private readonly string _modId; internal ArchiveBuilder(ZipArchive zip, string modId) { _zip = zip; _modId = modId; } public void AddFile(string path, byte[] data, CompressionLevel level = CompressionLevel.Optimal) { ZipArchiveEntry zipArchiveEntry = _zip.CreateEntry("extensions/" + _modId + "/" + path, level); using Stream stream = zipArchiveEntry.Open(); stream.Write(data, 0, data.Length); } } public sealed class ArchiveReader { private readonly ZipArchive _zip; private readonly string _modId; internal ArchiveReader(ZipArchive zip, string modId) { _zip = zip; _modId = modId; } private string GetFullPath(string relativePath) { return "extensions/" + _modId + "/" + relativePath; } public bool TryGetFile(string relativePath, out byte[] data) { ZipArchiveEntry entry = _zip.GetEntry(GetFullPath(relativePath)); if (entry == null) { data = null; return false; } using Stream stream = entry.Open(); using MemoryStream memoryStream = new MemoryStream(); stream.CopyTo(memoryStream); data = memoryStream.ToArray(); return true; } public bool FileExists(string relativePath) { return _zip.GetEntry(GetFullPath(relativePath)) != null; } } private static readonly List<ReplayExtension> _extensions = new List<ReplayExtension>(); private static readonly Dictionary<int, Action<BinaryReader, Frame>> _frameReaders = new Dictionary<int, Action<BinaryReader, Frame>>(); private static readonly Dictionary<int, Action<FrameExtensionWriter, Frame>> _frameWriters = new Dictionary<int, Action<FrameExtensionWriter, Frame>>(); public static bool IsRecording => Main.Recording.isRecording; public static bool IsBuffering => Main.Recording.isBuffering; public static bool IsPlaying => Main.Playback.isPlaying; public static bool IsPaused => Main.Playback.isPaused; public static float CurrentTime => Main.Playback.elapsedPlaybackTime; public static float Duration => (Main.Playback.currentReplay?.Header?.Duration).GetValueOrDefault(); public static Version FormatVersion => new Version("1.1.0"); public static string RootFolder => ReplayFiles.explorer.RootPath; public static string CurrentFolder => ReplayFiles.explorer.CurrentFolderPath; public static IReadOnlyList<ReplayExplorer.Entry> Entries => ReplayFiles.explorer.currentReplayEntries; public static ReplayExplorer.Entry SelectedEntry => ReplayFiles.explorer.currentlySelectedEntry; public static int ExplorerCurrentPage { get { return ReplayFiles.explorer.currentPage; } set { ReplayFiles.explorer.currentPage = value; } } public static int PageCount => ReplayFiles.explorer.pageCount; public static IReadOnlyList<ReplayPlayback.Clone> Players => Main.Playback.PlaybackPlayers; public static IReadOnlyList<ReplayPlayback.Clone> PlayerPool => Main.Playback.playerPool; public static IReadOnlyList<GameObject> Structures => Main.Playback.PlaybackStructures; public static ReplayInfo CurrentReplay => Main.Playback.currentReplay; internal static IEnumerable<ReplayExtension> Extensions => _extensions; public static event Action<ReplayExplorer.Entry, string> onReplaySelected; public static event Action<string> onExplorerFolderChanged; public static event Action onExplorerRefreshed; public static event Action<ReplayInfo> onReplayStarted; public static event Action<ReplayInfo> onReplayEnded; public static event Action onRecordingStarted; public static event Action onRecordingStopped; public static event Action<float> onReplayTimeChanged; public static event Action<bool> onReplayPauseChanged; public static event Action<Frame, Frame> OnPlaybackFrame; public static event Action<Frame, bool> OnRecordFrame; public static event Action<ReplayInfo, bool, string> onReplaySaved; public static event Action<string> onReplayDeleted; public static event Action<ReplaySerializer.ReplayHeader, string> onReplayRenamed; internal static void ReplaySelectedInternal(ReplayExplorer.Entry entry, string path) { ReplayAPI.onReplaySelected?.Invoke(entry, path); } internal static void ExplorerFolderChangedInternal(string path) { ReplayAPI.onExplorerFolderChanged?.Invoke(path); } internal static void ExplorerRefreshedInternal() { ReplayAPI.onExplorerRefreshed?.Invoke(); } internal static void ReplayStartedInternal(ReplayInfo info) { ReplayAPI.onReplayStarted?.Invoke(info); } internal static void ReplayEndedInternal(ReplayInfo info) { ReplayAPI.onReplayEnded?.Invoke(info); } internal static void RecordingStartedInternal() { ReplayAPI.onRecordingStarted?.Invoke(); } internal static void RecordingStoppedInternal() { ReplayAPI.onRecordingStopped?.Invoke(); } internal static void ReplayTimeChangedInternal(float time) { ReplayAPI.onReplayTimeChanged?.Invoke(time); } internal static void ReplayPauseChangedInternal(bool paused) { ReplayAPI.onReplayPauseChanged?.Invoke(paused); } internal static void OnPlaybackFrameInternal(Frame frame, Frame nextFrame) { ReplayAPI.OnPlaybackFrame?.Invoke(frame, nextFrame); } internal static void OnRecordFrameInternal(Frame frame, bool isBuffer) { ReplayAPI.OnRecordFrame?.Invoke(frame, isBuffer); } internal static void ReplaySavedInternal(ReplayInfo info, bool isBuffer, string path) { ReplayAPI.onReplaySaved?.Invoke(info, isBuffer, path); } internal static void ReplayDeletedInternal(string path) { ReplayAPI.onReplayDeleted?.Invoke(path); } internal static void ReplayRenamedInternal(ReplaySerializer.ReplayHeader header, string newPath) { ReplayAPI.onReplayRenamed?.Invoke(header, newPath); } public static bool SelectEntry(int index) { return ReplayFiles.explorer.Select(index); } public static void Enter(string path) { ReplayFiles.explorer.Enter(path); } public static void GoUp() { ReplayFiles.explorer.GoUp(); } public static void Refresh() { ReplayFiles.explorer.Refresh(); ReplayFiles.RefreshUI(); } public static IReadOnlyList<ReplayExplorer.Entry> GetPage() { return ReplayFiles.explorer.GetPage(); } public static string GetMetadataFormat(string sceneName) { return ReplayFiles.GetMetadataFormat(sceneName); } public static string FormatReplayTemplate(string template, ReplaySerializer.ReplayHeader replayInfo) { return ReplayFormatting.FormatReplayString(template, replayInfo); } public static string GetReplayDisplayName(string replayPath, ReplaySerializer.ReplayHeader replayInfo, string alternativeName = null, bool displayTitle = true) { return ReplayFormatting.GetReplayDisplayName(replayPath, replayInfo, alternativeName, displayTitle); } public static void Play(string path) { Main.Playback.LoadReplay(path); } public static void LoadSelectedReplay() { Main.instance.LoadSelectedReplay(); } public static void Stop() { Main.Playback.StopReplay(); } public static void StartRecording() { Main.Recording.StartRecording(); } public static void StopRecording() { Main.Recording.StopRecording(); } public static void StartBuffering() { Main.Recording.StartBuffering(); } public static void SaveBuffer() { Main.Recording.SaveReplayBuffer(); } public static void TogglePlayback(bool playing) { Main.Playback.TogglePlayback(playing); } public static void Seek(float time) { Main.Playback.SetPlaybackTime(time); } public static void Seek(int frame) { Main.Playback.SetPlaybackFrame(frame); } public static void SetSpeed(float speed) { Main.Playback.SetPlaybackSpeed(speed); } public static int ComputeStableId(string input) { int num = -2128831035; foreach (char c in input) { num ^= c; num *= 16777619; } return num; } public static ReplayExtension RegisterExtension(ReplayExtension extension) { if (_extensions.Any((ReplayExtension e) => e.Id == extension.Id)) { ((MelonBase)Main.instance).LoggerInstance.Error("Extension with an ID `" + extension.Id + "` already registered"); return null; } _extensions.Add(extension); ((MelonBase)Main.instance).LoggerInstance.Msg("Extension '" + extension.Id + "' created"); return extension; } internal static void InvokeArchiveBuild(ZipArchive zip) { foreach (ReplayExtension extension in Extensions) { ArchiveBuilder builder = new ArchiveBuilder(zip, extension.Id); extension.OnBuild(builder); } } internal static void InvokeArchiveRead(ZipArchive zip) { foreach (ReplayExtension extension in Extensions) { ArchiveReader reader = new ArchiveReader(zip, extension.Id); extension.OnRead(reader); } } } public abstract class ReplayExtension { public abstract string Id { get; } public MelonPreferences_Category Settings { get; internal set; } public MelonPreferences_Entry<bool> Enabled { get; internal set; } public bool IsEnabled => Enabled.Value; public int FrameExtensionId => ReplayAPI.ComputeStableId(Id); public virtual void OnBuild(ReplayAPI.ArchiveBuilder builder) { } public virtual void OnRead(ReplayAPI.ArchiveReader reader) { } public virtual void OnRecordFrame(Frame frame, bool isBuffer) { } public virtual void OnWriteFrame(ReplayAPI.FrameExtensionWriter writer, Frame frame) { } public virtual void OnReadFrame(BinaryReader reader, Frame frame, int index) { } public virtual void OnPlaybackFrame(Frame frame, Frame nextFrame) { } } public static class ReplayCache { public static Dictionary<string, StackType> NameToStackType = new Dictionary<string, StackType>(StringComparer.OrdinalIgnoreCase) { { "RockSlide", StackType.Dash }, { "Jump", StackType.Jump }, { "Flick", StackType.Flick }, { "Parry", StackType.Parry }, { "HoldLeft", StackType.HoldLeft }, { "HoldRight", StackType.HoldRight }, { "Stomp", StackType.Ground }, { "Straight", StackType.Straight }, { "Uppercut", StackType.Uppercut }, { "Kick", StackType.Kick }, { "Explode", StackType.Explode } }; public static readonly Dictionary<string, FXOneShotType> AudioCallToFX = new Dictionary<string, FXOneShotType> { { "Call_Structure_Impact_Light", FXOneShotType.ImpactLight }, { "Call_Structure_Impact_Medium", FXOneShotType.ImpactMedium }, { "Call_Structure_Impact_Heavy", FXOneShotType.ImpactHeavy }, { "Call_Structure_Impact_Massive", FXOneShotType.ImpactMassive }, { "Call_Structure_Ground", FXOneShotType.GroundedSFX }, { "Call_RockCam_Spawn", FXOneShotType.RockCamSpawn }, { "Call_RockCam_Despawn", FXOneShotType.RockCamDespawn }, { "Call_RockCam_Stick", FXOneShotType.RockCamStick }, { "Call_Bodyhit_Hard", FXOneShotType.Fistbump }, { "Call_FistBumpBonus", FXOneShotType.FistbumpGoin } }; public static readonly Dictionary<string, FXOneShotType> VFXNameToFX = new Dictionary<string, FXOneShotType> { { "StructureCollision_VFX", FXOneShotType.StructureCollision }, { "Ricochet_VFX", FXOneShotType.Ricochet }, { "VFX_Dust_Modifier_Ground", FXOneShotType.Grounded }, { "VFX_Dust_Modifier_Unground", FXOneShotType.Ungrounded }, { "VFX_Dust_Structure_Impact", FXOneShotType.DustImpact }, { "VFX_Dust_Structure_Spawn", FXOneShotType.Spawn }, { "VFX_Dust_Structure_Break", FXOneShotType.Break }, { "RockCamSpawn_VFX", FXOneShotType.RockCamSpawn }, { "RockCamDespawn_VFX", FXOneShotType.RockCamDespawn }, { "PlayerBoxInteractionVFX", FXOneShotType.Fistbump }, { "FistbumpCoin", FXOneShotType.FistbumpGoin }, { "Hitmarker", FXOneShotType.Hitmarker } }; public static readonly Dictionary<FXOneShotType, string> FXToVFXName = new Dictionary<FXOneShotType, string> { { FXOneShotType.StructureCollision, "StructureCollision_VFX" }, { FXOneShotType.Ricochet, "Ricochet_VFX" }, { FXOneShotType.Grounded, "Ground_VFX" }, { FXOneShotType.Ungrounded, "Unground_VFX" }, { FXOneShotType.DustImpact, "DustImpact_VFX" }, { FXOneShotType.Spawn, "DustSpawn_VFX" }, { FXOneShotType.Break, "DustBreak_VFX" }, { FXOneShotType.RockCamSpawn, "RockCamSpawn_VFX" }, { FXOneShotType.RockCamDespawn, "RockCamDespawn_VFX" }, { FXOneShotType.Fistbump, "PlayerBoxInteractionVFX" }, { FXOneShotType.FistbumpGoin, "PlayerFistBumpBonusVFX" }, { FXOneShotType.Jump, "Jump_VFX" }, { FXOneShotType.Dash, "Dash_VFX" } }; public static readonly Dictionary<FXOneShotType, string> FXToSFXName = new Dictionary<FXOneShotType, string> { { FXOneShotType.ImpactLight, "Call_Structure_Impact_Light" }, { FXOneShotType.ImpactMedium, "Call_Structure_Impact_Medium" }, { FXOneShotType.ImpactHeavy, "Call_Structure_Impact_Heavy" }, { FXOneShotType.ImpactMassive, "Call_Structure_Impact_Massive" }, { FXOneShotType.GroundedSFX, "Call_Structure_Ground" }, { FXOneShotType.RockCamSpawn, "Call_RockCam_Spawn" }, { FXOneShotType.RockCamDespawn, "Call_RockCam_Despawn" }, { FXOneShotType.RockCamStick, "Call_RockCam_Stick" }, { FXOneShotType.Fistbump, "Call_Bodyhit_Hard" }, { FXOneShotType.FistbumpGoin, "Call_FistBumpBonus" } }; public static Dictionary<StructureType, Pool<PooledMonoBehaviour>> structurePools; public static Dictionary<string, AudioCall> SFX; public static void BuildCacheTables() { structurePools = new Dictionary<StructureType, Pool<PooledMonoBehaviour>>(); SFX = new Dictionary<string, AudioCall>(); foreach (Pool<PooledMonoBehaviour> item in (Il2CppArrayBase<Pool<PooledMonoBehaviour>>)(object)Singleton<PoolManager>.instance.availablePools) { string resourceName = item.poolItem.resourceName; if (resourceName.Contains("RockCube")) { structurePools[StructureType.Cube] = item; } else if (resourceName.Contains("PrisonedPillar")) { structurePools[StructureType.PrisonedPillar] = item; } else if (resourceName.Contains("WrappedWall")) { structurePools[StructureType.WrappedWall] = item; } else if (resourceName.Contains("DockedDisk")) { structurePools[StructureType.DockedDisk] = item; } else if (resourceName.Contains("CageCube")) { structurePools[StructureType.CageCube] = item; } else if (resourceName.Contains("Pillar")) { structurePools[StructureType.Pillar] = item; } else if (resourceName.Contains("Disc")) { structurePools[StructureType.Disc] = item; } else if (resourceName.Contains("Wall")) { structurePools[StructureType.Wall] = item; } else if (resourceName == "Ball") { structurePools[StructureType.Ball] = item; } else if (resourceName.Contains("LargeRock")) { structurePools[StructureType.LargeRock] = item; } else if (resourceName.Contains("SmallRock")) { structurePools[StructureType.SmallRock] = item; } else if (resourceName.Contains("BoulderBall")) { structurePools[StructureType.CagedBall] = item; structurePools[StructureType.TetheredCagedBall] = item; } else if (resourceName.Contains("StructureTarget")) { structurePools[StructureType.Target] = item; } } AudioCall[] array = Il2CppArrayBase<AudioCall>.op_Implicit(Resources.FindObjectsOfTypeAll<AudioCall>()); AudioCall[] array2 = array; foreach (AudioCall val in array2) { if (!((Object)(object)val == (Object)null) && !string.IsNullOrEmpty(((Object)val).name)) { SFX[((Object)val).name] = val; } } } } public static class ReplayVoices { public class VoiceStreamWriter : IDisposable { public readonly int VoiceId; public readonly string Path; private readonly FileStream _fileStream; private readonly IOpusEncoder _encoder; private readonly OpusOggWriteStream _wav; public bool HasFrames { get; private set; } public float LastWriteTimestamp { get; private set; } public int SampleRate { get; private set; } public int Channels { get; private set; } public VoiceStreamWriter(int voiceId, int sampleRate, int channels, string path, int bitrate = 30000, int complexity = 8, float initalTimeStamp = 0f) { //IL_0080: Unknown result type (might be due to invalid IL or missing references) //IL_0086: Expected O, but got Unknown //IL_00d9: Unknown result type (might be due to invalid IL or missing references) //IL_00e3: Expected O, but got Unknown LastWriteTimestamp = Time.time; VoiceId = voiceId; Path = path; SampleRate = sampleRate; Channels = channels; _fileStream = File.Create(path); _encoder = OpusCodecFactory.CreateEncoder(sampleRate, channels, (OpusApplication)2048, (TextWriter)null); _encoder.Bitrate = bitrate; _encoder.Complexity = complexity; _encoder.SignalType = (OpusSignal)3001; OpusTags val = new OpusTags(); val.Fields.Add("Type", "Rumble ReplayMod Captured Voice"); val.Fields.Add("Origin", "https://github.com/xLoadingx/ReplayMod"); val.Fields.Add("Encoder", "https://github.com/lostromb/concentus"); _wav = new OpusOggWriteStream(_encoder, (Stream)_fileStream, val, 0, 5, false); } public void Write(float[] samples) { if (samples != null && samples.Length != 0) { HasFrames = true; int num = (int)((Time.time - LastWriteTimestamp) * (float)SampleRate); if (num >= SampleRate / 8) { Main.DebugLog($"Voice {VoiceId} silent for {Time.time - LastWriteTimestamp} seconds or {num} samples"); _wav.WriteSamples(new float[num], 0, num); } _wav.WriteSamples(samples, 0, samples.Length); LastWriteTimestamp = Time.time; } } public void Dispose() { _wav.Finish(); _fileStream?.Dispose(); } } private static PunVoiceClient voice; private static readonly Dictionary<(int actorId, int voiceId), VoiceStreamWriter> writers = new Dictionary<(int, int), VoiceStreamWriter>(); public static string VoiceCacheDir = Path.Combine(MelonEnvironment.UserDataDirectory, "ReplayMod", "VoiceCache"); public static List<VoiceTrackInfo> VoiceTrackInfos = new List<VoiceTrackInfo>(); public static bool isRecording; private static bool subscribed; public static bool HasActiveWriters => writers.Count > 0; public static void StartRecording() { if (!Main.instance.VoiceRecording.Value) { return; } isRecording = true; VoiceTrackInfos.Clear(); if (Directory.Exists(VoiceCacheDir)) { string[] files = Directory.GetFiles(VoiceCacheDir); foreach (string path in files) { File.Delete(path); } } else { Directory.CreateDirectory(VoiceCacheDir); } PunVoiceClient instance = PunVoiceClient.instance; if (instance != null) { if (!subscribed) { ((VoiceConnection)instance).RemoteVoiceAdded = ((VoiceConnection)instance).RemoteVoiceAdded + Action<RemoteVoiceLink>.op_Implicit((Action<RemoteVoiceLink>)OnRemoteVoiceLinkAdded); subscribed = true; } if (((Il2CppArrayBase<LocalVoice>)(object)((Il2CppObjectBase)((VoiceConnection)instance).VoiceClient.LocalVoices).Cast<Il2CppReferenceArray<LocalVoice>>()).Length == 0) { Main.DebugLog("Local voices is empty"); } Enumerator<RemoteVoiceLink> enumerator = ((VoiceConnection)instance).cachedRemoteVoices.GetEnumerator(); while (enumerator.MoveNext()) { RemoteVoiceLink current = enumerator.Current; OnRemoteVoiceLinkAdded(current); } Directory.CreateDirectory(VoiceCacheDir); } } public static void StopRecording() { isRecording = false; foreach (var item in writers.Keys.ToList()) { stopWriter(item.actorId, item.voiceId); } } public static AudioClip LoadVoiceClipFromFile(string path) { byte[] data = File.ReadAllBytes(path); int sampleRate; int channels; float[] array = DecodeOpus(data, out sampleRate, out channels); AudioClip val = AudioClip.Create(Path.GetFileNameWithoutExtension(path), array.Length / channels, channels, sampleRate, false); ((Object)val).hideFlags = (HideFlags)32; val.SetData(Il2CppStructArray<float>.op_Implicit(array), 0); return val; } public static float[] DecodeOpus(byte[] data, out int sampleRate, out int channels) { //IL_0017: Unknown result type (might be due to invalid IL or missing references) //IL_001d: Expected O, but got Unknown using MemoryStream memoryStream = new MemoryStream(data); IOpusDecoder val = OpusCodecFactory.CreateDecoder(48000, 1, (TextWriter)null); OpusOggReadStream val2 = new OpusOggReadStream(val, (Stream)memoryStream); sampleRate = 48000; channels = 1; List<float> list = new List<float>(); while (val2.HasNextPacket) { short[] array = val2.DecodeNextPacket(); if (array != null) { for (int i = 0; i < array.Length; i++) { list.Add((float)array[i] / 32768f); } } } return list.ToArray(); } public static void Cleanup() { VoiceTrackInfos.Clear(); if (Directory.Exists(VoiceCacheDir)) { Directory.Delete(VoiceCacheDir, recursive: true); } } private static void stopWriter(int actorId, int voiceId) { if (writers.Remove((actorId, voiceId), out var value)) { value.Dispose(); if (!value.HasFrames && File.Exists(value.Path)) { File.Delete(value.Path); } } } public static void OnRemoteVoiceLinkAdded(RemoteVoiceLink link) { if (!isRecording) { return; } int playerId = link.PlayerId; int voiceId = link.VoiceId; float startTime = Time.time - Main.Recording.recordingStartTime; Main.DebugLog($"VoiceAdded actor={playerId} voice={voiceId}"); Player val = ((IEnumerable<Player>)Singleton<PlayerManager>.instance.AllPlayers.ToArray()).FirstOrDefault((Func<Player, bool>)((Player p) => p.Data.GeneralData.ActorNo == playerId)); string name = ((val != null) ? val.Data.GeneralData.PublicUsername : null) ?? "Unknown"; string masterId = ((val != null) ? val.Data.GeneralData.PlayFabMasterId : null); name = Utilities.CleanName(name); RemoteVoiceLink obj = link; obj.FloatFrameDecoded += Action<FrameOut<float>>.op_Implicit((Action<FrameOut<float>>)delegate(FrameOut<float> frame) { if (isRecording) { if (!writers.TryGetValue((playerId, voiceId), out var value)) { string text = $"p{name}_{masterId}_v{voiceId}_{Time.frameCount}.ogg"; string path = Path.Combine(VoiceCacheDir, text); value = new VoiceStreamWriter(voiceId, link.VoiceInfo.SamplingRate, link.VoiceInfo.Channels, path, Mathf.Clamp(Main.instance.voiceBitrate.Value, 8, 128), 8, startTime); writers.Add((playerId, voiceId), value); VoiceTrackInfos.Add(new VoiceTrackInfo(masterId, playerId, voiceId, text, startTime)); } value.Write(Il2CppArrayBase<float>.op_Implicit(frame.Buf)); } }); RemoteVoiceLink obj2 = link; obj2.RemoteVoiceRemoved += Action.op_Implicit((Action)delegate { stopWriter(playerId, voiceId); }); } } public static class Utilities { private static GameObject customMultiplayerMaps; public static string GetFriendlySceneName(string scene) { if (1 == 0) { } string result = ((scene == "Map0") ? "Ring" : ((!(scene == "Map1")) ? scene : "Pit")); if (1 == 0) { } return result; } public static string GetActiveCustomMapName() { if (customMultiplayerMaps == null) { customMultiplayerMaps = GameObject.Find("CustomMultiplayerMaps"); } if ((Object)(object)customMultiplayerMaps == (Object)null) { return null; } for (int i = 0; i < customMultiplayerMaps.transform.childCount; i++) { Transform child = customMultiplayerMaps.transform.GetChild(i); if (((Component)child).gameObject.activeInHierarchy) { return ((Object)child).name; } } return null; } public static string[] RebuildCustomMapFromScene() { //IL_006c: Unknown result type (might be due to invalid IL or missing references) //IL_0071: Unknown result type (might be due to invalid IL or missing references) //IL_00bf: Unknown result type (might be due to invalid IL or missing references) //IL_00c4: Unknown result type (might be due to invalid IL or missing references) //IL_00c8: Unknown result type (might be due to invalid IL or missing references) //IL_00cd: Unknown result type (might be due to invalid IL or missing references) //IL_00d1: Unknown result type (might be due to invalid IL or missing references) //IL_00d6: Unknown result type (might be due to invalid IL or missing references) //IL_00da: Unknown result type (might be due to invalid IL or missing references) //IL_00df: Unknown result type (might be due to invalid IL or missing references) //IL_00b6: Unknown result type (might be due to invalid IL or missing references) //IL_00bb: Unknown result type (might be due to invalid IL or missing references) GameObject val = GameObject.Find("CustomMapParent"); if ((Object)(object)val == (Object)null) { return null; } List<string> list = new List<string>(); list.Add("1"); list.Add(val.transform.childCount.ToString()); for (int i = 0; i < val.transform.childCount; i++) { Transform child = val.transform.GetChild(i); string name = ((Object)child).name; Color val2 = Color.white; Renderer component = ((Component)child).GetComponent<Renderer>(); if ((Object)(object)component != (Object)null && (Object)(object)component.material != (Object)null && component.material.HasProperty("_Color")) { val2 = component.material.color; } Vector3 position = child.position; Quaternion rotation = child.rotation; Vector3 eulerAngles = ((Quaternion)(ref rotation)).eulerAngles; Vector3 localScale = child.localScale; list.Add(name); list.Add(val2.r.ToString(CultureInfo.InvariantCulture)); list.Add(val2.g.ToString(CultureInfo.InvariantCulture)); list.Add(val2.b.ToString(CultureInfo.InvariantCulture)); list.Add(position.x.ToString(CultureInfo.InvariantCulture)); list.Add(position.y.ToString(CultureInfo.InvariantCulture)); list.Add(position.z.ToString(CultureInfo.InvariantCulture)); list.Add(eulerAngles.x.ToString(CultureInfo.InvariantCulture)); list.Add(eulerAngles.y.ToString(CultureInfo.InvariantCulture)); list.Add(eulerAngles.z.ToString(CultureInfo.InvariantCulture)); list.Add(localScale.x.ToString(CultureInfo.InvariantCulture)); list.Add(localScale.y.ToString(CultureInfo.InvariantCulture)); list.Add(localScale.z.ToString(CultureInfo.InvariantCulture)); } return list.ToArray(); } public static GameObject GetCustomMap(string mapName) { //IL_002e: Unknown result type (might be due to invalid IL or missing references) //IL_0034: Unknown result type (might be due to invalid IL or missing references) //IL_007c: Unknown result type (might be due to invalid IL or missing references) //IL_0082: Unknown result type (might be due to invalid IL or missing references) if (customMultiplayerMaps == null) { customMultiplayerMaps = GameObject.Find("CustomMultiplayerMaps"); } if ((Object)(object)customMultiplayerMaps == (Object)null) { Main.ReplayError("Selected replay uses a custom map, but custom maps are not installed."); return null; } Transform obj = customMultiplayerMaps.transform.Find(mapName); GameObject val = ((obj != null) ? ((Component)obj).gameObject : null); if ((Object)(object)val == (Object)null) { Main.ReplayError("Could not find the custom map '" + mapName + "."); return null; } return val; } public static string CleanName(string name) { if (string.IsNullOrWhiteSpace(name)) { return "Unknown"; } string text = Regex.Replace(name, "<.*?>", string.Empty); StringBuilder stringBuilder = new StringBuilder(text.Length); string text2 = text; foreach (char c in text2) { if (!char.IsSurrogate(c)) { stringBuilder.Append(c); } } text = stringBuilder.ToString(); char[] invalidFileNameChars = Path.GetInvalidFileNameChars(); foreach (char c2 in invalidFileNameChars) { text = text.Replace(c2.ToString(), ""); } text = Regex.Replace(text, "\\s+", " ").Trim(); text = text.Replace("\\", "_").Replace("/", "_"); return string.IsNullOrEmpty(text) ? "Unknown" : text; } public static bool IsReplayClone(PlayerController controller) { if ((Object)(object)controller == (Object)null || Main.Playback.PlaybackPlayers == null) { return false; } return Object.op_Implicit((Object)(object)((Component)controller).GetComponent<ReplayPlayback.Clone>()); } public static IEnumerable<GameObject> EnumerateMatchPedestals() { return from p in ((IEnumerable<Pedestal>)Object.FindObjectsOfType<Pedestal>(true)).Where(delegate(Pedestal p) { //IL_0006: 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) Scene scene = ((Component)p).gameObject.scene; return ((Scene)(ref scene)).buildIndex != -1; }) select ((Component)p).gameObject; } public static IEnumerator LoadMap(int index, float fadeDuration = 2f, Action onLoaded = null, float onLoadedDelay = 0.01f) { SceneSingleton<CombatManager>.instance.CleanStructureList(); foreach (Structure structure in SceneSingleton<CombatManager>.instance.structures.ToArray()) { if (!((Object)(object)structure == (Object)null)) { try { structure.Kill(Vector3.zero, false, false, true); } catch { } } } Singleton<SceneManager>.instance.LoadSceneAsync(index, false, false, fadeDuration, (LoadSceneMode)0, (AudioCall)null); while (Singleton<SceneManager>.instance.IsLoadingScene) { yield return null; } yield return (object)new WaitForSeconds(onLoadedDelay); onLoaded?.Invoke(); } public static bool HasVFXType(string type, Transform obj) { Il2CppArrayBase<ReplayPlayback.ReplayTag> componentsInChildren = ((Component)obj).GetComponentsInChildren<ReplayPlayback.ReplayTag>(); foreach (ReplayPlayback.ReplayTag item in componentsInChildren) { if (item.Type == type) { return true; } } return false; } public static Vector3 GetPositionOverMesh(float a, float b, MeshRenderer renderer) { //IL_000b: Unknown result type (might be due to invalid IL or missing references) //IL_0010: Unknown result type (might be due to invalid IL or missing references) //IL_0013: Unknown result type (might be due to invalid IL or missing references) //IL_001f: Unknown result type (might be due to invalid IL or missing references) //IL_0032: Unknown result type (might be due to invalid IL or missing references) //IL_0037: Unknown result type (might be due to invalid IL or missing references) //IL_0046: Unknown result type (might be due to invalid IL or missing references) //IL_0047: Unknown result type (might be due to invalid IL or missing references) //IL_004c: Unknown result type (might be due to invalid IL or missing references) //IL_0050: Unknown result type (might be due to invalid IL or missing references) float num = Mathf.Clamp01(a / b); Bounds localBounds = ((Renderer)renderer).localBounds; float x = Mathf.Lerp(((Bounds)(ref localBounds)).min.x, ((Bounds)(ref localBounds)).max.x, num); Vector3 center = ((Bounds)(ref localBounds)).center; center.x = x; return ((Component)renderer).transform.TransformPoint(center); } public static float GetProgressFromMeshPosition(Vector3 worldPos, MeshRenderer renderer) { //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_0008: Unknown result type (might be due to invalid IL or missing references) //IL_000d: Unknown result type (might be due to invalid IL or missing references) //IL_000f: Unknown result type (might be due to invalid IL or missing references) //IL_0014: Unknown result type (might be due to invalid IL or missing references) //IL_0017: Unknown result type (might be due to invalid IL or missing references) //IL_0024: Unknown result type (might be due to invalid IL or missing references) //IL_0047: Unknown result type (might be due to invalid IL or missing references) Vector3 val = ((Component)renderer).transform.InverseTransformPoint(worldPos); Bounds localBounds = ((Renderer)renderer).localBounds; float x = ((Bounds)(ref localBounds)).min.x; float x2 = ((Bounds)(ref localBounds)).max.x; if (Mathf.Approximately(x2, x)) { return 0f; } float num = Mathf.InverseLerp(x, x2, val.x); return Mathf.Clamp01(num); } public static Marker[] AddMarkers(ReplaySerializer.ReplayHeader header, MeshRenderer timelineRenderer, bool hideMarkers = true) { //IL_007b: Unknown result type (might be due to invalid IL or missing references) //IL_0080: Unknown result type (might be due to invalid IL or missing references) //IL_00c6: Unknown result type (might be due to invalid IL or missing references) //IL_00d8: Unknown result type (might be due to invalid IL or missing references) //IL_0112: Unknown result type (might be due to invalid IL or missing references) foreach (ReplayPlayback.ReplayTag componentsInChild in ((Component)((Component)timelineRenderer).transform).GetComponentsInChildren<ReplayPlayback.ReplayTag>()) { Object.Destroy((Object)(object)((Component)componentsInChild).gameObject); } if (header.Markers == null) { return null; } Marker[] markers = header.Markers; Marker[] array = markers; Color val2 = default(Color); foreach (Marker marker in array) { Vector3 positionOverMesh = GetPositionOverMesh(marker.time, header.Duration, timelineRenderer); GameObject val = Object.Instantiate<GameObject>(ReplayPlaybackControls.markerPrefab, ((Component)timelineRenderer).transform); if (!hideMarkers) { val.layer = LayerMask.NameToLayer("Default"); } val.transform.localScale = new Vector3(0.0062f, 1.0836f, 0.0128f); val.transform.position = positionOverMesh; ((Color)(ref val2))..ctor(marker.r, marker.g, marker.b, 1f); ((Renderer)val.GetComponent<MeshRenderer>()).material.SetColor("_Overlay", val2); val.AddComponent<ReplayPlayback.ReplayTag>(); val.SetActive(true); } return markers; } public static string FormatBytes(long bytes) { if (!((double)bytes >= 1048576.0)) { if (!((double)bytes >= 1024.0)) { return $"{bytes} B"; } return $"{(double)bytes / 1024.0:0.##} KB"; } return $"{(double)bytes / 1048576.0:0.##} MB"; } public static float EaseInOut(float t) { return (t < 0.5f) ? (2f * t * t) : (1f - Mathf.Pow(-2f * t + 2f, 2f) / 2f); } public static float EaseOut(float t) { return 1f - Mathf.Pow(1f - t, 4f); } public static float EaseIn(float t) { return Mathf.Pow(t, 3f); } public static IEnumerator LerpValue<T>(Func<T> getter, Action<T> setter, Func<T, T, float, T> lerpFunc, T targetValue, float duration, Func<float, float> easing = null, Action done = null, float delay = 0f, Func<bool> isValid = null) { yield return (object)new WaitForSeconds(delay); T startValue = getter(); float t = 0f; while (t < 1f) { if (isValid != null && !isValid()) { yield break; } t += Time.deltaTime / duration; float easedT = easing?.Invoke(Mathf.Clamp01(t)) ?? t; setter(lerpFunc(startValue, targetValue, easedT)); yield return null; } setter(targetValue); done?.Invoke(); } public static Color32 RandomColor() { //IL_002a: Unknown result type (might be due to invalid IL or missing references) //IL_002f: Unknown result type (might be due to invalid IL or missing references) //IL_0030: Unknown result type (might be due to invalid IL or missing references) //IL_0031: Unknown result type (might be due to invalid IL or missing references) //IL_0036: Unknown result type (might be due to invalid IL or missing references) //IL_003a: Unknown result type (might be due to invalid IL or missing references) float value = Random.value; float num = Random.Range(0.6f, 0.9f); float num2 = Random.Range(0.7f, 1f); Color val = Color.HSVToRGB(value, num, num2); return Color32.op_Implicit(val); } public static T[] NewArray<T>(int count, T[] copyFrom = null) where T : new() { T[] array = new T[count]; for (int i = 0; i < count; i++) { array[i] = ((copyFrom != null && i < copyFrom.Length) ? copyFrom[i] : new T()); } return array; } public static int GetPageCount(int itemCount, int pageSize) { return (itemCount + pageSize - 1) / pageSize; } public static string FormatPage(int currentPage, int pageCount) { return (pageCount == 0) ? "0 / 0" : $"{currentPage + 1} / {pageCount}"; } } [RegisterTypeInIl2Cpp] public class LookAtPlayer : MonoBehaviour { public bool lockX; public bool lockY; public bool lockZ; private void Update() { //IL_0016: Unknown result type (might be due to invalid IL or missing references) //IL_0041: Unknown result type (might be due to invalid IL or missing references) //IL_004c: Unknown result type (might be due to invalid IL or missing references) //IL_0051: Unknown result type (might be due to invalid IL or missing references) //IL_0056: Unknown result type (might be due to invalid IL or missing references) //IL_0070: Unknown result type (might be due to invalid IL or missing references) //IL_0071: Unknown result type (might be due to invalid IL or missing references) //IL_0076: Unknown result type (might be due to invalid IL or missing references) //IL_008a: Unknown result type (might be due to invalid IL or missing references) //IL_008f: Unknown result type (might be due to invalid IL or missing references) //IL_0094: Unknown result type (might be due to invalid IL or missing references) //IL_0097: Unknown result type (might be due to invalid IL or missing references) //IL_009c: Unknown result type (might be due to invalid IL or missing references) //IL_00a3: Unknown result type (might be due to invalid IL or missing references) //IL_00a8: Unknown result type (might be due to invalid IL or missing references) //IL_00ac: Unknown result type (might be due to invalid IL or missing references) //IL_00b1: Unknown result type (might be due to invalid IL or missing references) //IL_00c1: Unknown result type (might be due to invalid IL or missing references) //IL_00db: Unknown result type (might be due to invalid IL or missing references) //IL_0107: Unknown result type (might be due to invalid IL or missing references) //IL_0108: Unknown result type (might be due to invalid IL or missing references) //IL_00f5: Unknown result type (might be due to invalid IL or missing references) ((Component)this).transform.rotation = Quaternion.Euler(0f, 270f, 0f); Camera main = Camera.main; if (!Object.op_Implicit((Object)(object)main)) { return; } Vector3 val = ((Component)main).transform.position - ((Component)this).transform.position; if (!(((Vector3)(ref val)).sqrMagnitude < 0.0001f)) { Quaternion val2 = Quaternion.LookRotation(val, Vector3.up) * Quaternion.Euler(0f, 180f, 0f); Vector3 eulerAngles = ((Quaternion)(ref val2)).eulerAngles; Quaternion rotation = ((Component)this).transform.rotation; Vector3 eulerAngles2 = ((Quaternion)(ref rotation)).eulerAngles; if (lockX) { eulerAngles.x = eulerAngles2.x; } if (lockY) { eulerAngles.y = eulerAngles2.y; } if (lockZ) { eulerAngles.z = eulerAngles2.z; } ((Component)this).transform.rotation = Quaternion.Euler(eulerAngles); } } } [RegisterTypeInIl2Cpp] public class DeleteAfterSeconds : MonoBehaviour { public float destroyTime = 10f; private float spawnTime; public void Awake() { spawnTime = Main.Playback.elapsedPlaybackTime; } public void Update() { if (Mathf.Abs(Main.Playback.elapsedPlaybackTime - spawnTime) >= destroyTime) { Object.Destroy((Object)(object)((Component)this).gameObject); } } } } namespace ReplayMod.Replay.UI { public static class ReplayCrystals { [RegisterTypeInIl2Cpp] public class Crystal : MonoBehaviour { public string ReplayPath; public string Title; public TextMeshPro titleText; public Color32 BaseColor; private Renderer rend; private MaterialPropertyBlock mpb; public bool isGrabbed; public bool isAnimation; public bool hasLeftTable; private Vector3 basePosition; private Vector3 velocity; private Vector3 lastPosition; private Vector3 angularVelocity; private Quaternion lastRotation; private bool hasSavedAfterRelease; private object scaleRoutine; private object positionRoutine; private bool isTextVisible; private void Awake() { //IL_0008: Unknown result type (might be due to invalid IL or missing references) //IL_000d: Unknown result type (might be due to invalid IL or missing references) //IL_0019: Unknown result type (might be due to invalid IL or missing references) //IL_001e: Unknown result type (might be due to invalid IL or missing references) basePosition = ((Component)this).transform.position; lastPosition = ((Component)this).transform.position; } public CrystalState CaptureState() { //IL_002b: Unknown result type (might be due to invalid IL or missing references) //IL_0042: Unknown result type (might be due to invalid IL or missing references) //IL_0059: Unknown result type (might be due to invalid IL or missing references) //IL_006b: Unknown result type (might be due to invalid IL or missing references) //IL_0070: Unknown result type (might be due to invalid IL or missing references) CrystalState result = default(CrystalState); result.ReplayPath = ReplayPath; result.Title = Title; result.x = ((Component)this).transform.position.x; result.y = ((Component)this).transform.position.y; result.z = ((Component)this).transform.position.z; result.BaseColor = BaseColor; return result; } public void RestoreState(CrystalState state) { //IL_000f: Unknown result type (might be due to invalid IL or missing references) //IL_0014: Unknown result type (might be due to invalid IL or missing references) //IL_002c: Unknown result type (might be due to invalid IL or missing references) //IL_0031: Unknown result type (might be due to invalid IL or missing references) //IL_003d: Unknown result type (might be due to invalid IL or missing references) ReplayPath = state.ReplayPath; BaseColor = state.BaseColor; basePosition = new Vector3(state.x, state.y, state.z); ((Component)this).transform.position = basePosition; Title = state.Title; ((TMP_Text)titleText).text = state.Title; ((TMP_Text)titleText).ForceMeshUpdate(false, false); ApplyVisuals(); } public void ApplyVisuals() { //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_000c: Unknown result type (might be due to invalid IL or missing references) //IL_0048: Unknown result type (might be due to invalid IL or missing references) //IL_0037: Unknown result type (might be due to invalid IL or missing references) //IL_0041: Expected O, but got Unknown //IL_0080: Unknown result type (might be due to invalid IL or missing references) Color baseColor = Color32.op_Implicit(BaseColor); if ((Object)(object)rend == (Object)null) { rend = ((Component)this).GetComponent<Renderer>(); } if (mpb == null) { mpb = new MaterialPropertyBlock(); } ApplyBlockToRenderer(rend, baseColor); foreach (Renderer componentsInChild in ((Component)this).GetComponentsInChildren<Renderer>(true)) { if (!((Object)(object)componentsInChild == (Object)(object)rend)) { ApplyBlockToRenderer(componentsInChild, baseColor); } } } private void ApplyBlockToRenderer(Renderer r, Color baseColor) { //IL_0025: Unknown result type (might be due to invalid IL or missing references) //IL_0037: Unknown result type (might be due to invalid IL or missing references) //IL_0038: Unknown result type (might be due to invalid IL or missing references) //IL_004e: Unknown result type (might be due to invalid IL or missing references) //IL_004f: Unknown result type (might be due to invalid IL or missing references) if (!((Object)(object)r == (Object)null)) { mpb.Clear(); mpb.SetColor("_Base_Color", baseColor); mpb.SetColor("_Edge_Color", DeriveEdge(baseColor)); mpb.SetColor("_Shadow_Color", DeriveShadow(baseColor)); r.SetPropertyBlock(mpb); } } public void ShowText() { //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_000c: Unknown result type (might be due to invalid IL or missing references) //IL_0020: Unknown result type (might be due to invalid IL or missing references) Animate(Vector3.one * 0.02f, new Vector3(0f, 0f, 0.0049f)); isTextVisible = true; } public void HideText() { //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_0007: Unknown result type (might be due to invalid IL or missing references) Animate(Vector3.zero, Vector3.zero); isTextVisible = false; } private void Animate(Vector3 scale, Vector3 position) { //IL_003f: Unknown result type (might be due to invalid IL or missing references) //IL_00b0: Unknown result type (might be due to invalid IL or missing references) if (scaleRoutine != null) { MelonCoroutines.Stop(scaleRoutine); } scaleRoutine = MelonCoroutines.Start(Utilities.LerpValue((Func<Vector3>)(() => titleText.transform.localScale), (Action<Vector3>)delegate(Vector3 v) { //IL_000b: Unknown result type (might be due to invalid IL or missing references) titleText.transform.localScale = v; }, (Func<Vector3, Vector3, float, Vector3>)Vector3.Lerp, scale, 0.5f, (Func<float, float>)Utilities.EaseInOut, (Action)null, 0f, (Func<bool>)(() => (Object)(object)titleText.transform != (Object)null))); if (positionRoutine != null) { MelonCoroutines.Stop(positionRoutine); } positionRoutine = MelonCoroutines.Start(Utilities.LerpValue((Func<Vector3>)(() => titleText.transform.localPosition), (Action<Vector3>)delegate(Vector3 v) { //IL_000b: Unknown result type (might be due to invalid IL or missing references) titleText.transform.localPosition = v; }, (Func<Vector3, Vector3, float, Vector3>)Vector3.Lerp, position, 0.5f, (Func<float, float>)Utilities.EaseInOut, (Action)null, 0f, (Func<bool>)(() => (Object)(object)titleText.transform != (Object)null))); } public void Grab() { //IL_002e: Unknown result type (might be due to invalid IL or missing references) //IL_0033: Unknown result type (might be due to invalid IL or missing references) if (!isGrabbed && !isAnimation) { isGrabbed = true; hasLeftTable = true; hasSavedAfterRelease = false; velocity = Vector3.zero; HideText(); } } public void Release() { if (isGrabbed) { isGrabbed = false; } } private void Update() { //IL_003c: Unknown result type (might be due to invalid IL or missing references) //IL_0041: Unknown result type (might be due to invalid IL or missing references) //IL_004d: Unknown result type (might be due to invalid IL or missing references) //IL_0053: Unknown result type (might be due to invalid IL or missing references) //IL_0058: Unknown result type (might be due to invalid IL or missing references) //IL_0062: Unknown result type (might be due to invalid IL or missing references) //IL_0067: Unknown result type (might be due to invalid IL or missing references) //IL_0072: Unknown result type (might be due to invalid IL or missing references) //IL_0077: Unknown result type (might be due to invalid IL or missing references) //IL_0078: Unknown result type (might be due to invalid IL or missing references) //IL_007a: Unknown result type (might be due to invalid IL or missing references) //IL_007f: Unknown result type (might be due to invalid IL or missing references) //IL_0084: Unknown result type (might be due to invalid IL or missing references) //IL_0089: Unknown result type (might be due to invalid IL or missing references) //IL_00b0: Unknown result type (might be due to invalid IL or missing references) //IL_00ba: Unknown result type (might be due to invalid IL or missing references) //IL_00c4: Unknown result type (might be due to invalid IL or missing references) //IL_00c9: Unknown result type (might be due to invalid IL or missing references) //IL_00cf: Unknown result type (might be due to invalid IL or missing references) //IL_00d0: Unknown result type (might be due to invalid IL or missing references) //IL_00dd: Unknown result type (might be due to invalid IL or missing references) //IL_00e2: Unknown result type (might be due to invalid IL or missing references) if (!isAnimation) { if (!isGrabbed) { ApplyThrowVelocity(); HandleProximity(); } else { basePosition = ((Component)this).transform.position; velocity = (((Component)this).transform.position - lastPosition) / Time.deltaTime; Quaternion rotation = ((Component)this).transform.rotation; Quaternion val = rotation * Quaternion.Inverse(lastRotation); float num = default(float); Vector3 val2 = default(Vector3); ((Quaternion)(ref val)).ToAngleAxis(ref num, ref val2); if (num > 180f) { num -= 360f; } angularVelocity = val2 * (num * Mathf.Deg2Rad) / Time.deltaTime; lastRotation = rotation; } lastPosition = ((Component)this).transform.position; } else { HideText(); } } private void HandleProximity() { //IL_001a: Unknown result type (might be due to invalid IL or missing references) //IL_0025: Unknown result type (might be due to invalid IL or missing references) Transform head = Main.instance.head; if (!((Object)(object)head == (Object)null)) { float num = Vector3.Distance(head.position, ((Component)this).transform.position); if (!isTextVisible && num < 2f) { isTextVisible = true; ShowText(); } else if (isTextVisible && num > 2.2f) { isTextVisible = false; HideText(); } } } private void ApplyThrowVelocity() { //IL_005c: Unknown result type (might be due to invalid IL or missing references) //IL_0062: Unknown result type (might be due to invalid IL or missing references) //IL_006c: Unknown result type (might be due to invalid IL or missing references) //IL_0071: Unknown result type (might be due to invalid IL or missing references) //IL_0076: Unknown result type (might be due to invalid IL or missing references) //IL_007d: Unknown result type (might be due to invalid IL or missing references) //IL_0082: Unknown result type (might be due to invalid IL or missing references) //IL_0092: Unknown result type (might be due to invalid IL or missing references) //IL_0097: Unknown result type (might be due to invalid IL or missing references) //IL_00a3: Unknown result type (might be due to invalid IL or missing references) //IL_0101: Unknown result type (might be due to invalid IL or missing references) //IL_0106: Unknown result type (might be due to invalid IL or missing references) //IL_0116: Unknown result type (might be due to invalid IL or missing references) //IL_011b: Unknown result type (might be due to invalid IL or missing references) //IL_00df: Unknown result type (might be due to invalid IL or missing references) //IL_00e4: Unknown result type (might be due to invalid IL or missing references) //IL_00ef: Unknown result type (might be due to invalid IL or missing references) //IL_00f4: Unknown result type (might be due to invalid IL or missing references) //IL_004b: Unknown result type (might be due to invalid IL or missing references) //IL_0050: Unknown result type (might be due to invalid IL or missing references) //IL_0142: Unknown result type (might be due to invalid IL or missing references) //IL_0156: Unknown result type (might be due to invalid IL or missing references) //IL_0168: Unknown result type (might be due to invalid IL or missing references) //IL_017c: Unknown result type (might be due to invalid IL or missing references) //IL_01cf: Unknown result type (might be due to invalid IL or missing references) //IL_01b9: Unknown result type (might be due to invalid IL or missing references) //IL_01d4: Unknown result type (might be due to invalid IL or missing references) //IL_01e2: Unknown result type (might be due to invalid IL or missing references) //IL_01e7: Unknown result type (might be due to invalid IL or missing references) //IL_01f4: Unknown result type (might be due to invalid IL or missing references) if (((Vector3)(ref velocity)).sqrMagnitude < 0.001f && ((Vector3)(ref angularVelocity)).sqrMagnitude < 0.001f) { if (!hasSavedAfterRelease) { SaveCrystals(); hasSavedAfterRelease = true; } velocity = Vector3.zero; return; } basePosition += velocity * Time.deltaTime; velocity = Vector3.Lerp(velocity, Vector3.zero, 8f * Time.deltaTime); ((Component)this).transform.position = basePosition; float num = ((Vector3)(ref angularVelocity)).magnitude * Time.deltaTime; if (num > 0f) { ((Component)this).transform.rotation = Quaternion.AngleAxis(num * Mathf.Rad2Deg, ((Vector3)(ref angularVelocity)).normalized) * ((Component)this).transform.rotation; } angularVelocity = Vector3.Lerp(angularVelocity, Vector3.zero, 8f * Time.deltaTime); if (((Vector3)(ref angularVelocity)).sqrMagnitude < 0.0005f) { float num2 = Quaternion.Angle(((Component)this).transform.rotation, Quaternion.Euler(-90f, 0f, 0f)); float num3 = Quaternion.Angle(((Component)this).transform.rotation, Quaternion.Euler(90f, 0f, 0f)); if (num2 < 5f || num3 < 5f) { Quaternion val = ((num2 < num3) ? Quaternion.Euler(-90f, 0f, 0f) : Quaternion.Euler(90f, 0f, 0f)); ((Component)this).transform.rotation = Quaternion.Slerp(((Component)this).transform.rotation, val, 8f * Time.deltaTime); } } } private static Color DeriveEdge(Color baseColor) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0027: Unknown result type (might be due to invalid IL or missing references) //IL_002c: Unknown result type (might be due to invalid IL or missing references) //IL_002f: Unknown result type (might be due to invalid IL or missing references) float num = default(float); float num2 = default(float); float num3 = default(float); Color.RGBToHSV(baseColor, ref num, ref num2, ref num3); return Color.HSVToRGB(num, Mathf.Clamp01(num2 + 0.05f), Mathf.Clamp01(num3 + 0.35f)); } private static Color DeriveShadow(Color baseColor) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0027: Unknown result type (might be due to invalid IL or missing references) //IL_002c: Unknown result type (might be due to invalid IL or missing references) //IL_002f: Unknown result type (might be due to invalid IL or missing references) float num = default(float); float num2 = default(float); float num3 = default(float); Color.RGBToHSV(baseColor, ref num, ref num2, ref num3); return Color.HSVToRGB(num, Mathf.Clamp01(num2 - 0.25f), Mathf.Clamp01(num3 - 0.4f)); } } [Serializable] public struct CrystalState { public string ReplayPath; public string Title; public float x; public float y; public float z; public Color32 BaseColor; } public static GameObject crystalPrefab; public static GameObject crystalParent; public static List<Crystal> Crystals = new List<Crystal>(); public static Crystal heldCrystal; public static bool isHeldByRight; public static VisualEffect crystalizeVFX; public static (string path, Color32 color) lastReplayColor = default((string, Color32)); public static void HandleCrystals() { //IL_00b0: Unknown result type (might be due to invalid IL or missing references) //IL_0123: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)heldCrystal != (Object)null && ((isHeldByRight && RightController.GetGrip() < 0.5f) || (!isHeldByRight && LeftController.GetGrip() < 0.5f))) { heldCrystal.Release(); ((Component)heldCrystal).transform.SetParent(crystalParent.transform, true); heldCrystal = null; } else { if (!((Object)(object)heldCrystal == (Object)null) || !Main.isSceneReady) { return; } if (RightController.GetGrip() > 0.5f) { Crystal crystal = FindClosestCrystal(Main.instance.rightHand.position, 0.1f); if ((Object)(object)crystal != (Object)null) { heldCrystal = crystal; isHeldByRight = true; crystal.Grab(); ((Component)crystal).transform.SetParent(((Component)Main.instance.rightHand).transform, true); } } else if (LeftController.GetGrip() > 0.5f) { Crystal crystal2 = FindClosestCrystal(Main.instance.leftHand.position, 0.1f); if ((Object)(object)crystal2 != (Object)null) { heldCrystal = crystal2; isHeldByRight = false; crystal2.Grab(); ((Component)crystal2).transform.SetParent(((Component)Main.instance.leftHand).transform, true); } } } } public static void LoadCrystals(string scene = null) { if (string.IsNullOrEmpty(scene)) { scene = Main.currentScene; } string path = Path.Combine(MelonEnvironment.UserDataDirectory, "ReplayMod", "Settings", "replayCrystals.json"); if (!File.Exists(path)) { return; } string text = File.ReadAllText(path); Dictionary<string, CrystalState[]> dictionary = JsonConvert.DeserializeObject<Dictionary<string, CrystalState[]>>(text); if (dictionary != null && dictionary.TryGetValue(scene, out var value)) { Crystals = new List<Crystal>(); CrystalState[] array = value; foreach (CrystalState state in array) { CreateCrystal().RestoreState(state); } } } public static void SaveCrystals(string scene = null) { if (string.IsNullOrEmpty(scene)) { scene = Main.currentScene; } string path = Path.Combine(MelonEnvironment.UserDataDirectory, "ReplayMod", "Settings", "replayCrystals.json"); Dictionary<string, CrystalState[]> dictionary = new Dictionary<string, CrystalState[]>(); if (File.Exists(path)) { string text = File.ReadAllText(path); dictionary = JsonConvert.DeserializeObject<Dictionary<string, CrystalState[]>>(text) ?? new Dictionary<string, CrystalState[]>(); } if (Crystals == null) { return; } List<CrystalState> list = new List<CrystalState>(); foreach (Crystal crystal in Crystals) { if ((Object)(object)crystal != (Object)null) { list.Add(crystal.CaptureState()); } } dictionary[scene] = list.ToArray(); string contents = JsonConvert.SerializeObject((object)dictionary, (Formatting)1); File.WriteAllText(path, contents); } public static Crystal FindClosestCrystal(Vector3 handPos, float maxDistance) { //IL_003e: Unknown result type (might be due to invalid IL or missing references) //IL_0043: Unknown result type (might be due to invalid IL or missing references) //IL_0044: Unknown result type (might be due to invalid IL or missing references) //IL_0049: Unknown result type (might be due to invalid IL or missing references) Crystal result = null; float num = maxDistance * maxDistance; foreach (Crystal crystal in Crystals) { if (!((Object)(object)((crystal != null) ? ((Component)crystal).transform : null) == (Object)null)) { Vector3 val = ((Component)crystal).transform.position - handPos; float sqrMagnitude = ((Vector3)(ref val)).sqrMagnitude; if (sqrMagnitude < num) { num = sqrMagnitude; result = crystal; } } } return result; } public static Crystal CreateCrystal(Vector3 position, ReplaySerializer.ReplayHeader header, string path, bool useAnimation = false, bool applyRandomColor = false) { //IL_0017: Unknown result type (might be due to invalid IL or missing references) //IL_0021: Expected O, but got Unknown //IL_00b8: Unknown result type (might be due to invalid IL or missing references) //IL_00cc: Unknown result type (might be due to invalid IL or missing references) //IL_00d1: Unknown result type (might be due to invalid IL or missing references) //IL_00d6: Unknown result type (might be due to invalid IL or missing references) //IL_0106: Unknown result type (might be due to invalid IL or missing references) //IL_0126: Unknown result type (might be due to invalid IL or missing references) //IL_019e: Unknown result type (might be due to invalid IL or missing references) //IL_01a3: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)crystalParent == (Object)null) { crystalParent = new GameObject("Crystals"); } Crystal crystal = Object.Instantiate<GameObject>(crystalPrefab, crystalParent.transform).AddComponent<Crystal>(); string text = (Path.GetFileNameWithoutExtension(path).StartsWith("Replay") ? header.Title : Path.GetFileNameWithoutExtension(path)); ((Object)crystal).name = $"Crystal ({text}, {header.Date})"; ((Component)crystal).transform.position = position; crystal.Title = text; GameObject val = Create.NewText(text, 1f, Color.white, Vector3.zero, Quaternion.identity); ((Object)val).name = "Replay Title"; val.transform.SetParent(((Component)crystal).transform, false); val.transform.localScale = Vector3.zero; val.transform.localRotation = Quaternion.Euler(0f, 270f, 270f); crystal.titleText = val.GetComponent<TextMeshPro>(); ((Component)crystal.titleText).GetComponent<RectTransform>().SetSizeWithCurrentAnchors((Axis)0, 2f); ((TMP_Text)crystal.titleText).horizontalAlignment = (HorizontalAlignmentOptions)2; ((TMP_Text)crystal.titleText).ForceMeshUpdate(false, false); LookAtPlayer lookAtPlayer = val.AddComponent<LookAtPlayer>(); lookAtPlayer.lockX = true; lookAtPlayer.lockZ = true; crystal.ReplayPath = ReplayFiles.explorer.CurrentReplayPath; if (applyRandomColor) { crystal.BaseColor = Utilities.RandomColor(); crystal.ApplyVisuals(); } ((Component)crystal).gameObject.SetActive(true); Crystals.Add(crystal); if (useAnimation) { MelonCoroutines.Start(CrystalSpawnAnimation(crystal)); } return crystal; } public static Crystal CreateCrystal() { //IL_0057: Unknown result type (might be due to invalid IL or missing references) //IL_005c: Unknown result type (might be due to invalid IL or missing references) //IL_0061: Unknown result type (might be due to invalid IL or missing references) //IL_0091: Unknown result type (might be due to invalid IL or missing references) //IL_00b1: Unknown result type (might be due to invalid IL or missing references) //IL_0015: Unknown result type (might be due to invalid IL or missing references) //IL_001f: Expected O, but got Unknown if ((Object)(object)crystalParent == (Object)null) { crystalParent = new GameObject("Crystals"); } Crystal crystal = Object.Instantiate<GameObject>(crystalPrefab, crystalParent.transform).AddComponent<Crystal>(); crystal.hasLeftTable = true; ((Component)crystal).gameObject.SetActive(true); GameObject val = Create.NewText("", 1f, Color.white, Vector3.zero, Quaternion.identity); ((Object)val).name = "Replay Title"; val.transform.SetParent(((Component)crystal).transform, false); val.transform.localScale = Vector3.zero; val.transform.localRotation = Quaternion.Euler(0f, 270f, 270f); crystal.titleText = val.GetComponent<TextMeshPro>(); ((Component)crystal.titleText).GetComponent<RectTransform>().SetSizeWithCurrentAnchors((Axis)0, 2f); ((TMP_Text)crystal.titleText).horizontalAlignment = (HorizontalAlignmentOptions)2; ((TMP_Text)crystal.titleText).ForceMeshUpdate(false, false); LookAtPlayer lookAtPlayer = val.AddComponent<LookAtPlayer>(); lookAtPlayer.lockX = true; lookAtPlayer.lockZ = true; Crystals.Add(crystal); return crystal; } public static IEnumerator ReadCrystal(Crystal crystal) { crystal.isAnimation = true; lastReplayColor = (crystal.ReplayPath, crystal.BaseColor); Singleton<AudioManager>.instance.Play(ReplayCache.SFX["Call_GearMarket_ButtonUnpress"], ((Component)crystal).transform.position, false); yield return Utilities.LerpValue((Func<Vector3>)(() => ((Component)crystal).transform.position), (Action<Vector3>)delegate(Vector3 v) { //IL_000b: Unknown result type (might be due to invalid IL or missing references) ((Component)crystal).transform.position = v; }, (Func<Vector3, Vector3, float, Vector3>)Vector3.Lerp, ((Component)Main.instance.replayTable).transform.position + new Vector3(0f, 0.3045f, 0f), 1f, (Func<float, float>)Utilities.EaseInOut, (Action)null, 0f, (Func<bool>)null); yield return (object)new WaitForSeconds(1f); Singleton<AudioManager>.instance.Play(ReplayCache.SFX["Call_Phone_ScreenDown"], ((Component)crystal).transform.position, false); yield return Utilities.LerpValue((Func<Color32>)(() => crystal.BaseColor), (Action<Color32>)delegate(Color32 v) { //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_0008: Unknown result type (might be due to invalid IL or missing references) crystal.BaseColor = v; crystal.ApplyVisuals(); }, (Func<Color32, Color32, float, Color32>)Color32.Lerp, new Color32((byte)50, (byte)50, (byte)50, byte.MaxValue), 1f, (Func<float, float>)Utilities.EaseInOut, (Action)null, 0f, (Func<bool>)null); yield return (object)new WaitForSeconds(0.5f); Singleton<AudioManager>.instance.Play(ReplayCache.SFX["Call_ToolTip_Close"], ((Component)crystal).transform.position, false); MelonCoroutines.Start(Utilities.LerpValue((Func<Vector3>)(() => ((Component)crystal).transform.position), (Action<Vector3>)delegate(Vector3 v) { //IL_000b: Unknown result type (might be due to invalid IL or missing references) ((Component)crystal).transform.position = v; }, (Func<Vector3, Vector3, float, Vector3>)Vector3.Lerp, ((Component)Main.instance.replayTable).transform.position, 0.5f, (Func<float, float>)Utilities.EaseInOut, (Action)null, 0f, (Func<bool>)null)); yield return Utilities.LerpValue((Func<Vector3>)(() => ((Component)crystal).transform.localScale), (Action<Vector3>)delegate(Vector3 v) { //IL_000b: Unknown result type (might be due to invalid IL or missing references) ((Component)crystal).transform.localScale = v; }, (Func<Vector3, Vector3, float, Vector3>)Vector3.Lerp, Vector3.zero, 0.5f, (Func<float, float>)Utilities.EaseInOut, (Action)delegate { //IL_0063: Unknown result type (might be due to invalid IL or missing references) int num = ReplayFiles.explorer.currentReplayEntries.FindIndex((ReplayExplorer.Entry e) => e.FullPath == crystal.ReplayPath); if (num != -1) { ReplayFiles.SelectReplay(num); } else { Singleton<AudioManager>.instance.Play(ReplayCache.SFX["Call_Measurement_Failure"], ((Component)crystal).transform.position, false); } Crystals.Remove(crystal); Object.Destroy((Object)(object)((Component)crystal).gameObject); SaveCrystals(); }, 0f, (Func<bool>)null); } public static IEnumerator CrystalBreakAnimation(string replayPath, Crystal crystal = null, float dist = 0.005f) { Singleton<AudioManager>.instance.Play(ReplayCache.SFX["Call_GearMarket_ButtonUnpress"], ((Component)Main.instance.replayTable).transform.position, false); ((Component)crystalizeVFX).transform.localPosition = new Vector3(0f, 0f, 0.3903f); bool isNewCrystal = false; if ((Object)(object)crystal == (Object)null) { isNewCrystal = true; crystal = CreateCrystal(); ((Component)crystal).transform.position = ((Component)Main.instance.replayTable).transform.position; ((Component)crystal).transform.localScale = Vector3.zero; ((Component)crystal).transform.localRotation = Quaternion.Euler(-90f, 0f, 0f); crystal.BaseColor = Utilities.RandomColor(); crystal.ApplyVisuals(); MelonCoroutines.Start(Utilities.LerpValue((Func<Vector3>)(() => ((Component)crystal).transform.localScale), (Action<Vector3>)delegate(Vector3 v) { //IL_000b: Unknown result type (might be due to invalid IL or missing references) ((Component)crystal).transform.localScale = v; }, (Func<Vector3, Vector3, float, Vector3>)Vector3.Lerp, Vector3.one * 50f, 1f, (Func<float, float>)Utilities.EaseInOut, (Action)null, 0f, (Func<bool>)null)); } crystal.isAnimation = true; if (isNewCrystal) { MelonCoroutines.Start(Utilities.LerpValue((Func<Vector3>)(() => ((Component)crystal).transform.position), (Action<Vector3>)delegate(Vector3 v) { //IL_000b: Unknown result type (might be due to invalid IL or missing references) ((Component)crystal).transform.position = v; }, (Func<Vector3, Vector3, float, Vector3>)Vector3.Lerp, ((Component)Main.instance.replayTable).transform.position + new Vector3(0f, 0.4045f, 0f), 1.5f, (Func<float, float>)Utilities.EaseInOut, (Action)null, 0f, (Func<bool>)null)); } else { yield return Utilities.LerpValue((Func<Vector3>)(() => ((Component)crystal).transform.position), (Action<Vector3>)delegate(Vector3 v) { //IL_000b: Unknown result type (might be due to invalid IL or missing references) ((Component)crystal).transform.position = v; }, (Func<Vector3, Vector3, float, Vector3>)Vector3.Lerp, ((Component)Main.instance.replayTable).transform.position + new Vector3(0f, 0.4045f, 0f), 1.5f, (Func<float, float>)Utilities.EaseInOut, (Action)null, 0f, (Func<bool>)null); } yield return (object)new WaitForSeconds(0.2f); MelonCoroutines.Start(SpinEaseInOut(((Component)crystal).transform, 360f, 2.4f, Vector3.forward)); yield return (object)new WaitForSeconds(1f); Singleton<AudioManager>.instance.Play(ReplayCache.SFX["Call_FistBumpBonus"], ((Component)crystal).transform.position, false); Singleton<AudioManager>.instance.Play(ReplayCache.SFX["Call_Slab_Dismiss"], ((Component)crystal).transform.position, false); Singleton<AudioManager>.instance.Play(ReplayCache.SFX["Call_Shiftstone_Use"], ((Component)crystal).transform.position, false); ((Component)((Component)Main.instance.replayTable).transform.GetChild(7)).GetComponent<VisualEffect>().Play(); Renderer mainRenderer = default(Renderer); if (((Component)crystal).TryGetComponent<Renderer>(ref mainRenderer)) { mainRenderer.enabled = false; } foreach (Transform shard2 in ((Component)crystal).GetComponentsInChildren<Transform>(true)) { if (!((Object)(object)shard2 == (Object)(object)((Component)crystal).transform)) { ((Component)shard2).gameObject.SetActive(true); Vector3 localPosition = shard2.localPosition; MelonCoroutines.Start(Utilities.LerpValue(targetValue: ((Vector3)(ref localPosition)).normalized * dist, getter: (Func<Vector3>)(() => shard2.localPosition), setter: (Action<Vector3>)delegate(Vector3 v) { //IL_0006: Unknown result type (might be due to invalid IL or missing references) shard2.localPosition = v; }, lerpFunc: (Func<Vector3, Vector3, float, Vector3>)Vector3.Lerp, duration: 1f, easing: (Func<float, float>)Utilities.EaseOut, done: (Action)null, delay: 0f, isValid: (Func<bool>)null)); } } yield return (object)new WaitForSeconds(0.948f); foreach (Transform shard in ((Component)crystal).GetComponentsInChildren<Transform>(true)) { if (!((Object)(object)shard == (Object)(object)((Component)crystal).transform)) { MelonCoroutines.Start(Utilities.LerpValue((Func<Vector3>)(() => shard.localScale), (Action<Vector3>)delegate(Vector3 v) { //IL_0006: Unknown result type (might be due to invalid IL or missing references) shard.localScale = v; }, (Func<Vector3, Vector3, float, Vector3>)Vector3.Lerp, Vector3.zero, 0.5f, (Func<float, float>)Utilities.EaseIn, (Action)delegate { //IL_001f: Unknown result type (might be due to invalid IL or missing references) Singleton<AudioManager>.instance.Play(ReplayCache.SFX["Call_DressingRoom_PartPanelTick_BackwardLocked"], ((Component)shard).transform.position, false); }, Random.value, (Func<bool>)(() => (Object)(object)shard != (Object)null))); } } yield return (object)new WaitForSeconds(1f); Crystals.Remove(crystal); Object.Destroy((Object)(object)crystal); SaveCrystals(); File.Delete(replayPath); ReplayAPI.ReplayDeletedInternal(replayPath); ReplayFiles.ReloadReplays(); Main.instance.crystalBreakCoroutine = null; } public static IEnumerator SpinEaseInOut(Transform target, float maxSpeed, float duration, Vector3 axis) { //IL_001c: Unknown result type (might be due to invalid IL or missing references) //IL_001d: Unknown result type (might be due to invalid IL or missing references) float elapsed = 0f; while (elapsed < duration) { float t = elapsed / duration; float eased = 6f * t * (1f - t); float spinSpeed = maxSpeed * eased; target.Rotate(axis, spinSpeed * Time.deltaTime, (Space)1); elapsed += Time.deltaTime; yield return null; } } public static IEnumerator CrystalSpawnAnimation(Crystal crystal) { crystal.isAnimation = true; ((Component)crystal).transform.localScale = Vector3.zero; ((Component)crystal).transform.position = ((Component)Main.instance.replayTable).transform.position; ((Component)crystalizeVFX).transform.localPosition = new Vector3(0f, 0f, 0.3045f); crystal.BaseColor = new Color32((byte)50, (byte)50, (byte)50, byte.MaxValue); crystal.ApplyVisuals(); Singleton<AudioManager>.instance.Play(ReplayCache.SFX["Call_MoveSelector_Unlock"], ((Component)crystal).transform.position, false); MelonCoroutines.Start(Utilities.LerpValue((Func<Vector3>)(() => ((Component)crystal).transform.position), (Action<Vector3>)delegate(Vector3 v) { //IL_000b: Unknown result type (might be due to invalid IL or missing references) ((Component)crystal).transform.position = v; }, (Func<Vector3, Vector3, float, Vector3>)Vector3.Lerp, ((Component)Main.instance.replayTable).transform.position + new Vector3(0f, 0.3045f, 0f), 1.5f, (Func<float, float>)Utilities.EaseInOut, (Action)null, 0f, (Func<bool>)null)); yield return Utilities.LerpValue((Func<Vector3>)(() => ((Component)crystal).transform.localScale), (Action<Vector3>)delegate(Vector3 v) { //IL_000b: Unknown result type (might be due to invalid IL or missing references) ((Component)crystal).transform.localScale = v; }, (Func<Vector3, Vector3, float, Vector3>)Vector3.Lerp, Vector3.one * 50f, 1.5f, (Func<float, float>)Utilities.EaseInOut, (Action)null, 0f, (Func<bool>)null); yield return (object)new WaitForSeconds(1f); Singleton<AudioManager>.instance.Play(ReplayCache.SFX["Call_Shiftstone_Use"], ((Component)crystal).transform.position, false); crystalizeVFX.Play(); MelonCoroutines.Start(Utilities.LerpValue((Func<Color32>)(() => crystal.BaseColor), (Action<Color32>)delegate(Color32 v) { //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_0008: Unknown result type (might be due to invalid IL or missing references) crystal.BaseColor = v; crystal.ApplyVisuals(); }, (Func<Color32, Color32, float, Color32>)Color32.Lerp, (!string.IsNullOrEmpty(lastReplayColor.path) && crystal.ReplayPath == lastReplayColor.path) ? lastReplayColor.color : Utilities.RandomColor(), 0.05f, (Func<float, float>)Utilities.EaseInOut, (Action)null, 0f, (Func<bool>)null)); yield return Utilities.LerpValue((Func<Quaternion>)(() => ((Component)crystal).transform.localRotation), (Action<Quaternion>)delegate(Quaternion v) { //IL_000b: Unknown result type (might be due to invalid IL or missing references) ((Component)crystal).transform.localRotation = v; }, (Func<Quaternion, Quaternion, float, Quaternion>)Quaternion.Slerp, Quaternion.Euler(290f, 0f, 0f), 0.05f, (Func<float, float>)null, (Action)null, 0f, (Func<bool>)null); yield return Utilities.LerpValue((Func<Quaternion>)(() => ((Component)crystal).transform.localRotation), (Action<Quaternion>)delegate(Quaternion v) { //IL_000b: Unknown result type (might be due to invalid IL or missing references) ((Component)crystal).transform.localRotation = v; }, (Func<Quaternion, Quaternion, float, Quaternion>)Quaternion.Slerp, Quaternion.Euler(270f, 0f, 0f), 0.7f, (Func<float, float>)Utilities.EaseInOut, (Action)null, 0f, (Func<bool>)null); crystal.isAnimation = false; } } public class ReplayFormatting { public static string GetReplayName(ReplayInfo replayInfo, bool isClip = false) { string friendlySceneName = Utilities.GetFriendlySceneName(replayInfo.Header.Scene); string customMap = replayInfo.Header.CustomMap; Player localPlayer = Singleton<PlayerManager>.instance.localPlayer; string value = Utilities.CleanName(localPlayer.Data.GeneralData.PublicUsername); string text = replayInfo.Header.Players.FirstOrDefault((PlayerInfo p) => p.MasterId != localPlayer.Data.GeneralData.PlayFabMasterId)?.Name; string value2 = ((!string.IsNullOrEmpty(text)) ? Utilities.CleanName(text) : "Unknown"); string value3 = (isClip ? "Clip_" : ""); DateTime.TryParse(replayInfo.Header.Date, out var result); string value4 = result.ToString("yyyy-MM-dd_hh-mm-ss"); string text2 = $"Replay_{value3}{value}-vs-{value2}_on_{friendlySceneName}_{value4}.replay"; if (!string.IsNullOrEmpty(customMap)) { return $"Replay_{value3}{value}-vs-{value2}_on_{customMap}_{value4}.replay"; } if (1 == 0) { } string result2; switch (friendlySceneName) { case "Ring": case "Pit": result2 = text2; break; case "Park": result2 = $"Replay_{value3}{friendlySceneName}_{replayInfo.Header.Players.Length}P_{value}_{value4}.replay"; break; default: result2 = $"Replay_{value3}{friendlySceneName}_{value}_{value4}.replay"; break; } if (1 == 0) { } return result2; } public static string GetReplayDisplayName(string path, ReplaySerializer.ReplayHeader header, string alternativeName = null, bool showTitle = true) { string text = alternativeName ?? Path.GetFileNameWithoutExtension(path); if (header == null) { return text; } string pattern = ((text.StartsWith("Replay", StringComparison.OrdinalIgnoreCase) && showTitle) ? header.Title : text); return FormatReplayString(pattern, header); } public static string FormatReplayString(string pattern, ReplaySerializer.ReplayHeader header) { string mapName = GetMapName(null, header); DateTime dateTime = (string.IsNullOrEmpty(header.Date) ? DateTime.MinValue : DateTime.Parse(header.Date, CultureInfo.InvariantCulture)); TimeSpan timeSpan = TimeSpan.FromSeconds(header.Duration); string text = ((timeSpan.TotalHours >= 1.0) ? $"{(int)timeSpan.TotalHours}:{timeSpan.Minutes:D2}:{timeSpan.Seconds:D2}" : $"{(int)timeSpan.TotalMinutes}:{timeSpan.Seconds:D2}"); Dictionary<string, object> obj = new Dictionary<string, object>(StringComparer.OrdinalIgnoreCase) { ["Host"] = "<#FFF>" + (header.Players?.FirstOrDefault((PlayerInfo p) => p?.WasHost ?? false)?.Name ?? "Unknown") + "<#FFF>", ["Client"] = "<#FFF>" + (header.Players?.FirstOrDefault((PlayerInfo p) => p != null && !p.WasHost)?.Name ?? "Unknown") + "<#FFF>" }; PlayerInfo[] players = header.Players; obj["LocalPlayer"] = "<#FFF>" + (((players == null) ? null : players[0]?.Name) ?? "Unknown") + "<#FFF>"; obj["Scene"] = mapName; obj["Map"] = mapName; obj["DateTime"] = ((dateTime == DateTime.MinValue) ? "Unknown Date" : ((object)dateTime)); DefaultInterpolatedStringHandler defaultInterpolatedStringHandler = new DefaultInterpolatedStringHandler(7, 2); PlayerInfo[] players2 = header.Players; defaultInterpolatedStringHandler.AppendFormatted((players2 != null) ? players2.Length : 0); defaultInterpolatedStringHandler.AppendLiteral(" Player"); PlayerInfo[] players3 = header.Players; defaultInterpolatedStringHandler.AppendFormatted((((players3 != null) ? players3.Length : 0) == 1) ? "" : "s"); obj["PlayerCount"] = defaultInterpolatedStringHandler.ToStringAndClear(); obj["Version"] = header.Version ?? "Unknown Version"; string obj2 = header.Structures?.Length.ToString() ?? "0"; StructureInfo[] structures = header.Structures; obj["StructureCount"] = obj2 + " Structure" + ((((structures != null) ? structures.Length : 0) == 1) ? "" : "s"); obj["MarkerCount"] = header.MarkerCount; obj["AveragePing"] = header.AvgPing; obj["MinimumPing"] = header.MinPing; obj["MaximumPing"] = header.MaxPing; obj["Title"] = ((!string.IsNullOrEmpty(header.Title)) ? header.Title : "Unknown Title"); obj["Duration"] = ((header.Duration > 0f) ? text : "Unknown"); obj["FPS"] = header.TargetFPS; Dictionary<string, object> values = obj; int num = 0; while (true) { int num2 = num; PlayerInfo[] players4 = header.Players; if (num2 >= ((players4 != null) ? players4.Length : 0)) { break; } values[$"Player{num + 1}"] = GetPlayer(num); num++; } Regex regex = new Regex("\\{(\\w+)(?::([^}]+))?\\}"); return regex.Replace(pattern, delegate(Match match) { string value = match.Groups[1].Value; string text2 = (match.Groups[2].Success ? match.Groups[2].Value : null); if (value.Equals("PlayerList", StringComparison.OrdinalIgnoreCase)) { int maxNames = 3; if (!string.IsNullOrEmpty(text2) && int.TryParse(text2, out var result)) { maxNames = result; } if (header.Players != null) { return ReplayFiles.BuildPlayerLine(header.Players, maxNames); } } object value2; return values.TryGetValue(value, out value2) ? ((value2 is DateTime dateTime2 && text2 != null) ? dateTime2.ToString(text2) : value2.ToString()) : match.Value; }); string GetPlayer(int index) { return (header?.Players != null && index >= 0 && index < header.Players.Length && header.Players[index] != null) ? ("<#FFF>" + header.Players[index].Name + "<#FFF>") : ""; } } public static string GetMapName(string path = null, ReplaySerializer.ReplayHeader header = null) { if (string.IsNullOrEmpty(path) && header == null) { return null; } if (header == null) { header = ReplayArchive.GetManifest(path); } if (!string.IsNullOrEmpty(header.CustomMap)) { if (header.CustomMap.StartsWith("1|")) { return "Unknown Custom Map"; } if (header.Scene == "FlatLandSingle") { return "FlatLand"; } return header.CustomMap; } return Utilities.GetFriendlySceneName(header.Scene); } } public static class ReplayPlaybackControls { public static bool playbackControlsOpen; public static GameObject playbackControls; public static GameObject timeline; public static TextMeshPro totalDuration; public static TextMeshPro currentDuration; public static TextMeshPro playbackTitle; public static TextMeshPro playbackSpeedText; public static MeshRenderer playButtonSprite; public static Texture2D pauseSprite; public static Texture2D playSprite; public static DestroyOnPunch destroyOnPunch; public static GameObject markerPrefab; public static float smoothing = 7f; public static void Update() { //IL_004c: Unknown result type (might be due to invalid IL or missing references) //IL_0061: Unknown result type (might be due to invalid IL or missing references) //IL_0067: Unknown result type (might be due to invalid IL or missing references) //IL_0088: Unknown result type (might be due to invalid IL or missing references) //IL_008d: Unknown result type (might be due to invalid IL or missing references) //IL_008e: Unknown result type (might be due to invalid IL or missing references) //IL_0093: Unknown result type (might be due to invalid IL or missing references) //IL_00d5: Unknown result type (might be due to invalid IL or missing references) //IL_00da: Unknown result type (might be due to invalid IL or missing references) //IL_00dd: Unknown result type (might be due to invalid IL or missing references) //IL_00fc: Unknown result type (might be due to invalid IL or missing references) //IL_0101: Unknown result type (might be due to invalid IL or missing references) //IL_0105: Unknown result type (might be due to invalid IL or missing references) Transform head = Main.instance.head; if (!((Object)(object)head == (Object)null) && !((Object)(object)playbackControls == (Object)null) && Main.instance.PlaybackControlsFollow.Value) { float armSpan = Main.LocalPlayer.Data.PlayerMeasurement.ArmSpan; float num = Vector3.Distance(playbackControls.transform.position, head.position); if (!(num < armSpan)) { (Vector3 position, Quaternion rotation) targetSlabTransform = GetTargetSlabTransform(head); Vector3 item = targetSlabTransform.position; Quaternion item2 = targetSlabTransform.rotation; float num2 = Mathf.InverseLerp(armSpan, armSpan * 1.5f, num); float num3 = (1f - Mathf.Exp((0f - smoothing) * Time.deltaTime)) * num2; playbackControls.transform.position = Vector3.Lerp(playbackControls.transform.position, item, num3); playbackControls.transform.rotation = Quaternion.Slerp(playbackControls.transform.rotation, item2, num3); } } } public static (Vector3 position, Quaternion rotation) GetTargetSlabTransform(Transform head) { //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_0015: Unknown result type (might be due to invalid IL or missing references) //IL_001a: Unknown result type (might be due to invalid IL or missing references) //IL_0020: Unknown result type (might be due to invalid IL or missing references) //IL_0025: Unknown result type (might be due to invalid IL or missing references) //IL_002a: Unknown result type (might be due to invalid IL or missing references) //IL_0034: Unknown result type (might be due to invalid IL or missing references) //IL_0039: Unknown result type (might be due to invalid IL or missing references) //IL_003e: Unknown result type (might be due to invalid IL or missing references) //IL_0040: Unknown result type (might be due to invalid IL or missing references) //IL_0045: Unknown result type (might be due to invalid IL or missing references) //IL_0046: Unknown result type (might be due to invalid IL or missing references) //IL_004b: Unknown result type (might be due to invalid IL or missing references) //IL_0058: Unknown result type (might be due to invalid IL or missing references) //IL_0059: Unknown result type (might be due to invalid IL or missing references) //IL_005e: 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_0060: Unknown result type (might be due to invalid IL or missing references) Vector3 forward = head.forward; forward.y = 0f; Vector3 val = head.position + forward * 0.6f + Vector3.down * 0.1f; Vector3 val2 = head.position - val; val2.y = 0f; Quaternion item = Quaternion.LookRotation(val2); return (val, item); } public static void Open() { //IL_0045: Unknown result type (might be due to invalid IL or missing references) //IL_004a: Unknown result type (might be due to invalid IL or missing references) //IL_004b: Unknown result type (might be due to invalid IL or missing references) //IL_0050: Unknown result type (might be due to invalid IL or missing references) //IL_0079: Unknown result type (might be due to invalid IL or missing references) //IL_008a: Unknown result type (might be due to invalid IL or missing references) //IL_00f3: Unknown result type (might be due to invalid IL or missing references) if (!((Object)(object)Main.instance.head == (Object)null)) { if (playbackControlsOpen) { playbackControls.SetActive(false); } playbackControlsOpen = true; var (position, rotation) = GetTargetSlabTransform(Main.instance.head); if (Main.LocalPlayer.Controller.GetSubsystem<PlayerMovement>().IsGrounded()) { playbackControls.transform.position = position; playbackControls.transform.rotation = rotation; playbackControls.SetActive(true); ((Component)playbackControls.transform.GetChild(0)).gameObject.SetActive(true); ((Component)playbackControls.transform.GetChild(1)).gameObject.SetActive(true); Singleton<AudioManager>.instance.Play(ReplayCache.SFX["Call_Slab_Construct"], Main.instance.head.position, false); } } } public static void Close() { //IL_007a: Unknown result type (might be due to invalid IL or missing references) //IL_009f: Unknown result type (might be due to invalid IL or missing references) //IL_00ae: Unknown result type (might be due to invalid IL or missing references) //IL_00bd: Unknown result type (might be due to invalid IL or missing references) //IL_00c7: Unknown result type (might be due to invalid IL or missing references) if (playbackControlsOpen) { playbackControlsOpen = false; playbackControls.SetActive(false); ((Component)playbackControls.transform.GetChild(0)).gameObject.SetActive(false); ((Component)playbackCo
UserLibs/Concentus.dll
Decompiled a month ago
The result has been truncated due to the large size, download it to view full contents!
using System; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Numerics; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Runtime.Versioning; using System.Security; using System.Security.Permissions; using System.Text; using System.Threading; using Concentus.Celt; using Concentus.Celt.Structs; using Concentus.Common; using Concentus.Common.CPlusPlus; using Concentus.Enums; using Concentus.Native; using Concentus.Silk; using Concentus.Silk.Enums; using Concentus.Silk.Structs; using Concentus.Structs; using Microsoft.CodeAnalysis; using Microsoft.Win32.SafeHandles; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: InternalsVisibleTo("ParityTest, PublicKey=002400000480000094000000060200000024000052534131000400000100010071a2f675c04c87e64b9be6d37f5833c5285fb4ed883780cf6d61e80aee5d77950b2f06dd45bc634f53405f2a2b7b2332f4dfdcb0554ffc97b935e7343e76e733eea44346e56ac1098c12a66de71e324f2f503f9f2e32560910e2082d6943df50db42679a330e52979bd1eefbb59485d2c7420d158f6ab6d41bdf42d2172675e1")] [assembly: InternalsVisibleTo("TestOpusEncode, PublicKey=002400000480000094000000060200000024000052534131000400000100010071a2f675c04c87e64b9be6d37f5833c5285fb4ed883780cf6d61e80aee5d77950b2f06dd45bc634f53405f2a2b7b2332f4dfdcb0554ffc97b935e7343e76e733eea44346e56ac1098c12a66de71e324f2f503f9f2e32560910e2082d6943df50db42679a330e52979bd1eefbb59485d2c7420d158f6ab6d41bdf42d2172675e1")] [assembly: TargetFramework(".NETFramework,Version=v4.5.2", FrameworkDisplayName = ".NET Framework 4.5.2")] [assembly: AssemblyCompany("Logan Stromberg")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyCopyright("© Xiph.Org Foundation, Skype Limited, CSIRO, Microsoft Corp.")] [assembly: AssemblyDescription("This package is a portable C# implementation of the Opus audio compression codec (see https://opus-codec.org/ for more details). This package contains the Opus encoder, decoder, multistream codecs, repacketizer, as well as a port of the libspeexdsp resampler. It does NOT contain code to parse .ogg or .opus container files or to manage RTP packet streams. For better performance depending on your platform, see also the Concentus.Native package.")] [assembly: AssemblyFileVersion("2.2.2.0")] [assembly: AssemblyInformationalVersion("2.2.2+6c2328dc19044601e33a9c11628b8d60e1f3011c")] [assembly: AssemblyProduct("Concentus")] [assembly: AssemblyTitle("Concentus")] [assembly: AssemblyMetadata("RepositoryUrl", "https://github.com/lostromb/concentus")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("2.2.2.0")] [module: UnverifiableCode] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] internal sealed class IsReadOnlyAttribute : Attribute { } } namespace Concentus { public interface IOpusDecoder : IDisposable { OpusBandwidth Bandwidth { get; } uint FinalRange { get; } int Gain { get; set; } int LastPacketDuration { get; } int NumChannels { get; } int Pitch { get; } int SampleRate { get; } int Decode(ReadOnlySpan<byte> in_data, Span<float> out_pcm, int frame_size, bool decode_fec = false); int Decode(ReadOnlySpan<byte> in_data, Span<short> out_pcm, int frame_size, bool decode_fec = false); void ResetState(); string GetVersionString(); } public interface IOpusEncoder : IDisposable { OpusApplication Application { get; set; } int Bitrate { get; set; } int ForceChannels { get; set; } OpusBandwidth MaxBandwidth { get; set; } OpusBandwidth Bandwidth { get; set; } bool UseDTX { get; set; } int Complexity { get; set; } bool UseInbandFEC { get; set; } int PacketLossPercent { get; set; } bool UseVBR { get; set; } bool UseConstrainedVBR { get; set; } OpusSignal SignalType { get; set; } int Lookahead { get; } int SampleRate { get; } int NumChannels { get; } uint FinalRange { get; } int LSBDepth { get; set; } OpusFramesize ExpertFrameDuration { get; set; } OpusMode ForceMode { set; } bool PredictionDisabled { get; set; } int Encode(ReadOnlySpan<short> in_pcm, int frame_size, Span<byte> out_data, int max_data_bytes); int Encode(ReadOnlySpan<float> in_pcm, int frame_size, Span<byte> out_data, int max_data_bytes); void ResetState(); string GetVersionString(); } public interface IOpusMultiStreamDecoder : IDisposable { OpusBandwidth Bandwidth { get; } uint FinalRange { get; } int Gain { get; set; } int LastPacketDuration { get; } int SampleRate { get; } int NumChannels { get; } int DecodeMultistream(ReadOnlySpan<byte> data, Span<float> out_pcm, int frame_size, bool decode_fec); int DecodeMultistream(ReadOnlySpan<byte> data, Span<short> out_pcm, int frame_size, bool decode_fec); void ResetState(); string GetVersionString(); } public interface IOpusMultiStreamEncoder : IDisposable { OpusApplication Application { get; set; } OpusBandwidth Bandwidth { get; set; } int Bitrate { get; set; } int Complexity { get; set; } int NumChannels { get; } OpusFramesize ExpertFrameDuration { get; set; } uint FinalRange { get; } OpusMode ForceMode { set; } int Lookahead { get; } int LSBDepth { get; set; } OpusBandwidth MaxBandwidth { get; set; } int PacketLossPercent { get; set; } bool PredictionDisabled { get; set; } int SampleRate { get; } OpusSignal SignalType { get; set; } bool UseConstrainedVBR { get; set; } bool UseDTX { get; set; } bool UseInbandFEC { get; set; } bool UseVBR { get; set; } int EncodeMultistream(ReadOnlySpan<float> in_pcm, int frame_size, Span<byte> out_data, int max_data_bytes); int EncodeMultistream(ReadOnlySpan<short> in_pcm, int frame_size, Span<byte> out_data, int max_data_bytes); void ResetState(); string GetVersionString(); } public interface IResampler : IDisposable { int InputLatency { get; } int InputStride { get; set; } int OutputLatencySamples { get; } TimeSpan OutputLatency { get; } int OutputStride { get; set; } int Quality { get; set; } void GetRateFraction(out int ratio_num, out int ratio_den); void GetRates(out int in_rate, out int out_rate); void ResetMem(); void SkipZeroes(); void Process(int channel_index, Span<float> input, ref int in_len, Span<float> output, ref int out_len); void Process(int channel_index, Span<short> input, ref int in_len, Span<short> output, ref int out_len); void ProcessInterleaved(Span<float> input, ref int in_len, Span<float> output, ref int out_len); void ProcessInterleaved(Span<short> input, ref int in_len, Span<short> output, ref int out_len); void SetRateFraction(int ratio_num, int ratio_den, int in_rate, int out_rate); void SetRates(int in_rate, int out_rate); } public static class OpusCodecFactory { private static readonly object _mutex = new object(); private static bool _nativeLibInitialized = false; private static bool _isNativeLibAvailable = false; private static bool _userAllowNativeLib = true; public static bool AttemptToUseNativeLibrary { get { return _userAllowNativeLib; } set { _userAllowNativeLib = value; } } public static IOpusEncoder CreateEncoder(int sampleRate, int numChannels, OpusApplication application = OpusApplication.OPUS_APPLICATION_AUDIO, TextWriter messageLogger = null) { if (_userAllowNativeLib && NativeLibraryAvailable(messageLogger)) { return NativeOpusEncoder.Create(sampleRate, numChannels, application); } return new OpusEncoder(sampleRate, numChannels, application); } public static IOpusDecoder CreateDecoder(int sampleRate, int numChannels, TextWriter messageLogger = null) { if (_userAllowNativeLib && NativeLibraryAvailable(messageLogger)) { return NativeOpusDecoder.Create(sampleRate, numChannels); } return new OpusDecoder(sampleRate, numChannels); } public static IOpusMultiStreamEncoder CreateMultiStreamEncoder(int sampleRate, int numChannels, int mappingFamily, out int streams, out int coupledStreams, byte[] mapping, OpusApplication application, TextWriter messageLogger = null) { if (_userAllowNativeLib && NativeLibraryAvailable(messageLogger)) { return NativeOpusMultistreamEncoder.Create(sampleRate, numChannels, mappingFamily, out streams, out coupledStreams, mapping, application); } return OpusMSEncoder.CreateSurround(sampleRate, numChannels, mappingFamily, out streams, out coupledStreams, mapping, application); } public static IOpusMultiStreamDecoder CreateMultiStreamDecoder(int sampleRate, int numChannels, int streams, int coupledStreams, byte[] mapping, TextWriter messageLogger = null) { if (_userAllowNativeLib && NativeLibraryAvailable(messageLogger)) { return NativeOpusMultistreamDecoder.Create(sampleRate, numChannels, streams, coupledStreams, mapping); } return new OpusMSDecoder(sampleRate, numChannels, streams, coupledStreams, mapping); } private static bool NativeLibraryAvailable(TextWriter messageLogger) { lock (_mutex) { if (!_nativeLibInitialized) { try { _isNativeLibAvailable = NativeOpus.Initialize(messageLogger); messageLogger?.WriteLine($"Is native opus available? {_isNativeLibAvailable}"); } catch (Exception ex) { messageLogger?.WriteLine(ex.ToString()); } _nativeLibInitialized = true; } return _isNativeLibAvailable; } } } internal static class Analysis { private const double M_PI = 3.141592653; private const float cA = 0.43157974f; private const float cB = 0.678484f; private const float cC = 0.08595542f; private const float cE = (float)Math.PI / 2f; private const int NB_TONAL_SKIP_BANDS = 9; internal static float fast_atan2f(float y, float x) { if (Inlines.ABS16(x) + Inlines.ABS16(y) < 1E-09f) { x *= 1E+12f; y *= 1E+12f; } float num = x * x; float num2 = y * y; if (num < num2) { float num3 = (num2 + 0.678484f * num) * (num2 + 0.08595542f * num); if (num3 != 0f) { return (0f - x) * y * (num2 + 0.43157974f * num) / num3 + ((y < 0f) ? (-(float)Math.PI / 2f) : ((float)Math.PI / 2f)); } if (!(y < 0f)) { return (float)Math.PI / 2f; } return -(float)Math.PI / 2f; } float num4 = (num + 0.678484f * num2) * (num + 0.08595542f * num2); if (num4 != 0f) { return x * y * (num + 0.43157974f * num2) / num4 + ((y < 0f) ? (-(float)Math.PI / 2f) : ((float)Math.PI / 2f)) - ((x * y < 0f) ? (-(float)Math.PI / 2f) : ((float)Math.PI / 2f)); } return ((y < 0f) ? (-(float)Math.PI / 2f) : ((float)Math.PI / 2f)) - ((x * y < 0f) ? (-(float)Math.PI / 2f) : ((float)Math.PI / 2f)); } internal static void tonality_analysis_init(TonalityAnalysisState tonal) { tonal.Reset(); } internal static void tonality_get_info(TonalityAnalysisState tonal, AnalysisInfo info_out, int len) { int num = tonal.read_pos; int num2 = tonal.write_pos - tonal.read_pos; if (num2 < 0) { num2 += 200; } if (len > 480 && num != tonal.write_pos) { num++; if (num == 200) { num = 0; } } if (num == tonal.write_pos) { num--; } if (num < 0) { num = 199; } info_out.Assign(tonal.info[num]); tonal.read_subframe += len / 120; while (tonal.read_subframe >= 4) { tonal.read_subframe -= 4; tonal.read_pos++; } if (tonal.read_pos >= 200) { tonal.read_pos -= 200; } num2 = Inlines.IMAX(num2 - 10, 0); float num3 = 0f; int i; for (i = 0; i < 200 - num2; i++) { num3 += tonal.pmusic[i]; } for (; i < 200; i++) { num3 += tonal.pspeech[i]; } num3 = num3 * tonal.music_confidence + (1f - num3) * tonal.speech_confidence; info_out.music_prob = num3; } internal static void tonality_analysis<T>(TonalityAnalysisState tonal, CeltMode celt_mode, ReadOnlySpan<T> x, int len, int offset, int c1, int c2, int C, int lsb_depth, Downmix.downmix_func<T> downmix) { int num = 480; int num2 = 240; float[] angle = tonal.angle; float[] d_angle = tonal.d_angle; float[] d2_angle = tonal.d2_angle; float[] array = new float[18]; float[] array2 = new float[18]; float[] array3 = new float[8]; float[] array4 = new float[25]; float num3 = 97.40909f; float num4 = 0f; float[] array5 = new float[2]; int num5 = 0; float num6 = 0f; tonal.last_transition++; float num7 = 1f / (float)Inlines.IMIN(20, 1 + tonal.count); float num8 = 1f / (float)Inlines.IMIN(50, 1 + tonal.count); float num9 = 1f / (float)Inlines.IMIN(1000, 1 + tonal.count); if (tonal.count < 4) { tonal.music_prob = 0.5f; } FFTState st = celt_mode.mdct.kfft[0]; if (tonal.count == 0) { tonal.mem_fill = 240; } downmix(x, tonal.inmem, tonal.mem_fill, Inlines.IMIN(len, 720 - tonal.mem_fill), offset, c1, c2, C); if (tonal.mem_fill + len < 720) { tonal.mem_fill += len; return; } AnalysisInfo analysisInfo = tonal.info[tonal.write_pos++]; if (tonal.write_pos >= 200) { tonal.write_pos -= 200; } int[] array6 = new int[960]; int[] array7 = new int[960]; float[] array8 = new float[240]; float[] array9 = new float[240]; for (int i = 0; i < num2; i++) { float num10 = Tables.analysis_window[i]; array6[2 * i] = (int)(num10 * (float)tonal.inmem[i]); array6[2 * i + 1] = (int)(num10 * (float)tonal.inmem[num2 + i]); array6[2 * (num - i - 1)] = (int)(num10 * (float)tonal.inmem[num - i - 1]); array6[2 * (num - i - 1) + 1] = (int)(num10 * (float)tonal.inmem[num + num2 - i - 1]); } Arrays.MemMoveInt(tonal.inmem, 480, 0, 240); int num11 = len - (720 - tonal.mem_fill); downmix(x, tonal.inmem, 240, num11, offset + 720 - tonal.mem_fill, c1, c2, C); tonal.mem_fill = 240 + num11; KissFFT.opus_fft(st, array6, array7); for (int i = 1; i < num2; i++) { float x2 = (float)array7[2 * i] + (float)array7[2 * (num - i)]; float y = (float)array7[2 * i + 1] - (float)array7[2 * (num - i) + 1]; float x3 = (float)array7[2 * i + 1] + (float)array7[2 * (num - i) + 1]; float y2 = (float)array7[2 * (num - i)] - (float)array7[2 * i]; float num12 = 1f / (2f * (float)Math.PI) * fast_atan2f(y, x2); float num13 = num12 - angle[i]; float num14 = num13 - d_angle[i]; float num15 = 1f / (2f * (float)Math.PI) * fast_atan2f(y2, x3); float num16 = num15 - num12; float num17 = num16 - num13; float num18 = num14 - (float)Math.Floor(0.5f + num14); array9[i] = Inlines.ABS16(num18); num18 *= num18; num18 *= num18; float num19 = num17 - (float)Math.Floor(0.5f + num17); array9[i] += Inlines.ABS16(num19); num19 *= num19; num19 *= num19; float num20 = 0.25f * (d2_angle[i] + 2f * num18 + num19); array8[i] = 1f / (1f + 640f * num3 * num20) - 0.015f; angle[i] = num15; d_angle[i] = num16; d2_angle[i] = num19; } float num21 = 0f; float num22 = 0f; analysisInfo.activity = 0f; float num23 = 0f; float num24 = 0f; if (tonal.count == 0) { for (int j = 0; j < 18; j++) { tonal.lowE[j] = 1E+10f; tonal.highE[j] = -1E+10f; } } float num25 = 0f; float num26 = 0f; for (int j = 0; j < 18; j++) { float num27 = 0f; float num28 = 0f; float num29 = 0f; for (int i = Tables.tbands[j]; i < Tables.tbands[j + 1]; i++) { float num30 = (float)array7[2 * i] * (float)array7[2 * i] + (float)array7[2 * (num - i)] * (float)array7[2 * (num - i)] + (float)array7[2 * i + 1] * (float)array7[2 * i + 1] + (float)array7[2 * (num - i) + 1] * (float)array7[2 * (num - i) + 1]; num30 *= 5.55E-17f; num27 += num30; num28 += num30 * array8[i]; num29 += num30 * 2f * (0.5f - array9[i]); } tonal.E[tonal.E_count][j] = num27; num23 += num29 / (1E-15f + num27); num26 += (float)Math.Sqrt(num27 + 1E-10f); array2[j] = (float)Math.Log(num27 + 1E-10f); tonal.lowE[j] = Inlines.MIN32(array2[j], tonal.lowE[j] + 0.01f); tonal.highE[j] = Inlines.MAX32(array2[j], tonal.highE[j] - 0.1f); if (tonal.highE[j] < tonal.lowE[j] + 1f) { tonal.highE[j] += 0.5f; tonal.lowE[j] -= 0.5f; } num25 += (array2[j] - tonal.lowE[j]) / (1E-15f + tonal.highE[j] - tonal.lowE[j]); float num31; float num32 = (num31 = 0f); for (int i = 0; i < 8; i++) { num32 += (float)Math.Sqrt(tonal.E[i][j]); num31 += tonal.E[i][j]; } float num33 = Inlines.MIN16(0.99f, num32 / (float)Math.Sqrt(1E-15 + (double)(8f * num31))); num33 *= num33; num33 *= num33; num24 += num33; array[j] = Inlines.MAX16(num28 / (1E-15f + num27), num33 * tonal.prev_band_tonality[j]); num21 += array[j]; if (j >= 9) { num21 -= array[j - 18 + 9]; } num22 = Inlines.MAX16(num22, (1f + 0.03f * (float)(j - 18)) * num21); num4 += array[j] * (float)(j - 8); tonal.prev_band_tonality[j] = array[j]; } float num34 = 0f; num5 = 0; num6 = 0f; float num35 = 0.00057f / (float)(1 << Inlines.IMAX(0, lsb_depth - 8)); num35 *= 134217730f; num35 *= num35; for (int j = 0; j < 21; j++) { float num36 = 0f; int num37 = Tables.extra_bands[j]; int num38 = Tables.extra_bands[j + 1]; for (int i = num37; i < num38; i++) { float num39 = (float)array7[2 * i] * (float)array7[2 * i] + (float)array7[2 * (num - i)] * (float)array7[2 * (num - i)] + (float)array7[2 * i + 1] * (float)array7[2 * i + 1] + (float)array7[2 * (num - i) + 1] * (float)array7[2 * (num - i) + 1]; num36 += num39; } num6 = Inlines.MAX32(num6, num36); tonal.meanE[j] = Inlines.MAX32((1f - num9) * tonal.meanE[j], num36); num36 = Inlines.MAX32(num36, tonal.meanE[j]); num34 = Inlines.MAX32(0.05f * num34, num36); if ((double)num36 > 0.1 * (double)num34 && num36 * 1E+09f > num6 && num36 > num35 * (float)(num38 - num37)) { num5 = j; } } if (tonal.count <= 2) { num5 = 20; } num26 = 20f * (float)Math.Log10(num26); tonal.Etracker = Inlines.MAX32(tonal.Etracker - 0.03f, num26); tonal.lowECount *= 1f - num8; if (num26 < tonal.Etracker - 30f) { tonal.lowECount += num8; } for (int i = 0; i < 8; i++) { float num40 = 0f; for (int j = 0; j < 16; j++) { num40 += Tables.dct_table[i * 16 + j] * array2[j]; } array3[i] = num40; } num24 /= 18f; num25 /= 18f; if (tonal.count < 10) { num25 = 0.5f; } num23 /= 18f; analysisInfo.activity = num23 + (1f - num23) * num25; num21 = num22 / 9f; num21 = (tonal.prev_tonality = Inlines.MAX16(num21, tonal.prev_tonality * 0.8f)); num4 /= 64f; analysisInfo.tonality_slope = num4; tonal.E_count = (tonal.E_count + 1) % 8; tonal.count++; analysisInfo.tonality = num21; for (int i = 0; i < 4; i++) { array4[i] = -0.12299f * (array3[i] + tonal.mem[i + 24]) + 0.49195f * (tonal.mem[i] + tonal.mem[i + 16]) + 0.69693f * tonal.mem[i + 8] - 1.4349f * tonal.cmean[i]; } for (int i = 0; i < 4; i++) { tonal.cmean[i] = (1f - num7) * tonal.cmean[i] + num7 * array3[i]; } for (int i = 0; i < 4; i++) { array4[4 + i] = 0.63246f * (array3[i] - tonal.mem[i + 24]) + 0.31623f * (tonal.mem[i] - tonal.mem[i + 16]); } for (int i = 0; i < 3; i++) { array4[8 + i] = 0.53452f * (array3[i] + tonal.mem[i + 24]) - 0.26726f * (tonal.mem[i] + tonal.mem[i + 16]) - 0.53452f * tonal.mem[i + 8]; } if (tonal.count > 5) { for (int i = 0; i < 9; i++) { tonal.std[i] = (1f - num7) * tonal.std[i] + num7 * array4[i] * array4[i]; } } for (int i = 0; i < 8; i++) { tonal.mem[i + 24] = tonal.mem[i + 16]; tonal.mem[i + 16] = tonal.mem[i + 8]; tonal.mem[i + 8] = tonal.mem[i]; tonal.mem[i] = array3[i]; } for (int i = 0; i < 9; i++) { array4[11 + i] = (float)Math.Sqrt(tonal.std[i]); } array4[20] = analysisInfo.tonality; array4[21] = analysisInfo.activity; array4[22] = num24; array4[23] = analysisInfo.tonality_slope; array4[24] = tonal.lowECount; MultiLayerPerceptron.mlp_process(Tables.net, array4, array5); array5[0] = 0.5f * (array5[0] + 1f); array5[0] = 0.01f + 1.21f * array5[0] * array5[0] - 0.23f * (float)Math.Pow(array5[0], 10.0); array5[1] = 0.5f * array5[1] + 0.5f; array5[0] = array5[1] * array5[0] + (1f - array5[1]) * 0.5f; float num41 = 5E-05f * array5[1]; float num42 = 0.05f; float num43 = Inlines.MAX16(0.05f, Inlines.MIN16(0.95f, array5[0])); float num44 = Inlines.MAX16(0.05f, Inlines.MIN16(0.95f, tonal.music_prob)); num42 = 0.01f + 0.05f * Inlines.ABS16(num43 - num44) / (num43 * (1f - num44) + num44 * (1f - num43)); float num45 = (1f - tonal.music_prob) * (1f - num41) + tonal.music_prob * num41; float num46 = tonal.music_prob * (1f - num41) + (1f - tonal.music_prob) * num41; num45 *= (float)Math.Pow(1f - array5[0], num42); num46 *= (float)Math.Pow(array5[0], num42); tonal.music_prob = num46 / (num45 + num46); analysisInfo.music_prob = tonal.music_prob; float num47 = 1E-20f; float num48 = (float)Math.Pow(1f - array5[0], num42); float num49 = (float)Math.Pow(array5[0], num42); if (tonal.count == 1) { tonal.pspeech[0] = 0.5f; tonal.pmusic[0] = 0.5f; } float num50 = tonal.pspeech[0] + tonal.pspeech[1]; float num51 = tonal.pmusic[0] + tonal.pmusic[1]; tonal.pspeech[0] = num50 * (1f - num41) * num48; tonal.pmusic[0] = num51 * (1f - num41) * num49; for (int i = 1; i < 199; i++) { tonal.pspeech[i] = tonal.pspeech[i + 1] * num48; tonal.pmusic[i] = tonal.pmusic[i + 1] * num49; } tonal.pspeech[199] = num51 * num41 * num48; tonal.pmusic[199] = num50 * num41 * num49; for (int i = 0; i < 200; i++) { num47 += tonal.pspeech[i] + tonal.pmusic[i]; } num47 = 1f / num47; for (int i = 0; i < 200; i++) { tonal.pspeech[i] *= num47; tonal.pmusic[i] *= num47; } num47 = tonal.pmusic[0]; for (int i = 1; i < 200; i++) { num47 += tonal.pspeech[i]; } if ((double)array5[1] > 0.75) { if ((double)tonal.music_prob > 0.9) { float num52 = 1f / (float)(++tonal.music_confidence_count); tonal.music_confidence_count = Inlines.IMIN(tonal.music_confidence_count, 500); tonal.music_confidence += num52 * Inlines.MAX16(-0.2f, array5[0] - tonal.music_confidence); } if ((double)tonal.music_prob < 0.1) { float num53 = 1f / (float)(++tonal.speech_confidence_count); tonal.speech_confidence_count = Inlines.IMIN(tonal.speech_confidence_count, 500); tonal.speech_confidence += num53 * Inlines.MIN16(0.2f, array5[0] - tonal.speech_confidence); } } else { if (tonal.music_confidence_count == 0) { tonal.music_confidence = 0.9f; } if (tonal.speech_confidence_count == 0) { tonal.speech_confidence = 0.1f; } } if (tonal.last_music != ((tonal.music_prob > 0.5f) ? 1 : 0)) { tonal.last_transition = 0; } tonal.last_music = ((tonal.music_prob > 0.5f) ? 1 : 0); analysisInfo.bandwidth = num5; analysisInfo.noisiness = num23; analysisInfo.valid = 1; } internal static void run_analysis<T>(TonalityAnalysisState analysis, CeltMode celt_mode, ReadOnlySpan<T> analysis_pcm, int analysis_frame_size, int frame_size, int c1, int c2, int C, int Fs, int lsb_depth, Downmix.downmix_func<T> downmix, AnalysisInfo analysis_info) { if (!analysis_pcm.IsEmpty) { analysis_frame_size = Inlines.IMIN(195 * Fs / 100, analysis_frame_size); int num = analysis_frame_size - analysis.analysis_offset; int num2 = analysis.analysis_offset; do { tonality_analysis(analysis, celt_mode, analysis_pcm, Inlines.IMIN(480, num), num2, c1, c2, C, lsb_depth, downmix); num2 += 480; num -= 480; } while (num > 0); analysis.analysis_offset = analysis_frame_size; analysis.analysis_offset -= frame_size; } analysis_info.valid = 0; tonality_get_info(analysis, analysis_info, frame_size); } } internal static class CodecHelpers { private const int MAX_DYNAMIC_FRAMESIZE = 24; internal static byte gen_toc(OpusMode mode, int framerate, OpusBandwidth bandwidth, int channels) { int num = 0; while (framerate < 400) { framerate <<= 1; num++; } byte b; switch (mode) { case OpusMode.MODE_SILK_ONLY: b = (byte)((int)(bandwidth - 1101) << 5); b |= (byte)(num - 2 << 3); break; case OpusMode.MODE_CELT_ONLY: { int num2 = (int)(bandwidth - 1102); if (num2 < 0) { num2 = 0; } b = 128; b |= (byte)(num2 << 5); b |= (byte)(num << 3); break; } default: b = 96; b |= (byte)((int)(bandwidth - 1104) << 4); b |= (byte)(num - 2 << 3); break; } return (byte)(b | (byte)(((channels == 2) ? 1u : 0u) << 2)); } internal static void hp_cutoff(ReadOnlySpan<short> input, int input_ptr, int cutoff_Hz, Span<short> output, int output_ptr, int[] hp_mem, int len, int channels, int Fs) { int[] array = new int[3]; int[] array2 = new int[2]; int num = Inlines.silk_DIV32_16(Inlines.silk_SMULBB(2471, cutoff_Hz), Fs / 1000); int num2 = (array[0] = 268435456 - Inlines.silk_MUL(471, num)); array[1] = Inlines.silk_LSHIFT(-num2, 1); array[2] = num2; int num3 = Inlines.silk_RSHIFT(num2, 6); array2[0] = Inlines.silk_SMULWW(num3, Inlines.silk_SMULWW(num, num) - 8388608); array2[1] = Inlines.silk_SMULWW(num3, num3); Filters.silk_biquad_alt(input, input_ptr, array, array2, hp_mem, 0, output, output_ptr, len, channels); if (channels == 2) { Filters.silk_biquad_alt(input, input_ptr + 1, array, array2, hp_mem, 2, output, output_ptr + 1, len, channels); } } internal static void dc_reject(ReadOnlySpan<short> input, int input_ptr, int cutoff_Hz, Span<short> output, int output_ptr, int[] hp_mem, int len, int channels, int Fs) { int shift = Inlines.celt_ilog2(Fs / (cutoff_Hz * 3)); for (int i = 0; i < channels; i++) { for (int j = 0; j < len; j++) { int num = Inlines.SHL32(Inlines.EXTEND32(input[channels * j + i + input_ptr]), 15); int num2 = num - hp_mem[2 * i]; hp_mem[2 * i] += Inlines.PSHR32(num - hp_mem[2 * i], shift); int a = num2 - hp_mem[2 * i + 1]; hp_mem[2 * i + 1] += Inlines.PSHR32(num2 - hp_mem[2 * i + 1], shift); output[channels * j + i + output_ptr] = Inlines.EXTRACT16(Inlines.SATURATE(Inlines.PSHR32(a, 15), 32767)); } } } internal static void stereo_fade(short[] pcm_buf, int g1, int g2, int overlap48, int frame_size, int channels, int[] window, int Fs) { int num = 48000 / Fs; int num2 = overlap48 / num; g1 = 32767 - g1; g2 = 32767 - g2; int i; for (i = 0; i < num2; i++) { int num3 = Inlines.MULT16_16_Q15(window[i * num], window[i * num]); int a = Inlines.SHR32(Inlines.MAC16_16(Inlines.MULT16_16(num3, g2), 32767 - num3, g1), 15); int b = Inlines.EXTRACT16(Inlines.HALF32(pcm_buf[i * channels] - pcm_buf[i * channels + 1])); b = Inlines.MULT16_16_Q15(a, b); pcm_buf[i * channels] = (short)(pcm_buf[i * channels] - b); pcm_buf[i * channels + 1] = (short)(pcm_buf[i * channels + 1] + b); } for (; i < frame_size; i++) { int b2 = Inlines.EXTRACT16(Inlines.HALF32(pcm_buf[i * channels] - pcm_buf[i * channels + 1])); b2 = Inlines.MULT16_16_Q15(g2, b2); pcm_buf[i * channels] = (short)(pcm_buf[i * channels] - b2); pcm_buf[i * channels + 1] = (short)(pcm_buf[i * channels + 1] + b2); } } internal static void gain_fade(short[] buffer, int buf_ptr, int g1, int g2, int overlap48, int frame_size, int channels, int[] window, int Fs) { int num = 48000 / Fs; int num2 = overlap48 / num; if (channels == 1) { for (int i = 0; i < num2; i++) { int num3 = Inlines.MULT16_16_Q15(window[i * num], window[i * num]); int a = Inlines.SHR32(Inlines.MAC16_16(Inlines.MULT16_16(num3, g2), 32767 - num3, g1), 15); buffer[buf_ptr + i] = (short)Inlines.MULT16_16_Q15(a, buffer[buf_ptr + i]); } } else { for (int i = 0; i < num2; i++) { int num4 = Inlines.MULT16_16_Q15(window[i * num], window[i * num]); int a2 = Inlines.SHR32(Inlines.MAC16_16(Inlines.MULT16_16(num4, g2), 32767 - num4, g1), 15); buffer[buf_ptr + i * 2] = (short)Inlines.MULT16_16_Q15(a2, buffer[buf_ptr + i * 2]); buffer[buf_ptr + i * 2 + 1] = (short)Inlines.MULT16_16_Q15(a2, buffer[buf_ptr + i * 2 + 1]); } } int num5 = 0; do { for (int i = num2; i < frame_size; i++) { buffer[buf_ptr + i * channels + num5] = (short)Inlines.MULT16_16_Q15(g2, buffer[buf_ptr + i * channels + num5]); } } while (++num5 < channels); } internal static float transient_boost(Span<float> E, int E_ptr, float[] E_1, int LM, int maxM) { float num = 0f; float num2 = 0f; int num3 = Inlines.IMIN(maxM, (1 << LM) + 1); for (int i = E_ptr; i < num3 + E_ptr; i++) { num += E[i]; num2 += E_1[i]; } float num4 = num * num2 / (float)(num3 * num3); return Inlines.MIN16(1f, (float)Math.Sqrt(Inlines.MAX16(0f, 0.05f * (num4 - 2f)))); } internal static int transient_viterbi(float[] E, float[] E_1, int N, int frame_cost, int rate) { float[][] array = Arrays.InitTwoDimensionalArray<float>(24, 16); int[][] array2 = Arrays.InitTwoDimensionalArray<int>(24, 16); float num = ((rate < 80) ? 0f : ((rate <= 160) ? (((float)rate - 80f) / 80f) : 1f)); for (int i = 0; i < 16; i++) { array2[0][i] = -1; array[0][i] = 1E+10f; } for (int i = 0; i < 4; i++) { array[0][1 << i] = (float)(frame_cost + rate * (1 << i)) * (1f + num * transient_boost(E, 0, E_1, i, N + 1)); array2[0][1 << i] = i; } for (int i = 1; i < N; i++) { for (int j = 2; j < 16; j++) { array[i][j] = array[i - 1][j - 1]; array2[i][j] = j - 1; } for (int j = 0; j < 4; j++) { array2[i][1 << j] = 1; float num2 = array[i - 1][1]; for (int k = 1; k < 4; k++) { float num3 = array[i - 1][(1 << k + 1) - 1]; if (num3 < num2) { array2[i][1 << j] = (1 << k + 1) - 1; num2 = num3; } } float num4 = (float)(frame_cost + rate * (1 << j)) * (1f + num * transient_boost(E, i, E_1, j, N - i + 1)); array[i][1 << j] = num2; if (N - i < 1 << j) { array[i][1 << j] += num4 * (float)(N - i) / (float)(1 << j); } else { array[i][1 << j] += num4; } } } int num5 = 1; float num6 = array[N - 1][1]; for (int i = 2; i < 16; i++) { if (array[N - 1][i] < num6) { num6 = array[N - 1][i]; num5 = i; } } for (int i = N - 1; i >= 0; i--) { num5 = array2[i][num5]; } return num5; } internal static int optimize_framesize<T>(ReadOnlySpan<T> x, int len, int C, int Fs, int bitrate, int tonality, float[] mem, int buffering, Downmix.downmix_func<T> downmix) { float[] array = new float[28]; float[] array2 = new float[27]; int num = 0; int num2 = Fs / 400; int[] array3 = new int[num2]; array[0] = mem[0]; array2[0] = 1f / (1f + mem[0]); int num3; int num4; if (buffering != 0) { num3 = 2 * num2 - buffering; len -= num3; array[1] = mem[1]; array2[1] = 1f / (1f + mem[1]); array[2] = mem[2]; array2[2] = 1f / (1f + mem[2]); num4 = 3; } else { num4 = 1; num3 = 0; } int num5 = Inlines.IMIN(len / num2, 24); int num6 = 0; int i; for (i = 0; i < num5; i++) { float num7 = 1f; downmix(x, array3, 0, num2, i * num2 + num3, 0, -2, C); if (i == 0) { num6 = array3[0]; } for (int j = 0; j < num2; j++) { int num8 = array3[j]; num7 += (float)(num8 - num6) * (float)(num8 - num6); num6 = num8; } array[i + num4] = num7; array2[i + num4] = 1f / num7; } array[i + num4] = array[i + num4 - 1]; if (buffering != 0) { num5 = Inlines.IMIN(24, num5 + 2); } num = transient_viterbi(array, array2, num5, (int)((1f + 0.5f * (float)tonality) * (float)(60 * C + 40)), bitrate / 400); mem[0] = array[1 << num]; if (buffering != 0) { mem[1] = array[(1 << num) + 1]; mem[2] = array[(1 << num) + 2]; } return num; } internal static int frame_size_select(int frame_size, OpusFramesize variable_duration, int Fs) { if (frame_size < Fs / 400) { return -1; } int num; switch (variable_duration) { case OpusFramesize.OPUS_FRAMESIZE_ARG: num = frame_size; break; case OpusFramesize.OPUS_FRAMESIZE_VARIABLE: num = Fs / 50; break; case OpusFramesize.OPUS_FRAMESIZE_2_5_MS: case OpusFramesize.OPUS_FRAMESIZE_5_MS: case OpusFramesize.OPUS_FRAMESIZE_10_MS: case OpusFramesize.OPUS_FRAMESIZE_20_MS: case OpusFramesize.OPUS_FRAMESIZE_40_MS: case OpusFramesize.OPUS_FRAMESIZE_60_MS: num = Inlines.IMIN(3 * Fs / 50, Fs / 400 << (int)(variable_duration - 5001)); break; default: return -1; } if (num > frame_size) { return -1; } if (400 * num != Fs && 200 * num != Fs && 100 * num != Fs && 50 * num != Fs && 25 * num != Fs && 50 * num != 3 * Fs) { return -1; } return num; } internal static int compute_frame_size<T>(ReadOnlySpan<T> analysis_pcm, int frame_size, OpusFramesize variable_duration, int C, int Fs, int bitrate_bps, int delay_compensation, Downmix.downmix_func<T> downmix, float[] subframe_mem, bool analysis_enabled) { if (analysis_enabled && variable_duration == OpusFramesize.OPUS_FRAMESIZE_VARIABLE && frame_size >= Fs / 200) { int num = 3; num = optimize_framesize(analysis_pcm, frame_size, C, Fs, bitrate_bps, 0, subframe_mem, delay_compensation, downmix); while (Fs / 400 << num > frame_size) { num--; } frame_size = Fs / 400 << num; } else { frame_size = frame_size_select(frame_size, variable_duration, Fs); } if (frame_size < 0) { return -1; } return frame_size; } internal static int compute_stereo_width(ReadOnlySpan<short> pcm, int pcm_ptr, int frame_size, int Fs, StereoWidthState mem) { int num = Fs / frame_size; int a = 32767 - 819175 / Inlines.IMAX(50, num); int num3; int num2; int num4 = (num3 = (num2 = 0)); for (int i = 0; i < frame_size - 3; i += 4) { int num5 = 0; int num6 = 0; int num7 = 0; int num8 = pcm_ptr + 2 * i; int num9 = pcm[num8]; int num10 = pcm[num8 + 1]; num5 = Inlines.SHR32(Inlines.MULT16_16(num9, num9), 2); num6 = Inlines.SHR32(Inlines.MULT16_16(num9, num10), 2); num7 = Inlines.SHR32(Inlines.MULT16_16(num10, num10), 2); num9 = pcm[num8 + 2]; num10 = pcm[num8 + 3]; num5 += Inlines.SHR32(Inlines.MULT16_16(num9, num9), 2); num6 += Inlines.SHR32(Inlines.MULT16_16(num9, num10), 2); num7 += Inlines.SHR32(Inlines.MULT16_16(num10, num10), 2); num9 = pcm[num8 + 4]; num10 = pcm[num8 + 5]; num5 += Inlines.SHR32(Inlines.MULT16_16(num9, num9), 2); num6 += Inlines.SHR32(Inlines.MULT16_16(num9, num10), 2); num7 += Inlines.SHR32(Inlines.MULT16_16(num10, num10), 2); num9 = pcm[num8 + 6]; num10 = pcm[num8 + 7]; num5 += Inlines.SHR32(Inlines.MULT16_16(num9, num9), 2); num6 += Inlines.SHR32(Inlines.MULT16_16(num9, num10), 2); num7 += Inlines.SHR32(Inlines.MULT16_16(num10, num10), 2); num4 += Inlines.SHR32(num5, 10); num3 += Inlines.SHR32(num6, 10); num2 += Inlines.SHR32(num7, 10); } mem.XX += Inlines.MULT16_32_Q15(a, num4 - mem.XX); mem.XY += Inlines.MULT16_32_Q15(a, num3 - mem.XY); mem.YY += Inlines.MULT16_32_Q15(a, num2 - mem.YY); mem.XX = Inlines.MAX32(0, mem.XX); mem.XY = Inlines.MAX32(0, mem.XY); mem.YY = Inlines.MAX32(0, mem.YY); if (Inlines.MAX32(mem.XX, mem.YY) > 210) { int num11 = Inlines.celt_sqrt(mem.XX); int num12 = Inlines.celt_sqrt(mem.YY); int num13 = Inlines.celt_sqrt(num11); int num14 = Inlines.celt_sqrt(num12); mem.XY = Inlines.MIN32(mem.XY, num11 * num12); int num15 = Inlines.SHR32(Inlines.frac_div32(mem.XY, 1 + Inlines.MULT16_16(num11, num12)), 16); int b = 32767 * Inlines.ABS16(num13 - num14) / (1 + num13 + num14); int num16 = Inlines.MULT16_16_Q15(Inlines.celt_sqrt(1073741824 - Inlines.MULT16_16(num15, num15)), b); mem.smoothed_width += (num16 - mem.smoothed_width) / num; mem.max_follower = Inlines.MAX16(mem.max_follower - 655 / num, mem.smoothed_width); } else { int num16 = 0; int num15 = 32767; int b = 0; } return Inlines.EXTRACT16(Inlines.MIN32(32767, 20 * mem.max_follower)); } internal static void smooth_fade(Span<short> in1, int in1_ptr, Span<short> in2, int in2_ptr, Span<short> output, int output_ptr, int overlap, int channels, int[] window, int Fs) { int num = 48000 / Fs; for (int i = 0; i < channels; i++) { for (int j = 0; j < overlap; j++) { int num2 = Inlines.MULT16_16_Q15(window[j * num], window[j * num]); output[output_ptr + j * channels + i] = (short)Inlines.SHR32(Inlines.MAC16_16(Inlines.MULT16_16(num2, in2[in2_ptr + j * channels + i]), 32767 - num2, in1[in1_ptr + j * channels + i]), 15); } } } internal static string opus_strerror(int error) { string[] array = new string[8] { "success", "invalid argument", "buffer too small", "internal error", "corrupted stream", "request not implemented", "invalid state", "memory allocation failed" }; if (error > 0 || error < -7) { return "unknown error"; } return array[-error]; } internal static string GetVersionString() { return "Concentus 2.1.2"; } } internal static class Downmix { internal delegate void downmix_func<T>(ReadOnlySpan<T> _x, Span<int> sub, int sub_ptr, int subframe, int offset, int c1, int c2, int C); internal static void downmix_float(ReadOnlySpan<float> x, Span<int> sub, int sub_ptr, int subframe, int offset, int c1, int c2, int C) { for (int i = 0; i < subframe; i++) { sub[sub_ptr + i] = Inlines.FLOAT2INT16(x[(i + offset) * C + c1]); } if (c2 > -1) { for (int i = 0; i < subframe; i++) { sub[sub_ptr + i] += Inlines.FLOAT2INT16(x[(i + offset) * C + c2]); } } else if (c2 == -2) { for (int j = 1; j < C; j++) { int num = j; for (int i = 0; i < subframe; i++) { sub[sub_ptr + i] += Inlines.FLOAT2INT16(x[(i + offset) * C + num]); } } } int num2 = 4096; num2 = ((C != -2) ? (num2 / 2) : (num2 / C)); for (int i = 0; i < subframe; i++) { sub[sub_ptr + i] *= num2; } } internal static void downmix_int(ReadOnlySpan<short> x, Span<int> sub, int sub_ptr, int subframe, int offset, int c1, int c2, int C) { for (int i = 0; i < subframe; i++) { sub[i + sub_ptr] = x[(i + offset) * C + c1]; } if (c2 > -1) { for (int i = 0; i < subframe; i++) { sub[i + sub_ptr] += x[(i + offset) * C + c2]; } } else if (c2 == -2) { for (int j = 1; j < C; j++) { for (int i = 0; i < subframe; i++) { sub[i + sub_ptr] += x[(i + offset) * C + j]; } } } int num = 4096; num = ((C != -2) ? (num / 2) : (num / C)); for (int i = 0; i < subframe; i++) { sub[i + sub_ptr] *= num; } } } internal static class MultiLayerPerceptron { private const int MAX_NEURONS = 100; internal static float tansig_approx(float x) { float num = 1f; if (!(x < 8f)) { return 1f; } if (!(x > -8f)) { return -1f; } if (x < 0f) { x = 0f - x; num = -1f; } int num2 = (int)Math.Floor(0.5f + 25f * x); x -= 0.04f * (float)num2; float num3 = Tables.tansig_table[num2]; float num4 = 1f - num3 * num3; num3 += x * num4 * (1f - num3 * x); return num * num3; } internal static void mlp_process(MLP m, float[] input, float[] output) { float[] array = new float[100]; float[] weights = m.weights; int num = 0; for (int i = 0; i < m.topo[1]; i++) { float num2 = weights[num]; num++; for (int j = 0; j < m.topo[0]; j++) { num2 += input[j] * weights[num]; num++; } array[i] = tansig_approx(num2); } for (int i = 0; i < m.topo[2]; i++) { float num3 = weights[num]; num++; for (int k = 0; k < m.topo[1]; k++) { num3 += array[k] * weights[num]; num++; } output[i] = tansig_approx(num3); } } } internal static class OpusCompare { private const int NBANDS = 21; private const int NFREQS = 240; private const int TEST_WIN_SIZE = 480; private const int TEST_WIN_STEP = 120; private static readonly int[] BANDS = new int[22] { 0, 2, 4, 6, 8, 10, 12, 14, 16, 20, 24, 28, 32, 40, 48, 56, 68, 80, 96, 120, 156, 200 }; private static void band_energy(Pointer<float> _out, Pointer<float> _ps, Pointer<int> _bands, int _nbands, Pointer<float> _in, int _nchannels, int _nframes, int _window_sz, int _step, int _downsample) { Pointer<float> pointer = Concentus.Common.CPlusPlus.Pointer.Malloc<float>((3 + _nchannels) * _window_sz); Pointer<float> pointer2 = pointer.Point(_window_sz); Pointer<float> pointer3 = pointer2.Point(_window_sz); Pointer<float> pointer4 = pointer3.Point(_window_sz); int num = _window_sz / 2; for (int i = 0; i < _window_sz; i++) { pointer[i] = (float)(0.5 - 0.5 * Math.Cos(Math.PI * 2.0 / (double)(_window_sz - 1) * (double)i)); } for (int i = 0; i < _window_sz; i++) { pointer2[i] = (float)Math.Cos(Math.PI * 2.0 / (double)_window_sz * (double)i); } for (int i = 0; i < _window_sz; i++) { pointer3[i] = (float)Math.Sin(Math.PI * 2.0 / (double)_window_sz * (double)i); } for (int j = 0; j < _nframes; j++) { for (int k = 0; k < _nchannels; k++) { for (int l = 0; l < _window_sz; l++) { pointer4[k * _window_sz + l] = pointer[l] * _in[(j * _step + l) * _nchannels + k]; } } int i; for (int m = (i = 0); m < _nbands; m++) { float[] array = new float[2]; for (; i < _bands[m + 1]; i++) { for (int k = 0; k < _nchannels; k++) { int num2 = 0; float num3; float num4 = (num3 = 0f); for (int l = 0; l < _window_sz; l++) { num4 += pointer2[num2] * pointer4[k * _window_sz + l]; num3 -= pointer3[num2] * pointer4[k * _window_sz + l]; num2 += i; if (num2 >= _window_sz) { num2 -= _window_sz; } } num4 *= (float)_downsample; num3 *= (float)_downsample; _ps[(j * num + i) * _nchannels + k] = num4 * num4 + num3 * num3 + 100000f; array[k] += _ps[(j * num + i) * _nchannels + k]; } } if (_out != null) { _out[(j * _nbands + m) * _nchannels] = array[0] / (float)(_bands[m + 1] - _bands[m]); if (_nchannels == 2) { _out[(j * _nbands + m) * _nchannels + 1] = array[1] / (float)(_bands[m + 1] - _bands[m]); } } } } } internal static float compare(float[] x, float[] y, int nchannels, int rate = 48000) { int num = x.Length; int num2 = y.Length; int num3 = 21; int num4 = 240; if (rate != 8000 && rate != 12000 && rate != 16000 && rate != 24000 && rate != 48000) { throw new ArgumentException("Sampling rate must be 8000, 12000, 16000, 24000, or 48000\n"); } int num5; if (rate != 48000) { num5 = 48000 / rate; switch (rate) { case 8000: num3 = 13; break; case 12000: num3 = 15; break; case 16000: num3 = 17; break; case 24000: num3 = 19; break; } num4 = 240 / num5; } else { num5 = 1; } if (num != num2 * num5) { throw new ArgumentException("Sample counts do not match"); } if (num < 480) { throw new ArgumentException("Insufficient sample data"); } int num6 = (num - 480 + 120) / 120; Pointer<float> pointer = Concentus.Common.CPlusPlus.Pointer.Malloc<float>(num6 * 21 * nchannels); Pointer<float> pointer2 = Concentus.Common.CPlusPlus.Pointer.Malloc<float>(num6 * 240 * nchannels); Pointer<float> pointer3 = Concentus.Common.CPlusPlus.Pointer.Malloc<float>(num6 * num4 * nchannels); band_energy(pointer, pointer2, BANDS.GetPointer(), 21, x.GetPointer(), nchannels, num6, 480, 120, 1); band_energy(null, pointer3, BANDS.GetPointer(), num3, y.GetPointer(), nchannels, num6, 480 / num5, 120 / num5, num5); for (int i = 0; i < num6; i++) { int j; for (j = 1; j < 21; j++) { for (int k = 0; k < nchannels; k++) { pointer[(i * 21 + j) * nchannels + k] += 0.1f * pointer[(i * 21 + j - 1) * nchannels + k]; } } j = 20; while (j-- > 0) { for (int k = 0; k < nchannels; k++) { pointer[(i * 21 + j) * nchannels + k] += 0.03f * pointer[(i * 21 + j + 1) * nchannels + k]; } } if (i > 0) { for (j = 0; j < 21; j++) { for (int k = 0; k < nchannels; k++) { pointer[(i * 21 + j) * nchannels + k] += 0.5f * pointer[((i - 1) * 21 + j) * nchannels + k]; } } } if (nchannels == 2) { for (j = 0; j < 21; j++) { float num7 = pointer[(i * 21 + j) * nchannels]; float num8 = pointer[(i * 21 + j) * nchannels + 1]; pointer[(i * 21 + j) * nchannels] += 0.01f * num8; pointer[(i * 21 + j) * nchannels + 1] += 0.01f * num7; } } for (j = 0; j < num3; j++) { for (int l = BANDS[j]; l < BANDS[j + 1]; l++) { for (int k = 0; k < nchannels; k++) { pointer2[(i * 240 + l) * nchannels + k] += 0.1f * pointer[(i * 21 + j) * nchannels + k]; pointer3[(i * num4 + l) * nchannels + k] += 0.1f * pointer[(i * 21 + j) * nchannels + k]; } } } } for (int j = 0; j < num3; j++) { for (int l = BANDS[j]; l < BANDS[j + 1]; l++) { for (int k = 0; k < nchannels; k++) { float num9 = pointer2[l * nchannels + k]; float num10 = pointer3[l * nchannels + k]; for (int i = 1; i < num6; i++) { float num11 = pointer2[(i * 240 + l) * nchannels + k]; float num12 = pointer3[(i * num4 + l) * nchannels + k]; pointer2[(i * 240 + l) * nchannels + k] += num9; pointer3[(i * num4 + l) * nchannels + k] += num10; num9 = num11; num10 = num12; } } } } int num13 = rate switch { 48000 => BANDS[21], 12000 => BANDS[num3], _ => BANDS[num3] - 3, }; double num14 = 0.0; for (int i = 0; i < num6; i++) { double num15 = 0.0; for (int j = 0; j < num3; j++) { double num16 = 0.0; for (int l = BANDS[j]; l < BANDS[j + 1] && l < num13; l++) { for (int k = 0; k < nchannels; k++) { float num17 = pointer3[(i * num4 + l) * nchannels + k] / pointer2[(i * 240 + l) * nchannels + k]; float num18 = num17 - (float)Math.Log(num17) - 1f; if (l >= 79 && l <= 81) { num18 *= 0.1f; } if (l == 80) { num18 *= 0.1f; } num16 += (double)num18; } } num16 /= (double)((BANDS[j + 1] - BANDS[j]) * nchannels); num15 += num16 * num16; } num15 /= 21.0; num15 *= num15; num14 += num15 * num15; } num14 = Math.Pow(num14 / (double)num6, 0.0625); float result = (float)(100.0 * (1.0 - 0.5 * Math.Log(1.0 + num14) / Math.Log(1.13))); _ = 0f; return result; } } internal static class OpusConstants { internal const int OPUS_AUTO = -1000; internal const int OPUS_BITRATE_MAX = -1; internal const int NB_FRAMES = 8; internal const int NB_TBANDS = 18; internal const int NB_TOT_BANDS = 21; internal const int NB_TONAL_SKIP_BANDS = 9; internal const int ANALYSIS_BUF_SIZE = 720; internal const int DETECT_SIZE = 200; internal const int MAX_ENCODER_BUFFER = 480; } public class OpusException : Exception { public int OpusErrorCode { get; private set; } internal OpusException() : base("Unknown error") { OpusErrorCode = -100; } internal OpusException(string message) : base(message) { OpusErrorCode = -100; } internal OpusException(int opusError) : base(CodecHelpers.opus_strerror(opusError)) { OpusErrorCode = opusError; } internal OpusException(string message, int opusError) : base(message) { OpusErrorCode = opusError; } } internal static class OpusMultistream { internal static int validate_layout(ChannelLayout layout) { int num = layout.nb_streams + layout.nb_coupled_streams; if (num > 255) { return 0; } for (int i = 0; i < layout.nb_channels; i++) { if (layout.mapping[i] >= num && layout.mapping[i] != byte.MaxValue) { return 0; } } return 1; } internal static int get_left_channel(ChannelLayout layout, int stream_id, int prev) { for (int i = ((prev >= 0) ? (prev + 1) : 0); i < layout.nb_channels; i++) { if (layout.mapping[i] == stream_id * 2) { return i; } } return -1; } internal static int get_right_channel(ChannelLayout layout, int stream_id, int prev) { for (int i = ((prev >= 0) ? (prev + 1) : 0); i < layout.nb_channels; i++) { if (layout.mapping[i] == stream_id * 2 + 1) { return i; } } return -1; } internal static int get_mono_channel(ChannelLayout layout, int stream_id, int prev) { for (int i = ((prev >= 0) ? (prev + 1) : 0); i < layout.nb_channels; i++) { if (layout.mapping[i] == stream_id + layout.nb_coupled_streams) { return i; } } return -1; } } internal static class Tables { internal static readonly float[] dct_table = new float[128] { 0.25f, 0.25f, 0.25f, 0.25f, 0.25f, 0.25f, 0.25f, 0.25f, 0.25f, 0.25f, 0.25f, 0.25f, 0.25f, 0.25f, 0.25f, 0.25f, 0.351851f, 0.33833f, 0.311806f, 0.2733f, 0.224292f, 0.166664f, 0.102631f, 0.034654f, -0.034654f, -0.102631f, -0.166664f, -0.224292f, -0.2733f, -0.311806f, -0.33833f, -0.351851f, 0.34676f, 0.293969f, 0.196424f, 0.068975f, -0.068975f, -0.196424f, -0.293969f, -0.34676f, -0.34676f, -0.293969f, -0.196424f, -0.068975f, 0.068975f, 0.196424f, 0.293969f, 0.34676f, 0.33833f, 0.224292f, 0.034654f, -0.166664f, -0.311806f, -0.351851f, -0.2733f, -0.102631f, 0.102631f, 0.2733f, 0.351851f, 0.311806f, 0.166664f, -0.034654f, -0.224292f, -0.33833f, 0.326641f, 0.135299f, -0.135299f, -0.326641f, -0.326641f, -0.135299f, 0.135299f, 0.326641f, 0.326641f, 0.135299f, -0.135299f, -0.326641f, -0.326641f, -0.135299f, 0.135299f, 0.326641f, 0.311806f, 0.034654f, -0.2733f, -0.33833f, -0.102631f, 0.224292f, 0.351851f, 0.166664f, -0.166664f, -0.351851f, -0.224292f, 0.102631f, 0.33833f, 0.2733f, -0.034654f, -0.311806f, 0.293969f, -0.068975f, -0.34676f, -0.196424f, 0.196424f, 0.34676f, 0.068975f, -0.293969f, -0.293969f, 0.068975f, 0.34676f, 0.196424f, -0.196424f, -0.34676f, -0.068975f, 0.293969f, 0.2733f, -0.166664f, -0.33833f, 0.034654f, 0.351851f, 0.102631f, -0.311806f, -0.224292f, 0.224292f, 0.311806f, -0.102631f, -0.351851f, -0.034654f, 0.33833f, 0.166664f, -0.2733f }; internal static readonly float[] analysis_window = new float[240] { 4.3E-05f, 0.000171f, 0.000385f, 0.000685f, 0.001071f, 0.001541f, 0.002098f, 0.002739f, 0.003466f, 0.004278f, 0.005174f, 0.006156f, 0.007222f, 0.008373f, 0.009607f, 0.010926f, 0.012329f, 0.013815f, 0.015385f, 0.017037f, 0.018772f, 0.02059f, 0.02249f, 0.024472f, 0.026535f, 0.028679f, 0.030904f, 0.03321f, 0.035595f, 0.03806f, 0.040604f, 0.043227f, 0.045928f, 0.048707f, 0.051564f, 0.054497f, 0.057506f, 0.060591f, 0.063752f, 0.066987f, 0.070297f, 0.07368f, 0.077136f, 0.080665f, 0.084265f, 0.087937f, 0.091679f, 0.095492f, 0.099373f, 0.103323f, 0.107342f, 0.111427f, 0.115579f, 0.119797f, 0.12408f, 0.128428f, 0.132839f, 0.137313f, 0.141849f, 0.146447f, 0.151105f, 0.155823f, 0.1606f, 0.165435f, 0.170327f, 0.175276f, 0.18028f, 0.18534f, 0.190453f, 0.195619f, 0.200838f, 0.206107f, 0.211427f, 0.216797f, 0.222215f, 0.22768f, 0.233193f, 0.238751f, 0.244353f, 0.25f, 0.255689f, 0.261421f, 0.267193f, 0.273005f, 0.278856f, 0.284744f, 0.29067f, 0.296632f, 0.302628f, 0.308658f, 0.314721f, 0.320816f, 0.326941f, 0.333097f, 0.33928f, 0.345492f, 0.351729f, 0.357992f, 0.36428f, 0.37059f, 0.376923f, 0.383277f, 0.389651f, 0.396044f, 0.402455f, 0.408882f, 0.415325f, 0.421783f, 0.428254f, 0.434737f, 0.441231f, 0.447736f, 0.454249f, 0.46077f, 0.467298f, 0.473832f, 0.48037f, 0.486912f, 0.493455f, 0.5f, 0.506545f, 0.513088f, 0.51963f, 0.526168f, 0.532702f, 0.53923f, 0.545751f, 0.552264f, 0.558769f, 0.565263f, 0.571746f, 0.578217f, 0.584675f, 0.591118f, 0.597545f, 0.603956f, 0.610349f, 0.616723f, 0.623077f, 0.62941f, 0.63572f, 0.642008f, 0.648271f, 0.654508f, 0.66072f, 0.666903f, 0.673059f, 0.679184f, 0.685279f, 0.691342f, 0.697372f, 0.703368f, 0.70933f, 0.715256f, 0.721144f, 0.726995f, 0.732807f, 0.738579f, 0.744311f, 0.75f, 0.755647f, 0.761249f, 0.766807f, 0.77232f, 0.777785f, 0.783203f, 0.788573f, 0.793893f, 0.799162f, 0.804381f, 0.809547f, 0.81466f, 0.81972f, 0.824724f, 0.829673f, 0.834565f, 0.8394f, 0.844177f, 0.848895f, 0.853553f, 0.858151f, 0.862687f, 0.867161f, 0.871572f, 0.87592f, 0.880203f, 0.884421f, 0.888573f, 0.892658f, 0.896677f, 0.900627f, 0.904508f, 0.908321f, 0.912063f, 0.915735f, 0.919335f, 0.922864f, 0.92632f, 0.929703f, 0.933013f, 0.936248f, 0.939409f, 0.942494f, 0.945503f, 0.948436f, 0.951293f, 0.954072f, 0.956773f, 0.959396f, 0.96194f, 0.964405f, 0.96679f, 0.969096f, 0.971321f, 0.973465f, 0.975528f, 0.97751f, 0.97941f, 0.981228f, 0.982963f, 0.984615f, 0.986185f, 0.987671f, 0.989074f, 0.990393f, 0.991627f, 0.992778f, 0.993844f, 0.994826f, 0.995722f, 0.996534f, 0.997261f, 0.997902f, 0.998459f, 0.998929f, 0.999315f, 0.999615f, 0.999829f, 0.999957f, 1f }; internal static readonly int[] tbands = new int[19] { 2, 4, 6, 8, 10, 12, 14, 16, 20, 24, 28, 32, 40, 48, 56, 68, 80, 96, 120 }; internal static readonly int[] extra_bands = new int[22] { 1, 2, 4, 6, 8, 10, 12, 14, 16, 20, 24, 28, 32, 40, 48, 56, 68, 80, 96, 120, 160, 200 }; internal static readonly float[] weights = new float[422] { -0.0941125f, -0.302976f, -0.603555f, -0.19393f, -0.185983f, -0.601617f, -0.0465317f, -0.114563f, -0.103599f, -0.618938f, -0.317859f, -0.169949f, -0.0702885f, 0.148065f, 0.409524f, 0.548432f, 0.367649f, -0.494393f, 0.764306f, -1.83957f, 0.170849f, 12.786f, -1.08848f, -1.27284f, -16.2606f, 24.1773f, -5.57454f, -0.17276f, -0.163388f, -0.224421f, -0.0948944f, -0.0728695f, -0.26557f, -0.100283f, -0.0515459f, -0.146142f, -0.120674f, -0.180655f, 0.12857f, 0.442138f, -0.493735f, 0.167767f, 0.206699f, -0.197567f, 0.417999f, 1.50364f, -0.773341f, -10.0401f, 0.401872f, 2.97966f, 15.2165f, -1.88905f, -1.19254f, 0.0285397f, -0.00405139f, 0.0707565f, 0.00825699f, -0.0927269f, -0.010393f, -0.00428882f, -0.00489743f, -0.0709731f, -0.00255992f, 0.0395619f, 0.226424f, 0.0325231f, 0.162175f, -0.100118f, 0.485789f, 0.12697f, 0.285937f, 0.0155637f, 0.10546f, 3.05558f, 1.15059f, -1.00904f, -1.83088f, 3.31766f, -3.42516f, -0.119135f, -0.0405654f, 0.00690068f, 0.0179877f, -0.0382487f, 0.00597941f, -0.0183611f, 0.00190395f, -0.144322f, -0.0435671f, 0.000990594f, 0.221087f, 0.142405f, 0.484066f, 0.404395f, 0.511955f, -0.237255f, 0.241742f, 0.35045f, -0.699428f, 10.3993f, 2.6507f, -2.43459f, -4.18838f, 1.05928f, 1.71067f, 0.00667811f, -0.0721335f, -0.0397346f, 0.0362704f, -0.11496f, -0.0235776f, 0.0082161f, -0.0141741f, -0.0329699f, -0.0354253f, 0.00277404f, -0.290654f, -1.14767f, -0.319157f, -0.686544f, 0.36897f, 0.478899f, 0.182579f, -0.411069f, 0.881104f, -4.60683f, 1.4697f, 325f / (356f * (float)Math.E), -1.81905f, -30.1699f, 5.55225f, 0.0019508f, -0.123576f, -0.0727332f, -0.0641597f, -0.0534458f, -0.108166f, -0.0937368f, -0.0697883f, -0.0275475f, -0.192309f, -0.110074f, 0.285375f, -0.405597f, 0.0926724f, -0.287881f, -0.851193f, -0.099493f, -0.233764f, -1.2852f, 1.13611f, 3.12168f, -0.0699f, -1.86216f, 2.65292f, -7.31036f, 2.44776f, -0.00111802f, -0.0632786f, -0.0376296f, -0.149851f, 0.142963f, 0.184368f, 0.123433f, 0.0756158f, 0.117312f, 0.0933395f, 0.0692163f, 0.0842592f, 0.0704683f, 0.0589963f, 0.0942205f, -0.448862f, 0.0262677f, 0.270352f, -0.262317f, 0.172586f, 2.00227f, -0.159216f, 0.038422f, 10.2073f, 4.15536f, -2.3407f, -0.0550265f, 0.00964792f, -0.141336f, 0.0274501f, 0.0343921f, -0.0487428f, 0.0950172f, -0.00775017f, -0.0372492f, -0.00548121f, -0.0663695f, 0.0960506f, -0.200008f, -0.0412827f, 0.58728f, 0.0515787f, 0.337254f, 0.855024f, 0.668371f, -0.114904f, -3.62962f, -0.467477f, -0.215472f, 2.61537f, 0.406117f, -1.36373f, 0.0425394f, 0.12208f, 0.0934502f, 0.123055f, 0.0340935f, -0.142466f, 0.035037f, -0.0490666f, 0.0733208f, 0.0576672f, 0.123984f, -0.0517194f, -0.253018f, 0.590565f, 0.145849f, 0.315185f, 0.221534f, -0.149081f, 0.216161f, -0.349575f, 24.5664f, -0.994196f, 0.614289f, -18.7905f, -2.83277f, -0.716801f, -0.347201f, 0.479515f, -0.246027f, 0.0758683f, 0.137293f, -0.17781f, 0.118751f, -0.00108329f, -0.237334f, 0.355732f, -0.12991f, -0.0547627f, -0.318576f, -0.325524f, 0.180494f, -0.0625604f, 0.141219f, 0.344064f, 0.37658f, -0.591772f, 5.8427f, -0.38075f, 0.221894f, -1.41934f, -1879430f, 1.34114f, 0.0283355f, -0.0447856f, -0.0211466f, -0.0256927f, 0.0139618f, 0.0207934f, -0.0107666f, 0.0110969f, 0.0586069f, -0.0253545f, -0.0328433f, 0.11872f, -0.216943f, 0.145748f, 0.119808f, -0.0915211f, -0.120647f, -0.0787719f, -0.143644f, -0.595116f, -1.152f, -1.25335f, -1.17092f, 4.34023f, -975268f, -1.37033f, -0.0401123f, 0.210602f, -0.136656f, 0.135962f, -0.0523293f, 0.0444604f, 0.0143928f, 0.00412666f, -0.0193003f, 0.218452f, -0.110204f, -2.02563f, 0.918238f, -2.45362f, 1.19542f, -0.061362f, -1.92243f, 0.308111f, 0.49764f, 0.912356f, 0.209272f, -2.34525f, 2.19326f, -6.47121f, 1.69771f, -0.725123f, 0.0118929f, 0.0377944f, 0.0554003f, 0.0226452f, -0.0704421f, -0.0300309f, 0.0122978f, -0.0041782f, -0.0686612f, 0.0313115f, 0.039111f, 0.364111f, -0.0945548f, 0.0229876f, -0.17414f, 0.329795f, 0.114714f, 0.30022f, 0.106997f, 0.132355f, 5.79932f, 0.908058f, -0.905324f, -3.3561f, 0.190647f, 0.184211f, -0.673648f, 0.231807f, -0.0586222f, 0.230752f, -0.438277f, 0.245857f, -0.17215f, 0.0876383f, -0.720512f, 0.162515f, 0.0170571f, 0.101781f, 0.388477f, 1.32931f, 1.08548f, -0.936301f, -2.36958f, -6.71988f, -3.44376f, 2.13818f, 14.2318f, 4.91459f, -3.09052f, -9.69191f, -0.768234f, 1.79604f, 0.0549653f, 0.163399f, 0.0797025f, 0.0343933f, -0.0555876f, -0.00505673f, 0.0187258f, 0.0326628f, 0.0231486f, 0.15573f, 0.0476223f, -0.254824f, 1.60155f, -0.801221f, 2.55496f, 0.737629f, -1.36249f, -0.695463f, -2.44301f, -1.73188f, 3.95279f, 1.89068f, 0.486087f, -11.3343f, 3941600f, -0.381439f, 0.12115f, -0.906927f, 2.93878f, 1.6388f, 0.882811f, 0.874344f, 1.21726f, -0.874545f, 0.321706f, 0.785055f, 0.946558f, -0.575066f, -3.46553f, 0.884905f, 0.0924047f, -9.90712f, 0.391338f, 0.160103f, -2.04954f, 4.1455f, 0.0684029f, -0.144761f, -0.285282f, 0.379244f, -1.1584f, -0.0277241f, -9.85f, -4.82386f, 3.71333f, 3.87308f, 3.52558f }; internal static readonly int[] topo = new int[3] { 25, 15, 2 }; internal static readonly MLP net = new MLP { layers = 3, topo = topo, weights = weights }; internal static readonly float[] tansig_table = new float[201] { 0f, 0.039979f, 0.07983f, 0.119427f, 0.158649f, 0.197375f, 0.235496f, 0.272905f, 0.309507f, 0.345214f, 0.379949f, 0.413644f, 0.446244f, 0.4777f, 0.507977f, 0.53705f, 0.5649f, 0.591519f, 0.616909f, 0.641077f, 0.664037f, 0.685809f, 0.706419f, 0.725897f, 0.744277f, 0.761594f, 0.777888f, 0.793199f, 0.807569f, 0.82104f, 0.833655f, 0.845456f, 0.856485f, 0.866784f, 0.876393f, 0.885352f, 0.893698f, 0.901468f, 0.908698f, 0.91542f, 0.921669f, 0.927473f, 0.932862f, 0.937863f, 0.942503f, 0.946806f, 0.950795f, 0.954492f, 0.957917f, 0.96109f, 0.964028f, 0.966747f, 0.969265f, 0.971594f, 0.973749f, 0.975743f, 0.977587f, 0.979293f, 0.980869f, 0.982327f, 0.983675f, 0.984921f, 0.986072f, 0.987136f, 0.988119f, 0.989027f, 0.989867f, 0.990642f, 0.991359f, 0.99202f, 0.992631f, 0.993196f, 0.993718f, 0.994199f, 0.994644f, 0.995055f, 0.995434f, 0.995784f, 0.996108f, 0.996407f, 0.996682f, 0.996937f, 0.997172f, 0.997389f, 0.99759f, 0.997775f, 0.997946f, 0.998104f, 0.998249f, 0.998384f, 0.998508f, 0.998623f, 0.998728f, 0.998826f, 0.998916f, 0.999f, 0.999076f, 0.999147f, 0.999213f, 0.999273f, 0.999329f, 0.999381f, 0.999428f, 0.999472f, 0.999513f, 0.99955f, 0.999585f, 0.999617f, 0.999646f, 0.999673f, 0.999699f, 0.999722f, 0.999743f, 0.999763f, 0.999781f, 0.999798f, 0.999813f, 0.999828f, 0.999841f, 0.999853f, 0.999865f, 0.999875f, 0.999885f, 0.999893f, 0.999902f, 0.999909f, 0.999916f, 0.999923f, 0.999929f, 0.999934f, 0.999939f, 0.999944f, 0.999948f, 0.999952f, 0.999956f, 0.999959f, 0.999962f, 0.999965f, 0.999968f, 0.99997f, 0.999973f, 0.999975f, 0.999977f, 0.999978f, 0.99998f, 0.999982f, 0.999983f, 0.999984f, 0.999986f, 0.999987f, 0.999988f, 0.999989f, 0.99999f, 0.99999f, 0.999991f, 0.999992f, 0.999992f, 0.999993f, 0.999994f, 0.999994f, 0.999994f, 0.999995f, 0.999995f, 0.999996f, 0.999996f, 0.999996f, 0.999997f, 0.999997f, 0.999997f, 0.999997f, 0.999997f, 0.999998f, 0.999998f, 0.999998f, 0.999998f, 0.999998f, 0.999998f, 0.999999f, 0.999999f, 0.999999f, 0.999999f, 0.999999f, 0.999999f, 0.999999f, 0.999999f, 0.999999f, 0.999999f, 0.999999f, 0.999999f, 0.999999f, 1f, 1f, 1f, 1f, 1f, 1f, 1f, 1f, 1f, 1f, 1f }; internal static readonly int[] mono_voice_bandwidth_thresholds = new int[8] { 11000, 1000, 14000, 1000, 17000, 1000, 21000, 2000 }; internal static readonly int[] mono_music_bandwidth_thresholds = new int[8] { 12000, 1000, 15000, 1000, 18000, 2000, 22000, 2000 }; internal static readonly int[] stereo_voice_bandwidth_thresholds = new int[8] { 11000, 1000, 14000, 1000, 21000, 2000, 28000, 2000 }; internal static readonly int[] stereo_music_bandwidth_thresholds = new int[8] { 12000, 1000, 18000, 2000, 21000, 2000, 30000, 2000 }; internal const int stereo_voice_threshold = 30000; internal const int stereo_music_threshold = 30000; internal static readonly int[][] mode_thresholds = new int[2][] { new int[2] { 64000, 16000 }, new int[2] { 36000, 16000 } }; } public static class ResamplerFactory { public static IResampler CreateResampler(int numChannels, int inRate, int outRate, int quality, TextWriter logger = null) { return CreateResampler(numChannels, inRate, outRate, inRate, outRate, quality, logger); } public static IResampler CreateResampler(int numChannels, int ratioNum, int ratioDen, int inRate, int outRate, int quality, TextWriter logger = null) { if (numChannels <= 0) { throw new ArgumentOutOfRangeException("numChannels"); } if (ratioNum <= 0) { throw new ArgumentOutOfRangeException("ratioNum"); } if (ratioDen <= 0) { throw new ArgumentOutOfRangeException("ratioDen"); } if (inRate <= 0) { throw new ArgumentOutOfRangeException("inRate"); } if (outRate <= 0) { throw new ArgumentOutOfRangeException("outRate"); } if (quality < 0 || quality > 10) { throw new ArgumentOutOfRangeException("quality", "Quality must be between 0 and 10"); } return new SpeexResampler(numChannels, ratioNum, ratioDen, inRate, outRate, quality); } } } namespace Concentus.Silk { internal static class ApplySineWindow { private static readonly short[] freq_table_Q16 = new short[27] { 12111, 9804, 8235, 7100, 6239, 5565, 5022, 4575, 4202, 3885, 3612, 3375, 3167, 2984, 2820, 2674, 2542, 2422, 2313, 2214, 2123, 2038, 1961, 1889, 1822, 1760, 1702 }; internal static void silk_apply_sine_window(short[] px_win, int px_win_ptr, short[] px, int px_ptr, int win_type, int length) { int num = (length >> 2) - 4; int num2 = freq_table_Q16[num]; int num3 = Inlines.silk_SMULWB(num2, -num2); int num4; int num5; if (win_type == 1) { num4 = 0; num5 = num2 + Inlines.silk_RSHIFT(length, 3); } else { num4 = 65536; num5 = 65536 + Inlines.silk_RSHIFT(num3, 1) + Inlines.silk_RSHIFT(length, 4); } for (num = 0; num < length; num += 4) { int num6 = px_win_ptr + num; int num7 = px_ptr + num; px_win[num6] = (short)Inlines.silk_SMULWB(Inlines.silk_RSHIFT(num4 + num5, 1), px[num7]); px_win[num6 + 1] = (short)Inlines.silk_SMULWB(num5, px[num7 + 1]); num4 = Inlines.silk_SMULWB(num5, num3) + Inlines.silk_LSHIFT(num5, 1) - num4 + 1; num4 = Inlines.silk_min(num4, 65536); px_win[num6 + 2] = (short)Inlines.silk_SMULWB(Inlines.silk_RSHIFT(num4 + num5, 1), px[num7 + 2]); px_win[num6 + 3] = (short)Inlines.silk_SMULWB(num4, px[num7 + 3]); num5 = Inlines.silk_SMULWB(num4, num3) + Inlines.silk_LSHIFT(num4, 1) - num5; num5 = Inlines.silk_min(num5, 65536); } } } internal static class BurgModified { private const int MAX_FRAME_SIZE = 384; private const int QA = 25; private const int N_BITS_HEAD_ROOM = 2; private const int MIN_RSHIFTS = -16; private const int MAX_RSHIFTS = 7; internal static void silk_burg_modified(BoxedValueInt res_nrg, BoxedValueInt res_nrg_Q, int[] A_Q16, short[] x, int x_ptr, int minInvGain_Q30, int subfr_length, int nb_subfr, int D) { int[] array = new int[16]; int[] array2 = new int[16]; int[] array3 = new int[16]; int[] array4 = new int[17]; int[] array5 = new int[17]; int[] array6 = new int[16]; long num = Inlines.silk_inner_prod16_aligned_64(x, x_ptr, x, x_ptr, subfr_length * nb_subfr); int num2 = Inlines.silk_CLZ64(num); int num3 = 35 - num2; if (num3 > 7) { num3 = 7; } if (num3 < -16) { num3 = -16; } int num4 = (int)((num3 <= 0) ? Inlines.silk_LSHIFT32((int)num, -num3) : Inlines.silk_RSHIFT64(num, num3)); array5[0] = (array4[0] = num4 + Inlines.silk_SMMUL(42950, num4) + 1); Arrays.MemSetInt(array, 0, 16); if (num3 > 0) { for (int i = 0; i < nb_subfr; i++) { int num5 = x_ptr + i * subfr_length; for (int j = 1; j < D + 1; j++) { array[j - 1] += (int)Inlines.silk_RSHIFT64(Inlines.silk_inner_prod16_aligned_64(x, num5, x, num5 + j, subfr_length - j), num3); } } } else { for (int i = 0; i < nb_subfr; i++) { int num5 = x_ptr + i * subfr_length; CeltPitchXCorr.pitch_xcorr(x, num5, x, num5 + 1, array6, subfr_length - D, D); for (int j = 1; j < D + 1; j++) { int k = j + subfr_length - D; int num6 = 0; for (; k < subfr_length; k++) { num6 = Inlines.MAC16_16(num6, x[num5 + k], x[num5 + k - j]); } array6[j - 1] += num6; } for (int j = 1; j < D + 1; j++) { array[j - 1] += Inlines.silk_LSHIFT32(array6[j - 1], -num3); } } } Arrays.MemCopy(array, 0, array2, 0, 16); array5[0] = (array4[0] = num4 + Inlines.silk_SMMUL(42950, num4) + 1); int num7 = 1073741824; int num8 = 0; for (int j = 0; j < D; j++) { int num9; int num10; if (num3 > -2) { for (int i = 0; i < nb_subfr; i++) { int num5 = x_ptr + i * subfr_length; int b = -Inlines.silk_LSHIFT32(x[num5 + j], 16 - num3); int b2 = -Inlines.silk_LSHIFT32(x[num5 + subfr_length - j - 1], 16 - num3); num9 = Inlines.silk_LSHIFT32(x[num5 + j], 9); num10 = Inlines.silk_LSHIFT32(x[num5 + subfr_length - j - 1], 9); for (int l = 0; l < j; l++) { array[l] = Inlines.silk_SMLAWB(array[l], b, x[num5 + j - l - 1]); array2[l] = Inlines.silk_SMLAWB(array2[l], b2, x[num5 + subfr_length - j + l]); int b3 = array3[l]; num9 = Inlines.silk_SMLAWB(num9, b3, x[num5 + j - l - 1]); num10 = Inlines.silk_SMLAWB(num10, b3, x[num5 + subfr_length - j + l]); } num9 = Inlines.silk_LSHIFT32(-num9, 7 - num3); num10 = Inlines.silk_LSHIFT32(-num10, 7 - num3); for (int l = 0; l <= j; l++) { array4[l] = Inlines.silk_SMLAWB(array4[l], num9, x[num5 + j - l]); array5[l] = Inlines.silk_SMLAWB(array5[l], num10, x[num5 + subfr_length - j + l - 1]); } } } else { for (int i = 0; i < nb_subfr; i++) { int num5 = x_ptr + i * subfr_length; int b = -Inlines.silk_LSHIFT32(x[num5 + j], -num3); int b2 = -Inlines.silk_LSHIFT32(x[num5 + subfr_length - j - 1], -num3); num9 = Inlines.silk_LSHIFT32(x[num5 + j], 17); num10 = Inlines.silk_LSHIFT32(x[num5 + subfr_length - j - 1], 17); for (int l = 0; l < j; l++) { array[l] = Inlines.silk_MLA(array[l], b, x[num5 + j - l - 1]); array2[l] = Inlines.silk_MLA(array2[l], b2, x[num5 + subfr_length - j + l]); int c = Inlines.silk_RSHIFT_ROUND(array3[l], 8); num9 = Inlines.silk_MLA(num9, x[num5 + j - l - 1], c); num10 = Inlines.silk_MLA(num10, x[num5 + subfr_length - j + l], c); } num9 = -num9; num10 = -num10; for (int l = 0; l <= j; l++) { array4[l] = Inlines.silk_SMLAWW(array4[l], num9, Inlines.silk_LSHIFT32(x[num5 + j - l], -num3 - 1)); array5[l] = Inlines.silk_SMLAWW(array5[l], num10, Inlines.silk_LSHIFT32(x[num5 + subfr_length - j + l - 1], -num3 - 1)); } } } num9 = array[j]; num10 = array2[j]; int a = 0; int num11 = Inlines.silk_ADD32(array5[0], array4[0]); for (int l = 0; l < j; l++) { int b3 = array3[l]; num2 = Inlines.silk_CLZ32(Inlines.silk_abs(b3)) - 1; num2 = Inlines.silk_min(7, num2); int c = Inlines.silk_LSHIFT32(b3, num2); num9 = Inlines.silk_ADD_LSHIFT32(num9, Inlines.silk_SMMUL(array2[j - l - 1], c), 7 - num2); num10 = Inlines.silk_ADD_LSHIFT32(num10, Inlines.silk_SMMUL(array[j - l - 1], c), 7 - num2); a = Inlines.silk_ADD_LSHIFT32(a, Inlines.silk_SMMUL(array5[j - l], c), 7 - num2); num11 = Inlines.silk_ADD_LSHIFT32(num11, Inlines.silk_SMMUL(Inlines.silk_ADD32(array5[l + 1], array4[l + 1]), c), 7 - num2); } array4[j + 1] = num9; array5[j + 1] = num10; a = Inlines.silk_ADD32(a, num10); a = Inlines.silk_LSHIFT32(-a, 1); int num12 = ((Inlines.silk_abs(a) >= num11) ? ((a > 0) ? int.MaxValue : int.MinValue) : Inlines.silk_DIV32_varQ(a, num11, 31)); num9 = 1073741824 - Inlines.silk_SMMUL(num12, num12); num9 = Inlines.silk_LSHIFT(Inlines.silk_SMMUL(num7, num9), 2); if (num9 <= minInvGain_Q30) { num10 = 1073741824 - Inlines.silk_DIV32_varQ(minInvGain_Q30, num7, 30); num12 = Inlines.silk_SQRT_APPROX(num10); num12 = Inlines.silk_RSHIFT32(num12 + Inlines.silk_DIV32(num10, num12), 1); num12 = Inlines.silk_LSHIFT32(num12, 16); if (a < 0) { num12 = -num12; } num7 = minInvGain_Q30; num8 = 1; } else { num7 = num9; } for (int l = 0; l < j + 1 >> 1; l++) { num9 = array3[l]; num10 = array3[j - l - 1]; array3[l] = Inlines.silk_ADD_LSHIFT32(num9, Inlines.silk_SMMUL(num10, num12), 1); array3[j - l - 1] = Inlines.silk_ADD_LSHIFT32(num10, Inlines.silk_SMMUL(num9, num12), 1); } array3[j] = Inlines.silk_RSHIFT32(num12, 6); if (num8 != 0) { for (int l = j + 1; l < D; l++) { array3[l] = 0; } break; } for (int l = 0; l <= j + 1; l++) { num9 = array4[l]; num10 = array5[j - l + 1]; array4[l] = Inlines.silk_ADD_LSHIFT32(num9, Inlines.silk_SMMUL(num10, num12), 1); array5[j - l + 1] = Inlines.silk_ADD_LSHIFT32(num10, Inlines.silk_SMMUL(num9, num12), 1); } } if (num8 != 0) { for (int l = 0; l < D; l++) { A_Q16[l] = -Inlines.silk_RSHIFT_ROUND(array3[l], 9); } if (num3 > 0) { for (int i = 0; i < nb_subfr; i++) { int num5 = x_ptr + i * subfr_length; num4 -= (int)Inlines.silk_RSHIFT64(Inlines.silk_inner_prod16_aligned_64(x, num5, x, num5, D), num3); } } else { for (int i = 0; i < nb_subfr; i++) { int num5 = x_ptr + i * subfr_length; num4 -= Inlines.silk_LSHIFT32(Inlines.silk_inner_prod_self(x, num5, D), -num3); } } res_nrg.Val = Inlines.silk_LSHIFT(Inlines.silk_SMMUL(num7, num4), 2); res_nrg_Q.Val = -num3; } else { int num11 = array4[0]; int num9 = 65536; for (int l = 0; l < D; l++) { int c = Inlines.silk_RSHIFT_ROUND(array3[l], 9); num11 = Inlines.silk_SMLAWW(num11, array4[l + 1], c); num9 = Inlines.silk_SMLAWW(num9, c, c); A_Q16[l] = -c; } res_nrg.Val = Inlines.silk_SMLAWW(num11, Inlines.silk_SMMUL(42950, num4), -num9); res_nrg_Q.Val = -num3; } } } internal static class BWExpander { internal static void silk_bwexpander_32(int[] ar, int d, int chirp_Q16) { int b = chirp_Q16 - 65536; for (int i = 0; i < d - 1; i++) { ar[i] = Inlines.silk_SMULWW(chirp_Q16, ar[i]); chirp_Q16 += Inlines.silk_RSHIFT_ROUND(Inlines.silk_MUL(chirp_Q16, b), 16); } ar[d - 1] = Inlines.silk_SMULWW(chirp_Q16, ar[d - 1]); } internal static void silk_bwexpander(short[] ar, int d, int chirp_Q16) { int b = chirp_Q16 - 65536; for (int i = 0; i < d - 1; i++) { ar[i] = (short)Inlines.silk_RSHIFT_ROUND(Inlines.silk_MUL(chirp_Q16, ar[i]), 16); chirp_Q16 += Inlines.silk_RSHIFT_ROUND(Inlines.silk_MUL(chirp_Q16, b), 16); } ar[d - 1] = (short)Inlines.silk_RSHIFT_ROUND(Inlines.silk_MUL(chirp_Q16, ar[d - 1]), 16); } } internal static class CNG { internal static void silk_CNG_exc(Span<int> exc_Q10, int exc_Q10_ptr, int[] exc_buf_Q14, int Gain_Q16, int length, ref int rand_seed) { int num; for (num = 255; num > length; num = Inlines.silk_RSHIFT(num, 1)) { } int num2 = rand_seed; for (int i = exc_Q10_ptr; i < exc_Q10_ptr + length; i++) { num2 = Inlines.silk_RAND(num2); int num3 = Inlines.silk_RSHIFT(num2, 24) & num; exc_Q10[i] = (short)Inlines.silk_SAT16(Inlines.silk_SMULWW(exc_buf_Q14[num3], Gain_Q16 >> 4)); } rand_seed = num2; } internal static void silk_CNG_Reset(SilkChannelDecoder psDec) { int num = Inlines.silk_DIV32_16(32767, (short)(psDec.LPC_order + 1)); int num2 = 0; for (int i = 0; i < psDec.LPC_order; i++) { num2 += num; psDec.sCNG.CNG_smth_NLSF_Q15[i] = (short)num2; } psDec.sCNG.CNG_smth_Gain_Q16 = 0; psDec.sCNG.rand_seed = 3176576; } internal static void silk_CNG(SilkChannelDecoder psDec, SilkDecoderControl psDecCtrl, Span<short> frame, int frame_ptr, int length) { short[] array = new short[psDec.LPC_order]; CNGState sCNG = psDec.sCNG; if (psDec.fs_kHz != sCNG.fs_kHz) { silk_CNG_Reset(psDec); sCNG.fs_kHz = psDec.fs_kHz; } if (psDec.lossCnt == 0 && psDec.prevSignalType == 0) { for (int i = 0; i < psDec.LPC_order; i++) { sCNG.CNG_smth_NLSF_Q15[i] += (short)Inlines.silk_SMULWB(psDec.prevNLSF_Q15[i] - sCNG.CNG_smth_NLSF_Q15[i], 16348); } int num = 0; for (int i = 0; i < psDec.nb_subfr; i++) { if (psDecCtrl.Gains_Q16[i] > num) { num = psDecCtrl.Gains_Q16[i]; } } Arrays.MemMoveInt(sCNG.CNG_exc_buf_Q14, 0, psDec.subfr_length, (psDec.nb_subfr - 1) * psDec.subfr_length); for (int i = 0; i < psDec.nb_subfr; i++) { sCNG.CNG_smth_Gain_Q16 += Inlines.silk_SMULWB(psDecCtrl.Gains_Q16[i] - sCNG.CNG_smth_Gain_Q16, 4634); } } if (psDec.lossCnt != 0) { int[] array2 = new int[length + 16]; int num2 = Inlines.silk_SMULWW(psDec.sPLC.randScale_Q14, psDec.sPLC.prevGain_Q16[1]); if (num2 >= 2097152 || sCNG.CNG_smth_Gain_Q16 > 8388608) { num2 = Inlines.silk_SMULTT(num2, num2); num2 = Inlines.silk_SUB_LSHIFT32(Inlines.silk_SMULTT(sCNG.CNG_smth_Gain_Q16, sCNG.CNG_smth_Gain_Q16), num2, 5); num2 = Inlines.silk_LSHIFT32(Inlines.silk_SQRT_APPROX(num2), 16); } else { num2 = Inlines.silk_SMULWW(num2, num2); num2 = Inlines.silk_SUB_LSHIFT32(Inlines.silk_SMULWW(sCNG.CNG_smth_Gain_Q16, sCNG.CNG_smth_Gain_Q16), num2, 5); num2 = Inlines.silk_LSHIFT32(Inlines.silk_SQRT_APPROX(num2), 8); } silk_CNG_exc(array2, 16, sCNG.CNG_exc_buf_Q14, num2, length, ref sCNG.rand_seed); NLSF.silk_NLSF2A(array, sCNG.CNG_smth_NLSF_Q15, psDec.LPC_order); Arrays.MemCopy(sCNG.CNG_synth_state, 0, array2, 0, 16); for (int i = 0; i < length; i++) { int num3 = 16 + i; int a = Inlines.silk_RSHIFT(psDec.LPC_order, 1); a = Inlines.silk_SMLAWB(a, array2[num3 - 1], array[0]); a = Inlines.silk_SMLAWB(a, array2[num3 - 2], array[1]); a = Inlines.silk_SMLAWB(a, array2[num3 - 3], array[2]); a = Inlines.silk_SMLAWB(a, array2[num3 - 4], array[3]); a = Inlines.silk_SMLAWB(a, array2[num3 - 5], array[4]); a = Inlines.silk_SMLAWB(a, array2[num3 - 6], array[5]); a = Inlines.silk_SMLAWB(a, array2[num3 - 7], array[6]); a = Inlines.silk_SMLAWB(a, array2[num3 - 8], array[7]); a = Inlines.silk_SMLAWB(a, array2[num3 - 9], array[8]); a = Inlines.silk_SMLAWB(a, array2[num3 - 10], array[9]); if (psDec.LPC_order == 16) { a = Inlines.silk_SMLAWB(a, array2[num3 - 11], array[10]); a = Inlines.silk_SMLAWB(a, array2[num3 - 12], array[11]); a = Inlines.silk_SMLAWB(a, array2[num3 - 13], array[12]); a = Inlines.silk_SMLAWB(a, array2[num3 - 14], array[13]); a = Inlines.silk_SMLAWB(a, array2[num3 - 15], array[14]); a = Inlines.silk_SMLAWB(a, array2[num3 - 16], array[15]); } array2[num3] = Inlines.silk_ADD_LSHIFT(array2[num3], a, 4); frame[frame_ptr + i] = Inlines.silk_ADD_SAT16(frame[frame_ptr + i], (short)Inlines.silk_RSHIFT_ROUND(array2[num3], 10)); } Arrays.MemCopy(array2, length, sCNG.CNG_synth_state, 0, 16); } else { Arrays.MemSetInt(sCNG.CNG_synth_state, 0, psDec.LPC_order); } } } internal static class CodeSigns { private static int silk_enc_map(int a) { return Inlines.silk_RSHIFT(a, 15) + 1; } private static int silk_dec_map(int a) { return Inlines.silk_LSHIFT(a, 1) - 1; } internal static void silk_encode_signs(EntropyCoder psRangeEnc, Span<byte> encodedData, Span<sbyte> pulses, int length, int signalType, int quantOffsetType, int[] sum_pulses) { byte[] array = new byte[2]; byte[] silk_sign_iCDF = Tables.silk_sign_iCDF; array[1] = 0; int num = 0; int num2 = Inlines.silk_SMULBB(7, Inlines.silk_ADD_LSHIFT(quantOffsetType, signalType, 1)); int num3 = num2; length = Inlines.silk_RSHIFT(length + 8, 4); for (num2 = 0; num2 < length; num2++) { int num4 = sum_pulses[num2]; if (num4 > 0) { array[0] = silk_sign_iCDF[num3 + Inlines.silk_min(num4 & 0x1F, 6)]; for (int i = num; i < num + 16; i++) { if (pulses[i] != 0) { psRangeEnc.enc_icdf(encodedData, silk_enc_map(pulses[i]), array, 8u); } } } num += 16; } } internal static void silk_decode_signs(EntropyCoder psRangeDec, ReadOnlySpan<byte> encodedData, short[] pulses, int length, int signalType, int quantOffsetType, int[] sum_pulses) { byte[] array = new byte[2]; byte[] silk_sign_iCDF = Tables.silk_sign_iCDF; array[1] = 0; int num = 0; int num2 = Inlines.silk_SMULBB(7, Inlines.silk_ADD_LSHIFT(quantOffsetType, signalType, 1)); int num3 = num2; length = Inlines.silk_RSHIFT(length + 8, 4); for (num2 = 0; num2 < length; num2++) { int num4 = sum_pulses[num2]; if (num4 > 0) { array[0] = silk_sign_iCDF[num3 + Inlines.silk_min(num4 & 0x1F, 6)]; for (int i = 0; i < 16; i++) { if (pulses[num + i] > 0) { pulses[num + i] *= (short)silk_dec_map(psRangeDec.dec_icdf(encodedData, array, 8u)); } } } num += 16; } } } internal static class CorrelateMatrix { internal static void silk_corrVector(short[] x, int x_ptr, short[] t, int t_ptr, int L, int order, int[] Xt, int rshifts) { int num = x_ptr + order - 1; if (rshifts > 0) { for (int i = 0; i < order; i++) { int num2 = 0; for (int j = 0; j < L; j++) { num2 += Inlines.silk_RSHIFT32(Inlines.silk_SMULBB(x[num + j], t[t_ptr + j]), rshifts); } Xt[i] = num2; num--; } } else { for (int i = 0; i < order; i++) { Xt[i] = Inlines.silk_inner_prod(x, num, t, t_ptr, L); num--; } } } internal static void silk_corrMatrix(short[] x, int x_ptr, int L, int order, int head_room, int[] XX, int XX_ptr, BoxedValueInt rshifts) { SumSqrShift.silk_sum_sqr_shift(out var energy, out var shift, x, x_ptr, L + order - 1); int num = Inlines.silk_max(head_room - Inlines.silk_CLZ32(energy), 0); energy = Inlines.silk_RSHIFT32(energy, num); shift += num; for (int i = x_ptr; i < x_ptr + order - 1; i++) { energy -= Inlines.silk_RSHIFT32(Inlines.silk_SMULBB(x[i], x[i]), shift); } if (shift < rshifts.Val) { energy = Inlines.silk_RSHIFT32(energy, rshifts.Val - shift); shift = rshifts.Val; } Inlines.MatrixSet(XX, XX_ptr, 0, 0, order, energy); int num2 = x_ptr + order - 1; for (int j = 1; j < order; j++) { energy = Inlines.silk_SUB32(energy, Inlines.silk_RSHIFT32(Inlines.silk_SMULBB(x[num2 + L - j], x[num2 + L - j]), shift)); energy = Inlines.silk_ADD32(energy, Inlines.silk_RSHIFT32(Inlines.silk_SMULBB(x[num2 - j], x[num2 - j]), shift)); Inlines.MatrixSet(XX, XX_ptr, j, j, order, energy); } int num3 = x_ptr + order - 2; if (shift > 0) { for (int k = 1; k < order; k++) { energy = 0; for (int i = 0; i < L; i++) { energy += Inlines.silk_RSHIFT32(Inlines.silk_SMULBB(x[num2 + i], x[num3 + i]), shift); } Inlines.MatrixSet(XX, XX_ptr, k, 0, order, energy); Inlines.MatrixSet(XX, XX_ptr, 0, k, order, energy); for (int j = 1; j < order - k; j++) { energy = Inlines.silk_SUB32(energy, Inlines.silk_RSHIFT32(Inlines.silk_SMULBB(x[num2 + L - j], x[num3 + L - j]), shift)); energy = Inlines.silk_ADD32(energy, Inlines.silk_RSHIFT32(Inlines.silk_SMULBB(x[num2 - j], x[num3 - j]), shift)); Inlines.MatrixSet(XX, XX_ptr, k + j, j, order, energy); Inlines.MatrixSet(XX, XX_ptr, j, k + j, order, energy); } num3--; } } else { for (int k = 1; k < order; k++) { energy = Inlines.silk_inner_prod(x, num2, x, num3, L); Inlines.MatrixSet(XX, XX_ptr, k, 0, order, energy); Inlines.MatrixSet(XX, XX_ptr, 0, k, order, energy); for (int j = 1; j < order - k; j++) { energy = Inlines.silk_SUB32(energy, Inlines.silk_SMULBB(x[num2 + L - j], x[num3 + L - j])); energy = Inlines.silk_SMLABB(energy, x[num2 - j], x[num3 - j]); Inlines.MatrixSet(XX, XX_ptr, k + j, j, order, energy); Inlines.MatrixSet(XX, XX_ptr, j, k + j, order, energy); } num3--; } } rshifts.Val = shift; } } internal static class DecodeAPI { internal static int silk_InitDecoder(SilkDecoder decState) { decState.Reset(); int result = SilkError.SILK_NO_ERROR; SilkChannelDecoder[] channel_state = decState.channel_state; for (int i = 0; i < 2; i++) { result = channel_state[i].silk_init_decoder(); } decState.sStereo.Reset(); decState.prev_decode_only_middle = 0; return result; } internal static int silk_Decode(SilkDecoder psDec, DecControlState decControl, ReadOnlySpan<byte> frameData, int lostFlag, int newPacketFlag, EntropyCoder psRangeDec, Span<short> samplesOut, int samplesOut_ptr, out int nSamplesOut) { int num = 0; int num2 = SilkError.SILK_NO_ERROR; BoxedValueInt boxedValueInt = new BoxedValueInt(); int[] array = new int[2]; int[] array2 = new int[2]; SilkChannelDecoder[] channel_state = psDec.channel_state; nSamplesOut = 0; if (newPacketFlag != 0) { for (int i = 0; i < decControl.nChannelsInternal; i++) { channel_state[i].nFramesDecoded = 0; } } if (decControl.nChannelsInternal > psDec.nChannelsInternal) { num2 += channel_state[1].silk_init_decoder(); } int num3 = ((decControl.nChannelsInternal == 1 && psDec.nChannelsInternal == 2 && decControl.internalSampleRate == 1000 * channel_state[0].fs_kHz) ? 1 : 0); if (channel_state[0].nFramesDecoded == 0) { for (int i = 0; i < decControl.nChannelsInternal; i++) { if (decControl.payloadSize_ms == 0) { channel_state[i].nFramesPerPacket = 1; channel_state[i].nb_subfr = 2; } else if (decControl.payloadSize_ms == 10) { channel_state[i].nFramesPerPacket = 1; channel_state[i].nb_subfr = 2; } else if (decControl.payloadSize_ms == 20) { channel_state[i].nFramesPerPacket = 1; channel_state[i].nb_subfr = 4; } else if (decControl.payloadSize_ms == 40) { channel_state[i].nFramesPerPacket = 2; channel_state[i].nb_subfr = 4; } else { if (decControl.payloadSize_ms != 60) { return SilkError.SILK_DEC_INVALID_FRAME_SIZE; } channel_state[i].nFramesPerPacket = 3; channel_state[i].nb_subfr = 4; } int num4 = (decControl.internalSampleRate >> 10) + 1; if (num4 != 8 && num4 != 12 && num4 != 16) { return SilkError.SILK_DEC_INVALID_SAMPLING_FREQUENCY; } num2 += channel_state[i].silk_decoder_set_fs(num4, decControl.API_sampleRate); } } if (decControl.nChannelsAPI == 2 && decControl.nChannelsInternal == 2 && (psDec.nChannelsAPI == 1 || psDec.nChannelsInternal == 1)) { Arrays.MemSetShort(psDec.sStereo.pred_prev_Q13, 0, 2); Arrays.MemSetShort(psDec.sStereo.sSide, 0, 2); channel_state[1].resampler_state.Assign(channel_state[0].resampler_state); } psDec.nChannelsAPI = decControl.nChannelsAPI; psDec.nChannelsInternal = decControl.nChannelsInternal; if (decControl.API_sampleRate > 48000 || decControl.API_sampleRate < 8000) { return SilkError.SILK_DEC_INVALID_SAMPLING_FREQUENCY; } if (lostFlag != 1 && channel_state[0].nFramesDecoded == 0) { for (int i = 0; i < decControl.nChannelsInternal; i++) { for (int j = 0; j < channel_state[i].nFramesPerPacket; j++) { channel_state[i].VAD_flags[j] = psRangeDec.dec_bit_logp(frameData, 1u); } channel_state[i].LBRR_flag = psRangeDec.dec_bit_logp(frameData, 1u); } for (int i = 0; i < decControl.nChannelsInternal; i++) { Arrays.MemSetInt(channel_state[i].LBRR_flags, 0, 3); if (channel_state[i].LBRR_flag == 0) { continue; } if (channel_state[i].nFramesPerPacket == 1) { channel_state[i].LBRR_flags[0] = 1; continue; } int a = psRangeDec.dec_icdf(frameData, Tables.silk_LBRR_flags_iCDF_ptr[channel_state[i].nFramesPerPacket - 2], 8u) + 1; for (int j = 0; j < channel_state[i].nFramesPerPacket; j++) { channel_state[i].LBRR_flags[j] = Inlines.silk_RSHIFT(a, j) & 1; } } if (lostFlag == 0) { for (int j = 0; j < channel_state[0].nFramesPerPacket; j++) { for (int i = 0; i < decControl.nChannelsInternal; i++) { if (channel_state[i].LBRR_flags[j] == 0) { continue; } short[] pulses = new short[320]; if (decControl.nChannelsInternal == 2 && i == 0) { Stereo.silk_stereo_decode_pred(psRangeDec, frameData, array2); if (channel_state[1].LBRR_flags[j] == 0) { BoxedValueInt boxedValueInt2 = new BoxedValueInt(num); Stereo.silk_stereo_decode_mid_only(psRangeDec, frameData, boxedValueInt2); num = boxedValueInt2.Val; } } DecodeIndices.silk_decode_indices(condCoding: (j > 0 && channel_state[i].LBRR_flags[j - 1] != 0) ? 2 : 0, psDec: channel_state[i], psRangeDec: psRangeDec, frameData: frameData, FrameIndex: j, decode_LBRR: 1); DecodePulses.silk_decode_pulses(psRangeDec, frameData, pulses, channel_state[i].indices.signalType, channel_state[i].indices.quantOffsetType, channel_state[i].frame_length); } } } } if (decControl.nChannelsInternal == 2) { if (lostFlag == 0 || (lostFlag == 2 && channel_state[0].LBRR_flags[channel_state[0].nFramesDecoded] == 1)) { Stereo.silk_stereo_decode_pred(psRangeDec, frameData, array2); if ((lostFlag == 0 && channel_state[1].VAD_flags[channel_state[0].nFramesDecoded] == 0) || (lostFlag == 2 && channel_state[1].LBRR_flags[channel_state[0].nFramesDecoded] == 0)) { BoxedValueInt boxedValueInt3 = new BoxedValueInt(num); Stereo.silk_stereo_decode_mid_only(psRangeDec, frameData, boxedValueInt3); num = boxedValueInt3.Val; } else { num = 0; } } else { for (int i = 0; i < 2; i++) { array2[i] = psDec.sStereo.pred_prev_Q13[i]; } } } if (decControl.nChannelsInternal == 2 && num == 0 && psDec.prev_decode_only_middle == 1) { Arrays.MemSetShort(psDec.channel_state[1].outBuf, 0, 480); Arrays.MemSetInt(psDec.channel_state[1].sLPC_Q14_buf, 0, 16); psDec.channel_state[1].lagPrev = 100; psDec.channel_state[1].LastGainIndex = 10; psDec.channel_state[1].prevSignalType = 0; psDec.channel_state[1].first_frame_after_reset = 1; } int num5 = ((decControl.internalSampleRate * decControl.nChannelsInternal < decControl.API_sampleRate * decControl.nChannelsAPI) ? 1 : 0); Span<short> span; if (num5 != 0) { span = samplesOut; array[0] = samplesOut_ptr; array[1] = samplesOut_ptr + channel_state[0].frame_length + 2; } else { span = new short[decControl.nChannelsInternal * (channel_state[0].frame_length + 2)]; array[0] = 0; array[1] = channel_state[0].frame_length + 2; } int num6 = ((lostFlag != 0) ? ((psDec.prev_decode_only_middle == 0 || (decControl.nChannelsInternal == 2 && lostFlag == 2 && channel_state[1].LBRR_flags[channel_state[1].nFramesDecoded] == 1)) ? 1 : 0) : ((num == 0) ? 1 : 0)); for (int i = 0; i < decControl.nChannelsInternal; i++) { if (i == 0 || num6 != 0) { int num7 = channel_state[0].nFramesDecoded - i; int condCoding2 = ((num7 > 0) ? ((lostFlag == 2) ? ((channel_state[i].LBRR_flags[num7 - 1] != 0) ? 2 : 0) : ((i > 0 && psDec.prev_decode_only_middle != 0) ? 1 : 2)) : 0); num2 += channel_state[i].silk_decode_frame(psRangeDec, frameData, span, array[i] + 2, boxedValueInt, lostFlag, condCoding2); } else { Arrays.MemSetWithOffset<short>(span, 0, array[i] + 2, boxedValueInt.Val); } channel_state[i].nFramesDecoded++; } if (decControl.nChannelsAPI == 2 && decControl.nChannelsInternal == 2) { Stereo.silk_stereo_MS_to_LR(psDec.sStereo, span, array[0], span, array[1], array2, channel_state[0].fs_kHz, boxedValueInt.Val); } else { psDec.sStereo.sMid.AsSpan(0, 2).CopyTo(span.Slice(array[0])); span.Slice(array[0] + boxedValueInt.Val, 2).CopyTo(psDec.sStereo.sMid); } nSamplesOut = Inlines.silk_DIV32(boxedValueInt.Val * decControl.API_sampleRate, Inlines.silk_SMULBB(channel_state[0].fs_kHz, 1000)); Span<short> output; int num8; if (decControl.nChannelsAPI == 2) { output = new short[nSamplesOut]; num8 = 0; } else { output = samplesOut; num8 = samplesOut_ptr; } if (num5 != 0) { short[] array3 = new short[decControl.nChannelsInternal * (channel_state[0].frame_length + 2)]; samplesOut.Slice(samplesOut_ptr, decControl.nChannelsInternal * (channel_state[0].frame_length + 2)).CopyTo(array3); span = array3; array[0] = 0; array[1] = channel_state[0].frame_length + 2; } for (int i = 0; i < Inlines.silk_min(decControl.nChannelsAPI, decControl.nChannelsInternal); i++) { num2 += Resampler.silk_resampler(channel_state[i].resampler_state, output, num8, span, array[i] + 1, boxedValueInt.Val); if (decControl.nChannelsAPI == 2) { int num9 = samplesOut_ptr + i; for (int j = 0; j < nSamplesOut; j++) { samplesOut[num9 + 2 * j] = output[num8 + j]; } } } if (decControl.nChannelsAPI == 2 && decControl.nChannelsInternal == 1) { if (num3 != 0) { num2 += Resampler.silk_resampler(channel_state[1].resampler_state, output, num8, span, array[0] + 1, boxedValueInt.Val); for (int j = 0; j < nSamplesOut; j++) { samplesOut[samplesOut_ptr + 1 + 2 * j] = output[num8 + j]; } } else { for (int j = 0; j < nSamplesOut; j++) { samplesOut[samplesOut_ptr + 1 + 2 * j] = samplesOut[samplesOut_ptr + 2 * j]; } } } if (channel_state[0].prevSignalType == 2) { int[] array4 = new int[3] { 6, 4, 3 }; decControl.prevPitchLag = channel_state[0].lagPrev * array4[channel_state[0].fs_kHz - 8 >> 2]; } else { decControl.prevPitchLag = 0; } if (lostFlag == 1) { for (int j = 0; j < psDec.nChannelsInternal; j++) { psDec.channel_state[j].LastGainIndex = 10; } } else { psDec.prev_decode_only_middle = num; } return num2; } } internal static class DecodeCore { internal static void silk_decode_core(SilkChannelDecoder psDec, SilkDecoderControl psDecCtrl, Span<short> xq, int xq_ptr, short[] pulses) { int num = 0; short[] lTPCoef_Q = psDecCtrl.LTPCoef_Q14; short[] array = new short[psDec.ltp_mem_length]; int[] array2 = new int[psDec.ltp_mem_length + psDec.frame_length]; int[] array3 = new int[psDec.subfr_length]; int[] array4 = new int[psDec.subfr_length + 16]; int num2 = Tables.silk_Quantization_Offsets_Q10[psDec.indices.signalType >> 1][psDec.indices.quantOffsetType]; int num3 = ((psDec.indices.NLSFInterpCoef_Q2 < 4) ? 1 : 0); int seed = psDec.indices.Seed; for (int i = 0; i < psDec.frame_length; i++) { seed = Inlines.silk_RAND(seed); psDec.exc_Q14[i] = Inlines.silk_LSHIFT(pulses[i], 14); if (psDec.exc_Q14[i] > 0) { psDec.exc_Q14[i] -= 1280; } else if (psDec.exc_Q14[i] < 0) { psDec.exc_Q14[i] += 1280; } psDec.exc_Q14[i] += num2 << 4; if (seed < 0) { psDec.exc_Q14[i] = -psDec.exc_Q14[i]; } seed = Inlines.silk_ADD32_ovflw(seed, pulses[i]); } Arrays.MemCopy(psDec.sLPC_Q14_buf, 0, array4, 0, 16); int num4 = 0; int num5 = xq_ptr; int num6 = psDec.ltp_mem_length; for (int j = 0; j < psDec.nb_subfr; j++) { int[] array5 = array3; int num7 = 0; short[] array6 = psDecCtrl.PredCoef_Q12[j >> 1]; int num8 = j * 5; int num9 = psDec.indices.signalType; int b = Inlines.silk_RSHIFT(psDecCtrl.Gains_Q16[j], 6); int a = Inlines.silk_INVERSE32_varQ(psDecCtrl.Gains_Q16[j], 47); int num10; if (psDecCtrl.Gains_Q16[j] != psDec.prev_gain_Q16) { num10 = Inlines.silk_DIV32_varQ(psDec.prev_gain_Q16, psDecCtrl.Gains_Q16[j], 16); for (int i = 0; i < 16; i++) { array4[i] = Inlines.silk_SMULWW(num10, array4[i]); } } else { num10 = 65536; } psDec.prev_gain_Q16 = psDecCtrl.Gains_Q16[j]; if (psDec.lossCnt != 0 && psDec.prevSignalType == 2 && psDec.indices.signalType != 2 && j < 2) { Arrays.MemSetWithOffset(lTPCoef_Q, (short)0, num8, 5); lTPCoef_Q[num8 + 2] = 4096; num9 = 2; psDecCtrl.pitchL[j] = psDec.lagPrev; } if (num9 == 2) { num = psDecCtrl.pitchL[j]; if (j == 0 || (j == 2 && num3 != 0)) { int num11 = psDec.ltp_mem_length - num - psDec.LPC_order - 2; if (j == 2) { xq.Slice(xq_ptr, 2 * psDec.subfr_length).CopyTo(psDec.outBuf.AsSpan(psDec.ltp_mem_length)); } Filters.silk_LPC_analysis_filter(array, num11, psDec.outBuf, num11 + j * psDec.subfr_length, array6, 0, psDec.ltp_mem_length - num11, psDec.LPC_order); if (j == 0) { a = Inlines.silk_LSHIFT(Inlines.silk_SMULWB(a, psDecCtrl.LTP_scale_Q14), 2); } for (int i = 0; i < num + 2; i++) { array2[num6 - i - 1] = Inlines.silk_SMULWB(a, array[psDec.ltp_mem_length - i - 1]); } } else if (num10 != 65536) { for (int i = 0; i < num + 2; i++) { array2[num6 - i - 1] = Inlines.silk_SMULWW(num10, array2[num6 - i - 1]); } } } if (num9 == 2) { int num12 = num6 - num + 2; for (int i = 0; i < psDec.subfr_length; i++) { int a2 = 2; a2 = Inlines.silk_SMLAWB(a2, array2[num12], lTPCoef_Q[num8]); a2 = Inlines.silk_SMLAWB(a2, array2[num12 - 1], lTPCoef_Q[num8 + 1]); a2 = Inlines.silk_SMLAWB(a2, array2[num12 - 2], lTPCoef_Q[num8 + 2]); a2 = Inlines.silk_SMLAWB(a2, array2[num12 - 3], lTPCoef_Q[num8 + 3]); a2 = Inlines.silk_SMLAWB(a2, array2[num12 - 4], lTPCoef_Q[num8 + 4]); num12++; array5[num7 + i] = Inlines.silk_ADD_LSHIFT32(psDec.exc_Q14[num4 + i], a2, 1); array2[num6] = Inlines.silk_LSHIFT(array5[num7 + i], 1); num6++; } } else { array5 = psDec.exc_Q14; num7 = num4; } for (int i = 0; i < psDec.subfr_length; i++) { int a3 = Inlines.silk_RSHIFT(psDec.LPC_order, 1); a3 = Inlines.silk_SMLAWB(a3, array4[16 + i - 1], array6[0]); a3 = Inlines.silk_SMLAWB(a3, array4[16 + i - 2], array6[1]); a3 = Inlines.silk_SMLAWB(a3, array4[16 + i - 3], array6[2]); a3 = Inlines.silk_SMLAWB(a3, array4[16 + i - 4], array6[3]); a3 = Inlines.silk_SMLAWB(a3, array4[16 + i - 5], array6[4]); a3 = Inlines.silk_SMLAWB(a3, array4[16 + i - 6], array6[5]); a3 = Inlines.silk_SMLAWB(a3, array4[16 + i - 7], array6[6]); a3 = Inlines.silk_SMLAWB(a3, array4[16 + i - 8], array6[7]); a3 = Inlines.silk_SMLAWB(a3, array4[16 + i - 9], array6[8]); a3 = Inlines.silk_SMLAWB(a3, array4[16 + i - 10], array6[9]); if (psDec.LPC_order == 16) { a3 = Inlines.silk_SMLAWB(a3, array4[16 + i - 11], array6[10]); a3 = Inlines.silk_SMLAWB(a3, array4[16 + i - 12], array6[11]); a3 = Inlines.silk_SMLAWB(a3, array4[16 + i - 13], array6[12]); a3 = Inlines.silk_SMLAWB(a3, array4[16 + i - 14], array6[13]); a3 = Inlines.silk_SMLAWB(a3, array4[16 + i - 15], array6[14]); a3 = Inlines.silk_SMLAWB(a3, array4[16 + i - 16], array6[15]); } array4[16 + i] = Inlines.silk_ADD_LSHIFT32(array5[num7 + i], a3, 4); xq[num5 + i] = (short)Inlines.silk_SAT16(Inlines.silk_RSHIFT_ROUND(Inlines.silk_SMULWW(array4[16 + i], b), 8)); } Arrays.MemCopy(array4, psDec.subfr_length, array4, 0, 16); num4 += psDec.subfr_length; num5 += psDec.subfr_length; } Arrays.MemCopy(array4, 0, psDec.sLPC_Q14_buf, 0, 16); } } internal static class DecodeIndices { internal static void silk_decode_indices(SilkChannelDecoder psDec, EntropyCoder psRangeDec, ReadOnlySpan<byte> frameData, int FrameIndex, int decode_LBRR, int condCoding) { short[] array = new short[psDec.LPC_order]; byte[] array2 = new byte[psDec.LPC_order]; int num = ((decode_LBRR == 0 && psDec.VAD_flags[FrameIndex] == 0) ? psRangeDec.dec_icdf(frameData, Tables.silk_type_offset_no_VAD_iCDF, 8u) : (psRangeDec.dec_icdf(frameData, Tables.silk_type_offset_VAD_iCDF, 8u) + 2)); psDec.indices.signalType = (sbyte)Inlines.silk_RSHIFT(num, 1); psDec.indices.quantOffsetType = (sbyte)(num & 1); if (condCoding == 2) { psDec.indices.GainsIndices[0] = (sbyte)psRangeDec.dec_icdf(frameData, Tables.silk_delta_gain_iCDF, 8u); } else { psDec.indices.GainsIndices[0] = (sbyte)Inlines.silk_LSHIFT(psRangeDec.dec_icdf(frameData, Tables.silk_gain_iCDF[psDec.indices.signalType], 8u), 3); psDec.indices.GainsIndices[0] += (sbyte)psRangeDec.dec_icdf(frameData, Tables.silk_uniform8_iCDF, 8u); } for (int i = 1; i < psDec.nb_subfr; i++) { psDec.indices.GainsIndices[i] = (sbyte)psRangeDec.dec_icdf(frameData, Tables.silk_delta_gain_iCDF, 8u); } psDec.indices.NLSFIndices[0] = (sbyte)psRangeDec.dec_icdf(frameData, psDec.psNLSF_CB.CB1_iCDF, (psDec.indices.signalType >> 1) * psDec.psNLSF_CB.nVectors, 8u); NLSF.silk_NLSF_unpack(array, array2, psDec.psNLSF_CB, psDec.indices.NLSFIndices[0]); for (int i = 0; i < psDec.psNLSF_CB.order; i++) { num = psRangeDec.dec_icdf(frameData, psDec.psNLSF_CB.ec_iCDF, array[i], 8u); switch (num) { case 0: num -= psRangeDec.dec_icdf(frameData, Tables.silk_NLSF_EXT_iCDF, 8u); break; case 8: num += psRangeDec.dec_icdf(frameData, Tables.silk_NLSF_EXT_iCDF, 8u); break; } psDec.indices.NLSFIndices[i + 1] = (sbyte)(num - 4); } if (psDec.nb_subfr =
UserLibs/Concentus.Oggfile.dll
Decompiled a month agousing System; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Linq; using System.Reflection; using System.Resources; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using System.Text; using Concentus.Structs; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: AssemblyTrademark("")] [assembly: NeutralResourcesLanguage("en")] [assembly: TargetFramework(".NETFramework,Version=v4.5.2", FrameworkDisplayName = ".NET Framework 4.5.2")] [assembly: AssemblyCompany("Logan Stromberg")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyCopyright("Copyright © Logan Stromberg, Andrew Ward")] [assembly: AssemblyDescription("This package implements file streams which can be used to extract or encode Opus packets in an Ogg-formatted audio file (usually .opus), giving developers a very simple API to perform the task of reading or writing audio files that can be played universally. The Concentus library is used to encode/decode the opus packets automatically. The codec can optionally be accelerated by also referencing the Concentus.Native package.")] [assembly: AssemblyFileVersion("1.0.7.0")] [assembly: AssemblyInformationalVersion("1.0.7.0+27c3125205ddcd891822a398284b246636fafb94")] [assembly: AssemblyProduct("Concentus.Oggfile")] [assembly: AssemblyTitle("Concentus.Oggfile")] [assembly: AssemblyMetadata("RepositoryUrl", "https://github.com/lostromb/concentus.oggfile")] [assembly: AssemblyVersion("1.0.7.0")] namespace Concentus.Oggfile; internal static class BinaryHelpers { internal static void Int16ToByteArrayLittleEndian(short val, byte[] target, int targetOffset) { UInt16ToByteArrayLittleEndian((ushort)val, target, targetOffset); } internal static void UInt16ToByteArrayLittleEndian(ushort val, byte[] target, int targetOffset) { target[targetOffset + 1] = (byte)((uint)(val >> 8) & 0xFFu); target[targetOffset] = (byte)(val & 0xFFu); } internal static void Int16ToByteSpanLittleEndian(short val, ref Span<byte> target) { UInt16ToByteArraySpanEndian((ushort)val, ref target); } internal static void UInt16ToByteArraySpanEndian(ushort val, ref Span<byte> target) { target[1] = (byte)((uint)(val >> 8) & 0xFFu); target[0] = (byte)(val & 0xFFu); } internal static void Int16ToByteArrayBigEndian(short val, byte[] target, int targetOffset) { UInt16ToByteArrayBigEndian((ushort)val, target, targetOffset); } internal static void UInt16ToByteArrayBigEndian(ushort val, byte[] target, int targetOffset) { target[targetOffset] = (byte)((uint)(val >> 8) & 0xFFu); target[targetOffset + 1] = (byte)(val & 0xFFu); } internal static void Int16ToByteSpanBigEndian(short val, ref Span<byte> target) { UInt16ToByteSpanBigEndian((ushort)val, ref target); } internal static void UInt16ToByteSpanBigEndian(ushort val, ref Span<byte> target) { target[0] = (byte)((uint)(val >> 8) & 0xFFu); target[1] = (byte)(val & 0xFFu); } internal static void Int32ToByteArrayLittleEndian(int val, byte[] target, int targetOffset) { UInt32ToByteArrayLittleEndian((uint)val, target, targetOffset); } internal static void UInt32ToByteArrayLittleEndian(uint val, byte[] target, int targetOffset) { target[targetOffset + 3] = (byte)((val >> 24) & 0xFFu); target[targetOffset + 2] = (byte)((val >> 16) & 0xFFu); target[targetOffset + 1] = (byte)((val >> 8) & 0xFFu); target[targetOffset] = (byte)(val & 0xFFu); } internal static void Int32ToByteSpanLittleEndian(int val, ref Span<byte> target) { UInt32ToByteSpanLittleEndian((uint)val, ref target); } internal static void UInt32ToByteSpanLittleEndian(uint val, ref Span<byte> target) { target[3] = (byte)((val >> 24) & 0xFFu); target[2] = (byte)((val >> 16) & 0xFFu); target[1] = (byte)((val >> 8) & 0xFFu); target[0] = (byte)(val & 0xFFu); } internal static void UInt24ToByteArrayBigEndian(uint val, byte[] target, int targetOffset) { target[targetOffset] = (byte)((val >> 16) & 0xFFu); target[targetOffset + 1] = (byte)((val >> 8) & 0xFFu); target[targetOffset + 2] = (byte)(val & 0xFFu); } internal static void UInt24ToByteSpanBigEndian(uint val, ref Span<byte> target) { target[0] = (byte)((val >> 16) & 0xFFu); target[1] = (byte)((val >> 8) & 0xFFu); target[2] = (byte)(val & 0xFFu); } internal static void Int32ToByteArrayBigEndian(int val, byte[] target, int targetOffset) { UInt32ToByteArrayBigEndian((uint)val, target, targetOffset); } internal static void UInt32ToByteArrayBigEndian(uint val, byte[] target, int targetOffset) { target[targetOffset] = (byte)((val >> 24) & 0xFFu); target[targetOffset + 1] = (byte)((val >> 16) & 0xFFu); target[targetOffset + 2] = (byte)((val >> 8) & 0xFFu); target[targetOffset + 3] = (byte)(val & 0xFFu); } internal static void Int32ToByteSpanBigEndian(int val, ref Span<byte> target) { UInt32ToByteSpanBigEndian((uint)val, ref target); } internal static void UInt32ToByteSpanBigEndian(uint val, ref Span<byte> target) { target[0] = (byte)((val >> 24) & 0xFFu); target[1] = (byte)((val >> 16) & 0xFFu); target[2] = (byte)((val >> 8) & 0xFFu); target[3] = (byte)(val & 0xFFu); } internal static void Int64ToByteArrayLittleEndian(long val, byte[] target, int targetOffset) { UInt64ToByteArrayLittleEndian((ulong)val, target, targetOffset); } internal static void UInt64ToByteArrayLittleEndian(ulong val, byte[] target, int targetOffset) { target[targetOffset + 7] = (byte)((val >> 56) & 0xFF); target[targetOffset + 6] = (byte)((val >> 48) & 0xFF); target[targetOffset + 5] = (byte)((val >> 40) & 0xFF); target[targetOffset + 4] = (byte)((val >> 32) & 0xFF); target[targetOffset + 3] = (byte)((val >> 24) & 0xFF); target[targetOffset + 2] = (byte)((val >> 16) & 0xFF); target[targetOffset + 1] = (byte)((val >> 8) & 0xFF); target[targetOffset] = (byte)(val & 0xFF); } internal static void Int64ToByteSpanLittleEndian(long val, ref Span<byte> target) { UInt64ToByteSpanLittleEndian((ulong)val, ref target); } internal static void UInt64ToByteSpanLittleEndian(ulong val, ref Span<byte> target) { target[7] = (byte)((val >> 56) & 0xFF); target[6] = (byte)((val >> 48) & 0xFF); target[5] = (byte)((val >> 40) & 0xFF); target[4] = (byte)((val >> 32) & 0xFF); target[3] = (byte)((val >> 24) & 0xFF); target[2] = (byte)((val >> 16) & 0xFF); target[1] = (byte)((val >> 8) & 0xFF); target[0] = (byte)(val & 0xFF); } internal static void Int64ToByteArrayBigEndian(long val, byte[] target, int targetOffset) { UInt64ToByteArrayBigEndian((ulong)val, target, targetOffset); } internal static void UInt64ToByteArrayBigEndian(ulong val, byte[] target, int targetOffset) { target[targetOffset] = (byte)((val >> 56) & 0xFF); target[targetOffset + 1] = (byte)((val >> 48) & 0xFF); target[targetOffset + 2] = (byte)((val >> 40) & 0xFF); target[targetOffset + 3] = (byte)((val >> 32) & 0xFF); target[targetOffset + 4] = (byte)((val >> 24) & 0xFF); target[targetOffset + 5] = (byte)((val >> 16) & 0xFF); target[targetOffset + 6] = (byte)((val >> 8) & 0xFF); target[targetOffset + 7] = (byte)(val & 0xFF); } internal static void Int64ToByteSpanBigEndian(long val, ref Span<byte> target) { UInt64ToByteSpanBigEndian((ulong)val, ref target); } internal static void UInt64ToByteSpanBigEndian(ulong val, ref Span<byte> target) { target[0] = (byte)((val >> 56) & 0xFF); target[1] = (byte)((val >> 48) & 0xFF); target[2] = (byte)((val >> 40) & 0xFF); target[3] = (byte)((val >> 32) & 0xFF); target[4] = (byte)((val >> 24) & 0xFF); target[5] = (byte)((val >> 16) & 0xFF); target[6] = (byte)((val >> 8) & 0xFF); target[7] = (byte)(val & 0xFF); } internal static short ByteArrayToInt16LittleEndian(byte[] source, int offset) { return (short)((short)(0 | (short)(source[offset + 1] << 8)) | (short)source[offset]); } internal static short ByteSpanToInt16LittleEndian(ref Span<byte> source) { return (short)((short)(0 | (short)(source[1] << 8)) | (short)source[0]); } internal static short ByteSpanToInt16LittleEndian(ref ReadOnlySpan<byte> source) { return (short)((short)(0 | (short)(source[1] << 8)) | (short)source[0]); } internal static ushort ByteArrayToUInt16LittleEndian(byte[] source, int offset) { return (ushort)((ushort)(0u | (ushort)(source[offset + 1] << 8)) | source[offset]); } internal static ushort ByteSpanToUInt16LittleEndian(ref Span<byte> source) { return (ushort)((ushort)(0u | (ushort)(source[1] << 8)) | source[0]); } internal static ushort ByteSpanToUInt16LittleEndian(ref ReadOnlySpan<byte> source) { return (ushort)((ushort)(0u | (ushort)(source[1] << 8)) | source[0]); } internal static short ByteArrayToInt16BigEndian(byte[] source, int offset) { return (short)((short)(0 | (short)(source[offset] << 8)) | (short)source[offset + 1]); } internal static short ByteSpanToInt16BigEndian(ref Span<byte> source) { return (short)((short)(0 | (short)(source[0] << 8)) | (short)source[1]); } internal static short ByteSpanToInt16BigEndian(ref ReadOnlySpan<byte> source) { return (short)((short)(0 | (short)(source[0] << 8)) | (short)source[1]); } internal static ushort ByteArrayToUInt16BigEndian(byte[] source, int offset) { return (ushort)((ushort)(0u | (ushort)(source[offset] << 8)) | source[offset + 1]); } internal static ushort ByteSpanToUInt16BigEndian(ref Span<byte> source) { return (ushort)((ushort)(0u | (ushort)(source[0] << 8)) | source[1]); } internal static ushort ByteSpanToUInt16BigEndian(ref ReadOnlySpan<byte> source) { return (ushort)((ushort)(0u | (ushort)(source[0] << 8)) | source[1]); } internal static int ByteArrayToInt32LittleEndian(byte[] source, int offset) { return 0 | (source[offset + 3] << 24) | (source[offset + 2] << 16) | (source[offset + 1] << 8) | source[offset]; } internal static int ByteSpanToInt32LittleEndian(ref Span<byte> source) { return 0 | (source[3] << 24) | (source[2] << 16) | (source[1] << 8) | source[0]; } internal static int ByteSpanToInt32LittleEndian(ref ReadOnlySpan<byte> source) { return 0 | (source[3] << 24) | (source[2] << 16) | (source[1] << 8) | source[0]; } internal static uint ByteArrayToUInt32LittleEndian(byte[] source, int offset) { return 0u | (uint)(source[offset + 3] << 24) | (uint)(source[offset + 2] << 16) | (uint)(source[offset + 1] << 8) | source[offset]; } internal static uint ByteSpanToUInt32LittleEndian(ref Span<byte> source) { return 0u | (uint)(source[3] << 24) | (uint)(source[2] << 16) | (uint)(source[1] << 8) | source[0]; } internal static uint ByteSpanToUInt32LittleEndian(ref ReadOnlySpan<byte> source) { return 0u | (uint)(source[3] << 24) | (uint)(source[2] << 16) | (uint)(source[1] << 8) | source[0]; } internal static uint ByteArrayToUInt24BigEndian(byte[] source, int offset) { return 0u | (uint)(source[offset] << 16) | (uint)(source[offset + 1] << 8) | source[offset + 2]; } internal static uint ByteSpanToUInt24BigEndian(ref Span<byte> source) { return 0u | (uint)(source[0] << 16) | (uint)(source[1] << 8) | source[2]; } internal static uint ByteSpanToUInt24BigEndian(ref ReadOnlySpan<byte> source) { return 0u | (uint)(source[0] << 16) | (uint)(source[1] << 8) | source[2]; } internal static int ByteArrayToInt32BigEndian(byte[] source, int offset) { return 0 | (source[offset] << 24) | (source[offset + 1] << 16) | (source[offset + 2] << 8) | source[offset + 3]; } internal static int ByteSpanToInt32BigEndian(ref Span<byte> source) { return 0 | (source[0] << 24) | (source[1] << 16) | (source[2] << 8) | source[3]; } internal static int ByteSpanToInt32BigEndian(ref ReadOnlySpan<byte> source) { return 0 | (source[0] << 24) | (source[1] << 16) | (source[2] << 8) | source[3]; } internal static uint ByteArrayToUInt32BigEndian(byte[] source, int offset) { return 0u | (uint)(source[offset] << 24) | (uint)(source[offset + 1] << 16) | (uint)(source[offset + 2] << 8) | source[offset + 3]; } internal static uint ByteSpanToUInt32BigEndian(ref Span<byte> source) { return 0u | (uint)(source[0] << 24) | (uint)(source[1] << 16) | (uint)(source[2] << 8) | source[3]; } internal static uint ByteSpanToUInt32BigEndian(ref ReadOnlySpan<byte> source) { return 0u | (uint)(source[0] << 24) | (uint)(source[1] << 16) | (uint)(source[2] << 8) | source[3]; } internal static long ByteArrayToInt64LittleEndian(byte[] source, int offset) { return (long)(0 | ((ulong)source[offset + 7] << 56) | ((ulong)source[offset + 6] << 48) | ((ulong)source[offset + 5] << 40) | ((ulong)source[offset + 4] << 32) | ((ulong)source[offset + 3] << 24) | ((ulong)source[offset + 2] << 16) | ((ulong)source[offset + 1] << 8) | source[offset]); } internal static long ByteSpanToInt64LittleEndian(ref Span<byte> source) { return (long)(0 | ((ulong)source[7] << 56) | ((ulong)source[6] << 48) | ((ulong)source[5] << 40) | ((ulong)source[4] << 32) | ((ulong)source[3] << 24) | ((ulong)source[2] << 16) | ((ulong)source[1] << 8) | source[0]); } internal static long ByteSpanToInt64LittleEndian(ref ReadOnlySpan<byte> source) { return (long)(0 | ((ulong)source[7] << 56) | ((ulong)source[6] << 48) | ((ulong)source[5] << 40) | ((ulong)source[4] << 32) | ((ulong)source[3] << 24) | ((ulong)source[2] << 16) | ((ulong)source[1] << 8) | source[0]); } internal static ulong ByteArrayToUInt64LittleEndian(byte[] source, int offset) { return 0 | ((ulong)source[offset + 7] << 56) | ((ulong)source[offset + 6] << 48) | ((ulong)source[offset + 5] << 40) | ((ulong)source[offset + 4] << 32) | ((ulong)source[offset + 3] << 24) | ((ulong)source[offset + 2] << 16) | ((ulong)source[offset + 1] << 8) | source[offset]; } internal static ulong ByteSpanToUInt64LittleEndian(ref Span<byte> source) { return 0 | ((ulong)source[7] << 56) | ((ulong)source[6] << 48) | ((ulong)source[5] << 40) | ((ulong)source[4] << 32) | ((ulong)source[3] << 24) | ((ulong)source[2] << 16) | ((ulong)source[1] << 8) | source[0]; } internal static ulong ByteSpanToUInt64LittleEndian(ref ReadOnlySpan<byte> source) { return 0 | ((ulong)source[7] << 56) | ((ulong)source[6] << 48) | ((ulong)source[5] << 40) | ((ulong)source[4] << 32) | ((ulong)source[3] << 24) | ((ulong)source[2] << 16) | ((ulong)source[1] << 8) | source[0]; } internal static long ByteArrayToInt64BigEndian(byte[] source, int offset) { return (long)(0 | ((ulong)source[offset] << 56) | ((ulong)source[offset + 1] << 48) | ((ulong)source[offset + 2] << 40) | ((ulong)source[offset + 3] << 32) | ((ulong)source[offset + 4] << 24) | ((ulong)source[offset + 5] << 16) | ((ulong)source[offset + 6] << 8) | source[offset + 7]); } internal static long ByteSpanToInt64BigEndian(ref Span<byte> source) { return (long)(0 | ((ulong)source[0] << 56) | ((ulong)source[1] << 48) | ((ulong)source[2] << 40) | ((ulong)source[3] << 32) | ((ulong)source[4] << 24) | ((ulong)source[5] << 16) | ((ulong)source[6] << 8) | source[7]); } internal static long ByteSpanToInt64BigEndian(ref ReadOnlySpan<byte> source) { return (long)(0 | ((ulong)source[0] << 56) | ((ulong)source[1] << 48) | ((ulong)source[2] << 40) | ((ulong)source[3] << 32) | ((ulong)source[4] << 24) | ((ulong)source[5] << 16) | ((ulong)source[6] << 8) | source[7]); } internal static ulong ByteArrayToUInt64BigEndian(byte[] source, int offset) { return 0 | ((ulong)source[offset] << 56) | ((ulong)source[offset + 1] << 48) | ((ulong)source[offset + 2] << 40) | ((ulong)source[offset + 3] << 32) | ((ulong)source[offset + 4] << 24) | ((ulong)source[offset + 5] << 16) | ((ulong)source[offset + 6] << 8) | source[offset + 7]; } internal static ulong ByteSpanToUInt64BigEndian(ref Span<byte> source) { return 0 | ((ulong)source[0] << 56) | ((ulong)source[1] << 48) | ((ulong)source[2] << 40) | ((ulong)source[3] << 32) | ((ulong)source[4] << 24) | ((ulong)source[5] << 16) | ((ulong)source[6] << 8) | source[7]; } internal static ulong ByteSpanToUInt64BigEndian(ref ReadOnlySpan<byte> source) { return 0 | ((ulong)source[0] << 56) | ((ulong)source[1] << 48) | ((ulong)source[2] << 40) | ((ulong)source[3] << 32) | ((ulong)source[4] << 24) | ((ulong)source[5] << 16) | ((ulong)source[6] << 8) | source[7]; } } internal class BufferedReadStream : Stream { private const int DEFAULT_INITIAL_SIZE = 32768; private const int DEFAULT_MAX_SIZE = 262144; private Stream _baseStream; private StreamReadBuffer _buffer; private long _readPosition; public bool CloseBaseStream { get; set; } public bool MinimalRead { get { return _buffer.MinimalRead; } set { _buffer.MinimalRead = value; } } public int MaxBufferSize { get { return _buffer.MaxSize; } set { CheckLock(); _buffer.MaxSize = value; } } public long BufferBaseOffset => _buffer.BaseOffset; public int BufferBytesFilled => _buffer.BytesFilled; public override bool CanRead => true; public override bool CanSeek => true; public override bool CanWrite => false; public override long Length => _baseStream.Length; public override long Position { get { return _readPosition; } set { Seek(value, SeekOrigin.Begin); } } public BufferedReadStream(Stream baseStream) : this(baseStream, 32768, 262144, minimalRead: false) { } public BufferedReadStream(Stream baseStream, bool minimalRead) : this(baseStream, 32768, 262144, minimalRead) { } public BufferedReadStream(Stream baseStream, int initialSize, int maxSize) : this(baseStream, initialSize, maxSize, minimalRead: false) { } public BufferedReadStream(Stream baseStream, int initialSize, int maxBufferSize, bool minimalRead) { if (baseStream == null) { throw new ArgumentNullException("baseStream"); } if (!baseStream.CanRead) { throw new ArgumentException("baseStream"); } if (maxBufferSize < 1) { maxBufferSize = 1; } if (initialSize < 1) { initialSize = 1; } if (initialSize > maxBufferSize) { initialSize = maxBufferSize; } _baseStream = baseStream; _buffer = new StreamReadBuffer(baseStream, initialSize, maxBufferSize, minimalRead); _buffer.MaxSize = maxBufferSize; _buffer.MinimalRead = minimalRead; } protected override void Dispose(bool disposing) { base.Dispose(disposing); if (disposing) { if (_buffer != null) { _buffer.Dispose(); _buffer = null; } _ = CloseBaseStream; } } public void TakeLock() { } private void CheckLock() { } public void ReleaseLock() { } public void Discard(int bytes) { CheckLock(); _buffer.DiscardThrough(_buffer.BaseOffset + bytes); } public void DiscardThrough(long offset) { CheckLock(); _buffer.DiscardThrough(offset); } public override void Flush() { } public override int ReadByte() { CheckLock(); int num = _buffer.ReadByte(Position); if (num > -1) { Seek(1L, SeekOrigin.Current); } return num; } public override int Read(byte[] buffer, int offset, int count) { CheckLock(); int num = _buffer.Read(Position, buffer, offset, count); Seek(num, SeekOrigin.Current); return num; } public override long Seek(long offset, SeekOrigin origin) { CheckLock(); switch (origin) { case SeekOrigin.Current: offset += Position; break; case SeekOrigin.End: offset += _baseStream.Length; break; } if (!_baseStream.CanSeek) { if (offset < _buffer.BaseOffset) { throw new InvalidOperationException("Cannot seek to before the start of the buffer!"); } if (offset > _buffer.BufferEndOffset) { throw new InvalidOperationException("Cannot seek to beyond the end of the buffer! Discard some bytes."); } } return _readPosition = offset; } public override void SetLength(long value) { throw new NotSupportedException(); } public override void Write(byte[] buffer, int offset, int count) { throw new NotSupportedException(); } } internal abstract class DataPacket { [Flags] protected enum PacketFlags : byte { IsResync = 1, IsEndOfStream = 2, IsShort = 4, HasGranuleCount = 8, User1 = 0x10, User2 = 0x20, User3 = 0x40, User4 = 0x80 } private ulong _bitBucket; private int _bitCount; private int _readBits; private byte _overflowBits; private PacketFlags _packetFlags; private long _granulePosition; private long _pageGranulePosition; private int _length; private int _granuleCount; private int _pageSequenceNumber; public bool IsResync { get { return GetFlag(PacketFlags.IsResync); } internal set { SetFlag(PacketFlags.IsResync, value); } } public long GranulePosition { get { return _granulePosition; } set { _granulePosition = value; } } public long PageGranulePosition { get { return _pageGranulePosition; } internal set { _pageGranulePosition = value; } } public int Length { get { return _length; } protected set { _length = value; } } public bool IsEndOfStream { get { return GetFlag(PacketFlags.IsEndOfStream); } internal set { SetFlag(PacketFlags.IsEndOfStream, value); } } public long BitsRead => _readBits; public int? GranuleCount { get { if (GetFlag(PacketFlags.HasGranuleCount)) { return _granuleCount; } return null; } set { if (value.HasValue) { _granuleCount = value.Value; SetFlag(PacketFlags.HasGranuleCount, value: true); } else { SetFlag(PacketFlags.HasGranuleCount, value: false); } } } internal int PageSequenceNumber { get { return _pageSequenceNumber; } set { _pageSequenceNumber = value; } } internal bool IsShort { get { return GetFlag(PacketFlags.IsShort); } private set { SetFlag(PacketFlags.IsShort, value); } } protected bool GetFlag(PacketFlags flag) { return (_packetFlags & flag) == flag; } protected void SetFlag(PacketFlags flag, bool value) { if (value) { _packetFlags |= flag; } else { _packetFlags &= (PacketFlags)(byte)(~(int)flag); } } protected DataPacket(int length) { Length = length; } protected abstract int ReadNextByte(); public virtual void Done() { } public ulong TryPeekBits(int count, out int bitsRead) { ulong num = 0uL; 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 num2 = ReadNextByte(); if (num2 == -1) { bitsRead = _bitCount; num = _bitBucket; _bitBucket = 0uL; _bitCount = 0; IsShort = true; return num; } _bitBucket = (ulong)((long)(num2 & 0xFF) << _bitCount) | _bitBucket; _bitCount += 8; if (_bitCount > 64) { _overflowBits = (byte)(num2 >> 72 - _bitCount); } } num = _bitBucket; if (count < 64) { num &= (ulong)((1L << count) - 1); } bitsRead = count; return num; } 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; } } protected void ResetBitReader() { _bitBucket = 0uL; _bitCount = 0; _readBits = 0; IsShort = false; } public ulong ReadBits(int count) { if (count == 0) { return 0uL; } int bitsRead; ulong result = TryPeekBits(count, out bitsRead); SkipBits(count); return result; } public byte PeekByte() { int bitsRead; return (byte)TryPeekBits(8, out bitsRead); } public byte ReadByte() { return (byte)ReadBits(8); } public byte[] ReadBytes(int count) { List<byte> list = new List<byte>(count); while (list.Count < count) { list.Add(ReadByte()); } return list.ToArray(); } public int Read(byte[] buffer, int index, int count) { if (index < 0 || index + count > buffer.Length) { throw new ArgumentOutOfRangeException("index"); } for (int i = 0; i < count; i++) { int bitsRead; byte b = (byte)TryPeekBits(8, out bitsRead); if (bitsRead == 0) { return i; } buffer[index++] = b; SkipBits(8); } return count; } public bool ReadBit() { return ReadBits(1) == 1; } public short ReadInt16() { return (short)ReadBits(16); } public int ReadInt32() { return (int)ReadBits(32); } public long ReadInt64() { return (long)ReadBits(64); } public ushort ReadUInt16() { return (ushort)ReadBits(16); } public uint ReadUInt32() { return (uint)ReadBits(32); } public ulong ReadUInt64() { return ReadBits(64); } public void SkipBytes(int count) { SkipBits(count * 8); } } internal interface IContainerReader : IDisposable { int[] StreamSerials { get; } bool CanSeek { get; } long WasteBits { get; } int PagesRead { get; } event EventHandler<NewStreamEventArgs> NewStream; bool Init(); bool FindNextStream(); int GetTotalPageCount(); } internal interface IPacketProvider : IDisposable { int StreamSerial { get; } bool CanSeek { get; } long ContainerBits { get; } event EventHandler<ParameterChangeEventArgs> ParameterChange; int GetTotalPageCount(); DataPacket GetNextPacket(); DataPacket PeekNextPacket(); DataPacket GetPacket(int packetIndex); long GetGranuleCount(); DataPacket FindPacket(long granulePos, Func<DataPacket, DataPacket, int> packetGranuleCountCallback); void SeekToPacket(DataPacket packet, int preRoll); } internal class NewStreamEventArgs : EventArgs { public IPacketProvider PacketProvider { get; private set; } public bool IgnoreStream { get; set; } public NewStreamEventArgs(IPacketProvider packetProvider) { if (packetProvider == null) { throw new ArgumentNullException("packetProvider"); } PacketProvider = packetProvider; } } internal class OggContainerReader : IContainerReader, IDisposable { private class PageHeader { public int StreamSerial { get; set; } public PageFlags Flags { get; set; } public long GranulePosition { get; set; } public int SequenceNumber { get; set; } public long DataOffset { get; set; } public int[] PacketSizes { get; set; } public bool LastPacketContinues { get; set; } public bool IsResync { get; set; } } private Crc _crc = new Crc(); private BufferedReadStream _stream; private Dictionary<int, PacketReader> _packetReaders; private List<int> _disposedStreamSerials; private long _nextPageOffset; private int _pageCount; private byte[] _readBuffer = new byte[65025]; private long _containerBits; private long _wasteBits; public int[] StreamSerials => _packetReaders.Keys.ToArray(); public int PagesRead => _pageCount; public bool CanSeek => _stream.CanSeek; public long WasteBits => _wasteBits; public event EventHandler<NewStreamEventArgs> NewStream; public OggContainerReader(Stream stream, bool closeOnDispose) { _packetReaders = new Dictionary<int, PacketReader>(); _disposedStreamSerials = new List<int>(); _stream = (stream as BufferedReadStream) ?? new BufferedReadStream(stream) { CloseBaseStream = closeOnDispose }; } public bool Init() { _stream.TakeLock(); try { return GatherNextPage() != -1; } finally { _stream.ReleaseLock(); } } public void Dispose() { int[] streamSerials = StreamSerials; foreach (int key in streamSerials) { _packetReaders[key].Dispose(); } _nextPageOffset = 0L; _containerBits = 0L; _wasteBits = 0L; _stream.Dispose(); } public IPacketProvider GetStream(int streamSerial) { if (!_packetReaders.TryGetValue(streamSerial, out var value)) { throw new ArgumentOutOfRangeException("streamSerial"); } return value; } public bool FindNextStream() { if (!CanSeek) { throw new InvalidOperationException(); } int count = _packetReaders.Count; while (_packetReaders.Count == count) { _stream.TakeLock(); try { if (GatherNextPage() == -1) { break; } } finally { _stream.ReleaseLock(); } } return count > _packetReaders.Count; } public int GetTotalPageCount() { if (!CanSeek) { throw new InvalidOperationException(); } while (true) { _stream.TakeLock(); try { if (GatherNextPage() == -1) { break; } } finally { _stream.ReleaseLock(); } } return _pageCount; } private PageHeader ReadPageHeader(long position) { _stream.Seek(position, SeekOrigin.Begin); if (_stream.Read(_readBuffer, 0, 27) != 27) { return null; } if (_readBuffer[0] != 79 || _readBuffer[1] != 103 || _readBuffer[2] != 103 || _readBuffer[3] != 83) { return null; } if (_readBuffer[4] != 0) { return null; } PageHeader pageHeader = new PageHeader(); pageHeader.Flags = (PageFlags)_readBuffer[5]; pageHeader.GranulePosition = BitConverter.ToInt64(_readBuffer, 6); pageHeader.StreamSerial = BitConverter.ToInt32(_readBuffer, 14); pageHeader.SequenceNumber = BitConverter.ToInt32(_readBuffer, 18); uint checkCrc = BitConverter.ToUInt32(_readBuffer, 22); _crc.Reset(); for (int i = 0; i < 22; i++) { _crc.Update(_readBuffer[i]); } _crc.Update(0); _crc.Update(0); _crc.Update(0); _crc.Update(0); _crc.Update(_readBuffer[26]); int num = _readBuffer[26]; if (_stream.Read(_readBuffer, 0, num) != num) { return null; } List<int> list = new List<int>(num); int num2 = 0; int num3 = 0; for (int j = 0; j < num; j++) { byte b = _readBuffer[j]; _crc.Update(b); if (num3 == list.Count) { list.Add(0); } list[num3] += b; if (b < byte.MaxValue) { num3++; pageHeader.LastPacketContinues = false; } else { pageHeader.LastPacketContinues = true; } num2 += b; } pageHeader.PacketSizes = list.ToArray(); pageHeader.DataOffset = position + 27 + num; if (_stream.Read(_readBuffer, 0, num2) != num2) { return null; } for (int k = 0; k < num2; k++) { _crc.Update(_readBuffer[k]); } if (_crc.Test(checkCrc)) { _containerBits += 8 * (27 + num); _pageCount++; return pageHeader; } return null; } private PageHeader FindNextPageHeader() { long num = _nextPageOffset; bool isResync = false; PageHeader pageHeader; while ((pageHeader = ReadPageHeader(num)) == null) { isResync = true; _wasteBits += 8L; num = (_stream.Position = num + 1); int num3 = 0; do { switch (_stream.ReadByte()) { case 79: if (_stream.ReadByte() == 103 && _stream.ReadByte() == 103 && _stream.ReadByte() == 83) { num += num3; goto end_IL_0032; } _stream.Seek(-3L, SeekOrigin.Current); break; case -1: return null; } _wasteBits += 8L; continue; end_IL_0032: break; } while (++num3 < 65536); if (num3 == 65536) { return null; } } pageHeader.IsResync = isResync; _nextPageOffset = pageHeader.DataOffset; for (int i = 0; i < pageHeader.PacketSizes.Length; i++) { _nextPageOffset += pageHeader.PacketSizes[i]; } return pageHeader; } private bool AddPage(PageHeader hdr) { if (!_packetReaders.TryGetValue(hdr.StreamSerial, out var value)) { value = new PacketReader(this, hdr.StreamSerial); } value.ContainerBits += _containerBits; _containerBits = 0L; bool isContinued = hdr.PacketSizes.Length == 1 && hdr.LastPacketContinues; bool isContinuation = (hdr.Flags & PageFlags.ContinuesPacket) == PageFlags.ContinuesPacket; bool isEndOfStream = false; bool isResync = hdr.IsResync; long num = hdr.DataOffset; int num2 = hdr.PacketSizes.Length; int[] packetSizes = hdr.PacketSizes; foreach (int num3 in packetSizes) { Packet packet = new Packet(this, num, num3) { PageGranulePosition = hdr.GranulePosition, IsEndOfStream = isEndOfStream, PageSequenceNumber = hdr.SequenceNumber, IsContinued = isContinued, IsContinuation = isContinuation, IsResync = isResync }; value.AddPacket(packet); num += num3; isContinuation = false; isResync = false; if (--num2 == 1) { isContinued = hdr.LastPacketContinues; isEndOfStream = (hdr.Flags & PageFlags.EndOfStream) == PageFlags.EndOfStream; } } if (!_packetReaders.ContainsKey(hdr.StreamSerial)) { _packetReaders.Add(hdr.StreamSerial, value); return true; } return false; } private int GatherNextPage() { PageHeader pageHeader; while (true) { pageHeader = FindNextPageHeader(); if (pageHeader == null) { return -1; } if (!_disposedStreamSerials.Contains(pageHeader.StreamSerial)) { if (!AddPage(pageHeader)) { break; } EventHandler<NewStreamEventArgs> newStream = this.NewStream; if (newStream == null) { break; } NewStreamEventArgs newStreamEventArgs = new NewStreamEventArgs(_packetReaders[pageHeader.StreamSerial]); newStream(this, newStreamEventArgs); if (!newStreamEventArgs.IgnoreStream) { break; } _packetReaders[pageHeader.StreamSerial].Dispose(); } } return pageHeader.StreamSerial; } internal void DisposePacketReader(PacketReader packetReader) { _disposedStreamSerials.Add(packetReader.StreamSerial); _packetReaders.Remove(packetReader.StreamSerial); } internal int PacketReadByte(long offset) { _stream.TakeLock(); try { _stream.Position = offset; return _stream.ReadByte(); } finally { _stream.ReleaseLock(); } } internal void PacketDiscardThrough(long offset) { _stream.TakeLock(); try { _stream.DiscardThrough(offset); } finally { _stream.ReleaseLock(); } } internal void GatherNextPage(int streamSerial) { if (!_packetReaders.ContainsKey(streamSerial)) { throw new ArgumentOutOfRangeException("streamSerial"); } int num; do { _stream.TakeLock(); try { if (_packetReaders[streamSerial].HasEndOfStream) { break; } num = GatherNextPage(); if (num != -1) { continue; } foreach (KeyValuePair<int, PacketReader> packetReader in _packetReaders) { if (!packetReader.Value.HasEndOfStream) { packetReader.Value.SetEndOfStream(); } } break; } finally { _stream.ReleaseLock(); } } while (num != streamSerial); } } internal class Crc { private const uint CRC32_POLY = 79764919u; private static uint[] crcTable; private uint _crc; public uint Value => _crc; static Crc() { 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); } crcTable[num] = num2; } } public Crc() { Reset(); } public void Reset() { _crc = 0u; } public void Update(int nextVal) { _crc = (_crc << 8) ^ crcTable[nextVal ^ (_crc >> 24)]; } public bool Test(uint checkCrc) { return _crc == checkCrc; } } internal class Packet : DataPacket { private long _offset; private int _length; private int _curOfs; private Packet _mergedPacket; private Packet _next; private Packet _prev; private OggContainerReader _containerReader; internal Packet Next { get { return _next; } set { _next = value; } } internal Packet Prev { get { return _prev; } set { _prev = value; } } internal bool IsContinued { get { return GetFlag(PacketFlags.User1); } set { SetFlag(PacketFlags.User1, value); } } internal bool IsContinuation { get { return GetFlag(PacketFlags.User2); } set { SetFlag(PacketFlags.User2, value); } } internal Packet(OggContainerReader containerReader, long streamOffset, int length) : base(length) { _containerReader = containerReader; _offset = streamOffset; _length = length; _curOfs = 0; } internal void MergeWith(DataPacket continuation) { if (!(continuation is Packet mergedPacket)) { throw new ArgumentException("Incorrect packet type!"); } base.Length += continuation.Length; if (_mergedPacket == null) { _mergedPacket = mergedPacket; } else { _mergedPacket.MergeWith(continuation); } base.PageGranulePosition = continuation.PageGranulePosition; base.PageSequenceNumber = continuation.PageSequenceNumber; } internal void Reset() { _curOfs = 0; ResetBitReader(); if (_mergedPacket != null) { _mergedPacket.Reset(); } } protected override int ReadNextByte() { if (_curOfs == _length) { if (_mergedPacket == null) { return -1; } return _mergedPacket.ReadNextByte(); } int num = _containerReader.PacketReadByte(_offset + _curOfs); if (num != -1) { _curOfs++; } return num; } public override void Done() { if (_mergedPacket != null) { _mergedPacket.Done(); } else { _containerReader.PacketDiscardThrough(_offset + _length); } } } [DebuggerTypeProxy(typeof(DebugView))] internal class PacketReader : IPacketProvider, IDisposable { internal class DebugView { private PacketReader _reader; private Packet _last; private Packet _first; private Packet[] _packetList = new Packet[0]; public OggContainerReader Container => _reader._container; public int StreamSerial => _reader._streamSerial; public bool EndOfStreamFound => _reader._eosFound; public int CurrentPacketIndex { get { if (_reader._current == null) { return -1; } return Array.IndexOf(Packets, _reader._current); } } public Packet[] Packets { get { if (_reader._last == _last && _reader._first == _first) { return _packetList; } _last = _reader._last; _first = _reader._first; List<Packet> list = new List<Packet>(); for (Packet packet = _first; packet != null; packet = packet.Next) { list.Add(packet); } _packetList = list.ToArray(); return _packetList; } } public DebugView(PacketReader reader) { if (reader == null) { throw new ArgumentNullException("reader"); } _reader = reader; } } private OggContainerReader _container; private int _streamSerial; private bool _eosFound; private Packet _first; private Packet _current; private Packet _last; private object _packetLock = new object(); internal bool HasEndOfStream => _eosFound; public int StreamSerial => _streamSerial; public long ContainerBits { get; set; } public bool CanSeek => _container.CanSeek; public event EventHandler<ParameterChangeEventArgs> ParameterChange; internal PacketReader(OggContainerReader container, int streamSerial) { _container = container; _streamSerial = streamSerial; } public void Dispose() { _eosFound = true; _container.DisposePacketReader(this); _container = null; _current = null; if (_first != null) { Packet packet = _first; _first = null; while (packet.Next != null) { Packet next = packet.Next; packet.Next = null; packet = next; packet.Prev = null; } packet = null; } _last = null; } internal void AddPacket(Packet packet) { lock (_packetLock) { if (_eosFound) { return; } if (packet.IsResync) { packet.IsContinuation = false; if (_last != null) { _last.IsContinued = false; } } if (packet.IsContinuation) { if (_last == null) { throw new InvalidDataException(); } if (!_last.IsContinued) { throw new InvalidDataException(); } _last.MergeWith(packet); _last.IsContinued = packet.IsContinued; } else { if (packet == null) { throw new ArgumentException("Wrong packet datatype", "packet"); } if (_first == null) { _first = packet; _last = packet; } else { Packet packet2 = (packet.Prev = _last); Packet last2 = (packet2.Next = packet); _last = last2; } } if (packet.IsEndOfStream) { SetEndOfStream(); } } } internal void SetEndOfStream() { lock (_packetLock) { _eosFound = true; if (_last.IsContinued) { _last = _last.Prev; _last.Next.Prev = null; _last.Next = null; } } } public DataPacket GetNextPacket() { return _current = PeekNextPacketInternal(); } public DataPacket PeekNextPacket() { return PeekNextPacketInternal(); } private Packet PeekNextPacketInternal() { Packet packet; if (_current == null) { packet = _first; } else { while (true) { lock (_packetLock) { packet = _current.Next; if ((packet != null && !packet.IsContinued) || _eosFound) { break; } goto IL_004f; } IL_004f: _container.GatherNextPage(_streamSerial); } } if (packet != null) { if (packet.IsContinued) { throw new InvalidDataException("Packet is incomplete!"); } packet.Reset(); } return packet; } internal void ReadAllPages() { if (!CanSeek) { throw new InvalidOperationException(); } while (!_eosFound) { _container.GatherNextPage(_streamSerial); } } internal DataPacket GetLastPacket() { ReadAllPages(); return _last; } public int GetTotalPageCount() { ReadAllPages(); int num = 0; int num2 = 0; for (Packet packet = _first; packet != null; packet = packet.Next) { if (packet.PageSequenceNumber != num2) { num++; num2 = packet.PageSequenceNumber; } } return num; } public DataPacket GetPacket(int packetIndex) { if (!CanSeek) { throw new InvalidOperationException(); } if (packetIndex < 0) { throw new ArgumentOutOfRangeException("index"); } if (_first == null) { throw new InvalidOperationException("Packet reader has no packets!"); } Packet packet = _first; while (--packetIndex >= 0) { while (packet.Next == null) { if (_eosFound) { throw new ArgumentOutOfRangeException("index"); } _container.GatherNextPage(_streamSerial); } packet = packet.Next; } packet.Reset(); return packet; } private Packet GetLastPacketInPage(Packet packet) { if (packet != null) { int pageSequenceNumber = packet.PageSequenceNumber; while (packet.Next != null && packet.Next.PageSequenceNumber == pageSequenceNumber) { packet = packet.Next; } if (packet != null && packet.IsContinued) { packet = packet.Prev; } } return packet; } private Packet FindPacketInPage(Packet pagePacket, long targetGranulePos, Func<DataPacket, DataPacket, int> packetGranuleCountCallback) { Packet lastPacketInPage = GetLastPacketInPage(pagePacket); if (lastPacketInPage == null) { return null; } Packet packet = lastPacketInPage; do { if (!packet.GranuleCount.HasValue) { if (packet == lastPacketInPage) { packet.GranulePosition = packet.PageGranulePosition; } else { packet.GranulePosition = packet.Next.GranulePosition - packet.Next.GranuleCount.Value; } if (packet == _last && _eosFound && packet.Prev.PageSequenceNumber < packet.PageSequenceNumber) { packet.GranuleCount = (int)(packet.GranulePosition - packet.Prev.PageGranulePosition); } else if (packet.Prev != null) { packet.Prev.Reset(); packet.Reset(); packet.GranuleCount = packetGranuleCountCallback(packet, packet.Prev); } else { if (packet.GranulePosition > packet.Next.GranulePosition - packet.Next.GranuleCount) { throw new InvalidOperationException("First data packet size mismatch"); } packet.GranuleCount = (int)packet.GranulePosition; } } if (targetGranulePos <= packet.GranulePosition && targetGranulePos > packet.GranulePosition - packet.GranuleCount) { if (packet.Prev != null && !packet.Prev.GranuleCount.HasValue) { packet.Prev.GranulePosition = packet.GranulePosition - packet.GranuleCount.Value; } return packet; } packet = packet.Prev; } while (packet != null && packet.PageSequenceNumber == lastPacketInPage.PageSequenceNumber); if (packet != null && packet.PageGranulePosition < targetGranulePos) { packet.GranulePosition = packet.PageGranulePosition; return packet.Next; } return null; } public DataPacket FindPacket(long granulePos, Func<DataPacket, DataPacket, int> packetGranuleCountCallback) { if (granulePos < 0) { throw new ArgumentOutOfRangeException("granulePos"); } Packet packet = null; Packet packet2 = _current ?? _first; if (granulePos > packet2.PageGranulePosition) { while (granulePos > packet2.PageGranulePosition) { if ((packet2.Next == null || packet2.IsContinued) && !_eosFound) { _container.GatherNextPage(_streamSerial); if (_eosFound) { packet2 = null; break; } } packet2 = packet2.Next; } return FindPacketInPage(packet2, granulePos, packetGranuleCountCallback); } while (packet2.Prev != null && (granulePos <= packet2.Prev.PageGranulePosition || packet2.Prev.PageGranulePosition == -1)) { packet2 = packet2.Prev; } return FindPacketInPage(packet2, granulePos, packetGranuleCountCallback); } public void SeekToPacket(DataPacket packet, int preRoll) { if (preRoll < 0) { throw new ArgumentOutOfRangeException("preRoll"); } if (packet == null) { throw new ArgumentNullException("granulePos"); } Packet packet2 = packet as Packet; if (packet2 == null) { throw new ArgumentException("Incorrect packet type!", "packet"); } while (--preRoll >= 0) { packet2 = packet2.Prev; if (packet2 == null) { throw new ArgumentOutOfRangeException("preRoll"); } } _current = packet2.Prev; } public long GetGranuleCount() { return GetLastPacket().PageGranulePosition; } } [Flags] internal enum PageFlags { None = 0, ContinuesPacket = 1, BeginningOfStream = 2, EndOfStream = 4 } internal class OpusHeader { private byte version; private byte channel_count; private ushort pre_skip; private uint input_sample_rate; private short output_gain; private byte mapping_family; private byte stream_count; private byte coupled_count; } public class OpusOggReadStream { private const double GranuleSampleRate = 48000.0; private readonly Stream _stream; private readonly IOpusDecoder _decoder; private byte[] _nextDataPacket; private IPacketProvider _packetProvider; private bool _endOfStream; private OggContainerReader _containerReader; public bool CanSeek => _stream.CanSeek; public OpusTags Tags { get; private set; } public bool HasNextPacket => !_endOfStream; public string LastError { get; private set; } public long PageGranulePosition { get; private set; } public TimeSpan CurrentTime => TimeSpan.FromSeconds((double)PageGranulePosition / 48000.0); public long GranuleCount { get; private set; } public TimeSpan TotalTime => TimeSpan.FromSeconds((double)GranuleCount / 48000.0); public long PagePosition { get; private set; } public long PageCount { get; private set; } public OpusOggReadStream(IOpusDecoder decoder, Stream stream) { if (stream == null) { throw new ArgumentNullException("stream"); } _stream = stream; _decoder = decoder; _endOfStream = !Initialize(); } public short[] DecodeNextPacket() { //IL_0091: Expected O, but got Unknown if (_decoder == null) { throw new InvalidOperationException("Cannot decode opus packets as a decoder was never provided"); } if (_nextDataPacket == null || _nextDataPacket.Length == 0) { _endOfStream = true; return null; } try { int numSamples = OpusPacketInfo.GetNumSamples((ReadOnlySpan<byte>)_nextDataPacket.AsSpan(), _decoder.SampleRate); short[] array = new short[numSamples * _decoder.NumChannels]; _decoder.Decode((ReadOnlySpan<byte>)_nextDataPacket.AsSpan(), array.AsSpan(), numSamples, false); QueueNextPacket(); return array; } catch (OpusException val) { OpusException val2 = val; LastError = "Opus decoder threw exception: " + ((Exception)(object)val2).Message; return null; } } public byte[] ReadNextRawPacket() { if (_nextDataPacket == null || _nextDataPacket.Length == 0) { _endOfStream = true; return null; } byte[] nextDataPacket = _nextDataPacket; QueueNextPacket(); return nextDataPacket; } private bool Initialize() { try { OggContainerReader oggContainerReader = new OggContainerReader(_stream, closeOnDispose: true); if (!oggContainerReader.Init()) { LastError = "Could not initialize stream"; oggContainerReader.Dispose(); return false; } if (oggContainerReader.StreamSerials.Length == 0) { LastError = "Initialization failed: No elementary streams found in input file"; oggContainerReader.Dispose(); return false; } _containerReader = oggContainerReader; int streamSerial = oggContainerReader.StreamSerials[0]; _packetProvider = oggContainerReader.GetStream(streamSerial); if (CanSeek) { GranuleCount = _packetProvider.GetGranuleCount(); PageCount = _packetProvider.GetTotalPageCount(); } QueueNextPacket(); return true; } catch (Exception ex) { LastError = "Unknown initialization error: " + ex.Message; return false; } } public void SeekTo(TimeSpan playbackTime) { if (!CanSeek) { throw new InvalidOperationException("Stream is not seekable."); } if (playbackTime < TimeSpan.Zero || playbackTime > TotalTime) { throw new ArgumentOutOfRangeException("playbackTime"); } long granulePosition = Convert.ToInt64(playbackTime.TotalSeconds * 48000.0); SeekToGranulePosition(granulePosition); } private void SeekToGranulePosition(long granulePosition) { if (!CanSeek) { throw new InvalidOperationException("Stream is not seekable."); } if (granulePosition < 0 || granulePosition > GranuleCount) { throw new ArgumentOutOfRangeException("granulePosition"); } DataPacket dataPacket = _packetProvider.FindPacket(granulePosition, GetPacketLength); if (dataPacket == null || dataPacket.IsEndOfStream) { _endOfStream = true; _nextDataPacket = null; return; } _packetProvider.SeekToPacket(dataPacket, 1); PageGranulePosition = _packetProvider.PeekNextPacket().PageGranulePosition; if (_decoder != null) { _decoder.ResetState(); } } private int GetPacketLength(DataPacket curPacket, DataPacket lastPacket) { if (lastPacket == null || curPacket.IsResync) { return 0; } if (curPacket.ReadBit()) { return 0; } if (lastPacket.ReadBit()) { return 0; } return 1; } private void QueueNextPacket() { if (_endOfStream) { return; } DataPacket nextPacket = _packetProvider.GetNextPacket(); if (nextPacket == null) { _endOfStream = true; _nextDataPacket = null; return; } PageGranulePosition = nextPacket.PageGranulePosition; PagePosition = nextPacket.PageSequenceNumber; byte[] array = new byte[nextPacket.Length]; nextPacket.Read(array, 0, nextPacket.Length); nextPacket.Done(); if (array.Length > 8 && "OpusHead".Equals(Encoding.UTF8.GetString(array, 0, 8))) { QueueNextPacket(); } else if (array.Length > 8 && "OpusTags".Equals(Encoding.UTF8.GetString(array, 0, 8))) { Tags = OpusTags.ParsePacket(array, array.Length); QueueNextPacket(); } else { _nextDataPacket = array; } } public void Close() { _containerReader?.Dispose(); } } public class OpusOggWriteStream { private const int FRAME_SIZE_MS = 20; private const byte EARLY_FINALIZE_SEGMENT_LIMIT = 248; private IOpusEncoder _encoder; private Stream _outputStream; private Crc _crc; private int _inputChannels; private IResampler _resampler; private int _inputSampleRate; private readonly bool _leaveOpen; private int _encoderSampleRate; private short[] _opusFrame; private int _opusFrameSamples; private int _opusFrameIndex; private byte[] _currentHeader = new byte[400]; private byte[] _currentPayload = new byte[65536]; private int _headerIndex; private int _payloadIndex; private int _pageCounter; private int _logicalStreamId; private long _granulePosition; private byte _packetsInPage; private byte _lacingTableCount; private byte _maxPacketsPerPage = 248; private const int PAGE_FLAGS_POS = 5; private const int GRANULE_COUNT_POS = 6; private const int CHECKSUM_HEADER_POS = 22; private const int SEGMENT_COUNT_POS = 26; private bool _finalized; public TimeSpan MaxAudioLengthPerPage { get { return TimeSpan.FromMilliseconds(20 * _maxPacketsPerPage); } set { if (value <= TimeSpan.Zero) { throw new ArgumentOutOfRangeException("MaxAudioLengthPerPage"); } _maxPacketsPerPage = (byte)Math.Min(248, Math.Max(1, (int)(value.TotalMilliseconds / 20.0))); } } public OpusOggWriteStream(IOpusEncoder encoder, Stream outputStream, OpusTags fileTags = null, int inputSampleRate = 0, int resamplerQuality = 5, bool leaveOpen = false) { _encoder = encoder; if (_encoder.UseDTX) { throw new ArgumentException("DTX is not currently supported in Ogg streams"); } _inputSampleRate = inputSampleRate; _leaveOpen = leaveOpen; if (_inputSampleRate == 0) { _inputSampleRate = _encoder.SampleRate; } _logicalStreamId = new Random().Next(); _encoderSampleRate = encoder.SampleRate; _inputChannels = encoder.NumChannels; _outputStream = outputStream; _opusFrameIndex = 0; _granulePosition = 0L; _opusFrameSamples = (int)((long)_encoderSampleRate * 20L / 1000); _opusFrame = new short[_opusFrameSamples * _inputChannels]; _crc = new Crc(); _resampler = ResamplerFactory.CreateResampler(_inputChannels, _inputSampleRate, _encoderSampleRate, resamplerQuality, (TextWriter)null); BeginNewPage(); WriteOpusHeadPage(); WriteOpusTagsPage(fileTags); } public void WriteSamples(short[] data, int offset, int count) { if (_finalized) { throw new InvalidOperationException("Cannot write new samples to Ogg file, the output stream is already closed!"); } if (data.Length - offset < count) { throw new ArgumentOutOfRangeException("The given audio buffer claims to have " + count + " samples, but it actually only has " + (data.Length - offset)); } int num = 0; for (int num2 = Math.Min(_opusFrame.Length - _opusFrameIndex, count - num); num2 > 0; num2 = Math.Min(_opusFrame.Length - _opusFrameIndex, count - num)) { if (_inputSampleRate != _encoderSampleRate) { int num3 = (count - num) / _inputChannels; int num4 = num2 / _inputChannels; _resampler.ProcessInterleaved(data.AsSpan(offset + num), ref num3, _opusFrame.AsSpan(_opusFrameIndex), ref num4); num += num3 * _inputChannels; _opusFrameIndex += num4 * _inputChannels; } else { data.AsSpan(offset + num, num2).CopyTo(_opusFrame.AsSpan(_opusFrameIndex, num2)); _opusFrameIndex += num2; num += num2; } if (_opusFrameIndex == _opusFrame.Length) { int num5 = _encoder.Encode((ReadOnlySpan<short>)_opusFrame.AsSpan(), _opusFrameSamples, _currentPayload.AsSpan(_payloadIndex), _currentPayload.Length - _payloadIndex); _payloadIndex += num5; _granulePosition += 960L; int num6 = num5; while (num6 >= 255) { num6 -= 255; _currentHeader[_headerIndex++] = byte.MaxValue; _lacingTableCount++; } _currentHeader[_headerIndex++] = (byte)num6; _lacingTableCount++; _packetsInPage++; if (_packetsInPage >= _maxPacketsPerPage || _lacingTableCount > 248) { FinalizePage(); } _opusFrameIndex = 0; } } } public void WriteSamples(float[] data, int offset, int count) { if (_finalized) { throw new InvalidOperationException("Cannot write new samples to Ogg file, the output stream is already closed!"); } short[] array = new short[count]; for (int i = 0; i < count; i++) { array[i] = (short)(data[i + offset] * 32767f); } WriteSamples(array, 0, count); } public void Finish() { int num = _opusFrame.Length - _opusFrameIndex; short[] data = new short[num]; WriteSamples(data, 0, num); FinalizePage(); WriteStreamFinishedPage(); _outputStream.Flush(); if (!_leaveOpen) { _outputStream.Dispose(); } _finalized = true; } private void WriteStreamFinishedPage() { _currentHeader[_headerIndex++] = 0; _lacingTableCount++; _currentHeader[5] = 4; FinalizePage(); } private void WriteOpusHeadPage() { if (_payloadIndex != 0) { throw new InvalidOperationException("Must begin writing OpusHead on a new page!"); } _payloadIndex += WriteValueToByteBuffer("OpusHead", _currentPayload, _payloadIndex); _currentPayload[_payloadIndex++] = 1; _currentPayload[_payloadIndex++] = (byte)_inputChannels; short val = 0; _payloadIndex += WriteValueToByteBuffer(val, _currentPayload, _payloadIndex); _payloadIndex += WriteValueToByteBuffer(_encoderSampleRate, _currentPayload, _payloadIndex); short val2 = 0; _payloadIndex += WriteValueToByteBuffer(val2, _currentPayload, _payloadIndex); _currentPayload[_payloadIndex++] = 0; _currentHeader[_headerIndex++] = (byte)_payloadIndex; _lacingTableCount++; _currentHeader[5] = 2; FinalizePage(); } private void WriteOpusTagsPage(OpusTags tags = null) { if (tags == null) { tags = new OpusTags(); } if (string.IsNullOrEmpty(tags.Comment)) { tags.Comment = _encoder.GetVersionString(); } if (_payloadIndex != 0) { throw new InvalidOperationException("Must begin writing OpusTags on a new page!"); } _payloadIndex += WriteValueToByteBuffer("OpusTags", _currentPayload, _payloadIndex); int num = WriteValueToByteBuffer(tags.Comment, _currentPayload, _payloadIndex + 4); _payloadIndex += WriteValueToByteBuffer(num, _currentPayload, _payloadIndex); _payloadIndex += num; int payloadIndex = _payloadIndex; _payloadIndex += 4; int num2 = 0; foreach (KeyValuePair<string, string> field in tags.Fields) { if (!string.IsNullOrEmpty(field.Key) && !string.IsNullOrEmpty(field.Value)) { num = WriteValueToByteBuffer(field.Key + "=" + field.Value, _currentPayload, _payloadIndex + 4); _payloadIndex += WriteValueToByteBuffer(num, _currentPayload, _payloadIndex); _payloadIndex += num; num2++; } } WriteValueToByteBuffer(num2, _currentPayload, payloadIndex); int num3; for (num3 = _payloadIndex; num3 >= 255; num3 -= 255) { _currentHeader[_headerIndex++] = byte.MaxValue; _lacingTableCount++; } _currentHeader[_headerIndex++] = (byte)num3; _lacingTableCount++; FinalizePage(); } private void BeginNewPage() { _headerIndex = 0; _payloadIndex = 0; _packetsInPage = 0; _lacingTableCount = 0; _headerIndex += WriteValueToByteBuffer("OggS", _currentHeader, _headerIndex); _currentHeader[_headerIndex++] = 0; _currentHeader[_headerIndex++] = 0; _headerIndex += WriteValueToByteBuffer(_granulePosition, _currentHeader, _headerIndex); _headerIndex += WriteValueToByteBuffer(_logicalStreamId, _currentHeader, _headerIndex); _headerIndex += WriteValueToByteBuffer(_pageCounter, _currentHeader, _headerIndex); _currentHeader[_headerIndex++] = 0; _currentHeader[_headerIndex++] = 0; _currentHeader[_headerIndex++] = 0; _currentHeader[_headerIndex++] = 0; _currentHeader[_headerIndex++] = _lacingTableCount; _pageCounter++; } private void FinalizePage() { if (_finalized) { throw new InvalidOperationException("Cannot finalize page, the output stream is already closed!"); } if (_lacingTableCount != 0) { _currentHeader[26] = _lacingTableCount; WriteValueToByteBuffer(_granulePosition, _currentHeader, 6); _crc.Reset(); for (int i = 0; i < _headerIndex; i++) { _crc.Update(_currentHeader[i]); } for (int j = 0; j < _payloadIndex; j++) { _crc.Update(_currentPayload[j]); } WriteValueToByteBuffer(_crc.Value, _currentHeader, 22); _outputStream.Write(_currentHeader, 0, _headerIndex); _outputStream.Write(_currentPayload, 0, _payloadIndex); BeginNewPage(); } } private static int WriteValueToByteBuffer(int val, byte[] target, int targetOffset) { BinaryHelpers.Int32ToByteArrayLittleEndian(val, target, targetOffset); return 4; } private static int WriteValueToByteBuffer(long val, byte[] target, int targetOffset) { BinaryHelpers.Int64ToByteArrayLittleEndian(val, target, targetOffset); return 8; } private static int WriteValueToByteBuffer(uint val, byte[] target, int targetOffset) { BinaryHelpers.UInt32ToByteArrayLittleEndian(val, target, targetOffset); return 4; } private static int WriteValueToByteBuffer(short val, byte[] target, int targetOffset) { BinaryHelpers.Int16ToByteArrayLittleEndian(val, target, targetOffset); return 2; } private static int WriteValueToByteBuffer(string val, byte[] target, int targetOffset) { if (string.IsNullOrEmpty(val)) { return 0; } return Encoding.UTF8.GetBytes(val, 0, val.Length, target, targetOffset); } } public class OpusRawWriteStream { private Stream _outputStream; private Crc _crc; private int _inputChannels; private readonly bool _leaveOpen; private int _inputSampleRate; private byte[] _currentHeader = new byte[400]; private byte[] _currentPayload = new byte[65536]; private int _headerIndex; private int _payloadIndex; private int _pageCounter; private int _logicalStreamId; private long _granulePosition; private byte _lacingTableCount; private const int PAGE_FLAGS_POS = 5; private const int GRANULE_COUNT_POS = 6; private const int CHECKSUM_HEADER_POS = 22; private const int SEGMENT_COUNT_POS = 26; private bool _finalized; public OpusRawWriteStream(Stream outputStream, OpusTags fileTags, int inputSampleRate, int inputNumChannels, bool leaveOpen = false) { _inputSampleRate = inputSampleRate; _logicalStreamId = new Random().Next(); _inputChannels = inputNumChannels; _leaveOpen = leaveOpen; _outputStream = outputStream; _granulePosition = 0L; _crc = new Crc(); BeginNewPage(); WriteOpusHeadPage(); WriteOpusTagsPage(fileTags); } public void WriteSinglePacket(byte[] opusPacket, int offset, int packetSize) { if (_finalized) { throw new InvalidOperationException("Cannot write new samples to Ogg file, the output stream is already closed!"); } if (opusPacket.Length - offset < packetSize) { throw new ArgumentOutOfRangeException("The given audio buffer claims to have " + packetSize + " samples, but it actually only has " + (opusPacket.Length - offset)); } Array.Copy(opusPacket, offset, _currentPayload, _payloadIndex, packetSize); _payloadIndex += packetSize; int numSamplesPerFrame = OpusPacketInfo.GetNumSamplesPerFrame((ReadOnlySpan<byte>)opusPacket.AsSpan(offset), 48000); _granulePosition += numSamplesPerFrame; int num = packetSize; while (num >= 255) { num -= 255; _currentHeader[_headerIndex++] = byte.MaxValue; _lacingTableCount++; } _currentHeader[_headerIndex++] = (byte)num; _lacingTableCount++; if (_lacingTableCount > 248) { FinalizePage(); } } public void Finish() { FinalizePage(); WriteStreamFinishedPage(); _outputStream.Flush(); if (!_leaveOpen) { _outputStream.Dispose(); } _finalized = true; } private void WriteStreamFinishedPage() { _currentHeader[_headerIndex++] = 0; _lacingTableCount++; _currentHeader[5] = 4; FinalizePage(); } private void WriteOpusHeadPage() { if (_payloadIndex != 0) { throw new InvalidOperationException("Must begin writing OpusHead on a new page!"); } _payloadIndex += WriteValueToByteBuffer("OpusHead", _currentPayload, _payloadIndex); _currentPayload[_payloadIndex++] = 1; _currentPayload[_payloadIndex++] = (byte)_inputChannels; short val = 0; _payloadIndex += WriteValueToByteBuffer(val, _currentPayload, _payloadIndex); _payloadIndex += WriteValueToByteBuffer(_inputSampleRate, _currentPayload, _payloadIndex); short val2 = 0; _payloadIndex += WriteValueToByteBuffer(val2, _currentPayload, _payloadIndex); _currentPayload[_payloadIndex++] = 0; _currentHeader[_headerIndex++] = (byte)_payloadIndex; _lacingTableCount++; _currentHeader[5] = 2; FinalizePage(); } private void WriteOpusTagsPage(OpusTags tags = null) { if (tags == null) { tags = new OpusTags(); } if (string.IsNullOrEmpty(tags.Comment)) { tags.Comment = "Concentus.OggFile"; } if (_payloadIndex != 0) { throw new InvalidOperationException("Must begin writing OpusTags on a new page!"); } _payloadIndex += WriteValueToByteBuffer("OpusTags", _currentPayload, _payloadIndex); int num = WriteValueToByteBuffer(tags.Comment, _currentPayload, _payloadIndex + 4); _payloadIndex += WriteValueToByteBuffer(num, _currentPayload, _payloadIndex); _payloadIndex += num; int payloadIndex = _payloadIndex; _payloadIndex += 4; int num2 = 0; foreach (KeyValuePair<string, string> field in tags.Fields) { if (!string.IsNullOrEmpty(field.Key) && !string.IsNullOrEmpty(field.Value)) { num = WriteValueToByteBuffer(field.Key + "=" + field.Value, _currentPayload, _payloadIndex + 4); _payloadIndex += WriteValueToByteBuffer(num, _currentPayload, _payloadIndex); _payloadIndex += num; num2++; } } WriteValueToByteBuffer(num2, _currentPayload, payloadIndex); int num3; for (num3 = _payloadIndex; num3 >= 255; num3 -= 255) { _currentHeader[_headerIndex++] = byte.MaxValue; _lacingTableCount++; } _currentHeader[_headerIndex++] = (byte)num3; _lacingTableCount++; FinalizePage(); } private void BeginNewPage() { _headerIndex = 0; _payloadIndex = 0; _lacingTableCount = 0; _headerIndex += WriteValueToByteBuffer("OggS", _currentHeader, _headerIndex); _currentHeader[_headerIndex++] = 0; _currentHeader[_headerIndex++] = 0; _headerIndex += WriteValueToByteBuffer(_granulePosition, _currentHeader, _headerIndex); _headerIndex += WriteValueToByteBuffer(_logicalStreamId, _currentHeader, _headerIndex); _headerIndex += WriteValueToByteBuffer(_pageCounter, _currentHeader, _headerIndex); _currentHeader[_headerIndex++] = 0; _currentHeader[_headerIndex++] = 0; _currentHeader[_headerIndex++] = 0; _currentHeader[_headerIndex++] = 0; _currentHeader[_headerIndex++] = _lacingTableCount; _pageCounter++; } private void FinalizePage() { if (_finalized) { throw new InvalidOperationException("Cannot finalize page, the output stream is already closed!"); } if (_lacingTableCount != 0) { _currentHeader[26] = _lacingTableCount; WriteValueToByteBuffer(_granulePosition, _currentHeader, 6); _crc.Reset(); for (int i = 0; i < _headerIndex; i++) { _crc.Update(_currentHeader[i]); } for (int j = 0; j < _payloadIndex; j++) { _crc.Update(_currentPayload[j]); } WriteValueToByteBuffer(_crc.Value, _currentHeader, 22); _outputStream.Write(_currentHeader, 0, _headerIndex); _outputStream.Write(_currentPayload, 0, _payloadIndex); BeginNewPage(); } } private static int WriteValueToByteBuffer(int val, byte[] target, int targetOffset) { Array.Copy(BitConverter.GetBytes(val), 0, target, targetOffset, 4); return 4; } private static int WriteValueToByteBuffer(long val, byte[] target, int targetOffset) { Array.Copy(BitConverter.GetBytes(val), 0, target, targetOffset, 8); return 8; } private static int WriteValueToByteBuffer(uint val, byte[] target, int targetOffset) { Array.Copy(BitConverter.GetBytes(val), 0, target, targetOffset, 4); return 4; } private static int WriteValueToByteBuffer(short val, byte[] target, int targetOffset) { Array.Copy(BitConverter.GetBytes(val), 0, target, targetOffset, 2); return 2; } private static int WriteValueToByteBuffer(string val, byte[] target, int targetOffset) { if (string.IsNullOrEmpty(val)) { return 0; } byte[] bytes = Encoding.UTF8.GetBytes(val); Array.Copy(bytes, 0, target, targetOffset, bytes.Length); return bytes.Length; } } public class OpusTagName { public const string Title = "title"; public const string Artist = "artist"; public const string Album = "album"; } public class OpusTags { private string _comment = string.Empty; private IDictionary<string, string> _fields = new Dictionary<string, string>(); public string Comment { get { return _comment; } set { _comment = value; } } public IDictionary<string, string> Fields => _fields; internal static OpusTags ParsePacket(byte[] packet, int packetLength) { if (packetLength < 8) { return null; } if (!"OpusTags".Equals(Encoding.UTF8.GetString(packet, 0, 8))) { return null; } OpusTags opusTags = new OpusTags(); int num = 8; int num2 = BitConverter.ToInt32(packet, num); num += 4; if (num2 > 0) { opusTags._comment = Encoding.UTF8.GetString(packet, num, num2); num += num2; } int num3 = BitConverter.ToInt32(packet, num); num += 4; for (int i = 0; i < num3; i++) { num2 = BitConverter.ToInt32(packet, num); num += 4; if (num2 > 0) { string @string = Encoding.UTF8.GetString(packet, num, num2); num += num2; int num4 = @string.IndexOf('='); if (num4 > 0) { string key = @string.Substring(0, num4); string value = @string.Substring(num4 + 1); opusTags._fields[key] = value; } } } return opusTags; } } internal class ParameterChangeEventArgs : EventArgs { public DataPacket FirstPacket { get; private set; } public ParameterChangeEventArgs(DataPacket firstPacket) { FirstPacket = firstPacket; } } internal class StreamReadBuffer : IDisposable { internal class StreamWrapper { internal Stream Source; internal object LockObject = new object(); internal long EofOffset = long.MaxValue; internal int RefCount = 1; } private class SavedBuffer { public byte[] Buffer; public long BaseOffset; public int End; public int DiscardCount; public long VersionSaved; } private static Dictionary<Stream, StreamWrapper> _lockObjects = new Dictionary<Stream, StreamWrapper>(); private StreamWrapper _wrapper; private int _maxSize; private byte[] _data; private long _baseOffset; private int _end; private int _discardCount; private bool _minimalRead; private long _versionCounter; private List<SavedBuffer> _savedBuffers; public bool MinimalRead { get { return _minimalRead; } set { _minimalRead = value; } } public int MaxSize { get { return _maxSize; } set { if (value < 1) { throw new ArgumentOutOfRangeException("Must be greater than zero."); } int num = 1 << (int)Math.Ceiling(Math.Log(value, 2.0)); if (num < _end) { if (num < _end - _discardCount) { throw new ArgumentOutOfRangeException("Must be greater than or equal to the number of bytes currently buffered."); } CommitDiscard(); byte[] array = new byte[num]; Buffer.BlockCopy(_data, 0, array, 0, _end); _data = array; } _maxSize = num; } } public long BaseOffset => _baseOffset + _discardCount; public int BytesFilled => _end - _discardCount; public int Length => _data.Length; internal long BufferEndOffset { get { if (_end - _discardCount > 0) { return _baseOffset + _discardCount + _maxSize; } if (_wrapper.Source.CanSeek) { return _wrapper.Source.Length; } return _baseOffset + Length; } } internal StreamReadBuffer(Stream source, int initialSize, int maxSize, bool minimalRead) { StreamWrapper value; lock (_lockObjects) { if (!_lockObjects.TryGetValue(source, out value)) { _lockObjects.Add(source, new StreamWrapper { Source = source }); value = _lockObjects[source]; if (source.CanSeek) { value.EofOffset = source.Length; } } else { value.RefCount++; } } initialSize = 2 << (int)Math.Log(initialSize - 1, 2.0); maxSize = 1 << (int)Math.Log(maxSize, 2.0); _wrapper = value; _data = new byte[initialSize]; _maxSize = maxSize; _minimalRead = minimalRead; _savedBuffers = new List<SavedBuffer>(); } public void Dispose() { lock (_lockObjects) { if (--_wrapper.RefCount == 0) { _lockObjects.Remove(_wrapper.Source); } } } public int Read(long offset, byte[] buffer, int index, int count) { if (offset < 0) { throw new ArgumentOutOfRangeException("offset"); } if (buffer == null) { throw new ArgumentNullException("buffer"); } if (index < 0 || index + count > buffer.Length) { throw new ArgumentOutOfRangeException("index"); } if (count < 0) { throw new ArgumentOutOfRangeException("count"); } if (offset >= _wrapper.EofOffset) { return 0; } int srcOffset = EnsureAvailable(offset, ref count, isRecursion: false); Buffer.BlockCopy(_data, srcOffset, buffer, index, count); return count; } internal int ReadByte(long offset) { if (offset < 0) { throw new ArgumentOutOfRangeException("offset"); } if (offset >= _wrapper.EofOffset) { return -1; } int count = 1; int num = EnsureAvailable(offset, ref count, isRecursion: false); if (count == 1) { return _data[num]; } return -1; } private int EnsureAvailable(long offset, ref int count, bool isRecursion) { if (offset >= _baseOffset && offset + count < _baseOffset + _end) { return (int)(offset - _baseOffset); } if (count > _maxSize) { throw new InvalidOperationException("Not enough room in the buffer! Increase the maximum size and try again."); } _versionCounter++; if (!isRecursion) { for (int i = 0; i < _savedBuffers.Count; i++) { long num = _savedBuffers[i].BaseOffset - offset; if ((num < 0 && _savedBuffers[i].End + num > 0) || (num > 0 && count - num > 0)) { SwapBuffers(_savedBuffers[i]); return EnsureAvailable(offset, ref count, isRecursion: true); } } } while (_savedBuffers.Count > 0 && _savedBuffers[0].VersionSaved + 25 < _versionCounter) { _savedBuffers[0].Buffer = null; _savedBuffers.RemoveAt(0); } if (offset < _baseOffset && !_wrapper.Source.CanSeek) { throw new InvalidOperationException("Cannot seek before buffer on forward-only streams!"); } CalcBuffer(offset, count, out var readStart, out var readEnd); count = FillBuffer(offset, count, readStart, readEnd); return (int)(offset - _baseOffset); } private void SaveBuffer() { _savedBuffers.Add(new SavedBuffer { Buffer = _data, BaseOffset = _baseOffset, End = _end, DiscardCount = _discardCount, VersionSaved = _versionCounter }); _data = null; _end = 0; _discardCount = 0; } private void CreateNewBuffer(long offset, int count) { SaveBuffer(); _data = new byte[Math.Min(2 << (int)Math.Log(count - 1, 2.0), _maxSize)]; _baseOffset = offset; } private void SwapBuffers(SavedBuffer savedBuffer) { _savedBuffers.Remove(savedBuffer); SaveBuffer(); _data = savedBuffer.Buffer; _baseOffset = savedBuffer.BaseOffset; _end = savedBuffer.End; _discardCount = savedBuffer.DiscardCount; } private void CalcBuffer(long offset, int count, out int readStart, out int readEnd) { readStart = 0; readEnd = 0; if (offset < _baseOffset) { if (offset + _maxSize <= _baseOffset) { if (_baseOffset - (offset + _maxSize) > _maxSize) { CreateNewBuffer(offset, count); } else { EnsureBufferSize(count, copyContents: false, 0); } _baseOffset = offset; readEnd = count; } else { readEnd = (int)(offset - _baseOffset); EnsureBufferSize(Math.Min((int)(offset + _maxSize - _baseOffset), _end) - readEnd, copyContents: true, readEnd); readEnd = (int)(offset - _baseOffset) - readEnd; } } else if (offset >= _baseOffset + _maxSize) { if (offset - (_baseOffset + _maxSize) > _maxSize) { CreateNewBuffer(offset, count); } else { EnsureBufferSize(count, copyContents: false, 0); } _baseOffset = offset; readEnd = count; } else { readEnd = (int)(offset + count - _baseOffset); int num = Math.Max(readEnd - _maxSize, 0); EnsureBufferSize(readEnd - num, copyContents: true, num); readStart = _end; readEnd = (int)(offset + count - _baseOffset); } } private void EnsureBufferSize(int reqSize, bool copyContents, int copyOffset) { byte[] array = _data; if (reqSize > _data.Length) { if (reqSize > _maxSize) { if (!_wrapper.Source.CanSeek && reqSize - _discardCount > _maxSize) { throw new InvalidOperationException("Not enough room in the buffer! Increase the maximum size and try again."); } int num = reqSize - _maxSize; copyOffset += num; reqSize = _maxSize; } else { int num2; for (num2 = _data.Length; num2 < reqSize; num2 *= 2) { } reqSize = num2; } if (reqSize > _data.Length) { array = new byte[reqSize]; } } if (copyContents) { if ((copyOffset > 0 && copyOffset < _end) || (copyOffset == 0 && array != _data)) { Buffer.BlockCopy(_data, copyOffset, array, 0, _end - copyOffset); if ((_discardCount -= copyOffset) < 0) { _discardCount = 0; } } else if (copyOffset < 0 && -copyOffset < _end) { if (array != _data || _end <= -copyOffset) { Buffer.BlockCopy(_data, 0, array, -copyOffset, Math.Max(_end, Math.Min(_end, _data.Length + copyOffset))); } else { _end = copyOffset; } _discardCount = 0; } else { _end = copyOffset; _discardCount = 0; } _baseOffset += copyOffset; _end -= copyOffset; if (_end > array.Length) { _end = array.Length; } } else { _discardCount = 0; _end = 0; } _data = array; } private int FillBuffer(long offset, int count, int readStart, int readEnd) { long readOffset = _baseOffset + readStart; int readCount = readEnd - readStart; lock (_wrapper.LockObject) { readCount = PrepareStreamForRead(readCount, readOffset); ReadStream(readStart, readCount, readOffset); if (_end < readStart + readCount) { count = Math.Max(0, (int)(_baseOffset + _end - offset)); } else if (!_minimalRead && _end < _data.Length) { readCount = _data.Length - _end; readCount = PrepareStreamForRead(readCount, _baseOffset + _end); _end += _wrapper.Source.Read(_data, _end, readCount); } } return count; } private int PrepareStreamForRead(int readCount, long readOffset) { if (readCount > 0 && _wrapper.Source.Position != readOffset) { if (readOffset < _wrapper.EofOffset) { if (_wrapper.Source.CanSeek) { _wrapper.Source.Position = readOffset; } else { long num = readOffset - _wrapper.Source.Position; if (num < 0) { readCount = 0; } else { while (--num >= 0) { if (_wrapper.Source.ReadByte() == -1) { _wrapper.EofOffset = _wrapper.Source.Position; readCount = 0; break; } } } } } else { readCount = 0; } } return readCount; } private void ReadStream(int readStart, int readCount, long readOffset) { while (readCount > 0 && readOffset < _wrapper.EofOffset) { int num = _wrapper.Source.Read(_data, readStart, readCount); if (num == 0) { break; } readStart += num; readOffset += num; readCount -= num; } if (readStart > _end) { _end = readStart; } } public void DiscardThrough(long offset) { int val = (int)(offset - _baseOffset); _discardCount = Math.Max(val, _discardCount); if (_discardCount >= _data.Length) { CommitDiscard(); } } private void CommitDiscard() { if (_discardCount >= _data.Length || _discardCount >= _end) { _baseOffset += _discardCount; _end = 0; } else { Buffer.BlockCopy(_data, _discardCount, _data, 0, _end - _discardCount); _baseOffset += _discardCount; _end -= _discardCount; } _discardCount = 0; } }