diff --git a/AssemblyInfo.cs b/AssemblyInfo.cs new file mode 100644 index 0000000..36a2b85 --- /dev/null +++ b/AssemblyInfo.cs @@ -0,0 +1,62 @@ +using System.Reflection; +using System.Runtime.CompilerServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. + +// TODO: Review the values of the assembly attributes + +[assembly: AssemblyTitle("")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("")] +[assembly: AssemblyCopyright("")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Revision +// Build Number +// +// You can specify all the values or you can default the Revision and Build Numbers +// by using the '*' as shown below: + +[assembly: AssemblyVersion("1.0.*")] + +// +// In order to sign your assembly you must specify a key to use. Refer to the +// Microsoft .NET Framework documentation for more information on assembly signing. +// +// Use the attributes below to control which key is used for signing. +// +// Notes: +// (*) If no key is specified, the assembly is not signed. +// (*) KeyName refers to a key that has been installed in the Crypto Service +// Provider (CSP) on your machine. KeyFile refers to a file which contains +// a key. +// (*) If the KeyFile and the KeyName values are both specified, the +// following processing occurs: +// (1) If the KeyName can be found in the CSP, that key is used. +// (2) If the KeyName does not exist and the KeyFile does exist, the key +// in the KeyFile is installed into the CSP and used. +// (*) In order to create a KeyFile, you can use the sn.exe (Strong Name) utility. +// When specifying the KeyFile, the location of the KeyFile should be +// relative to the project output directory which is +// %Project Directory%\obj\. For example, if your KeyFile is +// located in the project directory, you would specify the AssemblyKeyFile +// attribute as [assembly: AssemblyKeyFile("..\..\mykey.snk")] +// (*) Delay Signing is an advanced option - see the Microsoft .NET Framework +// documentation for more information on this. +// + +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyDelaySign(false)] +[assembly: AssemblyKeyFile("")] +[assembly: AssemblyKeyName("")] + + diff --git a/BitsUtils.cs b/BitsUtils.cs new file mode 100644 index 0000000..f894f3b --- /dev/null +++ b/BitsUtils.cs @@ -0,0 +1,158 @@ +using System; +/* +** BitsUtils.cs +** +** Copyright (c) 2010-2016 Peter McQuillan +** +** All Rights Reserved. +** +** Distributed under the BSD Software License (see license.txt) +***/ + +class BitsUtils +{ + internal static Bitstream getbit(Bitstream bs) + { + if (bs.bc > 0) + { + bs.bc--; + } + else + { + bs.ptr++; + bs.buf_index++; + bs.bc = 7; + + if (bs.ptr == bs.end) + { + // wrap call here + bs = bs_read(bs); + } + bs.sr = (bs.buf[bs.buf_index] & 0xff); + } + + bs.bitval = (int)(bs.sr & 1); + bs.sr = bs.sr >> 1; + return bs; + } + + internal static long getbits(int nbits, Bitstream bs) + { + int uns_buf; + long retval; + + while ((nbits) > bs.bc) + { + bs.ptr++; + bs.buf_index++; + + if (bs.ptr == bs.end) + { + bs = bs_read(bs); + } + uns_buf = (int) (bs.buf[bs.buf_index] & 0xff); + bs.sr = bs.sr | (uns_buf << bs.bc); // values in buffer must be unsigned + + bs.sr = bs.sr & 0xFFFFFFFFL; // sr is an unsigned 32 bit variable + + bs.bc += 8; + } + + retval = bs.sr; + + if (bs.bc > 32) + { + bs.bc -= (nbits); + bs.sr = (bs.buf[bs.buf_index] & 0xff) >> (8 - bs.bc); + } + else + { + bs.bc -= (nbits); + bs.sr >>= (nbits); + } + + return (retval); + } + + internal static Bitstream bs_open_read(byte[] stream, short buffer_start, short buffer_end, System.IO.BinaryReader file, int file_bytes, int passed) + { + // CLEAR (*bs); + Bitstream bs = new Bitstream(); + + bs.buf = stream; + bs.buf_index = buffer_start; + bs.end = buffer_end; + bs.sr = 0; + bs.bc = 0; + + if (passed != 0) + { + bs.ptr = (short) (bs.end - 1); + bs.file_bytes = file_bytes; + bs.file = file; + } + else + { + /* Strange to set an index to -1, but the very first call to getbit will iterate this */ + bs.buf_index = - 1; + bs.ptr = (short) (- 1); + } + + return bs; + } + + internal static Bitstream bs_read(Bitstream bs) + { + if (bs.file_bytes > 0) + { + int bytes_to_read; + int bytes_read; + + bytes_to_read = Defines.BITSTREAM_BUFFER_SIZE; + + if (bytes_to_read > bs.file_bytes) + bytes_to_read = bs.file_bytes; + + try + { + bytes_read = bs.file.BaseStream.Read(bs.buf, 0, bytes_to_read); + + bs.buf_index = 0; + } + catch (System.Exception e) + { + System.Console.Error.WriteLine("Big error while reading file: " + e); + bytes_read = 0; + } + + if (bytes_read > 0) + { + bs.end = (short) (bytes_read); + bs.file_bytes -= bytes_read; + } + else + { + for (int i = 0; i < Defines.BITSTREAM_BUFFER_SIZE; i++) + { + bs.buf[i] = unchecked((byte)-1); + } + bs.error = 1; + } + } + else + { + bs.error = 1; + + for (int i = 0; i < Defines.BITSTREAM_BUFFER_SIZE; i++) + { + bs.buf[i] = unchecked((byte)-1); + } + } + + + bs.ptr = 0; + bs.buf_index = 0; + + return bs; + } +} diff --git a/Bitstream.cs b/Bitstream.cs new file mode 100644 index 0000000..d5cf524 --- /dev/null +++ b/Bitstream.cs @@ -0,0 +1,22 @@ +using System; +/* +** Bitstream.cs +** +** Copyright (c) 2010-2016 Peter McQuillan +** +** All Rights Reserved. +** +** Distributed under the BSD Software License (see license.txt) +***/ + +class Bitstream +{ + internal short end, ptr; // was uchar in c + internal long sr; + internal int file_bytes; + internal int error, bc; + internal System.IO.BinaryReader file; + internal int bitval = 0; + internal byte[] buf = new byte[Defines.BITSTREAM_BUFFER_SIZE]; + internal int buf_index = 0; +} diff --git a/CSharpWavPackDecoder.application b/CSharpWavPackDecoder.application new file mode 100644 index 0000000..62ba02f --- /dev/null +++ b/CSharpWavPackDecoder.application @@ -0,0 +1,18 @@ + + + + + + + + + + + + + + cB9XwrUuuay8pS5bD6VZeprNw3I= + + + + \ No newline at end of file diff --git a/CSharpWavPackDecoder.csproj b/CSharpWavPackDecoder.csproj new file mode 100644 index 0000000..ea4b74d --- /dev/null +++ b/CSharpWavPackDecoder.csproj @@ -0,0 +1,169 @@ + + + + Local + 2.0 + Debug + AnyCPU + + + CSharpWavPackDecoder + JScript + Grid + IE50 + false + Exe + CSharpWavPackDecoder + false + OnBuildSuccess + + + + + 2.0 + v3.5 + {04F62C81-761F-4906-9C0A-B56FDC5AA1BF} + false + LocalIntranet + C:\dev\C#\publish\ + true + Disk + false + Foreground + 7 + Days + false + false + true + 1 + 1.1.0.%2a + false + true + true + 9.0.21022 + + + . + 285212672 + + + + + 4096 + false + false + 4 + none + prompt + False + + + . + 285212672 + + + + + 4096 + false + false + 4 + none + prompt + True + + + + mscorlib + + + System + + + System.Data + + + System.Design + + + System.Drawing + + + System.Management + + + System.Windows.Forms + + + System.Xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + False + .NET Framework Client Profile + false + + + False + .NET Framework 2.0 %28x86%29 + false + + + False + .NET Framework 3.0 %28x86%29 + false + + + False + .NET Framework 3.5 + false + + + False + .NET Framework 3.5 SP1 + true + + + False + Windows Installer 3.1 + true + + + + + + + + + + + + + \ No newline at end of file diff --git a/CSharpWavPackDecoder.csproj.user b/CSharpWavPackDecoder.csproj.user new file mode 100644 index 0000000..594343a --- /dev/null +++ b/CSharpWavPackDecoder.csproj.user @@ -0,0 +1,17 @@ + + + C:\dev\C#\publish\|publish\ + + + + + + + + + + + en-US + false + + \ No newline at end of file diff --git a/CSharpWavPackDecoder.exe.manifest b/CSharpWavPackDecoder.exe.manifest new file mode 100644 index 0000000..f1bca70 --- /dev/null +++ b/CSharpWavPackDecoder.exe.manifest @@ -0,0 +1,93 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + H04RxRl+9fm2DSKiELz8SfmBgxU= + + + + + + + + + + idc0ol+163U8h5M8uDkvPVFevxo= + + + + + + + + + DIsPjoQAQIUi/WjUw9754NE6di4= + + + \ No newline at end of file diff --git a/CSharpWavPackDecoder.sln b/CSharpWavPackDecoder.sln new file mode 100644 index 0000000..f91c5f4 --- /dev/null +++ b/CSharpWavPackDecoder.sln @@ -0,0 +1,20 @@ + +Microsoft Visual Studio Solution File, Format Version 10.00 +# Visual C# Express 2008 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CSharpWavPackDecoder", "CSharpWavPackDecoder.csproj", "{04F62C81-761F-4906-9C0A-B56FDC5AA1BF}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {04F62C81-761F-4906-9C0A-B56FDC5AA1BF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {04F62C81-761F-4906-9C0A-B56FDC5AA1BF}.Debug|Any CPU.Build.0 = Debug|Any CPU + {04F62C81-761F-4906-9C0A-B56FDC5AA1BF}.Release|Any CPU.ActiveCfg = Release|Any CPU + {04F62C81-761F-4906-9C0A-B56FDC5AA1BF}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/CSharpWavPackDecoder.userprefs b/CSharpWavPackDecoder.userprefs new file mode 100644 index 0000000..3deaeba --- /dev/null +++ b/CSharpWavPackDecoder.userprefs @@ -0,0 +1,24 @@ + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/ChunkHeader.cs b/ChunkHeader.cs new file mode 100644 index 0000000..98c40f8 --- /dev/null +++ b/ChunkHeader.cs @@ -0,0 +1,15 @@ +using System; +/* +** ChunkHeader.cs +** +** Copyright (c) 2010-2016 Peter McQuillan +** +** All Rights Reserved. +** +** Distributed under the BSD Software License (see license.txt) */ + +class ChunkHeader +{ + internal char[] ckID = new char[4]; + internal long ckSize; // was uint32_t in C +} diff --git a/Defines.cs b/Defines.cs new file mode 100644 index 0000000..08a5ed3 --- /dev/null +++ b/Defines.cs @@ -0,0 +1,129 @@ +using System; +/* +** Defines.cs +** +** Copyright (c) 2010-2016 Peter McQuillan +** +** All Rights Reserved. +** +** Distributed under the BSD Software License (see license.txt) +***/ + +public class Defines +{ + // Change the following value to an even number to reflect the maximum number of samples to be processed + // per call to WavPackUtils.WavpackUnpackSamples + + internal static int SAMPLE_BUFFER_SIZE = 256; + + internal static int BITSTREAM_BUFFER_SIZE = 1024; + + internal static int FALSE = 0; + internal static int TRUE = 1; + + // or-values for "flags" + + + internal static int BYTES_STORED = 3; // 1-4 bytes/sample + internal static int MONO_FLAG = 4; // not stereo + internal static int HYBRID_FLAG = 8; // hybrid mode + internal static int FALSE_STEREO = 0x40000000; // block is stereo, but data is mono + + internal static int SHIFT_LSB = 13; + internal static long SHIFT_MASK = (0x1fL << SHIFT_LSB); + + internal static int FLOAT_DATA = 0x80; // ieee 32-bit floating point data + + internal static int SRATE_LSB = 23; + internal static long SRATE_MASK = (0xfL << SRATE_LSB); + + internal static int FINAL_BLOCK = 0x1000; // final block of multichannel segment + + + internal static int MIN_STREAM_VERS = 0x402; // lowest stream version we'll decode + internal static int MAX_STREAM_VERS = 0x410; // highest stream version we'll decode + + + internal const short ID_DUMMY = (short) (0x0); + internal static short ID_ENCODER_INFO = (short) (0x1); + internal const short ID_DECORR_TERMS = (short) (0x2); + internal const short ID_DECORR_WEIGHTS = (short) (0x3); + internal const short ID_DECORR_SAMPLES = (short) (0x4); + internal const short ID_ENTROPY_VARS = (short) (0x5); + internal const short ID_HYBRID_PROFILE = (short) (0x6); + internal const short ID_SHAPING_WEIGHTS = (short) (0x7); + internal const short ID_FLOAT_INFO = (short) (0x8); + internal const short ID_INT32_INFO = (short) (0x9); + internal const short ID_WV_BITSTREAM = (short) (0xa); + internal const short ID_WVC_BITSTREAM = (short) (0xb); + internal const short ID_WVX_BITSTREAM = (short) (0xc); + internal const short ID_CHANNEL_INFO = (short) (0xd); + + internal static int JOINT_STEREO = 0x10; // joint stereo + internal static int CROSS_DECORR = 0x20; // no-delay cross decorrelation + internal static int HYBRID_SHAPE = 0x40; // noise shape (hybrid mode only) + + internal static int INT32_DATA = 0x100; // special extended int handling + internal static int HYBRID_BITRATE = 0x200; // bitrate noise (hybrid mode only) + internal static int HYBRID_BALANCE = 0x400; // balance noise (hybrid stereo mode only) + + internal static int INITIAL_BLOCK = 0x800; // initial block of multichannel segment + + internal static int FLOAT_SHIFT_ONES = 1; // bits left-shifted into float = '1' + internal static int FLOAT_SHIFT_SAME = 2; // bits left-shifted into float are the same + internal static int FLOAT_SHIFT_SENT = 4; // bits shifted into float are sent literally + internal static int FLOAT_ZEROS_SENT = 8; // "zeros" are not all real zeros + internal static int FLOAT_NEG_ZEROS = 0x10; // contains negative zeros + internal static int FLOAT_EXCEPTIONS = 0x20; // contains exceptions (inf, nan, etc.) + + + internal static short ID_OPTIONAL_DATA = (short) (0x20); + internal static int ID_ODD_SIZE = 0x40; + internal static int ID_LARGE = 0x80; + + internal static int MAX_NTERMS = 16; + internal static int MAX_TERM = 8; + + internal static int MAG_LSB = 18; + internal static long MAG_MASK = (0x1fL << MAG_LSB); + + internal const short ID_RIFF_HEADER = (short) (0x21); + internal const short ID_RIFF_TRAILER = (short) (0x22); + internal const short ID_REPLAY_GAIN = (short) (0x23); + internal const short ID_CUESHEET = (short) (0x24); + internal const short ID_CONFIG_BLOCK = (short) (0x25); + internal const short ID_MD5_CHECKSUM = (short) (0x26); + internal const short ID_SAMPLE_RATE = (short) (0x27); + + internal static long CONFIG_BYTES_STORED = 3; // 1-4 bytes/sample + internal static long CONFIG_MONO_FLAG = 4; // not stereo + internal static long CONFIG_HYBRID_FLAG = 8; // hybrid mode + internal static long CONFIG_JOINT_STEREO = 0x10; // joint stereo + internal static long CONFIG_CROSS_DECORR = 0x20; // no-delay cross decorrelation + internal static long CONFIG_HYBRID_SHAPE = 0x40; // noise shape (hybrid mode only) + internal static long CONFIG_FLOAT_DATA = 0x80; // ieee 32-bit floating point data + internal static long CONFIG_FAST_FLAG = 0x200; // fast mode + internal static long CONFIG_HIGH_FLAG = 0x800; // high quality mode + internal static long CONFIG_VERY_HIGH_FLAG = 0x1000; // very high + internal static long CONFIG_BITRATE_KBPS = 0x2000; // bitrate is kbps, not bits / sample + internal static long CONFIG_AUTO_SHAPING = 0x4000; // automatic noise shaping + internal static long CONFIG_SHAPE_OVERRIDE = 0x8000; // shaping mode specified + internal static long CONFIG_JOINT_OVERRIDE = 0x10000; // joint-stereo mode specified + internal static long CONFIG_CREATE_EXE = 0x40000; // create executable + internal static long CONFIG_CREATE_WVC = 0x80000; // create correction file + internal static long CONFIG_OPTIMIZE_WVC = 0x100000; // maximize bybrid compression + internal static long CONFIG_CALC_NOISE = 0x800000; // calc noise in hybrid mode + internal static long CONFIG_LOSSY_MODE = 0x1000000; // obsolete (for information) + internal static long CONFIG_EXTRA_MODE = 0x2000000; // extra processing mode + internal static long CONFIG_SKIP_WVX = 0x4000000; // no wvx stream w/ floats & big ints + internal static long CONFIG_MD5_CHECKSUM = 0x8000000; // compute & store MD5 signature + internal static long CONFIG_OPTIMIZE_MONO = 0x80000000; // optimize for mono streams posing as stereo + + internal static int MODE_WVC = 0x1; + internal static int MODE_LOSSLESS = 0x2; + internal static int MODE_HYBRID = 0x4; + internal static int MODE_FLOAT = 0x8; + internal static int MODE_VALID_TAG = 0x10; + internal static int MODE_HIGH = 0x20; + internal static int MODE_FAST = 0x40; +} diff --git a/FloatUtils.cs b/FloatUtils.cs new file mode 100644 index 0000000..96e6d1a --- /dev/null +++ b/FloatUtils.cs @@ -0,0 +1,66 @@ +using System; +/* +** FloatUtils.cs +** +** Copyright (c) 2010-2016 Peter McQuillan +** +** All Rights Reserved. +** +** Distributed under the BSD Software License (see license.txt) +***/ + +class FloatUtils +{ + + + internal static int read_float_info(WavpackStream wps, WavpackMetadata wpmd) + { + int bytecnt = wpmd.byte_length; + byte[] byteptr = wpmd.data; + int counter = 0; + + + if (bytecnt != 4) + return Defines.FALSE; + + wps.float_flags = byteptr[counter]; + counter++; + wps.float_shift = byteptr[counter]; + counter++; + wps.float_max_exp = byteptr[counter]; + counter++; + wps.float_norm_exp = byteptr[counter]; + + return Defines.TRUE; + } + + + internal static int[] float_values(WavpackStream wps, int[] values, long num_values, int bufferStartPos) + { + int shift = wps.float_max_exp - wps.float_norm_exp + wps.float_shift; + int value_counter = bufferStartPos; + + if (shift > 32) + shift = 32; + else if (shift < - 32) + shift = - 32; + + while (num_values > 0) + { + if (shift > 0) + values[value_counter] <<= shift; + else if (shift < 0) + values[value_counter] >>= - shift; + + if (values[value_counter] > 8388607L) + values[value_counter] = (int) SupportClass.Identity(8388607L); + else if (values[value_counter] < - 8388608L) + values[value_counter] = (int) SupportClass.Identity(- 8388608L); + + value_counter++; + num_values--; + } + + return values; + } +} diff --git a/MetadataUtils.cs b/MetadataUtils.cs new file mode 100644 index 0000000..823cdfd --- /dev/null +++ b/MetadataUtils.cs @@ -0,0 +1,241 @@ +using System; +/* +** MetadataUtils.cs +** +** Copyright (c) 2010-2016 Peter McQuillan +** +** All Rights Reserved. +** +** Distributed under the BSD Software License (see license.txt) +***/ + +class MetadataUtils +{ + internal static int read_metadata_buff(WavpackContext wpc, WavpackMetadata wpmd) + { + long bytes_to_read; + short tchar; + + if (wpmd.bytecount >= wpc.stream.wphdr.ckSize) + { + // we have read all the data in this block + return Defines.FALSE; + } + + try + { + wpmd.id = (short) wpc.infile.ReadByte(); + tchar = (short) wpc.infile.ReadByte(); + } + catch (System.Exception) + { + wpmd.status = 1; + return Defines.FALSE; + } + + wpmd.bytecount += 2; + + wpmd.byte_length = tchar << 1; + + if ((wpmd.id & Defines.ID_LARGE) != 0) + { + wpmd.id &= (short)~ Defines.ID_LARGE; + + try + { + tchar = (short) wpc.infile.ReadByte(); + } + catch (System.Exception) + { + wpmd.status = 1; + return Defines.FALSE; + } + + wpmd.byte_length += ((int) tchar << 9); + + try + { + tchar = (short) wpc.infile.ReadByte(); + } + catch (System.Exception) + { + wpmd.status = 1; + return Defines.FALSE; + } + + wpmd.byte_length += ((int) tchar << 17); + wpmd.bytecount += 2; + } + + if ((wpmd.id & Defines.ID_ODD_SIZE) != 0) + { + wpmd.id &= (short)~ Defines.ID_ODD_SIZE; + wpmd.byte_length--; + } + + if (wpmd.byte_length == 0 || wpmd.id == Defines.ID_WV_BITSTREAM) + { + wpmd.hasdata = Defines.FALSE; + return Defines.TRUE; + } + + bytes_to_read = wpmd.byte_length + (wpmd.byte_length & 1); + + wpmd.bytecount += bytes_to_read; + + if (bytes_to_read > wpc.read_buffer.Length) + { + int bytes_read; + wpmd.hasdata = Defines.FALSE; + + while (bytes_to_read > wpc.read_buffer.Length) + { + try + { + bytes_read = wpc.infile.BaseStream.Read(wpc.read_buffer, 0, wpc.read_buffer.Length); + + if (bytes_read != wpc.read_buffer.Length) + { + return Defines.FALSE; + } + } + catch (System.Exception) + { + return Defines.FALSE; + } + bytes_to_read -= wpc.read_buffer.Length; + } + } + else + { + wpmd.hasdata = Defines.TRUE; + wpmd.data = wpc.read_buffer; + } + + if (bytes_to_read != 0) + { + int bytes_read; + + try + { + bytes_read = wpc.infile.BaseStream.Read(wpc.read_buffer, 0, (int)bytes_to_read); + + if (bytes_read != (int) bytes_to_read) + { + wpmd.hasdata = Defines.FALSE; + return Defines.FALSE; + } + } + catch (System.Exception) + { + wpmd.hasdata = Defines.FALSE; + return Defines.FALSE; + } + } + + return Defines.TRUE; + } + + internal static int process_metadata(WavpackContext wpc, WavpackMetadata wpmd) + { + WavpackStream wps = wpc.stream; + + switch (wpmd.id) + { + + case Defines.ID_DUMMY: + { + return Defines.TRUE; + } + + + case Defines.ID_DECORR_TERMS: + { + return UnpackUtils.read_decorr_terms(wps, wpmd); + } + + + case Defines.ID_DECORR_WEIGHTS: + { + return UnpackUtils.read_decorr_weights(wps, wpmd); + } + + + case Defines.ID_DECORR_SAMPLES: + { + return UnpackUtils.read_decorr_samples(wps, wpmd); + } + + + case Defines.ID_ENTROPY_VARS: + { + return WordsUtils.read_entropy_vars(wps, wpmd); + } + + + case Defines.ID_HYBRID_PROFILE: + { + return WordsUtils.read_hybrid_profile(wps, wpmd); + } + + + case Defines.ID_FLOAT_INFO: + { + return FloatUtils.read_float_info(wps, wpmd); + } + + + case Defines.ID_INT32_INFO: + { + return UnpackUtils.read_int32_info(wps, wpmd); + } + + + case Defines.ID_CHANNEL_INFO: + { + return UnpackUtils.read_channel_info(wpc, wpmd); + } + + + case Defines.ID_SAMPLE_RATE: + { + return UnpackUtils.read_sample_rate(wpc, wpmd); + } + + + case Defines.ID_CONFIG_BLOCK: + { + return UnpackUtils.read_config_info(wpc, wpmd); + } + + + case Defines.ID_WV_BITSTREAM: + { + return UnpackUtils.init_wv_bitstream(wpc, wpmd); + } + + + case Defines.ID_SHAPING_WEIGHTS: + case Defines.ID_WVC_BITSTREAM: + case Defines.ID_WVX_BITSTREAM: + { + return Defines.TRUE; + } + + + default: + { + if ((wpmd.id & Defines.ID_OPTIONAL_DATA) != 0) + { + return Defines.TRUE; + } + else + { + return Defines.FALSE; + } + } + break; + + } + } +} diff --git a/RiffChunkHeader.cs b/RiffChunkHeader.cs new file mode 100644 index 0000000..35357ba --- /dev/null +++ b/RiffChunkHeader.cs @@ -0,0 +1,17 @@ +using System; +/* +** RiffChunkHeader.cs +** +** Copyright (c) 2010-2016 Peter McQuillan +** +** All Rights Reserved. +** +** Distributed under the BSD Software License (see license.txt) +***/ + +class RiffChunkHeader +{ + internal char[] ckID = new char[4]; + internal long ckSize; // was uint32_t in C + internal char[] formType = new char[4]; +} diff --git a/SupportClass.cs b/SupportClass.cs new file mode 100644 index 0000000..5c5b6cf --- /dev/null +++ b/SupportClass.cs @@ -0,0 +1,182 @@ +// +// In order to convert some functionality to Visual C#, the Java Language Conversion Assistant +// creates "support classes" that duplicate the original functionality. +// +// Support classes replicate the functionality of the original code, but in some cases they are +// substantially different architecturally. Although every effort is made to preserve the +// original architecture of the application in the converted project, the user should be aware that +// the primary goal of these support classes is to replicate functionality, and that at times +// the architecture of the resulting solution may differ somewhat. +// + +using System; + +/// +/// Contains conversion support elements such as classes, interfaces and static methods. +/// +public class SupportClass +{ + + /*******************************/ + /// + /// This method returns the literal value received + /// + /// The literal to return + /// The received value + public static long Identity(long literal) + { + return literal; + } + + /// + /// This method returns the literal value received + /// + /// The literal to return + /// The received value + public static ulong Identity(ulong literal) + { + return literal; + } + + /// + /// This method returns the literal value received + /// + /// The literal to return + /// The received value + public static float Identity(float literal) + { + return literal; + } + + /// + /// This method returns the literal value received + /// + /// The literal to return + /// The received value + public static double Identity(double literal) + { + return literal; + } + + /*******************************/ + /// + /// Performs an unsigned bitwise right shift with the specified number + /// + /// Number to operate on + /// Ammount of bits to shift + /// The resulting number from the shift operation + public static int URShift(int number, int bits) + { + if ( number >= 0) + return number >> bits; + else + return (number >> bits) + (2 << ~bits); + } + + /// + /// Performs an unsigned bitwise right shift with the specified number + /// + /// Number to operate on + /// Ammount of bits to shift + /// The resulting number from the shift operation + public static int URShift(int number, long bits) + { + return URShift(number, (int)bits); + } + + /// + /// Performs an unsigned bitwise right shift with the specified number + /// + /// Number to operate on + /// Ammount of bits to shift + /// The resulting number from the shift operation + public static long URShift(long number, int bits) + { + if ( number >= 0) + return number >> bits; + else + return (number >> bits) + (2L << ~bits); + } + + /// + /// Performs an unsigned bitwise right shift with the specified number + /// + /// Number to operate on + /// Ammount of bits to shift + /// The resulting number from the shift operation + public static long URShift(long number, long bits) + { + return URShift(number, (int)bits); + } + + /*******************************/ + /// + /// Converts an array of sbytes to an array of bytes + /// + /// The array of sbytes to be converted + /// The new array of bytes + public static byte[] ToByteArray(sbyte[] sbyteArray) + { + byte[] byteArray = null; + + if (sbyteArray != null) + { + byteArray = new byte[sbyteArray.Length]; + for(int index=0; index < sbyteArray.Length; index++) + byteArray[index] = (byte) sbyteArray[index]; + } + return byteArray; + } + + /// + /// Converts a string to an array of bytes + /// + /// The string to be converted + /// The new array of bytes + public static byte[] ToByteArray(System.String sourceString) + { + return System.Text.UTF8Encoding.UTF8.GetBytes(sourceString); + } + + /// + /// Converts a array of object-type instances to a byte-type array. + /// + /// Array to convert. + /// An array of byte type elements. + public static byte[] ToByteArray(System.Object[] tempObjectArray) + { + byte[] byteArray = null; + if (tempObjectArray != null) + { + byteArray = new byte[tempObjectArray.Length]; + for (int index = 0; index < tempObjectArray.Length; index++) + byteArray[index] = (byte)tempObjectArray[index]; + } + return byteArray; + } + + /*******************************/ + /// + /// Write an array of bytes int the FileStream specified. + /// + /// FileStream that must be updated. + /// Array of bytes that must be written in the FileStream. + public static void WriteOutput(System.IO.FileStream FileStreamWrite, sbyte[] Source) + { + FileStreamWrite.Write(ToByteArray(Source), 0, Source.Length); + } + + + /*******************************/ + /// + /// Writes the exception stack trace to the received stream + /// + /// Exception to obtain information from + /// Output sream used to write to + public static void WriteStackTrace(System.Exception throwable, System.IO.TextWriter stream) + { + stream.Write(throwable.StackTrace); + stream.Flush(); + } + +} diff --git a/UnpackUtils.cs b/UnpackUtils.cs new file mode 100644 index 0000000..8de4379 --- /dev/null +++ b/UnpackUtils.cs @@ -0,0 +1,1587 @@ +using System; +/* +** UnpackUtils.cs +** +** Copyright (c) 2010-2016 Peter McQuillan +** +** All Rights Reserved. +** +** Distributed under the BSD Software License (see license.txt) +***/ + +class UnpackUtils +{ + + ///////////////////////////// executable code //////////////////////////////// + + // This function initializes everything required to unpack a WavPack block + // and must be called before unpack_samples() is called to obtain audio data. + // It is assumed that the WavpackHeader has been read into the wps.wphdr + // (in the current WavpackStream). This is where all the metadata blocks are + // scanned up to the one containing the audio bitstream. + + internal static int unpack_init(WavpackContext wpc) + { + WavpackStream wps = wpc.stream; + WavpackMetadata wpmd = new WavpackMetadata(); + + if (wps.wphdr.block_samples > 0 && wps.wphdr.block_index != - 1) + wps.sample_index = wps.wphdr.block_index; + + wps.mute_error = 0; + wps.crc = 0xffffffff; + wps.wvbits.sr = 0; + + while ((MetadataUtils.read_metadata_buff(wpc, wpmd)) == Defines.TRUE) + { + if ((MetadataUtils.process_metadata(wpc, wpmd)) == Defines.FALSE) + { + wpc.error = true; + wpc.error_message = "invalid metadata!"; + return Defines.FALSE; + } + + if (wpmd.id == Defines.ID_WV_BITSTREAM) + break; + } + + if (wps.wphdr.block_samples != 0 && (null == wps.wvbits.file)) + { + wpc.error_message = "invalid WavPack file!"; + wpc.error = true; + return Defines.FALSE; + } + + + if (wps.wphdr.block_samples != 0) + { + if ((wps.wphdr.flags & Defines.INT32_DATA) != 0 && wps.int32_sent_bits != 0) + wpc.lossy_blocks = 1; + + if ((wps.wphdr.flags & Defines.FLOAT_DATA) != 0 && (wps.float_flags & (Defines.FLOAT_EXCEPTIONS | Defines.FLOAT_ZEROS_SENT | Defines.FLOAT_SHIFT_SENT | Defines.FLOAT_SHIFT_SAME)) != 0) + wpc.lossy_blocks = 1; + } + + wpc.error = false; + wpc.stream = wps; + return Defines.TRUE; + } + + // This function initialzes the main bitstream for audio samples, which must + // be in the "wv" file. + + internal static int init_wv_bitstream(WavpackContext wpc, WavpackMetadata wpmd) + { + WavpackStream wps = wpc.stream; + + if (wpmd.hasdata == Defines.TRUE) + wps.wvbits = BitsUtils.bs_open_read(wpmd.data, (short) 0, (short) wpmd.byte_length, wpc.infile, 0, 0); + else if (wpmd.byte_length > 0) + { + int len = wpmd.byte_length & 1; + wps.wvbits = BitsUtils.bs_open_read(wpc.read_buffer, (short) (- 1), (short) wpc.read_buffer.Length, wpc.infile, (wpmd.byte_length + len), 1); + } + + return Defines.TRUE; + } + + + // Read decorrelation terms from specified metadata block into the + // decorr_passes array. The terms range from -3 to 8, plus 17 & 18; + // other values are reserved and generate errors for now. The delta + // ranges from 0 to 7 with all values valid. Note that the terms are + // stored in the opposite order in the decorr_passes array compared + // to packing. + + internal static int read_decorr_terms(WavpackStream wps, WavpackMetadata wpmd) + { + int termcnt = wpmd.byte_length; + byte[] byteptr = wpmd.data; + WavpackStream tmpwps = new WavpackStream(); + + int counter = 0; + int dcounter = 0; + + if (termcnt > Defines.MAX_NTERMS) + return Defines.FALSE; + + tmpwps.num_terms = termcnt; + + dcounter = termcnt - 1; + + for (dcounter = termcnt - 1; dcounter >= 0; dcounter--) + { + tmpwps.decorr_passes[dcounter].term = (short) ((int) (byteptr[counter] & 0x1f) - 5); + tmpwps.decorr_passes[dcounter].delta = (short) ((byteptr[counter] >> 5) & 0x7); + + counter++; + + if (tmpwps.decorr_passes[dcounter].term < - 3 || (tmpwps.decorr_passes[dcounter].term > Defines.MAX_TERM && tmpwps.decorr_passes[dcounter].term < 17) || tmpwps.decorr_passes[dcounter].term > 18) + return Defines.FALSE; + } + + wps.decorr_passes = tmpwps.decorr_passes; + wps.num_terms = tmpwps.num_terms; + + return Defines.TRUE; + } + + + // Read decorrelation weights from specified metadata block into the + // decorr_passes array. The weights range +/-1024, but are rounded and + // truncated to fit in signed chars for metadata storage. Weights are + // separate for the two channels and are specified from the "last" term + // (first during encode). Unspecified weights are set to zero. + + internal static int read_decorr_weights(WavpackStream wps, WavpackMetadata wpmd) + { + int termcnt = wpmd.byte_length, tcount; + byte[] byteptr = wpmd.data; + decorr_pass dpp = new decorr_pass(); + int counter = 0; + int dpp_idx; + int myiterator = 0; + + if ((wps.wphdr.flags & (Defines.MONO_FLAG | Defines.FALSE_STEREO)) == 0) + termcnt /= 2; + + if (termcnt > wps.num_terms) + { + return Defines.FALSE; + } + + for (tcount = wps.num_terms; tcount > 0; tcount--) + dpp.weight_A = dpp.weight_B = 0; + + myiterator = wps.num_terms; + + while (termcnt > 0) + { + dpp_idx = myiterator - 1; + dpp.weight_A = (short) WordsUtils.restore_weight((sbyte) byteptr[counter]); + + wps.decorr_passes[dpp_idx].weight_A = dpp.weight_A; + + counter++; + + if ((wps.wphdr.flags & (Defines.MONO_FLAG | Defines.FALSE_STEREO)) == 0) + { + dpp.weight_B = (short) WordsUtils.restore_weight((sbyte) byteptr[counter]); + counter++; + } + wps.decorr_passes[dpp_idx].weight_B = dpp.weight_B; + + myiterator--; + termcnt--; + } + + return Defines.TRUE; + } + + + // Read decorrelation samples from specified metadata block into the + // decorr_passes array. The samples are signed 32-bit values, but are + // converted to signed log2 values for storage in metadata. Values are + // stored for both channels and are specified from the "last" term + // (first during encode) with unspecified samples set to zero. The + // number of samples stored varies with the actual term value, so + // those must obviously come first in the metadata. + + internal static int read_decorr_samples(WavpackStream wps, WavpackMetadata wpmd) + { + byte[] byteptr = wpmd.data; + decorr_pass dpp = new decorr_pass(); + int tcount; + int counter = 0; + int dpp_index = 0; + int uns_buf0, uns_buf1, uns_buf2, uns_buf3; + int sample_counter = 0; + + dpp_index = 0; + + for (tcount = wps.num_terms; tcount > 0; tcount--) + { + dpp.term = wps.decorr_passes[dpp_index].term; + + for (int internalc = 0; internalc < Defines.MAX_TERM; internalc++) + { + dpp.samples_A[internalc] = 0; + dpp.samples_B[internalc] = 0; + wps.decorr_passes[dpp_index].samples_A[internalc] = 0; + wps.decorr_passes[dpp_index].samples_B[internalc] = 0; + } + + dpp_index++; + } + + if (wps.wphdr.version == 0x402 && (wps.wphdr.flags & Defines.HYBRID_FLAG) > 0) + { + counter += 2; + + if ((wps.wphdr.flags & (Defines.MONO_FLAG | Defines.FALSE_STEREO)) == 0) + counter += 2; + } + + dpp_index--; + + while (counter < wpmd.byte_length) + { + if (dpp.term > Defines.MAX_TERM) + { + uns_buf0 = (int) (byteptr[counter] & 0xff); + uns_buf1 = (int) (byteptr[counter + 1] & 0xff); + uns_buf2 = (int) (byteptr[counter + 2] & 0xff); + uns_buf3 = (int) (byteptr[counter + 3] & 0xff); + + dpp.samples_A[0] = WordsUtils.exp2s((short) (uns_buf0 + (uns_buf1 << 8))); + dpp.samples_A[1] = WordsUtils.exp2s((short) (uns_buf2 + (uns_buf3 << 8))); + counter += 4; + + if ((wps.wphdr.flags & (Defines.MONO_FLAG | Defines.FALSE_STEREO)) == 0) + { + + uns_buf0 = (int) (byteptr[counter] & 0xff); + uns_buf1 = (int) (byteptr[counter + 1] & 0xff); + uns_buf2 = (int) (byteptr[counter + 2] & 0xff); + uns_buf3 = (int) (byteptr[counter + 3] & 0xff); + + dpp.samples_B[0] = WordsUtils.exp2s((short) (uns_buf0 + (uns_buf1 << 8))); + dpp.samples_B[1] = WordsUtils.exp2s((short) (uns_buf2 + (uns_buf3 << 8))); + counter += 4; + } + } + else if (dpp.term < 0) + { + + uns_buf0 = (int) (byteptr[counter] & 0xff); + uns_buf1 = (int) (byteptr[counter + 1] & 0xff); + uns_buf2 = (int) (byteptr[counter + 2] & 0xff); + uns_buf3 = (int) (byteptr[counter + 3] & 0xff); + + dpp.samples_A[0] = WordsUtils.exp2s((short) (uns_buf0 + (uns_buf1 << 8))); + dpp.samples_B[0] = WordsUtils.exp2s((short) (uns_buf2 + (uns_buf3 << 8))); + + counter += 4; + } + else + { + int m = 0, cnt = dpp.term; + + while (cnt > 0) + { + uns_buf0 = (int) (byteptr[counter] & 0xff); + uns_buf1 = (int) (byteptr[counter + 1] & 0xff); + + dpp.samples_A[m] = WordsUtils.exp2s((short) (uns_buf0 + (uns_buf1 << 8))); + counter += 2; + + if ((wps.wphdr.flags & (Defines.MONO_FLAG | Defines.FALSE_STEREO)) == 0) + { + uns_buf0 = (int) (byteptr[counter] & 0xff); + uns_buf1 = (int) (byteptr[counter + 1] & 0xff); + dpp.samples_B[m] = WordsUtils.exp2s((short) (uns_buf0 + (uns_buf1 << 8))); + counter += 2; + } + + m++; + cnt--; + } + } + + for (sample_counter = 0; sample_counter < Defines.MAX_TERM; sample_counter++) + { + wps.decorr_passes[dpp_index].samples_A[sample_counter] = dpp.samples_A[sample_counter]; + wps.decorr_passes[dpp_index].samples_B[sample_counter] = dpp.samples_B[sample_counter]; + } + + dpp_index--; + } + + return Defines.TRUE; + } + + + // Read the int32 data from the specified metadata into the specified stream. + // This data is used for integer data that has more than 24 bits of magnitude + // or, in some cases, used to eliminate redundant bits from any audio stream. + + internal static int read_int32_info(WavpackStream wps, WavpackMetadata wpmd) + { + int bytecnt = wpmd.byte_length; + byte[] byteptr = wpmd.data; + int counter = 0; + + if (bytecnt != 4) + return Defines.FALSE; // should also return 0 + + wps.int32_sent_bits = byteptr[counter]; + counter++; + wps.int32_zeros = byteptr[counter]; + counter++; + wps.int32_ones = byteptr[counter]; + counter++; + wps.int32_dups = byteptr[counter]; + + return Defines.TRUE; + } + + + // Read multichannel information from metadata. The first byte is the total + // number of channels and the following bytes represent the channel_mask + // as described for Microsoft WAVEFORMATEX. + + internal static int read_channel_info(WavpackContext wpc, WavpackMetadata wpmd) + { + int bytecnt = wpmd.byte_length, shift = 0; + byte[] byteptr = wpmd.data; + int counter = 0; + long mask = 0; + + if (bytecnt == 0 || bytecnt > 5) + return Defines.FALSE; + + wpc.config.num_channels = byteptr[counter]; + counter++; + + while (bytecnt >= 0) + { + mask |= (long) ((byteptr[counter] & 0xFF) << shift); + counter++; + shift += 8; + bytecnt--; + } + + wpc.config.channel_mask = mask; + return Defines.TRUE; + } + + + // Read configuration information from metadata. + + internal static int read_config_info(WavpackContext wpc, WavpackMetadata wpmd) + { + int bytecnt = wpmd.byte_length; + byte[] byteptr = wpmd.data; + int counter = 0; + + if (bytecnt >= 3) + { + wpc.config.flags &= 0xff; + wpc.config.flags |= (long) ((byteptr[counter] & 0xFF) << 8); + counter++; + wpc.config.flags |= (long) ((byteptr[counter] & 0xFF) << 16); + counter++; + wpc.config.flags |= (long) ((byteptr[counter] & 0xFF) << 24); + } + + return Defines.TRUE; + } + + // Read non-standard sampling rate from metadata. + + internal static int read_sample_rate(WavpackContext wpc, WavpackMetadata wpmd) + { + int bytecnt = wpmd.byte_length; + byte[] byteptr = wpmd.data; + int counter = 0; + + if (bytecnt == 3) + { + wpc.config.sample_rate = (long) (byteptr[counter] & 0xFF); + counter++; + wpc.config.sample_rate |= (long) ((byteptr[counter] & 0xFF) << 8); + counter++; + wpc.config.sample_rate |= (long) ((byteptr[counter] & 0xFF) << 16); + } + + return Defines.TRUE; + } + + + // This monster actually unpacks the WavPack bitstream(s) into the specified + // buffer as 32-bit integers or floats (depending on orignal data). Lossy + // samples will be clipped to their original limits (i.e. 8-bit samples are + // clipped to -128/+127) but are still returned in ints. It is up to the + // caller to potentially reformat this for the final output including any + // multichannel distribution, block alignment or endian compensation. The + // function unpack_init() must have been called and the entire WavPack block + // must still be visible (although wps.blockbuff will not be accessed again). + // For maximum clarity, the function is broken up into segments that handle + // various modes. This makes for a few extra infrequent flag checks, but + // makes the code easier to follow because the nesting does not become so + // deep. For maximum efficiency, the conversion is isolated to tight loops + // that handle an entire buffer. The function returns the total number of + // samples unpacked, which can be less than the number requested if an error + // occurs or the end of the block is reached. + + internal static long unpack_samples(WavpackContext wpc, int[] buffer, long sample_count, int bufferStartPos) + { + WavpackStream wps = wpc.stream; + long flags = wps.wphdr.flags; + long i; + int crc = (int) wps.crc; + + int mute_limit = (int) ((1L << (int) ((flags & Defines.MAG_MASK) >> Defines.MAG_LSB)) + 2); + decorr_pass dpp; + int tcount; + int buffer_counter = 0; + + int samples_processed = 0; + + if (wps.sample_index + sample_count > wps.wphdr.block_index + wps.wphdr.block_samples) + sample_count = wps.wphdr.block_index + wps.wphdr.block_samples - wps.sample_index; + + if (wps.mute_error > 0) + { + + long tempc; + + if ((flags & Defines.MONO_FLAG) > 0) + { + tempc = sample_count; + } + else + { + tempc = 2 * sample_count; + } + + buffer_counter = bufferStartPos; + while (tempc > 0) + { + buffer[buffer_counter] = 0; + tempc--; + buffer_counter++; + } + + wps.sample_index += sample_count; + + return sample_count; + } + + if ((flags & Defines.HYBRID_FLAG) > 0) + mute_limit *= 2; + + + ///////////////////// handle version 4 mono data ///////////////////////// + + if ((flags & (Defines.MONO_FLAG | Defines.FALSE_STEREO)) > 0) + { + + int dpp_index = 0; + + i = WordsUtils.get_words(sample_count, flags, wps.w, wps.wvbits, buffer, bufferStartPos); + + for (tcount = wps.num_terms - 1; tcount >= 0; tcount--) + { + dpp = wps.decorr_passes[dpp_index]; + decorr_mono_pass(dpp, buffer, sample_count, bufferStartPos); + dpp_index++; + } + + int bf_abs; + int crclimit = (int)(sample_count + bufferStartPos); + + for (int q = 0; q < crclimit; q++) + { + bf_abs = (buffer[q] < 0?- buffer[q]:buffer[q]); + + if (bf_abs > mute_limit) + { + i = q; + break; + } + crc = crc * 3 + buffer[q]; + } + } + //////////////////// handle version 4 stereo data //////////////////////// + else + { + samples_processed = WordsUtils.get_words(sample_count, flags, wps.w, wps.wvbits, buffer, bufferStartPos); + + i = samples_processed; + + if (sample_count < 16) + { + int dpp_index = 0; + + for (tcount = wps.num_terms - 1; tcount >= 0; tcount--) + { + dpp = wps.decorr_passes[dpp_index]; + decorr_stereo_pass(dpp, buffer, sample_count, bufferStartPos); + wps.decorr_passes[dpp_index] = dpp; + dpp_index++; + } + } + else + { + int dpp_index = 0; + + for (tcount = wps.num_terms - 1; tcount >= 0; tcount--) + { + dpp = wps.decorr_passes[dpp_index]; + + decorr_stereo_pass(dpp, buffer, 8, bufferStartPos); + + decorr_stereo_pass_cont(dpp, buffer, sample_count - 8, bufferStartPos + 16); + + wps.decorr_passes[dpp_index] = dpp; + + dpp_index++; + } + } + + if ((flags & Defines.JOINT_STEREO) > 0) + { + int bf_abs, bf1_abs; + + for (buffer_counter = 0; buffer_counter < sample_count * 2; buffer_counter += 2) + { + buffer[buffer_counter + bufferStartPos] += (buffer[buffer_counter + 1 + bufferStartPos] -= (buffer[buffer_counter + bufferStartPos] >> 1)); + + bf_abs = (buffer[buffer_counter + bufferStartPos] < 0?- buffer[buffer_counter + bufferStartPos]:buffer[buffer_counter + bufferStartPos]); + bf1_abs = (buffer[buffer_counter + 1 + bufferStartPos] < 0?- buffer[buffer_counter + 1 + bufferStartPos]:buffer[buffer_counter + 1 + bufferStartPos]); + + if (bf_abs > mute_limit || bf1_abs > mute_limit) + { + i = buffer_counter / 2; + break; + } + + crc = (crc * 3 + buffer[buffer_counter + bufferStartPos]) * 3 + buffer[buffer_counter + 1 + bufferStartPos]; + } + } + else + { + int bf_abs, bf1_abs; + + for (buffer_counter = 0; buffer_counter < sample_count * 2; buffer_counter += 2) + { + bf_abs = (buffer[buffer_counter + bufferStartPos] < 0?- buffer[buffer_counter + bufferStartPos]:buffer[buffer_counter + bufferStartPos]); + bf1_abs = (buffer[buffer_counter + 1 + bufferStartPos] < 0?- buffer[buffer_counter + 1 + bufferStartPos]:buffer[buffer_counter + 1 + bufferStartPos]); + + if (bf_abs > mute_limit || bf1_abs > mute_limit) + { + i = buffer_counter / 2; + break; + } + + crc = (crc * 3 + buffer[buffer_counter + bufferStartPos]) * 3 + buffer[buffer_counter + 1 + bufferStartPos]; + } + } + } + + if (i != sample_count) + { + long sc = 0; + + if ((flags & Defines.MONO_FLAG) > 0) + { + sc = sample_count; + } + else + { + sc = 2 * sample_count; + } + buffer_counter = bufferStartPos; + + while (sc > 0) + { + buffer[buffer_counter] = 0; + sc--; + buffer_counter++; + } + + wps.mute_error = 1; + i = sample_count; + } + + buffer = fixup_samples(wps, buffer, i, bufferStartPos); + + if ((flags & Defines.FALSE_STEREO) > 0) + { + int dest_idx = (int) i * 2; + int src_idx = (int) i; + int c = (int) i; + + dest_idx--; + src_idx--; + + while (c > 0) + { + buffer[dest_idx + bufferStartPos] = buffer[src_idx + bufferStartPos]; + dest_idx--; + buffer[dest_idx + bufferStartPos] = buffer[src_idx + bufferStartPos]; + dest_idx--; + src_idx--; + c--; + } + } + + wps.sample_index += i; + wps.crc = crc; + + return i; + } + + internal static void decorr_stereo_pass(decorr_pass dpp, int[] buffer, long sample_count, int buf_idx) + { + int delta = dpp.delta; + int weight_A = dpp.weight_A; + int weight_B = dpp.weight_B; + int sam_A, sam_B; + int m, k; + int bptr_counter = 0; + + switch (dpp.term) + { + + case 17: + for (bptr_counter = buf_idx; bptr_counter < buf_idx + sample_count * 2; bptr_counter += 2) + { + sam_A = 2 * dpp.samples_A[0] - dpp.samples_A[1]; + dpp.samples_A[1] = dpp.samples_A[0]; + dpp.samples_A[0] = (int) ((weight_A * (long) sam_A + 512) >> 10) + buffer[bptr_counter]; + + if (sam_A != 0 && buffer[bptr_counter] != 0) + { + if ((sam_A ^ buffer[bptr_counter]) < 0) + { + weight_A = weight_A - delta; + } + else + { + weight_A = weight_A + delta; + } + } + + buffer[bptr_counter] = dpp.samples_A[0]; + + sam_A = 2 * dpp.samples_B[0] - dpp.samples_B[1]; + dpp.samples_B[1] = dpp.samples_B[0]; + dpp.samples_B[0] = (int) ((weight_B * (long) sam_A + 512) >> 10) + buffer[bptr_counter + 1]; + + if (sam_A != 0 && buffer[bptr_counter + 1] != 0) + { + if ((sam_A ^ buffer[bptr_counter + 1]) < 0) + { + weight_B = weight_B - delta; + } + else + { + weight_B = weight_B + delta; + } + } + + buffer[bptr_counter + 1] = dpp.samples_B[0]; + } + + break; + + + case 18: + for (bptr_counter = buf_idx; bptr_counter < buf_idx + sample_count * 2; bptr_counter += 2) + { + + sam_A = (3 * dpp.samples_A[0] - dpp.samples_A[1]) >> 1; + dpp.samples_A[1] = dpp.samples_A[0]; + dpp.samples_A[0] = (int) ((weight_A * (long) sam_A + 512) >> 10) + buffer[bptr_counter]; + + if (sam_A != 0 && buffer[bptr_counter] != 0) + { + if ((sam_A ^ buffer[bptr_counter]) < 0) + { + weight_A = weight_A - delta; + } + else + { + weight_A = weight_A + delta; + } + } + + buffer[bptr_counter] = dpp.samples_A[0]; + + sam_A = (3 * dpp.samples_B[0] - dpp.samples_B[1]) >> 1; + dpp.samples_B[1] = dpp.samples_B[0]; + dpp.samples_B[0] = (int) ((weight_B * (long) sam_A + 512) >> 10) + buffer[bptr_counter + 1]; + + if (sam_A != 0 && buffer[bptr_counter + 1] != 0) + { + if ((sam_A ^ buffer[bptr_counter + 1]) < 0) + { + weight_B = weight_B - delta; + } + else + { + weight_B = weight_B + delta; + } + } + + buffer[bptr_counter + 1] = dpp.samples_B[0]; + } + + break; + + + case - 1: + for (bptr_counter = buf_idx; bptr_counter < buf_idx + sample_count * 2; bptr_counter += 2) + { + sam_A = buffer[bptr_counter] + (int) ((weight_A * (long) dpp.samples_A[0] + 512) >> 10); + + if ((dpp.samples_A[0] ^ buffer[bptr_counter]) < 0) + { + if (dpp.samples_A[0] != 0 && buffer[bptr_counter] != 0 && (weight_A -= delta) < - 1024) + { + if (weight_A < 0) + { + weight_A = - 1024; + } + else + { + weight_A = 1024; + } + } + } + else + { + if (dpp.samples_A[0] != 0 && buffer[bptr_counter] != 0 && (weight_A += delta) > 1024) + { + if (weight_A < 0) + { + weight_A = - 1024; + } + else + { + weight_A = 1024; + } + } + } + + buffer[bptr_counter] = sam_A; + dpp.samples_A[0] = buffer[bptr_counter + 1] + (int) ((weight_B * (long) sam_A + 512) >> 10); + + if ((sam_A ^ buffer[bptr_counter + 1]) < 0) + { + if (sam_A != 0 && buffer[bptr_counter + 1] != 0 && (weight_B -= delta) < - 1024) + { + if (weight_B < 0) + { + weight_B = - 1024; + } + else + { + weight_B = 1024; + } + } + } + else + { + if (sam_A != 0 && buffer[bptr_counter + 1] != 0 && (weight_B += delta) > 1024) + { + if (weight_B < 0) + { + weight_B = - 1024; + } + else + { + weight_B = 1024; + } + } + } + + buffer[bptr_counter + 1] = dpp.samples_A[0]; + } + + break; + + + case - 2: + sam_B = 0; + sam_A = 0; + + for (bptr_counter = buf_idx; bptr_counter < buf_idx + sample_count * 2; bptr_counter += 2) + { + sam_B = buffer[bptr_counter + 1] + (int) ((weight_B * (long) dpp.samples_B[0] + 512) >> 10); + + if ((dpp.samples_B[0] ^ buffer[bptr_counter + 1]) < 0) + { + if (dpp.samples_B[0] != 0 && buffer[bptr_counter + 1] != 0 && (weight_B -= delta) < - 1024) + { + if (weight_B < 0) + { + weight_B = - 1024; + } + else + { + weight_B = 1024; + } + } + } + else + { + if (dpp.samples_B[0] != 0 && buffer[bptr_counter + 1] != 0 && (weight_B += delta) > 1024) + { + if (weight_B < 0) + { + weight_B = - 1024; + } + else + { + weight_B = 1024; + } + } + } + + buffer[bptr_counter + 1] = sam_B; + + dpp.samples_B[0] = buffer[bptr_counter] + (int) ((weight_A * (long) sam_B + 512) >> 10); + + if ((sam_B ^ buffer[bptr_counter]) < 0) + { + if (sam_B != 0 && buffer[bptr_counter] != 0 && (weight_A -= delta) < - 1024) + { + if (weight_A < 0) + { + weight_A = - 1024; + } + else + { + weight_A = 1024; + } + } + } + else + { + if (sam_B != 0 && buffer[bptr_counter] != 0 && (weight_A += delta) > 1024) + { + if (weight_A < 0) + { + weight_A = - 1024; + } + else + { + weight_A = 1024; + } + } + } + buffer[bptr_counter] = dpp.samples_B[0]; + } + + break; + + + case - 3: + sam_A = 0; + + for (bptr_counter = buf_idx; bptr_counter < buf_idx + sample_count * 2; bptr_counter += 2) + { + sam_A = buffer[bptr_counter] + (int) ((weight_A * (long) dpp.samples_A[0] + 512) >> 10); + + if ((dpp.samples_A[0] ^ buffer[bptr_counter]) < 0) + { + if (dpp.samples_A[0] != 0 && buffer[bptr_counter] != 0 && (weight_A -= delta) < - 1024) + { + if (weight_A < 0) + { + weight_A = - 1024; + } + else + { + weight_A = 1024; + } + } + } + else + + { + if (dpp.samples_A[0] != 0 && buffer[bptr_counter] != 0 && (weight_A += delta) > 1024) + { + if (weight_A < 0) + { + weight_A = - 1024; + } + else + { + weight_A = 1024; + } + } + } + + sam_B = buffer[bptr_counter + 1] + (int) ((weight_B * (long) dpp.samples_B[0] + 512) >> 10); + + if ((dpp.samples_B[0] ^ buffer[bptr_counter + 1]) < 0) + { + if (dpp.samples_B[0] != 0 && buffer[bptr_counter + 1] != 0 && (weight_B -= delta) < - 1024) + { + if (weight_B < 0) + { + weight_B = - 1024; + } + else + { + weight_B = 1024; + } + } + } + else + { + if (dpp.samples_B[0] != 0 && buffer[bptr_counter + 1] != 0 && (weight_B += delta) > 1024) + { + if (weight_B < 0) + { + weight_B = - 1024; + } + else + { + weight_B = 1024; + } + } + } + + buffer[bptr_counter] = dpp.samples_B[0] = sam_A; + buffer[bptr_counter + 1] = dpp.samples_A[0] = sam_B; + } + + break; + + + default: + + sam_A = 0; + + for (m = 0, k = dpp.term & (Defines.MAX_TERM - 1), bptr_counter = buf_idx; bptr_counter < buf_idx + sample_count * 2; bptr_counter += 2) + { + sam_A = dpp.samples_A[m]; + dpp.samples_A[k] = (int) ((weight_A * (long) sam_A + 512) >> 10) + buffer[bptr_counter]; + + if (sam_A != 0 && buffer[bptr_counter] != 0) + { + if ((sam_A ^ buffer[bptr_counter]) < 0) + { + weight_A = weight_A - delta; + } + else + { + weight_A = weight_A + delta; + } + } + + buffer[bptr_counter] = dpp.samples_A[k]; + + sam_A = dpp.samples_B[m]; + dpp.samples_B[k] = (int) ((weight_B * (long) sam_A + 512) >> 10) + buffer[bptr_counter + 1]; + + if (sam_A != 0 && buffer[bptr_counter + 1] != 0) + { + if ((sam_A ^ buffer[bptr_counter + 1]) < 0) + { + weight_B = weight_B - delta; + } + else + { + weight_B = weight_B + delta; + } + } + + buffer[bptr_counter + 1] = dpp.samples_B[k]; + + m = (m + 1) & (Defines.MAX_TERM - 1); + k = (k + 1) & (Defines.MAX_TERM - 1); + } + + if (m != 0) + { + int[] temp_samples = new int[Defines.MAX_TERM]; + + for (int t = 0; t < dpp.samples_A.Length; t++) + { + temp_samples[t] = dpp.samples_A[t]; + } + + for (k = 0; k < Defines.MAX_TERM; k++, m++) + dpp.samples_A[k] = temp_samples[m & (Defines.MAX_TERM - 1)]; + + Array.Copy(dpp.samples_B, 0, temp_samples, 0, dpp.samples_B.Length); + + for (k = 0; k < Defines.MAX_TERM; k++, m++) + dpp.samples_B[k] = temp_samples[m & (Defines.MAX_TERM - 1)]; + } + + break; + + } + + dpp.weight_A = (short) weight_A; + dpp.weight_B = (short) weight_B; + } + + internal static void decorr_stereo_pass_cont(decorr_pass dpp, int[] buffer, long sample_count, int buf_idx) + { + int delta = dpp.delta, weight_A = dpp.weight_A, weight_B = dpp.weight_B; + int tptr; + int sam_A, sam_B; + int k, i; + int buffer_index = buf_idx; + long end_index = buf_idx + sample_count * 2; + + switch (dpp.term) + { + + case 17: + for (buffer_index = buf_idx; buffer_index < end_index; buffer_index += 2) + { + sam_A = 2 * buffer[buffer_index - 2] - buffer[buffer_index - 4]; + + buffer[buffer_index] = (int) ((weight_A * (long) sam_A + 512) >> 10) + (sam_B = buffer[buffer_index]); + + if (sam_A != 0 && sam_B != 0) + weight_A += (((sam_A ^ sam_B) >> 30) | 1) * delta; + + //update_weight (weight_A, delta, sam_A, sam_B); + + sam_A = 2 * buffer[buffer_index - 1] - buffer[buffer_index - 3]; + + buffer[buffer_index + 1] = (int) ((weight_B * (long) sam_A + 512) >> 10) + (sam_B = buffer[buffer_index + 1]); + + if (sam_A != 0 && sam_B != 0) + weight_B += (((sam_A ^ sam_B) >> 30) | 1) * delta; + } + + dpp.samples_B[0] = buffer[buffer_index - 1]; + dpp.samples_A[0] = buffer[buffer_index - 2]; + dpp.samples_B[1] = buffer[buffer_index - 3]; + dpp.samples_A[1] = buffer[buffer_index - 4]; + break; + + + case 18: + for (buffer_index = buf_idx; buffer_index < end_index; buffer_index += 2) + { + sam_A = (3 * buffer[buffer_index - 2] - buffer[buffer_index - 4]) >> 1; + + buffer[buffer_index] = (int) ((weight_A * (long) sam_A + 512) >> 10) + (sam_B = buffer[buffer_index]); + + if (sam_A != 0 && sam_B != 0) + weight_A += (((sam_A ^ sam_B) >> 30) | 1) * delta; + + sam_A = (3 * buffer[buffer_index - 1] - buffer[buffer_index - 3]) >> 1; + + buffer[buffer_index + 1] = (int) ((weight_B * (long) sam_A + 512) >> 10) + (sam_B = buffer[buffer_index + 1]); + + if (sam_A != 0 && sam_B != 0) + weight_B += (((sam_A ^ sam_B) >> 30) | 1) * delta; + + } + + dpp.samples_B[0] = buffer[buffer_index - 1]; + dpp.samples_A[0] = buffer[buffer_index - 2]; + dpp.samples_B[1] = buffer[buffer_index - 3]; + dpp.samples_A[1] = buffer[buffer_index - 4]; + break; + + + case - 1: + for (buffer_index = buf_idx; buffer_index < end_index; buffer_index += 2) + { + buffer[buffer_index] = (int) ((weight_A * (long) buffer[buffer_index - 1] + 512) >> 10) + (sam_A = buffer[buffer_index]); + + if ((buffer[buffer_index - 1] ^ sam_A) < 0) + { + if (buffer[buffer_index - 1] != 0 && sam_A != 0 && (weight_A -= delta) < - 1024) + { + if (weight_A < 0) + { + weight_A = - 1024; + } + else + { + weight_A = 1024; + } + } + } + else + { + if (buffer[buffer_index - 1] != 0 && sam_A != 0 && (weight_A += delta) > 1024) + { + if (weight_A < 0) + { + weight_A = - 1024; + } + else + { + weight_A = 1024; + } + } + } + + buffer[buffer_index + 1] = (int) ((weight_B * (long) buffer[buffer_index] + 512) >> 10) + (sam_A = buffer[buffer_index + 1]); + + if ((buffer[buffer_index] ^ sam_A) < 0) + { + if (buffer[buffer_index] != 0 && sam_A != 0 && (weight_B -= delta) < - 1024) + { + if (weight_B < 0) + { + weight_B = - 1024; + } + else + { + weight_B = 1024; + } + } + } + else + { + if (buffer[buffer_index] != 0 && sam_A != 0 && (weight_B += delta) > 1024) + { + if (weight_B < 0) + { + weight_B = - 1024; + } + else + { + weight_B = 1024; + } + } + } + } + + dpp.samples_A[0] = buffer[buffer_index - 1]; + break; + + + case - 2: + sam_A = 0; + sam_B = 0; + + for (buffer_index = buf_idx; buffer_index < end_index; buffer_index += 2) + { + + buffer[buffer_index + 1] = (int) ((weight_B * (long) buffer[buffer_index - 2] + 512) >> 10) + (sam_A = buffer[buffer_index + 1]); + + if ((buffer[buffer_index - 2] ^ sam_A) < 0) + { + if (buffer[buffer_index - 2] != 0 && sam_A != 0 && (weight_B -= delta) < - 1024) + { + if (weight_B < 0) + { + weight_B = - 1024; + } + else + { + weight_B = 1024; + } + } + } + else + { + if (buffer[buffer_index - 2] != 0 && sam_A != 0 && (weight_B += delta) > 1024) + { + if (weight_B < 0) + { + weight_B = - 1024; + } + else + { + weight_B = 1024; + } + } + } + + buffer[buffer_index] = (int) ((weight_A * (long) buffer[buffer_index + 1] + 512) >> 10) + (sam_A = buffer[buffer_index]); + + if ((buffer[buffer_index + 1] ^ sam_A) < 0) + { + if (buffer[buffer_index + 1] != 0 && sam_A != 0 && (weight_A -= delta) < - 1024) + { + if (weight_A < 0) + { + weight_A = - 1024; + } + else + { + weight_A = 1024; + } + } + } + else + { + if (buffer[buffer_index + 1] != 0 && sam_A != 0 && (weight_A += delta) > 1024) + { + if (weight_A < 0) + { + weight_A = - 1024; + } + else + { + weight_A = 1024; + } + } + } + } + + dpp.samples_B[0] = buffer[buffer_index - 2]; + break; + + + case - 3: + for (buffer_index = buf_idx; buffer_index < end_index; buffer_index += 2) + { + + buffer[buffer_index] = (int) ((weight_A * (long) buffer[buffer_index - 1] + 512) >> 10) + (sam_A = buffer[buffer_index]); + + if ((buffer[buffer_index - 1] ^ sam_A) < 0) + { + if (buffer[buffer_index - 1] != 0 && sam_A != 0 && (weight_A -= delta) < - 1024) + { + if (weight_A < 0) + { + weight_A = - 1024; + } + else + { + weight_A = 1024; + } + } + } + else + { + if (buffer[buffer_index - 1] != 0 && sam_A != 0 && (weight_A += delta) > 1024) + { + if (weight_A < 0) + { + weight_A = - 1024; + } + else + { + weight_A = 1024; + } + } + } + + buffer[buffer_index + 1] = (int) ((weight_B * (long) buffer[buffer_index - 2] + 512) >> 10) + (sam_A = buffer[buffer_index + 1]); + + if ((buffer[buffer_index - 2] ^ sam_A) < 0) + { + if (buffer[buffer_index - 2] != 0 && sam_A != 0 && (weight_B -= delta) < - 1024) + { + if (weight_B < 0) + { + weight_B = - 1024; + } + else + { + weight_B = 1024; + } + } + } + else + { + if (buffer[buffer_index - 2] != 0 && sam_A != 0 && (weight_B += delta) > 1024) + { + if (weight_B < 0) + { + weight_B = - 1024; + } + else + { + weight_B = 1024; + } + } + } + } + + dpp.samples_A[0] = buffer[buffer_index - 1]; + dpp.samples_B[0] = buffer[buffer_index - 2]; + break; + + + default: + tptr = buf_idx - (dpp.term * 2); + + for (buffer_index = buf_idx; buffer_index < end_index; buffer_index += 2) + { + buffer[buffer_index] = (int) ((weight_A * (long) buffer[tptr] + 512) >> 10) + (sam_A = buffer[buffer_index]); + + if (buffer[tptr] != 0 && sam_A != 0) + weight_A += (((buffer[tptr] ^ sam_A) >> 30) | 1) * delta; + + buffer[buffer_index + 1] = (int) ((weight_B * (long) buffer[tptr + 1] + 512) >> 10) + (sam_A = buffer[buffer_index + 1]); + + if (buffer[tptr + 1] != 0 && sam_A != 0) + weight_B += (((buffer[tptr + 1] ^ sam_A) >> 30) | 1) * delta; + + tptr += 2; + } + + buffer_index--; + + for (k = dpp.term - 1, i = 8; i > 0; k--) + { + i--; + dpp.samples_B[k & (Defines.MAX_TERM - 1)] = buffer[buffer_index]; + buffer_index--; + dpp.samples_A[k & (Defines.MAX_TERM - 1)] = buffer[buffer_index]; + buffer_index--; + } + + break; + + } + + dpp.weight_A = (short) weight_A; + dpp.weight_B = (short) weight_B; + } + + internal static void decorr_mono_pass(decorr_pass dpp, int[] buffer, long sample_count, int buf_idx) + { + int delta = dpp.delta, weight_A = dpp.weight_A; + int sam_A; + int m, k; + int bptr_counter = 0; + + switch (dpp.term) + { + + case 17: + for (bptr_counter = buf_idx; bptr_counter < buf_idx + sample_count; bptr_counter++) + { + sam_A = 2 * dpp.samples_A[0] - dpp.samples_A[1]; + dpp.samples_A[1] = dpp.samples_A[0]; + dpp.samples_A[0] = (int) ((weight_A * (long) sam_A + 512) >> 10) + buffer[bptr_counter]; + + if (sam_A != 0 && buffer[bptr_counter] != 0) + { + if ((sam_A ^ buffer[bptr_counter]) < 0) + { + weight_A = weight_A - delta; + } + else + { + weight_A = weight_A + delta; + } + } + buffer[bptr_counter] = dpp.samples_A[0]; + } + + break; + + + case 18: + for (bptr_counter = buf_idx; bptr_counter < buf_idx + sample_count; bptr_counter++) + { + sam_A = (3 * dpp.samples_A[0] - dpp.samples_A[1]) >> 1; + dpp.samples_A[1] = dpp.samples_A[0]; + dpp.samples_A[0] = (int) ((weight_A * (long) sam_A + 512) >> 10) + buffer[bptr_counter]; + + if (sam_A != 0 && buffer[bptr_counter] != 0) + { + if ((sam_A ^ buffer[bptr_counter]) < 0) + { + weight_A = weight_A - delta; + } + else + { + weight_A = weight_A + delta; + } + } + buffer[bptr_counter] = dpp.samples_A[0]; + } + + break; + + + default: + for (m = 0, k = dpp.term & (Defines.MAX_TERM - 1), bptr_counter = buf_idx; bptr_counter < buf_idx + sample_count; bptr_counter++) + { + sam_A = dpp.samples_A[m]; + dpp.samples_A[k] = (int) ((weight_A * (long) sam_A + 512) >> 10) + buffer[bptr_counter]; + + if (sam_A != 0 && buffer[bptr_counter] != 0) + { + if ((sam_A ^ buffer[bptr_counter]) < 0) + { + weight_A = weight_A - delta; + } + else + { + weight_A = weight_A + delta; + } + } + + buffer[bptr_counter] = dpp.samples_A[k]; + m = (m + 1) & (Defines.MAX_TERM - 1); + k = (k + 1) & (Defines.MAX_TERM - 1); + } + + if (m != 0) + { + int[] temp_samples = new int[Defines.MAX_TERM]; + + Array.Copy(dpp.samples_A, 0, temp_samples, 0, dpp.samples_A.Length); + + for (k = 0; k < Defines.MAX_TERM; k++, m++) + dpp.samples_A[k] = temp_samples[m & (Defines.MAX_TERM - 1)]; + } + + break; + + } + + dpp.weight_A = (short) weight_A; + } + + + // This is a helper function for unpack_samples() that applies several final + // operations. First, if the data is 32-bit float data, then that conversion + // is done in the float.c module (whether lossy or lossless) and we return. + // Otherwise, if the extended integer data applies, then that operation is + // executed first. If the unpacked data is lossy (and not corrected) then + // it is clipped and shifted in a single operation. Otherwise, if it's + // lossless then the last step is to apply the final shift (if any). + + internal static int[] fixup_samples(WavpackStream wps, int[] buffer, long sample_count, int bufferStartPos) + { + long flags = wps.wphdr.flags; + int shift = (int) ((flags & Defines.SHIFT_MASK) >> Defines.SHIFT_LSB); + + if ((flags & Defines.FLOAT_DATA) > 0) + { + long sc = 0; + + if ((flags & Defines.MONO_FLAG) > 0) + { + sc = sample_count; + } + else + { + sc = sample_count * 2; + } + + buffer = FloatUtils.float_values(wps, buffer, sc, bufferStartPos); + } + + if ((flags & Defines.INT32_DATA) > 0) + { + + int sent_bits = wps.int32_sent_bits, zeros = wps.int32_zeros; + int ones = wps.int32_ones, dups = wps.int32_dups; + int buffer_counter = bufferStartPos; + + long count; + + if ((flags & Defines.MONO_FLAG) > 0) + { + count = sample_count; + } + else + { + count = sample_count * 2; + } + + if ((flags & Defines.HYBRID_FLAG) == 0 && sent_bits == 0 && (zeros + ones + dups) != 0) + while (count > 0) + { + if (zeros != 0) + buffer[buffer_counter] <<= zeros; + else if (ones != 0) + buffer[buffer_counter] = ((buffer[buffer_counter] + 1) << ones) - 1; + else if (dups != 0) + buffer[buffer_counter] = ((buffer[buffer_counter] + (buffer[buffer_counter] & 1)) << dups) - (buffer[buffer_counter] & 1); + + buffer_counter++; + count--; + } + else + shift += zeros + sent_bits + ones + dups; + } + + if ((flags & Defines.HYBRID_FLAG) > 0) + { + int min_value, max_value, min_shifted, max_shifted; + int buffer_counter = bufferStartPos; + + switch ((int) (flags & (long) Defines.BYTES_STORED)) + { + + + case 0: + min_shifted = (min_value = - 128 >> shift) << shift; + max_shifted = (max_value = 127 >> shift) << shift; + break; + + + case 1: + min_shifted = (min_value = - 32768 >> shift) << shift; + max_shifted = (max_value = 32767 >> shift) << shift; + break; + + + case 2: + min_shifted = (min_value = - 8388608 >> shift) << shift; + max_shifted = (max_value = 8388607 >> shift) << shift; + break; + + + case 3: + default: + min_shifted = (min_value = (int) SupportClass.Identity(0x80000000) >> shift) << shift; + max_shifted = (max_value = (int) 0x7FFFFFFF >> shift) << shift; + break; + } + + if ((flags & Defines.MONO_FLAG) == 0) + sample_count *= 2; + + while (sample_count > 0) + { + if (buffer[buffer_counter] < min_value) + buffer[buffer_counter] = min_shifted; + else if (buffer[buffer_counter] > max_value) + buffer[buffer_counter] = max_shifted; + else + buffer[buffer_counter] <<= shift; + + buffer_counter++; + sample_count--; + } + } + else if (shift != 0) + { + + int buffer_counter = bufferStartPos; + + if ((flags & Defines.MONO_FLAG) == 0) + sample_count *= 2; + + while (sample_count > 0) + { + buffer[buffer_counter] = buffer[buffer_counter] << shift; + buffer_counter++; + sample_count--; + } + } + + return buffer; + } + + + // This function checks the crc value(s) for an unpacked block, returning the + // number of actual crc errors detected for the block. The block must be + // completely unpacked before this test is valid. For losslessly unpacked + // blocks of float or extended integer data the extended crc is also checked. + // Note that WavPack's crc is not a CCITT approved polynomial algorithm, but + // is a much simpler method that is virtually as robust for real world data. + + internal static int check_crc_error(WavpackContext wpc) + { + WavpackStream wps = wpc.stream; + int result = 0; + + if (wps.crc != wps.wphdr.crc) + { + ++result; + } + + return result; + } +} diff --git a/WavPackUtils.cs b/WavPackUtils.cs new file mode 100644 index 0000000..367cc55 --- /dev/null +++ b/WavPackUtils.cs @@ -0,0 +1,648 @@ +using System; +/* +** WavPackUtils.cs +** +** Copyright (c) 2010-2016 Peter McQuillan +** +** All Rights Reserved. +** +** Distributed under the BSD Software License (see license.txt) +***/ + +public class WavPackUtils +{ + + + ///////////////////////////// local table storage //////////////////////////// + + internal static long[] sample_rates = new long[]{6000, 8000, 9600, 11025, 12000, 16000, 22050, 24000, 32000, 44100, 48000, 64000, 88200, 96000, 192000}; + + ///////////////////////////// executable code //////////////////////////////// + + + // This function reads data from the specified stream in search of a valid + // WavPack 4.0 audio block. If this fails in 1 megabyte (or an invalid or + // unsupported WavPack block is encountered) then an appropriate message is + // copied to "error" and NULL is returned, otherwise a pointer to a + // WavpackContext structure is returned (which is used to call all other + // functions in this module). This can be initiated at the beginning of a + // WavPack file, or anywhere inside a WavPack file. To determine the exact + // position within the file use WavpackGetSampleIndex(). Also, + // this function will not handle "correction" files, plays only the first + // two channels of multi-channel files, and is limited in resolution in some + // large integer or floating point files (but always provides at least 24 bits + // of resolution). + + public static WavpackContext WavpackOpenFileInput(System.IO.BinaryReader infile) + { + WavpackContext wpc = new WavpackContext(); + WavpackStream wps = wpc.stream; + + wpc.infile = infile; + wpc.total_samples = - 1; + wpc.norm_offset = 0; + wpc.open_flags = 0; + + + // open the source file for reading and store the size + + while (wps.wphdr.block_samples == 0) + { + + wps.wphdr = read_next_header(wpc.infile, wps.wphdr); + + if (wps.wphdr.status == 1) + { + wpc.error_message = "not compatible with this version of WavPack file!"; + wpc.error = true; + return (wpc); + } + + if (wps.wphdr.block_samples > 0 && wps.wphdr.total_samples != - 1) + { + wpc.total_samples = wps.wphdr.total_samples; + } + + // lets put the stream back in the context + + wpc.stream = wps; + + if ((UnpackUtils.unpack_init(wpc)) == Defines.FALSE) + { + wpc.error = true; + return wpc; + } + } // end of while + + wpc.config.flags = wpc.config.flags & ~ 0xff; + wpc.config.flags = wpc.config.flags | (wps.wphdr.flags & 0xff); + + wpc.config.bytes_per_sample = (int) ((wps.wphdr.flags & Defines.BYTES_STORED) + 1); + wpc.config.float_norm_exp = wps.float_norm_exp; + + wpc.config.bits_per_sample = (int) ((wpc.config.bytes_per_sample * 8) - ((wps.wphdr.flags & Defines.SHIFT_MASK) >> Defines.SHIFT_LSB)); + + if ((wpc.config.flags & Defines.FLOAT_DATA) > 0) + { + wpc.config.bytes_per_sample = 3; + wpc.config.bits_per_sample = 24; + } + + if (wpc.config.sample_rate == 0) + { + if (wps.wphdr.block_samples == 0 || (wps.wphdr.flags & Defines.SRATE_MASK) == Defines.SRATE_MASK) + wpc.config.sample_rate = 44100; + else + wpc.config.sample_rate = sample_rates[(int) ((wps.wphdr.flags & Defines.SRATE_MASK) >> Defines.SRATE_LSB)]; + } + + if (wpc.config.num_channels == 0) + { + if ((wps.wphdr.flags & Defines.MONO_FLAG) > 0) + { + wpc.config.num_channels = 1; + } + else + { + wpc.config.num_channels = 2; + } + + wpc.config.channel_mask = 0x5 - wpc.config.num_channels; + } + + if ((wps.wphdr.flags & Defines.FINAL_BLOCK) == 0) + { + if ((wps.wphdr.flags & Defines.MONO_FLAG) != 0) + { + wpc.reduced_channels = 1; + } + else + { + wpc.reduced_channels = 2; + } + } + + return wpc; + } + + // This function obtains general information about an open file and returns + // a mask with the following bit values: + + // MODE_LOSSLESS: file is lossless (pure lossless only) + // MODE_HYBRID: file is hybrid mode (lossy part only) + // MODE_FLOAT: audio data is 32-bit ieee floating point (but will provided + // in 24-bit integers for convenience) + // MODE_HIGH: file was created in "high" mode (information only) + // MODE_FAST: file was created in "fast" mode (information only) + + + internal static int WavpackGetMode(WavpackContext wpc) + { + int mode = 0; + + if (null != wpc) + { + if ((wpc.config.flags & Defines.CONFIG_HYBRID_FLAG) != 0) + mode |= Defines.MODE_HYBRID; + else if ((wpc.config.flags & Defines.CONFIG_LOSSY_MODE) == 0) + mode |= Defines.MODE_LOSSLESS; + + if (wpc.lossy_blocks != 0) + mode &= ~ Defines.MODE_LOSSLESS; + + if ((wpc.config.flags & Defines.CONFIG_FLOAT_DATA) != 0) + mode |= Defines.MODE_FLOAT; + + if ((wpc.config.flags & Defines.CONFIG_HIGH_FLAG) != 0) + mode |= Defines.MODE_HIGH; + + if ((wpc.config.flags & Defines.CONFIG_FAST_FLAG) != 0) + mode |= Defines.MODE_FAST; + } + + return mode; + } + + + // Unpack the specified number of samples from the current file position. + // Note that "samples" here refers to "complete" samples, which would be + // 2 longs for stereo files. The audio data is returned right-justified in + // 32-bit longs in the endian mode native to the executing processor. So, + // if the original data was 16-bit, then the values returned would be + // +/-32k. Floating point data will be returned as 24-bit integers (and may + // also be clipped). The actual number of samples unpacked is returned, + // which should be equal to the number requested unless the end of fle is + // encountered or an error occurs. + + internal static long WavpackUnpackSamples(WavpackContext wpc, int[] buffer, long samples) + { + WavpackStream wps = wpc.stream; + long samples_unpacked = 0, samples_to_unpack; + int num_channels = wpc.config.num_channels; + int bcounter = 0; + + int buf_idx = 0; + int bytes_returned = 0; + + while (samples > 0) + { + if (wps.wphdr.block_samples == 0 || (wps.wphdr.flags & Defines.INITIAL_BLOCK) == 0 || wps.sample_index >= wps.wphdr.block_index + wps.wphdr.block_samples) + { + + wps.wphdr = read_next_header(wpc.infile, wps.wphdr); + + if (wps.wphdr.status == 1) + break; + + if (wps.wphdr.block_samples == 0 || wps.sample_index == wps.wphdr.block_index) + { + if ((UnpackUtils.unpack_init(wpc)) == Defines.FALSE) + break; + } + } + + if (wps.wphdr.block_samples == 0 || (wps.wphdr.flags & Defines.INITIAL_BLOCK) == 0 || wps.sample_index >= wps.wphdr.block_index + wps.wphdr.block_samples) + continue; + + if (wps.sample_index < wps.wphdr.block_index) + { + samples_to_unpack = wps.wphdr.block_index - wps.sample_index; + + if (samples_to_unpack > samples) + samples_to_unpack = samples; + + wps.sample_index += samples_to_unpack; + samples_unpacked += samples_to_unpack; + samples -= samples_to_unpack; + + if (wpc.reduced_channels > 0) + samples_to_unpack *= wpc.reduced_channels; + else + samples_to_unpack *= num_channels; + + bcounter = buf_idx; + + while (samples_to_unpack > 0) + { + buffer[bcounter] = 0; + bcounter++; + samples_to_unpack--; + } + buf_idx = bcounter; + + continue; + } + + samples_to_unpack = wps.wphdr.block_index + wps.wphdr.block_samples - wps.sample_index; + + if (samples_to_unpack > samples) + samples_to_unpack = samples; + + UnpackUtils.unpack_samples(wpc, buffer, samples_to_unpack, buf_idx); + + if (wpc.reduced_channels > 0) + bytes_returned = (int) (samples_to_unpack * wpc.reduced_channels); + else + bytes_returned = (int) (samples_to_unpack * num_channels); + + buf_idx += bytes_returned; + + samples_unpacked += samples_to_unpack; + samples -= samples_to_unpack; + + if (wps.sample_index == wps.wphdr.block_index + wps.wphdr.block_samples) + { + if (UnpackUtils.check_crc_error(wpc) > 0) + wpc.crc_errors++; + } + + if (wps.sample_index == wpc.total_samples) + break; + } + + return (samples_unpacked); + } + + + + + // Get total number of samples contained in the WavPack file, or -1 if unknown + + internal static long WavpackGetNumSamples(WavpackContext wpc) + { + // -1 would mean an unknown number of samples + + if (null != wpc) + { + return (wpc.total_samples); + } + else + { + return (long) (- 1); + } + } + + + // Get the current sample index position, or -1 if unknown + + internal static long WavpackGetSampleIndex(WavpackContext wpc) + { + if (null != wpc) + return wpc.stream.sample_index; + + return (long) (- 1); + } + + + + // Get the number of errors encountered so far + + internal static long WavpackGetNumErrors(WavpackContext wpc) + { + if (null != wpc) + { + return wpc.crc_errors; + } + else + { + return (long) 0; + } + } + + + // return if any uncorrected lossy blocks were actually written or read + + + internal static int WavpackLossyBlocks(WavpackContext wpc) + { + if (null != wpc) + { + return wpc.lossy_blocks; + } + else + { + return 0; + } + } + + + + // Returns the sample rate of the specified WavPack file + + internal static long WavpackGetSampleRate(WavpackContext wpc) + { + if (null != wpc && wpc.config.sample_rate != 0) + { + return wpc.config.sample_rate; + } + else + { + return (long) 44100; + } + } + + + // Returns the number of channels of the specified WavPack file. Note that + // this is the actual number of channels contained in the file, but this + // version can only decode the first two. + + internal static int WavpackGetNumChannels(WavpackContext wpc) + { + if (null != wpc && wpc.config.num_channels != 0) + { + return wpc.config.num_channels; + } + else + { + return 2; + } + } + + + // Returns the actual number of valid bits per sample contained in the + // original file, which may or may not be a multiple of 8. Floating data + // always has 32 bits, integers may be from 1 to 32 bits each. When this + // value is not a multiple of 8, then the "extra" bits are located in the + // LSBs of the results. That is, values are right justified when unpacked + // into longs, but are left justified in the number of bytes used by the + // original data. + + internal static int WavpackGetBitsPerSample(WavpackContext wpc) + { + if (null != wpc && wpc.config.bits_per_sample != 0) + { + return wpc.config.bits_per_sample; + } + else + { + return 16; + } + } + + + // Returns the number of bytes used for each sample (1 to 4) in the original + // file. This is required information for the user of this module because the + // audio data is returned in the LOWER bytes of the long buffer and must be + // left-shifted 8, 16, or 24 bits if normalized longs are required. + + internal static int WavpackGetBytesPerSample(WavpackContext wpc) + { + if (null != wpc && wpc.config.bytes_per_sample != 0) + { + return wpc.config.bytes_per_sample; + } + else + { + return 2; + } + } + + + // This function will return the actual number of channels decoded from the + // file (which may or may not be less than the actual number of channels, but + // will always be 1 or 2). Normally, this will be the front left and right + // channels of a multi-channel file. + + internal static int WavpackGetReducedChannels(WavpackContext wpc) + { + if (null != wpc && wpc.reduced_channels != 0) + { + return wpc.reduced_channels; + } + else if (null != wpc && wpc.config.num_channels != 0) + { + return wpc.config.num_channels; + } + else + { + return 2; + } + } + + // The following seek functionality has not yet been extensively tested + + public static void setTime(WavpackContext wpc, long milliseconds) + { + long targetSample = (long)(milliseconds / 1000 * wpc.config.sample_rate); + try + { + seek(wpc, wpc.infile, wpc.infile.BaseStream.Position, targetSample); + } + catch (System.IO.IOException) + { + } + } + + public static void setSample(WavpackContext wpc, long sample) + { + seek(wpc, wpc.infile, 0, sample); + } + + // Find the WavPack block that contains the specified sample. If "header_pos" + // is zero, then no information is assumed except the total number of samples + // in the file and its size in bytes. If "header_pos" is non-zero then we + // assume that it is the file position of the valid header image contained in + // the first stream and we can limit our search to either the portion above + // or below that point. If a .wvc file is being used, then this must be called + // for that file also. + private static void seek(WavpackContext wpc, System.IO.BinaryReader infile, long headerPos, long targetSample) + { + try + { + WavpackStream wps = wpc.stream; + long file_pos1 = 0; + long file_pos2 = wpc.infile.BaseStream.Length; + long sample_pos1 = 0, sample_pos2 = wpc.total_samples; + double ratio = 0.96; + int file_skip = 0; + if (targetSample >= wpc.total_samples) + return; + if (headerPos > 0 && wps.wphdr.block_samples > 0) + { + if (wps.wphdr.block_index > targetSample) + { + sample_pos2 = wps.wphdr.block_index; + file_pos2 = headerPos; + } + else if (wps.wphdr.block_index + wps.wphdr.block_samples <= targetSample) + { + sample_pos1 = wps.wphdr.block_index; + file_pos1 = headerPos; + } + else + return; + } + while (true) + { + double bytes_per_sample; + long seek_pos; + bytes_per_sample = file_pos2 - file_pos1; + bytes_per_sample /= sample_pos2 - sample_pos1; + seek_pos = file_pos1 + (file_skip > 0 ? 32 : 0); + seek_pos += (long)(bytes_per_sample * (targetSample - sample_pos1) * ratio); + infile.BaseStream.Seek(seek_pos, 0); + + long temppos = infile.BaseStream.Position; + wps.wphdr = read_next_header(infile, wps.wphdr); + + if (wps.wphdr.status == 1 || seek_pos >= file_pos2) + { + if (ratio > 0.0) + { + if ((ratio -= 0.24) < 0.0) + ratio = 0.0; + } + else + return; + } + else if (wps.wphdr.block_index > targetSample) + { + sample_pos2 = wps.wphdr.block_index; + file_pos2 = seek_pos; + } + else if (wps.wphdr.block_index + wps.wphdr.block_samples <= targetSample) + { + if (seek_pos == file_pos1) + file_skip = 1; + else + { + sample_pos1 = wps.wphdr.block_index; + file_pos1 = seek_pos; + } + } + else + { + int index = (int)(targetSample - wps.wphdr.block_index); + infile.BaseStream.Seek(seek_pos, 0); + WavpackContext c = WavpackOpenFileInput(infile); + wpc.stream = c.stream; + int[] temp_buf = new int[Defines.SAMPLE_BUFFER_SIZE]; + while (index > 0) + { + int toUnpack = Math.Min(index, Defines.SAMPLE_BUFFER_SIZE / WavpackGetReducedChannels(wpc)); + WavpackUnpackSamples(wpc, temp_buf, toUnpack); + index = index - toUnpack; + } + return; + } + } + } + catch (System.IO.IOException) + { + } + } + + // Read from current file position until a valid 32-byte WavPack 4.0 header is + // found and read into the specified pointer. If no WavPack header is found within 1 meg, + // then an error is returned. No additional bytes are read past the header. + + internal static WavpackHeader read_next_header(System.IO.BinaryReader infile, WavpackHeader wphdr) + { + byte[] buffer = new byte[32]; // 32 is the size of a WavPack Header + byte[] temp = new byte[32]; + + long bytes_skipped = 0; + int bleft = 0; // bytes left in buffer + int counter = 0; + int i = 0; + + while (true) + { + for (i = 0; i < bleft; i++) + { + buffer[i] = buffer[32 - bleft + i]; + } + + counter = 0; + + try + { + if (infile.BaseStream.Read(temp, 0, 32 - bleft) != 32 - bleft) + { + wphdr.status = 1; + return wphdr; + } + } + catch (System.Exception) + { + wphdr.status = 1; + return wphdr; + } + + for (i = 0; i < 32 - bleft; i++) + { + buffer[bleft + i] = temp[i]; + } + + bleft = 32; + + if (buffer[0] == 'w' && buffer[1] == 'v' && buffer[2] == 'p' && buffer[3] == 'k' && (buffer[4] & 1) == 0 && buffer[6] < 16 && buffer[7] == 0 && buffer[9] == 4 && buffer[8] >= (Defines.MIN_STREAM_VERS & 0xff) && buffer[8] <= (Defines.MAX_STREAM_VERS & 0xff)) + { + + wphdr.ckID[0] = 'w'; + wphdr.ckID[1] = 'v'; + wphdr.ckID[2] = 'p'; + wphdr.ckID[3] = 'k'; + + wphdr.ckSize = (long) ((buffer[7] & 0xFF) << 24); + wphdr.ckSize += (long) ((buffer[6] & 0xFF) << 16); + wphdr.ckSize += (long) ((buffer[5] & 0xFF) << 8); + wphdr.ckSize += (long) (buffer[4] & 0xFF); + + wphdr.version = (short) (buffer[9] << 8); + wphdr.version = (short) (wphdr.version + (short) (buffer[8])); + + wphdr.track_no = buffer[10]; + wphdr.index_no = buffer[11]; + + wphdr.total_samples = (long) ((buffer[15] & 0xFF) << 24); + wphdr.total_samples += (long) ((buffer[14] & 0xFF) << 16); + wphdr.total_samples += (long) ((buffer[13] & 0xFF) << 8); + wphdr.total_samples += (long) (buffer[12] & 0xFF); + + wphdr.block_index = (long) ((buffer[19] & 0xFF) << 24); + wphdr.block_index += (long) ((buffer[18] & 0xFF) << 16); + wphdr.block_index += (long) ((buffer[17] & 0xFF) << 8); + wphdr.block_index += ((long) (buffer[16]) & 0xFF); + + wphdr.block_samples = (long) ((buffer[23] & 0xFF) << 24); + wphdr.block_samples += (long) ((buffer[22] & 0xFF) << 16); + wphdr.block_samples += (long) ((buffer[21] & 0xFF) << 8); + wphdr.block_samples += (long) (buffer[20] & 0xFF); + + wphdr.flags = (long) ((buffer[27] & 0xFF) << 24); + wphdr.flags += (long) ((buffer[26] & 0xFF) << 16); + wphdr.flags += (long) ((buffer[25] & 0xFF) << 8); + wphdr.flags += (long) (buffer[24] & 0xFF); + + wphdr.crc = (long) ((buffer[31] & 0xFF) << 24); + wphdr.crc += (long) ((buffer[30] & 0xFF) << 16); + wphdr.crc += (long) ((buffer[29] & 0xFF) << 8); + wphdr.crc += (long) (buffer[28] & 0xFF); + + wphdr.status = 0; + + return wphdr; + } + else + { + counter++; + bleft--; + } + + while (bleft > 0 && buffer[counter] != 'w') + { + counter++; + bleft--; + } + + bytes_skipped = bytes_skipped + counter; + + if (bytes_skipped > 1048576L) + { + wphdr.status = 1; + return wphdr; + } + } + } +} diff --git a/WaveHeader.cs b/WaveHeader.cs new file mode 100644 index 0000000..d85eb61 --- /dev/null +++ b/WaveHeader.cs @@ -0,0 +1,17 @@ +using System; +/* +** WaveHeader.cs +** +** Copyright (c) 2010-2016 Peter McQuillan +** +** All Rights Reserved. +** +** Distributed under the BSD Software License (see license.txt) +***/ + +class WaveHeader +{ + internal int FormatTag, NumChannels; // was ushort in C + internal long SampleRate, BytesPerSecond; // was uint32_t in C + internal int BlockAlign, BitsPerSample; // was ushort in C +} diff --git a/WavpackConfig.cs b/WavpackConfig.cs new file mode 100644 index 0000000..ab489ae --- /dev/null +++ b/WavpackConfig.cs @@ -0,0 +1,17 @@ +using System; +/* +** WavpackConfig.cs +** +** Copyright (c) 2010-2016 Peter McQuillan +** +** All Rights Reserved. +** +** Distributed under the BSD Software License (see license.txt) +***/ + +class WavpackConfig +{ + internal int bits_per_sample, bytes_per_sample; + internal int num_channels, float_norm_exp; + internal long flags, sample_rate, channel_mask; // was uint32_t in C +} diff --git a/WavpackContext.cs b/WavpackContext.cs new file mode 100644 index 0000000..d6d1f81 --- /dev/null +++ b/WavpackContext.cs @@ -0,0 +1,28 @@ +using System; +/* +** WavpackContext.cs +** +** Copyright (c) 2010-2016 Peter McQuillan +** +** All Rights Reserved. +** +** Distributed under the BSD Software License (see license.txt) +***/ + +public class WavpackContext +{ + internal WavpackConfig config = new WavpackConfig(); + internal WavpackStream stream = new WavpackStream(); + + + internal byte[] read_buffer = new byte[1024]; // was uchar in C + internal System.String error_message = ""; + internal bool error; + + internal System.IO.BinaryReader infile; + internal long total_samples, crc_errors; // was uint32_t in C + internal int open_flags, norm_offset; + internal int reduced_channels = 0; + internal int lossy_blocks; + internal int status = 0; // 0 ok, 1 error +} diff --git a/WavpackHeader.cs b/WavpackHeader.cs new file mode 100644 index 0000000..6eb56e1 --- /dev/null +++ b/WavpackHeader.cs @@ -0,0 +1,21 @@ +using System; +/* +** WavpackHeader.cs +** +** Copyright (c) 2010-2016 Peter McQuillan +** +** All Rights Reserved. +** +** Distributed under the BSD Software License (see license.txt) +***/ + +class WavpackHeader +{ + + internal char[] ckID = new char[4]; + internal long ckSize; // was uint32_t in C + internal short version; + internal short track_no, index_no; // was uchar in C + internal long total_samples, block_index, block_samples, flags, crc; // was uint32_t in C + internal int status = 0; // 1 means error +} diff --git a/WavpackMetadata.cs b/WavpackMetadata.cs new file mode 100644 index 0000000..2902df3 --- /dev/null +++ b/WavpackMetadata.cs @@ -0,0 +1,22 @@ +using System; +/* +** WavpackMetadata.cs +** +** Copyright (c) 2010-2016 Peter McQuillan +** +** All Rights Reserved. +** +** Distributed under the BSD Software License (see license.txt) +***/ + +class WavpackMetadata +{ + internal int byte_length; + internal byte[] data; + internal short id; // was uchar in C + internal int hasdata = 0; // 0 does not have data, 1 has data + internal int status = 0; // 0 ok, 1 error + internal long bytecount = 24; // we use this to determine if we have read all the metadata + // in a block by checking bytecount again the block length + // ckSize is block size minus 8. WavPack header is 32 bytes long so we start at 24 +} diff --git a/WavpackStream.cs b/WavpackStream.cs new file mode 100644 index 0000000..3f69bd8 --- /dev/null +++ b/WavpackStream.cs @@ -0,0 +1,52 @@ +using System; +/* +** WavpackStream.cs +** +** Copyright (c) 2010-2016 Peter McQuillan +** +** All Rights Reserved. +** +** Distributed under the BSD Software License (see license.txt) +***/ + +class WavpackStream +{ + public WavpackStream() + { + InitBlock(); + } + private void InitBlock() + { + decorr_passes = new decorr_pass[]{dp1, dp2, dp3, dp4, dp5, dp6, dp7, dp8, dp9, dp10, dp11, dp12, dp13, dp14, dp15, dp16}; + } + internal WavpackHeader wphdr = new WavpackHeader(); + internal Bitstream wvbits = new Bitstream(); + + internal words_data w = new words_data(); + + internal int num_terms = 0; + internal int mute_error; + internal long sample_index, crc; // was uint32_t in C + + internal short int32_sent_bits, int32_zeros, int32_ones, int32_dups; // was uchar in C + internal short float_flags, float_shift, float_max_exp, float_norm_exp; // was uchar in C + + internal decorr_pass dp1 = new decorr_pass(); + internal decorr_pass dp2 = new decorr_pass(); + internal decorr_pass dp3 = new decorr_pass(); + internal decorr_pass dp4 = new decorr_pass(); + internal decorr_pass dp5 = new decorr_pass(); + internal decorr_pass dp6 = new decorr_pass(); + internal decorr_pass dp7 = new decorr_pass(); + internal decorr_pass dp8 = new decorr_pass(); + internal decorr_pass dp9 = new decorr_pass(); + internal decorr_pass dp10 = new decorr_pass(); + internal decorr_pass dp11 = new decorr_pass(); + internal decorr_pass dp12 = new decorr_pass(); + internal decorr_pass dp13 = new decorr_pass(); + internal decorr_pass dp14 = new decorr_pass(); + internal decorr_pass dp15 = new decorr_pass(); + internal decorr_pass dp16 = new decorr_pass(); + + internal decorr_pass[] decorr_passes; +} diff --git a/WordsUtils.cs b/WordsUtils.cs new file mode 100644 index 0000000..2d612b0 --- /dev/null +++ b/WordsUtils.cs @@ -0,0 +1,735 @@ +using System; +/* +** WordsUtils.cs +** +** Copyright (c) 2010-2016 Peter McQuillan +** +** All Rights Reserved. +** +** Distributed under the BSD Software License (see license.txt) +***/ + +class WordsUtils +{ + + + + //////////////////////////////// local macros ///////////////////////////////// + + internal static int LIMIT_ONES = 16; // maximum consecutive 1s sent for "div" data + + // these control the time constant "slow_level" which is used for hybrid mode + // that controls bitrate as a function of residual level (HYBRID_BITRATE). + internal static int SLS = 8; + internal static int SLO = ((1 << (SLS - 1))); + + + // these control the time constant of the 3 median level breakpoints + internal static int DIV0 = 128; // 5/7 of samples + internal static int DIV1 = 64; // 10/49 of samples + internal static int DIV2 = 32; // 20/343 of samples + + + ///////////////////////////// local table storage //////////////////////////// + + internal static int[] nbits_table = + new int[]{ + 0, 1, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4, // 0 - 15 + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, // 16 - 31 + 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, // 32 - 47 + 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, // 48 - 63 + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, // 64 - 79 + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, // 80 - 95 + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, // 96 - 111 + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, // 112 - 127 + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, // 128 - 143 + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, // 144 - 159 + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, // 160 - 175 + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, // 176 - 191 + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, // 192 - 207 + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, // 208 - 223 + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, // 224 - 239 + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8 // 240 - 255 + }; + + internal static int[] log2_table = new int[]{0x00, 0x01, 0x03, 0x04, 0x06, 0x07, 0x09, 0x0a, 0x0b, 0x0d, 0x0e, 0x10, 0x11, 0x12, 0x14, 0x15, 0x16, 0x18, 0x19, 0x1a, 0x1c, 0x1d, 0x1e, 0x20, 0x21, 0x22, 0x24, 0x25, 0x26, 0x28, 0x29, 0x2a, 0x2c, 0x2d, 0x2e, 0x2f, 0x31, 0x32, 0x33, 0x34, 0x36, 0x37, 0x38, 0x39, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, 0x41, 0x42, 0x43, 0x44, 0x45, 0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4d, 0x4e, 0x4f, 0x50, 0x51, 0x52, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0x5c, 0x5d, 0x5e, 0x5f, 0x60, 0x61, 0x62, 0x63, 0x64, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, 0x70, 0x71, 0x72, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f, 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9a, 0x9b, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f, 0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, 0xa8, 0xa9, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf, 0xb0, 0xb1, 0xb2, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf, 0xc0, 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xcb, 0xcb, 0xcc, 0xcd, 0xce, 0xcf, 0xd0, 0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdc, 0xdd, 0xde, 0xdf, 0xe0, 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe4, 0xe5, 0xe6, 0xe7, 0xe7, 0xe8, 0xe9, 0xea, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xee, 0xef, 0xf0, 0xf1, 0xf1, 0xf2, 0xf3, 0xf4, 0xf4, 0xf5, 0xf6, 0xf7, 0xf7, 0xf8, 0xf9, 0xf9, 0xfa, 0xfb, 0xfc, 0xfc, 0xfd, 0xfe, 0xff, 0xff}; + + + + internal static int[] exp2_table = new int[]{0x00, 0x01, 0x01, 0x02, 0x03, 0x03, 0x04, 0x05, 0x06, 0x06, 0x07, 0x08, 0x08, 0x09, 0x0a, 0x0b, 0x0b, 0x0c, 0x0d, 0x0e, 0x0e, 0x0f, 0x10, 0x10, 0x11, 0x12, 0x13, 0x13, 0x14, 0x15, 0x16, 0x16, 0x17, 0x18, 0x19, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1d, 0x1e, 0x1f, 0x20, 0x20, 0x21, 0x22, 0x23, 0x24, 0x24, 0x25, 0x26, 0x27, 0x28, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2c, 0x2d, 0x2e, 0x2f, 0x30, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, 0x40, 0x41, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, 0x50, 0x51, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5e, 0x5f, 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x87, 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f, 0x90, 0x91, 0x92, 0x93, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9f, 0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xaf, 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb6, 0xb7, 0xb8, 0xb9, 0xba, 0xbc, 0xbd, 0xbe, 0xbf, 0xc0, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc8, 0xc9, 0xca, 0xcb, 0xcd, 0xce, 0xcf, 0xd0, 0xd2, 0xd3, 0xd4, 0xd6, 0xd7, 0xd8, 0xd9, 0xdb, 0xdc, 0xdd, 0xde, 0xe0, 0xe1, 0xe2, 0xe4, 0xe5, 0xe6, 0xe8, 0xe9, 0xea, 0xec, 0xed, 0xee, 0xf0, 0xf1, 0xf2, 0xf4, 0xf5, 0xf6, 0xf8, 0xf9, 0xfa, 0xfc, 0xfd, 0xff}; + + + internal static int[] ones_count_table = new int[] { + 0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,4,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,5, + 0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,4,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,6, + 0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,4,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,5, + 0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,4,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,7, + 0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,4,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,5, + 0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,4,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,6, + 0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,4,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,5, + 0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,4,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,8 + }; + + ///////////////////////////// executable code //////////////////////////////// + + + // Read the median log2 values from the specifed metadata structure, convert + // them back to 32-bit unsigned values and store them. If length is not + // exactly correct then we flag and return an error. + + internal static int read_entropy_vars(WavpackStream wps, WavpackMetadata wpmd) + { + byte[] byteptr = wpmd.data; + int[] b_array = new int[12]; + int i = 0; + words_data w = new words_data(); + + for (i = 0; i < 6; i++) + { + b_array[i] = (int) (byteptr[i] & 0xff); + } + + w.holding_one = 0; + w.holding_zero = 0; + + if (wpmd.byte_length != 12) + { + if ((wps.wphdr.flags & (Defines.MONO_FLAG | Defines.FALSE_STEREO)) == 0) + { + return Defines.FALSE; + } + } + + w.c[0].median[0] = exp2s(b_array[0] + (b_array[1] << 8)); + w.c[0].median[1] = exp2s(b_array[2] + (b_array[3] << 8)); + w.c[0].median[2] = exp2s(b_array[4] + (b_array[5] << 8)); + + if ((wps.wphdr.flags & (Defines.MONO_FLAG | Defines.FALSE_STEREO)) == 0) + { + for (i = 6; i < 12; i++) + { + b_array[i] = (int) (byteptr[i] & 0xff); + } + w.c[1].median[0] = exp2s(b_array[6] + (b_array[7] << 8)); + w.c[1].median[1] = exp2s(b_array[8] + (b_array[9] << 8)); + w.c[1].median[2] = exp2s(b_array[10] + (b_array[11] << 8)); + } + + wps.w = w; + + return Defines.TRUE; + } + + + // Read the hybrid related values from the specifed metadata structure, convert + // them back to their internal formats and store them. The extended profile + // stuff is not implemented yet, so return an error if we get more data than + // we know what to do with. + + internal static int read_hybrid_profile(WavpackStream wps, WavpackMetadata wpmd) + { + byte[] byteptr = wpmd.data; + int bytecnt = wpmd.byte_length; + int buffer_counter = 0; + int uns_buf = 0; + int uns_buf_plusone = 0; + + if ((wps.wphdr.flags & Defines.HYBRID_BITRATE) != 0) + { + uns_buf = (int) (byteptr[buffer_counter] & 0xff); + uns_buf_plusone = (int) (byteptr[buffer_counter + 1] & 0xff); + + wps.w.c[0].slow_level = exp2s(uns_buf + (uns_buf_plusone << 8)); + buffer_counter = buffer_counter + 2; + + if ((wps.wphdr.flags & (Defines.MONO_FLAG | Defines.FALSE_STEREO)) == 0) + { + uns_buf = (int) (byteptr[buffer_counter] & 0xff); + uns_buf_plusone = (int) (byteptr[buffer_counter + 1] & 0xff); + wps.w.c[1].slow_level = exp2s(uns_buf + (uns_buf_plusone << 8)); + buffer_counter = buffer_counter + 2; + } + } + + uns_buf = (int) (byteptr[buffer_counter] & 0xff); + uns_buf_plusone = (int) (byteptr[buffer_counter + 1] & 0xff); + + wps.w.bitrate_acc[0] = (int) (uns_buf + (uns_buf_plusone << 8)) << 16; + buffer_counter = buffer_counter + 2; + + if ((wps.wphdr.flags & (Defines.MONO_FLAG | Defines.FALSE_STEREO)) == 0) + { + uns_buf = (int) (byteptr[buffer_counter] & 0xff); + uns_buf_plusone = (int) (byteptr[buffer_counter + 1] & 0xff); + + wps.w.bitrate_acc[1] = (int) (uns_buf + (uns_buf_plusone << 8)) << 16; + buffer_counter = buffer_counter + 2; + } + + if (buffer_counter < bytecnt) + { + uns_buf = (int) (byteptr[buffer_counter] & 0xff); + uns_buf_plusone = (int) (byteptr[buffer_counter + 1] & 0xff); + + wps.w.bitrate_delta[0] = exp2s((short) (uns_buf + (uns_buf_plusone << 8))); + buffer_counter = buffer_counter + 2; + + if ((wps.wphdr.flags & (Defines.MONO_FLAG | Defines.FALSE_STEREO)) == 0) + { + uns_buf = (int) (byteptr[buffer_counter] & 0xff); + uns_buf_plusone = (int) (byteptr[buffer_counter + 1] & 0xff); + wps.w.bitrate_delta[1] = exp2s((short) (uns_buf + (uns_buf_plusone << 8))); + buffer_counter = buffer_counter + 2; + } + + if (buffer_counter < bytecnt) + return Defines.FALSE; + } + else + wps.w.bitrate_delta[0] = wps.w.bitrate_delta[1] = 0; + + return Defines.TRUE; + } + + // This function is called during both encoding and decoding of hybrid data to + // update the "error_limit" variable which determines the maximum sample error + // allowed in the main bitstream. In the HYBRID_BITRATE mode (which is the only + // currently implemented) this is calculated from the slow_level values and the + // bitrate accumulators. Note that the bitrate accumulators can be changing. + + internal static words_data update_error_limit(words_data w, long flags) + { + int bitrate_0 = (int) ((w.bitrate_acc[0] += w.bitrate_delta[0]) >> 16); + + if ((flags & (Defines.MONO_FLAG | Defines.FALSE_STEREO)) != 0) + { + if ((flags & Defines.HYBRID_BITRATE) != 0) + { + int slow_log_0 = (int) ((w.c[0].slow_level + SLO) >> SLS); + + if (slow_log_0 - bitrate_0 > - 0x100) + w.c[0].error_limit = exp2s(slow_log_0 - bitrate_0 + 0x100); + else + w.c[0].error_limit = 0; + } + else + w.c[0].error_limit = exp2s(bitrate_0); + } + else + { + int bitrate_1 = (int) ((w.bitrate_acc[1] += w.bitrate_delta[1]) >> 16); + + if ((flags & Defines.HYBRID_BITRATE) != 0) + { + int slow_log_0 = (int) ((w.c[0].slow_level + SLO) >> SLS); + int slow_log_1 = (int) ((w.c[1].slow_level + SLO) >> SLS); + + if ((flags & Defines.HYBRID_BALANCE) != 0) + { + int balance = (slow_log_1 - slow_log_0 + bitrate_1 + 1) >> 1; + + if (balance > bitrate_0) + { + bitrate_1 = bitrate_0 * 2; + bitrate_0 = 0; + } + else if (- balance > bitrate_0) + { + bitrate_0 = bitrate_0 * 2; + bitrate_1 = 0; + } + else + { + bitrate_1 = bitrate_0 + balance; + bitrate_0 = bitrate_0 - balance; + } + } + + if (slow_log_0 - bitrate_0 > - 0x100) + w.c[0].error_limit = exp2s(slow_log_0 - bitrate_0 + 0x100); + else + w.c[0].error_limit = 0; + + if (slow_log_1 - bitrate_1 > - 0x100) + w.c[1].error_limit = exp2s(slow_log_1 - bitrate_1 + 0x100); + else + w.c[1].error_limit = 0; + } + else + { + w.c[0].error_limit = exp2s(bitrate_0); + w.c[1].error_limit = exp2s(bitrate_1); + } + } + + return w; + } + + + // Read the next word from the bitstream "wvbits" and return the value. This + // function can be used for hybrid or lossless streams, but since an + // optimized version is available for lossless this function would normally + // be used for hybrid only. If a hybrid lossless stream is being read then + // the "correction" offset is written at the specified pointer. A return value + // of WORD_EOF indicates that the end of the bitstream was reached (all 1s) or + // some other error occurred. + + internal static int get_words(long nsamples, long flags, words_data w, Bitstream bs, int[] buffer, int bufferStartPos) + { + entropy_data[] c = w.c; + int csamples; + int buffer_counter = bufferStartPos; + int entidx = 1; + + if ((flags & (Defines.MONO_FLAG | Defines.FALSE_STEREO)) == 0) + // if not mono + { + nsamples *= 2; + } + else + { + // it is mono + entidx = 0; + } + + for (csamples = 0; csamples < nsamples; ++csamples) + { + + long ones_count, low, high, mid; + + if ((flags & (Defines.MONO_FLAG | Defines.FALSE_STEREO)) == 0) + // if not mono + { + if (entidx == 1) + entidx = 0; + else + entidx = 1; + } + + if ((w.c[0].median[0] & ~ 1) == 0 && w.holding_zero == 0 && w.holding_one == 0 && (w.c[1].median[0] & ~ 1) == 0) + { + + long mask; + int cbits; + + if (w.zeros_acc > 0) + { + --w.zeros_acc; + + if (w.zeros_acc > 0) + { + c[entidx].slow_level -= ((c[entidx].slow_level + SLO) >> SLS); + buffer[buffer_counter] = 0; + buffer_counter++; + continue; + } + } + else + { + cbits = 0; + bs = BitsUtils.getbit(bs); + + while (cbits < 33 && bs.bitval > 0) + { + cbits++; + bs = BitsUtils.getbit(bs); + } + + if (cbits == 33) + { + break; + } + + if (cbits < 2) + w.zeros_acc = cbits; + else + { + + --cbits; + + for (mask = 1, w.zeros_acc = 0; cbits > 0; mask <<= 1) + { + bs = BitsUtils.getbit(bs); + + if (bs.bitval > 0) + w.zeros_acc |= mask; + cbits--; + } + + w.zeros_acc |= mask; + } + + if (w.zeros_acc > 0) + { + c[entidx].slow_level -= ((c[entidx].slow_level + SLO) >> SLS); + w.c[0].median[0] = 0; + w.c[0].median[1] = 0; + w.c[0].median[2] = 0; + w.c[1].median[0] = 0; + w.c[1].median[1] = 0; + w.c[1].median[2] = 0; + + buffer[buffer_counter] = 0; + buffer_counter++; + continue; + } + } + } + + if (w.holding_zero > 0) + ones_count = w.holding_zero = 0; + else + { + int next8; + int uns_buf; + + if (bs.bc < 8) + { + + bs.ptr++; + bs.buf_index++; + + if (bs.ptr == bs.end) + bs = BitsUtils.bs_read(bs); + + uns_buf = (int) (bs.buf[bs.buf_index] & 0xff); + + bs.sr = bs.sr | (uns_buf << bs.bc); // values in buffer must be unsigned + + next8 = (int)(bs.sr & 0xff); + + bs.bc += 8; + } + else + next8 = (int)(bs.sr & 0xff); + + if (next8 == 0xff) + { + + bs.bc -= 8; + bs.sr >>= 8; + + ones_count = 8; + bs = BitsUtils.getbit(bs); + + while (ones_count < (LIMIT_ONES + 1) && bs.bitval > 0) + { + ones_count++; + bs = BitsUtils.getbit(bs); + } + + if (ones_count == (LIMIT_ONES + 1)) + { + break; + } + + if (ones_count == LIMIT_ONES) + { + int mask; + int cbits; + + cbits = 0; + bs = BitsUtils.getbit(bs); + + while (cbits < 33 && bs.bitval > 0) + { + cbits++; + bs = BitsUtils.getbit(bs); + } + + if (cbits == 33) + { + break; + } + + if (cbits < 2) + ones_count = cbits; + else + { + for (mask = 1, ones_count = 0; --cbits > 0; mask <<= 1) + { + bs = BitsUtils.getbit(bs); + + if (bs.bitval > 0) + ones_count |= mask; + } + ones_count |= mask; + } + + ones_count += LIMIT_ONES; + } + } + else + { + bs.bc = (int) (bs.bc - ((ones_count = ones_count_table[next8]) + 1)); + bs.sr = bs.sr >> (int) (ones_count + 1); // needs to be unsigned + } + + if (w.holding_one > 0) + { + w.holding_one = ones_count & 1; + ones_count = (ones_count >> 1) + 1; + } + else + { + w.holding_one = ones_count & 1; + ones_count >>= 1; + } + + w.holding_zero = (int) (~ w.holding_one & 1); + } + + if ((flags & Defines.HYBRID_FLAG) > 0 && ((flags & (Defines.MONO_FLAG | Defines.FALSE_STEREO)) > 0 || (csamples & 1) == 0)) + w = update_error_limit(w, flags); + + if (ones_count == 0) + { + low = 0; + high = (((c[entidx].median[0]) >> 4) + 1) - 1; + + // for c# I replace the division by DIV0 with >> 7 + c[entidx].median[0] -= (((c[entidx].median[0] + (DIV0 - 2)) >> 7) * 2); + } + else + { + low = (((c[entidx].median[0]) >> 4) + 1); + + // for c# I replace the division by DIV0 with >> 7 + c[entidx].median[0] += ((c[entidx].median[0] + DIV0) >> 7) * 5; + + if (ones_count == 1) + { + + high = low + (((c[entidx].median[1]) >> 4) + 1) - 1; + // for c# I replace the division by DIV1 with >> 6 + c[entidx].median[1] -= ((c[entidx].median[1] + (DIV1 - 2)) >> 6) * 2; + } + else + { + low += (((c[entidx].median[1]) >> 4) + 1); + // for c# I replace the division by DIV1 with >> 6 + c[entidx].median[1] += ((c[entidx].median[1] + DIV1) >> 6) * 5; + + if (ones_count == 2) + { + high = low + (((c[entidx].median[2]) >> 4) + 1) - 1; + // for c# I replace the division by DIV2 with >> 5 + c[entidx].median[2] -= ((c[entidx].median[2] + (DIV2 - 2)) >> 5) * 2; + } + else + { + low += (ones_count - 2) * (((c[entidx].median[2]) >> 4) + 1); + high = low + (((c[entidx].median[2]) >> 4) + 1) - 1; + // for c# I replace the division by DIV2 with >> 5 + c[entidx].median[2] += ((c[entidx].median[2] + DIV2) >> 5) * 5; + } + } + } + + mid = (high + low + 1) >> 1; + + if (c[entidx].error_limit == 0) + { + mid = read_code(bs, high - low); + + mid = mid + low; + } + else + while (high - low > c[entidx].error_limit) + { + + bs = BitsUtils.getbit(bs); + + if (bs.bitval > 0) + { + mid = (high + (low = mid) + 1) >> 1; + } + else + { + mid = ((high = mid - 1) + low + 1) >> 1; + } + } + + bs = BitsUtils.getbit(bs); + + if (bs.bitval > 0) + { + buffer[buffer_counter] = (int) ~ mid; + } + else + { + buffer[buffer_counter] = (int) mid; + } + + buffer_counter++; + + if ((flags & Defines.HYBRID_BITRATE) > 0) + c[entidx].slow_level = c[entidx].slow_level - ((c[entidx].slow_level + SLO) >> SLS) + mylog2(mid); + } + + w.c = c; + + if ((flags & (Defines.MONO_FLAG | Defines.FALSE_STEREO)) != 0) + { + return csamples; + } + else + { + return (csamples / 2); + } + } + + internal static int count_bits(long av) + { + if (av < 256) // 1 << 8 + { + return nbits_table[av]; + } + else + { + if (av < 65536) // 1 << 16 + { + return nbits_table[(av>>8)] + 8; + } + else + { + if (av < 16777216) // 1 << 24 + { + return nbits_table[(av>>16)] + 16; + } + else + { + return nbits_table[(av>>24)] + 24; + } + } + } + } + + + // Read a single unsigned value from the specified bitstream with a value + // from 0 to maxcode. If there are exactly a power of two number of possible + // codes then this will read a fixed number of bits; otherwise it reads the + // minimum number of bits and then determines whether another bit is needed + // to define the code. + + internal static long read_code(Bitstream bs, long maxcode) + { + int bitcount = count_bits(maxcode); + long extras = (1 << bitcount) - maxcode - 1; + long code; + + if (bitcount == 0) + { + return (0); + } + + code = BitsUtils.getbits(bitcount - 1, bs); + + code &= (1 << (bitcount - 1)) - 1; + + if (code >= extras) + { + + code = (code << 1) - extras; + + bs = BitsUtils.getbit(bs); + + if (bs.bitval > 0) + + ++code; + } + + return (code); + } + + + // The concept of a base 2 logarithm is used in many parts of WavPack. It is + // a way of sufficiently accurately representing 32-bit signed and unsigned + // values storing only 16 bits (actually fewer). It is also used in the hybrid + // mode for quickly comparing the relative magnitude of large values (i.e. + // division) and providing smooth exponentials using only addition. + + // These are not strict logarithms in that they become linear around zero and + // can therefore represent both zero and negative values. They have 8 bits + // of precision and in "roundtrip" conversions the total error never exceeds 1 + // part in 225 except for the cases of +/-115 and +/-195 (which error by 1). + + + // This function returns the log2 for the specified 32-bit unsigned value. + // The maximum value allowed is about 0xff800000 and returns 8447. + + internal static int mylog2(long avalue) + { + int dbits; + + if ((avalue += (avalue >> 9)) < (1 << 8)) + { + dbits = nbits_table[(int) avalue]; + return (dbits << 8) + log2_table[(int) (avalue << (9 - dbits)) & 0xff]; + } + else + { + if (avalue < (1L << 16)) + dbits = nbits_table[(int) (avalue >> 8)] + 8; + else if (avalue < (1L << 24)) + dbits = nbits_table[(int) (avalue >> 16)] + 16; + else + dbits = nbits_table[(int) (avalue >> 24)] + 24; + + return (dbits << 8) + log2_table[(int) (avalue >> (dbits - 9)) & 0xff]; + } + } + + + // This function returns the log2 for the specified 32-bit signed value. + // All input values are valid and the return values are in the range of + // +/- 8192. + + internal virtual int log2s(int value_Renamed) + { + if (value_Renamed < 0) + { + return - mylog2(- value_Renamed); + } + else + { + return mylog2(value_Renamed); + } + } + + + // This function returns the original integer represented by the supplied + // logarithm (at least within the provided accuracy). The log is signed, + // but since a full 32-bit value is returned this can be used for unsigned + // conversions as well (i.e. the input range is -8192 to +8447). + + internal static int exp2s(int log) + { + long value_Renamed; + + if (log < 0) + return - exp2s(- log); + + value_Renamed = exp2_table[log & 0xff] | 0x100; + + if ((log >>= 8) <= 9) + return ((int) (value_Renamed >> (9 - log))); + else + return ((int) (value_Renamed << (log - 9))); + } + + + // These two functions convert internal weights (which are normally +/-1024) + // to and from an 8-bit signed character version for storage in metadata. The + // weights are clipped here in the case that they are outside that range. + + internal static int restore_weight(sbyte weight) + { + int result; + + if ((result = (int) weight << 3) > 0) + result += ((result + 64) >> 7); + + return result; + } +} diff --git a/WvDemo.cs b/WvDemo.cs new file mode 100644 index 0000000..193b8e6 --- /dev/null +++ b/WvDemo.cs @@ -0,0 +1,336 @@ +using System; +/* +** WvDemo.cs +** +** Copyright (c) 2010-2016 Peter McQuillan +** +** All Rights Reserved. +** +** Distributed under the BSD Software License (see license.txt) +***/ + +public class WvDemo +{ + public WvDemo() + { + InitBlock(); + } + private void InitBlock() + { + temp_buffer = new int[Defines.SAMPLE_BUFFER_SIZE]; + pcm_buffer = new byte[4 * Defines.SAMPLE_BUFFER_SIZE]; + } + + internal static int[] temp_buffer; + + internal static byte[] pcm_buffer; + + [STAThread] + public static void Main(System.String[] args) + { + ChunkHeader FormatChunkHeader = new ChunkHeader(); + ChunkHeader DataChunkHeader = new ChunkHeader(); + RiffChunkHeader myRiffChunkHeader = new RiffChunkHeader(); + WaveHeader WaveHeader = new WaveHeader(); + sbyte[] myRiffChunkHeaderAsByteArray = new sbyte[12]; + sbyte[] myFormatChunkHeaderAsByteArray = new sbyte[8]; + sbyte[] myWaveHeaderAsByteArray = new sbyte[16]; + sbyte[] myDataChunkHeaderAsByteArray = new sbyte[8]; + + long total_unpacked_samples = 0, total_samples; // was uint32_t in C + int num_channels, bps; + WavpackContext wpc = new WavpackContext(); + System.IO.FileStream fistream; + System.IO.FileStream fostream; + System.IO.BinaryReader in_Renamed; + long start, end; + + System.String inputWVFile; + + if (args.Length == 0) + { + inputWVFile = "input.wv"; + } + else + { + inputWVFile = args[0]; + } + + try + { + fistream = new System.IO.FileStream(inputWVFile, System.IO.FileMode.Open, System.IO.FileAccess.Read); + System.IO.BufferedStream bstream = new System.IO.BufferedStream(fistream,16384); + in_Renamed = new System.IO.BinaryReader(bstream); + wpc = WavPackUtils.WavpackOpenFileInput(in_Renamed); + } + catch (System.IO.FileNotFoundException) + { + System.Console.Error.WriteLine("Input file not found"); + System.Environment.Exit(1); + } + catch (System.IO.DirectoryNotFoundException) + { + System.Console.Error.WriteLine("Input file not found - invalid directory"); + System.Environment.Exit(1); + } + + if (wpc.error) + { + System.Console.Error.WriteLine("Sorry an error has occured"); + System.Console.Error.WriteLine(wpc.error_message); + System.Environment.Exit(1); + } + + num_channels = WavPackUtils.WavpackGetReducedChannels(wpc); + + System.Console.Out.WriteLine("The WavPack file has " + num_channels + " channels"); + + total_samples = WavPackUtils.WavpackGetNumSamples(wpc); + + System.Console.Out.WriteLine("The WavPack file has " + total_samples + " samples"); + + bps = WavPackUtils.WavpackGetBytesPerSample(wpc); + + System.Console.Out.WriteLine("The WavPack file has " + bps + " bytes per sample"); + + myRiffChunkHeader.ckID[0] = 'R'; + myRiffChunkHeader.ckID[1] = 'I'; + myRiffChunkHeader.ckID[2] = 'F'; + myRiffChunkHeader.ckID[3] = 'F'; + + myRiffChunkHeader.ckSize = total_samples * num_channels * bps + 8 * 2 + 16 + 4; + myRiffChunkHeader.formType[0] = 'W'; + myRiffChunkHeader.formType[1] = 'A'; + myRiffChunkHeader.formType[2] = 'V'; + myRiffChunkHeader.formType[3] = 'E'; + + FormatChunkHeader.ckID[0] = 'f'; + FormatChunkHeader.ckID[1] = 'm'; + FormatChunkHeader.ckID[2] = 't'; + FormatChunkHeader.ckID[3] = ' '; + + FormatChunkHeader.ckSize = 16; + + WaveHeader.FormatTag = 1; + WaveHeader.NumChannels = num_channels; + WaveHeader.SampleRate = WavPackUtils.WavpackGetSampleRate(wpc); + WaveHeader.BlockAlign = num_channels * bps; + WaveHeader.BytesPerSecond = WaveHeader.SampleRate * WaveHeader.BlockAlign; + WaveHeader.BitsPerSample = WavPackUtils.WavpackGetBitsPerSample(wpc); + + DataChunkHeader.ckID[0] = 'd'; + DataChunkHeader.ckID[1] = 'a'; + DataChunkHeader.ckID[2] = 't'; + DataChunkHeader.ckID[3] = 'a'; + DataChunkHeader.ckSize = total_samples * num_channels * bps; + + myRiffChunkHeaderAsByteArray[0] = (sbyte) myRiffChunkHeader.ckID[0]; + myRiffChunkHeaderAsByteArray[1] = (sbyte) myRiffChunkHeader.ckID[1]; + myRiffChunkHeaderAsByteArray[2] = (sbyte) myRiffChunkHeader.ckID[2]; + myRiffChunkHeaderAsByteArray[3] = (sbyte) myRiffChunkHeader.ckID[3]; + + // swap endians here + + myRiffChunkHeaderAsByteArray[7] = (sbyte) (SupportClass.URShift(myRiffChunkHeader.ckSize, 24)); + myRiffChunkHeaderAsByteArray[6] = (sbyte) (SupportClass.URShift(myRiffChunkHeader.ckSize, 16)); + myRiffChunkHeaderAsByteArray[5] = (sbyte) (SupportClass.URShift(myRiffChunkHeader.ckSize, 8)); + myRiffChunkHeaderAsByteArray[4] = (sbyte) (myRiffChunkHeader.ckSize); + + myRiffChunkHeaderAsByteArray[8] = (sbyte) myRiffChunkHeader.formType[0]; + myRiffChunkHeaderAsByteArray[9] = (sbyte) myRiffChunkHeader.formType[1]; + myRiffChunkHeaderAsByteArray[10] = (sbyte) myRiffChunkHeader.formType[2]; + myRiffChunkHeaderAsByteArray[11] = (sbyte) myRiffChunkHeader.formType[3]; + + myFormatChunkHeaderAsByteArray[0] = (sbyte) FormatChunkHeader.ckID[0]; + myFormatChunkHeaderAsByteArray[1] = (sbyte) FormatChunkHeader.ckID[1]; + myFormatChunkHeaderAsByteArray[2] = (sbyte) FormatChunkHeader.ckID[2]; + myFormatChunkHeaderAsByteArray[3] = (sbyte) FormatChunkHeader.ckID[3]; + + // swap endians here + myFormatChunkHeaderAsByteArray[7] = (sbyte) (SupportClass.URShift(FormatChunkHeader.ckSize, 24)); + myFormatChunkHeaderAsByteArray[6] = (sbyte) (SupportClass.URShift(FormatChunkHeader.ckSize, 16)); + myFormatChunkHeaderAsByteArray[5] = (sbyte) (SupportClass.URShift(FormatChunkHeader.ckSize, 8)); + myFormatChunkHeaderAsByteArray[4] = (sbyte) (FormatChunkHeader.ckSize); + + // swap endians + myWaveHeaderAsByteArray[1] = (sbyte) (SupportClass.URShift(WaveHeader.FormatTag, 8)); + myWaveHeaderAsByteArray[0] = (sbyte) (WaveHeader.FormatTag); + + // swap endians + myWaveHeaderAsByteArray[3] = (sbyte) (SupportClass.URShift(WaveHeader.NumChannels, 8)); + myWaveHeaderAsByteArray[2] = (sbyte) WaveHeader.NumChannels; + + + // swap endians + myWaveHeaderAsByteArray[7] = (sbyte) (SupportClass.URShift(WaveHeader.SampleRate, 24)); + myWaveHeaderAsByteArray[6] = (sbyte) (SupportClass.URShift(WaveHeader.SampleRate, 16)); + myWaveHeaderAsByteArray[5] = (sbyte) (SupportClass.URShift(WaveHeader.SampleRate, 8)); + myWaveHeaderAsByteArray[4] = (sbyte) (WaveHeader.SampleRate); + + // swap endians + + myWaveHeaderAsByteArray[11] = (sbyte) (SupportClass.URShift(WaveHeader.BytesPerSecond, 24)); + myWaveHeaderAsByteArray[10] = (sbyte) (SupportClass.URShift(WaveHeader.BytesPerSecond, 16)); + myWaveHeaderAsByteArray[9] = (sbyte) (SupportClass.URShift(WaveHeader.BytesPerSecond, 8)); + myWaveHeaderAsByteArray[8] = (sbyte) (WaveHeader.BytesPerSecond); + + // swap endians + myWaveHeaderAsByteArray[13] = (sbyte) (SupportClass.URShift(WaveHeader.BlockAlign, 8)); + myWaveHeaderAsByteArray[12] = (sbyte) WaveHeader.BlockAlign; + + // swap endians + myWaveHeaderAsByteArray[15] = (sbyte) (SupportClass.URShift(WaveHeader.BitsPerSample, 8)); + myWaveHeaderAsByteArray[14] = (sbyte) WaveHeader.BitsPerSample; + + myDataChunkHeaderAsByteArray[0] = (sbyte) DataChunkHeader.ckID[0]; + myDataChunkHeaderAsByteArray[1] = (sbyte) DataChunkHeader.ckID[1]; + myDataChunkHeaderAsByteArray[2] = (sbyte) DataChunkHeader.ckID[2]; + myDataChunkHeaderAsByteArray[3] = (sbyte) DataChunkHeader.ckID[3]; + + // swap endians + + myDataChunkHeaderAsByteArray[7] = (sbyte) (SupportClass.URShift(DataChunkHeader.ckSize, 24)); + myDataChunkHeaderAsByteArray[6] = (sbyte) (SupportClass.URShift(DataChunkHeader.ckSize, 16)); + myDataChunkHeaderAsByteArray[5] = (sbyte) (SupportClass.URShift(DataChunkHeader.ckSize, 8)); + myDataChunkHeaderAsByteArray[4] = (sbyte) DataChunkHeader.ckSize; + + try + { + fostream = new System.IO.FileStream("output.wav", System.IO.FileMode.Create); + SupportClass.WriteOutput(fostream, myRiffChunkHeaderAsByteArray); + SupportClass.WriteOutput(fostream, myFormatChunkHeaderAsByteArray); + SupportClass.WriteOutput(fostream, myWaveHeaderAsByteArray); + SupportClass.WriteOutput(fostream, myDataChunkHeaderAsByteArray); + + start = (System.DateTime.Now.Ticks - 621355968000000000) / 10000; + + while (true) + { + long samples_unpacked; // was uint32_t in C + + samples_unpacked = WavPackUtils.WavpackUnpackSamples(wpc, temp_buffer, Defines.SAMPLE_BUFFER_SIZE / num_channels); + + total_unpacked_samples += samples_unpacked; + + if (samples_unpacked > 0) + { + samples_unpacked = samples_unpacked * num_channels; + + pcm_buffer = format_samples(bps, temp_buffer, samples_unpacked); + fostream.Write(pcm_buffer, 0, (int) samples_unpacked * bps); + } + + if (samples_unpacked == 0) + break; + } // end of while + + end = (System.DateTime.Now.Ticks - 621355968000000000) / 10000; + + System.Console.Out.WriteLine(end - start + " milli seconds to process WavPack file in main loop"); + } + catch (System.Exception e) + { + System.Console.Error.WriteLine("Error when writing wav file, sorry: "); + SupportClass.WriteStackTrace(e, Console.Error); + System.Environment.Exit(1); + } + + if ((WavPackUtils.WavpackGetNumSamples(wpc) != - 1) && (total_unpacked_samples != WavPackUtils.WavpackGetNumSamples(wpc))) + { + System.Console.Error.WriteLine("Incorrect number of samples"); + System.Environment.Exit(1); + } + + if (WavPackUtils.WavpackGetNumErrors(wpc) > 0) + { + System.Console.Error.WriteLine("CRC errors detected"); + System.Environment.Exit(1); + } + + System.Environment.Exit(0); + } + + + // Reformat samples from longs in processor's native endian mode to + // little-endian data with (possibly) less than 4 bytes / sample. + + internal static byte[] format_samples(int bps, int[] src, long samcnt) + { + int temp; + int counter = 0; + int counter2 = 0; + byte[] dst = new byte[4 * Defines.SAMPLE_BUFFER_SIZE]; + + switch (bps) + { + + case 1: + while (samcnt > 0) + { + dst[counter] = (byte) (0x00FF & (src[counter] + 128)); + counter++; + samcnt--; + } + break; + + + case 2: + while (samcnt > 0) + { + temp = src[counter2]; + dst[counter] = (byte) temp; + counter++; + //dst[counter] = (byte) (SupportClass.URShift(temp, 8)); + dst[counter] = (byte) (temp >> 8); + counter++; + counter2++; + samcnt--; + } + + break; + + + case 3: + while (samcnt > 0) + { + temp = src[counter2]; + dst[counter] = (byte) temp; + counter++; + dst[counter] = (byte)(temp >> 8); + counter++; + dst[counter] = (byte)(temp >> 16); + counter++; + counter2++; + samcnt--; + } + + break; + + + case 4: + while (samcnt > 0) + { + temp = src[counter2]; + dst[counter] = (byte) temp; + counter++; + dst[counter] = (byte) (SupportClass.URShift(temp, 8)); + counter++; + dst[counter] = (byte) (SupportClass.URShift(temp, 16)); + counter++; + dst[counter] = (byte) (SupportClass.URShift(temp, 24)); + counter++; + counter2++; + samcnt--; + } + + break; + } + + return dst; + } + static WvDemo() + { + temp_buffer = new int[Defines.SAMPLE_BUFFER_SIZE]; + pcm_buffer = new byte[4 * Defines.SAMPLE_BUFFER_SIZE]; + } +} diff --git a/decorr_pass.cs b/decorr_pass.cs new file mode 100644 index 0000000..00a55ad --- /dev/null +++ b/decorr_pass.cs @@ -0,0 +1,26 @@ +using System; +/* +** decorr_pass.cs +** +** Copyright (c) 2010-2016 Peter McQuillan +** +** All Rights Reserved. +** +** Distributed under the BSD Software License (see license.txt) +***/ + +class decorr_pass +{ + public decorr_pass() + { + InitBlock(); + } + private void InitBlock() + { + samples_A = new int[Defines.MAX_TERM]; + samples_B = new int[Defines.MAX_TERM]; + } + internal short term, delta, weight_A, weight_B; + internal int[] samples_A; + internal int[] samples_B; +} diff --git a/entropy_data.cs b/entropy_data.cs new file mode 100644 index 0000000..6c02035 --- /dev/null +++ b/entropy_data.cs @@ -0,0 +1,17 @@ +using System; +/* +** entropy_data.cs +** +** Copyright (c) 2010-2016 Peter McQuillan +** +** All Rights Reserved. +** +** Distributed under the BSD Software License (see license.txt) +***/ + +class entropy_data +{ + internal long slow_level; + internal int[] median = new int[]{0, 0, 0}; // was uint32_t in C, we initialize in order to remove run time errors + internal long error_limit; // was uint32_t in C +} diff --git a/license.txt b/license.txt new file mode 100644 index 0000000..a1592ab --- /dev/null +++ b/license.txt @@ -0,0 +1,30 @@ +Copyright (c) 2010-2016 Peter McQuillan + +Portions Copyright (c) 1998 - 2016 Conifer Software + +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * The name of the author may not be used to endorse or promote products + derived from this software without specific prior written permission. + * Neither the name of Conifer Software nor the names of its contributors + may be used to endorse or promote products derived from this software + without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/readme.txt b/readme.txt new file mode 100644 index 0000000..ae727f2 --- /dev/null +++ b/readme.txt @@ -0,0 +1,25 @@ +//////////////////////////////////////////////////////////////////////////// +// C# Implementation of WavPack Decoder // +// Copyright (c) 2010-2016 Peter McQuillan // +// All Rights Reserved. // +// Distributed under the BSD Software License (see license.txt) // +//////////////////////////////////////////////////////////////////////////// + +This package contains a C# implementation of the tiny version of the WavPack +4.40 decoder. It is packaged with a demo command-line program that accepts a +WavPack audio file as input and outputs a RIFF wav file (with the filename +output.wav). The program was developed using MonoDevelop but has also been +tested against Visual Studio. + +To run the demo program, use the following command + +CSharpWavPackDecoder + +where input.wv is the name of the WavPack file you wish to decode to a WAV file. + +This decoder will not handle "correction" files, plays only the first two +channels of multi-channel files, and is limited in resolution in some large +integer or floating point files (but always provides at least 24 bits of +resolution). It also will not accept WavPack files from before version 4.0. + +Please direct any questions or comments to beatofthedrum@gmail.com \ No newline at end of file diff --git a/words_data.cs b/words_data.cs new file mode 100644 index 0000000..8b34c5e --- /dev/null +++ b/words_data.cs @@ -0,0 +1,30 @@ +using System; +/* +** words_data.cs +** +** Copyright (c) 2010-2016 Peter McQuillan +** +** All Rights Reserved. +** +** Distributed under the BSD Software License (see license.txt) +***/ + +class words_data +{ + public words_data() + { + InitBlock(); + } + private void InitBlock() + { + c = new entropy_data[]{temp_ed1, temp_ed2}; + } + internal long[] bitrate_delta = new long[2]; // was uint32_t in C + internal long[] bitrate_acc = new long[2]; // was uint32_t in C + internal long holding_one, zeros_acc; // was uint32_t in C + internal int holding_zero; + + internal entropy_data temp_ed1 = new entropy_data(); + internal entropy_data temp_ed2 = new entropy_data(); + internal entropy_data[] c; +}