using System; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Runtime.Versioning; using System.Security; using System.Security.Permissions; using System.Text; using System.Threading; using NAudio.Dmo; using NAudio.Dsp; using NAudio.FileFormats.Wav; using NAudio.Utils; using NAudio.Wave; using NAudio.Wave.SampleProviders; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: TargetFramework(".NETStandard,Version=v2.0", FrameworkDisplayName = ".NET Standard 2.0")] [assembly: AssemblyCompany("Mark Heath")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyCopyright("© Mark Heath 2026")] [assembly: AssemblyFileVersion("2.3.0.0")] [assembly: AssemblyInformationalVersion("2.3.0+c89fee940ee6f8d7374d18714a6b85d8b7a18ab0")] [assembly: AssemblyProduct("NAudio.Core")] [assembly: AssemblyTitle("NAudio.Core")] [assembly: AssemblyMetadata("RepositoryUrl", "https://github.com/naudio/NAudio")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("2.3.0.0")] [module: UnverifiableCode] namespace NAudio { public enum Manufacturers { Microsoft = 1, Creative = 2, Mediavision = 3, Fujitsu = 4, Artisoft = 20, TurtleBeach = 21, Ibm = 22, Vocaltec = 23, Roland = 24, DspSolutions = 25, Nec = 26, Ati = 27, Wanglabs = 28, Tandy = 29, Voyetra = 30, Antex = 31, IclPS = 32, Intel = 33, Gravis = 34, Val = 35, Interactive = 36, Yamaha = 37, Everex = 38, Echo = 39, Sierra = 40, Cat = 41, Apps = 42, DspGroup = 43, Melabs = 44, ComputerFriends = 45, Ess = 46, Audiofile = 47, Motorola = 48, Canopus = 49, Epson = 50, Truevision = 51, Aztech = 52, Videologic = 53, Scalacs = 54, Korg = 55, Apt = 56, Ics = 57, Iteratedsys = 58, Metheus = 59, Logitech = 60, Winnov = 61, Ncr = 62, Exan = 63, Ast = 64, Willowpond = 65, Sonicfoundry = 66, Vitec = 67, Moscom = 68, Siliconsoft = 69, Supermac = 73, Audiopt = 74, Speechcomp = 76, Ahead = 77, Dolby = 78, Oki = 79, Auravision = 80, Olivetti = 81, Iomagic = 82, Matsushita = 83, Controlres = 84, Xebec = 85, Newmedia = 86, Nms = 87, Lyrrus = 88, Compusic = 89, Opti = 90, Adlacc = 91, Compaq = 92, Dialogic = 93, Insoft = 94, Mptus = 95, Weitek = 96, LernoutAndHauspie = 97, Qciar = 98, Apple = 99, Digital = 100, Motu = 101, Workbit = 102, Ositech = 103, Miro = 104, Cirruslogic = 105, Isolution = 106, Horizons = 107, Concepts = 108, Vtg = 109, Radius = 110, Rockwell = 111, Xyz = 112, Opcode = 113, Voxware = 114, NorthernTelecom = 115, Apicom = 116, Grande = 117, Addx = 118, Wildcat = 119, Rhetorex = 120, Brooktree = 121, Ensoniq = 125, Fast = 126, Nvidia = 127, Oksori = 128, Diacoustics = 129, Gulbransen = 130, KayElemetrics = 131, Crystal = 132, SplashStudios = 133, Quarterdeck = 134, Tdk = 135, DigitalAudioLabs = 136, Seersys = 137, Picturetel = 138, AttMicroelectronics = 139, Osprey = 140, Mediatrix = 141, Soundesigns = 142, Aldigital = 143, SpectrumSignalProcessing = 144, Ecs = 145, Amd = 146, Coredynamics = 147, Canam = 148, Softsound = 149, Norris = 150, Ddd = 151, Euphonics = 152, Precept = 153, CrystalNet = 154, Chromatic = 155, Voiceinfo = 156, Viennasys = 157, Connectix = 158, Gadgetlabs = 159, Frontier = 160, Viona = 161, Casio = 162, Diamondmm = 163, S3 = 164, FraunhoferIis = 172 } public class MmException : Exception { public MmResult Result { get; } public string Function { get; } public MmException(MmResult result, string function) : base(ErrorMessage(result, function)) { Result = result; Function = function; } private static string ErrorMessage(MmResult result, string function) { return $"{result} calling {function}"; } public static void Try(MmResult result, string function) { if (result != 0) { throw new MmException(result, function); } } } public enum MmResult { NoError = 0, UnspecifiedError = 1, BadDeviceId = 2, NotEnabled = 3, AlreadyAllocated = 4, InvalidHandle = 5, NoDriver = 6, MemoryAllocationError = 7, NotSupported = 8, BadErrorNumber = 9, InvalidFlag = 10, InvalidParameter = 11, HandleBusy = 12, InvalidAlias = 13, BadRegistryDatabase = 14, RegistryKeyNotFound = 15, RegistryReadError = 16, RegistryWriteError = 17, RegistryDeleteError = 18, RegistryValueNotFound = 19, NoDriverCallback = 20, MoreData = 21, WaveBadFormat = 32, WaveStillPlaying = 33, WaveHeaderUnprepared = 34, WaveSync = 35, AcmNotPossible = 512, AcmBusy = 513, AcmHeaderUnprepared = 514, AcmCancelled = 515, MixerInvalidLine = 1024, MixerInvalidControl = 1025, MixerInvalidValue = 1026 } } namespace NAudio.CoreAudioApi { public enum CaptureState { Stopped, Starting, Capturing, Stopping } } namespace NAudio.Dmo { public class AudioMediaSubtypes { public static readonly Guid MEDIASUBTYPE_PCM = new Guid("00000001-0000-0010-8000-00AA00389B71"); public static readonly Guid MEDIASUBTYPE_PCMAudioObsolete = new Guid("e436eb8a-524f-11ce-9f53-0020af0ba770"); public static readonly Guid MEDIASUBTYPE_MPEG1Packet = new Guid("e436eb80-524f-11ce-9f53-0020af0ba770"); public static readonly Guid MEDIASUBTYPE_MPEG1Payload = new Guid("e436eb81-524f-11ce-9f53-0020af0ba770"); public static readonly Guid MEDIASUBTYPE_MPEG2_AUDIO = new Guid("e06d802b-db46-11cf-b4d1-00805f6cbbea"); public static readonly Guid MEDIASUBTYPE_DVD_LPCM_AUDIO = new Guid("e06d8032-db46-11cf-b4d1-00805f6cbbea"); public static readonly Guid MEDIASUBTYPE_DRM_Audio = new Guid("00000009-0000-0010-8000-00aa00389b71"); public static readonly Guid MEDIASUBTYPE_IEEE_FLOAT = new Guid("00000003-0000-0010-8000-00aa00389b71"); public static readonly Guid MEDIASUBTYPE_DOLBY_AC3 = new Guid("e06d802c-db46-11cf-b4d1-00805f6cbbea"); public static readonly Guid MEDIASUBTYPE_DOLBY_AC3_SPDIF = new Guid("00000092-0000-0010-8000-00aa00389b71"); public static readonly Guid MEDIASUBTYPE_RAW_SPORT = new Guid("00000240-0000-0010-8000-00aa00389b71"); public static readonly Guid MEDIASUBTYPE_SPDIF_TAG_241h = new Guid("00000241-0000-0010-8000-00aa00389b71"); public static readonly Guid MEDIASUBTYPE_I420 = new Guid("30323449-0000-0010-8000-00AA00389B71"); public static readonly Guid MEDIASUBTYPE_IYUV = new Guid("56555949-0000-0010-8000-00AA00389B71"); public static readonly Guid MEDIASUBTYPE_RGB1 = new Guid("e436eb78-524f-11ce-9f53-0020af0ba770"); public static readonly Guid MEDIASUBTYPE_RGB24 = new Guid("e436eb7d-524f-11ce-9f53-0020af0ba770"); public static readonly Guid MEDIASUBTYPE_RGB32 = new Guid("e436eb7e-524f-11ce-9f53-0020af0ba770"); public static readonly Guid MEDIASUBTYPE_RGB4 = new Guid("e436eb79-524f-11ce-9f53-0020af0ba770"); public static readonly Guid MEDIASUBTYPE_RGB555 = new Guid("e436eb7c-524f-11ce-9f53-0020af0ba770"); public static readonly Guid MEDIASUBTYPE_RGB565 = new Guid("e436eb7b-524f-11ce-9f53-0020af0ba770"); public static readonly Guid MEDIASUBTYPE_RGB8 = new Guid("e436eb7a-524f-11ce-9f53-0020af0ba770"); public static readonly Guid MEDIASUBTYPE_UYVY = new Guid("59565955-0000-0010-8000-00AA00389B71"); public static readonly Guid MEDIASUBTYPE_VIDEOIMAGE = new Guid("1d4a45f2-e5f6-4b44-8388-f0ae5c0e0c37"); public static readonly Guid MEDIASUBTYPE_YUY2 = new Guid("32595559-0000-0010-8000-00AA00389B71"); public static readonly Guid MEDIASUBTYPE_YV12 = new Guid("31313259-0000-0010-8000-00AA00389B71"); public static readonly Guid MEDIASUBTYPE_YVU9 = new Guid("39555659-0000-0010-8000-00AA00389B71"); public static readonly Guid MEDIASUBTYPE_YVYU = new Guid("55595659-0000-0010-8000-00AA00389B71"); public static readonly Guid WMFORMAT_MPEG2Video = new Guid("e06d80e3-db46-11cf-b4d1-00805f6cbbea"); public static readonly Guid WMFORMAT_Script = new Guid("5C8510F2-DEBE-4ca7-BBA5-F07A104F8DFF"); public static readonly Guid WMFORMAT_VideoInfo = new Guid("05589f80-c356-11ce-bf01-00aa0055595a"); public static readonly Guid WMFORMAT_WaveFormatEx = new Guid("05589f81-c356-11ce-bf01-00aa0055595a"); public static readonly Guid WMFORMAT_WebStream = new Guid("da1e6b13-8359-4050-b398-388e965bf00c"); public static readonly Guid WMMEDIASUBTYPE_ACELPnet = new Guid("00000130-0000-0010-8000-00AA00389B71"); public static readonly Guid WMMEDIASUBTYPE_Base = new Guid("00000000-0000-0010-8000-00AA00389B71"); public static readonly Guid WMMEDIASUBTYPE_DRM = new Guid("00000009-0000-0010-8000-00AA00389B71"); public static readonly Guid WMMEDIASUBTYPE_MP3 = new Guid("00000055-0000-0010-8000-00AA00389B71"); public static readonly Guid WMMEDIASUBTYPE_MP43 = new Guid("3334504D-0000-0010-8000-00AA00389B71"); public static readonly Guid WMMEDIASUBTYPE_MP4S = new Guid("5334504D-0000-0010-8000-00AA00389B71"); public static readonly Guid WMMEDIASUBTYPE_M4S2 = new Guid("3253344D-0000-0010-8000-00AA00389B71"); public static readonly Guid WMMEDIASUBTYPE_P422 = new Guid("32323450-0000-0010-8000-00AA00389B71"); public static readonly Guid WMMEDIASUBTYPE_MPEG2_VIDEO = new Guid("e06d8026-db46-11cf-b4d1-00805f6cbbea"); public static readonly Guid WMMEDIASUBTYPE_MSS1 = new Guid("3153534D-0000-0010-8000-00AA00389B71"); public static readonly Guid WMMEDIASUBTYPE_MSS2 = new Guid("3253534D-0000-0010-8000-00AA00389B71"); public static readonly Guid WMMEDIASUBTYPE_PCM = new Guid("00000001-0000-0010-8000-00AA00389B71"); public static readonly Guid WMMEDIASUBTYPE_WebStream = new Guid("776257d4-c627-41cb-8f81-7ac7ff1c40cc"); public static readonly Guid WMMEDIASUBTYPE_WMAudio_Lossless = new Guid("00000163-0000-0010-8000-00AA00389B71"); public static readonly Guid WMMEDIASUBTYPE_WMAudioV2 = new Guid("00000161-0000-0010-8000-00AA00389B71"); public static readonly Guid WMMEDIASUBTYPE_WMAudioV7 = new Guid("00000161-0000-0010-8000-00AA00389B71"); public static readonly Guid WMMEDIASUBTYPE_WMAudioV8 = new Guid("00000161-0000-0010-8000-00AA00389B71"); public static readonly Guid WMMEDIASUBTYPE_WMAudioV9 = new Guid("00000162-0000-0010-8000-00AA00389B71"); public static readonly Guid WMMEDIASUBTYPE_WMSP1 = new Guid("0000000A-0000-0010-8000-00AA00389B71"); public static readonly Guid WMMEDIASUBTYPE_WMV1 = new Guid("31564D57-0000-0010-8000-00AA00389B71"); public static readonly Guid WMMEDIASUBTYPE_WMV2 = new Guid("32564D57-0000-0010-8000-00AA00389B71"); public static readonly Guid WMMEDIASUBTYPE_WMV3 = new Guid("33564D57-0000-0010-8000-00AA00389B71"); public static readonly Guid WMMEDIASUBTYPE_WMVA = new Guid("41564D57-0000-0010-8000-00AA00389B71"); public static readonly Guid WMMEDIASUBTYPE_WMVP = new Guid("50564D57-0000-0010-8000-00AA00389B71"); public static readonly Guid WMMEDIASUBTYPE_WVP2 = new Guid("32505657-0000-0010-8000-00AA00389B71"); public static readonly Guid WMMEDIATYPE_Audio = new Guid("73647561-0000-0010-8000-00AA00389B71"); public static readonly Guid WMMEDIATYPE_FileTransfer = new Guid("D9E47579-930E-4427-ADFC-AD80F290E470"); public static readonly Guid WMMEDIATYPE_Image = new Guid("34A50FD8-8AA5-4386-81FE-A0EFE0488E31"); public static readonly Guid WMMEDIATYPE_Script = new Guid("73636d64-0000-0010-8000-00AA00389B71"); public static readonly Guid WMMEDIATYPE_Text = new Guid("9BBA1EA7-5AB2-4829-BA57-0940209BCF3E"); public static readonly Guid WMMEDIATYPE_Video = new Guid("73646976-0000-0010-8000-00AA00389B71"); public static readonly Guid WMSCRIPTTYPE_TwoStrings = new Guid("82f38a70-c29f-11d1-97ad-00a0c95ea850"); public static readonly Guid MEDIASUBTYPE_WAVE = new Guid("e436eb8b-524f-11ce-9f53-0020af0ba770"); public static readonly Guid MEDIASUBTYPE_AU = new Guid("e436eb8c-524f-11ce-9f53-0020af0ba770"); public static readonly Guid MEDIASUBTYPE_AIFF = new Guid("e436eb8d-524f-11ce-9f53-0020af0ba770"); public static readonly Guid[] AudioSubTypes = new Guid[13] { MEDIASUBTYPE_PCM, MEDIASUBTYPE_PCMAudioObsolete, MEDIASUBTYPE_MPEG1Packet, MEDIASUBTYPE_MPEG1Payload, MEDIASUBTYPE_MPEG2_AUDIO, MEDIASUBTYPE_DVD_LPCM_AUDIO, MEDIASUBTYPE_DRM_Audio, MEDIASUBTYPE_IEEE_FLOAT, MEDIASUBTYPE_DOLBY_AC3, MEDIASUBTYPE_DOLBY_AC3_SPDIF, MEDIASUBTYPE_RAW_SPORT, MEDIASUBTYPE_SPDIF_TAG_241h, WMMEDIASUBTYPE_MP3 }; public static readonly string[] AudioSubTypeNames = new string[13] { "PCM", "PCM Obsolete", "MPEG1Packet", "MPEG1Payload", "MPEG2_AUDIO", "DVD_LPCM_AUDIO", "DRM_Audio", "IEEE_FLOAT", "DOLBY_AC3", "DOLBY_AC3_SPDIF", "RAW_SPORT", "SPDIF_TAG_241h", "MP3" }; public static string GetAudioSubtypeName(Guid subType) { for (int i = 0; i < AudioSubTypes.Length; i++) { if (subType == AudioSubTypes[i]) { return AudioSubTypeNames[i]; } } return subType.ToString(); } } } namespace NAudio.Utils { public static class BufferHelpers { public static byte[] Ensure(byte[] buffer, int bytesRequired) { if (buffer == null || buffer.Length < bytesRequired) { buffer = new byte[bytesRequired]; } return buffer; } public static float[] Ensure(float[] buffer, int samplesRequired) { if (buffer == null || buffer.Length < samplesRequired) { buffer = new float[samplesRequired]; } return buffer; } } public static class ByteArrayExtensions { public static bool IsEntirelyNull(byte[] buffer) { for (int i = 0; i < buffer.Length; i++) { if (buffer[i] != 0) { return false; } } return true; } public static string DescribeAsHex(byte[] buffer, string separator, int bytesPerLine) { StringBuilder stringBuilder = new StringBuilder(); int num = 0; foreach (byte b in buffer) { stringBuilder.AppendFormat("{0:X2}{1}", b, separator); if (++num % bytesPerLine == 0) { stringBuilder.Append("\r\n"); } } stringBuilder.Append("\r\n"); return stringBuilder.ToString(); } public static string DecodeAsString(byte[] buffer, int offset, int length, Encoding encoding) { for (int i = 0; i < length; i++) { if (buffer[offset + i] == 0) { length = i; } } return encoding.GetString(buffer, offset, length); } public static byte[] Concat(params byte[][] byteArrays) { int num = 0; byte[][] array = byteArrays; foreach (byte[] array2 in array) { num += array2.Length; } if (num <= 0) { return new byte[0]; } byte[] array3 = new byte[num]; int num2 = 0; array = byteArrays; foreach (byte[] array4 in array) { Array.Copy(array4, 0, array3, num2, array4.Length); num2 += array4.Length; } return array3; } } public class ByteEncoding : Encoding { public static readonly ByteEncoding Instance = new ByteEncoding(); private ByteEncoding() { } public override int GetByteCount(char[] chars, int index, int count) { return count; } public override int GetBytes(char[] chars, int charIndex, int charCount, byte[] bytes, int byteIndex) { for (int i = 0; i < charCount; i++) { bytes[byteIndex + i] = (byte)chars[charIndex + i]; } return charCount; } public override int GetCharCount(byte[] bytes, int index, int count) { for (int i = 0; i < count; i++) { if (bytes[index + i] == 0) { return i; } } return count; } public override int GetChars(byte[] bytes, int byteIndex, int byteCount, char[] chars, int charIndex) { for (int i = 0; i < byteCount; i++) { byte b = bytes[byteIndex + i]; if (b == 0) { return i; } chars[charIndex + i] = (char)b; } return byteCount; } public override int GetMaxCharCount(int byteCount) { return byteCount; } public override int GetMaxByteCount(int charCount) { return charCount; } } public class ChunkIdentifier { public static int ChunkIdentifierToInt32(string s) { if (s.Length != 4) { throw new ArgumentException("Must be a four character string"); } byte[] bytes = Encoding.UTF8.GetBytes(s); if (bytes.Length != 4) { throw new ArgumentException("Must encode to exactly four bytes"); } return BitConverter.ToInt32(bytes, 0); } } public class CircularBuffer { private readonly byte[] buffer; private readonly object lockObject; private int writePosition; private int readPosition; private int byteCount; public int MaxLength => buffer.Length; public int Count { get { lock (lockObject) { return byteCount; } } } public CircularBuffer(int size) { buffer = new byte[size]; lockObject = new object(); } public int Write(byte[] data, int offset, int count) { lock (lockObject) { int num = 0; if (count > buffer.Length - byteCount) { count = buffer.Length - byteCount; } int num2 = Math.Min(buffer.Length - writePosition, count); Array.Copy(data, offset, buffer, writePosition, num2); writePosition += num2; writePosition %= buffer.Length; num += num2; if (num < count) { Array.Copy(data, offset + num, buffer, writePosition, count - num); writePosition += count - num; num = count; } byteCount += num; return num; } } public int Read(byte[] data, int offset, int count) { lock (lockObject) { if (count > byteCount) { count = byteCount; } int num = 0; int num2 = Math.Min(buffer.Length - readPosition, count); Array.Copy(buffer, readPosition, data, offset, num2); num += num2; readPosition += num2; readPosition %= buffer.Length; if (num < count) { Array.Copy(buffer, readPosition, data, offset + num, count - num); readPosition += count - num; num = count; } byteCount -= num; return num; } } public void Reset() { lock (lockObject) { ResetInner(); } } private void ResetInner() { byteCount = 0; readPosition = 0; writePosition = 0; } public void Advance(int count) { lock (lockObject) { if (count >= byteCount) { ResetInner(); return; } byteCount -= count; readPosition += count; readPosition %= MaxLength; } } } public class Decibels { private const double LOG_2_DB = 8.685889638065037; private const double DB_2_LOG = 0.11512925464970228; public static double LinearToDecibels(double lin) { return Math.Log(lin) * 8.685889638065037; } public static double DecibelsToLinear(double dB) { return Math.Exp(dB * 0.11512925464970228); } } [AttributeUsage(AttributeTargets.Field)] public class FieldDescriptionAttribute : Attribute { public string Description { get; } public FieldDescriptionAttribute(string description) { Description = description; } public override string ToString() { return Description; } } public static class FieldDescriptionHelper { public static string Describe(Type t, Guid guid) { FieldInfo[] fields = t.GetFields(BindingFlags.Static | BindingFlags.Public); foreach (FieldInfo fieldInfo in fields) { if (!fieldInfo.IsPublic || !fieldInfo.IsStatic || !(fieldInfo.FieldType == typeof(Guid)) || !((Guid)fieldInfo.GetValue(null) == guid)) { continue; } object[] customAttributes = fieldInfo.GetCustomAttributes(inherit: false); for (int j = 0; j < customAttributes.Length; j++) { if (customAttributes[j] is FieldDescriptionAttribute fieldDescriptionAttribute) { return fieldDescriptionAttribute.Description; } } return fieldInfo.Name; } return guid.ToString(); } } public static class HResult { public const int S_OK = 0; public const int S_FALSE = 1; public const int E_INVALIDARG = -2147483645; private const int FACILITY_AAF = 18; private const int FACILITY_ACS = 20; private const int FACILITY_BACKGROUNDCOPY = 32; private const int FACILITY_CERT = 11; private const int FACILITY_COMPLUS = 17; private const int FACILITY_CONFIGURATION = 33; private const int FACILITY_CONTROL = 10; private const int FACILITY_DISPATCH = 2; private const int FACILITY_DPLAY = 21; private const int FACILITY_HTTP = 25; private const int FACILITY_INTERNET = 12; private const int FACILITY_ITF = 4; private const int FACILITY_MEDIASERVER = 13; private const int FACILITY_MSMQ = 14; private const int FACILITY_NULL = 0; private const int FACILITY_RPC = 1; private const int FACILITY_SCARD = 16; private const int FACILITY_SECURITY = 9; private const int FACILITY_SETUPAPI = 15; private const int FACILITY_SSPI = 9; private const int FACILITY_STORAGE = 3; private const int FACILITY_SXS = 23; private const int FACILITY_UMI = 22; private const int FACILITY_URT = 19; private const int FACILITY_WIN32 = 7; private const int FACILITY_WINDOWS = 8; private const int FACILITY_WINDOWS_CE = 24; public static int MAKE_HRESULT(int sev, int fac, int code) { return (sev << 31) | (fac << 16) | code; } public static int GetHResult(this COMException exception) { return exception.ErrorCode; } } public static class IEEE { private static double UnsignedToFloat(ulong u) { return (double)(long)(u - int.MaxValue - 1) + 2147483648.0; } private static double ldexp(double x, int exp) { return x * Math.Pow(2.0, exp); } private static double frexp(double x, out int exp) { exp = (int)Math.Floor(Math.Log(x) / Math.Log(2.0)) + 1; return 1.0 - (Math.Pow(2.0, exp) - x) / Math.Pow(2.0, exp); } private static ulong FloatToUnsigned(double f) { return (ulong)((long)(f - 2147483648.0) + int.MaxValue + 1); } public static byte[] ConvertToIeeeExtended(double num) { int num2; if (num < 0.0) { num2 = 32768; num *= -1.0; } else { num2 = 0; } ulong num4; ulong num5; int num3; if (num == 0.0) { num3 = 0; num4 = 0uL; num5 = 0uL; } else { double num6 = frexp(num, out num3); if (num3 > 16384 || !(num6 < 1.0)) { num3 = num2 | 0x7FFF; num4 = 0uL; num5 = 0uL; } else { num3 += 16382; if (num3 < 0) { num6 = ldexp(num6, num3); num3 = 0; } num3 |= num2; num6 = ldexp(num6, 32); double num7 = Math.Floor(num6); num4 = FloatToUnsigned(num7); num6 = ldexp(num6 - num7, 32); num7 = Math.Floor(num6); num5 = FloatToUnsigned(num7); } } return new byte[10] { (byte)(num3 >> 8), (byte)num3, (byte)(num4 >> 24), (byte)(num4 >> 16), (byte)(num4 >> 8), (byte)num4, (byte)(num5 >> 24), (byte)(num5 >> 16), (byte)(num5 >> 8), (byte)num5 }; } public static double ConvertFromIeeeExtended(byte[] bytes) { if (bytes.Length != 10) { throw new Exception("Incorrect length for IEEE extended."); } int num = ((bytes[0] & 0x7F) << 8) | bytes[1]; uint num2 = (uint)((bytes[2] << 24) | (bytes[3] << 16) | (bytes[4] << 8) | bytes[5]); uint num3 = (uint)((bytes[6] << 24) | (bytes[7] << 16) | (bytes[8] << 8) | bytes[9]); double num4; if (num == 0 && num2 == 0 && num3 == 0) { num4 = 0.0; } else if (num == 32767) { num4 = double.NaN; } else { num -= 16383; num4 = ldexp(UnsignedToFloat(num2), num -= 31); num4 += ldexp(UnsignedToFloat(num3), num -= 32); } if ((bytes[0] & 0x80) == 128) { return 0.0 - num4; } return num4; } } public class IgnoreDisposeStream : Stream { public Stream SourceStream { get; private set; } public bool IgnoreDispose { get; set; } public override bool CanRead => SourceStream.CanRead; public override bool CanSeek => SourceStream.CanSeek; public override bool CanWrite => SourceStream.CanWrite; public override long Length => SourceStream.Length; public override long Position { get { return SourceStream.Position; } set { SourceStream.Position = value; } } public IgnoreDisposeStream(Stream sourceStream) { SourceStream = sourceStream; IgnoreDispose = true; } public override void Flush() { SourceStream.Flush(); } public override int Read(byte[] buffer, int offset, int count) { return SourceStream.Read(buffer, offset, count); } public override long Seek(long offset, SeekOrigin origin) { return SourceStream.Seek(offset, origin); } public override void SetLength(long value) { SourceStream.SetLength(value); } public override void Write(byte[] buffer, int offset, int count) { SourceStream.Write(buffer, offset, count); } protected override void Dispose(bool disposing) { if (!IgnoreDispose) { SourceStream.Dispose(); SourceStream = null; } } } public static class NativeMethods { [DllImport("kernel32.dll")] public static extern IntPtr LoadLibrary(string dllToLoad); [DllImport("kernel32.dll")] public static extern IntPtr GetProcAddress(IntPtr hModule, string procedureName); [DllImport("kernel32.dll")] public static extern bool FreeLibrary(IntPtr hModule); } public static class WavePositionExtensions { public static TimeSpan GetPositionTimeSpan(this IWavePosition @this) { return TimeSpan.FromMilliseconds((double)(@this.GetPosition() / (@this.OutputWaveFormat.Channels * @this.OutputWaveFormat.BitsPerSample / 8)) * 1000.0 / (double)@this.OutputWaveFormat.SampleRate); } } } namespace NAudio.FileFormats.Wav { public class WaveFileChunkReader { private WaveFormat waveFormat; private long dataChunkPosition; private long dataChunkLength; private List riffChunks; private readonly bool strictMode; private bool isRf64; private readonly bool storeAllChunks; private long riffSize; public WaveFormat WaveFormat => waveFormat; public long DataChunkPosition => dataChunkPosition; public long DataChunkLength => dataChunkLength; public List RiffChunks => riffChunks; public WaveFileChunkReader() { storeAllChunks = true; strictMode = false; } public void ReadWaveHeader(Stream stream) { dataChunkPosition = -1L; waveFormat = null; riffChunks = new List(); dataChunkLength = 0L; BinaryReader binaryReader = new BinaryReader(stream); ReadRiffHeader(binaryReader); riffSize = binaryReader.ReadUInt32(); if (binaryReader.ReadInt32() != ChunkIdentifier.ChunkIdentifierToInt32("WAVE")) { throw new FormatException("Not a WAVE file - no WAVE header"); } if (isRf64) { ReadDs64Chunk(binaryReader); } int num = ChunkIdentifier.ChunkIdentifierToInt32("data"); int num2 = ChunkIdentifier.ChunkIdentifierToInt32("fmt "); long num3 = Math.Min(riffSize + 8, stream.Length); while (stream.Position <= num3 - 8) { int num4 = binaryReader.ReadInt32(); uint num5 = binaryReader.ReadUInt32(); if (num4 == num) { dataChunkPosition = stream.Position; if (!isRf64) { dataChunkLength = num5; } stream.Position += dataChunkLength; } else if (num4 == num2) { if (num5 > int.MaxValue) { throw new InvalidDataException($"Format chunk length must be between 0 and {int.MaxValue}."); } waveFormat = WaveFormat.FromFormatChunk(binaryReader, (int)num5); } else { if (num5 > stream.Length - stream.Position) { if (!strictMode) { } break; } if (storeAllChunks) { if (num5 > int.MaxValue) { throw new InvalidDataException($"RiffChunk chunk length must be between 0 and {int.MaxValue}."); } riffChunks.Add(GetRiffChunk(stream, num4, (int)num5)); } stream.Position += num5; } if (num5 % 2 != 0 && binaryReader.PeekChar() == 0) { stream.Position++; } } if (waveFormat == null) { throw new FormatException("Invalid WAV file - No fmt chunk found"); } if (dataChunkPosition == -1) { throw new FormatException("Invalid WAV file - No data chunk found"); } } private void ReadDs64Chunk(BinaryReader reader) { int num = ChunkIdentifier.ChunkIdentifierToInt32("ds64"); if (reader.ReadInt32() != num) { throw new FormatException("Invalid RF64 WAV file - No ds64 chunk found"); } int num2 = reader.ReadInt32(); riffSize = reader.ReadInt64(); dataChunkLength = reader.ReadInt64(); reader.ReadInt64(); reader.ReadBytes(num2 - 24); } private static RiffChunk GetRiffChunk(Stream stream, int chunkIdentifier, int chunkLength) { return new RiffChunk(chunkIdentifier, chunkLength, stream.Position); } private void ReadRiffHeader(BinaryReader br) { int num = br.ReadInt32(); if (num == ChunkIdentifier.ChunkIdentifierToInt32("RF64")) { isRf64 = true; } else if (num != ChunkIdentifier.ChunkIdentifierToInt32("RIFF")) { throw new FormatException("Not a WAVE file - no RIFF header"); } } } } namespace NAudio.SoundFont { public class Generator { public GeneratorEnum GeneratorType { get; set; } public ushort UInt16Amount { get; set; } public short Int16Amount { get { return (short)UInt16Amount; } set { UInt16Amount = (ushort)value; } } public byte LowByteAmount { get { return (byte)(UInt16Amount & 0xFFu); } set { UInt16Amount &= 65280; UInt16Amount += value; } } public byte HighByteAmount { get { return (byte)((UInt16Amount & 0xFF00) >> 8); } set { UInt16Amount &= 255; UInt16Amount += (ushort)(value << 8); } } public Instrument Instrument { get; set; } public SampleHeader SampleHeader { get; set; } public override string ToString() { if (GeneratorType == GeneratorEnum.Instrument) { return "Generator Instrument " + Instrument.Name; } if (GeneratorType == GeneratorEnum.SampleID) { return $"Generator SampleID {SampleHeader}"; } return $"Generator {GeneratorType} {UInt16Amount}"; } } internal class GeneratorBuilder : StructureBuilder { public override int Length => 4; public Generator[] Generators => data.ToArray(); public override Generator Read(BinaryReader br) { Generator generator = new Generator(); generator.GeneratorType = (GeneratorEnum)br.ReadUInt16(); generator.UInt16Amount = br.ReadUInt16(); data.Add(generator); return generator; } public override void Write(BinaryWriter bw, Generator o) { } public void Load(Instrument[] instruments) { Generator[] generators = Generators; foreach (Generator generator in generators) { if (generator.GeneratorType == GeneratorEnum.Instrument) { generator.Instrument = instruments[generator.UInt16Amount]; } } } public void Load(SampleHeader[] sampleHeaders) { Generator[] generators = Generators; foreach (Generator generator in generators) { if (generator.GeneratorType == GeneratorEnum.SampleID) { generator.SampleHeader = sampleHeaders[generator.UInt16Amount]; } } } } public enum GeneratorEnum { StartAddressOffset, EndAddressOffset, StartLoopAddressOffset, EndLoopAddressOffset, StartAddressCoarseOffset, ModulationLFOToPitch, VibratoLFOToPitch, ModulationEnvelopeToPitch, InitialFilterCutoffFrequency, InitialFilterQ, ModulationLFOToFilterCutoffFrequency, ModulationEnvelopeToFilterCutoffFrequency, EndAddressCoarseOffset, ModulationLFOToVolume, Unused1, ChorusEffectsSend, ReverbEffectsSend, Pan, Unused2, Unused3, Unused4, DelayModulationLFO, FrequencyModulationLFO, DelayVibratoLFO, FrequencyVibratoLFO, DelayModulationEnvelope, AttackModulationEnvelope, HoldModulationEnvelope, DecayModulationEnvelope, SustainModulationEnvelope, ReleaseModulationEnvelope, KeyNumberToModulationEnvelopeHold, KeyNumberToModulationEnvelopeDecay, DelayVolumeEnvelope, AttackVolumeEnvelope, HoldVolumeEnvelope, DecayVolumeEnvelope, SustainVolumeEnvelope, ReleaseVolumeEnvelope, KeyNumberToVolumeEnvelopeHold, KeyNumberToVolumeEnvelopeDecay, Instrument, Reserved1, KeyRange, VelocityRange, StartLoopAddressCoarseOffset, KeyNumber, Velocity, InitialAttenuation, Reserved2, EndLoopAddressCoarseOffset, CoarseTune, FineTune, SampleID, SampleModes, Reserved3, ScaleTuning, ExclusiveClass, OverridingRootKey, Unused5, UnusedEnd } public class InfoChunk { public SFVersion SoundFontVersion { get; } public string WaveTableSoundEngine { get; set; } public string BankName { get; set; } public string DataROM { get; set; } public string CreationDate { get; set; } public string Author { get; set; } public string TargetProduct { get; set; } public string Copyright { get; set; } public string Comments { get; set; } public string Tools { get; set; } public SFVersion ROMVersion { get; set; } internal InfoChunk(RiffChunk chunk) { bool flag = false; bool flag2 = false; if (chunk.ReadChunkID() != "INFO") { throw new InvalidDataException("Not an INFO chunk"); } RiffChunk nextSubChunk; while ((nextSubChunk = chunk.GetNextSubChunk()) != null) { switch (nextSubChunk.ChunkID) { case "ifil": flag = true; SoundFontVersion = nextSubChunk.GetDataAsStructure(new SFVersionBuilder()); break; case "isng": WaveTableSoundEngine = nextSubChunk.GetDataAsString(); break; case "INAM": flag2 = true; BankName = nextSubChunk.GetDataAsString(); break; case "irom": DataROM = nextSubChunk.GetDataAsString(); break; case "iver": ROMVersion = nextSubChunk.GetDataAsStructure(new SFVersionBuilder()); break; case "ICRD": CreationDate = nextSubChunk.GetDataAsString(); break; case "IENG": Author = nextSubChunk.GetDataAsString(); break; case "IPRD": TargetProduct = nextSubChunk.GetDataAsString(); break; case "ICOP": Copyright = nextSubChunk.GetDataAsString(); break; case "ICMT": Comments = nextSubChunk.GetDataAsString(); break; case "ISFT": Tools = nextSubChunk.GetDataAsString(); break; default: throw new InvalidDataException("Unknown chunk type " + nextSubChunk.ChunkID); } } if (!flag) { throw new InvalidDataException("Missing SoundFont version information"); } if (!flag2) { throw new InvalidDataException("Missing SoundFont name information"); } } public override string ToString() { return string.Format("Bank Name: {0}\r\nAuthor: {1}\r\nCopyright: {2}\r\nCreation Date: {3}\r\nTools: {4}\r\nComments: {5}\r\nSound Engine: {6}\r\nSoundFont Version: {7}\r\nTarget Product: {8}\r\nData ROM: {9}\r\nROM Version: {10}", BankName, Author, Copyright, CreationDate, Tools, "TODO-fix comments", WaveTableSoundEngine, SoundFontVersion, TargetProduct, DataROM, ROMVersion); } } public class Instrument { internal ushort startInstrumentZoneIndex; internal ushort endInstrumentZoneIndex; public string Name { get; set; } public Zone[] Zones { get; set; } public override string ToString() { return Name; } } internal class InstrumentBuilder : StructureBuilder { private Instrument lastInstrument; public override int Length => 22; public Instrument[] Instruments => data.ToArray(); public override Instrument Read(BinaryReader br) { Instrument instrument = new Instrument(); string text = Encoding.UTF8.GetString(br.ReadBytes(20), 0, 20); if (text.IndexOf('\0') >= 0) { text = text.Substring(0, text.IndexOf('\0')); } instrument.Name = text; instrument.startInstrumentZoneIndex = br.ReadUInt16(); if (lastInstrument != null) { lastInstrument.endInstrumentZoneIndex = (ushort)(instrument.startInstrumentZoneIndex - 1); } data.Add(instrument); lastInstrument = instrument; return instrument; } public override void Write(BinaryWriter bw, Instrument instrument) { } public void LoadZones(Zone[] zones) { for (int i = 0; i < data.Count - 1; i++) { Instrument instrument = data[i]; instrument.Zones = new Zone[instrument.endInstrumentZoneIndex - instrument.startInstrumentZoneIndex + 1]; Array.Copy(zones, instrument.startInstrumentZoneIndex, instrument.Zones, 0, instrument.Zones.Length); } data.RemoveAt(data.Count - 1); } } public enum TransformEnum { Linear } public class Modulator { public ModulatorType SourceModulationData { get; set; } public GeneratorEnum DestinationGenerator { get; set; } public short Amount { get; set; } public ModulatorType SourceModulationAmount { get; set; } public TransformEnum SourceTransform { get; set; } public override string ToString() { return $"Modulator {SourceModulationData} {DestinationGenerator} {Amount} {SourceModulationAmount} {SourceTransform}"; } } internal class ModulatorBuilder : StructureBuilder { public override int Length => 10; public Modulator[] Modulators => data.ToArray(); public override Modulator Read(BinaryReader br) { Modulator modulator = new Modulator(); modulator.SourceModulationData = new ModulatorType(br.ReadUInt16()); modulator.DestinationGenerator = (GeneratorEnum)br.ReadUInt16(); modulator.Amount = br.ReadInt16(); modulator.SourceModulationAmount = new ModulatorType(br.ReadUInt16()); modulator.SourceTransform = (TransformEnum)br.ReadUInt16(); data.Add(modulator); return modulator; } public override void Write(BinaryWriter bw, Modulator o) { } } public enum ControllerSourceEnum { NoController = 0, NoteOnVelocity = 2, NoteOnKeyNumber = 3, PolyPressure = 10, ChannelPressure = 13, PitchWheel = 14, PitchWheelSensitivity = 16 } public enum SourceTypeEnum { Linear, Concave, Convex, Switch } public class ModulatorType { private bool polarity; private bool direction; private bool midiContinuousController; private ControllerSourceEnum controllerSource; private SourceTypeEnum sourceType; private ushort midiContinuousControllerNumber; internal ModulatorType(ushort raw) { polarity = (raw & 0x200) == 512; direction = (raw & 0x100) == 256; midiContinuousController = (raw & 0x80) == 128; sourceType = (SourceTypeEnum)((raw & 0xFC00) >> 10); controllerSource = (ControllerSourceEnum)(raw & 0x7F); midiContinuousControllerNumber = (ushort)(raw & 0x7Fu); } public override string ToString() { if (midiContinuousController) { return $"{sourceType} CC{midiContinuousControllerNumber}"; } return $"{sourceType} {controllerSource}"; } } public class Preset { internal ushort startPresetZoneIndex; internal ushort endPresetZoneIndex; internal uint library; internal uint genre; internal uint morphology; public string Name { get; set; } public ushort PatchNumber { get; set; } public ushort Bank { get; set; } public Zone[] Zones { get; set; } public override string ToString() { return $"{Bank}-{PatchNumber} {Name}"; } } internal class PresetBuilder : StructureBuilder { private Preset lastPreset; public override int Length => 38; public Preset[] Presets => data.ToArray(); public override Preset Read(BinaryReader br) { Preset preset = new Preset(); string text = Encoding.UTF8.GetString(br.ReadBytes(20), 0, 20); if (text.IndexOf('\0') >= 0) { text = text.Substring(0, text.IndexOf('\0')); } preset.Name = text; preset.PatchNumber = br.ReadUInt16(); preset.Bank = br.ReadUInt16(); preset.startPresetZoneIndex = br.ReadUInt16(); preset.library = br.ReadUInt32(); preset.genre = br.ReadUInt32(); preset.morphology = br.ReadUInt32(); if (lastPreset != null) { lastPreset.endPresetZoneIndex = (ushort)(preset.startPresetZoneIndex - 1); } data.Add(preset); lastPreset = preset; return preset; } public override void Write(BinaryWriter bw, Preset preset) { } public void LoadZones(Zone[] presetZones) { for (int i = 0; i < data.Count - 1; i++) { Preset preset = data[i]; preset.Zones = new Zone[preset.endPresetZoneIndex - preset.startPresetZoneIndex + 1]; Array.Copy(presetZones, preset.startPresetZoneIndex, preset.Zones, 0, preset.Zones.Length); } data.RemoveAt(data.Count - 1); } } public class PresetsChunk { private PresetBuilder presetHeaders = new PresetBuilder(); private ZoneBuilder presetZones = new ZoneBuilder(); private ModulatorBuilder presetZoneModulators = new ModulatorBuilder(); private GeneratorBuilder presetZoneGenerators = new GeneratorBuilder(); private InstrumentBuilder instruments = new InstrumentBuilder(); private ZoneBuilder instrumentZones = new ZoneBuilder(); private ModulatorBuilder instrumentZoneModulators = new ModulatorBuilder(); private GeneratorBuilder instrumentZoneGenerators = new GeneratorBuilder(); private SampleHeaderBuilder sampleHeaders = new SampleHeaderBuilder(); public Preset[] Presets => presetHeaders.Presets; public Instrument[] Instruments => instruments.Instruments; public SampleHeader[] SampleHeaders => sampleHeaders.SampleHeaders; internal PresetsChunk(RiffChunk chunk) { string text = chunk.ReadChunkID(); if (text != "pdta") { throw new InvalidDataException($"Not a presets data chunk ({text})"); } RiffChunk nextSubChunk; while ((nextSubChunk = chunk.GetNextSubChunk()) != null) { switch (nextSubChunk.ChunkID) { case "phdr": case "PHDR": nextSubChunk.GetDataAsStructureArray(presetHeaders); break; case "pbag": case "PBAG": nextSubChunk.GetDataAsStructureArray(presetZones); break; case "pmod": case "PMOD": nextSubChunk.GetDataAsStructureArray(presetZoneModulators); break; case "pgen": case "PGEN": nextSubChunk.GetDataAsStructureArray(presetZoneGenerators); break; case "inst": case "INST": nextSubChunk.GetDataAsStructureArray(instruments); break; case "ibag": case "IBAG": nextSubChunk.GetDataAsStructureArray(instrumentZones); break; case "imod": case "IMOD": nextSubChunk.GetDataAsStructureArray(instrumentZoneModulators); break; case "igen": case "IGEN": nextSubChunk.GetDataAsStructureArray(instrumentZoneGenerators); break; case "shdr": case "SHDR": nextSubChunk.GetDataAsStructureArray(sampleHeaders); break; default: throw new InvalidDataException($"Unknown chunk type {nextSubChunk.ChunkID}"); } } instrumentZoneGenerators.Load(sampleHeaders.SampleHeaders); instrumentZones.Load(instrumentZoneModulators.Modulators, instrumentZoneGenerators.Generators); instruments.LoadZones(instrumentZones.Zones); presetZoneGenerators.Load(instruments.Instruments); presetZones.Load(presetZoneModulators.Modulators, presetZoneGenerators.Generators); presetHeaders.LoadZones(presetZones.Zones); sampleHeaders.RemoveEOS(); } public override string ToString() { StringBuilder stringBuilder = new StringBuilder(); stringBuilder.Append("Preset Headers:\r\n"); Preset[] presets = presetHeaders.Presets; foreach (Preset arg in presets) { stringBuilder.AppendFormat("{0}\r\n", arg); } stringBuilder.Append("Instruments:\r\n"); Instrument[] array = instruments.Instruments; foreach (Instrument arg2 in array) { stringBuilder.AppendFormat("{0}\r\n", arg2); } return stringBuilder.ToString(); } } internal class RiffChunk { private string chunkID; private BinaryReader riffFile; public string ChunkID { get { return chunkID; } set { if (value == null) { throw new ArgumentNullException("ChunkID may not be null"); } if (value.Length != 4) { throw new ArgumentException("ChunkID must be four characters"); } chunkID = value; } } public uint ChunkSize { get; private set; } public long DataOffset { get; private set; } public static RiffChunk GetTopLevelChunk(BinaryReader file) { RiffChunk riffChunk = new RiffChunk(file); riffChunk.ReadChunk(); return riffChunk; } private RiffChunk(BinaryReader file) { riffFile = file; chunkID = "????"; ChunkSize = 0u; DataOffset = 0L; } public string ReadChunkID() { byte[] array = riffFile.ReadBytes(4); if (array.Length != 4) { throw new InvalidDataException("Couldn't read Chunk ID"); } return ByteEncoding.Instance.GetString(array, 0, array.Length); } private void ReadChunk() { chunkID = ReadChunkID(); ChunkSize = riffFile.ReadUInt32(); DataOffset = riffFile.BaseStream.Position; } public RiffChunk GetNextSubChunk() { if (riffFile.BaseStream.Position + 8 < DataOffset + ChunkSize) { RiffChunk riffChunk = new RiffChunk(riffFile); riffChunk.ReadChunk(); return riffChunk; } return null; } public byte[] GetData() { riffFile.BaseStream.Position = DataOffset; byte[] array = riffFile.ReadBytes((int)ChunkSize); if (array.Length != ChunkSize) { throw new InvalidDataException($"Couldn't read chunk's data Chunk: {this}, read {array.Length} bytes"); } return array; } public string GetDataAsString() { byte[] data = GetData(); if (data == null) { return null; } return ByteEncoding.Instance.GetString(data, 0, data.Length); } public T GetDataAsStructure(StructureBuilder s) { riffFile.BaseStream.Position = DataOffset; if (s.Length != ChunkSize) { throw new InvalidDataException($"Chunk size is: {ChunkSize} so can't read structure of: {s.Length}"); } return s.Read(riffFile); } public T[] GetDataAsStructureArray(StructureBuilder s) { riffFile.BaseStream.Position = DataOffset; if (ChunkSize % s.Length != 0L) { throw new InvalidDataException($"Chunk size is: {ChunkSize} not a multiple of structure size: {s.Length}"); } int num = (int)(ChunkSize / s.Length); T[] array = new T[num]; for (int i = 0; i < num; i++) { array[i] = s.Read(riffFile); } return array; } public override string ToString() { return $"RiffChunk ID: {ChunkID} Size: {ChunkSize} Data Offset: {DataOffset}"; } } internal class SampleDataChunk { public byte[] SampleData { get; private set; } public SampleDataChunk(RiffChunk chunk) { string text = chunk.ReadChunkID(); if (text != "sdta") { throw new InvalidDataException("Not a sample data chunk (" + text + ")"); } SampleData = chunk.GetData(); } } public class SampleHeader { public string SampleName; public uint Start; public uint End; public uint StartLoop; public uint EndLoop; public uint SampleRate; public byte OriginalPitch; public sbyte PitchCorrection; public ushort SampleLink; public SFSampleLink SFSampleLink; public override string ToString() { return SampleName; } } internal class SampleHeaderBuilder : StructureBuilder { public override int Length => 46; public SampleHeader[] SampleHeaders => data.ToArray(); public override SampleHeader Read(BinaryReader br) { SampleHeader sampleHeader = new SampleHeader(); byte[] array = br.ReadBytes(20); sampleHeader.SampleName = ByteEncoding.Instance.GetString(array, 0, array.Length); sampleHeader.Start = br.ReadUInt32(); sampleHeader.End = br.ReadUInt32(); sampleHeader.StartLoop = br.ReadUInt32(); sampleHeader.EndLoop = br.ReadUInt32(); sampleHeader.SampleRate = br.ReadUInt32(); sampleHeader.OriginalPitch = br.ReadByte(); sampleHeader.PitchCorrection = br.ReadSByte(); sampleHeader.SampleLink = br.ReadUInt16(); sampleHeader.SFSampleLink = (SFSampleLink)br.ReadUInt16(); data.Add(sampleHeader); return sampleHeader; } public override void Write(BinaryWriter bw, SampleHeader sampleHeader) { } internal void RemoveEOS() { data.RemoveAt(data.Count - 1); } } public enum SampleMode { NoLoop, LoopContinuously, ReservedNoLoop, LoopAndContinue } public enum SFSampleLink : ushort { MonoSample = 1, RightSample = 2, LeftSample = 4, LinkedSample = 8, RomMonoSample = 32769, RomRightSample = 32770, RomLeftSample = 32772, RomLinkedSample = 32776 } public class SFVersion { public short Major { get; set; } public short Minor { get; set; } } internal class SFVersionBuilder : StructureBuilder { public override int Length => 4; public override SFVersion Read(BinaryReader br) { SFVersion sFVersion = new SFVersion(); sFVersion.Major = br.ReadInt16(); sFVersion.Minor = br.ReadInt16(); data.Add(sFVersion); return sFVersion; } public override void Write(BinaryWriter bw, SFVersion v) { bw.Write(v.Major); bw.Write(v.Minor); } } public class SoundFont { private InfoChunk info; private PresetsChunk presetsChunk; private SampleDataChunk sampleData; public InfoChunk FileInfo => info; public Preset[] Presets => presetsChunk.Presets; public Instrument[] Instruments => presetsChunk.Instruments; public SampleHeader[] SampleHeaders => presetsChunk.SampleHeaders; public byte[] SampleData => sampleData.SampleData; public SoundFont(string fileName) : this(new FileStream(fileName, FileMode.Open, FileAccess.Read)) { } public SoundFont(Stream sfFile) { using (sfFile) { RiffChunk topLevelChunk = RiffChunk.GetTopLevelChunk(new BinaryReader(sfFile)); if (topLevelChunk.ChunkID == "RIFF") { string text = topLevelChunk.ReadChunkID(); if (text != "sfbk") { throw new InvalidDataException($"Not a SoundFont ({text})"); } RiffChunk nextSubChunk = topLevelChunk.GetNextSubChunk(); if (nextSubChunk.ChunkID == "LIST") { info = new InfoChunk(nextSubChunk); RiffChunk nextSubChunk2 = topLevelChunk.GetNextSubChunk(); sampleData = new SampleDataChunk(nextSubChunk2); nextSubChunk2 = topLevelChunk.GetNextSubChunk(); presetsChunk = new PresetsChunk(nextSubChunk2); return; } throw new InvalidDataException($"Not info list found ({nextSubChunk.ChunkID})"); } throw new InvalidDataException("Not a RIFF file"); } } public override string ToString() { return $"Info Chunk:\r\n{info}\r\nPresets Chunk:\r\n{presetsChunk}"; } } internal abstract class StructureBuilder { protected List data; public abstract int Length { get; } public T[] Data => data.ToArray(); public StructureBuilder() { Reset(); } public abstract T Read(BinaryReader br); public abstract void Write(BinaryWriter bw, T o); public void Reset() { data = new List(); } } public class Zone { internal ushort generatorIndex; internal ushort modulatorIndex; internal ushort generatorCount; internal ushort modulatorCount; public Modulator[] Modulators { get; set; } public Generator[] Generators { get; set; } public override string ToString() { return $"Zone {generatorCount} Gens:{generatorIndex} {modulatorCount} Mods:{modulatorIndex}"; } } internal class ZoneBuilder : StructureBuilder { private Zone lastZone; public Zone[] Zones => data.ToArray(); public override int Length => 4; public override Zone Read(BinaryReader br) { Zone zone = new Zone(); zone.generatorIndex = br.ReadUInt16(); zone.modulatorIndex = br.ReadUInt16(); if (lastZone != null) { lastZone.generatorCount = (ushort)(zone.generatorIndex - lastZone.generatorIndex); lastZone.modulatorCount = (ushort)(zone.modulatorIndex - lastZone.modulatorIndex); } data.Add(zone); lastZone = zone; return zone; } public override void Write(BinaryWriter bw, Zone zone) { } public void Load(Modulator[] modulators, Generator[] generators) { for (int i = 0; i < data.Count - 1; i++) { Zone zone = data[i]; zone.Generators = new Generator[zone.generatorCount]; Array.Copy(generators, zone.generatorIndex, zone.Generators, 0, zone.generatorCount); zone.Modulators = new Modulator[zone.modulatorCount]; Array.Copy(modulators, zone.modulatorIndex, zone.Modulators, 0, zone.modulatorCount); } data.RemoveAt(data.Count - 1); } } } namespace NAudio.Wave { public enum ChannelMode { Stereo, JointStereo, DualChannel, Mono } public class Id3v2Tag { private long tagStartPosition; private long tagEndPosition; private byte[] rawData; public byte[] RawData => rawData; public static Id3v2Tag ReadTag(Stream input) { try { return new Id3v2Tag(input); } catch (FormatException) { return null; } } public static Id3v2Tag Create(IEnumerable> tags) { return ReadTag(CreateId3v2TagStream(tags)); } private static byte[] FrameSizeToBytes(int n) { byte[] bytes = BitConverter.GetBytes(n); Array.Reverse((Array)bytes); return bytes; } private static byte[] CreateId3v2Frame(string key, string value) { if (string.IsNullOrEmpty(key)) { throw new ArgumentNullException("key"); } if (string.IsNullOrEmpty(value)) { throw new ArgumentNullException("value"); } if (key.Length != 4) { throw new ArgumentOutOfRangeException("key", "key " + key + " must be 4 characters long"); } byte[] array = new byte[2] { 255, 254 }; byte[] array2 = new byte[3]; byte[] array3 = new byte[2]; byte[] array4 = ((!(key == "COMM")) ? ByteArrayExtensions.Concat(new byte[1] { 1 }, array, Encoding.Unicode.GetBytes(value)) : ByteArrayExtensions.Concat(new byte[1] { 1 }, array2, array3, array, Encoding.Unicode.GetBytes(value))); return ByteArrayExtensions.Concat(Encoding.UTF8.GetBytes(key), FrameSizeToBytes(array4.Length), new byte[2], array4); } private static byte[] GetId3TagHeaderSize(int size) { byte[] array = new byte[4]; for (int num = array.Length - 1; num >= 0; num--) { array[num] = (byte)(size % 128); size /= 128; } return array; } private static byte[] CreateId3v2TagHeader(IEnumerable frames) { int num = 0; foreach (byte[] frame in frames) { num += frame.Length; } return ByteArrayExtensions.Concat(Encoding.UTF8.GetBytes("ID3"), new byte[2] { 3, 0 }, new byte[1], GetId3TagHeaderSize(num)); } private static Stream CreateId3v2TagStream(IEnumerable> tags) { List list = new List(); foreach (KeyValuePair tag in tags) { list.Add(CreateId3v2Frame(tag.Key, tag.Value)); } byte[] array = CreateId3v2TagHeader(list); MemoryStream memoryStream = new MemoryStream(); memoryStream.Write(array, 0, array.Length); foreach (byte[] item in list) { memoryStream.Write(item, 0, item.Length); } memoryStream.Position = 0L; return memoryStream; } private Id3v2Tag(Stream input) { tagStartPosition = input.Position; BinaryReader binaryReader = new BinaryReader(input); byte[] array = binaryReader.ReadBytes(10); if (array.Length >= 3 && array[0] == 73 && array[1] == 68 && array[2] == 51) { if ((array[5] & 0x40) == 64) { byte[] array2 = binaryReader.ReadBytes(4); _ = array2[0] * 2097152 + array2[1] * 16384 + array2[2] * 128; _ = array2[3]; } int num = array[6] * 2097152; num += array[7] * 16384; num += array[8] * 128; num += array[9]; binaryReader.ReadBytes(num); if ((array[5] & 0x10) == 16) { binaryReader.ReadBytes(10); } tagEndPosition = input.Position; input.Position = tagStartPosition; rawData = binaryReader.ReadBytes((int)(tagEndPosition - tagStartPosition)); return; } input.Position = tagStartPosition; throw new FormatException("Not an ID3v2 tag"); } } public interface IMp3FrameDecompressor : IDisposable { WaveFormat OutputFormat { get; } int DecompressFrame(Mp3Frame frame, byte[] dest, int destOffset); void Reset(); } public class Mp3Frame { private static readonly int[,,] bitRates = new int[2, 3, 15] { { { 0, 32, 64, 96, 128, 160, 192, 224, 256, 288, 320, 352, 384, 416, 448 }, { 0, 32, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320, 384 }, { 0, 32, 40, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320 } }, { { 0, 32, 48, 56, 64, 80, 96, 112, 128, 144, 160, 176, 192, 224, 256 }, { 0, 8, 16, 24, 32, 40, 48, 56, 64, 80, 96, 112, 128, 144, 160 }, { 0, 8, 16, 24, 32, 40, 48, 56, 64, 80, 96, 112, 128, 144, 160 } } }; private static readonly int[,] samplesPerFrame = new int[2, 3] { { 384, 1152, 1152 }, { 384, 1152, 576 } }; private static readonly int[] sampleRatesVersion1 = new int[3] { 44100, 48000, 32000 }; private static readonly int[] sampleRatesVersion2 = new int[3] { 22050, 24000, 16000 }; private static readonly int[] sampleRatesVersion25 = new int[3] { 11025, 12000, 8000 }; private const int MaxFrameLength = 16384; public int SampleRate { get; private set; } public int FrameLength { get; private set; } public int BitRate { get; private set; } public byte[] RawData { get; private set; } public MpegVersion MpegVersion { get; private set; } public MpegLayer MpegLayer { get; private set; } public ChannelMode ChannelMode { get; private set; } public int SampleCount { get; private set; } public int ChannelExtension { get; private set; } public int BitRateIndex { get; private set; } public bool Copyright { get; private set; } public bool CrcPresent { get; private set; } public long FileOffset { get; private set; } public static Mp3Frame LoadFromStream(Stream input) { return LoadFromStream(input, readData: true); } public static Mp3Frame LoadFromStream(Stream input, bool readData) { Mp3Frame mp3Frame = new Mp3Frame(); mp3Frame.FileOffset = input.Position; byte[] array = new byte[4]; if (input.Read(array, 0, array.Length) < array.Length) { return null; } while (!IsValidHeader(array, mp3Frame)) { array[0] = array[1]; array[1] = array[2]; array[2] = array[3]; if (input.Read(array, 3, 1) < 1) { return null; } mp3Frame.FileOffset++; } int num = mp3Frame.FrameLength - 4; if (readData) { mp3Frame.RawData = new byte[mp3Frame.FrameLength]; Array.Copy(array, mp3Frame.RawData, 4); if (input.Read(mp3Frame.RawData, 4, num) < num) { throw new EndOfStreamException("Unexpected end of stream before frame complete"); } } else { input.Position += num; } return mp3Frame; } private Mp3Frame() { } private static bool IsValidHeader(byte[] headerBytes, Mp3Frame frame) { if (headerBytes[0] == byte.MaxValue && (headerBytes[1] & 0xE0) == 224) { frame.MpegVersion = (MpegVersion)((headerBytes[1] & 0x18) >> 3); if (frame.MpegVersion == MpegVersion.Reserved) { return false; } frame.MpegLayer = (MpegLayer)((headerBytes[1] & 6) >> 1); if (frame.MpegLayer == MpegLayer.Reserved) { return false; } int num = ((frame.MpegLayer != MpegLayer.Layer1) ? ((frame.MpegLayer == MpegLayer.Layer2) ? 1 : 2) : 0); frame.CrcPresent = (headerBytes[1] & 1) == 0; frame.BitRateIndex = (headerBytes[2] & 0xF0) >> 4; if (frame.BitRateIndex == 15) { return false; } int num2 = ((frame.MpegVersion != MpegVersion.Version1) ? 1 : 0); frame.BitRate = bitRates[num2, num, frame.BitRateIndex] * 1000; if (frame.BitRate == 0) { return false; } int num3 = (headerBytes[2] & 0xC) >> 2; if (num3 == 3) { return false; } if (frame.MpegVersion == MpegVersion.Version1) { frame.SampleRate = sampleRatesVersion1[num3]; } else if (frame.MpegVersion == MpegVersion.Version2) { frame.SampleRate = sampleRatesVersion2[num3]; } else { frame.SampleRate = sampleRatesVersion25[num3]; } bool flag = (headerBytes[2] & 2) == 2; _ = headerBytes[2]; frame.ChannelMode = (ChannelMode)((headerBytes[3] & 0xC0) >> 6); frame.ChannelExtension = (headerBytes[3] & 0x30) >> 4; if (frame.ChannelExtension != 0 && frame.ChannelMode != ChannelMode.JointStereo) { return false; } frame.Copyright = (headerBytes[3] & 8) == 8; _ = headerBytes[3]; _ = headerBytes[3]; int num4 = (flag ? 1 : 0); frame.SampleCount = samplesPerFrame[num2, num]; int num5 = frame.SampleCount / 8; if (frame.MpegLayer == MpegLayer.Layer1) { frame.FrameLength = (num5 * frame.BitRate / frame.SampleRate + num4) * 4; } else { frame.FrameLength = num5 * frame.BitRate / frame.SampleRate + num4; } if (frame.FrameLength > 16384) { return false; } return true; } return false; } } public enum MpegLayer { Reserved, Layer3, Layer2, Layer1 } public enum MpegVersion { Version25, Reserved, Version2, Version1 } public class XingHeader { [Flags] private enum XingHeaderOptions { Frames = 1, Bytes = 2, Toc = 4, VbrScale = 8 } private static int[] sr_table = new int[4] { 44100, 48000, 32000, 99999 }; private int vbrScale = -1; private int startOffset; private int endOffset; private int tocOffset = -1; private int framesOffset = -1; private int bytesOffset = -1; private Mp3Frame frame; public int Frames { get { if (framesOffset == -1) { return -1; } return ReadBigEndian(frame.RawData, framesOffset); } set { if (framesOffset == -1) { throw new InvalidOperationException("Frames flag is not set"); } WriteBigEndian(frame.RawData, framesOffset, value); } } public int Bytes { get { if (bytesOffset == -1) { return -1; } return ReadBigEndian(frame.RawData, bytesOffset); } set { if (framesOffset == -1) { throw new InvalidOperationException("Bytes flag is not set"); } WriteBigEndian(frame.RawData, bytesOffset, value); } } public int VbrScale => vbrScale; public Mp3Frame Mp3Frame => frame; private static int ReadBigEndian(byte[] buffer, int offset) { return (((((buffer[offset] << 8) | buffer[offset + 1]) << 8) | buffer[offset + 2]) << 8) | buffer[offset + 3]; } private void WriteBigEndian(byte[] buffer, int offset, int value) { byte[] bytes = BitConverter.GetBytes(value); for (int i = 0; i < 4; i++) { buffer[offset + 3 - i] = bytes[i]; } } public static XingHeader LoadXingHeader(Mp3Frame frame) { XingHeader xingHeader = new XingHeader(); xingHeader.frame = frame; int num = 0; if (frame.MpegVersion == MpegVersion.Version1) { num = ((frame.ChannelMode == ChannelMode.Mono) ? 21 : 36); } else { if (frame.MpegVersion != MpegVersion.Version2) { return null; } num = ((frame.ChannelMode == ChannelMode.Mono) ? 13 : 21); } if (frame.RawData[num] == 88 && frame.RawData[num + 1] == 105 && frame.RawData[num + 2] == 110 && frame.RawData[num + 3] == 103) { xingHeader.startOffset = num; num += 4; } else { if (frame.RawData[num] != 73 || frame.RawData[num + 1] != 110 || frame.RawData[num + 2] != 102 || frame.RawData[num + 3] != 111) { return null; } xingHeader.startOffset = num; num += 4; } int num2 = ReadBigEndian(frame.RawData, num); num += 4; if (((uint)num2 & (true ? 1u : 0u)) != 0) { xingHeader.framesOffset = num; num += 4; } if (((uint)num2 & 2u) != 0) { xingHeader.bytesOffset = num; num += 4; } if (((uint)num2 & 4u) != 0) { xingHeader.tocOffset = num; num += 100; } if (((uint)num2 & 8u) != 0) { xingHeader.vbrScale = ReadBigEndian(frame.RawData, num); num += 4; } xingHeader.endOffset = num; return xingHeader; } private XingHeader() { } } public static class WaveExtensionMethods { public static ISampleProvider ToSampleProvider(this IWaveProvider waveProvider) { return SampleProviderConverters.ConvertWaveProviderIntoSampleProvider(waveProvider); } public static void Init(this IWavePlayer wavePlayer, ISampleProvider sampleProvider, bool convertTo16Bit = false) { IWaveProvider waveProvider2; if (!convertTo16Bit) { IWaveProvider waveProvider = new SampleToWaveProvider(sampleProvider); waveProvider2 = waveProvider; } else { IWaveProvider waveProvider = new SampleToWaveProvider16(sampleProvider); waveProvider2 = waveProvider; } IWaveProvider waveProvider3 = waveProvider2; wavePlayer.Init(waveProvider3); } public static WaveFormat AsStandardWaveFormat(this WaveFormat waveFormat) { if (!(waveFormat is WaveFormatExtensible waveFormatExtensible)) { return waveFormat; } return waveFormatExtensible.ToStandardWaveFormat(); } public static IWaveProvider ToWaveProvider(this ISampleProvider sampleProvider) { return new SampleToWaveProvider(sampleProvider); } public static IWaveProvider ToWaveProvider16(this ISampleProvider sampleProvider) { return new SampleToWaveProvider16(sampleProvider); } public static ISampleProvider FollowedBy(this ISampleProvider sampleProvider, ISampleProvider next) { return new ConcatenatingSampleProvider(new ISampleProvider[2] { sampleProvider, next }); } public static ISampleProvider FollowedBy(this ISampleProvider sampleProvider, TimeSpan silenceDuration, ISampleProvider next) { OffsetSampleProvider offsetSampleProvider = new OffsetSampleProvider(sampleProvider) { LeadOut = silenceDuration }; return new ConcatenatingSampleProvider(new ISampleProvider[2] { offsetSampleProvider, next }); } public static ISampleProvider Skip(this ISampleProvider sampleProvider, TimeSpan skipDuration) { return new OffsetSampleProvider(sampleProvider) { SkipOver = skipDuration }; } public static ISampleProvider Take(this ISampleProvider sampleProvider, TimeSpan takeDuration) { return new OffsetSampleProvider(sampleProvider) { Take = takeDuration }; } public static ISampleProvider ToMono(this ISampleProvider sourceProvider, float leftVol = 0.5f, float rightVol = 0.5f) { if (sourceProvider.WaveFormat.Channels == 1) { return sourceProvider; } return new StereoToMonoSampleProvider(sourceProvider) { LeftVolume = leftVol, RightVolume = rightVol }; } public static ISampleProvider ToStereo(this ISampleProvider sourceProvider, float leftVol = 1f, float rightVol = 1f) { if (sourceProvider.WaveFormat.Channels == 2) { return sourceProvider; } return new MonoToStereoSampleProvider(sourceProvider) { LeftVolume = leftVol, RightVolume = rightVol }; } } [StructLayout(LayoutKind.Sequential, Pack = 2)] public class AdpcmWaveFormat : WaveFormat { private short samplesPerBlock; private short numCoeff; [MarshalAs(UnmanagedType.ByValArray, SizeConst = 14)] private short[] coefficients; public int SamplesPerBlock => samplesPerBlock; public int NumCoefficients => numCoeff; public short[] Coefficients => coefficients; private AdpcmWaveFormat() : this(8000, 1) { } public AdpcmWaveFormat(int sampleRate, int channels) : base(sampleRate, 0, channels) { waveFormatTag = WaveFormatEncoding.Adpcm; extraSize = 32; switch (base.sampleRate) { case 8000: case 11025: blockAlign = 256; break; case 22050: blockAlign = 512; break; default: blockAlign = 1024; break; } bitsPerSample = 4; samplesPerBlock = (short)((blockAlign - 7 * channels) * 8 / (bitsPerSample * channels) + 2); averageBytesPerSecond = base.SampleRate * blockAlign / samplesPerBlock; numCoeff = 7; coefficients = new short[14] { 256, 0, 512, -256, 0, 0, 192, 64, 240, 0, 460, -208, 392, -232 }; } public override void Serialize(BinaryWriter writer) { base.Serialize(writer); writer.Write(samplesPerBlock); writer.Write(numCoeff); short[] array = coefficients; foreach (short value in array) { writer.Write(value); } } public override string ToString() { return $"Microsoft ADPCM {base.SampleRate} Hz {channels} channels {bitsPerSample} bits per sample {samplesPerBlock} samples per block"; } } [StructLayout(LayoutKind.Sequential, Pack = 2)] public class Gsm610WaveFormat : WaveFormat { private readonly short samplesPerBlock; public short SamplesPerBlock => samplesPerBlock; public Gsm610WaveFormat() { waveFormatTag = WaveFormatEncoding.Gsm610; channels = 1; averageBytesPerSecond = 1625; bitsPerSample = 0; blockAlign = 65; sampleRate = 8000; extraSize = 2; samplesPerBlock = 320; } public override void Serialize(BinaryWriter writer) { base.Serialize(writer); writer.Write(samplesPerBlock); } } [StructLayout(LayoutKind.Sequential, Pack = 2)] public class ImaAdpcmWaveFormat : WaveFormat { private short samplesPerBlock; private ImaAdpcmWaveFormat() { } public ImaAdpcmWaveFormat(int sampleRate, int channels, int bitsPerSample) { waveFormatTag = WaveFormatEncoding.DviAdpcm; base.sampleRate = sampleRate; base.channels = (short)channels; base.bitsPerSample = (short)bitsPerSample; extraSize = 2; blockAlign = 0; averageBytesPerSecond = 0; samplesPerBlock = 0; } } [StructLayout(LayoutKind.Sequential, Pack = 2)] public class Mp3WaveFormat : WaveFormat { public Mp3WaveFormatId id; public Mp3WaveFormatFlags flags; public ushort blockSize; public ushort framesPerBlock; public ushort codecDelay; private const short Mp3WaveFormatExtraBytes = 12; public Mp3WaveFormat(int sampleRate, int channels, int blockSize, int bitRate) { waveFormatTag = WaveFormatEncoding.MpegLayer3; base.channels = (short)channels; averageBytesPerSecond = bitRate / 8; bitsPerSample = 0; blockAlign = 1; base.sampleRate = sampleRate; extraSize = 12; id = Mp3WaveFormatId.Mpeg; flags = Mp3WaveFormatFlags.PaddingIso; this.blockSize = (ushort)blockSize; framesPerBlock = 1; codecDelay = 0; } } [Flags] public enum Mp3WaveFormatFlags { PaddingIso = 0, PaddingOn = 1, PaddingOff = 2 } public enum Mp3WaveFormatId : ushort { Unknown, Mpeg, ConstantFrameSize } [StructLayout(LayoutKind.Sequential, Pack = 2)] internal class OggWaveFormat : WaveFormat { public uint dwVorbisACMVersion; public uint dwLibVorbisVersion; } [StructLayout(LayoutKind.Sequential, Pack = 2)] public class TrueSpeechWaveFormat : WaveFormat { [MarshalAs(UnmanagedType.ByValArray, SizeConst = 16)] private short[] unknown; public TrueSpeechWaveFormat() { waveFormatTag = WaveFormatEncoding.DspGroupTrueSpeech; channels = 1; averageBytesPerSecond = 1067; bitsPerSample = 1; blockAlign = 32; sampleRate = 8000; extraSize = 32; unknown = new short[16]; unknown[0] = 1; unknown[1] = 240; } public override void Serialize(BinaryWriter writer) { base.Serialize(writer); short[] array = unknown; foreach (short value in array) { writer.Write(value); } } } [StructLayout(LayoutKind.Sequential, Pack = 2)] public class WaveFormat { protected WaveFormatEncoding waveFormatTag; protected short channels; protected int sampleRate; protected int averageBytesPerSecond; protected short blockAlign; protected short bitsPerSample; protected short extraSize; public WaveFormatEncoding Encoding => waveFormatTag; public int Channels => channels; public int SampleRate => sampleRate; public int AverageBytesPerSecond => averageBytesPerSecond; public virtual int BlockAlign => blockAlign; public int BitsPerSample => bitsPerSample; public int ExtraSize => extraSize; public WaveFormat() : this(44100, 16, 2) { } public WaveFormat(int sampleRate, int channels) : this(sampleRate, 16, channels) { } public int ConvertLatencyToByteSize(int milliseconds) { int num = (int)((double)AverageBytesPerSecond / 1000.0 * (double)milliseconds); if (num % BlockAlign != 0) { num = num + BlockAlign - num % BlockAlign; } return num; } public static WaveFormat CreateCustomFormat(WaveFormatEncoding tag, int sampleRate, int channels, int averageBytesPerSecond, int blockAlign, int bitsPerSample) { return new WaveFormat { waveFormatTag = tag, channels = (short)channels, sampleRate = sampleRate, averageBytesPerSecond = averageBytesPerSecond, blockAlign = (short)blockAlign, bitsPerSample = (short)bitsPerSample, extraSize = 0 }; } public static WaveFormat CreateALawFormat(int sampleRate, int channels) { return CreateCustomFormat(WaveFormatEncoding.ALaw, sampleRate, channels, sampleRate * channels, channels, 8); } public static WaveFormat CreateMuLawFormat(int sampleRate, int channels) { return CreateCustomFormat(WaveFormatEncoding.MuLaw, sampleRate, channels, sampleRate * channels, channels, 8); } public WaveFormat(int rate, int bits, int channels) { if (channels < 1) { throw new ArgumentOutOfRangeException("channels", "Channels must be 1 or greater"); } waveFormatTag = WaveFormatEncoding.Pcm; this.channels = (short)channels; sampleRate = rate; bitsPerSample = (short)bits; extraSize = 0; blockAlign = (short)(channels * (bits / 8)); averageBytesPerSecond = sampleRate * blockAlign; } public static WaveFormat CreateIeeeFloatWaveFormat(int sampleRate, int channels) { WaveFormat waveFormat = new WaveFormat(); waveFormat.waveFormatTag = WaveFormatEncoding.IeeeFloat; waveFormat.channels = (short)channels; waveFormat.bitsPerSample = 32; waveFormat.sampleRate = sampleRate; waveFormat.blockAlign = (short)(4 * channels); waveFormat.averageBytesPerSecond = sampleRate * waveFormat.blockAlign; waveFormat.extraSize = 0; return waveFormat; } public static WaveFormat MarshalFromPtr(IntPtr pointer) { WaveFormat waveFormat = Marshal.PtrToStructure(pointer); switch (waveFormat.Encoding) { case WaveFormatEncoding.Pcm: waveFormat.extraSize = 0; break; case WaveFormatEncoding.Extensible: waveFormat = Marshal.PtrToStructure(pointer); break; case WaveFormatEncoding.Adpcm: waveFormat = Marshal.PtrToStructure(pointer); break; case WaveFormatEncoding.Gsm610: waveFormat = Marshal.PtrToStructure(pointer); break; default: if (waveFormat.ExtraSize > 0) { waveFormat = Marshal.PtrToStructure(pointer); } break; } return waveFormat; } public static IntPtr MarshalToPtr(WaveFormat format) { IntPtr intPtr = Marshal.AllocHGlobal(Marshal.SizeOf(format)); Marshal.StructureToPtr(format, intPtr, fDeleteOld: false); return intPtr; } public static WaveFormat FromFormatChunk(BinaryReader br, int formatChunkLength) { WaveFormatExtraData waveFormatExtraData = new WaveFormatExtraData(); waveFormatExtraData.ReadWaveFormat(br, formatChunkLength); waveFormatExtraData.ReadExtraData(br); return waveFormatExtraData; } private void ReadWaveFormat(BinaryReader br, int formatChunkLength) { if (formatChunkLength < 16) { throw new InvalidDataException("Invalid WaveFormat Structure"); } waveFormatTag = (WaveFormatEncoding)br.ReadUInt16(); channels = br.ReadInt16(); sampleRate = br.ReadInt32(); averageBytesPerSecond = br.ReadInt32(); blockAlign = br.ReadInt16(); bitsPerSample = br.ReadInt16(); if (formatChunkLength > 16) { extraSize = br.ReadInt16(); if (extraSize != formatChunkLength - 18) { extraSize = (short)(formatChunkLength - 18); } } } public WaveFormat(BinaryReader br) { int formatChunkLength = br.ReadInt32(); ReadWaveFormat(br, formatChunkLength); } public override string ToString() { switch (waveFormatTag) { case WaveFormatEncoding.Pcm: case WaveFormatEncoding.Extensible: return $"{bitsPerSample} bit PCM: {sampleRate}Hz {channels} channels"; case WaveFormatEncoding.IeeeFloat: return $"{bitsPerSample} bit IEEFloat: {sampleRate}Hz {channels} channels"; default: return waveFormatTag.ToString(); } } public override bool Equals(object obj) { if (obj is WaveFormat waveFormat) { if (waveFormatTag == waveFormat.waveFormatTag && channels == waveFormat.channels && sampleRate == waveFormat.sampleRate && averageBytesPerSecond == waveFormat.averageBytesPerSecond && blockAlign == waveFormat.blockAlign) { return bitsPerSample == waveFormat.bitsPerSample; } return false; } return false; } public override int GetHashCode() { return (int)waveFormatTag ^ (int)channels ^ sampleRate ^ averageBytesPerSecond ^ blockAlign ^ bitsPerSample; } public virtual void Serialize(BinaryWriter writer) { writer.Write(18 + extraSize); writer.Write((short)Encoding); writer.Write((short)Channels); writer.Write(SampleRate); writer.Write(AverageBytesPerSecond); writer.Write((short)BlockAlign); writer.Write((short)BitsPerSample); writer.Write(extraSize); } } public sealed class WaveFormatCustomMarshaler : ICustomMarshaler { private static WaveFormatCustomMarshaler marshaler; public static ICustomMarshaler GetInstance(string cookie) { if (marshaler == null) { marshaler = new WaveFormatCustomMarshaler(); } return marshaler; } public void CleanUpManagedData(object ManagedObj) { } public void CleanUpNativeData(IntPtr pNativeData) { Marshal.FreeHGlobal(pNativeData); } public int GetNativeDataSize() { throw new NotImplementedException(); } public IntPtr MarshalManagedToNative(object ManagedObj) { return WaveFormat.MarshalToPtr((WaveFormat)ManagedObj); } public object MarshalNativeToManaged(IntPtr pNativeData) { return WaveFormat.MarshalFromPtr(pNativeData); } } public enum WaveFormatEncoding : ushort { Unknown = 0, Pcm = 1, Adpcm = 2, IeeeFloat = 3, Vselp = 4, IbmCvsd = 5, ALaw = 6, MuLaw = 7, Dts = 8, Drm = 9, WmaVoice9 = 10, OkiAdpcm = 16, DviAdpcm = 17, ImaAdpcm = 17, MediaspaceAdpcm = 18, SierraAdpcm = 19, G723Adpcm = 20, DigiStd = 21, DigiFix = 22, DialogicOkiAdpcm = 23, MediaVisionAdpcm = 24, CUCodec = 25, YamahaAdpcm = 32, SonarC = 33, DspGroupTrueSpeech = 34, EchoSpeechCorporation1 = 35, AudioFileAf36 = 36, Aptx = 37, AudioFileAf10 = 38, Prosody1612 = 39, Lrc = 40, DolbyAc2 = 48, Gsm610 = 49, MsnAudio = 50, AntexAdpcme = 51, ControlResVqlpc = 52, DigiReal = 53, DigiAdpcm = 54, ControlResCr10 = 55, WAVE_FORMAT_NMS_VBXADPCM = 56, WAVE_FORMAT_CS_IMAADPCM = 57, WAVE_FORMAT_ECHOSC3 = 58, WAVE_FORMAT_ROCKWELL_ADPCM = 59, WAVE_FORMAT_ROCKWELL_DIGITALK = 60, WAVE_FORMAT_XEBEC = 61, WAVE_FORMAT_G721_ADPCM = 64, WAVE_FORMAT_G728_CELP = 65, WAVE_FORMAT_MSG723 = 66, Mpeg = 80, WAVE_FORMAT_RT24 = 82, WAVE_FORMAT_PAC = 83, MpegLayer3 = 85, WAVE_FORMAT_LUCENT_G723 = 89, WAVE_FORMAT_CIRRUS = 96, WAVE_FORMAT_ESPCM = 97, WAVE_FORMAT_VOXWARE = 98, WAVE_FORMAT_CANOPUS_ATRAC = 99, WAVE_FORMAT_G726_ADPCM = 100, WAVE_FORMAT_G722_ADPCM = 101, WAVE_FORMAT_DSAT_DISPLAY = 103, WAVE_FORMAT_VOXWARE_BYTE_ALIGNED = 105, WAVE_FORMAT_VOXWARE_AC8 = 112, WAVE_FORMAT_VOXWARE_AC10 = 113, WAVE_FORMAT_VOXWARE_AC16 = 114, WAVE_FORMAT_VOXWARE_AC20 = 115, WAVE_FORMAT_VOXWARE_RT24 = 116, WAVE_FORMAT_VOXWARE_RT29 = 117, WAVE_FORMAT_VOXWARE_RT29HW = 118, WAVE_FORMAT_VOXWARE_VR12 = 119, WAVE_FORMAT_VOXWARE_VR18 = 120, WAVE_FORMAT_VOXWARE_TQ40 = 121, WAVE_FORMAT_SOFTSOUND = 128, WAVE_FORMAT_VOXWARE_TQ60 = 129, WAVE_FORMAT_MSRT24 = 130, WAVE_FORMAT_G729A = 131, WAVE_FORMAT_MVI_MVI2 = 132, WAVE_FORMAT_DF_G726 = 133, WAVE_FORMAT_DF_GSM610 = 134, WAVE_FORMAT_ISIAUDIO = 136, WAVE_FORMAT_ONLIVE = 137, WAVE_FORMAT_SBC24 = 145, WAVE_FORMAT_DOLBY_AC3_SPDIF = 146, WAVE_FORMAT_MEDIASONIC_G723 = 147, WAVE_FORMAT_PROSODY_8KBPS = 148, WAVE_FORMAT_ZYXEL_ADPCM = 151, WAVE_FORMAT_PHILIPS_LPCBB = 152, WAVE_FORMAT_PACKED = 153, WAVE_FORMAT_MALDEN_PHONYTALK = 160, Gsm = 161, G729 = 162, G723 = 163, Acelp = 164, RawAac = 255, WAVE_FORMAT_RHETOREX_ADPCM = 256, WAVE_FORMAT_IRAT = 257, WAVE_FORMAT_VIVO_G723 = 273, WAVE_FORMAT_VIVO_SIREN = 274, WAVE_FORMAT_DIGITAL_G723 = 291, WAVE_FORMAT_SANYO_LD_ADPCM = 293, WAVE_FORMAT_SIPROLAB_ACEPLNET = 304, WAVE_FORMAT_SIPROLAB_ACELP4800 = 305, WAVE_FORMAT_SIPROLAB_ACELP8V3 = 306, WAVE_FORMAT_SIPROLAB_G729 = 307, WAVE_FORMAT_SIPROLAB_G729A = 308, WAVE_FORMAT_SIPROLAB_KELVIN = 309, WAVE_FORMAT_G726ADPCM = 320, WAVE_FORMAT_QUALCOMM_PUREVOICE = 336, WAVE_FORMAT_QUALCOMM_HALFRATE = 337, WAVE_FORMAT_TUBGSM = 341, WAVE_FORMAT_MSAUDIO1 = 352, WindowsMediaAudio = 353, WindowsMediaAudioProfessional = 354, WindowsMediaAudioLosseless = 355, WindowsMediaAudioSpdif = 356, WAVE_FORMAT_UNISYS_NAP_ADPCM = 368, WAVE_FORMAT_UNISYS_NAP_ULAW = 369, WAVE_FORMAT_UNISYS_NAP_ALAW = 370, WAVE_FORMAT_UNISYS_NAP_16K = 371, WAVE_FORMAT_CREATIVE_ADPCM = 512, WAVE_FORMAT_CREATIVE_FASTSPEECH8 = 514, WAVE_FORMAT_CREATIVE_FASTSPEECH10 = 515, WAVE_FORMAT_UHER_ADPCM = 528, WAVE_FORMAT_QUARTERDECK = 544, WAVE_FORMAT_ILINK_VC = 560, WAVE_FORMAT_RAW_SPORT = 576, WAVE_FORMAT_ESST_AC3 = 577, WAVE_FORMAT_IPI_HSX = 592, WAVE_FORMAT_IPI_RPELP = 593, WAVE_FORMAT_CS2 = 608, WAVE_FORMAT_SONY_SCX = 624, WAVE_FORMAT_FM_TOWNS_SND = 768, WAVE_FORMAT_BTV_DIGITAL = 1024, WAVE_FORMAT_QDESIGN_MUSIC = 1104, WAVE_FORMAT_VME_VMPCM = 1664, WAVE_FORMAT_TPC = 1665, WAVE_FORMAT_OLIGSM = 4096, WAVE_FORMAT_OLIADPCM = 4097, WAVE_FORMAT_OLICELP = 4098, WAVE_FORMAT_OLISBC = 4099, WAVE_FORMAT_OLIOPR = 4100, WAVE_FORMAT_LH_CODEC = 4352, WAVE_FORMAT_NORRIS = 5120, WAVE_FORMAT_SOUNDSPACE_MUSICOMPRESS = 5376, MPEG_ADTS_AAC = 5632, MPEG_RAW_AAC = 5633, MPEG_LOAS = 5634, NOKIA_MPEG_ADTS_AAC = 5640, NOKIA_MPEG_RAW_AAC = 5641, VODAFONE_MPEG_ADTS_AAC = 5642, VODAFONE_MPEG_RAW_AAC = 5643, MPEG_HEAAC = 5648, WAVE_FORMAT_DVM = 8192, Vorbis1 = 26447, Vorbis2 = 26448, Vorbis3 = 26449, Vorbis1P = 26479, Vorbis2P = 26480, Vorbis3P = 26481, Extensible = 65534, WAVE_FORMAT_DEVELOPMENT = ushort.MaxValue } [StructLayout(LayoutKind.Sequential, Pack = 2)] public class WaveFormatExtensible : WaveFormat { private short wValidBitsPerSample; private int dwChannelMask; private Guid subFormat; public Guid SubFormat => subFormat; private WaveFormatExtensible() { } public WaveFormatExtensible(int rate, int bits, int channels, int channelMask = 0) : base(rate, bits, channels) { waveFormatTag = WaveFormatEncoding.Extensible; extraSize = 22; wValidBitsPerSample = (short)bits; if (channelMask != 0) { dwChannelMask = channelMask; } else { for (int i = 0; i < channels; i++) { dwChannelMask |= 1 << i; } } if (bits == 32) { subFormat = AudioMediaSubtypes.MEDIASUBTYPE_IEEE_FLOAT; } else { subFormat = AudioMediaSubtypes.MEDIASUBTYPE_PCM; } } public WaveFormat ToStandardWaveFormat() { if (subFormat == AudioMediaSubtypes.MEDIASUBTYPE_IEEE_FLOAT && bitsPerSample == 32) { return WaveFormat.CreateIeeeFloatWaveFormat(sampleRate, channels); } if (subFormat == AudioMediaSubtypes.MEDIASUBTYPE_PCM) { return new WaveFormat(sampleRate, bitsPerSample, channels); } return this; } public override void Serialize(BinaryWriter writer) { base.Serialize(writer); writer.Write(wValidBitsPerSample); writer.Write(dwChannelMask); byte[] array = subFormat.ToByteArray(); writer.Write(array, 0, array.Length); } public override string ToString() { return "WAVE_FORMAT_EXTENSIBLE " + AudioMediaSubtypes.GetAudioSubtypeName(subFormat) + " " + $"{base.SampleRate}Hz {base.Channels} channels {base.BitsPerSample} bit"; } } [StructLayout(LayoutKind.Sequential, Pack = 2)] public class WaveFormatExtraData : WaveFormat { [MarshalAs(UnmanagedType.ByValArray, SizeConst = 100)] private byte[] extraData = new byte[100]; public byte[] ExtraData => extraData; internal WaveFormatExtraData() { } public WaveFormatExtraData(BinaryReader reader) : base(reader) { ReadExtraData(reader); } internal void ReadExtraData(BinaryReader reader) { if (extraSize > 0) { reader.Read(extraData, 0, extraSize); } } public override void Serialize(BinaryWriter writer) { base.Serialize(writer); if (extraSize > 0) { writer.Write(extraData, 0, extraSize); } } } public interface IWaveIn : IDisposable { WaveFormat WaveFormat { get; set; } event EventHandler DataAvailable; event EventHandler RecordingStopped; void StartRecording(); void StopRecording(); } public class WaveInEventArgs : EventArgs { private byte[] buffer; private int bytes; public byte[] Buffer => buffer; public int BytesRecorded => bytes; public WaveInEventArgs(byte[] buffer, int bytes) { this.buffer = buffer; this.bytes = bytes; } } public class AiffFileWriter : Stream { private Stream outStream; private BinaryWriter writer; private long dataSizePos; private long commSampleCountPos; private long dataChunkSize = 8L; private WaveFormat format; private string filename; private byte[] value24 = new byte[3]; public string Filename => filename; public override long Length => dataChunkSize; public WaveFormat WaveFormat => format; public override bool CanRead => false; public override bool CanWrite => true; public override bool CanSeek => false; public override long Position { get { return dataChunkSize; } set { throw new InvalidOperationException("Repositioning an AiffFileWriter is not supported"); } } public static void CreateAiffFile(string filename, WaveStream sourceProvider) { using AiffFileWriter aiffFileWriter = new AiffFileWriter(filename, sourceProvider.WaveFormat); byte[] array = new byte[16384]; while (sourceProvider.Position < sourceProvider.Length) { int count = Math.Min((int)(sourceProvider.Length - sourceProvider.Position), array.Length); int num = sourceProvider.Read(array, 0, count); if (num == 0) { break; } aiffFileWriter.Write(array, 0, num); } } public AiffFileWriter(Stream outStream, WaveFormat format) { this.outStream = outStream; this.format = format; writer = new BinaryWriter(outStream, Encoding.UTF8); writer.Write(Encoding.UTF8.GetBytes("FORM")); writer.Write(0); writer.Write(Encoding.UTF8.GetBytes("AIFF")); CreateCommChunk(); WriteSsndChunkHeader(); } public AiffFileWriter(string filename, WaveFormat format) : this(new FileStream(filename, FileMode.Create, FileAccess.Write, FileShare.Read), format) { this.filename = filename; } private void WriteSsndChunkHeader() { writer.Write(Encoding.UTF8.GetBytes("SSND")); dataSizePos = outStream.Position; writer.Write(0); writer.Write(0); writer.Write(SwapEndian(format.BlockAlign)); } private byte[] SwapEndian(short n) { return new byte[2] { (byte)(n >> 8), (byte)((uint)n & 0xFFu) }; } private byte[] SwapEndian(int n) { return new byte[4] { (byte)((uint)(n >> 24) & 0xFFu), (byte)((uint)(n >> 16) & 0xFFu), (byte)((uint)(n >> 8) & 0xFFu), (byte)((uint)n & 0xFFu) }; } private void CreateCommChunk() { writer.Write(Encoding.UTF8.GetBytes("COMM")); writer.Write(SwapEndian(18)); writer.Write(SwapEndian((short)format.Channels)); commSampleCountPos = outStream.Position; writer.Write(0); writer.Write(SwapEndian((short)format.BitsPerSample)); writer.Write(IEEE.ConvertToIeeeExtended(format.SampleRate)); } public override int Read(byte[] buffer, int offset, int count) { throw new InvalidOperationException("Cannot read from an AiffFileWriter"); } public override long Seek(long offset, SeekOrigin origin) { throw new InvalidOperationException("Cannot seek within an AiffFileWriter"); } public override void SetLength(long value) { throw new InvalidOperationException("Cannot set length of an AiffFileWriter"); } public override void Write(byte[] data, int offset, int count) { byte[] array = new byte[data.Length]; int num = format.BitsPerSample / 8; for (int i = 0; i < data.Length; i++) { int num2 = (int)Math.Floor((double)i / (double)num) * num + (num - i % num - 1); array[i] = data[num2]; } outStream.Write(array, offset, count); dataChunkSize += count; } public void WriteSample(float sample) { if (WaveFormat.BitsPerSample == 16) { writer.Write(SwapEndian((short)(32767f * sample))); dataChunkSize += 2L; } else if (WaveFormat.BitsPerSample == 24) { byte[] bytes = BitConverter.GetBytes((int)(2.1474836E+09f * sample)); value24[2] = bytes[1]; value24[1] = bytes[2]; value24[0] = bytes[3]; writer.Write(value24); dataChunkSize += 3L; } else { if (WaveFormat.BitsPerSample != 32 || WaveFormat.Encoding != WaveFormatEncoding.Extensible) { throw new InvalidOperationException("Only 16, 24 or 32 bit PCM or IEEE float audio data supported"); } writer.Write(SwapEndian(65535 * (int)sample)); dataChunkSize += 4L; } } public void WriteSamples(float[] samples, int offset, int count) { for (int i = 0; i < count; i++) { WriteSample(samples[offset + i]); } } public void WriteSamples(short[] samples, int offset, int count) { if (WaveFormat.BitsPerSample == 16) { for (int i = 0; i < count; i++) { writer.Write(SwapEndian(samples[i + offset])); } dataChunkSize += count * 2; } else if (WaveFormat.BitsPerSample == 24) { for (int j = 0; j < count; j++) { byte[] bytes = BitConverter.GetBytes(65535 * samples[j + offset]); value24[2] = bytes[1]; value24[1] = bytes[2]; value24[0] = bytes[3]; writer.Write(value24); } dataChunkSize += count * 3; } else { if (WaveFormat.BitsPerSample != 32 || WaveFormat.Encoding != WaveFormatEncoding.Extensible) { throw new InvalidOperationException("Only 16, 24 or 32 bit PCM audio data supported"); } for (int k = 0; k < count; k++) { writer.Write(SwapEndian(65535 * samples[k + offset])); } dataChunkSize += count * 4; } } public override void Flush() { writer.Flush(); } protected override void Dispose(bool disposing) { if (disposing && outStream != null) { try { UpdateHeader(writer); } finally { outStream.Dispose(); outStream = null; } } } protected virtual void UpdateHeader(BinaryWriter writer) { Flush(); writer.Seek(4, SeekOrigin.Begin); writer.Write(SwapEndian((int)(outStream.Length - 8))); UpdateCommChunk(writer); UpdateSsndChunk(writer); } private void UpdateCommChunk(BinaryWriter writer) { writer.Seek((int)commSampleCountPos, SeekOrigin.Begin); writer.Write(SwapEndian((int)(dataChunkSize * 8 / format.BitsPerSample / format.Channels))); } private void UpdateSsndChunk(BinaryWriter writer) { writer.Seek((int)dataSizePos, SeekOrigin.Begin); writer.Write(SwapEndian((int)dataChunkSize)); } ~AiffFileWriter() { Dispose(disposing: false); } } public class BextChunkInfo { public string Description { get; set; } public string Originator { get; set; } public string OriginatorReference { get; set; } public DateTime OriginationDateTime { get; set; } public string OriginationDate => OriginationDateTime.ToString("yyyy-MM-dd"); public string OriginationTime => OriginationDateTime.ToString("HH:mm:ss"); public long TimeReference { get; set; } public ushort Version => 1; public string UniqueMaterialIdentifier { get; set; } public byte[] Reserved { get; } public string CodingHistory { get; set; } public BextChunkInfo() { Reserved = new byte[190]; } } public class BwfWriter : IDisposable { private readonly WaveFormat format; private readonly BinaryWriter writer; private readonly long dataChunkSizePosition; private long dataLength; private bool isDisposed; public BwfWriter(string filename, WaveFormat format, BextChunkInfo bextChunkInfo) { this.format = format; writer = new BinaryWriter(File.OpenWrite(filename)); writer.Write(Encoding.UTF8.GetBytes("RIFF")); writer.Write(0); writer.Write(Encoding.UTF8.GetBytes("WAVE")); writer.Write(Encoding.UTF8.GetBytes("JUNK")); writer.Write(28); writer.Write(0L); writer.Write(0L); writer.Write(0L); writer.Write(0); writer.Write(Encoding.UTF8.GetBytes("bext")); byte[] bytes = Encoding.ASCII.GetBytes(bextChunkInfo.CodingHistory ?? ""); int num = 602 + bytes.Length; if (num % 2 != 0) { num++; } writer.Write(num); _ = writer.BaseStream.Position; writer.Write(GetAsBytes(bextChunkInfo.Description, 256)); writer.Write(GetAsBytes(bextChunkInfo.Originator, 32)); writer.Write(GetAsBytes(bextChunkInfo.OriginatorReference, 32)); writer.Write(GetAsBytes(bextChunkInfo.OriginationDate, 10)); writer.Write(GetAsBytes(bextChunkInfo.OriginationTime, 8)); writer.Write(bextChunkInfo.TimeReference); writer.Write(bextChunkInfo.Version); writer.Write(GetAsBytes(bextChunkInfo.UniqueMaterialIdentifier, 64)); writer.Write(bextChunkInfo.Reserved); writer.Write(bytes); if (bytes.Length % 2 != 0) { writer.Write((byte)0); } writer.Write(Encoding.UTF8.GetBytes("fmt ")); format.Serialize(writer); writer.Write(Encoding.UTF8.GetBytes("data")); dataChunkSizePosition = writer.BaseStream.Position; writer.Write(-1); } public void Write(byte[] buffer, int offset, int count) { if (isDisposed) { throw new ObjectDisposedException("This BWF Writer already disposed"); } writer.Write(buffer, offset, count); dataLength += count; } public void Flush() { if (isDisposed) { throw new ObjectDisposedException("This BWF Writer already disposed"); } writer.Flush(); FixUpChunkSizes(restorePosition: true); } private void FixUpChunkSizes(bool restorePosition) { long position = writer.BaseStream.Position; bool num = dataLength > int.MaxValue; long num2 = writer.BaseStream.Length - 8; if (num) { int num3 = format.BitsPerSample / 8 * format.Channels; writer.BaseStream.Position = 0L; writer.Write(Encoding.UTF8.GetBytes("RF64")); writer.Write(-1); writer.BaseStream.Position += 4L; writer.Write(Encoding.UTF8.GetBytes("ds64")); writer.BaseStream.Position += 4L; writer.Write(num2); writer.Write(dataLength); writer.Write(dataLength / num3); } else { writer.BaseStream.Position = 4L; writer.Write((uint)num2); writer.BaseStream.Position = dataChunkSizePosition; writer.Write((uint)dataLength); } if (restorePosition) { writer.BaseStream.Position = position; } } public void Dispose() { if (!isDisposed) { FixUpChunkSizes(restorePosition: false); writer.Dispose(); isDisposed = true; } } private static byte[] GetAsBytes(string message, int byteSize) { byte[] array = new byte[byteSize]; byte[] bytes = Encoding.ASCII.GetBytes(message ?? ""); Array.Copy(bytes, array, Math.Min(bytes.Length, byteSize)); return array; } } public class CueWaveFileWriter : WaveFileWriter { private CueList cues; public CueWaveFileWriter(string fileName, WaveFormat waveFormat) : base(fileName, waveFormat) { } public void AddCue(int position, string label) { if (cues == null) { cues = new CueList(); } cues.Add(new Cue(position, label)); } private void WriteCues(BinaryWriter w) { if (cues != null) { int count = cues.GetRiffChunks().Length; w.Seek(0, SeekOrigin.End); if (w.BaseStream.Length % 2 == 1) { w.Write((byte)0); } w.Write(cues.GetRiffChunks(), 0, count); w.Seek(4, SeekOrigin.Begin); w.Write((int)(w.BaseStream.Length - 8)); } } protected override void UpdateHeader(BinaryWriter writer) { base.UpdateHeader(writer); WriteCues(writer); } } public class DirectSoundOut : IWavePlayer, IDisposable { [StructLayout(LayoutKind.Sequential, Pack = 2)] internal class BufferDescription { public int dwSize; [MarshalAs(UnmanagedType.U4)] public DirectSoundBufferCaps dwFlags; public uint dwBufferBytes; public int dwReserved; public IntPtr lpwfxFormat; public Guid guidAlgo; } [StructLayout(LayoutKind.Sequential, Pack = 2)] internal class BufferCaps { public int dwSize; public int dwFlags; public int dwBufferBytes; public int dwUnlockTransferRate; public int dwPlayCpuOverhead; } internal enum DirectSoundCooperativeLevel : uint { DSSCL_NORMAL = 1u, DSSCL_PRIORITY, DSSCL_EXCLUSIVE, DSSCL_WRITEPRIMARY } [Flags] internal enum DirectSoundPlayFlags : uint { DSBPLAY_LOOPING = 1u, DSBPLAY_LOCHARDWARE = 2u, DSBPLAY_LOCSOFTWARE = 4u, DSBPLAY_TERMINATEBY_TIME = 8u, DSBPLAY_TERMINATEBY_DISTANCE = 0x10u, DSBPLAY_TERMINATEBY_PRIORITY = 0x20u } internal enum DirectSoundBufferLockFlag : uint { None, FromWriteCursor, EntireBuffer } [Flags] internal enum DirectSoundBufferStatus : uint { DSBSTATUS_PLAYING = 1u, DSBSTATUS_BUFFERLOST = 2u, DSBSTATUS_LOOPING = 4u, DSBSTATUS_LOCHARDWARE = 8u, DSBSTATUS_LOCSOFTWARE = 0x10u, DSBSTATUS_TERMINATED = 0x20u } [Flags] internal enum DirectSoundBufferCaps : uint { DSBCAPS_PRIMARYBUFFER = 1u, DSBCAPS_STATIC = 2u, DSBCAPS_LOCHARDWARE = 4u, DSBCAPS_LOCSOFTWARE = 8u, DSBCAPS_CTRL3D = 0x10u, DSBCAPS_CTRLFREQUENCY = 0x20u, DSBCAPS_CTRLPAN = 0x40u, DSBCAPS_CTRLVOLUME = 0x80u, DSBCAPS_CTRLPOSITIONNOTIFY = 0x100u, DSBCAPS_CTRLFX = 0x200u, DSBCAPS_STICKYFOCUS = 0x4000u, DSBCAPS_GLOBALFOCUS = 0x8000u, DSBCAPS_GETCURRENTPOSITION2 = 0x10000u, DSBCAPS_MUTE3DATMAXDISTANCE = 0x20000u, DSBCAPS_LOCDEFER = 0x40000u } internal struct DirectSoundBufferPositionNotify { public uint dwOffset; public IntPtr hEventNotify; } [ComImport] [Guid("279AFA83-4981-11CE-A521-0020AF0BE560")] [SuppressUnmanagedCodeSecurity] [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] internal interface IDirectSound { void CreateSoundBuffer([In] BufferDescription desc, [MarshalAs(UnmanagedType.Interface)] out object dsDSoundBuffer, IntPtr pUnkOuter); void GetCaps(IntPtr caps); void DuplicateSoundBuffer([In][MarshalAs(UnmanagedType.Interface)] IDirectSoundBuffer bufferOriginal, [In][MarshalAs(UnmanagedType.Interface)] IDirectSoundBuffer bufferDuplicate); void SetCooperativeLevel(IntPtr HWND, [In][MarshalAs(UnmanagedType.U4)] DirectSoundCooperativeLevel dwLevel); void Compact(); void GetSpeakerConfig(IntPtr pdwSpeakerConfig); void SetSpeakerConfig(uint pdwSpeakerConfig); void Initialize([In][MarshalAs(UnmanagedType.LPStruct)] Guid guid); } [ComImport] [Guid("279AFA85-4981-11CE-A521-0020AF0BE560")] [SuppressUnmanagedCodeSecurity] [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] internal interface IDirectSoundBuffer { void GetCaps([MarshalAs(UnmanagedType.LPStruct)] BufferCaps pBufferCaps); void GetCurrentPosition(out uint currentPlayCursor, out uint currentWriteCursor); void GetFormat(); [return: MarshalAs(UnmanagedType.I4)] int GetVolume(); void GetPan(out uint pan); [return: MarshalAs(UnmanagedType.I4)] int GetFrequency(); [return: MarshalAs(UnmanagedType.U4)] DirectSoundBufferStatus GetStatus(); void Initialize([In][MarshalAs(UnmanagedType.Interface)] IDirectSound directSound, [In] BufferDescription desc); void Lock(int dwOffset, uint dwBytes, out IntPtr audioPtr1, out int audioBytes1, out IntPtr audioPtr2, out int audioBytes2, [MarshalAs(UnmanagedType.U4)] DirectSoundBufferLockFlag dwFlags); void Play(uint dwReserved1, uint dwPriority, [In][MarshalAs(UnmanagedType.U4)] DirectSoundPlayFlags dwFlags); void SetCurrentPosition(uint dwNewPosition); void SetFormat([In] WaveFormat pcfxFormat); void SetVolume(int volume); void SetPan(uint pan); void SetFrequency(uint frequency); void Stop(); void Unlock(IntPtr pvAudioPtr1, int dwAudioBytes1, IntPtr pvAudioPtr2, int dwAudioBytes2); void Restore(); } [ComImport] [Guid("b0210783-89cd-11d0-af08-00a0c925cd16")] [SuppressUnmanagedCodeSecurity] [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] internal interface IDirectSoundNotify { void SetNotificationPositions(uint dwPositionNotifies, [In][MarshalAs(UnmanagedType.LPArray)] DirectSoundBufferPositionNotify[] pcPositionNotifies); } private delegate bool DSEnumCallback(IntPtr lpGuid, IntPtr lpcstrDescription, IntPtr lpcstrModule, IntPtr lpContext); private PlaybackState playbackState; private WaveFormat waveFormat; private int samplesTotalSize; private int samplesFrameSize; private int nextSamplesWriteIndex; private int desiredLatency; private Guid device; private byte[] samples; private IWaveProvider waveStream; private IDirectSound directSound; private IDirectSoundBuffer primarySoundBuffer; private IDirectSoundBuffer secondaryBuffer; private EventWaitHandle frameEventWaitHandle1; private EventWaitHandle frameEventWaitHandle2; private EventWaitHandle endEventWaitHandle; private Thread notifyThread; private SynchronizationContext syncContext; private long bytesPlayed; private object m_LockObject = new object(); private static List devices; public static readonly Guid DSDEVID_DefaultPlayback = new Guid("DEF00000-9C6D-47ED-AAF1-4DDA8F2B5C03"); public static readonly Guid DSDEVID_DefaultCapture = new Guid("DEF00001-9C6D-47ED-AAF1-4DDA8F2B5C03"); public static readonly Guid DSDEVID_DefaultVoicePlayback = new Guid("DEF00002-9C6D-47ED-AAF1-4DDA8F2B5C03"); public static readonly Guid DSDEVID_DefaultVoiceCapture = new Guid("DEF00003-9C6D-47ED-AAF1-4DDA8F2B5C03"); public static IEnumerable Devices { get { devices = new List(); DirectSoundEnumerate(EnumCallback, IntPtr.Zero); return devices; } } public TimeSpan PlaybackPosition => TimeSpan.FromMilliseconds((double)(GetPosition() / (waveFormat.Channels * waveFormat.BitsPerSample / 8)) * 1000.0 / (double)waveFormat.SampleRate); public PlaybackState PlaybackState => playbackState; public float Volume { get { return 1f; } set { if (value != 1f) { throw new InvalidOperationException("Setting volume not supported on DirectSoundOut, adjust the volume on your WaveProvider instead"); } } } public WaveFormat OutputWaveFormat => waveFormat; public event EventHandler PlaybackStopped; private static bool EnumCallback(IntPtr lpGuid, IntPtr lpcstrDescription, IntPtr lpcstrModule, IntPtr lpContext) { DirectSoundDeviceInfo directSoundDeviceInfo = new DirectSoundDeviceInfo(); if (lpGuid == IntPtr.Zero) { directSoundDeviceInfo.Guid = Guid.Empty; } else { byte[] array = new byte[16]; Marshal.Copy(lpGuid, array, 0, 16); directSoundDeviceInfo.Guid = new Guid(array); } directSoundDeviceInfo.Description = Marshal.PtrToStringAnsi(lpcstrDescription); directSoundDeviceInfo.ModuleName = Marshal.PtrToStringAnsi(lpcstrModule); devices.Add(directSoundDeviceInfo); return true; } public DirectSoundOut() : this(DSDEVID_DefaultPlayback) { } public DirectSoundOut(Guid device) : this(device, 40) { } public DirectSoundOut(int latency) : this(DSDEVID_DefaultPlayback, latency) { } public DirectSoundOut(Guid device, int latency) { if (device == Guid.Empty) { device = DSDEVID_DefaultPlayback; } this.device = device; desiredLatency = latency; syncContext = SynchronizationContext.Current; } ~DirectSoundOut() { Dispose(); } public void Play() { if (playbackState == PlaybackState.Stopped) { notifyThread = new Thread(PlaybackThreadFunc); notifyThread.Priority = ThreadPriority.Normal; notifyThread.IsBackground = true; notifyThread.Start(); } lock (m_LockObject) { playbackState = PlaybackState.Playing; } } public void Stop() { if (Monitor.TryEnter(m_LockObject, 50)) { playbackState = PlaybackState.Stopped; Monitor.Exit(m_LockObject); } else if (notifyThread != null) { notifyThread.Abort(); notifyThread = null; } } public void Pause() { lock (m_LockObject) { playbackState = PlaybackState.Paused; } } public long GetPosition() { if (playbackState != 0) { IDirectSoundBuffer directSoundBuffer = secondaryBuffer; if (directSoundBuffer != null) { directSoundBuffer.GetCurrentPosition(out var currentPlayCursor, out var _); return currentPlayCursor + bytesPlayed; } } return 0L; } public void Init(IWaveProvider waveProvider) { waveStream = waveProvider; waveFormat = waveProvider.WaveFormat; } private void InitializeDirectSound() { lock (m_LockObject) { DirectSoundCreate(ref device, out directSound, IntPtr.Zero); if (directSound != null) { directSound.SetCooperativeLevel(GetDesktopWindow(), DirectSoundCooperativeLevel.DSSCL_PRIORITY); BufferDescription bufferDescription = new BufferDescription(); bufferDescription.dwSize = Marshal.SizeOf(bufferDescription); bufferDescription.dwBufferBytes = 0u; bufferDescription.dwFlags = DirectSoundBufferCaps.DSBCAPS_PRIMARYBUFFER; bufferDescription.dwReserved = 0; bufferDescription.lpwfxFormat = IntPtr.Zero; bufferDescription.guidAlgo = Guid.Empty; directSound.CreateSoundBuffer(bufferDescription, out var dsDSoundBuffer, IntPtr.Zero); primarySoundBuffer = (IDirectSoundBuffer)dsDSoundBuffer; primarySoundBuffer.Play(0u, 0u, DirectSoundPlayFlags.DSBPLAY_LOOPING); samplesFrameSize = MsToBytes(desiredLatency); BufferDescription bufferDescription2 = new BufferDescription(); bufferDescription2.dwSize = Marshal.SizeOf(bufferDescription2); bufferDescription2.dwBufferBytes = (uint)(samplesFrameSize * 2); bufferDescription2.dwFlags = DirectSoundBufferCaps.DSBCAPS_CTRLVOLUME | DirectSoundBufferCaps.DSBCAPS_CTRLPOSITIONNOTIFY | DirectSoundBufferCaps.DSBCAPS_STICKYFOCUS | DirectSoundBufferCaps.DSBCAPS_GLOBALFOCUS | DirectSoundBufferCaps.DSBCAPS_GETCURRENTPOSITION2; bufferDescription2.dwReserved = 0; GCHandle gCHandle = GCHandle.Alloc(waveFormat, GCHandleType.Pinned); bufferDescription2.lpwfxFormat = gCHandle.AddrOfPinnedObject(); bufferDescription2.guidAlgo = Guid.Empty; directSound.CreateSoundBuffer(bufferDescription2, out dsDSoundBuffer, IntPtr.Zero); secondaryBuffer = (IDirectSoundBuffer)dsDSoundBuffer; gCHandle.Free(); BufferCaps bufferCaps = new BufferCaps(); bufferCaps.dwSize = Marshal.SizeOf(bufferCaps); secondaryBuffer.GetCaps(bufferCaps); nextSamplesWriteIndex = 0; samplesTotalSize = bufferCaps.dwBufferBytes; samples = new byte[samplesTotalSize]; IDirectSoundNotify obj = (IDirectSoundNotify)dsDSoundBuffer; frameEventWaitHandle1 = new EventWaitHandle(initialState: false, EventResetMode.AutoReset); frameEventWaitHandle2 = new EventWaitHandle(initialState: false, EventResetMode.AutoReset); endEventWaitHandle = new EventWaitHandle(initialState: false, EventResetMode.AutoReset); DirectSoundBufferPositionNotify[] array = new DirectSoundBufferPositionNotify[3] { default(DirectSoundBufferPositionNotify), default(DirectSoundBufferPositionNotify), default(DirectSoundBufferPositionNotify) }; array[0].dwOffset = 0u; array[0].hEventNotify = frameEventWaitHandle1.SafeWaitHandle.DangerousGetHandle(); array[1] = default(DirectSoundBufferPositionNotify); array[1].dwOffset = (uint)samplesFrameSize; array[1].hEventNotify = frameEventWaitHandle2.SafeWaitHandle.DangerousGetHandle(); array[2] = default(DirectSoundBufferPositionNotify); array[2].dwOffset = uint.MaxValue; array[2].hEventNotify = endEventWaitHandle.SafeWaitHandle.DangerousGetHandle(); obj.SetNotificationPositions(3u, array); } } } public void Dispose() { Stop(); GC.SuppressFinalize(this); } private bool IsBufferLost() { return (secondaryBuffer.GetStatus() & DirectSoundBufferStatus.DSBSTATUS_BUFFERLOST) != 0; } private int MsToBytes(int ms) { int num = ms * (waveFormat.AverageBytesPerSecond / 1000); return num - num % waveFormat.BlockAlign; } private void PlaybackThreadFunc() { bool flag = false; bool flag2 = false; bytesPlayed = 0L; Exception ex = null; try { InitializeDirectSound(); int num = 1; if (PlaybackState == PlaybackState.Stopped) { secondaryBuffer.SetCurrentPosition(0u); nextSamplesWriteIndex = 0; num = Feed(samplesTotalSize); } if (num <= 0) { return; } lock (m_LockObject) { playbackState = PlaybackState.Playing; } secondaryBuffer.Play(0u, 0u, DirectSoundPlayFlags.DSBPLAY_LOOPING); WaitHandle[] waitHandles = new WaitHandle[3] { frameEventWaitHandle1, frameEventWaitHandle2, endEventWaitHandle }; bool flag3 = true; while (PlaybackState != PlaybackState.Stopped && flag3) { int num2 = WaitHandle.WaitAny(waitHandles, 3 * desiredLatency, exitContext: false); if (num2 != 258) { switch (num2) { case 2: StopPlayback(); flag = true; flag3 = false; continue; case 0: if (flag2) { bytesPlayed += samplesFrameSize * 2; } break; default: flag2 = true; break; } num2 = ((num2 == 0) ? 1 : 0); nextSamplesWriteIndex = num2 * samplesFrameSize; if (Feed(samplesFrameSize) == 0) { StopPlayback(); flag = true; flag3 = false; } continue; } StopPlayback(); flag = true; flag3 = false; throw new Exception("DirectSound buffer timeout"); } } catch (Exception ex2) { ex = ex2; } finally { if (!flag) { try { StopPlayback(); } catch (Exception ex3) { if (ex == null) { ex = ex3; } } } lock (m_LockObject) { playbackState = PlaybackState.Stopped; } bytesPlayed = 0L; RaisePlaybackStopped(ex); } } private void RaisePlaybackStopped(Exception e) { EventHandler handler = this.PlaybackStopped; if (handler == null) { return; } if (syncContext == null) { handler(this, new StoppedEventArgs(e)); return; } syncContext.Post(delegate { handler(this, new StoppedEventArgs(e)); }, null); } private void StopPlayback() { lock (m_LockObject) { if (secondaryBuffer != null) { CleanUpSecondaryBuffer(); secondaryBuffer.Stop(); Marshal.ReleaseComObject(secondaryBuffer); secondaryBuffer = null; } if (primarySoundBuffer != null) { primarySoundBuffer.Stop(); Marshal.ReleaseComObject(primarySoundBuffer); primarySoundBuffer = null; } if (directSound != null) { Marshal.ReleaseComObject(directSound); directSound = null; } } } private void CleanUpSecondaryBuffer() { if (secondaryBuffer == null) { return; } byte[] source = new byte[samplesTotalSize]; secondaryBuffer.Lock(0, (uint)samplesTotalSize, out var audioPtr, out var audioBytes, out var audioPtr2, out var audioBytes2, DirectSoundBufferLockFlag.None); if (audioPtr != IntPtr.Zero) { Marshal.Copy(source, 0, audioPtr, audioBytes); if (audioPtr2 != IntPtr.Zero) { Marshal.Copy(source, 0, audioPtr, audioBytes); } } secondaryBuffer.Unlock(audioPtr, audioBytes, audioPtr2, audioBytes2); } private int Feed(int bytesToCopy) { int num = bytesToCopy; if (IsBufferLost()) { secondaryBuffer.Restore(); } if (playbackState == PlaybackState.Paused) { Array.Clear(samples, 0, samples.Length); } else { num = waveStream.Read(samples, 0, bytesToCopy); if (num == 0) { Array.Clear(samples, 0, samples.Length); return 0; } } secondaryBuffer.Lock(nextSamplesWriteIndex, (uint)num, out var audioPtr, out var audioBytes, out var audioPtr2, out var audioBytes2, DirectSoundBufferLockFlag.None); if (audioPtr != IntPtr.Zero) { Marshal.Copy(samples, 0, audioPtr, audioBytes); if (audioPtr2 != IntPtr.Zero) { Marshal.Copy(samples, 0, audioPtr, audioBytes); } } secondaryBuffer.Unlock(audioPtr, audioBytes, audioPtr2, audioBytes2); return num; } [DllImport("dsound.dll", CallingConvention = CallingConvention.StdCall, CharSet = CharSet.Unicode, ExactSpelling = true, SetLastError = true)] private static extern void DirectSoundCreate(ref Guid GUID, [MarshalAs(UnmanagedType.Interface)] out IDirectSound directSound, IntPtr pUnkOuter); [DllImport("dsound.dll", CallingConvention = CallingConvention.StdCall, CharSet = CharSet.Unicode, EntryPoint = "DirectSoundEnumerateA", ExactSpelling = true, SetLastError = true)] private static extern void DirectSoundEnumerate(DSEnumCallback lpDSEnumCallback, IntPtr lpContext); [DllImport("user32.dll")] private static extern IntPtr GetDesktopWindow(); } public class DirectSoundDeviceInfo { public Guid Guid { get; set; } public string Description { get; set; } public string ModuleName { get; set; } } public interface IWaveBuffer { byte[] ByteBuffer { get; } float[] FloatBuffer { get; } short[] ShortBuffer { get; } int[] IntBuffer { get; } int MaxSize { get; } int ByteBufferCount { get; } int FloatBufferCount { get; } int ShortBufferCount { get; } int IntBufferCount { get; } } public interface IWavePlayer : IDisposable { float Volume { get; set; } PlaybackState PlaybackState { get; } WaveFormat OutputWaveFormat { get; } event EventHandler PlaybackStopped; void Play(); void Stop(); void Pause(); void Init(IWaveProvider waveProvider); } public interface IWavePosition { WaveFormat OutputWaveFormat { get; } long GetPosition(); } public interface IWaveProvider { WaveFormat WaveFormat { get; } int Read(byte[] buffer, int offset, int count); } public interface ISampleProvider { WaveFormat WaveFormat { get; } int Read(float[] buffer, int offset, int count); } public enum PlaybackState { Stopped, Playing, Paused } public class StoppedEventArgs : EventArgs { private readonly Exception exception; public Exception Exception => exception; public StoppedEventArgs(Exception exception = null) { this.exception = exception; } } [StructLayout(LayoutKind.Explicit, Pack = 2)] public class WaveBuffer : IWaveBuffer { [FieldOffset(0)] public int numberOfBytes; [FieldOffset(8)] private byte[] byteBuffer; [FieldOffset(8)] private float[] floatBuffer; [FieldOffset(8)] private short[] shortBuffer; [FieldOffset(8)] private int[] intBuffer; public byte[] ByteBuffer => byteBuffer; public float[] FloatBuffer => floatBuffer; public short[] ShortBuffer => shortBuffer; public int[] IntBuffer => intBuffer; public int MaxSize => byteBuffer.Length; public int ByteBufferCount { get { return numberOfBytes; } set { numberOfBytes = CheckValidityCount("ByteBufferCount", value, 1); } } public int FloatBufferCount { get { return numberOfBytes / 4; } set { numberOfBytes = CheckValidityCount("FloatBufferCount", value, 4); } } public int ShortBufferCount { get { return numberOfBytes / 2; } set { numberOfBytes = CheckValidityCount("ShortBufferCount", value, 2); } } public int IntBufferCount { get { return numberOfBytes / 4; } set { numberOfBytes = CheckValidityCount("IntBufferCount", value, 4); } } public WaveBuffer(int sizeToAllocateInBytes) { int num = sizeToAllocateInBytes % 4; sizeToAllocateInBytes = ((num == 0) ? sizeToAllocateInBytes : (sizeToAllocateInBytes + 4 - num)); byteBuffer = new byte[sizeToAllocateInBytes]; numberOfBytes = 0; } public WaveBuffer(byte[] bufferToBoundTo) { BindTo(bufferToBoundTo); } public void BindTo(byte[] bufferToBoundTo) { byteBuffer = bufferToBoundTo; numberOfBytes = 0; } public static implicit operator byte[](WaveBuffer waveBuffer) { return waveBuffer.byteBuffer; } public static implicit operator float[](WaveBuffer waveBuffer) { return waveBuffer.floatBuffer; } public static implicit operator int[](WaveBuffer waveBuffer) { return waveBuffer.intBuffer; } public static implicit operator short[](WaveBuffer waveBuffer) { return waveBuffer.shortBuffer; } public void Clear() { Array.Clear(byteBuffer, 0, byteBuffer.Length); } public void Copy(Array destinationArray) { Array.Copy(byteBuffer, destinationArray, numberOfBytes); } private int CheckValidityCount(string argName, int value, int sizeOfValue) { int num = value * sizeOfValue; if (num % 4 != 0) { throw new ArgumentOutOfRangeException(argName, $"{argName} cannot set a count ({num}) that is not 4 bytes aligned "); } if (value < 0 || value > byteBuffer.Length / sizeOfValue) { throw new ArgumentOutOfRangeException(argName, $"{argName} cannot set a count that exceed max count {byteBuffer.Length / sizeOfValue}"); } return num; } } public class WaveFileWriter : Stream { private Stream outStream; private readonly BinaryWriter writer; private long dataSizePos; private long factSampleCountPos; private long dataChunkSize; private readonly WaveFormat format; private readonly string filename; private readonly byte[] value24 = new byte[3]; public string Filename => filename; public override long Length => dataChunkSize; public TimeSpan TotalTime => TimeSpan.FromSeconds((double)Length / (double)WaveFormat.AverageBytesPerSecond); public WaveFormat WaveFormat => format; public override bool CanRead => false; public override bool CanWrite => true; public override bool CanSeek => false; public override long Position { get { return dataChunkSize; } set { throw new InvalidOperationException("Repositioning a WaveFileWriter is not supported"); } } public static void CreateWaveFile16(string filename, ISampleProvider sourceProvider) { CreateWaveFile(filename, new SampleToWaveProvider16(sourceProvider)); } public static void CreateWaveFile(string filename, IWaveProvider sourceProvider) { using WaveFileWriter waveFileWriter = new WaveFileWriter(filename, sourceProvider.WaveFormat); byte[] array = new byte[sourceProvider.WaveFormat.AverageBytesPerSecond * 4]; while (true) { int num = sourceProvider.Read(array, 0, array.Length); if (num == 0) { break; } waveFileWriter.Write(array, 0, num); } } public static void WriteWavFileToStream(Stream outStream, IWaveProvider sourceProvider) { using WaveFileWriter waveFileWriter = new WaveFileWriter(new IgnoreDisposeStream(outStream), sourceProvider.WaveFormat); byte[] array = new byte[sourceProvider.WaveFormat.AverageBytesPerSecond * 4]; while (true) { int num = sourceProvider.Read(array, 0, array.Length); if (num == 0) { break; } waveFileWriter.Write(array, 0, num); } outStream.Flush(); } public WaveFileWriter(Stream outStream, WaveFormat format) { this.outStream = outStream; this.format = format; writer = new BinaryWriter(outStream, Encoding.UTF8); writer.Write(Encoding.UTF8.GetBytes("RIFF")); writer.Write(0); writer.Write(Encoding.UTF8.GetBytes("WAVE")); writer.Write(Encoding.UTF8.GetBytes("fmt ")); format.Serialize(writer); CreateFactChunk(); WriteDataChunkHeader(); } public WaveFileWriter(string filename, WaveFormat format) : this(new FileStream(filename, FileMode.Create, FileAccess.Write, FileShare.Read), format) { this.filename = filename; } private void WriteDataChunkHeader() { writer.Write(Encoding.UTF8.GetBytes("data")); dataSizePos = outStream.Position; writer.Write(0); } private void CreateFactChunk() { if (HasFactChunk()) { writer.Write(Encoding.UTF8.GetBytes("fact")); writer.Write(4); factSampleCountPos = outStream.Position; writer.Write(0); } } private bool HasFactChunk() { if (format.Encoding != WaveFormatEncoding.Pcm) { return format.BitsPerSample != 0; } return false; } public override int Read(byte[] buffer, int offset, int count) { throw new InvalidOperationException("Cannot read from a WaveFileWriter"); } public override long Seek(long offset, SeekOrigin origin) { throw new InvalidOperationException("Cannot seek within a WaveFileWriter"); } public override void SetLength(long value) { throw new InvalidOperationException("Cannot set length of a WaveFileWriter"); } [Obsolete("Use Write instead")] public void WriteData(byte[] data, int offset, int count) { Write(data, offset, count); } public override void Write(byte[] data, int offset, int count) { if (outStream.Length + count > uint.MaxValue) { throw new ArgumentException("WAV file too large", "count"); } outStream.Write(data, offset, count); dataChunkSize += count; } public void WriteSample(float sample) { if (WaveFormat.BitsPerSample == 16) { writer.Write((short)(32767f * sample)); dataChunkSize += 2L; } else if (WaveFormat.BitsPerSample == 24) { byte[] bytes = BitConverter.GetBytes((int)(2.1474836E+09f * sample)); value24[0] = bytes[1]; value24[1] = bytes[2]; value24[2] = bytes[3]; writer.Write(value24); dataChunkSize += 3L; } else if (WaveFormat.BitsPerSample == 32 && WaveFormat.Encoding == WaveFormatEncoding.Extensible) { writer.Write(65535 * (int)sample); dataChunkSize += 4L; } else { if (WaveFormat.Encoding != WaveFormatEncoding.IeeeFloat) { throw new InvalidOperationException("Only 16, 24 or 32 bit PCM or IEEE float audio data supported"); } writer.Write(sample); dataChunkSize += 4L; } } public void WriteSamples(float[] samples, int offset, int count) { for (int i = 0; i < count; i++) { WriteSample(samples[offset + i]); } } [Obsolete("Use WriteSamples instead")] public void WriteData(short[] samples, int offset, int count) { WriteSamples(samples, offset, count); } public void WriteSamples(short[] samples, int offset, int count) { if (WaveFormat.BitsPerSample == 16) { for (int i = 0; i < count; i++) { writer.Write(samples[i + offset]); } dataChunkSize += count * 2; } else if (WaveFormat.BitsPerSample == 24) { for (int j = 0; j < count; j++) { byte[] bytes = BitConverter.GetBytes(65535 * samples[j + offset]); value24[0] = bytes[1]; value24[1] = bytes[2]; value24[2] = bytes[3]; writer.Write(value24); } dataChunkSize += count * 3; } else if (WaveFormat.BitsPerSample == 32 && WaveFormat.Encoding == WaveFormatEncoding.Extensible) { for (int k = 0; k < count; k++) { writer.Write(65535 * samples[k + offset]); } dataChunkSize += count * 4; } else { if (WaveFormat.BitsPerSample != 32 || WaveFormat.Encoding != WaveFormatEncoding.IeeeFloat) { throw new InvalidOperationException("Only 16, 24 or 32 bit PCM or IEEE float audio data supported"); } for (int l = 0; l < count; l++) { writer.Write((float)samples[l + offset] / 32768f); } dataChunkSize += count * 4; } } public override void Flush() { long position = writer.BaseStream.Position; UpdateHeader(writer); writer.BaseStream.Position = position; } protected override void Dispose(bool disposing) { if (disposing && outStream != null) { try { UpdateHeader(writer); } finally { outStream.Dispose(); outStream = null; } } } protected virtual void UpdateHeader(BinaryWriter writer) { writer.Flush(); UpdateRiffChunk(writer); UpdateFactChunk(writer); UpdateDataChunk(writer); } private void UpdateDataChunk(BinaryWriter writer) { writer.Seek((int)dataSizePos, SeekOrigin.Begin); writer.Write((uint)dataChunkSize); } private void UpdateRiffChunk(BinaryWriter writer) { writer.Seek(4, SeekOrigin.Begin); writer.Write((uint)(outStream.Length - 8)); } private void UpdateFactChunk(BinaryWriter writer) { if (HasFactChunk()) { int num = format.BitsPerSample * format.Channels; if (num != 0) { writer.Seek((int)factSampleCountPos, SeekOrigin.Begin); writer.Write((int)(dataChunkSize * 8 / num)); } } } ~WaveFileWriter() { Dispose(disposing: false); } } public class BufferedWaveProvider : IWaveProvider { private CircularBuffer circularBuffer; private readonly WaveFormat waveFormat; public bool ReadFully { get; set; } public int BufferLength { get; set; } public TimeSpan BufferDuration { get { return TimeSpan.FromSeconds((double)BufferLength / (double)WaveFormat.AverageBytesPerSecond); } set { BufferLength = (int)(value.TotalSeconds * (double)WaveFormat.AverageBytesPerSecond); } } public bool DiscardOnBufferOverflow { get; set; } public int BufferedBytes { get { if (circularBuffer != null) { return circularBuffer.Count; } return 0; } } public TimeSpan BufferedDuration => TimeSpan.FromSeconds((double)BufferedBytes / (double)WaveFormat.AverageBytesPerSecond); public WaveFormat WaveFormat => waveFormat; public BufferedWaveProvider(WaveFormat waveFormat) { this.waveFormat = waveFormat; BufferLength = waveFormat.AverageBytesPerSecond * 5; ReadFully = true; } public void AddSamples(byte[] buffer, int offset, int count) { if (circularBuffer == null) { circularBuffer = new CircularBuffer(BufferLength); } if (circularBuffer.Write(buffer, offset, count) < count && !DiscardOnBufferOverflow) { throw new InvalidOperationException("Buffer full"); } } public int Read(byte[] buffer, int offset, int count) { int num = 0; if (circularBuffer != null) { num = circularBuffer.Read(buffer, offset, count); } if (ReadFully && num < count) { Array.Clear(buffer, offset + num, count - num); num = count; } return num; } public void ClearBuffer() { if (circularBuffer != null) { circularBuffer.Reset(); } } } public class MixingWaveProvider32 : IWaveProvider { private List inputs; private WaveFormat waveFormat; private int bytesPerSample; public int InputCount => inputs.Count; public WaveFormat WaveFormat => waveFormat; public MixingWaveProvider32() { waveFormat = WaveFormat.CreateIeeeFloatWaveFormat(44100, 2); bytesPerSample = 4; inputs = new List(); } public MixingWaveProvider32(IEnumerable inputs) : this() { foreach (IWaveProvider input in inputs) { AddInputStream(input); } } public void AddInputStream(IWaveProvider waveProvider) { if (waveProvider.WaveFormat.Encoding != WaveFormatEncoding.IeeeFloat) { throw new ArgumentException("Must be IEEE floating point", "waveProvider.WaveFormat"); } if (waveProvider.WaveFormat.BitsPerSample != 32) { throw new ArgumentException("Only 32 bit audio currently supported", "waveProvider.WaveFormat"); } if (inputs.Count == 0) { int sampleRate = waveProvider.WaveFormat.SampleRate; int channels = waveProvider.WaveFormat.Channels; waveFormat = WaveFormat.CreateIeeeFloatWaveFormat(sampleRate, channels); } else if (!waveProvider.WaveFormat.Equals(waveFormat)) { throw new ArgumentException("All incoming channels must have the same format", "waveProvider.WaveFormat"); } lock (inputs) { inputs.Add(waveProvider); } } public void RemoveInputStream(IWaveProvider waveProvider) { lock (inputs) { inputs.Remove(waveProvider); } } public int Read(byte[] buffer, int offset, int count) { if (count % bytesPerSample != 0) { throw new ArgumentException("Must read an whole number of samples", "count"); } Array.Clear(buffer, offset, count); int num = 0; byte[] array = new byte[count]; lock (inputs) { foreach (IWaveProvider input in inputs) { int num2 = input.Read(array, 0, count); num = Math.Max(num, num2); if (num2 > 0) { Sum32BitAudio(buffer, offset, array, num2); } } return num; } } private unsafe static void Sum32BitAudio(byte[] destBuffer, int offset, byte[] sourceBuffer, int bytesRead) { fixed (byte* ptr = &destBuffer[offset]) { fixed (byte* ptr3 = &sourceBuffer[0]) { float* ptr2 = (float*)ptr; float* ptr4 = (float*)ptr3; int num = bytesRead / 4; for (int i = 0; i < num; i++) { ptr2[i] += ptr4[i]; } } } } } public class MonoToStereoProvider16 : IWaveProvider { private readonly IWaveProvider sourceProvider; private byte[] sourceBuffer; public float LeftVolume { get; set; } public float RightVolume { get; set; } public WaveFormat WaveFormat { get; } public MonoToStereoProvider16(IWaveProvider sourceProvider) { if (sourceProvider.WaveFormat.Encoding != WaveFormatEncoding.Pcm) { throw new ArgumentException("Source must be PCM"); } if (sourceProvider.WaveFormat.Channels != 1) { throw new ArgumentException("Source must be Mono"); } if (sourceProvider.WaveFormat.BitsPerSample != 16) { throw new ArgumentException("Source must be 16 bit"); } this.sourceProvider = sourceProvider; WaveFormat = new WaveFormat(sourceProvider.WaveFormat.SampleRate, 2); RightVolume = 1f; LeftVolume = 1f; } public int Read(byte[] buffer, int offset, int count) { int num = count / 2; sourceBuffer = BufferHelpers.Ensure(sourceBuffer, num); WaveBuffer waveBuffer = new WaveBuffer(sourceBuffer); WaveBuffer waveBuffer2 = new WaveBuffer(buffer); int num2 = sourceProvider.Read(sourceBuffer, 0, num) / 2; int num3 = offset / 2; for (int i = 0; i < num2; i++) { short num4 = waveBuffer.ShortBuffer[i]; waveBuffer2.ShortBuffer[num3++] = (short)(LeftVolume * (float)num4); waveBuffer2.ShortBuffer[num3++] = (short)(RightVolume * (float)num4); } return num2 * 4; } } public class MultiplexingWaveProvider : IWaveProvider { private readonly IList inputs; private readonly int outputChannelCount; private readonly int inputChannelCount; private readonly List mappings; private readonly int bytesPerSample; private byte[] inputBuffer; public WaveFormat WaveFormat { get; } public int InputChannelCount => inputChannelCount; public int OutputChannelCount => outputChannelCount; public MultiplexingWaveProvider(IEnumerable inputs) : this(inputs, -1) { } public MultiplexingWaveProvider(IEnumerable inputs, int numberOfOutputChannels) { this.inputs = new List(inputs); outputChannelCount = ((numberOfOutputChannels == -1) ? this.inputs.Sum((IWaveProvider i) => i.WaveFormat.Channels) : numberOfOutputChannels); if (this.inputs.Count == 0) { throw new ArgumentException("You must provide at least one input"); } if (outputChannelCount < 1) { throw new ArgumentException("You must provide at least one output"); } foreach (IWaveProvider input in this.inputs) { if (WaveFormat == null) { if (input.WaveFormat.Encoding == WaveFormatEncoding.Pcm) { WaveFormat = new WaveFormat(input.WaveFormat.SampleRate, input.WaveFormat.BitsPerSample, outputChannelCount); } else { if (input.WaveFormat.Encoding != WaveFormatEncoding.IeeeFloat) { throw new ArgumentException("Only PCM and 32 bit float are supported"); } WaveFormat = WaveFormat.CreateIeeeFloatWaveFormat(input.WaveFormat.SampleRate, outputChannelCount); } } else { if (input.WaveFormat.BitsPerSample != WaveFormat.BitsPerSample) { throw new ArgumentException("All inputs must have the same bit depth"); } if (input.WaveFormat.SampleRate != WaveFormat.SampleRate) { throw new ArgumentException("All inputs must have the same sample rate"); } } inputChannelCount += input.WaveFormat.Channels; } bytesPerSample = WaveFormat.BitsPerSample / 8; mappings = new List(); for (int j = 0; j < outputChannelCount; j++) { mappings.Add(j % inputChannelCount); } } public int Read(byte[] buffer, int offset, int count) { int num = bytesPerSample * outputChannelCount; int num2 = count / num; int num3 = 0; int num4 = 0; foreach (IWaveProvider input in inputs) { int num5 = bytesPerSample * input.WaveFormat.Channels; int num6 = num2 * num5; inputBuffer = BufferHelpers.Ensure(inputBuffer, num6); int num7 = input.Read(inputBuffer, 0, num6); num4 = Math.Max(num4, num7 / num5); for (int i = 0; i < input.WaveFormat.Channels; i++) { int num8 = num3 + i; for (int j = 0; j < outputChannelCount; j++) { if (mappings[j] != num8) { continue; } int num9 = i * bytesPerSample; int num10 = offset + j * bytesPerSample; int k; for (k = 0; k < num2; k++) { if (num9 >= num7) { break; } Array.Copy(inputBuffer, num9, buffer, num10, bytesPerSample); num10 += num; num9 += num5; } for (; k < num2; k++) { Array.Clear(buffer, num10, bytesPerSample); num10 += num; } } } num3 += input.WaveFormat.Channels; } return num4 * num; } public void ConnectInputToOutput(int inputChannel, int outputChannel) { if (inputChannel < 0 || inputChannel >= InputChannelCount) { throw new ArgumentException("Invalid input channel"); } if (outputChannel < 0 || outputChannel >= OutputChannelCount) { throw new ArgumentException("Invalid output channel"); } mappings[outputChannel] = inputChannel; } } public class SilenceProvider : IWaveProvider { public WaveFormat WaveFormat { get; private set; } public SilenceProvider(WaveFormat wf) { WaveFormat = wf; } public int Read(byte[] buffer, int offset, int count) { Array.Clear(buffer, offset, count); return count; } } public class StereoToMonoProvider16 : IWaveProvider { private readonly IWaveProvider sourceProvider; private byte[] sourceBuffer; public float LeftVolume { get; set; } public float RightVolume { get; set; } public WaveFormat WaveFormat { get; private set; } public StereoToMonoProvider16(IWaveProvider sourceProvider) { LeftVolume = 0.5f; RightVolume = 0.5f; if (sourceProvider.WaveFormat.Encoding != WaveFormatEncoding.Pcm) { throw new ArgumentException("Source must be PCM"); } if (sourceProvider.WaveFormat.Channels != 2) { throw new ArgumentException("Source must be stereo"); } if (sourceProvider.WaveFormat.BitsPerSample != 16) { throw new ArgumentException("Source must be 16 bit"); } this.sourceProvider = sourceProvider; WaveFormat = new WaveFormat(sourceProvider.WaveFormat.SampleRate, 1); } public int Read(byte[] buffer, int offset, int count) { int num = count * 2; sourceBuffer = BufferHelpers.Ensure(sourceBuffer, num); WaveBuffer waveBuffer = new WaveBuffer(sourceBuffer); WaveBuffer waveBuffer2 = new WaveBuffer(buffer); int num2 = sourceProvider.Read(sourceBuffer, 0, num); int num3 = num2 / 2; int num4 = offset / 2; for (int i = 0; i < num3; i += 2) { short num5 = waveBuffer.ShortBuffer[i]; short num6 = waveBuffer.ShortBuffer[i + 1]; float num7 = (float)num5 * LeftVolume + (float)num6 * RightVolume; if (num7 > 32767f) { num7 = 32767f; } if (num7 < -32768f) { num7 = -32768f; } waveBuffer2.ShortBuffer[num4++] = (short)num7; } return num2 / 2; } } public class VolumeWaveProvider16 : IWaveProvider { private readonly IWaveProvider sourceProvider; private float volume; public float Volume { get { return volume; } set { volume = value; } } public WaveFormat WaveFormat => sourceProvider.WaveFormat; public VolumeWaveProvider16(IWaveProvider sourceProvider) { Volume = 1f; this.sourceProvider = sourceProvider; if (sourceProvider.WaveFormat.Encoding != WaveFormatEncoding.Pcm) { throw new ArgumentException("Expecting PCM input"); } if (sourceProvider.WaveFormat.BitsPerSample != 16) { throw new ArgumentException("Expecting 16 bit"); } } public int Read(byte[] buffer, int offset, int count) { int num = sourceProvider.Read(buffer, offset, count); if (volume == 0f) { for (int i = 0; i < num; i++) { buffer[offset++] = 0; } } else if (volume != 1f) { for (int j = 0; j < num; j += 2) { short num2 = (short)((buffer[offset + 1] << 8) | buffer[offset]); float num3 = (float)num2 * volume; num2 = (short)num3; if (Volume > 1f) { if (num3 > 32767f) { num2 = short.MaxValue; } else if (num3 < -32768f) { num2 = short.MinValue; } } buffer[offset++] = (byte)((uint)num2 & 0xFFu); buffer[offset++] = (byte)(num2 >> 8); } } return num; } } public class Wave16ToFloatProvider : IWaveProvider { private IWaveProvider sourceProvider; private readonly WaveFormat waveFormat; private volatile float volume; private byte[] sourceBuffer; public WaveFormat WaveFormat => waveFormat; public float Volume { get { return volume; } set { volume = value; } } public Wave16ToFloatProvider(IWaveProvider sourceProvider) { if (sourceProvider.WaveFormat.Encoding != WaveFormatEncoding.Pcm) { throw new ArgumentException("Only PCM supported"); } if (sourceProvider.WaveFormat.BitsPerSample != 16) { throw new ArgumentException("Only 16 bit audio supported"); } waveFormat = WaveFormat.CreateIeeeFloatWaveFormat(sourceProvider.WaveFormat.SampleRate, sourceProvider.WaveFormat.Channels); this.sourceProvider = sourceProvider; volume = 1f; } public int Read(byte[] destBuffer, int offset, int numBytes) { int num = numBytes / 2; sourceBuffer = BufferHelpers.Ensure(sourceBuffer, num); int num2 = sourceProvider.Read(sourceBuffer, offset, num); WaveBuffer waveBuffer = new WaveBuffer(sourceBuffer); WaveBuffer waveBuffer2 = new WaveBuffer(destBuffer); int num3 = num2 / 2; int num4 = offset / 4; for (int i = 0; i < num3; i++) { waveBuffer2.FloatBuffer[num4++] = (float)waveBuffer.ShortBuffer[i] / 32768f * volume; } return num3 * 4; } } public class WaveFloatTo16Provider : IWaveProvider { private readonly IWaveProvider sourceProvider; private readonly WaveFormat waveFormat; private volatile float volume; private byte[] sourceBuffer; public WaveFormat WaveFormat => waveFormat; public float Volume { get { return volume; } set { volume = value; } } public WaveFloatTo16Provider(IWaveProvider sourceProvider) { if (sourceProvider.WaveFormat.Encoding != WaveFormatEncoding.IeeeFloat) { throw new ArgumentException("Input wave provider must be IEEE float", "sourceProvider"); } if (sourceProvider.WaveFormat.BitsPerSample != 32) { throw new ArgumentException("Input wave provider must be 32 bit", "sourceProvider"); } waveFormat = new WaveFormat(sourceProvider.WaveFormat.SampleRate, 16, sourceProvider.WaveFormat.Channels); this.sourceProvider = sourceProvider; volume = 1f; } public int Read(byte[] destBuffer, int offset, int numBytes) { int num = numBytes * 2; sourceBuffer = BufferHelpers.Ensure(sourceBuffer, num); int num2 = sourceProvider.Read(sourceBuffer, 0, num); WaveBuffer waveBuffer = new WaveBuffer(sourceBuffer); WaveBuffer waveBuffer2 = new WaveBuffer(destBuffer); int num3 = num2 / 4; int num4 = offset / 2; for (int i = 0; i < num3; i++) { float num5 = waveBuffer.FloatBuffer[i] * volume; if (num5 > 1f) { num5 = 1f; } if (num5 < -1f) { num5 = -1f; } waveBuffer2.ShortBuffer[num4++] = (short)(num5 * 32767f); } return num3 * 2; } } public class WaveInProvider : IWaveProvider { private readonly IWaveIn waveIn; private readonly BufferedWaveProvider bufferedWaveProvider; public WaveFormat WaveFormat => waveIn.WaveFormat; public WaveInProvider(IWaveIn waveIn) { this.waveIn = waveIn; waveIn.DataAvailable += OnDataAvailable; bufferedWaveProvider = new BufferedWaveProvider(WaveFormat); } private void OnDataAvailable(object sender, WaveInEventArgs e) { bufferedWaveProvider.AddSamples(e.Buffer, 0, e.BytesRecorded); } public int Read(byte[] buffer, int offset, int count) { return bufferedWaveProvider.Read(buffer, offset, count); } } public abstract class WaveProvider16 : IWaveProvider { private WaveFormat waveFormat; public WaveFormat WaveFormat => waveFormat; public WaveProvider16() : this(44100, 1) { } public WaveProvider16(int sampleRate, int channels) { SetWaveFormat(sampleRate, channels); } public void SetWaveFormat(int sampleRate, int channels) { waveFormat = new WaveFormat(sampleRate, 16, channels); } public int Read(byte[] buffer, int offset, int count) { WaveBuffer waveBuffer = new WaveBuffer(buffer); int sampleCount = count / 2; return Read(waveBuffer.ShortBuffer, offset / 2, sampleCount) * 2; } public abstract int Read(short[] buffer, int offset, int sampleCount); } public abstract class WaveProvider32 : IWaveProvider, ISampleProvider { private WaveFormat waveFormat; public WaveFormat WaveFormat => waveFormat; public WaveProvider32() : this(44100, 1) { } public WaveProvider32(int sampleRate, int channels) { SetWaveFormat(sampleRate, channels); } public void SetWaveFormat(int sampleRate, int channels) { waveFormat = WaveFormat.CreateIeeeFloatWaveFormat(sampleRate, channels); } public int Read(byte[] buffer, int offset, int count) { WaveBuffer waveBuffer = new WaveBuffer(buffer); int sampleCount = count / 4; return Read(waveBuffer.FloatBuffer, offset / 4, sampleCount) * 4; } public abstract int Read(float[] buffer, int offset, int sampleCount); } public class WaveRecorder : IWaveProvider, IDisposable { private WaveFileWriter writer; private IWaveProvider source; public WaveFormat WaveFormat => source.WaveFormat; public WaveRecorder(IWaveProvider source, string destination) { this.source = source; writer = new WaveFileWriter(destination, source.WaveFormat); } public int Read(byte[] buffer, int offset, int count) { int num = source.Read(buffer, offset, count); writer.Write(buffer, offset, num); return num; } public void Dispose() { if (writer != null) { writer.Dispose(); writer = null; } } } public class AiffFileReader : WaveStream { public struct AiffChunk { public string ChunkName; public uint ChunkLength; public uint ChunkStart; public AiffChunk(uint start, string name, uint length) { ChunkStart = start; ChunkName = name; ChunkLength = length + ((length % 2 == 1) ? 1u : 0u); } } private readonly WaveFormat waveFormat; private readonly bool ownInput; private readonly long dataPosition; private readonly int dataChunkLength; private readonly List chunks = new List(); private Stream waveStream; private readonly object lockObject = new object(); public override WaveFormat WaveFormat => waveFormat; public override long Length => dataChunkLength; public long SampleCount { get { if (waveFormat.Encoding == WaveFormatEncoding.Pcm || waveFormat.Encoding == WaveFormatEncoding.Extensible || waveFormat.Encoding == WaveFormatEncoding.IeeeFloat) { return dataChunkLength / BlockAlign; } throw new FormatException("Sample count is calculated only for the standard encodings"); } } public override long Position { get { return waveStream.Position - dataPosition; } set { lock (lockObject) { value = Math.Min(value, Length); value -= value % waveFormat.BlockAlign; waveStream.Position = value + dataPosition; } } } public AiffFileReader(string aiffFile) : this(File.OpenRead(aiffFile)) { ownInput = true; } public AiffFileReader(Stream inputStream) { waveStream = inputStream; ReadAiffHeader(waveStream, out waveFormat, out dataPosition, out dataChunkLength, chunks); Position = 0L; } public static void ReadAiffHeader(Stream stream, out WaveFormat format, out long dataChunkPosition, out int dataChunkLength, List chunks) { dataChunkPosition = -1L; format = null; BinaryReader binaryReader = new BinaryReader(stream); if (ReadChunkName(binaryReader) != "FORM") { throw new FormatException("Not an AIFF file - no FORM header."); } ConvertInt(binaryReader.ReadBytes(4)); string text = ReadChunkName(binaryReader); if (text != "AIFC" && text != "AIFF") { throw new FormatException("Not an AIFF file - no AIFF/AIFC header."); } dataChunkLength = 0; while (binaryReader.BaseStream.Position < binaryReader.BaseStream.Length) { AiffChunk item = ReadChunkHeader(binaryReader); if (item.ChunkName == "\0\0\0\0" || binaryReader.BaseStream.Position + item.ChunkLength > binaryReader.BaseStream.Length) { break; } if (item.ChunkName == "COMM") { short channels = ConvertShort(binaryReader.ReadBytes(2)); ConvertInt(binaryReader.ReadBytes(4)); short bits = ConvertShort(binaryReader.ReadBytes(2)); double num = IEEE.ConvertFromIeeeExtended(binaryReader.ReadBytes(10)); format = new WaveFormat((int)num, bits, channels); if (item.ChunkLength > 18 && text == "AIFC") { if (new string(binaryReader.ReadChars(4)).ToLower() != "none") { throw new FormatException("Compressed AIFC is not supported."); } binaryReader.ReadBytes((int)(item.ChunkLength - 22)); } else { binaryReader.ReadBytes((int)(item.ChunkLength - 18)); } } else if (item.ChunkName == "SSND") { uint num2 = ConvertInt(binaryReader.ReadBytes(4)); ConvertInt(binaryReader.ReadBytes(4)); dataChunkPosition = item.ChunkStart + 16 + num2; dataChunkLength = (int)(item.ChunkLength - 8); binaryReader.BaseStream.Position += item.ChunkLength - 8; } else { chunks?.Add(item); binaryReader.BaseStream.Position += item.ChunkLength; } } if (format == null) { throw new FormatException("Invalid AIFF file - No COMM chunk found."); } if (dataChunkPosition == -1) { throw new FormatException("Invalid AIFF file - No SSND chunk found."); } } protected override void Dispose(bool disposing) { if (disposing && waveStream != null) { if (ownInput) { waveStream.Dispose(); } waveStream = null; } base.Dispose(disposing); } public override int Read(byte[] array, int offset, int count) { if (count % waveFormat.BlockAlign != 0) { throw new ArgumentException($"Must read complete blocks: requested {count}, block align is {WaveFormat.BlockAlign}"); } lock (lockObject) { if (Position + count > dataChunkLength) { count = dataChunkLength - (int)Position; } byte[] array2 = new byte[count]; int num = waveStream.Read(array2, offset, count); int num2 = WaveFormat.BitsPerSample / 8; for (int i = 0; i < num; i += num2) { if (WaveFormat.BitsPerSample == 8) { array[i] = array2[i]; continue; } if (WaveFormat.BitsPerSample == 16) { array[i] = array2[i + 1]; array[i + 1] = array2[i]; continue; } if (WaveFormat.BitsPerSample == 24) { array[i] = array2[i + 2]; array[i + 1] = array2[i + 1]; array[i + 2] = array2[i]; continue; } if (WaveFormat.BitsPerSample == 32) { array[i] = array2[i + 3]; array[i + 1] = array2[i + 2]; array[i + 2] = array2[i + 1]; array[i + 3] = array2[i]; continue; } throw new FormatException("Unsupported PCM format."); } return num; } } private static uint ConvertInt(byte[] buffer) { if (buffer.Length != 4) { throw new Exception("Incorrect length for long."); } return (uint)((buffer[0] << 24) | (buffer[1] << 16) | (buffer[2] << 8) | buffer[3]); } private static short ConvertShort(byte[] buffer) { if (buffer.Length != 2) { throw new Exception("Incorrect length for int."); } return (short)((buffer[0] << 8) | buffer[1]); } private static AiffChunk ReadChunkHeader(BinaryReader br) { return new AiffChunk((uint)br.BaseStream.Position, ReadChunkName(br), ConvertInt(br.ReadBytes(4))); } private static string ReadChunkName(BinaryReader br) { return new string(br.ReadChars(4)); } } public class BlockAlignReductionStream : WaveStream { private WaveStream sourceStream; private long position; private readonly CircularBuffer circularBuffer; private long bufferStartPosition; private byte[] sourceBuffer; private readonly object lockObject = new object(); public override int BlockAlign => WaveFormat.BitsPerSample / 8 * WaveFormat.Channels; public override WaveFormat WaveFormat => sourceStream.WaveFormat; public override long Length => sourceStream.Length; public override long Position { get { return position; } set { lock (lockObject) { if (position != value) { if (position % BlockAlign != 0L) { throw new ArgumentException("Position must be block aligned"); } long num = value - value % sourceStream.BlockAlign; if (sourceStream.Position != num) { sourceStream.Position = num; circularBuffer.Reset(); bufferStartPosition = sourceStream.Position; } position = value; } } } } private long BufferEndPosition => bufferStartPosition + circularBuffer.Count; public BlockAlignReductionStream(WaveStream sourceStream) { this.sourceStream = sourceStream; circularBuffer = new CircularBuffer(sourceStream.WaveFormat.AverageBytesPerSecond * 4); } private byte[] GetSourceBuffer(int size) { if (sourceBuffer == null || sourceBuffer.Length < size) { sourceBuffer = new byte[size * 2]; } return sourceBuffer; } protected override void Dispose(bool disposing) { if (disposing && sourceStream != null) { sourceStream.Dispose(); sourceStream = null; } base.Dispose(disposing); } public override int Read(byte[] buffer, int offset, int count) { lock (lockObject) { while (BufferEndPosition < position + count) { int num = count; if (num % sourceStream.BlockAlign != 0) { num = count + sourceStream.BlockAlign - count % sourceStream.BlockAlign; } int num2 = sourceStream.Read(GetSourceBuffer(num), 0, num); circularBuffer.Write(GetSourceBuffer(num), 0, num2); if (num2 == 0) { break; } } if (bufferStartPosition < position) { circularBuffer.Advance((int)(position - bufferStartPosition)); bufferStartPosition = position; } int num3 = circularBuffer.Read(buffer, offset, count); position += num3; bufferStartPosition = position; return num3; } } } public class Cue { public int Position { get; } public string Label { get; } public Cue(int position, string label) { Position = position; Label = label ?? string.Empty; } } public class CueList { private readonly List cues = new List(); public int[] CuePositions { get { int[] array = new int[cues.Count]; for (int i = 0; i < cues.Count; i++) { array[i] = cues[i].Position; } return array; } } public string[] CueLabels { get { string[] array = new string[cues.Count]; for (int i = 0; i < cues.Count; i++) { array[i] = cues[i].Label; } return array; } } public int Count => cues.Count; public Cue this[int index] => cues[index]; public CueList() { } public void Add(Cue cue) { cues.Add(cue); } internal CueList(byte[] cueChunkData, byte[] listChunkData) { int num = BitConverter.ToInt32(cueChunkData, 0); Dictionary dictionary = new Dictionary(); int[] array = new int[num]; int num2 = 0; int num3 = 4; while (cueChunkData.Length - num3 >= 24) { dictionary[BitConverter.ToInt32(cueChunkData, num3)] = num2; array[num2] = BitConverter.ToInt32(cueChunkData, num3 + 20); num3 += 24; num2++; } string[] array2 = new string[num]; int num4 = 0; int num5 = ChunkIdentifier.ChunkIdentifierToInt32("labl"); for (int i = 4; listChunkData.Length - i >= 16; i += num4 + num4 % 2 + 12) { if (BitConverter.ToInt32(listChunkData, i) == num5) { num4 = BitConverter.ToInt32(listChunkData, i + 4) - 4; int key = BitConverter.ToInt32(listChunkData, i + 8); num2 = dictionary[key]; array2[num2] = Encoding.UTF8.GetString(listChunkData, i + 12, num4 - 1); } } for (int j = 0; j < num; j++) { cues.Add(new Cue(array[j], array2[j])); } } internal byte[] GetRiffChunks() { if (Count == 0) { return null; } int num = 12 + 24 * Count; int num2 = 12; for (int i = 0; i < Count; i++) { int num3 = Encoding.UTF8.GetBytes(this[i].Label).Length + 1; num2 += num3 + num3 % 2 + 12; } byte[] array = new byte[num + num2]; int value = ChunkIdentifier.ChunkIdentifierToInt32("cue "); int value2 = ChunkIdentifier.ChunkIdentifierToInt32("data"); int value3 = ChunkIdentifier.ChunkIdentifierToInt32("LIST"); int value4 = ChunkIdentifier.ChunkIdentifierToInt32("adtl"); int value5 = ChunkIdentifier.ChunkIdentifierToInt32("labl"); using MemoryStream output = new MemoryStream(array); using BinaryWriter binaryWriter = new BinaryWriter(output); binaryWriter.Write(value); binaryWriter.Write(num - 8); binaryWriter.Write(Count); for (int j = 0; j < Count; j++) { int position = this[j].Position; binaryWriter.Write(j); binaryWriter.Write(position); binaryWriter.Write(value2); binaryWriter.Seek(8, SeekOrigin.Current); binaryWriter.Write(position); } binaryWriter.Write(value3); binaryWriter.Write(num2 - 8); binaryWriter.Write(value4); for (int k = 0; k < Count; k++) { byte[] bytes = Encoding.UTF8.GetBytes(this[k].Label); binaryWriter.Write(value5); binaryWriter.Write(bytes.Length + 1 + 4); binaryWriter.Write(k); binaryWriter.Write(bytes); if (bytes.Length % 2 == 0) { binaryWriter.Seek(2, SeekOrigin.Current); } else { binaryWriter.Seek(1, SeekOrigin.Current); } } return array; } internal static CueList FromChunks(WaveFileReader reader) { CueList result = null; byte[] array = null; byte[] array2 = null; foreach (RiffChunk extraChunk in reader.ExtraChunks) { if (extraChunk.IdentifierAsString.ToLower() == "cue ") { array = reader.GetChunkData(extraChunk); } else if (extraChunk.IdentifierAsString.ToLower() == "list") { array2 = reader.GetChunkData(extraChunk); } } if (array != null && array2 != null) { result = new CueList(array, array2); } return result; } } public class CueWaveFileReader : WaveFileReader { private CueList cues; public CueList Cues { get { if (cues == null) { cues = CueList.FromChunks(this); } return cues; } } public CueWaveFileReader(string fileName) : base(fileName) { } public CueWaveFileReader(Stream inputStream) : base(inputStream) { } } public interface ISampleNotifier { event EventHandler Sample; } public class SampleEventArgs : EventArgs { public float Left { get; set; } public float Right { get; set; } public SampleEventArgs(float left, float right) { Left = left; Right = right; } } internal class Mp3Index { public long FilePosition { get; set; } public long SamplePosition { get; set; } public int SampleCount { get; set; } public int ByteCount { get; set; } } public class Mp3FileReaderBase : WaveStream { public delegate IMp3FrameDecompressor FrameDecompressorBuilder(WaveFormat mp3Format); private readonly WaveFormat waveFormat; private Stream mp3Stream; private readonly long mp3DataLength; private readonly long dataStartPosition; private readonly XingHeader xingHeader; private readonly bool ownInputStream; private List tableOfContents; private int tocIndex; private long totalSamples; private readonly int bytesPerSample; private readonly int bytesPerDecodedFrame; private IMp3FrameDecompressor decompressor; private readonly byte[] decompressBuffer; private int decompressBufferOffset; private int decompressLeftovers; private bool repositionedFlag; private long position; private readonly object repositionLock = new object(); public Mp3WaveFormat Mp3WaveFormat { get; private set; } public Id3v2Tag Id3v2Tag { get; } public byte[] Id3v1Tag { get; } public override long Length => totalSamples * bytesPerSample; public override WaveFormat WaveFormat => waveFormat; public override long Position { get { return position; } set { lock (repositionLock) { value = Math.Max(Math.Min(value, Length), 0L); long num = value / bytesPerSample; Mp3Index mp3Index = null; for (int i = 0; i < tableOfContents.Count; i++) { if (tableOfContents[i].SamplePosition + tableOfContents[i].SampleCount > num) { mp3Index = tableOfContents[i]; tocIndex = i; break; } } decompressBufferOffset = 0; decompressLeftovers = 0; repositionedFlag = true; if (mp3Index != null) { mp3Stream.Position = mp3Index.FilePosition; long num2 = num - mp3Index.SamplePosition; if (num2 > 0) { decompressBufferOffset = (int)num2 * bytesPerSample; } } else { mp3Stream.Position = mp3DataLength + dataStartPosition; } position = value; } } } public XingHeader XingHeader => xingHeader; public Mp3FileReaderBase(string mp3FileName, FrameDecompressorBuilder frameDecompressorBuilder) : this(File.OpenRead(mp3FileName), frameDecompressorBuilder, ownInputStream: true) { } public Mp3FileReaderBase(Stream inputStream, FrameDecompressorBuilder frameDecompressorBuilder) : this(inputStream, frameDecompressorBuilder, ownInputStream: false) { } protected Mp3FileReaderBase(Stream inputStream, FrameDecompressorBuilder frameDecompressorBuilder, bool ownInputStream) { if (inputStream == null) { throw new ArgumentNullException("inputStream"); } if (frameDecompressorBuilder == null) { throw new ArgumentNullException("frameDecompressorBuilder"); } this.ownInputStream = ownInputStream; try { mp3Stream = inputStream; Id3v2Tag = Id3v2Tag.ReadTag(mp3Stream); dataStartPosition = mp3Stream.Position; Mp3Frame mp3Frame = Mp3Frame.LoadFromStream(mp3Stream); if (mp3Frame == null) { throw new InvalidDataException("Invalid MP3 file - no MP3 Frames Detected"); } double num = mp3Frame.BitRate; xingHeader = XingHeader.LoadXingHeader(mp3Frame); if (xingHeader != null) { dataStartPosition = mp3Stream.Position; } Mp3Frame mp3Frame2 = Mp3Frame.LoadFromStream(mp3Stream); if (mp3Frame2 != null && (mp3Frame2.SampleRate != mp3Frame.SampleRate || mp3Frame2.ChannelMode != mp3Frame.ChannelMode)) { dataStartPosition = mp3Frame2.FileOffset; mp3Frame = mp3Frame2; } mp3DataLength = mp3Stream.Length - dataStartPosition; mp3Stream.Position = mp3Stream.Length - 128; byte[] array = new byte[128]; mp3Stream.Read(array, 0, 128); if (array[0] == 84 && array[1] == 65 && array[2] == 71) { Id3v1Tag = array; mp3DataLength -= 128L; } mp3Stream.Position = dataStartPosition; Mp3WaveFormat = new Mp3WaveFormat(mp3Frame.SampleRate, (mp3Frame.ChannelMode == ChannelMode.Mono) ? 1 : 2, mp3Frame.FrameLength, (int)num); CreateTableOfContents(); tocIndex = 0; num = (double)mp3DataLength * 8.0 / TotalSeconds(); mp3Stream.Position = dataStartPosition; Mp3WaveFormat = new Mp3WaveFormat(mp3Frame.SampleRate, (mp3Frame.ChannelMode == ChannelMode.Mono) ? 1 : 2, mp3Frame.FrameLength, (int)num); decompressor = frameDecompressorBuilder(Mp3WaveFormat); waveFormat = decompressor.OutputFormat; bytesPerSample = decompressor.OutputFormat.BitsPerSample / 8 * decompressor.OutputFormat.Channels; bytesPerDecodedFrame = 1152 * bytesPerSample; decompressBuffer = new byte[bytesPerDecodedFrame * 2]; } catch (Exception) { if (ownInputStream) { inputStream.Dispose(); } throw; } } private void CreateTableOfContents() { try { tableOfContents = new List((int)(mp3DataLength / 400)); Mp3Frame mp3Frame; do { Mp3Index mp3Index = new Mp3Index(); mp3Index.FilePosition = mp3Stream.Position; mp3Index.SamplePosition = totalSamples; mp3Frame = ReadNextFrame(readData: false); if (mp3Frame != null) { ValidateFrameFormat(mp3Frame); totalSamples += mp3Frame.SampleCount; mp3Index.SampleCount = mp3Frame.SampleCount; mp3Index.ByteCount = (int)(mp3Stream.Position - mp3Index.FilePosition); tableOfContents.Add(mp3Index); } } while (mp3Frame != null); } catch (EndOfStreamException) { } } private void ValidateFrameFormat(Mp3Frame frame) { if (frame.SampleRate != Mp3WaveFormat.SampleRate) { throw new InvalidOperationException($"Got a frame at sample rate {frame.SampleRate}, in an MP3 with sample rate {Mp3WaveFormat.SampleRate}. Mp3FileReader does not support sample rate changes."); } if (((frame.ChannelMode == ChannelMode.Mono) ? 1 : 2) != Mp3WaveFormat.Channels) { throw new InvalidOperationException($"Got a frame with channel mode {frame.ChannelMode}, in an MP3 with {Mp3WaveFormat.Channels} channels. Mp3FileReader does not support changes to channel count."); } } private double TotalSeconds() { return (double)totalSamples / (double)Mp3WaveFormat.SampleRate; } public Mp3Frame ReadNextFrame() { Mp3Frame mp3Frame = ReadNextFrame(readData: true); if (mp3Frame != null) { position += mp3Frame.SampleCount * bytesPerSample; } return mp3Frame; } private Mp3Frame ReadNextFrame(bool readData) { Mp3Frame mp3Frame = null; try { mp3Frame = Mp3Frame.LoadFromStream(mp3Stream, readData); if (mp3Frame != null) { tocIndex++; } } catch (EndOfStreamException) { } return mp3Frame; } public override int Read(byte[] sampleBuffer, int offset, int numBytes) { int num = 0; lock (repositionLock) { if (decompressLeftovers != 0) { int num2 = Math.Min(decompressLeftovers, numBytes); Array.Copy(decompressBuffer, decompressBufferOffset, sampleBuffer, offset, num2); decompressLeftovers -= num2; if (decompressLeftovers == 0) { decompressBufferOffset = 0; } else { decompressBufferOffset += num2; } num += num2; offset += num2; } int num3 = tocIndex; if (repositionedFlag) { decompressor.Reset(); tocIndex = Math.Max(0, tocIndex - 3); mp3Stream.Position = tableOfContents[tocIndex].FilePosition; repositionedFlag = false; } while (num < numBytes) { Mp3Frame mp3Frame = ReadNextFrame(readData: true); if (mp3Frame == null) { break; } int num4 = decompressor.DecompressFrame(mp3Frame, decompressBuffer, 0); if (tocIndex > num3 && num4 != 0) { if (tocIndex == num3 + 1 && num4 == bytesPerDecodedFrame * 2) { Array.Copy(decompressBuffer, bytesPerDecodedFrame, decompressBuffer, 0, bytesPerDecodedFrame); num4 = bytesPerDecodedFrame; } int num5 = Math.Min(num4 - decompressBufferOffset, numBytes - num); Array.Copy(decompressBuffer, decompressBufferOffset, sampleBuffer, offset, num5); if (num5 + decompressBufferOffset < num4) { decompressBufferOffset = num5 + decompressBufferOffset; decompressLeftovers = num4 - decompressBufferOffset; } else { decompressBufferOffset = 0; decompressLeftovers = 0; } offset += num5; num += num5; } } } position += num; return num; } protected override void Dispose(bool disposing) { if (disposing) { if (mp3Stream != null) { if (ownInputStream) { mp3Stream.Dispose(); } mp3Stream = null; } if (decompressor != null) { decompressor.Dispose(); decompressor = null; } } base.Dispose(disposing); } } public class RawSourceWaveStream : WaveStream { private readonly Stream sourceStream; private readonly WaveFormat waveFormat; public override WaveFormat WaveFormat => waveFormat; public override long Length => sourceStream.Length; public override long Position { get { return sourceStream.Position; } set { sourceStream.Position = value - value % waveFormat.BlockAlign; } } public RawSourceWaveStream(Stream sourceStream, WaveFormat waveFormat) { this.sourceStream = sourceStream; this.waveFormat = waveFormat; } public RawSourceWaveStream(byte[] byteStream, int offset, int count, WaveFormat waveFormat) { sourceStream = new MemoryStream(byteStream, offset, count); this.waveFormat = waveFormat; } public override int Read(byte[] buffer, int offset, int count) { try { return sourceStream.Read(buffer, offset, count); } catch (EndOfStreamException) { return 0; } } } public class RiffChunk { public int Identifier { get; } public string IdentifierAsString => Encoding.UTF8.GetString(BitConverter.GetBytes(Identifier)); public int Length { get; private set; } public long StreamPosition { get; private set; } public RiffChunk(int identifier, int length, long streamPosition) { Identifier = identifier; Length = length; StreamPosition = streamPosition; } } public class SimpleCompressorEffect : ISampleProvider { private readonly ISampleProvider sourceStream; private readonly SimpleCompressor simpleCompressor; private readonly int channels; private readonly object lockObject = new object(); public double MakeUpGain { get { return simpleCompressor.MakeUpGain; } set { lock (lockObject) { simpleCompressor.MakeUpGain = value; } } } public double Threshold { get { return simpleCompressor.Threshold; } set { lock (lockObject) { simpleCompressor.Threshold = value; } } } public double Ratio { get { return simpleCompressor.Ratio; } set { lock (lockObject) { simpleCompressor.Ratio = value; } } } public double Attack { get { return simpleCompressor.Attack; } set { lock (lockObject) { simpleCompressor.Attack = value; } } } public double Release { get { return simpleCompressor.Release; } set { lock (lockObject) { simpleCompressor.Release = value; } } } public bool Enabled { get; set; } public WaveFormat WaveFormat => sourceStream.WaveFormat; public SimpleCompressorEffect(ISampleProvider sourceStream) { this.sourceStream = sourceStream; channels = sourceStream.WaveFormat.Channels; simpleCompressor = new SimpleCompressor(5.0, 10.0, sourceStream.WaveFormat.SampleRate); simpleCompressor.Threshold = 16.0; simpleCompressor.Ratio = 6.0; simpleCompressor.MakeUpGain = 16.0; } public int Read(float[] array, int offset, int count) { lock (lockObject) { int num = sourceStream.Read(array, offset, count); if (Enabled) { for (int i = 0; i < num; i += channels) { double @in = array[offset + i]; double in2 = ((channels == 1) ? 0f : array[offset + i + 1]); simpleCompressor.Process(ref @in, ref in2); array[offset + i] = (float)@in; if (channels > 1) { array[offset + i + 1] = (float)in2; } } } return num; } } } public class Wave32To16Stream : WaveStream { private WaveStream sourceStream; private readonly WaveFormat waveFormat; private readonly long length; private long position; private bool clip; private float volume; private readonly object lockObject = new object(); private byte[] sourceBuffer; public float Volume { get { return volume; } set { volume = value; } } public override int BlockAlign => sourceStream.BlockAlign / 2; public override long Length => length; public override long Position { get { return position; } set { lock (lockObject) { value -= value % BlockAlign; sourceStream.Position = value * 2; position = value; } } } public override WaveFormat WaveFormat => waveFormat; public bool Clip { get { return clip; } set { clip = value; } } public Wave32To16Stream(WaveStream sourceStream) { if (sourceStream.WaveFormat.Encoding != WaveFormatEncoding.IeeeFloat) { throw new ArgumentException("Only 32 bit Floating point supported"); } if (sourceStream.WaveFormat.BitsPerSample != 32) { throw new ArgumentException("Only 32 bit Floating point supported"); } waveFormat = new WaveFormat(sourceStream.WaveFormat.SampleRate, 16, sourceStream.WaveFormat.Channels); volume = 1f; this.sourceStream = sourceStream; length = sourceStream.Length / 2; position = sourceStream.Position / 2; } public override int Read(byte[] destBuffer, int offset, int numBytes) { lock (lockObject) { int num = numBytes * 2; sourceBuffer = BufferHelpers.Ensure(sourceBuffer, num); int num2 = sourceStream.Read(sourceBuffer, 0, num); Convert32To16(destBuffer, offset, sourceBuffer, num2); position += num2 / 2; return num2 / 2; } } private unsafe void Convert32To16(byte[] destBuffer, int offset, byte[] source, int bytesRead) { fixed (byte* ptr = &destBuffer[offset]) { fixed (byte* ptr3 = &source[0]) { short* ptr2 = (short*)ptr; float* ptr4 = (float*)ptr3; int num = bytesRead / 4; for (int i = 0; i < num; i++) { float num2 = ptr4[i] * volume; if (num2 > 1f) { ptr2[i] = short.MaxValue; clip = true; } else if (num2 < -1f) { ptr2[i] = short.MinValue; clip = true; } else { ptr2[i] = (short)(num2 * 32767f); } } } } } protected override void Dispose(bool disposing) { if (disposing && sourceStream != null) { sourceStream.Dispose(); sourceStream = null; } base.Dispose(disposing); } } public class WaveChannel32 : WaveStream, ISampleNotifier { private WaveStream sourceStream; private readonly WaveFormat waveFormat; private readonly long length; private readonly int destBytesPerSample; private readonly int sourceBytesPerSample; private volatile float volume; private volatile float pan; private long position; private readonly ISampleChunkConverter sampleProvider; private readonly object lockObject = new object(); private SampleEventArgs sampleEventArgs = new SampleEventArgs(0f, 0f); public override int BlockAlign => (int)SourceToDest(sourceStream.BlockAlign); public override long Length => length; public override long Position { get { return position; } set { lock (lockObject) { value -= value % BlockAlign; if (value < 0) { sourceStream.Position = 0L; } else { sourceStream.Position = DestToSource(value); } position = SourceToDest(sourceStream.Position); } } } public bool PadWithZeroes { get; set; } public override WaveFormat WaveFormat => waveFormat; public float Volume { get { return volume; } set { volume = value; } } public float Pan { get { return pan; } set { pan = value; } } public event EventHandler Sample; public WaveChannel32(WaveStream sourceStream, float volume, float pan) { PadWithZeroes = true; ISampleChunkConverter[] array = new ISampleChunkConverter[8] { new Mono8SampleChunkConverter(), new Stereo8SampleChunkConverter(), new Mono16SampleChunkConverter(), new Stereo16SampleChunkConverter(), new Mono24SampleChunkConverter(), new Stereo24SampleChunkConverter(), new MonoFloatSampleChunkConverter(), new StereoFloatSampleChunkConverter() }; foreach (ISampleChunkConverter sampleChunkConverter in array) { if (sampleChunkConverter.Supports(sourceStream.WaveFormat)) { sampleProvider = sampleChunkConverter; break; } } if (sampleProvider == null) { throw new ArgumentException("Unsupported sourceStream format"); } waveFormat = WaveFormat.CreateIeeeFloatWaveFormat(sourceStream.WaveFormat.SampleRate, 2); destBytesPerSample = 8; this.sourceStream = sourceStream; this.volume = volume; this.pan = pan; sourceBytesPerSample = sourceStream.WaveFormat.Channels * sourceStream.WaveFormat.BitsPerSample / 8; length = SourceToDest(sourceStream.Length); position = 0L; } private long SourceToDest(long sourceBytes) { return sourceBytes / sourceBytesPerSample * destBytesPerSample; } private long DestToSource(long destBytes) { return destBytes / destBytesPerSample * sourceBytesPerSample; } public WaveChannel32(WaveStream sourceStream) : this(sourceStream, 1f, 0f) { } public override int Read(byte[] destBuffer, int offset, int numBytes) { lock (lockObject) { int num = 0; WaveBuffer waveBuffer = new WaveBuffer(destBuffer); if (position < 0) { num = (int)Math.Min(numBytes, -position); for (int i = 0; i < num; i++) { destBuffer[i + offset] = 0; } } if (num < numBytes) { sampleProvider.LoadNextChunk(sourceStream, (numBytes - num) / 8); int num2 = offset / 4 + num / 4; float sampleLeft; float sampleRight; while (sampleProvider.GetNextSample(out sampleLeft, out sampleRight) && num < numBytes) { sampleLeft = ((pan <= 0f) ? sampleLeft : (sampleLeft * (1f - pan) / 2f)); sampleRight = ((pan >= 0f) ? sampleRight : (sampleRight * (pan + 1f) / 2f)); sampleLeft *= volume; sampleRight *= volume; waveBuffer.FloatBuffer[num2++] = sampleLeft; waveBuffer.FloatBuffer[num2++] = sampleRight; num += 8; if (this.Sample != null) { RaiseSample(sampleLeft, sampleRight); } } } if (PadWithZeroes && num < numBytes) { Array.Clear(destBuffer, offset + num, numBytes - num); num = numBytes; } position += num; return num; } } public override bool HasData(int count) { if (sourceStream.HasData(count)) { if (position + count < 0) { return false; } if (position < length) { return volume != 0f; } return false; } return false; } protected override void Dispose(bool disposing) { if (disposing && sourceStream != null) { sourceStream.Dispose(); sourceStream = null; } base.Dispose(disposing); } private void RaiseSample(float left, float right) { sampleEventArgs.Left = left; sampleEventArgs.Right = right; this.Sample(this, sampleEventArgs); } } public class WaveFileReader : WaveStream { private readonly WaveFormat waveFormat; private readonly bool ownInput; private readonly long dataPosition; private readonly long dataChunkLength; private readonly object lockObject = new object(); private Stream waveStream; public List ExtraChunks { get; } public override WaveFormat WaveFormat => waveFormat; public override long Length => dataChunkLength; public long SampleCount { get { if (waveFormat.Encoding == WaveFormatEncoding.Pcm || waveFormat.Encoding == WaveFormatEncoding.Extensible || waveFormat.Encoding == WaveFormatEncoding.IeeeFloat) { return dataChunkLength / BlockAlign; } throw new InvalidOperationException("Sample count is calculated only for the standard encodings"); } } public override long Position { get { return waveStream.Position - dataPosition; } set { lock (lockObject) { value = Math.Min(value, Length); value -= value % waveFormat.BlockAlign; waveStream.Position = value + dataPosition; } } } public WaveFileReader(string waveFile) : this(File.OpenRead(waveFile), ownInput: true) { } public WaveFileReader(Stream inputStream) : this(inputStream, ownInput: false) { } private WaveFileReader(Stream inputStream, bool ownInput) { waveStream = inputStream; WaveFileChunkReader waveFileChunkReader = new WaveFileChunkReader(); try { waveFileChunkReader.ReadWaveHeader(inputStream); waveFormat = waveFileChunkReader.WaveFormat; dataPosition = waveFileChunkReader.DataChunkPosition; dataChunkLength = waveFileChunkReader.DataChunkLength; ExtraChunks = waveFileChunkReader.RiffChunks; } catch { if (ownInput) { inputStream.Dispose(); } throw; } Position = 0L; this.ownInput = ownInput; } public byte[] GetChunkData(RiffChunk chunk) { long position = waveStream.Position; waveStream.Position = chunk.StreamPosition; byte[] array = new byte[chunk.Length]; waveStream.Read(array, 0, array.Length); waveStream.Position = position; return array; } protected override void Dispose(bool disposing) { if (disposing && waveStream != null) { if (ownInput) { waveStream.Dispose(); } waveStream = null; } base.Dispose(disposing); } public override int Read(byte[] array, int offset, int count) { if (count % waveFormat.BlockAlign != 0) { throw new ArgumentException($"Must read complete blocks: requested {count}, block align is {WaveFormat.BlockAlign}"); } lock (lockObject) { if (Position + count > dataChunkLength) { count = (int)(dataChunkLength - Position); } return waveStream.Read(array, offset, count); } } public float[] ReadNextSampleFrame() { WaveFormatEncoding encoding = waveFormat.Encoding; if (encoding != WaveFormatEncoding.Pcm && encoding != WaveFormatEncoding.IeeeFloat && encoding != WaveFormatEncoding.Extensible) { throw new InvalidOperationException("Only 16, 24 or 32 bit PCM or IEEE float audio data supported"); } float[] array = new float[waveFormat.Channels]; int num = waveFormat.Channels * (waveFormat.BitsPerSample / 8); byte[] array2 = new byte[num]; int num2 = Read(array2, 0, num); if (num2 == 0) { return null; } if (num2 < num) { throw new InvalidDataException("Unexpected end of file"); } int num3 = 0; for (int i = 0; i < waveFormat.Channels; i++) { if (waveFormat.BitsPerSample == 16) { array[i] = (float)BitConverter.ToInt16(array2, num3) / 32768f; num3 += 2; continue; } if (waveFormat.BitsPerSample == 24) { array[i] = (float)(((sbyte)array2[num3 + 2] << 16) | (array2[num3 + 1] << 8) | array2[num3]) / 8388608f; num3 += 3; continue; } if (waveFormat.BitsPerSample == 32 && waveFormat.Encoding == WaveFormatEncoding.IeeeFloat) { array[i] = BitConverter.ToSingle(array2, num3); num3 += 4; continue; } if (waveFormat.BitsPerSample == 32) { array[i] = (float)BitConverter.ToInt32(array2, num3) / 2.1474836E+09f; num3 += 4; continue; } throw new InvalidOperationException("Unsupported bit depth"); } return array; } [Obsolete("Use ReadNextSampleFrame instead (this version does not support stereo properly)")] public bool TryReadFloat(out float sampleValue) { float[] array = ReadNextSampleFrame(); sampleValue = ((array != null) ? array[0] : 0f); return array != null; } } public class WaveMixerStream32 : WaveStream { private readonly List inputStreams; private readonly object inputsLock; private WaveFormat waveFormat; private long length; private long position; private readonly int bytesPerSample; public int InputCount => inputStreams.Count; public bool AutoStop { get; set; } public override int BlockAlign => waveFormat.BlockAlign; public override long Length => length; public override long Position { get { return position; } set { lock (inputsLock) { value = Math.Min(value, Length); foreach (WaveStream inputStream in inputStreams) { inputStream.Position = Math.Min(value, inputStream.Length); } position = value; } } } public override WaveFormat WaveFormat => waveFormat; public WaveMixerStream32() { AutoStop = true; waveFormat = WaveFormat.CreateIeeeFloatWaveFormat(44100, 2); bytesPerSample = 4; inputStreams = new List(); inputsLock = new object(); } public WaveMixerStream32(IEnumerable inputStreams, bool autoStop) : this() { AutoStop = autoStop; foreach (WaveStream inputStream in inputStreams) { AddInputStream(inputStream); } } public void AddInputStream(WaveStream waveStream) { if (waveStream.WaveFormat.Encoding != WaveFormatEncoding.IeeeFloat) { throw new ArgumentException("Must be IEEE floating point", "waveStream"); } if (waveStream.WaveFormat.BitsPerSample != 32) { throw new ArgumentException("Only 32 bit audio currently supported", "waveStream"); } if (inputStreams.Count == 0) { int sampleRate = waveStream.WaveFormat.SampleRate; int channels = waveStream.WaveFormat.Channels; waveFormat = WaveFormat.CreateIeeeFloatWaveFormat(sampleRate, channels); } else if (!waveStream.WaveFormat.Equals(waveFormat)) { throw new ArgumentException("All incoming channels must have the same format", "waveStream"); } lock (inputsLock) { inputStreams.Add(waveStream); length = Math.Max(length, waveStream.Length); waveStream.Position = Position; } } public void RemoveInputStream(WaveStream waveStream) { lock (inputsLock) { if (!inputStreams.Remove(waveStream)) { return; } long val = 0L; foreach (WaveStream inputStream in inputStreams) { val = Math.Max(val, inputStream.Length); } length = val; } } public override int Read(byte[] buffer, int offset, int count) { if (AutoStop && position + count > length) { count = (int)(length - position); } if (count % bytesPerSample != 0) { throw new ArgumentException("Must read an whole number of samples", "count"); } Array.Clear(buffer, offset, count); int val = 0; byte[] array = new byte[count]; lock (inputsLock) { foreach (WaveStream inputStream in inputStreams) { if (inputStream.HasData(count)) { int num = inputStream.Read(array, 0, count); val = Math.Max(val, num); if (num > 0) { Sum32BitAudio(buffer, offset, array, num); } } else { val = Math.Max(val, count); inputStream.Position += count; } } } position += count; return count; } private unsafe static void Sum32BitAudio(byte[] destBuffer, int offset, byte[] sourceBuffer, int bytesRead) { fixed (byte* ptr = &destBuffer[offset]) { fixed (byte* ptr3 = &sourceBuffer[0]) { float* ptr2 = (float*)ptr; float* ptr4 = (float*)ptr3; int num = bytesRead / 4; for (int i = 0; i < num; i++) { ptr2[i] += ptr4[i]; } } } } protected override void Dispose(bool disposing) { if (disposing) { lock (inputsLock) { foreach (WaveStream inputStream in inputStreams) { inputStream.Dispose(); } } } base.Dispose(disposing); } } public class WaveOffsetStream : WaveStream { private WaveStream sourceStream; private long audioStartPosition; private long sourceOffsetBytes; private long sourceLengthBytes; private long length; private readonly int bytesPerSample; private long position; private TimeSpan startTime; private TimeSpan sourceOffset; private TimeSpan sourceLength; private readonly object lockObject = new object(); public TimeSpan StartTime { get { return startTime; } set { lock (lockObject) { startTime = value; audioStartPosition = (long)(startTime.TotalSeconds * (double)sourceStream.WaveFormat.SampleRate) * bytesPerSample; length = audioStartPosition + sourceLengthBytes; Position = Position; } } } public TimeSpan SourceOffset { get { return sourceOffset; } set { lock (lockObject) { sourceOffset = value; sourceOffsetBytes = (long)(sourceOffset.TotalSeconds * (double)sourceStream.WaveFormat.SampleRate) * bytesPerSample; Position = Position; } } } public TimeSpan SourceLength { get { return sourceLength; } set { lock (lockObject) { sourceLength = value; sourceLengthBytes = (long)(sourceLength.TotalSeconds * (double)sourceStream.WaveFormat.SampleRate) * bytesPerSample; length = audioStartPosition + sourceLengthBytes; Position = Position; } } } public override int BlockAlign => sourceStream.BlockAlign; public override long Length => length; public override long Position { get { return position; } set { lock (lockObject) { value -= value % BlockAlign; if (value < audioStartPosition) { sourceStream.Position = sourceOffsetBytes; } else { sourceStream.Position = sourceOffsetBytes + (value - audioStartPosition); } position = value; } } } public override WaveFormat WaveFormat => sourceStream.WaveFormat; public WaveOffsetStream(WaveStream sourceStream, TimeSpan startTime, TimeSpan sourceOffset, TimeSpan sourceLength) { if (sourceStream.WaveFormat.Encoding != WaveFormatEncoding.Pcm) { throw new ArgumentException("Only PCM supported"); } this.sourceStream = sourceStream; position = 0L; bytesPerSample = sourceStream.WaveFormat.BitsPerSample / 8 * sourceStream.WaveFormat.Channels; StartTime = startTime; SourceOffset = sourceOffset; SourceLength = sourceLength; } public WaveOffsetStream(WaveStream sourceStream) : this(sourceStream, TimeSpan.Zero, TimeSpan.Zero, sourceStream.TotalTime) { } public override int Read(byte[] destBuffer, int offset, int numBytes) { lock (lockObject) { int num = 0; if (position < audioStartPosition) { num = (int)Math.Min(numBytes, audioStartPosition - position); for (int i = 0; i < num; i++) { destBuffer[i + offset] = 0; } } if (num < numBytes) { int count = (int)Math.Min(numBytes - num, sourceLengthBytes + sourceOffsetBytes - sourceStream.Position); int num2 = sourceStream.Read(destBuffer, num + offset, count); num += num2; } for (int j = num; j < numBytes; j++) { destBuffer[offset + j] = 0; } position += numBytes; return numBytes; } } public override bool HasData(int count) { if (position + count < audioStartPosition) { return false; } if (position >= length) { return false; } return sourceStream.HasData(count); } protected override void Dispose(bool disposing) { if (disposing && sourceStream != null) { sourceStream.Dispose(); sourceStream = null; } base.Dispose(disposing); } } public abstract class WaveStream : Stream, IWaveProvider { public abstract WaveFormat WaveFormat { get; } public override bool CanRead => true; public override bool CanSeek => true; public override bool CanWrite => false; public virtual int BlockAlign => WaveFormat.BlockAlign; public virtual TimeSpan CurrentTime { get { return TimeSpan.FromSeconds((double)Position / (double)WaveFormat.AverageBytesPerSecond); } set { Position = (long)(value.TotalSeconds * (double)WaveFormat.AverageBytesPerSecond); } } public virtual TimeSpan TotalTime => TimeSpan.FromSeconds((double)Length / (double)WaveFormat.AverageBytesPerSecond); public override void Flush() { } public override long Seek(long offset, SeekOrigin origin) { switch (origin) { case SeekOrigin.Begin: Position = offset; break; case SeekOrigin.Current: Position += offset; break; default: Position = Length + offset; break; } return Position; } public override void SetLength(long length) { throw new NotSupportedException("Can't set length of a WaveFormatString"); } public override void Write(byte[] buffer, int offset, int count) { throw new NotSupportedException("Can't write to a WaveFormatString"); } public void Skip(int seconds) { long num = Position + WaveFormat.AverageBytesPerSecond * seconds; if (num > Length) { Position = Length; } else if (num < 0) { Position = 0L; } else { Position = num; } } public virtual bool HasData(int count) { return Position < Length; } } } namespace NAudio.Wave.WaveFormats { [StructLayout(LayoutKind.Sequential, Pack = 2)] internal class WmaWaveFormat : WaveFormat { private short wValidBitsPerSample; private int dwChannelMask; private int dwReserved1; private int dwReserved2; private short wEncodeOptions; private short wReserved3; public WmaWaveFormat(int sampleRate, int bitsPerSample, int channels) : base(sampleRate, bitsPerSample, channels) { wValidBitsPerSample = (short)bitsPerSample; switch (channels) { case 1: dwChannelMask = 1; break; case 2: dwChannelMask = 3; break; } waveFormatTag = WaveFormatEncoding.WindowsMediaAudio; } } } namespace NAudio.Wave.SampleProviders { internal interface ISampleChunkConverter { bool Supports(WaveFormat format); void LoadNextChunk(IWaveProvider sourceProvider, int samplePairsRequired); bool GetNextSample(out float sampleLeft, out float sampleRight); } internal class Mono16SampleChunkConverter : ISampleChunkConverter { private int sourceSample; private byte[] sourceBuffer; private WaveBuffer sourceWaveBuffer; private int sourceSamples; public bool Supports(WaveFormat waveFormat) { if (waveFormat.Encoding == WaveFormatEncoding.Pcm && waveFormat.BitsPerSample == 16) { return waveFormat.Channels == 1; } return false; } public void LoadNextChunk(IWaveProvider source, int samplePairsRequired) { int num = samplePairsRequired * 2; sourceSample = 0; sourceBuffer = BufferHelpers.Ensure(sourceBuffer, num); sourceWaveBuffer = new WaveBuffer(sourceBuffer); sourceSamples = source.Read(sourceBuffer, 0, num) / 2; } public bool GetNextSample(out float sampleLeft, out float sampleRight) { if (sourceSample < sourceSamples) { sampleLeft = (float)sourceWaveBuffer.ShortBuffer[sourceSample++] / 32768f; sampleRight = sampleLeft; return true; } sampleLeft = 0f; sampleRight = 0f; return false; } } internal class Mono24SampleChunkConverter : ISampleChunkConverter { private int offset; private byte[] sourceBuffer; private int sourceBytes; public bool Supports(WaveFormat waveFormat) { if (waveFormat.Encoding == WaveFormatEncoding.Pcm && waveFormat.BitsPerSample == 24) { return waveFormat.Channels == 1; } return false; } public void LoadNextChunk(IWaveProvider source, int samplePairsRequired) { int num = samplePairsRequired * 3; sourceBuffer = BufferHelpers.Ensure(sourceBuffer, num); sourceBytes = source.Read(sourceBuffer, 0, num); offset = 0; } public bool GetNextSample(out float sampleLeft, out float sampleRight) { if (offset < sourceBytes) { sampleLeft = (float)(((sbyte)sourceBuffer[offset + 2] << 16) | (sourceBuffer[offset + 1] << 8) | sourceBuffer[offset]) / 8388608f; offset += 3; sampleRight = sampleLeft; return true; } sampleLeft = 0f; sampleRight = 0f; return false; } } internal class Mono8SampleChunkConverter : ISampleChunkConverter { private int offset; private byte[] sourceBuffer; private int sourceBytes; public bool Supports(WaveFormat waveFormat) { if (waveFormat.Encoding == WaveFormatEncoding.Pcm && waveFormat.BitsPerSample == 8) { return waveFormat.Channels == 1; } return false; } public void LoadNextChunk(IWaveProvider source, int samplePairsRequired) { sourceBuffer = BufferHelpers.Ensure(sourceBuffer, samplePairsRequired); sourceBytes = source.Read(sourceBuffer, 0, samplePairsRequired); offset = 0; } public bool GetNextSample(out float sampleLeft, out float sampleRight) { if (offset < sourceBytes) { sampleLeft = (float)(int)sourceBuffer[offset] / 256f; offset++; sampleRight = sampleLeft; return true; } sampleLeft = 0f; sampleRight = 0f; return false; } } internal class MonoFloatSampleChunkConverter : ISampleChunkConverter { private int sourceSample; private byte[] sourceBuffer; private WaveBuffer sourceWaveBuffer; private int sourceSamples; public bool Supports(WaveFormat waveFormat) { if (waveFormat.Encoding == WaveFormatEncoding.IeeeFloat) { return waveFormat.Channels == 1; } return false; } public void LoadNextChunk(IWaveProvider source, int samplePairsRequired) { int num = samplePairsRequired * 4; sourceBuffer = BufferHelpers.Ensure(sourceBuffer, num); sourceWaveBuffer = new WaveBuffer(sourceBuffer); sourceSamples = source.Read(sourceBuffer, 0, num) / 4; sourceSample = 0; } public bool GetNextSample(out float sampleLeft, out float sampleRight) { if (sourceSample < sourceSamples) { sampleLeft = sourceWaveBuffer.FloatBuffer[sourceSample++]; sampleRight = sampleLeft; return true; } sampleLeft = 0f; sampleRight = 0f; return false; } } internal class Stereo16SampleChunkConverter : ISampleChunkConverter { private int sourceSample; private byte[] sourceBuffer; private WaveBuffer sourceWaveBuffer; private int sourceSamples; public bool Supports(WaveFormat waveFormat) { if (waveFormat.Encoding == WaveFormatEncoding.Pcm && waveFormat.BitsPerSample == 16) { return waveFormat.Channels == 2; } return false; } public void LoadNextChunk(IWaveProvider source, int samplePairsRequired) { int num = samplePairsRequired * 4; sourceBuffer = BufferHelpers.Ensure(sourceBuffer, num); sourceWaveBuffer = new WaveBuffer(sourceBuffer); sourceSamples = source.Read(sourceBuffer, 0, num) / 2; sourceSample = 0; } public bool GetNextSample(out float sampleLeft, out float sampleRight) { if (sourceSample < sourceSamples) { sampleLeft = (float)sourceWaveBuffer.ShortBuffer[sourceSample++] / 32768f; sampleRight = (float)sourceWaveBuffer.ShortBuffer[sourceSample++] / 32768f; return true; } sampleLeft = 0f; sampleRight = 0f; return false; } } internal class Stereo24SampleChunkConverter : ISampleChunkConverter { private int offset; private byte[] sourceBuffer; private int sourceBytes; public bool Supports(WaveFormat waveFormat) { if (waveFormat.Encoding == WaveFormatEncoding.Pcm && waveFormat.BitsPerSample == 24) { return waveFormat.Channels == 2; } return false; } public void LoadNextChunk(IWaveProvider source, int samplePairsRequired) { int num = samplePairsRequired * 6; sourceBuffer = BufferHelpers.Ensure(sourceBuffer, num); sourceBytes = source.Read(sourceBuffer, 0, num); offset = 0; } public bool GetNextSample(out float sampleLeft, out float sampleRight) { if (offset < sourceBytes) { sampleLeft = (float)(((sbyte)sourceBuffer[offset + 2] << 16) | (sourceBuffer[offset + 1] << 8) | sourceBuffer[offset]) / 8388608f; offset += 3; sampleRight = (float)(((sbyte)sourceBuffer[offset + 2] << 16) | (sourceBuffer[offset + 1] << 8) | sourceBuffer[offset]) / 8388608f; offset += 3; return true; } sampleLeft = 0f; sampleRight = 0f; return false; } } internal class Stereo8SampleChunkConverter : ISampleChunkConverter { private int offset; private byte[] sourceBuffer; private int sourceBytes; public bool Supports(WaveFormat waveFormat) { if (waveFormat.Encoding == WaveFormatEncoding.Pcm && waveFormat.BitsPerSample == 8) { return waveFormat.Channels == 2; } return false; } public void LoadNextChunk(IWaveProvider source, int samplePairsRequired) { int num = samplePairsRequired * 2; sourceBuffer = BufferHelpers.Ensure(sourceBuffer, num); sourceBytes = source.Read(sourceBuffer, 0, num); offset = 0; } public bool GetNextSample(out float sampleLeft, out float sampleRight) { if (offset < sourceBytes) { sampleLeft = (float)(int)sourceBuffer[offset++] / 256f; sampleRight = (float)(int)sourceBuffer[offset++] / 256f; return true; } sampleLeft = 0f; sampleRight = 0f; return false; } } internal class StereoFloatSampleChunkConverter : ISampleChunkConverter { private int sourceSample; private byte[] sourceBuffer; private WaveBuffer sourceWaveBuffer; private int sourceSamples; public bool Supports(WaveFormat waveFormat) { if (waveFormat.Encoding == WaveFormatEncoding.IeeeFloat) { return waveFormat.Channels == 2; } return false; } public void LoadNextChunk(IWaveProvider source, int samplePairsRequired) { int num = samplePairsRequired * 8; sourceBuffer = BufferHelpers.Ensure(sourceBuffer, num); sourceWaveBuffer = new WaveBuffer(sourceBuffer); sourceSamples = source.Read(sourceBuffer, 0, num) / 4; sourceSample = 0; } public bool GetNextSample(out float sampleLeft, out float sampleRight) { if (sourceSample < sourceSamples) { sampleLeft = sourceWaveBuffer.FloatBuffer[sourceSample++]; sampleRight = sourceWaveBuffer.FloatBuffer[sourceSample++]; return true; } sampleLeft = 0f; sampleRight = 0f; return false; } } public class AdsrSampleProvider : ISampleProvider { private readonly ISampleProvider source; private readonly EnvelopeGenerator adsr; private float attackSeconds; private float releaseSeconds; public float AttackSeconds { get { return attackSeconds; } set { attackSeconds = value; adsr.AttackRate = attackSeconds * (float)WaveFormat.SampleRate; } } public float ReleaseSeconds { get { return releaseSeconds; } set { releaseSeconds = value; adsr.ReleaseRate = releaseSeconds * (float)WaveFormat.SampleRate; } } public WaveFormat WaveFormat => source.WaveFormat; public AdsrSampleProvider(ISampleProvider source) { if (source.WaveFormat.Channels > 1) { throw new ArgumentException("Currently only supports mono inputs"); } this.source = source; adsr = new EnvelopeGenerator(); AttackSeconds = 0.01f; adsr.SustainLevel = 1f; adsr.DecayRate = 0f * (float)WaveFormat.SampleRate; ReleaseSeconds = 0.3f; adsr.Gate(gate: true); } public int Read(float[] buffer, int offset, int count) { if (adsr.State == EnvelopeGenerator.EnvelopeState.Idle) { return 0; } int num = source.Read(buffer, offset, count); for (int i = 0; i < num; i++) { buffer[offset++] *= adsr.Process(); } return num; } public void Stop() { adsr.Gate(gate: false); } } public class ConcatenatingSampleProvider : ISampleProvider { private readonly ISampleProvider[] providers; private int currentProviderIndex; public WaveFormat WaveFormat => providers[0].WaveFormat; public ConcatenatingSampleProvider(IEnumerable providers) { if (providers == null) { throw new ArgumentNullException("providers"); } this.providers = providers.ToArray(); if (this.providers.Length == 0) { throw new ArgumentException("Must provide at least one input", "providers"); } if (this.providers.Any((ISampleProvider p) => p.WaveFormat.Channels != WaveFormat.Channels)) { throw new ArgumentException("All inputs must have the same channel count", "providers"); } if (this.providers.Any((ISampleProvider p) => p.WaveFormat.SampleRate != WaveFormat.SampleRate)) { throw new ArgumentException("All inputs must have the same sample rate", "providers"); } } public int Read(float[] buffer, int offset, int count) { int num = 0; while (num < count && currentProviderIndex < providers.Length) { int count2 = count - num; int num2 = providers[currentProviderIndex].Read(buffer, offset + num, count2); num += num2; if (num2 == 0) { currentProviderIndex++; } } return num; } } public class FadeInOutSampleProvider : ISampleProvider { private enum FadeState { Silence, FadingIn, FullVolume, FadingOut } private readonly object lockObject = new object(); private readonly ISampleProvider source; private int fadeSamplePosition; private int fadeSampleCount; private FadeState fadeState; public WaveFormat WaveFormat => source.WaveFormat; public FadeInOutSampleProvider(ISampleProvider source, bool initiallySilent = false) { this.source = source; fadeState = ((!initiallySilent) ? FadeState.FullVolume : FadeState.Silence); } public void BeginFadeIn(double fadeDurationInMilliseconds) { lock (lockObject) { fadeSamplePosition = 0; fadeSampleCount = (int)(fadeDurationInMilliseconds * (double)source.WaveFormat.SampleRate / 1000.0); fadeState = FadeState.FadingIn; } } public void BeginFadeOut(double fadeDurationInMilliseconds) { lock (lockObject) { fadeSamplePosition = 0; fadeSampleCount = (int)(fadeDurationInMilliseconds * (double)source.WaveFormat.SampleRate / 1000.0); fadeState = FadeState.FadingOut; } } public int Read(float[] buffer, int offset, int count) { int num = source.Read(buffer, offset, count); lock (lockObject) { if (fadeState == FadeState.FadingIn) { FadeIn(buffer, offset, num); } else if (fadeState == FadeState.FadingOut) { FadeOut(buffer, offset, num); } else if (fadeState == FadeState.Silence) { ClearBuffer(buffer, offset, count); } } return num; } private static void ClearBuffer(float[] buffer, int offset, int count) { for (int i = 0; i < count; i++) { buffer[i + offset] = 0f; } } private void FadeOut(float[] buffer, int offset, int sourceSamplesRead) { int num = 0; while (num < sourceSamplesRead) { float num2 = 1f - (float)fadeSamplePosition / (float)fadeSampleCount; for (int i = 0; i < source.WaveFormat.Channels; i++) { buffer[offset + num++] *= num2; } fadeSamplePosition++; if (fadeSamplePosition > fadeSampleCount) { fadeState = FadeState.Silence; ClearBuffer(buffer, num + offset, sourceSamplesRead - num); break; } } } private void FadeIn(float[] buffer, int offset, int sourceSamplesRead) { int num = 0; while (num < sourceSamplesRead) { float num2 = (float)fadeSamplePosition / (float)fadeSampleCount; for (int i = 0; i < source.WaveFormat.Channels; i++) { buffer[offset + num++] *= num2; } fadeSamplePosition++; if (fadeSamplePosition > fadeSampleCount) { fadeState = FadeState.FullVolume; break; } } } } public class MeteringSampleProvider : ISampleProvider { private readonly ISampleProvider source; private readonly float[] maxSamples; private int sampleCount; private readonly int channels; private readonly StreamVolumeEventArgs args; public int SamplesPerNotification { get; set; } public WaveFormat WaveFormat => source.WaveFormat; public event EventHandler StreamVolume; public MeteringSampleProvider(ISampleProvider source) : this(source, source.WaveFormat.SampleRate / 10) { } public MeteringSampleProvider(ISampleProvider source, int samplesPerNotification) { this.source = source; channels = source.WaveFormat.Channels; maxSamples = new float[channels]; SamplesPerNotification = samplesPerNotification; args = new StreamVolumeEventArgs { MaxSampleValues = maxSamples }; } public int Read(float[] buffer, int offset, int count) { int num = source.Read(buffer, offset, count); if (this.StreamVolume != null) { for (int i = 0; i < num; i += channels) { for (int j = 0; j < channels; j++) { float val = Math.Abs(buffer[offset + i + j]); maxSamples[j] = Math.Max(maxSamples[j], val); } sampleCount++; if (sampleCount >= SamplesPerNotification) { this.StreamVolume(this, args); sampleCount = 0; Array.Clear(maxSamples, 0, channels); } } } return num; } } public class StreamVolumeEventArgs : EventArgs { public float[] MaxSampleValues { get; set; } } public class MixingSampleProvider : ISampleProvider { private readonly List sources; private float[] sourceBuffer; private const int MaxInputs = 1024; public IEnumerable MixerInputs => sources; public bool ReadFully { get; set; } public WaveFormat WaveFormat { get; private set; } public event EventHandler MixerInputEnded; public MixingSampleProvider(WaveFormat waveFormat) { if (waveFormat.Encoding != WaveFormatEncoding.IeeeFloat) { throw new ArgumentException("Mixer wave format must be IEEE float"); } sources = new List(); WaveFormat = waveFormat; } public MixingSampleProvider(IEnumerable sources) { this.sources = new List(); foreach (ISampleProvider source in sources) { AddMixerInput(source); } if (this.sources.Count == 0) { throw new ArgumentException("Must provide at least one input in this constructor"); } } public void AddMixerInput(IWaveProvider mixerInput) { AddMixerInput(SampleProviderConverters.ConvertWaveProviderIntoSampleProvider(mixerInput)); } public void AddMixerInput(ISampleProvider mixerInput) { lock (sources) { if (sources.Count >= 1024) { throw new InvalidOperationException("Too many mixer inputs"); } sources.Add(mixerInput); } if (WaveFormat == null) { WaveFormat = mixerInput.WaveFormat; } else if (WaveFormat.SampleRate != mixerInput.WaveFormat.SampleRate || WaveFormat.Channels != mixerInput.WaveFormat.Channels) { throw new ArgumentException("All mixer inputs must have the same WaveFormat"); } } public void RemoveMixerInput(ISampleProvider mixerInput) { lock (sources) { sources.Remove(mixerInput); } } public void RemoveAllMixerInputs() { lock (sources) { sources.Clear(); } } public int Read(float[] buffer, int offset, int count) { int num = 0; sourceBuffer = BufferHelpers.Ensure(sourceBuffer, count); lock (sources) { for (int num2 = sources.Count - 1; num2 >= 0; num2--) { ISampleProvider sampleProvider = sources[num2]; int num3 = sampleProvider.Read(sourceBuffer, 0, count); int num4 = offset; for (int i = 0; i < num3; i++) { if (i >= num) { buffer[num4++] = sourceBuffer[i]; } else { buffer[num4++] += sourceBuffer[i]; } } num = Math.Max(num3, num); if (num3 < count) { this.MixerInputEnded?.Invoke(this, new SampleProviderEventArgs(sampleProvider)); sources.RemoveAt(num2); } } } if (ReadFully && num < count) { int num5 = offset + num; while (num5 < offset + count) { buffer[num5++] = 0f; } num = count; } return num; } } public class SampleProviderEventArgs : EventArgs { public ISampleProvider SampleProvider { get; private set; } public SampleProviderEventArgs(ISampleProvider sampleProvider) { SampleProvider = sampleProvider; } } public class MonoToStereoSampleProvider : ISampleProvider { private readonly ISampleProvider source; private float[] sourceBuffer; public WaveFormat WaveFormat { get; } public float LeftVolume { get; set; } public float RightVolume { get; set; } public MonoToStereoSampleProvider(ISampleProvider source) { LeftVolume = 1f; RightVolume = 1f; if (source.WaveFormat.Channels != 1) { throw new ArgumentException("Source must be mono"); } this.source = source; WaveFormat = WaveFormat.CreateIeeeFloatWaveFormat(source.WaveFormat.SampleRate, 2); } public int Read(float[] buffer, int offset, int count) { int count2 = count / 2; int num = offset; EnsureSourceBuffer(count2); int num2 = source.Read(sourceBuffer, 0, count2); for (int i = 0; i < num2; i++) { buffer[num++] = sourceBuffer[i] * LeftVolume; buffer[num++] = sourceBuffer[i] * RightVolume; } return num2 * 2; } private void EnsureSourceBuffer(int count) { if (sourceBuffer == null || sourceBuffer.Length < count) { sourceBuffer = new float[count]; } } } public class MultiplexingSampleProvider : ISampleProvider { private readonly IList inputs; private readonly WaveFormat waveFormat; private readonly int outputChannelCount; private readonly int inputChannelCount; private readonly List mappings; private float[] inputBuffer; public WaveFormat WaveFormat => waveFormat; public int InputChannelCount => inputChannelCount; public int OutputChannelCount => outputChannelCount; public MultiplexingSampleProvider(IEnumerable inputs, int numberOfOutputChannels) { this.inputs = new List(inputs); outputChannelCount = numberOfOutputChannels; if (this.inputs.Count == 0) { throw new ArgumentException("You must provide at least one input"); } if (numberOfOutputChannels < 1) { throw new ArgumentException("You must provide at least one output"); } foreach (ISampleProvider input in this.inputs) { if (waveFormat == null) { if (input.WaveFormat.Encoding != WaveFormatEncoding.IeeeFloat) { throw new ArgumentException("Only 32 bit float is supported"); } waveFormat = WaveFormat.CreateIeeeFloatWaveFormat(input.WaveFormat.SampleRate, numberOfOutputChannels); } else { if (input.WaveFormat.BitsPerSample != waveFormat.BitsPerSample) { throw new ArgumentException("All inputs must have the same bit depth"); } if (input.WaveFormat.SampleRate != waveFormat.SampleRate) { throw new ArgumentException("All inputs must have the same sample rate"); } } inputChannelCount += input.WaveFormat.Channels; } mappings = new List(); for (int i = 0; i < outputChannelCount; i++) { mappings.Add(i % inputChannelCount); } } public int Read(float[] buffer, int offset, int count) { int num = count / outputChannelCount; int num2 = 0; int num3 = 0; foreach (ISampleProvider input in inputs) { int num4 = num * input.WaveFormat.Channels; inputBuffer = BufferHelpers.Ensure(inputBuffer, num4); int num5 = input.Read(inputBuffer, 0, num4); num3 = Math.Max(num3, num5 / input.WaveFormat.Channels); for (int i = 0; i < input.WaveFormat.Channels; i++) { int num6 = num2 + i; for (int j = 0; j < outputChannelCount; j++) { if (mappings[j] != num6) { continue; } int num7 = i; int num8 = offset + j; int k; for (k = 0; k < num; k++) { if (num7 >= num5) { break; } buffer[num8] = inputBuffer[num7]; num8 += outputChannelCount; num7 += input.WaveFormat.Channels; } for (; k < num; k++) { buffer[num8] = 0f; num8 += outputChannelCount; } } } num2 += input.WaveFormat.Channels; } return num3 * outputChannelCount; } public void ConnectInputToOutput(int inputChannel, int outputChannel) { if (inputChannel < 0 || inputChannel >= InputChannelCount) { throw new ArgumentException("Invalid input channel"); } if (outputChannel < 0 || outputChannel >= OutputChannelCount) { throw new ArgumentException("Invalid output channel"); } mappings[outputChannel] = inputChannel; } } public class NotifyingSampleProvider : ISampleProvider, ISampleNotifier { private readonly ISampleProvider source; private readonly SampleEventArgs sampleArgs = new SampleEventArgs(0f, 0f); private readonly int channels; public WaveFormat WaveFormat => source.WaveFormat; public event EventHandler Sample; public NotifyingSampleProvider(ISampleProvider source) { this.source = source; channels = WaveFormat.Channels; } public int Read(float[] buffer, int offset, int sampleCount) { int num = source.Read(buffer, offset, sampleCount); if (this.Sample != null) { for (int i = 0; i < num; i += channels) { sampleArgs.Left = buffer[offset + i]; sampleArgs.Right = ((channels > 1) ? buffer[offset + i + 1] : sampleArgs.Left); this.Sample(this, sampleArgs); } } return num; } } public class OffsetSampleProvider : ISampleProvider { private readonly ISampleProvider sourceProvider; private int phase; private int phasePos; private int delayBySamples; private int skipOverSamples; private int takeSamples; private int leadOutSamples; public int DelayBySamples { get { return delayBySamples; } set { if (phase != 0) { throw new InvalidOperationException("Can't set DelayBySamples after calling Read"); } if (value % WaveFormat.Channels != 0) { throw new ArgumentException("DelayBySamples must be a multiple of WaveFormat.Channels"); } delayBySamples = value; } } public TimeSpan DelayBy { get { return SamplesToTimeSpan(delayBySamples); } set { delayBySamples = TimeSpanToSamples(value); } } public int SkipOverSamples { get { return skipOverSamples; } set { if (phase != 0) { throw new InvalidOperationException("Can't set SkipOverSamples after calling Read"); } if (value % WaveFormat.Channels != 0) { throw new ArgumentException("SkipOverSamples must be a multiple of WaveFormat.Channels"); } skipOverSamples = value; } } public TimeSpan SkipOver { get { return SamplesToTimeSpan(skipOverSamples); } set { skipOverSamples = TimeSpanToSamples(value); } } public int TakeSamples { get { return takeSamples; } set { if (phase != 0) { throw new InvalidOperationException("Can't set TakeSamples after calling Read"); } if (value % WaveFormat.Channels != 0) { throw new ArgumentException("TakeSamples must be a multiple of WaveFormat.Channels"); } takeSamples = value; } } public TimeSpan Take { get { return SamplesToTimeSpan(takeSamples); } set { takeSamples = TimeSpanToSamples(value); } } public int LeadOutSamples { get { return leadOutSamples; } set { if (phase != 0) { throw new InvalidOperationException("Can't set LeadOutSamples after calling Read"); } if (value % WaveFormat.Channels != 0) { throw new ArgumentException("LeadOutSamples must be a multiple of WaveFormat.Channels"); } leadOutSamples = value; } } public TimeSpan LeadOut { get { return SamplesToTimeSpan(leadOutSamples); } set { leadOutSamples = TimeSpanToSamples(value); } } public WaveFormat WaveFormat => sourceProvider.WaveFormat; private int TimeSpanToSamples(TimeSpan time) { return (int)(time.TotalSeconds * (double)WaveFormat.SampleRate) * WaveFormat.Channels; } private TimeSpan SamplesToTimeSpan(int samples) { return TimeSpan.FromSeconds((double)(samples / WaveFormat.Channels) / (double)WaveFormat.SampleRate); } public OffsetSampleProvider(ISampleProvider sourceProvider) { this.sourceProvider = sourceProvider; } public int Read(float[] buffer, int offset, int count) { int num = 0; if (phase == 0) { phase++; } if (phase == 1) { int num2 = Math.Min(count, DelayBySamples - phasePos); for (int i = 0; i < num2; i++) { buffer[offset + i] = 0f; } phasePos += num2; num += num2; if (phasePos >= DelayBySamples) { phase++; phasePos = 0; } } if (phase == 2) { if (SkipOverSamples > 0) { float[] array = new float[WaveFormat.SampleRate * WaveFormat.Channels]; int num3; for (int j = 0; j < SkipOverSamples; j += num3) { int count2 = Math.Min(SkipOverSamples - j, array.Length); num3 = sourceProvider.Read(array, 0, count2); if (num3 == 0) { break; } } } phase++; phasePos = 0; } if (phase == 3) { int num4 = count - num; if (takeSamples != 0) { num4 = Math.Min(num4, takeSamples - phasePos); } int num5 = sourceProvider.Read(buffer, offset + num, num4); phasePos += num5; num += num5; if (num5 < num4 || (takeSamples > 0 && phasePos >= takeSamples)) { phase++; phasePos = 0; } } if (phase == 4) { int num6 = Math.Min(count - num, LeadOutSamples - phasePos); for (int k = 0; k < num6; k++) { buffer[offset + num + k] = 0f; } phasePos += num6; num += num6; if (phasePos >= LeadOutSamples) { phase++; phasePos = 0; } } return num; } } public class PanningSampleProvider : ISampleProvider { private readonly ISampleProvider source; private float pan; private float leftMultiplier; private float rightMultiplier; private readonly WaveFormat waveFormat; private float[] sourceBuffer; private IPanStrategy panStrategy; public float Pan { get { return pan; } set { if (value < -1f || value > 1f) { throw new ArgumentOutOfRangeException("value", "Pan must be in the range -1 to 1"); } pan = value; UpdateMultipliers(); } } public IPanStrategy PanStrategy { get { return panStrategy; } set { panStrategy = value; UpdateMultipliers(); } } public WaveFormat WaveFormat => waveFormat; public PanningSampleProvider(ISampleProvider source) { if (source.WaveFormat.Channels != 1) { throw new ArgumentException("Source sample provider must be mono"); } this.source = source; waveFormat = WaveFormat.CreateIeeeFloatWaveFormat(source.WaveFormat.SampleRate, 2); panStrategy = new SinPanStrategy(); } private void UpdateMultipliers() { StereoSamplePair multipliers = panStrategy.GetMultipliers(Pan); leftMultiplier = multipliers.Left; rightMultiplier = multipliers.Right; } public int Read(float[] buffer, int offset, int count) { int num = count / 2; sourceBuffer = BufferHelpers.Ensure(sourceBuffer, num); int num2 = source.Read(sourceBuffer, 0, num); int num3 = offset; for (int i = 0; i < num2; i++) { buffer[num3++] = leftMultiplier * sourceBuffer[i]; buffer[num3++] = rightMultiplier * sourceBuffer[i]; } return num2 * 2; } } public struct StereoSamplePair { public float Left { get; set; } public float Right { get; set; } } public interface IPanStrategy { StereoSamplePair GetMultipliers(float pan); } public class StereoBalanceStrategy : IPanStrategy { public StereoSamplePair GetMultipliers(float pan) { float left = ((pan <= 0f) ? 1f : ((1f - pan) / 2f)); float right = ((pan >= 0f) ? 1f : ((pan + 1f) / 2f)); StereoSamplePair result = default(StereoSamplePair); result.Left = left; result.Right = right; return result; } } public class SquareRootPanStrategy : IPanStrategy { public StereoSamplePair GetMultipliers(float pan) { float num = (0f - pan + 1f) / 2f; float left = (float)Math.Sqrt(num); float right = (float)Math.Sqrt(1f - num); StereoSamplePair result = default(StereoSamplePair); result.Left = left; result.Right = right; return result; } } public class SinPanStrategy : IPanStrategy { private const float HalfPi = MathF.PI / 2f; public StereoSamplePair GetMultipliers(float pan) { float num = (0f - pan + 1f) / 2f; float left = (float)Math.Sin(num * (MathF.PI / 2f)); float right = (float)Math.Cos(num * (MathF.PI / 2f)); StereoSamplePair result = default(StereoSamplePair); result.Left = left; result.Right = right; return result; } } public class LinearPanStrategy : IPanStrategy { public StereoSamplePair GetMultipliers(float pan) { float num = (0f - pan + 1f) / 2f; float left = num; float right = 1f - num; StereoSamplePair result = default(StereoSamplePair); result.Left = left; result.Right = right; return result; } } public class Pcm16BitToSampleProvider : SampleProviderConverterBase { public Pcm16BitToSampleProvider(IWaveProvider source) : base(source) { } public override int Read(float[] buffer, int offset, int count) { int num = count * 2; EnsureSourceBuffer(num); int num2 = source.Read(sourceBuffer, 0, num); int num3 = offset; for (int i = 0; i < num2; i += 2) { buffer[num3++] = (float)BitConverter.ToInt16(sourceBuffer, i) / 32768f; } return num2 / 2; } } public class Pcm24BitToSampleProvider : SampleProviderConverterBase { public Pcm24BitToSampleProvider(IWaveProvider source) : base(source) { } public override int Read(float[] buffer, int offset, int count) { int num = count * 3; EnsureSourceBuffer(num); int num2 = source.Read(sourceBuffer, 0, num); int num3 = offset; for (int i = 0; i < num2; i += 3) { buffer[num3++] = (float)(((sbyte)sourceBuffer[i + 2] << 16) | (sourceBuffer[i + 1] << 8) | sourceBuffer[i]) / 8388608f; } return num2 / 3; } } public class Pcm32BitToSampleProvider : SampleProviderConverterBase { public Pcm32BitToSampleProvider(IWaveProvider source) : base(source) { } public override int Read(float[] buffer, int offset, int count) { int num = count * 4; EnsureSourceBuffer(num); int num2 = source.Read(sourceBuffer, 0, num); int num3 = offset; for (int i = 0; i < num2; i += 4) { buffer[num3++] = (float)(((sbyte)sourceBuffer[i + 3] << 24) | (sourceBuffer[i + 2] << 16) | (sourceBuffer[i + 1] << 8) | sourceBuffer[i]) / 2.1474836E+09f; } return num2 / 4; } } public class Pcm8BitToSampleProvider : SampleProviderConverterBase { public Pcm8BitToSampleProvider(IWaveProvider source) : base(source) { } public override int Read(float[] buffer, int offset, int count) { EnsureSourceBuffer(count); int num = source.Read(sourceBuffer, 0, count); int num2 = offset; for (int i = 0; i < num; i++) { buffer[num2++] = (float)(int)sourceBuffer[i] / 128f - 1f; } return num; } } public class SampleChannel : ISampleProvider { private readonly VolumeSampleProvider volumeProvider; private readonly MeteringSampleProvider preVolumeMeter; private readonly WaveFormat waveFormat; public WaveFormat WaveFormat => waveFormat; public float Volume { get { return volumeProvider.Volume; } set { volumeProvider.Volume = value; } } public event EventHandler PreVolumeMeter { add { preVolumeMeter.StreamVolume += value; } remove { preVolumeMeter.StreamVolume -= value; } } public SampleChannel(IWaveProvider waveProvider) : this(waveProvider, forceStereo: false) { } public SampleChannel(IWaveProvider waveProvider, bool forceStereo) { ISampleProvider sampleProvider = SampleProviderConverters.ConvertWaveProviderIntoSampleProvider(waveProvider); if (sampleProvider.WaveFormat.Channels == 1 && forceStereo) { sampleProvider = new MonoToStereoSampleProvider(sampleProvider); } waveFormat = sampleProvider.WaveFormat; preVolumeMeter = new MeteringSampleProvider(sampleProvider); volumeProvider = new VolumeSampleProvider(preVolumeMeter); } public int Read(float[] buffer, int offset, int sampleCount) { return volumeProvider.Read(buffer, offset, sampleCount); } } public abstract class SampleProviderConverterBase : ISampleProvider { protected IWaveProvider source; private readonly WaveFormat waveFormat; protected byte[] sourceBuffer; public WaveFormat WaveFormat => waveFormat; public SampleProviderConverterBase(IWaveProvider source) { this.source = source; waveFormat = WaveFormat.CreateIeeeFloatWaveFormat(source.WaveFormat.SampleRate, source.WaveFormat.Channels); } public abstract int Read(float[] buffer, int offset, int count); protected void EnsureSourceBuffer(int sourceBytesRequired) { sourceBuffer = BufferHelpers.Ensure(sourceBuffer, sourceBytesRequired); } } internal static class SampleProviderConverters { public static ISampleProvider ConvertWaveProviderIntoSampleProvider(IWaveProvider waveProvider) { if (waveProvider.WaveFormat.Encoding == WaveFormatEncoding.Pcm) { if (waveProvider.WaveFormat.BitsPerSample == 8) { return new Pcm8BitToSampleProvider(waveProvider); } if (waveProvider.WaveFormat.BitsPerSample == 16) { return new Pcm16BitToSampleProvider(waveProvider); } if (waveProvider.WaveFormat.BitsPerSample == 24) { return new Pcm24BitToSampleProvider(waveProvider); } if (waveProvider.WaveFormat.BitsPerSample == 32) { return new Pcm32BitToSampleProvider(waveProvider); } throw new InvalidOperationException("Unsupported bit depth"); } if (waveProvider.WaveFormat.Encoding == WaveFormatEncoding.IeeeFloat) { if (waveProvider.WaveFormat.BitsPerSample == 64) { return new WaveToSampleProvider64(waveProvider); } return new WaveToSampleProvider(waveProvider); } throw new ArgumentException("Unsupported source encoding"); } } public class SampleToWaveProvider : IWaveProvider { private readonly ISampleProvider source; public WaveFormat WaveFormat => source.WaveFormat; public SampleToWaveProvider(ISampleProvider source) { if (source.WaveFormat.Encoding != WaveFormatEncoding.IeeeFloat) { throw new ArgumentException("Must be already floating point"); } this.source = source; } public int Read(byte[] buffer, int offset, int count) { int count2 = count / 4; WaveBuffer waveBuffer = new WaveBuffer(buffer); return source.Read(waveBuffer.FloatBuffer, offset / 4, count2) * 4; } } public class SampleToWaveProvider16 : IWaveProvider { private readonly ISampleProvider sourceProvider; private readonly WaveFormat waveFormat; private volatile float volume; private float[] sourceBuffer; public WaveFormat WaveFormat => waveFormat; public float Volume { get { return volume; } set { volume = value; } } public SampleToWaveProvider16(ISampleProvider sourceProvider) { if (sourceProvider.WaveFormat.Encoding != WaveFormatEncoding.IeeeFloat) { throw new ArgumentException("Input source provider must be IEEE float", "sourceProvider"); } if (sourceProvider.WaveFormat.BitsPerSample != 32) { throw new ArgumentException("Input source provider must be 32 bit", "sourceProvider"); } waveFormat = new WaveFormat(sourceProvider.WaveFormat.SampleRate, 16, sourceProvider.WaveFormat.Channels); this.sourceProvider = sourceProvider; volume = 1f; } public int Read(byte[] destBuffer, int offset, int numBytes) { int num = numBytes / 2; sourceBuffer = BufferHelpers.Ensure(sourceBuffer, num); int num2 = sourceProvider.Read(sourceBuffer, 0, num); WaveBuffer waveBuffer = new WaveBuffer(destBuffer); int num3 = offset / 2; for (int i = 0; i < num2; i++) { float num4 = sourceBuffer[i] * volume; if (num4 > 1f) { num4 = 1f; } if (num4 < -1f) { num4 = -1f; } waveBuffer.ShortBuffer[num3++] = (short)(num4 * 32767f); } return num2 * 2; } } public class SampleToWaveProvider24 : IWaveProvider { private readonly ISampleProvider sourceProvider; private readonly WaveFormat waveFormat; private volatile float volume; private float[] sourceBuffer; public WaveFormat WaveFormat => waveFormat; public float Volume { get { return volume; } set { volume = value; } } public SampleToWaveProvider24(ISampleProvider sourceProvider) { if (sourceProvider.WaveFormat.Encoding != WaveFormatEncoding.IeeeFloat) { throw new ArgumentException("Input source provider must be IEEE float", "sourceProvider"); } if (sourceProvider.WaveFormat.BitsPerSample != 32) { throw new ArgumentException("Input source provider must be 32 bit", "sourceProvider"); } waveFormat = new WaveFormat(sourceProvider.WaveFormat.SampleRate, 24, sourceProvider.WaveFormat.Channels); this.sourceProvider = sourceProvider; volume = 1f; } public int Read(byte[] destBuffer, int offset, int numBytes) { int num = numBytes / 3; sourceBuffer = BufferHelpers.Ensure(sourceBuffer, num); int num2 = sourceProvider.Read(sourceBuffer, 0, num); int num3 = offset; for (int i = 0; i < num2; i++) { float num4 = sourceBuffer[i] * volume; if (num4 > 1f) { num4 = 1f; } if (num4 < -1f) { num4 = -1f; } int num5 = (int)((double)num4 * 8388607.0); destBuffer[num3++] = (byte)num5; destBuffer[num3++] = (byte)(num5 >> 8); destBuffer[num3++] = (byte)(num5 >> 16); } return num2 * 3; } } public class SignalGenerator : ISampleProvider { private readonly WaveFormat waveFormat; private readonly Random random = new Random(); private readonly double[] pinkNoiseBuffer = new double[7]; private const double TwoPi = Math.PI * 2.0; private int nSample; private double phi; public WaveFormat WaveFormat => waveFormat; public double Frequency { get; set; } public double FrequencyLog => Math.Log(Frequency); public double FrequencyEnd { get; set; } public double FrequencyEndLog => Math.Log(FrequencyEnd); public double Gain { get; set; } public bool[] PhaseReverse { get; } public SignalGeneratorType Type { get; set; } public double SweepLengthSecs { get; set; } public SignalGenerator() : this(44100, 2) { } public SignalGenerator(int sampleRate, int channel) { phi = 0.0; waveFormat = WaveFormat.CreateIeeeFloatWaveFormat(sampleRate, channel); Type = SignalGeneratorType.Sin; Frequency = 440.0; Gain = 1.0; PhaseReverse = new bool[channel]; SweepLengthSecs = 2.0; } public int Read(float[] buffer, int offset, int count) { int num = offset; for (int i = 0; i < count / waveFormat.Channels; i++) { double num2; switch (Type) { case SignalGeneratorType.Sin: { double num4 = Math.PI * 2.0 * Frequency / (double)waveFormat.SampleRate; num2 = Gain * Math.Sin((double)nSample * num4); nSample++; break; } case SignalGeneratorType.Square: { double num4 = 2.0 * Frequency / (double)waveFormat.SampleRate; double num7 = (double)nSample * num4 % 2.0 - 1.0; num2 = ((num7 >= 0.0) ? Gain : (0.0 - Gain)); nSample++; break; } case SignalGeneratorType.Triangle: { double num4 = 2.0 * Frequency / (double)waveFormat.SampleRate; double num7 = (double)nSample * num4 % 2.0; num2 = 2.0 * num7; if (num2 > 1.0) { num2 = 2.0 - num2; } if (num2 < -1.0) { num2 = -2.0 - num2; } num2 *= Gain; nSample++; break; } case SignalGeneratorType.SawTooth: { double num4 = 2.0 * Frequency / (double)waveFormat.SampleRate; double num7 = (double)nSample * num4 % 2.0 - 1.0; num2 = Gain * num7; nSample++; break; } case SignalGeneratorType.White: num2 = Gain * NextRandomTwo(); break; case SignalGeneratorType.Pink: { double num5 = NextRandomTwo(); pinkNoiseBuffer[0] = 0.99886 * pinkNoiseBuffer[0] + num5 * 0.0555179; pinkNoiseBuffer[1] = 0.99332 * pinkNoiseBuffer[1] + num5 * 0.0750759; pinkNoiseBuffer[2] = 0.969 * pinkNoiseBuffer[2] + num5 * 0.153852; pinkNoiseBuffer[3] = 0.8665 * pinkNoiseBuffer[3] + num5 * 0.3104856; pinkNoiseBuffer[4] = 0.55 * pinkNoiseBuffer[4] + num5 * 0.5329522; pinkNoiseBuffer[5] = -0.7616 * pinkNoiseBuffer[5] - num5 * 0.016898; double num6 = pinkNoiseBuffer[0] + pinkNoiseBuffer[1] + pinkNoiseBuffer[2] + pinkNoiseBuffer[3] + pinkNoiseBuffer[4] + pinkNoiseBuffer[5] + pinkNoiseBuffer[6] + num5 * 0.5362; pinkNoiseBuffer[6] = num5 * 0.115926; num2 = Gain * (num6 / 5.0); break; } case SignalGeneratorType.Sweep: { double num3 = Math.Exp(FrequencyLog + (double)nSample * (FrequencyEndLog - FrequencyLog) / (SweepLengthSecs * (double)waveFormat.SampleRate)); double num4 = Math.PI * 2.0 * num3 / (double)waveFormat.SampleRate; phi += num4; num2 = Gain * Math.Sin(phi); nSample++; if ((double)nSample > SweepLengthSecs * (double)waveFormat.SampleRate) { nSample = 0; phi = 0.0; } break; } default: num2 = 0.0; break; } for (int j = 0; j < waveFormat.Channels; j++) { if (PhaseReverse[j]) { buffer[num++] = (float)(0.0 - num2); } else { buffer[num++] = (float)num2; } } } return count; } private double NextRandomTwo() { return 2.0 * random.NextDouble() - 1.0; } } public enum SignalGeneratorType { Pink, White, Sweep, Sin, Square, Triangle, SawTooth } public class SmbPitchShiftingSampleProvider : ISampleProvider { private readonly ISampleProvider sourceStream; private readonly WaveFormat waveFormat; private float pitch = 1f; private readonly int fftSize; private readonly long osamp; private readonly SmbPitchShifter shifterLeft = new SmbPitchShifter(); private readonly SmbPitchShifter shifterRight = new SmbPitchShifter(); private const float LIM_THRESH = 0.95f; private const float LIM_RANGE = 0.050000012f; private const float M_PI_2 = MathF.PI / 2f; public WaveFormat WaveFormat => waveFormat; public float PitchFactor { get { return pitch; } set { pitch = value; } } public SmbPitchShiftingSampleProvider(ISampleProvider sourceProvider) : this(sourceProvider, 4096, 4L, 1f) { } public SmbPitchShiftingSampleProvider(ISampleProvider sourceProvider, int fftSize, long osamp, float initialPitch) { sourceStream = sourceProvider; waveFormat = sourceProvider.WaveFormat; this.fftSize = fftSize; this.osamp = osamp; PitchFactor = initialPitch; } public int Read(float[] buffer, int offset, int count) { int num = sourceStream.Read(buffer, offset, count); if (pitch == 1f) { return num; } if (waveFormat.Channels == 1) { float[] array = new float[num]; int num2 = 0; for (int i = offset; i <= num + offset - 1; i++) { array[num2] = buffer[i]; num2++; } shifterLeft.PitchShift(pitch, num, fftSize, osamp, waveFormat.SampleRate, array); num2 = 0; for (int j = offset; j <= num + offset - 1; j++) { buffer[j] = Limiter(array[num2]); num2++; } return num; } if (waveFormat.Channels == 2) { float[] array2 = new float[num >> 1]; float[] array3 = new float[num >> 1]; int num3 = 0; for (int k = offset; k <= num + offset - 1; k += 2) { array2[num3] = buffer[k]; array3[num3] = buffer[k + 1]; num3++; } shifterLeft.PitchShift(pitch, num >> 1, fftSize, osamp, waveFormat.SampleRate, array2); shifterRight.PitchShift(pitch, num >> 1, fftSize, osamp, waveFormat.SampleRate, array3); num3 = 0; for (int l = offset; l <= num + offset - 1; l += 2) { buffer[l] = Limiter(array2[num3]); buffer[l + 1] = Limiter(array3[num3]); num3++; } return num; } throw new Exception("Shifting of more than 2 channels is currently not supported."); } private float Limiter(float sample) { if (0.95f < sample) { float num = (sample - 0.95f) / 0.050000012f; return (float)(Math.Atan(num) / 1.5707963705062866 * 0.050000011920928955 + 0.949999988079071); } if (sample < -0.95f) { float num = (0f - (sample + 0.95f)) / 0.050000012f; return 0f - (float)(Math.Atan(num) / 1.5707963705062866 * 0.050000011920928955 + 0.949999988079071); } return sample; } } public class StereoToMonoSampleProvider : ISampleProvider { private readonly ISampleProvider sourceProvider; private float[] sourceBuffer; public float LeftVolume { get; set; } public float RightVolume { get; set; } public WaveFormat WaveFormat { get; } public StereoToMonoSampleProvider(ISampleProvider sourceProvider) { LeftVolume = 0.5f; RightVolume = 0.5f; if (sourceProvider.WaveFormat.Channels != 2) { throw new ArgumentException("Source must be stereo"); } this.sourceProvider = sourceProvider; WaveFormat = WaveFormat.CreateIeeeFloatWaveFormat(sourceProvider.WaveFormat.SampleRate, 1); } public int Read(float[] buffer, int offset, int count) { int num = count * 2; if (sourceBuffer == null || sourceBuffer.Length < num) { sourceBuffer = new float[num]; } int num2 = sourceProvider.Read(sourceBuffer, 0, num); int num3 = offset; for (int i = 0; i < num2; i += 2) { float num4 = sourceBuffer[i]; float num5 = sourceBuffer[i + 1]; float num6 = num4 * LeftVolume + num5 * RightVolume; buffer[num3++] = num6; } return num2 / 2; } } public class VolumeSampleProvider : ISampleProvider { private readonly ISampleProvider source; public WaveFormat WaveFormat => source.WaveFormat; public float Volume { get; set; } public VolumeSampleProvider(ISampleProvider source) { this.source = source; Volume = 1f; } public int Read(float[] buffer, int offset, int sampleCount) { int result = source.Read(buffer, offset, sampleCount); if (Volume != 1f) { for (int i = 0; i < sampleCount; i++) { buffer[offset + i] *= Volume; } } return result; } } public class WaveToSampleProvider : SampleProviderConverterBase { public WaveToSampleProvider(IWaveProvider source) : base(source) { if (source.WaveFormat.Encoding != WaveFormatEncoding.IeeeFloat) { throw new ArgumentException("Must be already floating point"); } } public unsafe override int Read(float[] buffer, int offset, int count) { int num = count * 4; EnsureSourceBuffer(num); int num2 = source.Read(sourceBuffer, 0, num); int result = num2 / 4; int num3 = offset; fixed (byte* ptr = &sourceBuffer[0]) { float* ptr2 = (float*)ptr; int num4 = 0; int num5 = 0; while (num4 < num2) { buffer[num3++] = ptr2[num5]; num4 += 4; num5++; } } return result; } } public class WaveToSampleProvider64 : SampleProviderConverterBase { public WaveToSampleProvider64(IWaveProvider source) : base(source) { if (source.WaveFormat.Encoding != WaveFormatEncoding.IeeeFloat) { throw new ArgumentException("Must be already floating point"); } } public override int Read(float[] buffer, int offset, int count) { int num = count * 8; EnsureSourceBuffer(num); int num2 = source.Read(sourceBuffer, 0, num); int result = num2 / 8; int num3 = offset; for (int i = 0; i < num2; i += 8) { long value = BitConverter.ToInt64(sourceBuffer, i); buffer[num3++] = (float)BitConverter.Int64BitsToDouble(value); } return result; } } public class WdlResamplingSampleProvider : ISampleProvider { private readonly WdlResampler resampler; private readonly WaveFormat outFormat; private readonly ISampleProvider source; private readonly int channels; public WaveFormat WaveFormat => outFormat; public WdlResamplingSampleProvider(ISampleProvider source, int newSampleRate) { channels = source.WaveFormat.Channels; outFormat = WaveFormat.CreateIeeeFloatWaveFormat(newSampleRate, channels); this.source = source; resampler = new WdlResampler(); resampler.SetMode(interp: true, 2, sinc: false); resampler.SetFilterParms(); resampler.SetFeedMode(wantInputDriven: false); resampler.SetRates(source.WaveFormat.SampleRate, newSampleRate); } public int Read(float[] buffer, int offset, int count) { int num = count / channels; float[] inbuffer; int inbufferOffset; int num2 = resampler.ResamplePrepare(num, outFormat.Channels, out inbuffer, out inbufferOffset); int nsamples_in = source.Read(inbuffer, inbufferOffset, num2 * channels) / channels; return resampler.ResampleOut(buffer, offset, nsamples_in, num, channels) * channels; } } } namespace NAudio.Dsp { public class BiQuadFilter { private double a0; private double a1; private double a2; private double a3; private double a4; private float x1; private float x2; private float y1; private float y2; public float Transform(float inSample) { double num = a0 * (double)inSample + a1 * (double)x1 + a2 * (double)x2 - a3 * (double)y1 - a4 * (double)y2; x2 = x1; x1 = inSample; y2 = y1; y1 = (float)num; return y1; } private void SetCoefficients(double aa0, double aa1, double aa2, double b0, double b1, double b2) { a0 = b0 / aa0; a1 = b1 / aa0; a2 = b2 / aa0; a3 = aa1 / aa0; a4 = aa2 / aa0; } public void SetLowPassFilter(float sampleRate, float cutoffFrequency, float q) { double num = Math.PI * 2.0 * (double)cutoffFrequency / (double)sampleRate; double num2 = Math.Cos(num); double num3 = Math.Sin(num) / (double)(2f * q); double b = (1.0 - num2) / 2.0; double b2 = 1.0 - num2; double b3 = (1.0 - num2) / 2.0; double aa = 1.0 + num3; double aa2 = -2.0 * num2; double aa3 = 1.0 - num3; SetCoefficients(aa, aa2, aa3, b, b2, b3); } public void SetPeakingEq(float sampleRate, float centreFrequency, float q, float dbGain) { double num = Math.PI * 2.0 * (double)centreFrequency / (double)sampleRate; double num2 = Math.Cos(num); double num3 = Math.Sin(num) / (double)(2f * q); double num4 = Math.Pow(10.0, dbGain / 40f); double b = 1.0 + num3 * num4; double b2 = -2.0 * num2; double b3 = 1.0 - num3 * num4; double aa = 1.0 + num3 / num4; double aa2 = -2.0 * num2; double aa3 = 1.0 - num3 / num4; SetCoefficients(aa, aa2, aa3, b, b2, b3); } public void SetHighPassFilter(float sampleRate, float cutoffFrequency, float q) { double num = Math.PI * 2.0 * (double)cutoffFrequency / (double)sampleRate; double num2 = Math.Cos(num); double num3 = Math.Sin(num) / (double)(2f * q); double b = (1.0 + num2) / 2.0; double b2 = 0.0 - (1.0 + num2); double b3 = (1.0 + num2) / 2.0; double aa = 1.0 + num3; double aa2 = -2.0 * num2; double aa3 = 1.0 - num3; SetCoefficients(aa, aa2, aa3, b, b2, b3); } public static BiQuadFilter LowPassFilter(float sampleRate, float cutoffFrequency, float q) { BiQuadFilter biQuadFilter = new BiQuadFilter(); biQuadFilter.SetLowPassFilter(sampleRate, cutoffFrequency, q); return biQuadFilter; } public static BiQuadFilter HighPassFilter(float sampleRate, float cutoffFrequency, float q) { BiQuadFilter biQuadFilter = new BiQuadFilter(); biQuadFilter.SetHighPassFilter(sampleRate, cutoffFrequency, q); return biQuadFilter; } public static BiQuadFilter BandPassFilterConstantSkirtGain(float sampleRate, float centreFrequency, float q) { double num = Math.PI * 2.0 * (double)centreFrequency / (double)sampleRate; double num2 = Math.Cos(num); double num3 = Math.Sin(num); double num4 = num3 / (double)(2f * q); double b = num3 / 2.0; int num5 = 0; double b2 = (0.0 - num3) / 2.0; double num6 = 1.0 + num4; double num7 = -2.0 * num2; double num8 = 1.0 - num4; return new BiQuadFilter(num6, num7, num8, b, num5, b2); } public static BiQuadFilter BandPassFilterConstantPeakGain(float sampleRate, float centreFrequency, float q) { double num = Math.PI * 2.0 * (double)centreFrequency / (double)sampleRate; double num2 = Math.Cos(num); double num3 = Math.Sin(num) / (double)(2f * q); double b = num3; int num4 = 0; double b2 = 0.0 - num3; double num5 = 1.0 + num3; double num6 = -2.0 * num2; double num7 = 1.0 - num3; return new BiQuadFilter(num5, num6, num7, b, num4, b2); } public static BiQuadFilter NotchFilter(float sampleRate, float centreFrequency, float q) { double num = Math.PI * 2.0 * (double)centreFrequency / (double)sampleRate; double num2 = Math.Cos(num); double num3 = Math.Sin(num) / (double)(2f * q); int num4 = 1; double b = -2.0 * num2; int num5 = 1; double num6 = 1.0 + num3; double num7 = -2.0 * num2; double num8 = 1.0 - num3; return new BiQuadFilter(num6, num7, num8, num4, b, num5); } public static BiQuadFilter AllPassFilter(float sampleRate, float centreFrequency, float q) { double num = Math.PI * 2.0 * (double)centreFrequency / (double)sampleRate; double num2 = Math.Cos(num); double num3 = Math.Sin(num) / (double)(2f * q); double b = 1.0 - num3; double b2 = -2.0 * num2; double b3 = 1.0 + num3; double num4 = 1.0 + num3; double num5 = -2.0 * num2; double num6 = 1.0 - num3; return new BiQuadFilter(num4, num5, num6, b, b2, b3); } public static BiQuadFilter PeakingEQ(float sampleRate, float centreFrequency, float q, float dbGain) { BiQuadFilter biQuadFilter = new BiQuadFilter(); biQuadFilter.SetPeakingEq(sampleRate, centreFrequency, q, dbGain); return biQuadFilter; } public static BiQuadFilter LowShelf(float sampleRate, float cutoffFrequency, float shelfSlope, float dbGain) { double num = Math.PI * 2.0 * (double)cutoffFrequency / (double)sampleRate; double num2 = Math.Cos(num); double num3 = Math.Sin(num); double num4 = Math.Pow(10.0, dbGain / 40f); double num5 = num3 / 2.0 * Math.Sqrt((num4 + 1.0 / num4) * (double)(1f / shelfSlope - 1f) + 2.0); double num6 = 2.0 * Math.Sqrt(num4) * num5; double b = num4 * (num4 + 1.0 - (num4 - 1.0) * num2 + num6); double b2 = 2.0 * num4 * (num4 - 1.0 - (num4 + 1.0) * num2); double b3 = num4 * (num4 + 1.0 - (num4 - 1.0) * num2 - num6); double num7 = num4 + 1.0 + (num4 - 1.0) * num2 + num6; double num8 = -2.0 * (num4 - 1.0 + (num4 + 1.0) * num2); double num9 = num4 + 1.0 + (num4 - 1.0) * num2 - num6; return new BiQuadFilter(num7, num8, num9, b, b2, b3); } public static BiQuadFilter HighShelf(float sampleRate, float cutoffFrequency, float shelfSlope, float dbGain) { double num = Math.PI * 2.0 * (double)cutoffFrequency / (double)sampleRate; double num2 = Math.Cos(num); double num3 = Math.Sin(num); double num4 = Math.Pow(10.0, dbGain / 40f); double num5 = num3 / 2.0 * Math.Sqrt((num4 + 1.0 / num4) * (double)(1f / shelfSlope - 1f) + 2.0); double num6 = 2.0 * Math.Sqrt(num4) * num5; double b = num4 * (num4 + 1.0 + (num4 - 1.0) * num2 + num6); double b2 = -2.0 * num4 * (num4 - 1.0 + (num4 + 1.0) * num2); double b3 = num4 * (num4 + 1.0 + (num4 - 1.0) * num2 - num6); double num7 = num4 + 1.0 - (num4 - 1.0) * num2 + num6; double num8 = 2.0 * (num4 - 1.0 - (num4 + 1.0) * num2); double num9 = num4 + 1.0 - (num4 - 1.0) * num2 - num6; return new BiQuadFilter(num7, num8, num9, b, b2, b3); } private BiQuadFilter() { x1 = (x2 = 0f); y1 = (y2 = 0f); } private BiQuadFilter(double a0, double a1, double a2, double b0, double b1, double b2) { SetCoefficients(a0, a1, a2, b0, b1, b2); x1 = (x2 = 0f); y1 = (y2 = 0f); } } public struct Complex { public float X; public float Y; } internal class EnvelopeDetector { private double sampleRate; private double ms; private double coeff; public double TimeConstant { get { return ms; } set { ms = value; SetCoef(); } } public double SampleRate { get { return sampleRate; } set { sampleRate = value; SetCoef(); } } public EnvelopeDetector() : this(1.0, 44100.0) { } public EnvelopeDetector(double ms, double sampleRate) { this.sampleRate = sampleRate; this.ms = ms; SetCoef(); } public double Run(double inValue, double state) { return inValue + coeff * (state - inValue); } private void SetCoef() { coeff = Math.Exp(-1.0 / (0.001 * ms * sampleRate)); } } internal class AttRelEnvelope { protected const double DC_OFFSET = 1E-25; private readonly EnvelopeDetector attack; private readonly EnvelopeDetector release; public double Attack { get { return attack.TimeConstant; } set { attack.TimeConstant = value; } } public double Release { get { return release.TimeConstant; } set { release.TimeConstant = value; } } public double SampleRate { get { return attack.SampleRate; } set { EnvelopeDetector envelopeDetector = attack; double sampleRate = (release.SampleRate = value); envelopeDetector.SampleRate = sampleRate; } } public AttRelEnvelope(double attackMilliseconds, double releaseMilliseconds, double sampleRate) { attack = new EnvelopeDetector(attackMilliseconds, sampleRate); release = new EnvelopeDetector(releaseMilliseconds, sampleRate); } public double Run(double inValue, double state) { if (!(inValue > state)) { return release.Run(inValue, state); } return attack.Run(inValue, state); } } public class EnvelopeGenerator { public enum EnvelopeState { Idle, Attack, Decay, Sustain, Release } private EnvelopeState state; private float output; private float attackRate; private float decayRate; private float releaseRate; private float attackCoef; private float decayCoef; private float releaseCoef; private float sustainLevel; private float targetRatioAttack; private float targetRatioDecayRelease; private float attackBase; private float decayBase; private float releaseBase; public float AttackRate { get { return attackRate; } set { attackRate = value; attackCoef = CalcCoef(value, targetRatioAttack); attackBase = (1f + targetRatioAttack) * (1f - attackCoef); } } public float DecayRate { get { return decayRate; } set { decayRate = value; decayCoef = CalcCoef(value, targetRatioDecayRelease); decayBase = (sustainLevel - targetRatioDecayRelease) * (1f - decayCoef); } } public float ReleaseRate { get { return releaseRate; } set { releaseRate = value; releaseCoef = CalcCoef(value, targetRatioDecayRelease); releaseBase = (0f - targetRatioDecayRelease) * (1f - releaseCoef); } } public float SustainLevel { get { return sustainLevel; } set { sustainLevel = value; decayBase = (sustainLevel - targetRatioDecayRelease) * (1f - decayCoef); } } public EnvelopeState State => state; public EnvelopeGenerator() { Reset(); AttackRate = 0f; DecayRate = 0f; ReleaseRate = 0f; SustainLevel = 1f; SetTargetRatioAttack(0.3f); SetTargetRatioDecayRelease(0.0001f); } private static float CalcCoef(float rate, float targetRatio) { return (float)Math.Exp((0.0 - Math.Log((1f + targetRatio) / targetRatio)) / (double)rate); } private void SetTargetRatioAttack(float targetRatio) { if (targetRatio < 1E-09f) { targetRatio = 1E-09f; } targetRatioAttack = targetRatio; attackBase = (1f + targetRatioAttack) * (1f - attackCoef); } private void SetTargetRatioDecayRelease(float targetRatio) { if (targetRatio < 1E-09f) { targetRatio = 1E-09f; } targetRatioDecayRelease = targetRatio; decayBase = (sustainLevel - targetRatioDecayRelease) * (1f - decayCoef); releaseBase = (0f - targetRatioDecayRelease) * (1f - releaseCoef); } public float Process() { switch (state) { case EnvelopeState.Attack: output = attackBase + output * attackCoef; if (output >= 1f) { output = 1f; state = EnvelopeState.Decay; } break; case EnvelopeState.Decay: output = decayBase + output * decayCoef; if (output <= sustainLevel) { output = sustainLevel; state = EnvelopeState.Sustain; } break; case EnvelopeState.Release: output = releaseBase + output * releaseCoef; if ((double)output <= 0.0) { output = 0f; state = EnvelopeState.Idle; } break; } return output; } public void Gate(bool gate) { if (gate) { state = EnvelopeState.Attack; } else if (state != 0) { state = EnvelopeState.Release; } } public void Reset() { state = EnvelopeState.Idle; output = 0f; } public float GetOutput() { return output; } } public static class FastFourierTransform { public static void FFT(bool forward, int m, Complex[] data) { int num = 1; for (int i = 0; i < m; i++) { num *= 2; } int num2 = num >> 1; int num3 = 0; for (int i = 0; i < num - 1; i++) { if (i < num3) { float x = data[i].X; float y = data[i].Y; data[i].X = data[num3].X; data[i].Y = data[num3].Y; data[num3].X = x; data[num3].Y = y; } int num4; for (num4 = num2; num4 <= num3; num4 >>= 1) { num3 -= num4; } num3 += num4; } float num5 = -1f; float num6 = 0f; int num7 = 1; for (int j = 0; j < m; j++) { int num8 = num7; num7 <<= 1; float num9 = 1f; float num10 = 0f; for (num3 = 0; num3 < num8; num3++) { for (int i = num3; i < num; i += num7) { int num11 = i + num8; float num12 = num9 * data[num11].X - num10 * data[num11].Y; float num13 = num9 * data[num11].Y + num10 * data[num11].X; data[num11].X = data[i].X - num12; data[num11].Y = data[i].Y - num13; data[i].X += num12; data[i].Y += num13; } float num14 = num9 * num5 - num10 * num6; num10 = num9 * num6 + num10 * num5; num9 = num14; } num6 = (float)Math.Sqrt((1f - num5) / 2f); if (forward) { num6 = 0f - num6; } num5 = (float)Math.Sqrt((1f + num5) / 2f); } if (forward) { for (int i = 0; i < num; i++) { data[i].X /= num; data[i].Y /= num; } } } public static double HammingWindow(int n, int frameSize) { return 0.54 - 0.46 * Math.Cos(Math.PI * 2.0 * (double)n / (double)(frameSize - 1)); } public static double HannWindow(int n, int frameSize) { return 0.5 * (1.0 - Math.Cos(Math.PI * 2.0 * (double)n / (double)(frameSize - 1))); } public static double BlackmannHarrisWindow(int n, int frameSize) { return 287.0 / 800.0 - 0.48829 * Math.Cos(Math.PI * 2.0 * (double)n / (double)(frameSize - 1)) + 0.14128 * Math.Cos(Math.PI * 4.0 * (double)n / (double)(frameSize - 1)) - 0.01168 * Math.Cos(Math.PI * 6.0 * (double)n / (double)(frameSize - 1)); } } public class ImpulseResponseConvolution { public float[] Convolve(float[] input, float[] impulseResponse) { float[] array = new float[input.Length + impulseResponse.Length]; for (int i = 0; i < array.Length; i++) { for (int j = 0; j < impulseResponse.Length; j++) { if (i >= j && i - j < input.Length) { array[i] += impulseResponse[j] * input[i - j]; } } } Normalize(array); return array; } public void Normalize(float[] data) { float num = 0f; for (int i = 0; i < data.Length; i++) { num = Math.Max(num, Math.Abs(data[i])); } if ((double)num > 1.0) { for (int j = 0; j < data.Length; j++) { data[j] /= num; } } } } internal class SimpleCompressor : AttRelEnvelope { private double envdB; public double MakeUpGain { get; set; } public double Threshold { get; set; } public double Ratio { get; set; } public SimpleCompressor(double attackTime, double releaseTime, double sampleRate) : base(attackTime, releaseTime, sampleRate) { Threshold = 0.0; Ratio = 1.0; MakeUpGain = 0.0; envdB = 1E-25; } public SimpleCompressor() : this(10.0, 10.0, 44100.0) { } public void InitRuntime() { envdB = 1E-25; } public void Process(ref double in1, ref double in2) { double val = Math.Abs(in1); double val2 = Math.Abs(in2); double num = Decibels.LinearToDecibels(Math.Max(val, val2) + 1E-25) - Threshold; if (num < 0.0) { num = 0.0; } num += 1E-25; envdB = Run(num, envdB); num = envdB - 1E-25; double dB = num * (Ratio - 1.0); dB = Decibels.DecibelsToLinear(dB) * Decibels.DecibelsToLinear(MakeUpGain); in1 *= dB; in2 *= dB; } } internal class SimpleGate : AttRelEnvelope { private double threshdB; private double thresh; private double env; public double Threshold { get { return threshdB; } set { threshdB = value; thresh = Decibels.DecibelsToLinear(value); } } public SimpleGate() : base(10.0, 10.0, 44100.0) { threshdB = 0.0; thresh = 1.0; env = 1E-25; } public void Process(ref double in1, ref double in2) { double val = Math.Abs(in1); double val2 = Math.Abs(in2); double num = ((Math.Max(val, val2) > thresh) ? 1.0 : 0.0); num += 1E-25; env = Run(num, env); num = env - 1E-25; in1 *= num; in2 *= num; } } public class SmbPitchShifter { private static int MAX_FRAME_LENGTH = 16000; private float[] gInFIFO = new float[MAX_FRAME_LENGTH]; private float[] gOutFIFO = new float[MAX_FRAME_LENGTH]; private float[] gFFTworksp = new float[2 * MAX_FRAME_LENGTH]; private float[] gLastPhase = new float[MAX_FRAME_LENGTH / 2 + 1]; private float[] gSumPhase = new float[MAX_FRAME_LENGTH / 2 + 1]; private float[] gOutputAccum = new float[2 * MAX_FRAME_LENGTH]; private float[] gAnaFreq = new float[MAX_FRAME_LENGTH]; private float[] gAnaMagn = new float[MAX_FRAME_LENGTH]; private float[] gSynFreq = new float[MAX_FRAME_LENGTH]; private float[] gSynMagn = new float[MAX_FRAME_LENGTH]; private long gRover; public void PitchShift(float pitchShift, long numSampsToProcess, float sampleRate, float[] indata) { PitchShift(pitchShift, numSampsToProcess, 2048L, 10L, sampleRate, indata); } public void PitchShift(float pitchShift, long numSampsToProcess, long fftFrameSize, long osamp, float sampleRate, float[] indata) { long num = fftFrameSize / 2; long num2 = fftFrameSize / osamp; double num3 = (double)sampleRate / (double)fftFrameSize; double num4 = Math.PI * 2.0 * (double)num2 / (double)fftFrameSize; long num5 = fftFrameSize - num2; if (gRover == 0L) { gRover = num5; } for (long num6 = 0L; num6 < numSampsToProcess; num6++) { gInFIFO[gRover] = indata[num6]; indata[num6] = gOutFIFO[gRover - num5]; gRover++; if (gRover < fftFrameSize) { continue; } gRover = num5; for (long num7 = 0L; num7 < fftFrameSize; num7++) { double num8 = -0.5 * Math.Cos(Math.PI * 2.0 * (double)num7 / (double)fftFrameSize) + 0.5; gFFTworksp[2 * num7] = (float)((double)gInFIFO[num7] * num8); gFFTworksp[2 * num7 + 1] = 0f; } ShortTimeFourierTransform(gFFTworksp, fftFrameSize, -1L); for (long num7 = 0L; num7 <= num; num7++) { double num9 = gFFTworksp[2 * num7]; double num10 = gFFTworksp[2 * num7 + 1]; double num11 = 2.0 * Math.Sqrt(num9 * num9 + num10 * num10); double num12 = Math.Atan2(num10, num9); double num13 = num12 - (double)gLastPhase[num7]; gLastPhase[num7] = (float)num12; num13 -= (double)num7 * num4; long num14 = (long)(num13 / Math.PI); num14 = ((num14 < 0) ? (num14 - (num14 & 1)) : (num14 + (num14 & 1))); num13 -= Math.PI * (double)num14; num13 = (double)osamp * num13 / (Math.PI * 2.0); num13 = (double)num7 * num3 + num13 * num3; gAnaMagn[num7] = (float)num11; gAnaFreq[num7] = (float)num13; } for (int i = 0; i < fftFrameSize; i++) { gSynMagn[i] = 0f; gSynFreq[i] = 0f; } for (long num7 = 0L; num7 <= num; num7++) { long num15 = (long)((float)num7 * pitchShift); if (num15 <= num) { gSynMagn[num15] += gAnaMagn[num7]; gSynFreq[num15] = gAnaFreq[num7] * pitchShift; } } for (long num7 = 0L; num7 <= num; num7++) { double num11 = gSynMagn[num7]; double num13 = gSynFreq[num7]; num13 -= (double)num7 * num3; num13 /= num3; num13 = Math.PI * 2.0 * num13 / (double)osamp; num13 += (double)num7 * num4; gSumPhase[num7] += (float)num13; double num12 = gSumPhase[num7]; gFFTworksp[2 * num7] = (float)(num11 * Math.Cos(num12)); gFFTworksp[2 * num7 + 1] = (float)(num11 * Math.Sin(num12)); } for (long num7 = fftFrameSize + 2; num7 < 2 * fftFrameSize; num7++) { gFFTworksp[num7] = 0f; } ShortTimeFourierTransform(gFFTworksp, fftFrameSize, 1L); for (long num7 = 0L; num7 < fftFrameSize; num7++) { double num8 = -0.5 * Math.Cos(Math.PI * 2.0 * (double)num7 / (double)fftFrameSize) + 0.5; gOutputAccum[num7] += (float)(2.0 * num8 * (double)gFFTworksp[2 * num7] / (double)(num * osamp)); } for (long num7 = 0L; num7 < num2; num7++) { gOutFIFO[num7] = gOutputAccum[num7]; } for (long num7 = 0L; num7 < fftFrameSize; num7++) { gOutputAccum[num7] = gOutputAccum[num7 + num2]; } for (long num7 = 0L; num7 < num5; num7++) { gInFIFO[num7] = gInFIFO[num7 + num2]; } } } public void ShortTimeFourierTransform(float[] fftBuffer, long fftFrameSize, long sign) { for (long num = 2L; num < 2 * fftFrameSize - 2; num += 2) { long num2 = 2L; long num3 = 0L; while (num2 < 2 * fftFrameSize) { if ((num & num2) != 0L) { num3++; } num3 <<= 1; num2 <<= 1; } if (num < num3) { float num4 = fftBuffer[num]; fftBuffer[num] = fftBuffer[num3]; fftBuffer[num3] = num4; num4 = fftBuffer[num + 1]; fftBuffer[num + 1] = fftBuffer[num3 + 1]; fftBuffer[num3 + 1] = num4; } } long num5 = (long)(Math.Log(fftFrameSize) / Math.Log(2.0) + 0.5); long num6 = 0L; long num7 = 2L; for (; num6 < num5; num6++) { num7 <<= 1; long num8 = num7 >> 1; float num9 = 1f; float num10 = 0f; float num11 = MathF.PI / (float)(num8 >> 1); float num12 = (float)Math.Cos(num11); float num13 = (float)((double)sign * Math.Sin(num11)); for (long num3 = 0L; num3 < num8; num3 += 2) { float num14; for (long num = num3; num < 2 * fftFrameSize; num += num7) { num14 = fftBuffer[num + num8] * num9 - fftBuffer[num + num8 + 1] * num10; float num15 = fftBuffer[num + num8] * num10 + fftBuffer[num + num8 + 1] * num9; fftBuffer[num + num8] = fftBuffer[num] - num14; fftBuffer[num + num8 + 1] = fftBuffer[num + 1] - num15; fftBuffer[num] += num14; fftBuffer[num + 1] += num15; } num14 = num9 * num12 - num10 * num13; num10 = num9 * num13 + num10 * num12; num9 = num14; } } } } public class WdlResampler { private class WDL_Resampler_IIRFilter { private double m_fpos; private double m_a1; private double m_a2; private double m_b0; private double m_b1; private double m_b2; private double[,] m_hist; public WDL_Resampler_IIRFilter() { m_fpos = -1.0; Reset(); } public void Reset() { m_hist = new double[256, 4]; } public void setParms(double fpos, double Q) { if (!(Math.Abs(fpos - m_fpos) < 1E-06)) { m_fpos = fpos; double num = fpos * Math.PI; double num2 = Math.Cos(num); double num3 = Math.Sin(num) / (2.0 * Q); double num4 = 1.0 / (1.0 + num3); m_b1 = (1.0 - num2) * num4; m_b2 = (m_b0 = m_b1 * 0.5); m_a1 = -2.0 * num2 * num4; m_a2 = (1.0 - num3) * num4; } } public void Apply(float[] inBuffer, int inIndex, float[] outBuffer, int outIndex, int ns, int span, int w) { double b = m_b0; double b2 = m_b1; double b3 = m_b2; double a = m_a1; double a2 = m_a2; while (ns-- != 0) { double num = inBuffer[inIndex]; inIndex += span; double x = num * b + m_hist[w, 0] * b2 + m_hist[w, 1] * b3 - m_hist[w, 2] * a - m_hist[w, 3] * a2; m_hist[w, 1] = m_hist[w, 0]; m_hist[w, 0] = num; m_hist[w, 3] = m_hist[w, 2]; m_hist[w, 2] = denormal_filter(x); outBuffer[outIndex] = (float)m_hist[w, 2]; outIndex += span; } } private double denormal_filter(float x) { return x; } private double denormal_filter(double x) { return x; } } private const int WDL_RESAMPLE_MAX_FILTERS = 4; private const int WDL_RESAMPLE_MAX_NCH = 64; private const double PI = Math.PI; private double m_sratein; private double m_srateout; private double m_fracpos; private double m_ratio; private double m_filter_ratio; private float m_filterq; private float m_filterpos; private float[] m_rsinbuf; private float[] m_filter_coeffs; private WDL_Resampler_IIRFilter m_iirfilter; private int m_filter_coeffs_size; private int m_last_requested; private int m_filtlatency; private int m_samples_in_rsinbuf; private int m_lp_oversize; private int m_sincsize; private int m_filtercnt; private int m_sincoversize; private bool m_interp; private bool m_feedmode; public WdlResampler() { m_filterq = 0.707f; m_filterpos = 0.693f; m_sincoversize = 0; m_lp_oversize = 1; m_sincsize = 0; m_filtercnt = 1; m_interp = true; m_feedmode = false; m_filter_coeffs_size = 0; m_sratein = 44100.0; m_srateout = 44100.0; m_ratio = 1.0; m_filter_ratio = -1.0; Reset(); } public void SetMode(bool interp, int filtercnt, bool sinc, int sinc_size = 64, int sinc_interpsize = 32) { m_sincsize = ((sinc && sinc_size >= 4) ? ((sinc_size > 8192) ? 8192 : sinc_size) : 0); m_sincoversize = ((m_sincsize == 0) ? 1 : ((sinc_interpsize <= 1) ? 1 : ((sinc_interpsize >= 4096) ? 4096 : sinc_interpsize))); m_filtercnt = ((m_sincsize == 0) ? ((filtercnt > 0) ? ((filtercnt >= 4) ? 4 : filtercnt) : 0) : 0); m_interp = interp && m_sincsize == 0; if (m_sincsize == 0) { m_filter_coeffs = new float[0]; m_filter_coeffs_size = 0; } if (m_filtercnt == 0) { m_iirfilter = null; } } public void SetFilterParms(float filterpos = 0.693f, float filterq = 0.707f) { m_filterpos = filterpos; m_filterq = filterq; } public void SetFeedMode(bool wantInputDriven) { m_feedmode = wantInputDriven; } public void Reset(double fracpos = 0.0) { m_last_requested = 0; m_filtlatency = 0; m_fracpos = fracpos; m_samples_in_rsinbuf = 0; if (m_iirfilter != null) { m_iirfilter.Reset(); } } public void SetRates(double rate_in, double rate_out) { if (rate_in < 1.0) { rate_in = 1.0; } if (rate_out < 1.0) { rate_out = 1.0; } if (rate_in != m_sratein || rate_out != m_srateout) { m_sratein = rate_in; m_srateout = rate_out; m_ratio = m_sratein / m_srateout; } } public double GetCurrentLatency() { double num = ((double)m_samples_in_rsinbuf - (double)m_filtlatency) / m_sratein; if (num < 0.0) { num = 0.0; } return num; } public int ResamplePrepare(int out_samples, int nch, out float[] inbuffer, out int inbufferOffset) { if (nch > 64 || nch < 1) { inbuffer = null; inbufferOffset = 0; return 0; } int num = 0; if (m_sincsize > 1) { num = m_sincsize; } int num2 = num / 2; if (num2 > 1 && m_samples_in_rsinbuf < num2 - 1) { m_filtlatency += num2 - 1 - m_samples_in_rsinbuf; m_samples_in_rsinbuf = num2 - 1; if (m_samples_in_rsinbuf > 0) { m_rsinbuf = new float[m_samples_in_rsinbuf * nch]; } } int num3 = 0; num3 = (m_feedmode ? out_samples : ((int)(m_ratio * (double)out_samples) + 4 + num - m_samples_in_rsinbuf)); if (num3 < 0) { num3 = 0; } while (true) { Array.Resize(ref m_rsinbuf, (m_samples_in_rsinbuf + num3) * nch); int num4 = m_rsinbuf.Length / ((nch == 0) ? 1 : nch) - m_samples_in_rsinbuf; if (num4 == num3) { break; } if (num3 > 4 && num4 == 0) { num3 /= 2; continue; } num3 = num4; break; } inbuffer = m_rsinbuf; inbufferOffset = m_samples_in_rsinbuf * nch; m_last_requested = num3; return num3; } public int ResampleOut(float[] outBuffer, int outBufferIndex, int nsamples_in, int nsamples_out, int nch) { if (nch > 64 || nch < 1) { return 0; } if (m_filtercnt > 0 && m_ratio > 1.0 && nsamples_in > 0) { if (m_iirfilter == null) { m_iirfilter = new WDL_Resampler_IIRFilter(); } int filtercnt = m_filtercnt; m_iirfilter.setParms(1.0 / m_ratio * (double)m_filterpos, m_filterq); int num = m_samples_in_rsinbuf * nch; int num2 = 0; for (int i = 0; i < nch; i++) { for (int j = 0; j < filtercnt; j++) { m_iirfilter.Apply(m_rsinbuf, num + i, m_rsinbuf, num + i, nsamples_in, nch, num2++); } } } m_samples_in_rsinbuf += Math.Min(nsamples_in, m_last_requested); int num3 = m_samples_in_rsinbuf; if (nsamples_in < m_last_requested) { int num4 = (m_last_requested - nsamples_in) * 2 + m_sincsize * 2; int num5 = (m_samples_in_rsinbuf + num4) * nch; Array.Resize(ref m_rsinbuf, num5); if (m_rsinbuf.Length == num5) { Array.Clear(m_rsinbuf, m_samples_in_rsinbuf * nch, num4 * nch); num3 = m_samples_in_rsinbuf + num4; } } int num6 = 0; double num7 = m_fracpos; double ratio = m_ratio; int num8 = 0; int num9 = outBufferIndex; int num10 = nsamples_out; int num11 = 0; if (m_sincsize != 0) { if (m_ratio > 1.0) { BuildLowPass(1.0 / (m_ratio * 1.03)); } else { BuildLowPass(1.0); } int filter_coeffs_size = m_filter_coeffs_size; int num12 = num3 - filter_coeffs_size; num11 = filter_coeffs_size / 2 - 1; int filterIndex = 0; if (nch == 1) { while (num10-- != 0) { int num13 = (int)num7; if (num13 >= num12 - 1) { break; } SincSample1(outBuffer, num9, m_rsinbuf, num8 + num13, num7 - (double)num13, m_filter_coeffs, filterIndex, filter_coeffs_size); num9++; num7 += ratio; num6++; } } else if (nch == 2) { while (num10-- != 0) { int num14 = (int)num7; if (num14 >= num12 - 1) { break; } SincSample2(outBuffer, num9, m_rsinbuf, num8 + num14 * 2, num7 - (double)num14, m_filter_coeffs, filterIndex, filter_coeffs_size); num9 += 2; num7 += ratio; num6++; } } else { while (num10-- != 0) { int num15 = (int)num7; if (num15 >= num12 - 1) { break; } SincSample(outBuffer, num9, m_rsinbuf, num8 + num15 * nch, num7 - (double)num15, nch, m_filter_coeffs, filterIndex, filter_coeffs_size); num9 += nch; num7 += ratio; num6++; } } } else if (!m_interp) { if (nch == 1) { while (num10-- != 0) { int num16 = (int)num7; if (num16 >= num3) { break; } outBuffer[num9++] = m_rsinbuf[num8 + num16]; num7 += ratio; num6++; } } else if (nch == 2) { while (num10-- != 0) { int num17 = (int)num7; if (num17 >= num3) { break; } num17 += num17; outBuffer[num9] = m_rsinbuf[num8 + num17]; outBuffer[num9 + 1] = m_rsinbuf[num8 + num17 + 1]; num9 += 2; num7 += ratio; num6++; } } else { while (num10-- != 0) { int num18 = (int)num7; if (num18 >= num3) { break; } Array.Copy(m_rsinbuf, num8 + num18 * nch, outBuffer, num9, nch); num9 += nch; num7 += ratio; num6++; } } } else if (nch == 1) { while (num10-- != 0) { int num19 = (int)num7; double num20 = num7 - (double)num19; if (num19 >= num3 - 1) { break; } double num21 = 1.0 - num20; int num22 = num8 + num19; outBuffer[num9++] = (float)((double)m_rsinbuf[num22] * num21 + (double)m_rsinbuf[num22 + 1] * num20); num7 += ratio; num6++; } } else if (nch == 2) { while (num10-- != 0) { int num23 = (int)num7; double num24 = num7 - (double)num23; if (num23 >= num3 - 1) { break; } double num25 = 1.0 - num24; int num26 = num8 + num23 * 2; outBuffer[num9] = (float)((double)m_rsinbuf[num26] * num25 + (double)m_rsinbuf[num26 + 2] * num24); outBuffer[num9 + 1] = (float)((double)m_rsinbuf[num26 + 1] * num25 + (double)m_rsinbuf[num26 + 3] * num24); num9 += 2; num7 += ratio; num6++; } } else { while (num10-- != 0) { int num27 = (int)num7; double num28 = num7 - (double)num27; if (num27 >= num3 - 1) { break; } double num29 = 1.0 - num28; int num30 = nch; int num31 = num8 + num27 * nch; while (num30-- != 0) { outBuffer[num9++] = (float)((double)m_rsinbuf[num31] * num29 + (double)m_rsinbuf[num31 + nch] * num28); num31++; } num7 += ratio; num6++; } } if (m_filtercnt > 0 && m_ratio < 1.0 && num6 > 0) { if (m_iirfilter == null) { m_iirfilter = new WDL_Resampler_IIRFilter(); } int filtercnt2 = m_filtercnt; m_iirfilter.setParms(m_ratio * (double)m_filterpos, m_filterq); int num32 = 0; for (int k = 0; k < nch; k++) { for (int l = 0; l < filtercnt2; l++) { m_iirfilter.Apply(outBuffer, k, outBuffer, k, num6, nch, num32++); } } } if (num6 > 0 && num3 > m_samples_in_rsinbuf) { double num33 = (num7 - (double)m_samples_in_rsinbuf + (double)num11) / ratio; if (num33 > 0.0) { num6 -= (int)(num33 + 0.5); if (num6 < 0) { num6 = 0; } } } int num34 = (int)num7; m_fracpos = num7 - (double)num34; m_samples_in_rsinbuf -= num34; if (m_samples_in_rsinbuf <= 0) { m_samples_in_rsinbuf = 0; } else { Array.Copy(m_rsinbuf, num8 + num34 * nch, m_rsinbuf, num8, m_samples_in_rsinbuf * nch); } return num6; } private void BuildLowPass(double filtpos) { int sincsize = m_sincsize; int sincoversize = m_sincoversize; if (m_filter_ratio == filtpos && m_filter_coeffs_size == sincsize && m_lp_oversize == sincoversize) { return; } m_lp_oversize = sincoversize; m_filter_ratio = filtpos; int num = (sincsize + 1) * m_lp_oversize; Array.Resize(ref m_filter_coeffs, num); if (m_filter_coeffs.Length == num) { m_filter_coeffs_size = sincsize; int num2 = sincsize * m_lp_oversize; int num3 = num2 / 2; double num4 = 0.0; double num5 = 0.0; double num6 = Math.PI * 2.0 / (double)num2; double num7 = Math.PI / (double)m_lp_oversize * filtpos; double num8 = num7 * (double)(-num3); for (int i = -num3; i < num3 + m_lp_oversize; i++) { double num9 = 287.0 / 800.0 - 0.48829 * Math.Cos(num5) + 0.14128 * Math.Cos(2.0 * num5) - 0.01168 * Math.Cos(6.0 * num5); if (i != 0) { num9 *= Math.Sin(num8) / num8; } num5 += num6; num8 += num7; m_filter_coeffs[num3 + i] = (float)num9; if (i < num3) { num4 += num9; } } num4 = (double)m_lp_oversize / num4; for (int i = 0; i < num2 + m_lp_oversize; i++) { m_filter_coeffs[i] = (float)((double)m_filter_coeffs[i] * num4); } } else { m_filter_coeffs_size = 0; } } private void SincSample(float[] outBuffer, int outBufferIndex, float[] inBuffer, int inBufferIndex, double fracpos, int nch, float[] filter, int filterIndex, int filtsz) { int lp_oversize = m_lp_oversize; fracpos *= (double)lp_oversize; int num = (int)fracpos; filterIndex += lp_oversize - 1 - num; fracpos -= (double)num; for (int i = 0; i < nch; i++) { double num2 = 0.0; double num3 = 0.0; int num4 = filterIndex; int num5 = inBufferIndex + i; int num6 = filtsz; while (num6-- != 0) { num2 += (double)(filter[num4] * inBuffer[num5]); num3 += (double)(filter[num4 + 1] * inBuffer[num5]); num5 += nch; num4 += lp_oversize; } outBuffer[outBufferIndex + i] = (float)(num2 * fracpos + num3 * (1.0 - fracpos)); } } private void SincSample1(float[] outBuffer, int outBufferIndex, float[] inBuffer, int inBufferIndex, double fracpos, float[] filter, int filterIndex, int filtsz) { int lp_oversize = m_lp_oversize; fracpos *= (double)lp_oversize; int num = (int)fracpos; filterIndex += lp_oversize - 1 - num; fracpos -= (double)num; double num2 = 0.0; double num3 = 0.0; int num4 = filterIndex; int num5 = inBufferIndex; int num6 = filtsz; while (num6-- != 0) { num2 += (double)(filter[num4] * inBuffer[num5]); num3 += (double)(filter[num4 + 1] * inBuffer[num5]); num5++; num4 += lp_oversize; } outBuffer[outBufferIndex] = (float)(num2 * fracpos + num3 * (1.0 - fracpos)); } private void SincSample2(float[] outptr, int outBufferIndex, float[] inBuffer, int inBufferIndex, double fracpos, float[] filter, int filterIndex, int filtsz) { int lp_oversize = m_lp_oversize; fracpos *= (double)lp_oversize; int num = (int)fracpos; filterIndex += lp_oversize - 1 - num; fracpos -= (double)num; double num2 = 0.0; double num3 = 0.0; double num4 = 0.0; double num5 = 0.0; int num6 = filterIndex; int num7 = inBufferIndex; int num8 = filtsz / 2; while (num8-- != 0) { num2 += (double)(filter[num6] * inBuffer[num7]); num3 += (double)(filter[num6] * inBuffer[num7 + 1]); num4 += (double)(filter[num6 + 1] * inBuffer[num7]); num5 += (double)(filter[num6 + 1] * inBuffer[num7 + 1]); num2 += (double)(filter[num6 + lp_oversize] * inBuffer[num7 + 2]); num3 += (double)(filter[num6 + lp_oversize] * inBuffer[num7 + 3]); num4 += (double)(filter[num6 + lp_oversize + 1] * inBuffer[num7 + 2]); num5 += (double)(filter[num6 + lp_oversize + 1] * inBuffer[num7 + 3]); num7 += 4; num6 += lp_oversize * 2; } outptr[outBufferIndex] = (float)(num2 * fracpos + num4 * (1.0 - fracpos)); outptr[outBufferIndex + 1] = (float)(num3 * fracpos + num5 * (1.0 - fracpos)); } } } namespace NAudio.Codecs { public class ALawDecoder { private static readonly short[] ALawDecompressTable = new short[256] { -5504, -5248, -6016, -5760, -4480, -4224, -4992, -4736, -7552, -7296, -8064, -7808, -6528, -6272, -7040, -6784, -2752, -2624, -3008, -2880, -2240, -2112, -2496, -2368, -3776, -3648, -4032, -3904, -3264, -3136, -3520, -3392, -22016, -20992, -24064, -23040, -17920, -16896, -19968, -18944, -30208, -29184, -32256, -31232, -26112, -25088, -28160, -27136, -11008, -10496, -12032, -11520, -8960, -8448, -9984, -9472, -15104, -14592, -16128, -15616, -13056, -12544, -14080, -13568, -344, -328, -376, -360, -280, -264, -312, -296, -472, -456, -504, -488, -408, -392, -440, -424, -88, -72, -120, -104, -24, -8, -56, -40, -216, -200, -248, -232, -152, -136, -184, -168, -1376, -1312, -1504, -1440, -1120, -1056, -1248, -1184, -1888, -1824, -2016, -1952, -1632, -1568, -1760, -1696, -688, -656, -752, -720, -560, -528, -624, -592, -944, -912, -1008, -976, -816, -784, -880, -848, 5504, 5248, 6016, 5760, 4480, 4224, 4992, 4736, 7552, 7296, 8064, 7808, 6528, 6272, 7040, 6784, 2752, 2624, 3008, 2880, 2240, 2112, 2496, 2368, 3776, 3648, 4032, 3904, 3264, 3136, 3520, 3392, 22016, 20992, 24064, 23040, 17920, 16896, 19968, 18944, 30208, 29184, 32256, 31232, 26112, 25088, 28160, 27136, 11008, 10496, 12032, 11520, 8960, 8448, 9984, 9472, 15104, 14592, 16128, 15616, 13056, 12544, 14080, 13568, 344, 328, 376, 360, 280, 264, 312, 296, 472, 456, 504, 488, 408, 392, 440, 424, 88, 72, 120, 104, 24, 8, 56, 40, 216, 200, 248, 232, 152, 136, 184, 168, 1376, 1312, 1504, 1440, 1120, 1056, 1248, 1184, 1888, 1824, 2016, 1952, 1632, 1568, 1760, 1696, 688, 656, 752, 720, 560, 528, 624, 592, 944, 912, 1008, 976, 816, 784, 880, 848 }; public static short ALawToLinearSample(byte aLaw) { return ALawDecompressTable[aLaw]; } } public static class ALawEncoder { private const int cBias = 132; private const int cClip = 32635; private static readonly byte[] ALawCompressTable = new byte[128] { 1, 1, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7 }; public static byte LinearToALawSample(short sample) { int num = (~sample >> 8) & 0x80; if (num == 0) { sample = (short)(-sample); } if (sample > 32635) { sample = 32635; } byte b; if (sample >= 256) { int num2 = ALawCompressTable[(sample >> 8) & 0x7F]; int num3 = (sample >> num2 + 3) & 0xF; b = (byte)((num2 << 4) | num3); } else { b = (byte)(sample >> 4); } return (byte)(b ^ (byte)((uint)num ^ 0x55u)); } } public class G722Codec { private static readonly int[] wl = new int[8] { -60, -30, 58, 172, 334, 538, 1198, 3042 }; private static readonly int[] rl42 = new int[16] { 0, 7, 6, 5, 4, 3, 2, 1, 7, 6, 5, 4, 3, 2, 1, 0 }; private static readonly int[] ilb = new int[32] { 2048, 2093, 2139, 2186, 2233, 2282, 2332, 2383, 2435, 2489, 2543, 2599, 2656, 2714, 2774, 2834, 2896, 2960, 3025, 3091, 3158, 3228, 3298, 3371, 3444, 3520, 3597, 3676, 3756, 3838, 3922, 4008 }; private static readonly int[] wh = new int[3] { 0, -214, 798 }; private static readonly int[] rh2 = new int[4] { 2, 1, 2, 1 }; private static readonly int[] qm2 = new int[4] { -7408, -1616, 7408, 1616 }; private static readonly int[] qm4 = new int[16] { 0, -20456, -12896, -8968, -6288, -4240, -2584, -1200, 20456, 12896, 8968, 6288, 4240, 2584, 1200, 0 }; private static readonly int[] qm5 = new int[32] { -280, -280, -23352, -17560, -14120, -11664, -9752, -8184, -6864, -5712, -4696, -3784, -2960, -2208, -1520, -880, 23352, 17560, 14120, 11664, 9752, 8184, 6864, 5712, 4696, 3784, 2960, 2208, 1520, 880, 280, -280 }; private static readonly int[] qm6 = new int[64] { -136, -136, -136, -136, -24808, -21904, -19008, -16704, -14984, -13512, -12280, -11192, -10232, -9360, -8576, -7856, -7192, -6576, -6000, -5456, -4944, -4464, -4008, -3576, -3168, -2776, -2400, -2032, -1688, -1360, -1040, -728, 24808, 21904, 19008, 16704, 14984, 13512, 12280, 11192, 10232, 9360, 8576, 7856, 7192, 6576, 6000, 5456, 4944, 4464, 4008, 3576, 3168, 2776, 2400, 2032, 1688, 1360, 1040, 728, 432, 136, -432, -136 }; private static readonly int[] qmf_coeffs = new int[12] { 3, -11, 12, 32, -210, 951, 3876, -805, 362, -156, 53, -11 }; private static readonly int[] q6 = new int[32] { 0, 35, 72, 110, 150, 190, 233, 276, 323, 370, 422, 473, 530, 587, 650, 714, 786, 858, 940, 1023, 1121, 1219, 1339, 1458, 1612, 1765, 1980, 2195, 2557, 2919, 0, 0 }; private static readonly int[] iln = new int[32] { 0, 63, 62, 31, 30, 29, 28, 27, 26, 25, 24, 23, 22, 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 0 }; private static readonly int[] ilp = new int[32] { 0, 61, 60, 59, 58, 57, 56, 55, 54, 53, 52, 51, 50, 49, 48, 47, 46, 45, 44, 43, 42, 41, 40, 39, 38, 37, 36, 35, 34, 33, 32, 0 }; private static readonly int[] ihn = new int[3] { 0, 1, 0 }; private static readonly int[] ihp = new int[3] { 0, 3, 2 }; private static short Saturate(int amp) { short num = (short)amp; if (amp == num) { return num; } if (amp > 32767) { return short.MaxValue; } return short.MinValue; } private static void Block4(G722CodecState s, int band, int d) { s.Band[band].d[0] = d; s.Band[band].r[0] = Saturate(s.Band[band].s + d); s.Band[band].p[0] = Saturate(s.Band[band].sz + d); for (int i = 0; i < 3; i++) { s.Band[band].sg[i] = s.Band[band].p[i] >> 15; } int num = Saturate(s.Band[band].a[1] << 2); int num2 = ((s.Band[band].sg[0] == s.Band[band].sg[1]) ? (-num) : num); if (num2 > 32767) { num2 = 32767; } int num3 = ((s.Band[band].sg[0] == s.Band[band].sg[2]) ? 128 : (-128)); num3 += num2 >> 7; num3 += s.Band[band].a[2] * 32512 >> 15; if (num3 > 12288) { num3 = 12288; } else if (num3 < -12288) { num3 = -12288; } s.Band[band].ap[2] = num3; s.Band[band].sg[0] = s.Band[band].p[0] >> 15; s.Band[band].sg[1] = s.Band[band].p[1] >> 15; num = ((s.Band[band].sg[0] == s.Band[band].sg[1]) ? 192 : (-192)); num2 = s.Band[band].a[1] * 32640 >> 15; s.Band[band].ap[1] = Saturate(num + num2); num3 = Saturate(15360 - s.Band[band].ap[2]); if (s.Band[band].ap[1] > num3) { s.Band[band].ap[1] = num3; } else if (s.Band[band].ap[1] < -num3) { s.Band[band].ap[1] = -num3; } num = ((d != 0) ? 128 : 0); s.Band[band].sg[0] = d >> 15; for (int i = 1; i < 7; i++) { s.Band[band].sg[i] = s.Band[band].d[i] >> 15; num2 = ((s.Band[band].sg[i] == s.Band[band].sg[0]) ? num : (-num)); num3 = s.Band[band].b[i] * 32640 >> 15; s.Band[band].bp[i] = Saturate(num2 + num3); } for (int i = 6; i > 0; i--) { s.Band[band].d[i] = s.Band[band].d[i - 1]; s.Band[band].b[i] = s.Band[band].bp[i]; } for (int i = 2; i > 0; i--) { s.Band[band].r[i] = s.Band[band].r[i - 1]; s.Band[band].p[i] = s.Band[band].p[i - 1]; s.Band[band].a[i] = s.Band[band].ap[i]; } num = Saturate(s.Band[band].r[1] + s.Band[band].r[1]); num = s.Band[band].a[1] * num >> 15; num2 = Saturate(s.Band[band].r[2] + s.Band[band].r[2]); num2 = s.Band[band].a[2] * num2 >> 15; s.Band[band].sp = Saturate(num + num2); s.Band[band].sz = 0; for (int i = 6; i > 0; i--) { num = Saturate(s.Band[band].d[i] + s.Band[band].d[i]); s.Band[band].sz += s.Band[band].b[i] * num >> 15; } s.Band[band].sz = Saturate(s.Band[band].sz); s.Band[band].s = Saturate(s.Band[band].sp + s.Band[band].sz); } public int Decode(G722CodecState state, short[] outputBuffer, byte[] inputG722Data, int inputLength) { int result = 0; int num = 0; int num2 = 0; while (num2 < inputLength) { int num3; if (state.Packed) { if (state.InBits < state.BitsPerSample) { state.InBuffer |= (uint)(inputG722Data[num2++] << state.InBits); state.InBits += 8; } num3 = (int)state.InBuffer & ((1 << state.BitsPerSample) - 1); state.InBuffer >>= state.BitsPerSample; state.InBits -= state.BitsPerSample; } else { num3 = inputG722Data[num2++]; } int num5; int num4; int num6; switch (state.BitsPerSample) { default: num4 = num3 & 0x3F; num5 = (num3 >> 6) & 3; num6 = qm6[num4]; num4 >>= 2; break; case 7: num4 = num3 & 0x1F; num5 = (num3 >> 5) & 3; num6 = qm5[num4]; num4 >>= 1; break; case 6: num4 = num3 & 0xF; num5 = (num3 >> 4) & 3; num6 = qm4[num4]; break; } num6 = state.Band[0].det * num6 >> 15; int num7 = state.Band[0].s + num6; if (num7 > 16383) { num7 = 16383; } else if (num7 < -16384) { num7 = -16384; } num6 = qm4[num4]; int d = state.Band[0].det * num6 >> 15; num6 = rl42[num4]; num4 = state.Band[0].nb * 127 >> 7; num4 += wl[num6]; if (num4 < 0) { num4 = 0; } else if (num4 > 18432) { num4 = 18432; } state.Band[0].nb = num4; num4 = (state.Band[0].nb >> 6) & 0x1F; num6 = 8 - (state.Band[0].nb >> 11); int num8 = ((num6 < 0) ? (ilb[num4] << -num6) : (ilb[num4] >> num6)); state.Band[0].det = num8 << 2; Block4(state, 0, d); if (!state.EncodeFrom8000Hz) { num6 = qm2[num5]; int num9 = state.Band[1].det * num6 >> 15; num = num9 + state.Band[1].s; if (num > 16383) { num = 16383; } else if (num < -16384) { num = -16384; } num6 = rh2[num5]; num4 = state.Band[1].nb * 127 >> 7; num4 += wh[num6]; if (num4 < 0) { num4 = 0; } else if (num4 > 22528) { num4 = 22528; } state.Band[1].nb = num4; num4 = (state.Band[1].nb >> 6) & 0x1F; num6 = 10 - (state.Band[1].nb >> 11); num8 = ((num6 < 0) ? (ilb[num4] << -num6) : (ilb[num4] >> num6)); state.Band[1].det = num8 << 2; Block4(state, 1, num9); } if (state.ItuTestMode) { outputBuffer[result++] = (short)(num7 << 1); outputBuffer[result++] = (short)(num << 1); continue; } if (state.EncodeFrom8000Hz) { outputBuffer[result++] = (short)(num7 << 1); continue; } for (int i = 0; i < 22; i++) { state.QmfSignalHistory[i] = state.QmfSignalHistory[i + 2]; } state.QmfSignalHistory[22] = num7 + num; state.QmfSignalHistory[23] = num7 - num; int num10 = 0; int num11 = 0; for (int i = 0; i < 12; i++) { num11 += state.QmfSignalHistory[2 * i] * qmf_coeffs[i]; num10 += state.QmfSignalHistory[2 * i + 1] * qmf_coeffs[11 - i]; } outputBuffer[result++] = (short)(num10 >> 11); outputBuffer[result++] = (short)(num11 >> 11); } return result; } public int Encode(G722CodecState state, byte[] outputBuffer, short[] inputBuffer, int inputBufferCount) { int result = 0; int num = 0; int num2 = 0; while (num2 < inputBufferCount) { int num3; int i; if (state.ItuTestMode) { num3 = (num = inputBuffer[num2++] >> 1); } else if (state.EncodeFrom8000Hz) { num3 = inputBuffer[num2++] >> 1; } else { for (i = 0; i < 22; i++) { state.QmfSignalHistory[i] = state.QmfSignalHistory[i + 2]; } state.QmfSignalHistory[22] = inputBuffer[num2++]; state.QmfSignalHistory[23] = inputBuffer[num2++]; int num4 = 0; int num5 = 0; for (i = 0; i < 12; i++) { num5 += state.QmfSignalHistory[2 * i] * qmf_coeffs[i]; num4 += state.QmfSignalHistory[2 * i + 1] * qmf_coeffs[11 - i]; } num3 = num4 + num5 >> 14; num = num4 - num5 >> 14; } int num6 = Saturate(num3 - state.Band[0].s); int num7 = ((num6 >= 0) ? num6 : (-(num6 + 1))); int num8; for (i = 1; i < 30; i++) { num8 = q6[i] * state.Band[0].det >> 12; if (num7 < num8) { break; } } int num9 = ((num6 < 0) ? iln[i] : ilp[i]); int num10 = num9 >> 2; int num11 = qm4[num10]; int d = state.Band[0].det * num11 >> 15; int num12 = rl42[num10]; num7 = state.Band[0].nb * 127 >> 7; state.Band[0].nb = num7 + wl[num12]; if (state.Band[0].nb < 0) { state.Band[0].nb = 0; } else if (state.Band[0].nb > 18432) { state.Band[0].nb = 18432; } num8 = (state.Band[0].nb >> 6) & 0x1F; num11 = 8 - (state.Band[0].nb >> 11); int num13 = ((num11 < 0) ? (ilb[num8] << -num11) : (ilb[num8] >> num11)); state.Band[0].det = num13 << 2; Block4(state, 0, d); int num14; if (state.EncodeFrom8000Hz) { num14 = (0xC0 | num9) >> 8 - state.BitsPerSample; } else { int num15 = Saturate(num - state.Band[1].s); num7 = ((num15 >= 0) ? num15 : (-(num15 + 1))); num8 = 564 * state.Band[1].det >> 12; int num16 = ((num7 < num8) ? 1 : 2); int num17 = ((num15 < 0) ? ihn[num16] : ihp[num16]); num11 = qm2[num17]; int d2 = state.Band[1].det * num11 >> 15; int num18 = rh2[num17]; num7 = state.Band[1].nb * 127 >> 7; state.Band[1].nb = num7 + wh[num18]; if (state.Band[1].nb < 0) { state.Band[1].nb = 0; } else if (state.Band[1].nb > 22528) { state.Band[1].nb = 22528; } num8 = (state.Band[1].nb >> 6) & 0x1F; num11 = 10 - (state.Band[1].nb >> 11); num13 = ((num11 < 0) ? (ilb[num8] << -num11) : (ilb[num8] >> num11)); state.Band[1].det = num13 << 2; Block4(state, 1, d2); num14 = ((num17 << 6) | num9) >> 8 - state.BitsPerSample; } if (state.Packed) { state.OutBuffer |= (uint)(num14 << state.OutBits); state.OutBits += state.BitsPerSample; if (state.OutBits >= 8) { outputBuffer[result++] = (byte)(state.OutBuffer & 0xFFu); state.OutBits -= 8; state.OutBuffer >>= 8; } } else { outputBuffer[result++] = (byte)num14; } } return result; } } public class G722CodecState { public bool ItuTestMode { get; set; } public bool Packed { get; private set; } public bool EncodeFrom8000Hz { get; private set; } public int BitsPerSample { get; private set; } public int[] QmfSignalHistory { get; private set; } public Band[] Band { get; private set; } public uint InBuffer { get; internal set; } public int InBits { get; internal set; } public uint OutBuffer { get; internal set; } public int OutBits { get; internal set; } public G722CodecState(int rate, G722Flags options) { Band = new Band[2] { new Band(), new Band() }; QmfSignalHistory = new int[24]; ItuTestMode = false; switch (rate) { case 48000: BitsPerSample = 6; break; case 56000: BitsPerSample = 7; break; case 64000: BitsPerSample = 8; break; default: throw new ArgumentException("Invalid rate, should be 48000, 56000 or 64000"); } if ((options & G722Flags.SampleRate8000) == G722Flags.SampleRate8000) { EncodeFrom8000Hz = true; } if ((options & G722Flags.Packed) == G722Flags.Packed && BitsPerSample != 8) { Packed = true; } else { Packed = false; } Band[0].det = 32; Band[1].det = 8; } } public class Band { public int s; public int sp; public int sz; public int[] r = new int[3]; public int[] a = new int[3]; public int[] ap = new int[3]; public int[] p = new int[3]; public int[] d = new int[7]; public int[] b = new int[7]; public int[] bp = new int[7]; public int[] sg = new int[7]; public int nb; public int det; } [Flags] public enum G722Flags { None = 0, SampleRate8000 = 1, Packed = 2 } public static class MuLawDecoder { private static readonly short[] MuLawDecompressTable = new short[256] { -32124, -31100, -30076, -29052, -28028, -27004, -25980, -24956, -23932, -22908, -21884, -20860, -19836, -18812, -17788, -16764, -15996, -15484, -14972, -14460, -13948, -13436, -12924, -12412, -11900, -11388, -10876, -10364, -9852, -9340, -8828, -8316, -7932, -7676, -7420, -7164, -6908, -6652, -6396, -6140, -5884, -5628, -5372, -5116, -4860, -4604, -4348, -4092, -3900, -3772, -3644, -3516, -3388, -3260, -3132, -3004, -2876, -2748, -2620, -2492, -2364, -2236, -2108, -1980, -1884, -1820, -1756, -1692, -1628, -1564, -1500, -1436, -1372, -1308, -1244, -1180, -1116, -1052, -988, -924, -876, -844, -812, -780, -748, -716, -684, -652, -620, -588, -556, -524, -492, -460, -428, -396, -372, -356, -340, -324, -308, -292, -276, -260, -244, -228, -212, -196, -180, -164, -148, -132, -120, -112, -104, -96, -88, -80, -72, -64, -56, -48, -40, -32, -24, -16, -8, -1, 32124, 31100, 30076, 29052, 28028, 27004, 25980, 24956, 23932, 22908, 21884, 20860, 19836, 18812, 17788, 16764, 15996, 15484, 14972, 14460, 13948, 13436, 12924, 12412, 11900, 11388, 10876, 10364, 9852, 9340, 8828, 8316, 7932, 7676, 7420, 7164, 6908, 6652, 6396, 6140, 5884, 5628, 5372, 5116, 4860, 4604, 4348, 4092, 3900, 3772, 3644, 3516, 3388, 3260, 3132, 3004, 2876, 2748, 2620, 2492, 2364, 2236, 2108, 1980, 1884, 1820, 1756, 1692, 1628, 1564, 1500, 1436, 1372, 1308, 1244, 1180, 1116, 1052, 988, 924, 876, 844, 812, 780, 748, 716, 684, 652, 620, 588, 556, 524, 492, 460, 428, 396, 372, 356, 340, 324, 308, 292, 276, 260, 244, 228, 212, 196, 180, 164, 148, 132, 120, 112, 104, 96, 88, 80, 72, 64, 56, 48, 40, 32, 24, 16, 8, 0 }; public static short MuLawToLinearSample(byte muLaw) { return MuLawDecompressTable[muLaw]; } } public static class MuLawEncoder { private const int cBias = 132; private const int cClip = 32635; private static readonly byte[] MuLawCompressTable = new byte[256] { 0, 0, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7 }; public static byte LinearToMuLawSample(short sample) { int num = (sample >> 8) & 0x80; if (num != 0) { sample = (short)(-sample); } if (sample > 32635) { sample = 32635; } sample += 132; int num2 = MuLawCompressTable[(sample >> 7) & 0xFF]; int num3 = (sample >> num2 + 3) & 0xF; return (byte)(~(num | (num2 << 4) | num3)); } } }