diff --git a/CUETools.Codecs.FLAKE/BitReader.cs b/CUETools.Codecs.FLAKE/BitReader.cs
new file mode 100644
index 0000000..1305464
--- /dev/null
+++ b/CUETools.Codecs.FLAKE/BitReader.cs
@@ -0,0 +1,245 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace CUETools.Codecs.FLAKE
+{
+ class BitReader
+ {
+ byte[] buffer;
+ byte[] byte_to_unary_table;
+ int pos, len;
+ int _bitaccumulator;
+
+ public int Position
+ {
+ get { return pos; }
+ }
+
+ public byte[] Buffer
+ {
+ get
+ {
+ return buffer;
+ }
+ }
+
+ public BitReader(byte[] _buffer, int _pos, int _len)
+ {
+ buffer = _buffer;
+ pos = _pos;
+ len = _len;
+ _bitaccumulator = 0;
+
+ byte_to_unary_table = new byte[] {
+ 8, 7, 6, 6, 5, 5, 5, 5, 4, 4, 4, 4, 4, 4, 4, 4,
+ 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+ };
+ }
+
+ /* supports reading 1 to 24 bits, in big endian format */
+ public uint readbits_24(int bits)
+ {
+ uint result = (((uint)buffer[pos]) << 24) | (((uint)buffer[pos + 1]) << 16) | (((uint)buffer[pos + 2]) << 8) | ((uint)buffer[pos + 3]);
+ result <<= _bitaccumulator;
+ result >>= 32 - bits;
+
+ int new_accumulator = (_bitaccumulator + bits);
+ pos += (new_accumulator >> 3);
+ _bitaccumulator = (new_accumulator & 7);
+ return result;
+ }
+
+ public uint readbits_8(int bits)
+ {
+ uint result = (((uint)buffer[pos]) << 24) | (((uint)buffer[pos + 1]) << 16);
+ result <<= _bitaccumulator;
+ result >>= 32 - bits;
+
+ int new_accumulator = (_bitaccumulator + bits);
+ pos += (new_accumulator >> 3);
+ _bitaccumulator = (new_accumulator & 7);
+ return result;
+ }
+
+ public uint peekbits_24(int bits)
+ {
+ uint result = (((uint)buffer[pos]) << 24) | (((uint)buffer[pos + 1]) << 16) | (((uint)buffer[pos + 2]) << 8) | ((uint)buffer[pos + 3]);
+ result <<= _bitaccumulator;
+ result >>= 32 - bits;
+ return result;
+ }
+
+ ///* supports reading 1 to 16 bits, in big endian format */
+ //private unsafe uint peekbits_9(byte* buff, int pos)
+ //{
+ // uint result = (((uint)buff[pos]) << 8) | (((uint)buff[pos + 1]));
+ // result <<= _bitaccumulator;
+ // result &= 0x0000ffff;
+ // result >>= 7;
+ // return result;
+ //}
+
+ /* supports reading 1 to 16 bits, in big endian format */
+ public void skipbits(int bits)
+ {
+ int new_accumulator = (_bitaccumulator + bits);
+ pos += (new_accumulator >> 3);
+ _bitaccumulator = (new_accumulator & 7);
+ }
+
+ /* supports reading 1 to 32 bits, in big endian format */
+ public uint readbits(int bits)
+ {
+ if (bits <= 24)
+ return readbits_24(bits);
+
+ ulong result = (((ulong)buffer[pos]) << 32) | (((ulong)buffer[pos + 1]) << 24) | (((ulong)buffer[pos + 2]) << 16) | (((ulong)buffer[pos + 3]) << 8) | ((ulong)buffer[pos + 4]);
+ result <<= _bitaccumulator;
+ result &= 0x00ffffffffff;
+ result >>= 40 - bits;
+ int new_accumulator = (_bitaccumulator + bits);
+ pos += (new_accumulator >> 3);
+ _bitaccumulator = (new_accumulator & 7);
+ return (uint)result;
+ }
+
+ /* reads a single bit */
+ public uint readbit()
+ {
+ int new_accumulator;
+ uint result = buffer[pos];
+ result <<= _bitaccumulator;
+ result = result >> 7 & 1;
+ new_accumulator = (_bitaccumulator + 1);
+ pos += (new_accumulator / 8);
+ _bitaccumulator = (new_accumulator % 8);
+ return result;
+ }
+
+ public uint read_unary()
+ {
+ uint val = 0;
+
+ int result = (buffer[pos] << _bitaccumulator) & 0xff;
+ if (result == 0)
+ {
+ val = 8 - (uint)_bitaccumulator;
+ _bitaccumulator = 0;
+ pos++;
+ return val + read_unary();
+ // check eof
+ }
+
+ val = byte_to_unary_table[result];
+
+ int new_accumulator = (_bitaccumulator + (int)val + 1);
+ pos += (new_accumulator / 8);
+ _bitaccumulator = (new_accumulator % 8);
+ return val;
+ }
+
+ public void flush()
+ {
+ if (_bitaccumulator > 0)
+ readbits(8 - _bitaccumulator);
+ }
+
+ public int readbits_signed(int bits)
+ {
+ int val = (int) readbits(bits);
+ val <<= (32 - bits);
+ val >>= (32 - bits);
+ return val;
+ }
+
+ public uint read_utf8()
+ {
+ uint x = readbits(8);
+ uint v;
+ int i;
+ if (0 == (x & 0x80))
+ {
+ v = x;
+ i = 0;
+ }
+ else if (0xC0 == (x & 0xE0)) /* 110xxxxx */
+ {
+ v = x & 0x1F;
+ i = 1;
+ }
+ else if (0xE0 == (x & 0xF0)) /* 1110xxxx */
+ {
+ v = x & 0x0F;
+ i = 2;
+ }
+ else if (0xF0 == (x & 0xF8)) /* 11110xxx */
+ {
+ v = x & 0x07;
+ i = 3;
+ }
+ else if (0xF8 == (x & 0xFC)) /* 111110xx */
+ {
+ v = x & 0x03;
+ i = 4;
+ }
+ else if (0xFC == (x & 0xFE)) /* 1111110x */
+ {
+ v = x & 0x01;
+ i = 5;
+ }
+ else if (0xFE == x) /* 11111110 */
+ {
+ v = 0;
+ i = 6;
+ }
+ else
+ throw new Exception("invalid utf8 encoding");
+ for (; i > 0; i--)
+ {
+ x = readbits(8);
+ if (0x80 != (x & 0xC0)) /* 10xxxxxx */
+ throw new Exception("invalid utf8 encoding");
+ v <<= 6;
+ v |= (x & 0x3F);
+ }
+ return v;
+ }
+
+ public int read_rice_signed(int k)
+ {
+ uint msbs = read_unary();
+ uint lsbs = readbits_24(k);
+ uint uval = (msbs << k) | lsbs;
+ return (int)(uval >> 1 ^ -(int)(uval & 1));
+ }
+
+ public int read_rice_signed8(int k)
+ {
+ uint msbs = read_unary();
+ uint lsbs = readbits_8(k);
+ uint uval = (msbs << k) | lsbs;
+ return (int)(uval >> 1 ^ -(int)(uval & 1));
+ }
+
+ public int read_unary_signed()
+ {
+ uint uval = read_unary();
+ return (int)(uval >> 1 ^ -(int)(uval & 1));
+ }
+ }
+}
diff --git a/CUETools.Codecs.FLAKE/BitWriter.cs b/CUETools.Codecs.FLAKE/BitWriter.cs
new file mode 100644
index 0000000..0f727b7
--- /dev/null
+++ b/CUETools.Codecs.FLAKE/BitWriter.cs
@@ -0,0 +1,154 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace CUETools.Codecs.FLAKE
+{
+ class BitWriter
+ {
+ uint bit_buf;
+ int bit_left;
+ byte[] buffer;
+ int buf_start, buf_ptr, buf_end;
+ bool eof;
+
+ public BitWriter(byte[] buf, int pos, int len)
+ {
+ buffer = buf;
+ buf_start = pos;
+ buf_ptr = pos;
+ buf_end = pos + len;
+ bit_left = 32;
+ bit_buf = 0;
+ eof = false;
+ }
+
+ public void writebits_signed(int bits, int val)
+ {
+ writebits(bits, val & ((1 << bits) - 1));
+ }
+
+ public void writebits_signed(uint bits, int val)
+ {
+ writebits((int) bits, val & ((1 << (int) bits) - 1));
+ }
+
+ public void writebits(int bits, int val)
+ {
+ writebits(bits, (uint)val);
+ }
+
+ public void writebits(int bits, uint val)
+ {
+ //assert(bits == 32 || val < (1U << bits));
+
+ if (bits == 0 || eof) return;
+ if ((buf_ptr + 3) >= buf_end)
+ {
+ eof = true;
+ return;
+ }
+ if (bits < bit_left)
+ {
+ bit_buf = (bit_buf << bits) | val;
+ bit_left -= bits;
+ }
+ else
+ {
+ uint bb = 0;
+ if (bit_left == 32)
+ {
+ //assert(bits == 32);
+ bb = val;
+ }
+ else
+ {
+ bb = (bit_buf << bit_left) | (val >> (bits - bit_left));
+ bit_left += (32 - bits);
+ }
+ if (buffer != null)
+ {
+ buffer[buf_ptr + 3] = (byte)(bb & 0xFF); bb >>= 8;
+ buffer[buf_ptr + 2] = (byte)(bb & 0xFF); bb >>= 8;
+ buffer[buf_ptr + 1] = (byte)(bb & 0xFF); bb >>= 8;
+ buffer[buf_ptr + 0] = (byte)(bb & 0xFF);
+ }
+ buf_ptr += 4;
+ bit_buf = val;
+ }
+ }
+
+ public void write_utf8(int val)
+ {
+ write_utf8((uint)val);
+ }
+
+ public void write_utf8(uint val)
+ {
+ if (val < 0x80)
+ {
+ writebits(8, val);
+ return;
+ }
+ int bytes = (Flake.log2i(val) + 4) / 5;
+ int shift = (bytes - 1) * 6;
+ writebits(8, (256U - (256U >> bytes)) | (val >> shift));
+ while (shift >= 6)
+ {
+ shift -= 6;
+ writebits(8, 0x80 | ((val >> shift) & 0x3F));
+ }
+ }
+
+ public void write_rice_signed(int k, int val)
+ {
+ int v, q;
+
+ if (k < 0) return;
+
+ // convert signed to unsigned
+ v = -2 * val - 1;
+ v ^= (v >> 31);
+
+ // write quotient in unary
+ q = (v >> k) + 1;
+ while (q > 31)
+ {
+ writebits(31, 0);
+ q -= 31;
+ }
+ writebits(q, 1);
+
+ // write write remainder in binary using 'k' bits
+ writebits(k, v & ((1 << k) - 1));
+ }
+
+ public void flush()
+ {
+ bit_buf <<= bit_left;
+ while (bit_left < 32 && !eof)
+ {
+ if (buf_ptr >= buf_end)
+ {
+ eof = true;
+ break;
+ }
+ if (buffer != null)
+ buffer[buf_ptr] = (byte)(bit_buf >> 24);
+ buf_ptr++;
+ bit_buf <<= 8;
+ bit_left += 8;
+ }
+ bit_left = 32;
+ bit_buf = 0;
+ }
+
+ public int Length
+ {
+ get
+ {
+ return buf_ptr - buf_start;
+ }
+ }
+ }
+}
diff --git a/CUETools.Codecs.FLAKE/CUETools.Codecs.FLAKE.csproj b/CUETools.Codecs.FLAKE/CUETools.Codecs.FLAKE.csproj
new file mode 100644
index 0000000..50608c2
--- /dev/null
+++ b/CUETools.Codecs.FLAKE/CUETools.Codecs.FLAKE.csproj
@@ -0,0 +1,60 @@
+
+
+ Debug
+ AnyCPU
+ 8.0.50727
+ 2.0
+ {082D6B9E-326E-4D15-9798-EDAE9EDE70A6}
+ Library
+ Properties
+ CUETools.Codecs.FLAKE
+ CUETools.Codecs.FLAKE
+
+
+ true
+ full
+ false
+ bin\Debug\
+ DEBUG;TRACE
+ prompt
+ 4
+ true
+
+
+ pdbonly
+ true
+ bin\Release\
+ TRACE
+ prompt
+ 4
+ true
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {6458A13A-30EF-45A9-9D58-E5031B17BEE2}
+ CUETools.Codecs
+
+
+
+
+
\ No newline at end of file
diff --git a/CUETools.Codecs.FLAKE/Flake.cs b/CUETools.Codecs.FLAKE/Flake.cs
new file mode 100644
index 0000000..794c4a4
--- /dev/null
+++ b/CUETools.Codecs.FLAKE/Flake.cs
@@ -0,0 +1,248 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace CUETools.Codecs.FLAKE
+{
+ public class Flake
+ {
+ public const int MAX_BLOCKSIZE = 65535;
+ public const int MAX_RICE_PARAM = 14;
+ public const int MAX_PARTITION_ORDER = 8;
+ public const int MAX_PARTITIONS = 1 << MAX_PARTITION_ORDER;
+ public static int log2i(int v)
+ {
+ return log2i((uint)v);
+ }
+ public static int log2i(uint v)
+ {
+ int i;
+ int n = 0;
+ if (0 != (v & 0xffff0000)) { v >>= 16; n += 16; }
+ if (0 != (v & 0xff00)) { v >>= 8; n += 8; }
+ for (i = 2; i < 256; i <<= 1)
+ {
+ if (v >= i) n++;
+ else break;
+ }
+ return n;
+ }
+
+ public static PredictionType LookupPredictionType(string name)
+ {
+ switch (name)
+ {
+ case "fixed": return PredictionType.Fixed;
+ case "levinson": return PredictionType.Levinson;
+ case "search" : return PredictionType.Search;
+ }
+ return (PredictionType)Int32.Parse(name);
+ }
+
+ public static StereoMethod LookupStereoMethod(string name)
+ {
+ switch (name)
+ {
+ case "independent": return StereoMethod.Independent;
+ case "estimate": return StereoMethod.Estimate;
+ case "estimate2": return StereoMethod.Estimate2;
+ case "estimate3": return StereoMethod.Estimate3;
+ case "estimate4": return StereoMethod.Estimate4;
+ case "estimate5": return StereoMethod.Estimate5;
+ case "search": return StereoMethod.Search;
+ }
+ return (StereoMethod)Int32.Parse(name);
+ }
+
+ public static OrderMethod LookupOrderMethod(string name)
+ {
+ switch (name)
+ {
+ case "estimate": return OrderMethod.Estimate;
+ case "logfast": return OrderMethod.LogFast;
+ case "logsearch": return OrderMethod.LogSearch;
+ case "estsearch": return OrderMethod.EstSearch;
+ case "search": return OrderMethod.Search;
+ }
+ return (OrderMethod)Int32.Parse(name);
+ }
+
+ public static WindowFunction LookupWindowFunction(string name)
+ {
+ string[] parts = name.Split(',');
+ WindowFunction res = (WindowFunction)0;
+ foreach (string part in parts)
+ {
+ switch (part)
+ {
+ case "welch": res |= WindowFunction.Welch; break;
+ case "tukey": res |= WindowFunction.Tukey; break;
+ case "hann": res |= WindowFunction.Hann; break;
+ case "flattop": res |= WindowFunction.Flattop; break;
+ default: res |= (WindowFunction)Int32.Parse(name); break;
+ }
+ }
+ return res;
+ }
+
+ unsafe public static bool memcmp(int* res, int* smp, int n)
+ {
+ for (int i = n; i > 0; i--)
+ if (*(res++) != *(smp++))
+ return true;
+ return false;
+ }
+ unsafe public static void memcpy(int* res, int* smp, int n)
+ {
+ for (int i = n; i > 0; i--)
+ *(res++) = *(smp++);
+ }
+ unsafe public static void memset(int* res, int smp, int n)
+ {
+ for (int i = n; i > 0; i--)
+ *(res++) = smp;
+ }
+ }
+
+ unsafe struct RiceContext
+ {
+ public int porder; /* partition order */
+ public fixed uint rparams[Flake.MAX_PARTITIONS]; /* Rice parameters */
+ public fixed int esc_bps[Flake.MAX_PARTITIONS]; /* bps if using escape code */
+ };
+
+ unsafe struct FlacSubframe
+ {
+ public SubframeType type;
+ public int order;
+ public uint obits;
+ public uint wbits;
+ public int cbits;
+ public int shift;
+ public fixed int coefs[lpc.MAX_LPC_ORDER];
+ public int* samples;
+ public int* residual;
+ public RiceContext rc;
+ public uint size;
+ public fixed uint done_lpcs[lpc.MAX_LPC_WINDOWS];
+ public uint done_fixed;
+ public int window;
+ };
+
+ unsafe struct FlacFrame
+ {
+ public int blocksize;
+ public int bs_code0, bs_code1;
+ public ChannelMode ch_mode;
+ public int ch_order0, ch_order1;
+ public byte crc8;
+ public FlacSubframe* subframes;
+ public uint frame_count;
+ public FlacSubframe current;
+ }
+
+ public enum OrderMethod
+ {
+ Max = 0,
+ Estimate = 1,
+ LogFast = 2,
+ LogSearch = 3,
+ EstSearch = 4,
+ Search = 5
+ }
+
+ ///
+ /// Type of linear prediction
+ ///
+ public enum PredictionType
+ {
+ ///
+ /// verbatim
+ ///
+ None = 0,
+ ///
+ /// Fixed only
+ ///
+ Fixed = 1,
+ ///
+ /// Levinson-Durbin recursion
+ ///
+ Levinson = 2,
+ ///
+ /// Exhaustive search
+ ///
+ Search = 3,
+ ///
+ /// Internal; Use prediction type from previous estimation
+ ///
+ Estimated = 4
+ }
+
+ public enum StereoMethod
+ {
+ Independent = 0,
+ Estimate = 1,
+ Estimate2 = 2,
+ Estimate3 = 3,
+ Estimate4 = 4,
+ Estimate5 = 5,
+ Search = 9
+ }
+
+ public enum SubframeType
+ {
+ Constant = 0,
+ Verbatim = 1,
+ Fixed = 8,
+ LPC = 32
+ };
+
+ public enum ChannelMode
+ {
+ NotStereo = 0,
+ LeftRight = 1,
+ LeftSide = 8,
+ RightSide = 9,
+ MidSide = 10
+ }
+
+ public enum WindowFunction
+ {
+ Welch = 1,
+ Tukey = 2,
+ Hann = 4,
+ Flattop = 8
+ }
+
+ public enum MetadataType
+ {
+
+ ///
+ /// STREAMINFO block
+ ///
+ FLAC__METADATA_TYPE_STREAMINFO = 0,
+
+ ///
+ /// PADDING block
+ ///
+ FLAC__METADATA_TYPE_PADDING = 1,
+
+ FLAC__METADATA_TYPE_APPLICATION = 2,
+ /**< APPLICATION block */
+
+ FLAC__METADATA_TYPE_SEEKTABLE = 3,
+ /**< SEEKTABLE block */
+
+ FLAC__METADATA_TYPE_VORBIS_COMMENT = 4,
+ /**< VORBISCOMMENT block (a.k.a. FLAC tags) */
+
+ FLAC__METADATA_TYPE_CUESHEET = 5,
+ /**< CUESHEET block */
+
+ FLAC__METADATA_TYPE_PICTURE = 6,
+ /**< PICTURE block */
+
+ FLAC__METADATA_TYPE_UNDEFINED = 7
+ /**< marker to denote beginning of undefined type range; this number will increase as new metadata types are added */
+ };
+}
diff --git a/CUETools.Codecs.FLAKE/FlakeReader.cs b/CUETools.Codecs.FLAKE/FlakeReader.cs
new file mode 100644
index 0000000..21ceb64
--- /dev/null
+++ b/CUETools.Codecs.FLAKE/FlakeReader.cs
@@ -0,0 +1,644 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+using System.IO;
+
+namespace CUETools.Codecs.FLAKE
+{
+ public class FlakeReader: IAudioSource
+ {
+ int[] flac_blocksizes;
+ int[] flac_bitdepths;
+
+ int[] samplesBuffer;
+ int[] residualBuffer;
+
+ byte[] _framesBuffer;
+ int _framesBufferLength = 0, _framesBufferOffset = 0;
+
+ Crc8 crc8;
+ Crc16 crc16;
+ int channels;
+ uint bits_per_sample;
+ int sample_rate = 44100;
+
+ uint min_block_size = 0;
+ uint max_block_size = 0;
+ uint min_frame_size = 0;
+ uint max_frame_size = 0;
+
+ uint _samplesInBuffer, _samplesBufferOffset;
+ ulong _sampleCount = 0;
+ ulong _sampleOffset = 0;
+
+ string _path;
+ Stream _IO;
+
+ public int[] Samples
+ {
+ get
+ {
+ return samplesBuffer;
+ }
+ }
+
+ public FlakeReader(string path, Stream IO)
+ {
+ _path = path;
+ _IO = IO != null ? IO : new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.Read);
+
+ flac_bitdepths = new int[8] { 0, 8, 12, 0, 16, 20, 24, 0 };
+ flac_blocksizes = new int[15] { 0, 192, 576, 1152, 2304, 4608, 0, 0, 256, 512, 1024, 2048, 4096, 8192, 16384 };
+
+ crc8 = new Crc8();
+ crc16 = new Crc16();
+
+ _framesBuffer = new byte[0x10000];
+ decode_metadata();
+
+ //max_frame_size = 16 + ((Flake.MAX_BLOCKSIZE * (int)(bits_per_sample * channels + 1) + 7) >> 3);
+ if (max_frame_size * 2 > _framesBuffer.Length)
+ {
+ byte[] temp = _framesBuffer;
+ _framesBuffer = new byte[max_frame_size * 2];
+ if (_framesBufferLength > 0)
+ Array.Copy(temp, _framesBufferOffset, _framesBuffer, 0, _framesBufferLength);
+ _framesBufferOffset = 0;
+ }
+ _samplesInBuffer = 0;
+
+ if (bits_per_sample != 16 || channels != 2 || sample_rate != 44100)
+ throw new Exception("invalid flac file");
+
+ samplesBuffer = new int[Flake.MAX_BLOCKSIZE * channels];
+ residualBuffer = new int[Flake.MAX_BLOCKSIZE * channels];
+ }
+
+ public FlakeReader(int _channels, uint _bits_per_sample)
+ {
+ flac_bitdepths = new int[8] { 0, 8, 12, 0, 16, 20, 24, 0 };
+ flac_blocksizes = new int[15] { 0, 192, 576, 1152, 2304, 4608, 0, 0, 256, 512, 1024, 2048, 4096, 8192, 16384 };
+
+ crc8 = new Crc8();
+ crc16 = new Crc16();
+
+ channels = _channels;
+ bits_per_sample = _bits_per_sample;
+ samplesBuffer = new int[Flake.MAX_BLOCKSIZE * channels];
+ residualBuffer = new int[Flake.MAX_BLOCKSIZE * channels];
+ }
+
+ public void Close()
+ {
+ _IO.Close();
+ }
+
+ public int[,] Read(int[,] buff)
+ {
+ return AudioSamples.Read(this, buff);
+ }
+
+ public ulong Length
+ {
+ get
+ {
+ return _sampleCount;
+ }
+ }
+
+ public ulong Remaining
+ {
+ get
+ {
+ return _sampleCount - _sampleOffset + _samplesInBuffer;
+ }
+ }
+
+ public ulong Position
+ {
+ get
+ {
+ return _sampleOffset - _samplesInBuffer;
+ }
+ set
+ {
+ throw new Exception("seeking not yet supported");
+ }
+ }
+
+ public int BitsPerSample
+ {
+ get
+ {
+ return (int)bits_per_sample;
+ }
+ }
+
+ public int ChannelCount
+ {
+ get
+ {
+ return channels;
+ }
+ }
+
+ public int SampleRate
+ {
+ get
+ {
+ return sample_rate;
+ }
+ }
+
+ public string Path
+ {
+ get
+ {
+ return _path;
+ }
+ }
+
+ void interlace(int [,] buff, int offset, int count)
+ {
+ for (int ch = 0; ch < channels ; ch++)
+ for (int i = 0; i < count; i++)
+ buff[offset + i, ch] = samplesBuffer[_samplesBufferOffset + i + ch * Flake.MAX_BLOCKSIZE];
+ }
+
+ public uint Read(int[,] buff, uint sampleCount)
+ {
+ uint offset = 0;
+
+ while (_samplesInBuffer < sampleCount)
+ {
+ if (_samplesInBuffer > 0)
+ {
+ interlace(buff, (int)offset, (int)_samplesInBuffer);
+ sampleCount -= (uint)_samplesInBuffer;
+ offset += _samplesInBuffer;
+ _samplesInBuffer = 0;
+ _samplesBufferOffset = 0;
+ }
+
+ fill_frames_buffer();
+
+ if (_framesBufferLength == 0)
+ return offset;
+
+ int bytesDecoded = DecodeFrame(_framesBuffer, _framesBufferOffset, _framesBufferLength);
+ _framesBufferLength -= bytesDecoded;
+ _framesBufferOffset += bytesDecoded;
+
+ _samplesInBuffer -= _samplesBufferOffset; // can be set by Seek, otherwise zero
+ _sampleOffset += _samplesInBuffer;
+ }
+
+ interlace(buff, (int)offset, (int)sampleCount);
+ _samplesInBuffer -= sampleCount;
+ _samplesBufferOffset += sampleCount;
+ if (_samplesInBuffer == 0)
+ _samplesBufferOffset = 0;
+ return (uint)offset + sampleCount;
+ }
+
+ void fill_frames_buffer()
+ {
+ if (_framesBufferLength == 0)
+ _framesBufferOffset = 0;
+ else if (_framesBufferLength < _framesBuffer.Length / 2 && _framesBufferOffset >= _framesBuffer.Length / 2)
+ {
+ Array.Copy(_framesBuffer, _framesBufferOffset, _framesBuffer, 0, _framesBufferLength);
+ _framesBufferOffset = 0;
+ }
+ while (_framesBufferLength < _framesBuffer.Length / 2)
+ {
+ int read = _IO.Read(_framesBuffer, _framesBufferOffset + _framesBufferLength, _framesBuffer.Length - _framesBufferOffset - _framesBufferLength);
+ _framesBufferLength += read;
+ if (read == 0)
+ break;
+ }
+ }
+
+ unsafe void decode_frame_header(BitReader bitreader, FlacFrame* frame)
+ {
+ int header_start = bitreader.Position;
+
+ if (bitreader.readbits(16) != 0xFFF8)
+ throw new Exception("invalid frame");
+
+ frame->bs_code0 = (int) bitreader.readbits(4);
+ uint sr_code0 = bitreader.readbits(4);
+ frame->ch_mode = (ChannelMode)bitreader.readbits(4);
+ uint bps_code = bitreader.readbits(3);
+ if (flac_bitdepths[bps_code] != bits_per_sample)
+ throw new Exception("unsupported bps coding");
+ uint t1 = bitreader.readbit(); // == 0?????
+ if (t1 != 0)
+ throw new Exception("unsupported frame coding");
+ frame->frame_count = bitreader.read_utf8();
+
+ // custom block size
+ if (frame->bs_code0 == 6)
+ {
+ frame->bs_code1 = (int)bitreader.readbits(8);
+ frame->blocksize = frame->bs_code1 + 1;
+ }
+ else if (frame->bs_code0 == 7)
+ {
+ frame->bs_code1 = (int)bitreader.readbits(16);
+ frame->blocksize = frame->bs_code1 + 1;
+ }
+ else
+ frame->blocksize = flac_blocksizes[frame->bs_code0];
+
+ // custom sample rate
+ if (sr_code0 < 4 || sr_code0 > 11)
+ {
+ // sr_code0 == 12 -> sr == bitreader.readbits(8) * 1000;
+ // sr_code0 == 13 -> sr == bitreader.readbits(16);
+ // sr_code0 == 14 -> sr == bitreader.readbits(16) * 10;
+ throw new Exception("invalid sample rate mode");
+ }
+
+ int frame_channels = (int)frame->ch_mode + 1;
+ if (frame_channels > 11)
+ throw new Exception("invalid channel mode");
+ if (frame_channels == 2 || frame_channels > 8) // Mid/Left/Right Side Stereo
+ frame_channels = 2;
+ else
+ frame->ch_mode = ChannelMode.NotStereo;
+ if (frame_channels != channels)
+ throw new Exception("invalid channel mode");
+
+ // CRC-8 of frame header
+ byte crc = crc8.ComputeChecksum(bitreader.Buffer, header_start, bitreader.Position - header_start);
+ frame->crc8 = (byte)bitreader.readbits(8);
+ if (frame->crc8 != crc)
+ throw new Exception("header crc mismatch");
+ }
+
+ unsafe void decode_subframe_constant(BitReader bitreader, FlacFrame* frame, int ch)
+ {
+ int obits = (int)frame->subframes[ch].obits;
+ frame->subframes[ch].residual[0] = bitreader.readbits_signed(obits);
+ }
+
+ unsafe void decode_subframe_verbatim(BitReader bitreader, FlacFrame* frame, int ch)
+ {
+ int obits = (int)frame->subframes[ch].obits;
+ for (int i = 0; i < frame->blocksize; i++)
+ frame->subframes[ch].residual[i] = bitreader.readbits_signed(obits);
+ }
+
+ unsafe void decode_residual(BitReader bitreader, FlacFrame* frame, int ch)
+ {
+ // rice-encoded block
+ uint coding_method = bitreader.readbits(2); // ????? == 0
+ if (coding_method != 0) // if 1, then parameter length == 5 bits instead of 4
+ throw new Exception("unsupported residual coding");
+ // partition order
+ frame->subframes[ch].rc.porder = (int)bitreader.readbits(4);
+ if (frame->subframes[ch].rc.porder > 8)
+ throw new Exception("invalid partition order");
+ int psize = frame->blocksize >> frame->subframes[ch].rc.porder;
+ int res_cnt = psize - frame->subframes[ch].order;
+
+ // residual
+ int j = frame->subframes[ch].order;
+ int* r = frame->subframes[ch].residual + j;
+ for (int p = 0; p < (1 << frame->subframes[ch].rc.porder); p++)
+ {
+ uint k = frame->subframes[ch].rc.rparams[p] = bitreader.readbits(4);
+ if (p == 1) res_cnt = psize;
+ int n = Math.Min(res_cnt, frame->blocksize - j);
+ if (k == 0)
+ for (int i = n; i > 0; i--)
+ *(r++) = bitreader.read_unary_signed();
+ else if (k <= 8)
+ for (int i = n; i > 0; i--)
+ *(r++) = bitreader.read_rice_signed8((int)k);
+ else
+ for (int i = n; i > 0; i--)
+ *(r++) = bitreader.read_rice_signed((int)k);
+ j += n;
+ }
+ }
+
+ unsafe void decode_subframe_fixed(BitReader bitreader, FlacFrame* frame, int ch)
+ {
+ // warm-up samples
+ int obits = (int)frame->subframes[ch].obits;
+ for (int i = 0; i < frame->subframes[ch].order; i++)
+ frame->subframes[ch].residual[i] = bitreader.readbits_signed(obits);
+
+ // residual
+ decode_residual(bitreader, frame, ch);
+ }
+
+ unsafe void decode_subframe_lpc(BitReader bitreader, FlacFrame* frame, int ch)
+ {
+ // warm-up samples
+ int obits = (int)frame->subframes[ch].obits;
+ for (int i = 0; i < frame->subframes[ch].order; i++)
+ frame->subframes[ch].residual[i] = bitreader.readbits_signed(obits);
+
+ // LPC coefficients
+ frame->subframes[ch].cbits = (int)bitreader.readbits(4) + 1; // lpc_precision
+ frame->subframes[ch].shift = bitreader.readbits_signed(5);
+ for (int i = 0; i < frame->subframes[ch].order; i++)
+ frame->subframes[ch].coefs[i] = bitreader.readbits_signed(frame->subframes[ch].cbits);
+
+ // residual
+ decode_residual(bitreader, frame, ch);
+ }
+
+ unsafe void decode_subframes(BitReader bitreader, FlacFrame* frame)
+ {
+ fixed (int *r = residualBuffer, s = samplesBuffer)
+ for (int ch = 0; ch < channels; ch++)
+ {
+ // subframe header
+ uint t1 = bitreader.readbit(); // ?????? == 0
+ if (t1 != 0)
+ throw new Exception("unsupported subframe coding");
+ int type_code = (int)bitreader.readbits(6);
+ frame->subframes[ch].wbits = bitreader.readbit();
+ if (frame->subframes[ch].wbits != 0)
+ frame->subframes[ch].wbits += bitreader.read_unary();
+
+ frame->subframes[ch].obits = bits_per_sample - frame->subframes[ch].wbits;
+ switch (frame->ch_mode)
+ {
+ case ChannelMode.MidSide: frame->subframes[ch].obits += (uint)ch; break;
+ case ChannelMode.LeftSide: frame->subframes[ch].obits += (uint)ch; break;
+ case ChannelMode.RightSide: frame->subframes[ch].obits += 1 - (uint)ch; break;
+ }
+
+ frame->subframes[ch].type = (SubframeType)type_code;
+ frame->subframes[ch].order = 0;
+
+ if ((type_code & (uint)SubframeType.LPC) != 0)
+ {
+ frame->subframes[ch].order = (type_code - (int)SubframeType.LPC) + 1;
+ frame->subframes[ch].type = SubframeType.LPC;
+ }
+ else if ((type_code & (uint)SubframeType.Fixed) != 0)
+ {
+ frame->subframes[ch].order = (type_code - (int)SubframeType.Fixed);
+ frame->subframes[ch].type = SubframeType.Fixed;
+ }
+
+ frame->subframes[ch].residual = r + ch * Flake.MAX_BLOCKSIZE;
+ frame->subframes[ch].samples = s + ch * Flake.MAX_BLOCKSIZE;
+
+ // subframe
+ switch (frame->subframes[ch].type)
+ {
+ case SubframeType.Constant:
+ decode_subframe_constant(bitreader, frame, ch);
+ break;
+ case SubframeType.Verbatim:
+ decode_subframe_verbatim(bitreader, frame, ch);
+ break;
+ case SubframeType.Fixed:
+ decode_subframe_fixed(bitreader, frame, ch);
+ break;
+ case SubframeType.LPC:
+ decode_subframe_lpc(bitreader, frame, ch);
+ break;
+ default:
+ throw new Exception("invalid subframe type");
+ }
+ }
+ }
+
+ unsafe void restore_samples_fixed(FlacFrame* frame, int ch)
+ {
+ FlacSubframe* sub = frame->subframes + ch;
+
+ Flake.memcpy(sub->samples, sub->residual, sub->order);
+ int* data = sub->samples + sub->order;
+ int* residual = sub->residual + sub->order;
+ int data_len = frame->blocksize - sub->order;
+ switch (sub->order)
+ {
+ case 0:
+ Flake.memcpy(data, residual, data_len);
+ break;
+ case 1:
+ for (int i = 0; i < data_len; i++)
+ data[i] = residual[i] + data[i - 1];
+ break;
+ case 2:
+ for (int i = 0; i < data_len; i++)
+ data[i] = residual[i] + (data[i - 1] << 1) - data[i - 2];
+ break;
+ case 3:
+ for (int i = 0; i < data_len; i++)
+ data[i] = residual[i] + (((data[i - 1] - data[i - 2]) << 1) + (data[i - 1] - data[i - 2])) + data[i - 3];
+ break;
+ case 4:
+ for (int i = 0; i < data_len; i++)
+ data[i] = residual[i] + ((data[i - 1] + data[i - 3]) << 2) - ((data[i - 2] << 2) + (data[i - 2] << 1)) - data[i - 4];
+ break;
+ }
+ }
+
+ unsafe void restore_samples_lpc(FlacFrame* frame, int ch)
+ {
+ FlacSubframe* sub = frame->subframes + ch;
+
+ if ((ulong)sub->order * ((1UL << (int)sub->obits) - 1) * ((1U << sub->cbits) - 1) >= (1UL << 32))
+ lpc.decode_residual_long(sub->residual, sub->samples, frame->blocksize, sub->order, sub->coefs, sub->shift);
+ else
+ lpc.decode_residual(sub->residual, sub->samples, frame->blocksize, sub->order, sub->coefs, sub->shift);
+ }
+
+ unsafe void restore_samples(FlacFrame* frame)
+ {
+ for (int ch = 0; ch < channels; ch++)
+ {
+ switch (frame->subframes[ch].type)
+ {
+ case SubframeType.Constant:
+ Flake.memset(frame->subframes[ch].samples, frame->subframes[ch].residual[0], frame->blocksize);
+ break;
+ case SubframeType.Verbatim:
+ Flake.memcpy(frame->subframes[ch].samples, frame->subframes[ch].residual, frame->blocksize);
+ break;
+ case SubframeType.Fixed:
+ restore_samples_fixed(frame, ch);
+ break;
+ case SubframeType.LPC:
+ restore_samples_lpc(frame, ch);
+ break;
+ }
+ if (frame->subframes[ch].wbits != 0)
+ {
+ int x = (int) frame->subframes[ch].wbits;
+ for (int i = 0; i < frame->blocksize; i++)
+ frame->subframes[ch].samples[i] <<= x;
+ }
+ }
+ switch (frame->ch_mode)
+ {
+ case ChannelMode.NotStereo:
+ break;
+ case ChannelMode.LeftRight:
+ break;
+ case ChannelMode.MidSide:
+ for (int i = 0; i < frame->blocksize; i++)
+ {
+ int mid = frame->subframes[0].samples[i];
+ int side = frame->subframes[1].samples[i];
+ mid <<= 1;
+ mid |= (side & 1); /* i.e. if 'side' is odd... */
+ frame->subframes[0].samples[i] = (mid + side) >> 1;
+ frame->subframes[1].samples[i] = (mid - side) >> 1;
+ }
+ break;
+ case ChannelMode.LeftSide:
+ for (int i = 0; i < frame->blocksize; i++)
+ frame->subframes[1].samples[i] = frame->subframes[0].samples[i] - frame->subframes[1].samples[i];
+ break;
+ case ChannelMode.RightSide:
+ for (int i = 0; i < frame->blocksize; i++)
+ frame->subframes[0].samples[i] += frame->subframes[1].samples[i];
+ break;
+ }
+ }
+
+ public unsafe int DecodeFrame(byte[] buffer, int pos, int len)
+ {
+ BitReader bitreader = new BitReader(buffer, pos, len);
+ FlacFrame frame;
+ FlacSubframe* subframes = stackalloc FlacSubframe[channels];
+ frame.subframes = subframes;
+ decode_frame_header(bitreader, &frame);
+ decode_subframes(bitreader, &frame);
+ bitreader.flush();
+ ushort crc = crc16.ComputeChecksum(bitreader.Buffer, pos, bitreader.Position - pos);
+ if (crc != bitreader.readbits(16))
+ throw new Exception("frame crc mismatch");
+ restore_samples(&frame);
+ restore_samples(&frame);
+ _samplesInBuffer = (uint)frame.blocksize;
+ return bitreader.Position - pos;
+ }
+
+
+ bool skip_bytes(int bytes)
+ {
+ for (int j = 0; j < bytes; j++)
+ if (0 == _IO.Read(_framesBuffer, 0, 1))
+ return false;
+ return true;
+ }
+
+ void decode_metadata()
+ {
+ byte x;
+ int i, id;
+ bool first = true;
+ byte[] FLAC__STREAM_SYNC_STRING = new byte[] { (byte)'f', (byte)'L', (byte)'a', (byte)'C' };
+ byte[] ID3V2_TAG_ = new byte[] { (byte)'I', (byte)'D', (byte)'3' };
+
+ for (i = id = 0; i < 4; )
+ {
+ if (_IO.Read(_framesBuffer, 0, 1) == 0)
+ throw new Exception("FLAC stream not found");
+ x = _framesBuffer[0];
+ if (x == FLAC__STREAM_SYNC_STRING[i])
+ {
+ first = true;
+ i++;
+ id = 0;
+ continue;
+ }
+ if (id < 3 && x == ID3V2_TAG_[id])
+ {
+ id++;
+ i = 0;
+ if (id == 3)
+ {
+ if (!skip_bytes(3))
+ throw new Exception("FLAC stream not found");
+ int skip = 0;
+ for (int j = 0; j < 4; j++)
+ {
+ if (0 == _IO.Read(_framesBuffer, 0, 1))
+ throw new Exception("FLAC stream not found");
+ skip <<= 7;
+ skip |= ((int)_framesBuffer[0] & 0x7f);
+ }
+ if (!skip_bytes(skip))
+ throw new Exception("FLAC stream not found");
+ }
+ continue;
+ }
+ id = 0;
+ if (x == 0xff) /* MAGIC NUMBER for the first 8 frame sync bits */
+ {
+ do
+ {
+ if (_IO.Read(_framesBuffer, 0, 1) == 0)
+ throw new Exception("FLAC stream not found");
+ x = _framesBuffer[0];
+ } while (x == 0xff);
+ if (x >> 2 == 0x3e) /* MAGIC NUMBER for the last 6 sync bits */
+ {
+ //_IO.Position -= 2;
+ // state = frame
+ throw new Exception("headerless file unsupported");
+ }
+ }
+ throw new Exception("FLAC stream not found");
+ }
+
+ do
+ {
+ long pos = _IO.Position;
+ fill_frames_buffer();
+ BitReader bitreader = new BitReader(_framesBuffer, _framesBufferOffset, _framesBufferLength - _framesBufferOffset);
+ bool is_last = bitreader.readbit() != 0;
+ MetadataType type = (MetadataType)bitreader.readbits(7);
+ int len = (int)bitreader.readbits(24);
+
+ if (type == MetadataType.FLAC__METADATA_TYPE_STREAMINFO)
+ {
+ const int FLAC__STREAM_METADATA_STREAMINFO_MIN_BLOCK_SIZE_LEN = 16; /* bits */
+ const int FLAC__STREAM_METADATA_STREAMINFO_MAX_BLOCK_SIZE_LEN = 16; /* bits */
+ const int FLAC__STREAM_METADATA_STREAMINFO_MIN_FRAME_SIZE_LEN = 24; /* bits */
+ const int FLAC__STREAM_METADATA_STREAMINFO_MAX_FRAME_SIZE_LEN = 24; /* bits */
+ const int FLAC__STREAM_METADATA_STREAMINFO_SAMPLE_RATE_LEN = 20; /* bits */
+ const int FLAC__STREAM_METADATA_STREAMINFO_CHANNELS_LEN = 3; /* bits */
+ const int FLAC__STREAM_METADATA_STREAMINFO_BITS_PER_SAMPLE_LEN = 5; /* bits */
+ const int FLAC__STREAM_METADATA_STREAMINFO_TOTAL_SAMPLES_LEN = 36; /* bits */
+ const int FLAC__STREAM_METADATA_STREAMINFO_MD5SUM_LEN = 128; /* bits */
+
+ min_block_size = bitreader.readbits(FLAC__STREAM_METADATA_STREAMINFO_MIN_BLOCK_SIZE_LEN);
+ max_block_size = bitreader.readbits(FLAC__STREAM_METADATA_STREAMINFO_MAX_BLOCK_SIZE_LEN);
+ min_frame_size = bitreader.readbits(FLAC__STREAM_METADATA_STREAMINFO_MIN_FRAME_SIZE_LEN);
+ max_frame_size = bitreader.readbits(FLAC__STREAM_METADATA_STREAMINFO_MAX_FRAME_SIZE_LEN);
+ sample_rate = (int) bitreader.readbits(FLAC__STREAM_METADATA_STREAMINFO_SAMPLE_RATE_LEN);
+ channels = 1 + (int) bitreader.readbits(FLAC__STREAM_METADATA_STREAMINFO_CHANNELS_LEN);
+ bits_per_sample = 1 + bitreader.readbits(FLAC__STREAM_METADATA_STREAMINFO_BITS_PER_SAMPLE_LEN);
+ _sampleCount = (((ulong)bitreader.readbits(4)) << 4) + (ulong)bitreader.readbits(32);
+ bitreader.skipbits(FLAC__STREAM_METADATA_STREAMINFO_MD5SUM_LEN);
+ }
+ if (_framesBufferLength < 4 + len)
+ {
+ _IO.Position = pos + 4 + len;
+ _framesBufferLength = 0;
+ }
+ else
+ {
+ _framesBufferLength -= 4 + len;
+ _framesBufferOffset += 4 + len;
+ }
+ if (is_last)
+ break;
+ } while (true);
+ }
+ }
+}
diff --git a/CUETools.Codecs.FLAKE/FlakeWriter.cs b/CUETools.Codecs.FLAKE/FlakeWriter.cs
new file mode 100644
index 0000000..0974c1e
--- /dev/null
+++ b/CUETools.Codecs.FLAKE/FlakeWriter.cs
@@ -0,0 +1,1656 @@
+using System;
+using System.Text;
+using System.IO;
+using System.Collections.Generic;
+using System.Collections.Specialized;
+using System.Security.Cryptography;
+using System.Runtime.InteropServices;
+using CUETools.Codecs;
+
+namespace CUETools.Codecs.FLAKE
+{
+ public class FlakeWriter : IAudioDest
+ {
+ Stream _IO = null;
+ string _path;
+ long _position;
+
+ // number of audio channels
+ // set by user prior to calling flake_encode_init
+ // valid values are 1 to 8
+ int channels, ch_code;
+
+ // audio sample rate in Hz
+ // set by user prior to calling flake_encode_init
+ int sample_rate, sr_code0, sr_code1;
+
+ // sample size in bits
+ // set by user prior to calling flake_encode_init
+ // only 16-bit is currently supported
+ uint bits_per_sample;
+ int bps_code;
+
+ // total stream samples
+ // set by user prior to calling flake_encode_init
+ // if 0, stream length is unknown
+ int sample_count;
+
+ FlakeEncodeParams eparams;
+
+ // maximum frame size in bytes
+ // set by flake_encode_init
+ // this can be used to allocate memory for output
+ int max_frame_size;
+
+ byte[] frame_buffer = null;
+
+ uint lpc_precision;
+
+ int frame_count = 0;
+
+ TimeSpan _userProcessorTime;
+
+ // header bytes
+ // allocated by flake_encode_init and freed by flake_encode_close
+ byte[] header;
+
+ int[] samplesBuffer;
+ int[] verifyBuffer;
+ int[] residualBuffer;
+ double[] windowBuffer;
+ int samplesInBuffer = 0;
+
+ int _compressionLevel = 5;
+ int _blocksize = 0;
+ int _totalSize = 0;
+ int _windowsize = 0, _windowcount = 0;
+
+ Crc8 crc8;
+ Crc16 crc16;
+ MD5 md5;
+
+ FlakeReader verify;
+
+ bool inited = false;
+
+ public FlakeWriter(string path, int bitsPerSample, int channelCount, int sampleRate, Stream IO)
+ {
+ if (bitsPerSample != 16)
+ throw new Exception("Bits per sample must be 16.");
+ if (channelCount != 2)
+ throw new Exception("ChannelCount must be 2.");
+
+ flac_samplerates = new int[16] {
+ 0, 0, 0, 0,
+ 8000, 16000, 22050, 24000, 32000, 44100, 48000, 96000,
+ 0, 0, 0, 0
+ };
+ flac_bitdepths = new int[8] { 0, 8, 12, 0, 16, 20, 24, 0 };
+ flac_blocksizes = new int[15] { 0, 192, 576, 1152, 2304, 4608, 0, 0, 256, 512, 1024, 2048, 4096, 8192, 16384 };
+
+ channels = channelCount;
+ sample_rate = sampleRate;
+ bits_per_sample = (uint) bitsPerSample;
+
+ // flake_validate_params
+
+ _path = path;
+ _IO = IO;
+
+ //verify = new FlakeReader(channels, bits_per_sample);
+
+ samplesBuffer = new int[Flake.MAX_BLOCKSIZE * (channels == 2 ? 4 : channels)];
+ residualBuffer = new int[Flake.MAX_BLOCKSIZE * (channels == 2 ? 4 : channels)];
+ windowBuffer = new double[Flake.MAX_BLOCKSIZE * lpc.MAX_LPC_WINDOWS];
+ if (verify != null)
+ verifyBuffer = new int[Flake.MAX_BLOCKSIZE * channels];
+
+ eparams.flake_set_defaults(_compressionLevel);
+ eparams.padding_size = 8192;
+
+ crc8 = new Crc8();
+ crc16 = new Crc16();
+ md5 = new MD5CryptoServiceProvider();
+ }
+
+ public int TotalSize
+ {
+ get
+ {
+ return _totalSize;
+ }
+ }
+
+ public int PaddingLength
+ {
+ get
+ {
+ return eparams.padding_size;
+ }
+ set
+ {
+ eparams.padding_size = value;
+ }
+ }
+
+ public int CompressionLevel
+ {
+ get
+ {
+ return _compressionLevel;
+ }
+ set
+ {
+ _compressionLevel = value;
+ eparams.flake_set_defaults(_compressionLevel);
+ }
+ }
+
+ [DllImport("kernel32.dll")]
+ static extern bool GetThreadTimes(IntPtr hThread, out long lpCreationTime, out long lpExitTime, out long lpKernelTime, out long lpUserTime);
+ [DllImport("kernel32.dll")]
+ static extern IntPtr GetCurrentThread();
+
+ void DoClose()
+ {
+ if (samplesInBuffer > 0)
+ {
+ eparams.block_size = samplesInBuffer;
+ output_frame();
+ }
+
+ long fake, KernelStart, UserStart;
+ GetThreadTimes(GetCurrentThread(), out fake, out fake, out KernelStart, out UserStart);
+ _userProcessorTime = new TimeSpan(UserStart);
+
+ md5.TransformFinalBlock(frame_buffer, 0, 0);
+ if (_IO.CanSeek)
+ {
+ _IO.Position = 26;
+ _IO.Write(md5.Hash, 0, md5.Hash.Length);
+ }
+ _IO.Close();
+ }
+
+ public void Close()
+ {
+ DoClose();
+ if (sample_count != 0 && _position != sample_count)
+ throw new Exception("Samples written differs from the expected sample count.");
+ }
+
+ public void Delete()
+ {
+ DoClose();
+ File.Delete(_path);
+ }
+
+ public long Position
+ {
+ get
+ {
+ return _position;
+ }
+ }
+
+ public long FinalSampleCount
+ {
+ set { sample_count = (int)value; }
+ }
+
+ public long BlockSize
+ {
+ set { _blocksize = (int)value; }
+ }
+
+ public OrderMethod OrderMethod
+ {
+ get { return eparams.order_method; }
+ set { eparams.order_method = value; }
+ }
+
+ public PredictionType PredictionType
+ {
+ get { return eparams.prediction_type; }
+ set { eparams.prediction_type = value; }
+ }
+
+ public StereoMethod StereoMethod
+ {
+ get { return eparams.stereo_method; }
+ set { eparams.stereo_method = value; }
+ }
+
+ public WindowFunction WindowFunction
+ {
+ get { return eparams.window_function; }
+ set { eparams.window_function = value; }
+ }
+
+ public int MinPredictionOrder
+ {
+ get
+ {
+ return eparams.prediction_type == PredictionType.Fixed ?
+ eparams.min_fixed_order : eparams.min_prediction_order;
+ }
+ set
+ {
+ if (eparams.prediction_type == PredictionType.Fixed)
+ {
+ if (value < 0 || value > eparams.max_fixed_order)
+ throw new Exception("invalid order");
+ eparams.min_fixed_order = value;
+ return;
+ }
+ if (value < 0 || value > eparams.max_prediction_order)
+ throw new Exception("invalid order");
+ eparams.min_prediction_order = value;
+ }
+ }
+
+ public int MaxPredictionOrder
+ {
+ get
+ {
+ return eparams.prediction_type == PredictionType.Fixed ?
+ eparams.max_fixed_order : eparams.max_prediction_order;
+ }
+ set
+ {
+ if (eparams.prediction_type == PredictionType.Fixed)
+ {
+ if (value > 4 || value < eparams.min_fixed_order)
+ throw new Exception("invalid order");
+ eparams.max_fixed_order = value;
+ return;
+ }
+ if (value > 32 || value < eparams.min_prediction_order)
+ throw new Exception("invalid order");
+ eparams.max_prediction_order = value;
+ }
+ }
+
+ public int MaxPartitionOrder
+ {
+ get { return eparams.max_partition_order; }
+ set
+ {
+ if (value > 8 || value < eparams.min_partition_order)
+ throw new Exception("invalid order");
+ eparams.max_partition_order = value;
+ }
+ }
+
+ public TimeSpan UserProcessorTime
+ {
+ get { return _userProcessorTime; }
+ }
+
+ public int BitsPerSample
+ {
+ get { return 16; }
+ }
+
+ int encode_frame_vbs()
+ {
+ throw new Exception("vbs not supported");
+ }
+
+ unsafe int get_wasted_bits(int* signal, int samples)
+ {
+ int i, shift;
+ int x = 0;
+
+ for (i = 0; i < samples && 0 == (x & 1); i++)
+ x |= signal[i];
+
+ if (x == 0)
+ {
+ shift = 0;
+ }
+ else
+ {
+ for (shift = 0; 0 == (x & 1); shift++)
+ x >>= 1;
+ }
+
+ if (shift > 0)
+ {
+ for (i = 0; i < samples; i++)
+ signal[i] >>= shift;
+ }
+
+ return shift;
+ }
+
+ unsafe void init_frame(FlacFrame * frame)
+ {
+ //if (channels == 2)
+ //max_frame_size =
+
+ int i = 15;
+ if (eparams.variable_block_size == 0)
+ {
+ for (i = 0; i < 15; i++)
+ {
+ if (eparams.block_size == flac_blocksizes[i])
+ {
+ frame->blocksize = flac_blocksizes[i];
+ frame->bs_code0 = i;
+ frame->bs_code1 = -1;
+ break;
+ }
+ }
+ }
+ if (i == 15)
+ {
+ frame->blocksize = eparams.block_size;
+ if (frame->blocksize <= 256)
+ {
+ frame->bs_code0 = 6;
+ frame->bs_code1 = frame->blocksize - 1;
+ }
+ else
+ {
+ frame->bs_code0 = 7;
+ frame->bs_code1 = frame->blocksize - 1;
+ }
+ }
+ }
+
+ /**
+ * Copy channel-interleaved input samples into separate subframes
+ */
+ unsafe void copy_samples(int[,] samples, int pos, int block)
+ {
+ fixed (int* fsamples = samplesBuffer)
+ for (int ch = 0; ch < channels; ch++)
+ {
+ int* psamples = fsamples + ch * Flake.MAX_BLOCKSIZE + samplesInBuffer;
+ for (int i = 0; i < block; i++)
+ psamples[i] = samples[pos + i, ch];
+ }
+ samplesInBuffer += block;
+ }
+
+ static uint rice_encode_count(uint sum, uint n, uint k)
+ {
+ return n*(k+1) + ((sum-(n>>1))>>(int)k);
+ }
+
+ //static unsafe uint find_optimal_rice_param(uint sum, uint n)
+ //{
+ // uint* nbits = stackalloc uint[Flake.MAX_RICE_PARAM + 1];
+ // int k_opt = 0;
+
+ // nbits[0] = UINT32_MAX;
+ // for (int k = 0; k <= Flake.MAX_RICE_PARAM; k++)
+ // {
+ // nbits[k] = rice_encode_count(sum, n, (uint)k);
+ // if (nbits[k] < nbits[k_opt])
+ // k_opt = k;
+ // }
+ // return (uint)k_opt;
+ //}
+
+ static unsafe uint find_optimal_rice_param(uint sum, uint n, out uint nbits_best)
+ {
+ int k_opt = 0;
+ uint a = n;
+ uint b = sum - (n >> 1);
+ uint nbits = a + b;
+ for (int k = 1; k <= Flake.MAX_RICE_PARAM; k++)
+ {
+ a += n;
+ b >>= 1;
+ uint nbits_k = a + b;
+ if (nbits_k < nbits)
+ {
+ k_opt = k;
+ nbits = nbits_k;
+ }
+ }
+ nbits_best = nbits;
+ return (uint)k_opt;
+ }
+
+ unsafe uint calc_decorr_score(FlacFrame* frame, int ch, FlacSubframe* sub)
+ {
+ int* s = sub->samples;
+ int n = frame->blocksize;
+ ulong sum = 0;
+ for (int i = 2; i < n; i++)
+ sum += (ulong)Math.Abs(s[i] - 2 * s[i - 1] + s[i - 2]);
+ uint nbits;
+ find_optimal_rice_param((uint)(2 * sum), (uint)n, out nbits);
+ return nbits;
+ }
+
+ unsafe void initialize_subframe(FlacFrame* frame, int ch, int *s, int * r, uint bps)
+ {
+ int w = get_wasted_bits(s, frame->blocksize);
+ if (w > bps)
+ throw new Exception("internal error");
+ frame->subframes[ch].samples = s;
+ frame->subframes[ch].residual = r;
+ frame->subframes[ch].obits = bps - (uint)w;
+ frame->subframes[ch].wbits = (uint)w;
+ frame->subframes[ch].type = SubframeType.Verbatim;
+ frame->subframes[ch].size = UINT32_MAX;
+ for (int iWindow = 0; iWindow < lpc.MAX_LPC_WINDOWS; iWindow++)
+ frame->subframes[ch].done_lpcs[iWindow] = 0;
+ frame->subframes[ch].done_fixed = 0;
+ }
+
+ unsafe static void channel_decorrelation(int* leftS, int* rightS, int *leftM, int *rightM, int blocksize)
+ {
+ for (int i = 0; i < blocksize; i++)
+ {
+ leftM[i] = (leftS[i] + rightS[i]) >> 1;
+ rightM[i] = leftS[i] - rightS[i];
+ }
+ }
+
+ unsafe void encode_residual_verbatim(int* res, int* smp, uint n)
+ {
+ Flake.memcpy(res, smp, (int) n);
+ }
+
+ unsafe void encode_residual_fixed(int* res, int* smp, int n, int order)
+ {
+ int i;
+ int s0, s1, s2;
+ switch (order)
+ {
+ case 0:
+ Flake.memcpy(res, smp, n);
+ return;
+ case 1:
+ *(res++) = s1 = *(smp++);
+ for (i = n - 1; i > 0; i--)
+ {
+ s0 = *(smp++);
+ *(res++) = s0 - s1;
+ s1 = s0;
+ }
+ return;
+ case 2:
+ *(res++) = s2 = *(smp++);
+ *(res++) = s1 = *(smp++);
+ for (i = n - 2; i > 0; i--)
+ {
+ s0 = *(smp++);
+ *(res++) = s0 - 2 * s1 + s2;
+ s2 = s1;
+ s1 = s0;
+ }
+ return;
+ case 3:
+ res[0] = smp[0];
+ res[1] = smp[1];
+ res[2] = smp[2];
+ for (i = 3; i < n; i++)
+ {
+ res[i] = smp[i] - 3 * smp[i - 1] + 3 * smp[i - 2] - smp[i - 3];
+ }
+ return;
+ case 4:
+ res[0] = smp[0];
+ res[1] = smp[1];
+ res[2] = smp[2];
+ res[3] = smp[3];
+ for (i = 4; i < n; i++)
+ {
+ res[i] = smp[i] - 4 * smp[i - 1] + 6 * smp[i - 2] - 4 * smp[i - 3] + smp[i - 4];
+ }
+ return;
+ default:
+ return;
+ }
+ }
+
+ public const uint UINT32_MAX = 0xffffffff;
+
+ static unsafe uint calc_optimal_rice_params(RiceContext* rc, int porder, uint* sums, uint n, uint pred_order)
+ {
+ uint part = (1U << porder);
+ uint all_bits = 0;
+ rc->rparams[0] = find_optimal_rice_param(sums[0], (n >> porder) - pred_order, out all_bits);
+ uint cnt = (n >> porder);
+ for (uint i = 1; i < part; i++)
+ {
+ uint nbits;
+ rc->rparams[i] = find_optimal_rice_param(sums[i], cnt, out nbits);
+ all_bits += nbits;
+ }
+ all_bits += (4 * part);
+ rc->porder = porder;
+ return all_bits;
+ }
+
+ static unsafe void calc_sums(int pmin, int pmax, uint* data, uint n, uint pred_order, uint* sums)
+ {
+ uint* res = &data[pred_order];
+
+ // sums for highest level
+ int parts = (1 << pmax);
+ uint cnt = (n >> pmax) - pred_order;
+ for (int i = 0; i < parts; i++)
+ {
+ if (i == 1) cnt = (n >> pmax);
+ if (i > 0) res = &data[i * cnt];
+ sums[pmax * Flake.MAX_PARTITIONS + i] = 0;
+ for (int j = 0; j < cnt; j++)
+ {
+ sums[pmax * Flake.MAX_PARTITIONS + i] += res[j];
+ }
+ }
+ // sums for lower levels
+ for (int i = pmax - 1; i >= pmin; i--)
+ {
+ parts = (1 << i);
+ for (int j = 0; j < parts; j++)
+ {
+ sums[i * Flake.MAX_PARTITIONS + j] =
+ sums[(i + 1) * Flake.MAX_PARTITIONS + 2 * j] +
+ sums[(i + 1) * Flake.MAX_PARTITIONS + 2 * j + 1];
+ }
+ }
+ }
+
+ static unsafe uint calc_rice_params(RiceContext* rc, int pmin, int pmax, int* data, uint n, uint pred_order)
+ {
+ RiceContext tmp_rc;
+ uint* udata = stackalloc uint[(int)n];
+ uint* sums = stackalloc uint[(pmax + 1) * Flake.MAX_PARTITIONS];
+ //uint* bits = stackalloc uint[Flake.MAX_PARTITION_ORDER];
+
+ //assert(pmin >= 0 && pmin <= Flake.MAX_PARTITION_ORDER);
+ //assert(pmax >= 0 && pmax <= Flake.MAX_PARTITION_ORDER);
+ //assert(pmin <= pmax);
+
+ for (uint i = 0; i < n; i++)
+ udata[i] = (uint) ((2 * data[i]) ^ (data[i] >> 31));
+
+ calc_sums(pmin, pmax, udata, n, pred_order, sums);
+
+ int opt_porder = pmin;
+ uint opt_bits = UINT32_MAX;
+ for (int i = pmin; i <= pmax; i++)
+ {
+ uint bits = calc_optimal_rice_params(&tmp_rc, i, sums + i * Flake.MAX_PARTITIONS, n, pred_order);
+ if (bits <= opt_bits)
+ {
+ opt_porder = i;
+ opt_bits = bits;
+ *rc = tmp_rc;
+ }
+ }
+
+ return opt_bits;
+ }
+
+ static int get_max_p_order(int max_porder, int n, int order)
+ {
+ int porder = Math.Min(max_porder, Flake.log2i(n ^ (n - 1)));
+ if (order > 0)
+ porder = Math.Min(porder, Flake.log2i(n / order));
+ return porder;
+ }
+
+ static unsafe uint calc_rice_params_fixed(RiceContext* rc, int pmin, int pmax,
+ int* data, int n, int pred_order, uint bps)
+ {
+ pmin = get_max_p_order(pmin, n, pred_order);
+ pmax = get_max_p_order(pmax, n, pred_order);
+ uint bits = (uint)pred_order * bps + 6;
+ bits += calc_rice_params(rc, pmin, pmax, data, (uint)n, (uint)pred_order);
+ return bits;
+ }
+
+ static unsafe uint calc_rice_params_lpc(RiceContext* rc, int pmin, int pmax,
+ int* data, int n, int pred_order, uint bps, uint precision)
+ {
+ pmin = get_max_p_order(pmin, n, pred_order);
+ pmax = get_max_p_order(pmax, n, pred_order);
+ uint bits = (uint)pred_order * bps + 4 + 5 + (uint)pred_order * precision + 6;
+ bits += calc_rice_params(rc, pmin, pmax, data, (uint)n, (uint)pred_order);
+ return bits;
+ }
+
+ unsafe void choose_best_subframe(FlacFrame* frame, int ch)
+ {
+ if (frame->current.size < frame->subframes[ch].size)
+ {
+ FlacSubframe tmp = frame->subframes[ch];
+ frame->subframes[ch] = frame->current;
+ for (int iWindow = 0; iWindow < lpc.MAX_LPC_WINDOWS; iWindow++)
+ frame->subframes[ch].done_lpcs[iWindow] = tmp.done_lpcs[iWindow];
+ frame->subframes[ch].done_fixed = tmp.done_fixed;
+ frame->current = tmp;
+ }
+ }
+
+ unsafe void encode_residual_lpc_sub(FlacFrame* frame, double* lpcs, int iWindow, int order, int ch)
+ {
+ if ((frame->subframes[ch].done_lpcs[iWindow] & (1U << (order - 1))) != 0)
+ return; // already calculated;
+
+ frame->subframes[ch].done_lpcs[iWindow] |= (1U << (order - 1));
+
+ frame->current.type = SubframeType.LPC;
+ frame->current.order = order;
+ frame->current.window = iWindow;
+
+ lpc.quantize_lpc_coefs(lpcs + (frame->current.order - 1) * lpc.MAX_LPC_ORDER,
+ frame->current.order, lpc_precision, frame->current.coefs, out frame->current.shift);
+
+ lpc.encode_residual(frame->current.residual, frame->current.samples, frame->blocksize, frame->current.order, frame->current.coefs, frame->current.shift);
+
+ frame->current.size = calc_rice_params_lpc(&frame->current.rc, eparams.min_partition_order, eparams.max_partition_order,
+ frame->current.residual, frame->blocksize, frame->current.order, frame->current.obits, lpc_precision);
+
+ choose_best_subframe(frame, ch);
+ }
+
+ unsafe void encode_residual_fixed_sub(FlacFrame* frame, int order, int ch)
+ {
+ if ((frame->subframes[ch].done_fixed & (1U << order)) != 0)
+ return; // already calculated;
+
+ frame->current.order = order;
+ frame->current.type = SubframeType.Fixed;
+
+ encode_residual_fixed(frame->current.residual, frame->current.samples, frame->blocksize, frame->current.order);
+
+ frame->current.size = calc_rice_params_fixed(&frame->current.rc, eparams.min_partition_order, eparams.max_partition_order,
+ frame->current.residual, frame->blocksize, frame->current.order, frame->current.obits);
+
+ frame->subframes[ch].done_fixed |= (1U << order);
+
+ choose_best_subframe(frame, ch);
+ }
+
+ unsafe void encode_residual(FlacFrame* frame, int ch, PredictionType predict, OrderMethod omethod)
+ {
+ int i;
+ FlacSubframe* sub = frame->subframes + ch;
+ int* smp = sub->samples;
+ int n = frame->blocksize;
+
+ frame->current.samples = sub->samples;
+ frame->current.obits = sub->obits;
+ frame->current.wbits = sub->wbits;
+
+ // CONSTANT
+ for (i = 1; i < n; i++)
+ {
+ if (smp[i] != smp[0]) break;
+ }
+ if (i == n)
+ {
+ sub->type = SubframeType.Constant;
+ sub->residual[0] = smp[0];
+ sub->size = sub->obits;
+ return;
+ }
+
+ // VERBATIM
+ frame->current.type = SubframeType.Verbatim;
+ frame->current.size = frame->current.obits * (uint)frame->blocksize;
+ choose_best_subframe(frame, ch);
+
+ if (n < 5 || predict == PredictionType.None)
+ return;
+
+ // FIXED
+ if (predict == PredictionType.Fixed ||
+ predict == PredictionType.Search ||
+ (predict == PredictionType.Estimated && sub->type == SubframeType.Fixed) ||
+ n <= eparams.max_prediction_order)
+ {
+ int max_fixed_order = Math.Min(eparams.max_fixed_order, 4);
+ int min_fixed_order = Math.Min(eparams.min_fixed_order, max_fixed_order);
+
+ for (i = min_fixed_order; i <= max_fixed_order; i++)
+ encode_residual_fixed_sub(frame, i, ch);
+ }
+
+ // LPC
+ if (n > eparams.max_prediction_order &&
+ (predict == PredictionType.Levinson ||
+ predict == PredictionType.Search ||
+ (predict == PredictionType.Estimated && sub->type == SubframeType.LPC)))
+ {
+ double* lpcs = stackalloc double[lpc.MAX_LPC_ORDER * lpc.MAX_LPC_ORDER];
+ int min_order = eparams.min_prediction_order;
+ int max_order = eparams.max_prediction_order;
+
+ fixed (double* window = windowBuffer)
+ for (int iWindow = 0; iWindow < _windowcount; iWindow++)
+ {
+ if (predict == PredictionType.Estimated && sub->window != iWindow)
+ continue;
+
+ int est_order = (int)lpc.calc_coefs(smp, (uint)n, (uint)max_order, omethod, lpcs,
+ window + iWindow * _windowsize);
+
+ switch (omethod)
+ {
+ case OrderMethod.Max:
+ // always use maximum order
+ encode_residual_lpc_sub(frame, lpcs, iWindow, max_order, ch);
+ break;
+ case OrderMethod.Estimate:
+ // estimated order
+ encode_residual_lpc_sub(frame, lpcs, iWindow, est_order, ch);
+ break;
+ case OrderMethod.Search:
+ // brute-force optimal order search
+ for (i = max_order; i > 0; i--)
+ encode_residual_lpc_sub(frame, lpcs, iWindow, i, ch);
+ break;
+ case OrderMethod.EstSearch:
+ // brute-force search starting from estimate
+ for (i = est_order; i > 0; i--)
+ encode_residual_lpc_sub(frame, lpcs, iWindow, i, ch);
+ break;
+ case OrderMethod.LogFast:
+ // Try max, est, 32,16,8,4,2,1
+ encode_residual_lpc_sub(frame, lpcs, iWindow, max_order, ch);
+ //encode_residual_lpc_sub(frame, lpcs, est_order, ch);
+ for (i = lpc.MAX_LPC_ORDER; i > 0; i >>= 1)
+ if (i < max_order)
+ encode_residual_lpc_sub(frame, lpcs, iWindow, i, ch);
+ break;
+ case OrderMethod.LogSearch:
+ // do LogFast first
+ encode_residual_lpc_sub(frame, lpcs, iWindow, max_order, ch);
+ //encode_residual_lpc_sub(frame, lpcs, est_order, ch);
+ for (i = lpc.MAX_LPC_ORDER; i > 0; i >>= 1)
+ if (i < max_order)
+ encode_residual_lpc_sub(frame, lpcs, iWindow, i, ch);
+ // if found a good order, try to search around it
+ if (frame->subframes[ch].type == SubframeType.LPC)
+ {
+ // log search (written by Michael Niedermayer for FFmpeg)
+ for (int step = lpc.MAX_LPC_ORDER; step > 0; step >>= 1)
+ {
+ int last = frame->subframes[ch].order;
+ if (step <= (last + 1) / 2)
+ for (i = last - step; i <= last + step; i += step)
+ if (i >= min_order && i <= max_order)
+ encode_residual_lpc_sub(frame, lpcs, iWindow, i, ch);
+ }
+ }
+ break;
+ default:
+ throw new Exception("unknown ordermethod");
+ }
+ }
+ }
+ }
+
+ unsafe void output_frame_header(FlacFrame* frame, BitWriter bitwriter)
+ {
+ bitwriter.writebits(16, 0xFFF8);
+ bitwriter.writebits(4, frame->bs_code0);
+ bitwriter.writebits(4, sr_code0);
+ if (frame->ch_mode == ChannelMode.NotStereo)
+ bitwriter.writebits(4, ch_code);
+ else
+ bitwriter.writebits(4, (int) frame->ch_mode);
+ bitwriter.writebits(3, bps_code);
+ bitwriter.writebits(1, 0);
+ bitwriter.write_utf8(frame_count);
+
+ // custom block size
+ if (frame->bs_code1 >= 0)
+ {
+ if (frame->bs_code1 < 256)
+ bitwriter.writebits(8, frame->bs_code1);
+ else
+ bitwriter.writebits(16, frame->bs_code1);
+ }
+
+ // custom sample rate
+ if (sr_code1 > 0)
+ {
+ if (sr_code1 < 256)
+ bitwriter.writebits(8, sr_code1);
+ else
+ bitwriter.writebits(16, sr_code1);
+ }
+
+ // CRC-8 of frame header
+ bitwriter.flush();
+ byte crc = crc8.ComputeChecksum(frame_buffer, 0, bitwriter.Length);
+ bitwriter.writebits(8, crc);
+ }
+
+ unsafe void output_residual(FlacFrame* frame, BitWriter bitwriter, FlacSubframe* sub)
+ {
+ // rice-encoded block
+ bitwriter.writebits(2, 0);
+
+ // partition order
+ int porder = sub->rc.porder;
+ int psize = frame->blocksize >> porder;
+ //assert(porder >= 0);
+ bitwriter.writebits(4, porder);
+ int res_cnt = psize - sub->order;
+
+ // residual
+ int j = sub->order;
+ for (int p = 0; p < (1 << porder); p++)
+ {
+ int k = (int) sub->rc.rparams[p];
+ bitwriter.writebits(4, k);
+ if (p == 1) res_cnt = psize;
+ for (int i = 0; i < res_cnt && j < frame->blocksize; i++, j++)
+ {
+ bitwriter.write_rice_signed(k, sub->residual[j]);
+ }
+ }
+ }
+
+ unsafe void
+ output_subframe_constant(FlacFrame* frame, BitWriter bitwriter, FlacSubframe* sub)
+ {
+ bitwriter.writebits_signed(sub->obits, sub->residual[0]);
+ }
+
+ unsafe void
+ output_subframe_verbatim(FlacFrame* frame, BitWriter bitwriter, FlacSubframe* sub)
+ {
+ int n = frame->blocksize;
+ for (int i = 0; i < n; i++)
+ bitwriter.writebits_signed(sub->obits, sub->samples[i]);
+ // Don't use residual here, because we don't copy samples to residual for verbatim frames.
+ }
+
+ unsafe void
+ output_subframe_fixed(FlacFrame* frame, BitWriter bitwriter, FlacSubframe* sub)
+ {
+ // warm-up samples
+ for (int i = 0; i < sub->order; i++)
+ bitwriter.writebits_signed(sub->obits, sub->residual[i]);
+
+ // residual
+ output_residual(frame, bitwriter, sub);
+ }
+
+ unsafe void
+ output_subframe_lpc(FlacFrame* frame, BitWriter bitwriter, FlacSubframe* sub)
+ {
+ // warm-up samples
+ for (int i = 0; i < sub->order; i++)
+ bitwriter.writebits_signed(sub->obits, sub->residual[i]);
+
+ // LPC coefficients
+ uint cbits = lpc_precision;
+ bitwriter.writebits(4, cbits - 1);
+ bitwriter.writebits_signed(5, sub->shift);
+ for (int i = 0; i < sub->order; i++)
+ bitwriter.writebits_signed(cbits, sub->coefs[i]);
+
+ // residual
+ output_residual(frame, bitwriter, sub);
+ }
+
+ unsafe void output_subframes(FlacFrame* frame, BitWriter bitwriter)
+ {
+ for (int ch = 0; ch < channels; ch++)
+ {
+ FlacSubframe* sub = frame->subframes + ch;
+ // subframe header
+ int type_code = (int) sub->type;
+ if (sub->type == SubframeType.Fixed)
+ type_code |= sub->order;
+ if (sub->type == SubframeType.LPC)
+ type_code |= sub->order - 1;
+ bitwriter.writebits(1, 0);
+ bitwriter.writebits(6, type_code);
+ bitwriter.writebits(1, sub->wbits != 0 ? 1 : 0);
+ if (sub->wbits > 0)
+ bitwriter.writebits((int)sub->wbits, 1);
+
+ // subframe
+ switch (sub->type)
+ {
+ case SubframeType.Constant:
+ output_subframe_constant(frame, bitwriter, sub);
+ break;
+ case SubframeType.Verbatim:
+ output_subframe_verbatim(frame, bitwriter, sub);
+ break;
+ case SubframeType.Fixed:
+ output_subframe_fixed(frame, bitwriter, sub);
+ break;
+ case SubframeType.LPC:
+ output_subframe_lpc(frame, bitwriter, sub);
+ break;
+ }
+ }
+ }
+
+ void output_frame_footer(BitWriter bitwriter)
+ {
+ bitwriter.flush();
+ ushort crc = crc16.ComputeChecksum(frame_buffer, 0, bitwriter.Length);
+ bitwriter.writebits(16, crc);
+ bitwriter.flush();
+ }
+
+ unsafe void window_welch(double* window, int L)
+ {
+ int N = L - 1;
+ double N2 = (double)N / 2.0;
+
+ for (int n = 0; n <= N; n++)
+ {
+ double k = 1 / N2 - 1.0 - Math.Min(n, N-n);
+ //double k = ((double)n - N2) / N2;
+ window[n] = 1.0 - k * k;
+ }
+ }
+
+ unsafe void window_rectangle(double* window, int L)
+ {
+ for (int n = 0; n < L; n++)
+ window[n] = 1.0;
+ }
+
+ unsafe void window_flattop(double* window, int L)
+ {
+ int N = L - 1;
+ for (int n = 0; n < L; n++)
+ window[n] = 1.0 - 1.93 * Math.Cos(2.0 * Math.PI * n / N) + 1.29 * Math.Cos(4.0 * Math.PI * n / N) - 0.388 * Math.Cos(6.0 * Math.PI * n / N) + 0.0322 * Math.Cos(8.0 * Math.PI * n / N);
+ }
+
+ unsafe void window_tukey(double* window, int L)
+ {
+ window_rectangle(window, L);
+ double p = 0.5;
+ int Np = (int) (p / 2.0 * L) - 1;
+ if (Np > 0)
+ {
+ for (int n = 0; n <= Np; n++)
+ {
+ window[n] = 0.5 - 0.5 * Math.Cos(Math.PI * n / Np);
+ window[L - Np - 1 + n] = 0.5 - 0.5 * Math.Cos(Math.PI * (n + Np) / Np);
+ }
+ }
+ }
+
+ unsafe void window_hann(double* window, int L)
+ {
+ int N = L - 1;
+ for (int n = 0; n < L; n++)
+ window[n] = 0.5 - 0.5 * Math.Cos(2.0 * Math.PI * n / N);
+ }
+
+ unsafe int encode_frame()
+ {
+ FlacFrame* frame = stackalloc FlacFrame[1];
+ FlacSubframe* sf = stackalloc FlacSubframe[channels * 3];
+
+ fixed (int* s = samplesBuffer, r = residualBuffer)
+ {
+ frame->subframes = sf;
+ init_frame(frame);
+ int* current_residual = stackalloc int[frame->blocksize * 3];
+ bool alreadyEncoded = false;
+ if (frame->blocksize != _windowsize && frame->blocksize > 4)
+ fixed (double* window = windowBuffer)
+ {
+ _windowsize = frame->blocksize;
+ _windowcount = 0;
+ if ((eparams.window_function & WindowFunction.Welch) != 0)
+ window_welch(window + (_windowcount++)*_windowsize, _windowsize);
+ if ((eparams.window_function & WindowFunction.Tukey) != 0)
+ window_tukey(window + (_windowcount++) * _windowsize, _windowsize);
+ if ((eparams.window_function & WindowFunction.Hann) != 0)
+ window_hann(window + (_windowcount++) * _windowsize, _windowsize);
+ if ((eparams.window_function & WindowFunction.Flattop) != 0)
+ window_flattop(window + (_windowcount++) * _windowsize, _windowsize);
+ if (_windowcount == 0)
+ throw new Exception("invalid windowfunction");
+ }
+
+ if (channels != 2 || frame->blocksize <= 32 || eparams.stereo_method == StereoMethod.Independent)
+ {
+ frame->ch_mode = channels != 2 ? ChannelMode.NotStereo : ChannelMode.LeftRight;
+ frame->current.residual = current_residual;
+ for (int ch = 0; ch < channels; ch++)
+ initialize_subframe(frame, ch, s + ch * Flake.MAX_BLOCKSIZE, r + ch * Flake.MAX_BLOCKSIZE, bits_per_sample);
+ }
+ else
+ {
+ FlacFrame* frameM = stackalloc FlacFrame[1];
+ FlacFrame* frameS = stackalloc FlacFrame[1];
+ init_frame(frameS);
+ frameS->subframes = sf + channels;
+ frameS->ch_mode = ChannelMode.LeftRight;
+ frameS->current.residual = current_residual;
+ init_frame(frameM);
+ frameM->subframes = sf + channels * 2;
+ frameM->ch_mode = ChannelMode.MidSide;
+ frameM->current.residual = current_residual + frame->blocksize;
+
+ channel_decorrelation(s, s + Flake.MAX_BLOCKSIZE, s + 2 * Flake.MAX_BLOCKSIZE, s + 3 * Flake.MAX_BLOCKSIZE, frame->blocksize);
+
+ for (int ch = 0; ch < channels; ch++)
+ {
+ initialize_subframe(frameS, ch, s + ch * Flake.MAX_BLOCKSIZE, r + ch * Flake.MAX_BLOCKSIZE, bits_per_sample);
+ initialize_subframe(frameM, ch, s + (channels + ch) * Flake.MAX_BLOCKSIZE, r + (channels + ch) * Flake.MAX_BLOCKSIZE, bits_per_sample + (uint)ch);
+ }
+
+ uint bitsBest = UINT32_MAX;
+ ChannelMode modeBest = ChannelMode.LeftRight;
+
+ if (eparams.stereo_method == StereoMethod.Estimate)
+ {
+ frameM->subframes[0].size = (uint)frame->blocksize * 32 + calc_decorr_score(frameM, 0, frameM->subframes + 0);
+ frameM->subframes[1].size = (uint)frame->blocksize * 32 + calc_decorr_score(frameM, 1, frameM->subframes + 1);
+ frameS->subframes[0].size = (uint)frame->blocksize * 32 + calc_decorr_score(frameS, 0, frameS->subframes + 0);
+ frameS->subframes[1].size = (uint)frame->blocksize * 32 + calc_decorr_score(frameS, 1, frameS->subframes + 1);
+ }
+ else if (eparams.stereo_method == StereoMethod.Estimate2)
+ {
+ int max_prediction_order = eparams.max_prediction_order;
+ int max_window = _windowcount;
+ eparams.max_prediction_order /= 2;
+ _windowcount = 1;
+ encode_residual(frameM, 0, PredictionType.Levinson, OrderMethod.Estimate);
+ encode_residual(frameM, 1, PredictionType.Levinson, OrderMethod.Estimate);
+ encode_residual(frameS, 0, PredictionType.Levinson, OrderMethod.Estimate);
+ encode_residual(frameS, 1, PredictionType.Levinson, OrderMethod.Estimate);
+ _windowcount = max_window;
+ eparams.max_prediction_order = max_prediction_order;
+ }
+ else if (eparams.stereo_method == StereoMethod.Estimate3)
+ {
+ int max_fixed_order = eparams.max_fixed_order;
+ eparams.max_fixed_order = 2;
+ encode_residual(frameM, 0, PredictionType.Fixed, OrderMethod.Estimate);
+ encode_residual(frameM, 1, PredictionType.Fixed, OrderMethod.Estimate);
+ encode_residual(frameS, 0, PredictionType.Fixed, OrderMethod.Estimate);
+ encode_residual(frameS, 1, PredictionType.Fixed, OrderMethod.Estimate);
+ eparams.max_fixed_order = max_fixed_order;
+ }
+ else if (eparams.stereo_method == StereoMethod.Estimate4)
+ {
+ int max_prediction_order = eparams.max_prediction_order;
+ int max_fixed_order = eparams.max_fixed_order;
+ int min_fixed_order = eparams.min_fixed_order;
+ eparams.min_fixed_order = 2;
+ eparams.max_fixed_order = 2;
+ eparams.max_prediction_order = Math.Min(eparams.max_prediction_order, 8);
+ encode_residual(frameM, 0, eparams.prediction_type, OrderMethod.Estimate);
+ encode_residual(frameM, 1, eparams.prediction_type, OrderMethod.Estimate);
+ encode_residual(frameS, 0, eparams.prediction_type, OrderMethod.Estimate);
+ encode_residual(frameS, 1, eparams.prediction_type, OrderMethod.Estimate);
+ eparams.min_fixed_order = min_fixed_order;
+ eparams.max_fixed_order = max_fixed_order;
+ eparams.max_prediction_order = max_prediction_order;
+ }
+ else if (eparams.stereo_method == StereoMethod.Estimate5)
+ {
+ int max_prediction_order = eparams.max_prediction_order;
+ int max_fixed_order = eparams.max_fixed_order;
+ int min_fixed_order = eparams.min_fixed_order;
+ eparams.min_fixed_order = 2;
+ eparams.max_fixed_order = 2;
+ eparams.max_prediction_order = Math.Min(eparams.max_prediction_order, 12);
+ encode_residual(frameM, 0, eparams.prediction_type, OrderMethod.Estimate);
+ encode_residual(frameM, 1, eparams.prediction_type, OrderMethod.Estimate);
+ encode_residual(frameS, 0, eparams.prediction_type, OrderMethod.Estimate);
+ encode_residual(frameS, 1, eparams.prediction_type, OrderMethod.Estimate);
+ eparams.min_fixed_order = min_fixed_order;
+ eparams.max_fixed_order = max_fixed_order;
+ eparams.max_prediction_order = max_prediction_order;
+ }
+ else // StereoMethod.Search
+ {
+ encode_residual(frameM, 0, eparams.prediction_type, eparams.order_method);
+ encode_residual(frameM, 1, eparams.prediction_type, eparams.order_method);
+ encode_residual(frameS, 0, eparams.prediction_type, eparams.order_method);
+ encode_residual(frameS, 1, eparams.prediction_type, eparams.order_method);
+ alreadyEncoded = true;
+ }
+
+ if (bitsBest > frameM->subframes[0].size + frameM->subframes[1].size)
+ {
+ bitsBest = frameM->subframes[0].size + frameM->subframes[1].size;
+ modeBest = ChannelMode.MidSide;
+ frame->subframes[0] = frameM->subframes[0];
+ frame->subframes[1] = frameM->subframes[1];
+ }
+ if (bitsBest > frameM->subframes[1].size + frameS->subframes[1].size)
+ {
+ bitsBest = frameM->subframes[1].size + frameS->subframes[1].size;
+ modeBest = ChannelMode.RightSide;
+ frame->subframes[0] = frameM->subframes[1];
+ frame->subframes[1] = frameS->subframes[1];
+ }
+ if (bitsBest > frameM->subframes[1].size + frameS->subframes[0].size)
+ {
+ bitsBest = frameM->subframes[1].size + frameS->subframes[0].size;
+ modeBest = ChannelMode.LeftSide;
+ frame->subframes[0] = frameS->subframes[0];
+ frame->subframes[1] = frameM->subframes[1];
+ }
+ if (bitsBest > frameS->subframes[0].size + frameS->subframes[1].size)
+ {
+ bitsBest = frameS->subframes[0].size + frameS->subframes[1].size;
+ modeBest = ChannelMode.LeftRight;
+ frame->subframes[0] = frameS->subframes[0];
+ frame->subframes[1] = frameS->subframes[1];
+ }
+ frame->ch_mode = modeBest;
+ frame->current.residual = current_residual + 2 * frame->blocksize;
+ if (eparams.stereo_method == StereoMethod.Estimate4 || eparams.stereo_method == StereoMethod.Estimate5)
+ {
+ encode_residual(frame, 0, PredictionType.Estimated, eparams.order_method);
+ encode_residual(frame, 1, PredictionType.Estimated, eparams.order_method);
+ alreadyEncoded = true;
+ }
+ }
+
+ if (!alreadyEncoded)
+ for (int ch = 0; ch < channels; ch++)
+ encode_residual(frame, ch, eparams.prediction_type, eparams.order_method);
+
+ BitWriter bitwriter = new BitWriter(frame_buffer, 0, max_frame_size);
+
+ output_frame_header(frame, bitwriter);
+ output_subframes(frame, bitwriter);
+ output_frame_footer(bitwriter);
+
+ if (frame_buffer != null)
+ {
+ if (eparams.variable_block_size > 0)
+ frame_count += eparams.block_size;
+ else
+ frame_count++;
+ }
+ return bitwriter.Length;
+ }
+ }
+
+ unsafe void output_frame()
+ {
+ if (verify != null)
+ {
+ fixed (int* s = verifyBuffer, r = samplesBuffer)
+ for (int ch = 0; ch < channels; ch++)
+ Flake.memcpy(s + ch * Flake.MAX_BLOCKSIZE, r + ch * Flake.MAX_BLOCKSIZE, eparams.block_size);
+ }
+
+ int fs;
+ if (0 != eparams.variable_block_size && 0 == (eparams.block_size & 7) && eparams.block_size >= 128)
+ fs = encode_frame_vbs();
+ else
+ fs = encode_frame();
+
+ _position += eparams.block_size;
+ _IO.Write(frame_buffer, 0, fs);
+ _totalSize += fs;
+
+ if (verify != null)
+ {
+ int decoded = verify.DecodeFrame(frame_buffer, 0, fs);
+ if (decoded != fs || verify.Remaining != (ulong)eparams.block_size)
+ throw new Exception("validation failed!");
+ fixed (int* s = verifyBuffer, r = verify.Samples)
+ {
+ for (int ch = 0; ch < channels; ch++)
+ if (Flake.memcmp(s + ch * Flake.MAX_BLOCKSIZE, r +ch * Flake.MAX_BLOCKSIZE, decoded))
+ throw new Exception("validation failed!");
+ }
+ }
+ }
+
+ public void Write(int[,] buff, int pos, int sampleCount)
+ {
+ if (!inited)
+ {
+ int header_size = flake_encode_init();
+ if (_IO == null)
+ _IO = new FileStream(_path, FileMode.Create, FileAccess.Write, FileShare.Read);
+ _IO.Write(header, 0, header_size);
+ inited = true;
+ }
+
+ int len = sampleCount;
+ while (len > 0)
+ {
+ int block = Math.Min(len, eparams.block_size - samplesInBuffer);
+
+ copy_samples(buff, pos, block);
+
+ AudioSamples.FLACSamplesToBytes(buff, pos, frame_buffer, 0, block, channels, (int)bits_per_sample);
+ md5.TransformBlock(frame_buffer, 0, block * channels * ((int)bits_per_sample >> 3), null, 0);
+
+ if (samplesInBuffer < eparams.block_size)
+ return;
+
+ output_frame();
+
+ samplesInBuffer = 0;
+ len -= block;
+ pos += block;
+ }
+ }
+
+ public string Path { get { return _path; } }
+
+ int[] flac_samplerates;
+ int[] flac_bitdepths;
+ int[] flac_blocksizes;
+ string vendor_string = "Flake#0.1";
+
+ int select_blocksize(int samplerate, int time_ms)
+ {
+ int blocksize = flac_blocksizes[1];
+ int target = (samplerate * time_ms) / 1000;
+ for (int i = 0; i < flac_blocksizes.Length; i++)
+ if (target >= flac_blocksizes[i] && flac_blocksizes[i] > blocksize)
+ {
+ blocksize = flac_blocksizes[i];
+ }
+ return blocksize;
+ }
+
+ void write_streaminfo(byte[] header, int pos, int last)
+ {
+ Array.Clear(header, pos, 38);
+ BitWriter bitwriter = new BitWriter(header, pos, 38);
+
+ // metadata header
+ bitwriter.writebits(1, last);
+ bitwriter.writebits(7, 0);
+ bitwriter.writebits(24, 34);
+
+ if (eparams.variable_block_size > 0)
+ bitwriter.writebits(16, 0);
+ else
+ bitwriter.writebits(16, eparams.block_size);
+
+ bitwriter.writebits(16, eparams.block_size);
+ bitwriter.writebits(24, 0);
+ bitwriter.writebits(24, max_frame_size);
+ bitwriter.writebits(20, sample_rate);
+ bitwriter.writebits(3, channels - 1);
+ bitwriter.writebits(5, bits_per_sample - 1);
+
+ // total samples
+ if (sample_count > 0)
+ {
+ bitwriter.writebits(4, 0);
+ bitwriter.writebits(32, sample_count);
+ }
+ else
+ {
+ bitwriter.writebits(4, 0);
+ bitwriter.writebits(32, 0);
+ }
+ bitwriter.flush();
+ }
+
+ /**
+ * Write vorbis comment metadata block to byte array.
+ * Just writes the vendor string for now.
+ */
+ int write_vorbis_comment(byte[] comment, int pos, int last)
+ {
+ BitWriter bitwriter = new BitWriter(comment, pos, 4);
+ Encoding enc = new ASCIIEncoding();
+ int vendor_len = enc.GetBytes(vendor_string, 0, vendor_string.Length, comment, pos + 8);
+
+ // metadata header
+ bitwriter.writebits(1, last);
+ bitwriter.writebits(7, 4);
+ bitwriter.writebits(24, vendor_len + 8);
+
+ comment[pos + 4] = (byte)(vendor_len & 0xFF);
+ comment[pos + 5] = (byte)((vendor_len >> 8) & 0xFF);
+ comment[pos + 6] = (byte)((vendor_len >> 16) & 0xFF);
+ comment[pos + 7] = (byte)((vendor_len >> 24) & 0xFF);
+ comment[pos + 8 + vendor_len] = 0;
+ comment[pos + 9 + vendor_len] = 0;
+ comment[pos + 10 + vendor_len] = 0;
+ comment[pos + 11 + vendor_len] = 0;
+ bitwriter.flush();
+ return vendor_len + 12;
+ }
+
+ /**
+ * Write padding metadata block to byte array.
+ */
+ int
+ write_padding(byte[] padding, int pos, int last, int padlen)
+ {
+ BitWriter bitwriter = new BitWriter(padding, pos, 4);
+
+ // metadata header
+ bitwriter.writebits(1, last);
+ bitwriter.writebits(7, 1);
+ bitwriter.writebits(24, padlen);
+
+ return padlen + 4;
+ }
+
+ int write_headers()
+ {
+ int header_size = 0;
+ int last = 0;
+
+ // stream marker
+ header[0] = 0x66;
+ header[1] = 0x4C;
+ header[2] = 0x61;
+ header[3] = 0x43;
+ header_size += 4;
+
+ // streaminfo
+ write_streaminfo(header, header_size, last);
+ header_size += 38;
+
+ // vorbis comment
+ if (eparams.padding_size == 0) last = 1;
+ header_size += write_vorbis_comment(header, header_size, last);
+
+ // padding
+ if (eparams.padding_size > 0)
+ {
+ last = 1;
+ header_size += write_padding(header, header_size, last, eparams.padding_size);
+ }
+
+ return header_size;
+ }
+
+ int flake_encode_init()
+ {
+ int i, header_len;
+
+ //if(flake_validate_params(s) < 0)
+
+ ch_code = channels - 1;
+
+ // find samplerate in table
+ for (i = 4; i < 12; i++)
+ {
+ if (sample_rate == flac_samplerates[i])
+ {
+ sr_code0 = i;
+ break;
+ }
+ }
+
+ // if not in table, samplerate is non-standard
+ if (i == 12)
+ throw new Exception("non-standard samplerate");
+
+ for (i = 1; i < 8; i++)
+ {
+ if (bits_per_sample == flac_bitdepths[i])
+ {
+ bps_code = i;
+ break;
+ }
+ }
+ if (i == 8)
+ throw new Exception("non-standard bps");
+ // FIXME: For now, only 16-bit encoding is supported
+ if (bits_per_sample != 16)
+ throw new Exception("non-standard bps");
+
+ if (eparams.block_size == 0)
+ if (_blocksize == 0)
+ eparams.block_size = select_blocksize(sample_rate, eparams.block_time_ms);
+ else
+ eparams.block_size = _blocksize;
+
+ // select LPC precision based on block size
+ if (eparams.block_size <= 192) lpc_precision = 7U;
+ else if (eparams.block_size <= 384) lpc_precision = 8U;
+ else if (eparams.block_size <= 576) lpc_precision = 9U;
+ else if (eparams.block_size <= 1152) lpc_precision = 10U;
+ else if (eparams.block_size <= 2304) lpc_precision = 11U;
+ else if (eparams.block_size <= 4608) lpc_precision = 12U;
+ else if (eparams.block_size <= 8192) lpc_precision = 13U;
+ else if (eparams.block_size <= 16384) lpc_precision = 14U;
+ else lpc_precision = 15;
+
+ // set maximum encoded frame size (if larger, re-encodes in verbatim mode)
+ if (channels == 2)
+ max_frame_size = 16 + ((eparams.block_size * (int)(bits_per_sample + bits_per_sample + 1) + 7) >> 3);
+ else
+ max_frame_size = 16 + ((eparams.block_size * channels * (int)bits_per_sample + 7) >> 3);
+
+ // output header bytes
+ header = new byte[eparams.padding_size + 1024];
+ header_len = write_headers();
+
+ // initialize CRC & MD5
+ //crc_init();
+ //md5_init(&ctx->md5ctx);
+
+ frame_buffer = new byte[max_frame_size];
+
+ return header_len;
+ }
+ }
+
+ struct FlakeEncodeParams
+ {
+ // compression quality
+ // set by user prior to calling flake_encode_init
+ // standard values are 0 to 8
+ // 0 is lower compression, faster encoding
+ // 8 is higher compression, slower encoding
+ // extended values 9 to 12 are slower and/or use
+ // higher prediction orders
+ public int compression;
+
+ // prediction order selection method
+ // set by user prior to calling flake_encode_init
+ // if set to less than 0, it is chosen based on compression.
+ // valid values are 0 to 5
+ // 0 = use maximum order only
+ // 1 = use estimation
+ // 2 = 2-level
+ // 3 = 4-level
+ // 4 = 8-level
+ // 5 = full search
+ // 6 = log search
+ public OrderMethod order_method;
+
+
+ // stereo decorrelation method
+ // set by user prior to calling flake_encode_init
+ // if set to less than 0, it is chosen based on compression.
+ // valid values are 0 to 2
+ // 0 = independent L+R channels
+ // 1 = mid-side encoding
+ public StereoMethod stereo_method;
+
+ // block size in samples
+ // set by the user prior to calling flake_encode_init
+ // if set to 0, a block size is chosen based on block_time_ms
+ // can also be changed by user before encoding a frame
+ public int block_size;
+
+ // block time in milliseconds
+ // set by the user prior to calling flake_encode_init
+ // used to calculate block_size based on sample rate
+ // can also be changed by user before encoding a frame
+ public int block_time_ms;
+
+ // padding size in bytes
+ // set by the user prior to calling flake_encode_init
+ // if set to less than 0, defaults to 4096
+ public int padding_size;
+
+ // maximum encoded frame size
+ // this is set by flake_encode_init based on input audio format
+ // it can be used by the user to allocate an output buffer
+ public int max_frame_size;
+
+ // minimum LPC order
+ // set by user prior to calling flake_encode_init
+ // if set to less than 0, it is chosen based on compression.
+ // valid values are 1 to 32
+ public int min_prediction_order;
+
+ // maximum LPC order
+ // set by user prior to calling flake_encode_init
+ // if set to less than 0, it is chosen based on compression.
+ // valid values are 1 to 32
+ public int max_prediction_order;
+
+ // minimum fixed prediction order
+ // set by user prior to calling flake_encode_init
+ // if set to less than 0, it is chosen based on compression.
+ // valid values are 0 to 4
+ public int min_fixed_order;
+
+ // maximum fixed prediction order
+ // set by user prior to calling flake_encode_init
+ // if set to less than 0, it is chosen based on compression.
+ // valid values are 0 to 4
+ public int max_fixed_order;
+
+ // type of linear prediction
+ // set by user prior to calling flake_encode_init
+ public PredictionType prediction_type;
+
+ // minimum partition order
+ // set by user prior to calling flake_encode_init
+ // if set to less than 0, it is chosen based on compression.
+ // valid values are 0 to 8
+ public int min_partition_order;
+
+ // maximum partition order
+ // set by user prior to calling flake_encode_init
+ // if set to less than 0, it is chosen based on compression.
+ // valid values are 0 to 8
+ public int max_partition_order;
+
+ // whether to use variable block sizes
+ // set by user prior to calling flake_encode_init
+ // 0 = fixed block size
+ // 1 = variable block size
+ public int variable_block_size;
+
+
+ public WindowFunction window_function;
+
+ public int flake_set_defaults(int lvl)
+ {
+ compression = lvl;
+
+ if ((lvl < 0 || lvl > 12) && (lvl != 99))
+ {
+ return -1;
+ }
+
+ // default to level 5 params
+ window_function = WindowFunction.Flattop | WindowFunction.Tukey;
+ order_method = OrderMethod.Estimate;
+ stereo_method = StereoMethod.Estimate;
+ block_size = 0;
+ block_time_ms = 105;
+ prediction_type = PredictionType.Search;
+ min_prediction_order = 1;
+ max_prediction_order = 12;
+ min_fixed_order = 0;
+ max_fixed_order = 4;
+ min_partition_order = 0;
+ max_partition_order = 6;
+ variable_block_size = 0;
+
+ // differences from level 5
+ switch (lvl)
+ {
+ case 0:
+ stereo_method = StereoMethod.Independent;
+ block_time_ms = 27;
+ prediction_type = PredictionType.Fixed;
+ min_fixed_order = 2;
+ max_fixed_order = 2;
+ min_partition_order = 4;
+ max_partition_order = 4;
+ break;
+ case 1:
+ block_time_ms = 27;
+ prediction_type = PredictionType.Fixed;
+ min_fixed_order = 2;
+ max_fixed_order = 3;
+ min_partition_order = 2;
+ max_partition_order = 2;
+ break;
+ case 2:
+ block_time_ms = 27;
+ prediction_type = PredictionType.Fixed;
+ //prediction_type = PredictionType.Fixed;
+ min_fixed_order = 2;
+ max_fixed_order = 4;
+ //min_partition_order = 0;
+ //max_partition_order = 3;
+ break;
+ case 3:
+ prediction_type = PredictionType.Levinson;
+ window_function = WindowFunction.Welch;
+ max_prediction_order = 8;
+ break;
+ case 4:
+ prediction_type = PredictionType.Levinson;
+ window_function = WindowFunction.Welch;
+ break;
+ case 5:
+ prediction_type = PredictionType.Levinson;
+ break;
+ case 6:
+ stereo_method = StereoMethod.Estimate4;
+ break;
+ case 7:
+ order_method = OrderMethod.LogSearch;
+ stereo_method = StereoMethod.Estimate4;
+ break;
+ case 8:
+ order_method = OrderMethod.Search;
+ stereo_method = StereoMethod.Estimate4;
+ break;
+ case 9:
+ stereo_method = StereoMethod.Estimate4;
+ max_prediction_order = 32;
+ break;
+ case 10:
+ order_method = OrderMethod.LogFast;
+ stereo_method = StereoMethod.Estimate5;
+ max_prediction_order = 32;
+ break;
+ case 11:
+ order_method = OrderMethod.LogSearch;
+ stereo_method = StereoMethod.Estimate5;
+ max_prediction_order = 32;
+ max_partition_order = 8;
+ break;
+ case 99:
+ order_method = OrderMethod.Search;
+ block_time_ms = 186;
+ max_prediction_order = 32;
+ max_partition_order = 8;
+ variable_block_size = 2;
+ break;
+ }
+
+ return 0;
+ }
+ }
+}
diff --git a/CUETools.Codecs.FLAKE/Properties/AssemblyInfo.cs b/CUETools.Codecs.FLAKE/Properties/AssemblyInfo.cs
new file mode 100644
index 0000000..ca83661
--- /dev/null
+++ b/CUETools.Codecs.FLAKE/Properties/AssemblyInfo.cs
@@ -0,0 +1,35 @@
+using System.Reflection;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+
+// 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.
+[assembly: AssemblyTitle("CUETools.Codecs.FLAKE")]
+[assembly: AssemblyDescription("")]
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyCompany("Microsoft")]
+[assembly: AssemblyProduct("CUETools.Codecs.FLAKE")]
+[assembly: AssemblyCopyright("Copyright © Microsoft 2009")]
+[assembly: AssemblyTrademark("")]
+[assembly: AssemblyCulture("")]
+
+// Setting ComVisible to false makes the types in this assembly not visible
+// to COM components. If you need to access a type in this assembly from
+// COM, set the ComVisible attribute to true on that type.
+[assembly: ComVisible(false)]
+
+// The following GUID is for the ID of the typelib if this project is exposed to COM
+[assembly: Guid("af385bb5-1d2e-409b-97db-b96d9ed491a0")]
+
+// Version information for an assembly consists of the following four values:
+//
+// Major Version
+// Minor Version
+// Build Number
+// Revision
+//
+// 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.0.0")]
+[assembly: AssemblyFileVersion("1.0.0.0")]
diff --git a/CUETools.Codecs.FLAKE/lpc.cs b/CUETools.Codecs.FLAKE/lpc.cs
new file mode 100644
index 0000000..6e8e01e
--- /dev/null
+++ b/CUETools.Codecs.FLAKE/lpc.cs
@@ -0,0 +1,650 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace CUETools.Codecs.FLAKE
+{
+ class lpc
+ {
+ public const int MAX_LPC_ORDER = 32;
+ public const int MAX_LPC_WINDOWS = 4;
+
+ /**
+ * Apply Welch window function to audio block
+ */
+ static unsafe void
+ apply_welch_window(/*const*/int* data, uint len, double* w_data)
+ {
+ double c = (2.0 / (len - 1.0)) - 1.0;
+ for (uint i = 0; i < (len >> 1); i++)
+ {
+ double w = 1.0 - ((c - i) * (c - i));
+ w_data[i] = data[i] * w;
+ w_data[len - 1 - i] = data[len - 1 - i] * w;
+ }
+ }
+
+ /**
+ * Calculates autocorrelation data from audio samples
+ * A Welch window function is applied before calculation.
+ */
+ static unsafe void
+ compute_autocorr(/*const*/ int* data, uint len, uint lag, double* autoc, double* window)
+ {
+ double* data1 = stackalloc double[(int)len + 16];
+ uint i, j;
+ double temp, temp2;
+
+ if (window == null)
+ apply_welch_window(data, len, data1);
+ else
+ {
+ for (i = 0; i < len; i++)
+ data1[i] = data[i] * window[i];
+ }
+ data1[len] = 0;
+
+ for (i = 0; i <= lag; ++i)
+ {
+ temp = 1.0;
+ temp2 = 1.0;
+ for (j = 0; j <= lag - i; ++j)
+ temp += data1[j + i] * data1[j];
+
+ double* finish = data1 + len - i;
+ for (double* pdata = data1 + lag + 1 - i; pdata < finish; pdata += 2)
+ {
+ temp += pdata[i] * pdata[0];
+ temp2 += pdata[i + 1] * pdata[1];
+ }
+ autoc[i] = temp + temp2;
+ }
+
+ //int sample, coeff;
+ //for (coeff = 0; coeff <= lag; coeff++)
+ // autoc[coeff] = 0.0;
+ //int data_len = (int)len;
+ //int limit = data_len - (int)lag - 1;
+ //for (sample = 0; sample <= limit; sample++)
+ //{
+ // double d = data1[sample];
+ // for (coeff = 0; coeff <= lag; coeff++)
+ // autoc[coeff] += d * data1[sample + coeff];
+ //}
+ //for (; sample < data_len; sample++)
+ //{
+ // double d = data1[sample];
+ // for (coeff = 0; coeff < data_len - sample; coeff++)
+ // autoc[coeff] += d * data1[sample + coeff];
+ //}
+ }
+
+ /**
+ * Levinson-Durbin recursion.
+ * Produces LPC coefficients from autocorrelation data.
+ */
+ static unsafe void
+ compute_lpc_coefs(/*const*/ double* autoc, uint max_order, double* reff,
+ double* lpc/*[][MAX_LPC_ORDER]*/)
+ {
+ double* lpc_tmp = stackalloc double[MAX_LPC_ORDER];
+
+ int i, j, i2;
+ double r, err, tmp;
+
+ if (max_order > MAX_LPC_ORDER)
+ throw new Exception("wierd");
+
+ for (i = 0; i < max_order; i++)
+ lpc_tmp[i] = 0;
+
+ err = 1.0;
+ if (autoc != null)
+ err = autoc[0];
+
+ for (i = 0; i < max_order; i++)
+ {
+ if (reff != null)
+ {
+ r = reff[i];
+ }
+ else
+ {
+ r = -autoc[i + 1];
+ for (j = 0; j < i; j++)
+ {
+ r -= lpc_tmp[j] * autoc[i - j];
+ }
+ r /= err;
+ err *= 1.0 - (r * r);
+ }
+
+ i2 = (i >> 1);
+ lpc_tmp[i] = r;
+ for (j = 0; j < i2; j++)
+ {
+ tmp = lpc_tmp[j];
+ lpc_tmp[j] += r * lpc_tmp[i - 1 - j];
+ lpc_tmp[i - 1 - j] += r * tmp;
+ }
+ if (0 != (i & 1))
+ {
+ lpc_tmp[j] += lpc_tmp[j] * r;
+ }
+
+ for (j = 0; j <= i; j++)
+ {
+ lpc[i * MAX_LPC_ORDER + j] = -lpc_tmp[j];
+ }
+ }
+ }
+
+ /**
+ * Compute LPC coefs for Flake.OrderMethod._EST
+ * Faster LPC coeff computation by first calculating the reflection coefficients
+ * using Schur recursion. That allows for estimating the optimal order before
+ * running Levinson recursion.
+ */
+ static unsafe uint
+ compute_lpc_coefs_est(/*const*/ double* autoc, uint max_order,
+ double* lpc/*[][MAX_LPC_ORDER]*/)
+ {
+ double* gen0 = stackalloc double[MAX_LPC_ORDER];
+ double* gen1 = stackalloc double[MAX_LPC_ORDER];
+ double* reff = stackalloc double[MAX_LPC_ORDER];
+
+ // Schur recursion
+ for (uint i = 0; i < max_order; i++)
+ gen0[i] = gen1[i] = autoc[i + 1];
+
+ double error = autoc[0];
+ reff[0] = -gen1[0] / error;
+ error += gen1[0] * reff[0];
+ for (uint i = 1; i < max_order; i++)
+ {
+ for (uint j = 0; j < max_order - i; j++)
+ {
+ gen1[j] = gen1[j + 1] + reff[i - 1] * gen0[j];
+ gen0[j] = gen1[j + 1] * reff[i - 1] + gen0[j];
+ }
+ reff[i] = -gen1[0] / error;
+ error += gen1[0] * reff[i];
+ }
+
+ // Estimate optimal order using reflection coefficients
+ uint order_est = 1;
+ for (int i = (int)max_order - 1; i >= 0; i--)
+ {
+ if (Math.Abs(reff[i]) > 0.10)
+ {
+ order_est = (uint)i + 1;
+ break;
+ }
+ }
+
+ // Levinson recursion
+ compute_lpc_coefs(null, order_est, reff, lpc);
+ return order_est;
+ }
+
+ /**
+ * Quantize LPC coefficients
+ */
+ public static unsafe void
+ quantize_lpc_coefs(double* lpc_in, int order, uint precision, int* lpc_out,
+ out int shift)
+ {
+ int i;
+ double d, cmax, error;
+ int qmax;
+ int sh, q;
+
+ // define maximum levels
+ qmax = (1 << ((int)precision - 1)) - 1;
+
+ // find maximum coefficient value
+ cmax = 0.0;
+ for (i = 0; i < order; i++)
+ {
+ d = Math.Abs(lpc_in[i]);
+ if (d > cmax)
+ cmax = d;
+ }
+ // if maximum value quantizes to zero, return all zeros
+ if (cmax * (1 << 15) < 1.0)
+ {
+ shift = 0;
+ for (i = 0; i < order; i++)
+ lpc_out[i] = 0;
+ return;
+ }
+
+ // calculate level shift which scales max coeff to available bits
+ sh = 15;
+ while ((cmax * (1 << sh) > qmax) && (sh > 0))
+ {
+ sh--;
+ }
+
+ // since negative shift values are unsupported in decoder, scale down
+ // coefficients instead
+ if (sh == 0 && cmax > qmax)
+ {
+ double scale = ((double)qmax) / cmax;
+ for (i = 0; i < order; i++)
+ {
+ lpc_in[i] *= scale;
+ }
+ }
+
+ // output quantized coefficients and level shift
+ error = 0;
+ for (i = 0; i < order; i++)
+ {
+ error += lpc_in[i] * (1 << sh);
+ q = (int)(error + 0.5);
+ if (q <= -qmax) q = -qmax + 1;
+ if (q > qmax) q = qmax;
+ error -= q;
+ lpc_out[i] = q;
+ }
+ shift = sh;
+ }
+
+ /**
+ * Calculate LPC coefficients for multiple orders
+ */
+ public static unsafe uint
+ calc_coefs(/*const*/ int* samples, uint blocksize, uint max_order, OrderMethod omethod, double* lpcs, double* window)
+ {
+ double* autoc = stackalloc double[MAX_LPC_ORDER + 1];
+
+ compute_autocorr(samples, blocksize, max_order + 1, autoc, window);
+
+ uint opt_order = max_order;
+ if (omethod == OrderMethod.Estimate || omethod == OrderMethod.EstSearch)
+ opt_order = compute_lpc_coefs_est(autoc, max_order, lpcs);
+ else
+ compute_lpc_coefs(autoc, max_order, null, lpcs);
+
+ return opt_order;
+ }
+
+ public static unsafe void
+ encode_residual(int* res, int* smp, int n, int order,
+ int* coefs, int shift)
+ {
+ for (int i = 0; i < order; i++)
+ res[i] = smp[i];
+
+ int* s = smp;
+ int* r = res + order;
+ int c0 = coefs[0];
+ int c1 = coefs[1];
+ switch (order)
+ {
+ case 1:
+ for (int i = n - order; i > 0; i--)
+ {
+ int pred = c0 * *(s++);
+ *(r++) = *s - (pred >> shift);
+ }
+ break;
+ case 2:
+ for (int i = n - order; i > 0; i--)
+ {
+ int pred = c1 * *(s++);
+ pred += c0 * *(s++);
+ *(r++) = *(s--) - (pred >> shift);
+ }
+ break;
+ case 3:
+ for (int i = n - order; i > 0; i--)
+ {
+ int pred = coefs[2] * *(s++);
+ pred += c1 * *(s++);
+ pred += c0 * *(s++);
+ *(r++) = *s - (pred >> shift);
+ s -= 2;
+ }
+ break;
+ case 4:
+ for (int i = n - order; i > 0; i--)
+ {
+ int pred = coefs[3] * *(s++);
+ pred += coefs[2] * *(s++);
+ pred += c1 * *(s++);
+ pred += c0 * *(s++);
+ *(r++) = *s - (pred >> shift);
+ s -= 3;
+ }
+ break;
+ case 5:
+ for (int i = n - order; i > 0; i--)
+ {
+ int pred = coefs[4] * *(s++);
+ pred += coefs[3] * *(s++);
+ pred += coefs[2] * *(s++);
+ pred += c1 * *(s++);
+ pred += c0 * *(s++);
+ *(r++) = *s - (pred >> shift);
+ s -= 4;
+ }
+ break;
+ case 6:
+ for (int i = n - order; i > 0; i--)
+ {
+ int pred = coefs[5] * *(s++);
+ pred += coefs[4] * *(s++);
+ pred += coefs[3] * *(s++);
+ pred += coefs[2] * *(s++);
+ pred += c1 * *(s++);
+ pred += c0 * *(s++);
+ *(r++) = *s - (pred >> shift);
+ s -= 5;
+ }
+ break;
+ case 7:
+ for (int i = n - order; i > 0; i--)
+ {
+ int pred = coefs[6] * *(s++);
+ pred += coefs[5] * *(s++);
+ pred += coefs[4] * *(s++);
+ pred += coefs[3] * *(s++);
+ pred += coefs[2] * *(s++);
+ pred += c1 * *(s++);
+ pred += c0 * *(s++);
+ *(r++) = *s - (pred >> shift);
+ s -= 6;
+ }
+ break;
+ case 8:
+ for (int i = n - order; i > 0; i--)
+ {
+ int pred = coefs[7] * *(s++);
+ pred += coefs[6] * *(s++);
+ pred += coefs[5] * *(s++);
+ pred += coefs[4] * *(s++);
+ pred += coefs[3] * *(s++);
+ pred += coefs[2] * *(s++);
+ pred += c1 * *(s++);
+ pred += c0 * *(s++);
+ *(r++) = *s - (pred >> shift);
+ s -= 7;
+ }
+ break;
+ default:
+ for (int i = order; i < n; i++)
+ {
+ s = smp + i - order;
+ int pred = 0;
+ int* co = coefs + order - 1;
+ int* c7 = coefs + 7;
+ while (co > c7)
+ pred += *(co--) * *(s++);
+ pred += coefs[7] * *(s++);
+ pred += coefs[6] * *(s++);
+ pred += coefs[5] * *(s++);
+ pred += coefs[4] * *(s++);
+ pred += coefs[3] * *(s++);
+ pred += coefs[2] * *(s++);
+ pred += c1 * *(s++);
+ pred += c0 * *(s++);
+ *(r++) = *s - (pred >> shift);
+ }
+ break;
+ }
+ }
+
+ public static unsafe void
+ decode_residual(int* res, int* smp, int n, int order,
+ int* coefs, int shift)
+ {
+ for (int i = 0; i < order; i++)
+ smp[i] = res[i];
+
+ int* s = smp;
+ int* r = res + order;
+ int c0 = coefs[0];
+ int c1 = coefs[1];
+ switch (order)
+ {
+ case 1:
+ for (int i = n - order; i > 0; i--)
+ {
+ int pred = c0 * *(s++);
+ *s = *(r++) + (pred >> shift);
+ }
+ break;
+ case 2:
+ for (int i = n - order; i > 0; i--)
+ {
+ int pred = c1 * *(s++);
+ pred += c0 * *(s++);
+ *(s--) = *(r++) + (pred >> shift);
+ }
+ break;
+ case 3:
+ for (int i = n - order; i > 0; i--)
+ {
+ int pred = coefs[2] * *(s++);
+ pred += c1 * *(s++);
+ pred += c0 * *(s++);
+ *s = *(r++) + (pred >> shift);
+ s -= 2;
+ }
+ break;
+ case 4:
+ for (int i = n - order; i > 0; i--)
+ {
+ int pred = coefs[3] * *(s++);
+ pred += coefs[2] * *(s++);
+ pred += c1 * *(s++);
+ pred += c0 * *(s++);
+ *s = *(r++) + (pred >> shift);
+ s -= 3;
+ }
+ break;
+ case 5:
+ for (int i = n - order; i > 0; i--)
+ {
+ int pred = coefs[4] * *(s++);
+ pred += coefs[3] * *(s++);
+ pred += coefs[2] * *(s++);
+ pred += c1 * *(s++);
+ pred += c0 * *(s++);
+ *s = *(r++) + (pred >> shift);
+ s -= 4;
+ }
+ break;
+ case 6:
+ for (int i = n - order; i > 0; i--)
+ {
+ int pred = coefs[5] * *(s++);
+ pred += coefs[4] * *(s++);
+ pred += coefs[3] * *(s++);
+ pred += coefs[2] * *(s++);
+ pred += c1 * *(s++);
+ pred += c0 * *(s++);
+ *s = *(r++) + (pred >> shift);
+ s -= 5;
+ }
+ break;
+ case 7:
+ for (int i = n - order; i > 0; i--)
+ {
+ int pred = coefs[6] * *(s++);
+ pred += coefs[5] * *(s++);
+ pred += coefs[4] * *(s++);
+ pred += coefs[3] * *(s++);
+ pred += coefs[2] * *(s++);
+ pred += c1 * *(s++);
+ pred += c0 * *(s++);
+ *s = *(r++) + (pred >> shift);
+ s -= 6;
+ }
+ break;
+ case 8:
+ for (int i = n - order; i > 0; i--)
+ {
+ int pred = coefs[7] * *(s++);
+ pred += coefs[6] * *(s++);
+ pred += coefs[5] * *(s++);
+ pred += coefs[4] * *(s++);
+ pred += coefs[3] * *(s++);
+ pred += coefs[2] * *(s++);
+ pred += c1 * *(s++);
+ pred += c0 * *(s++);
+ *s = *(r++) + (pred >> shift);
+ s -= 7;
+ }
+ break;
+ default:
+ for (int i = order; i < n; i++)
+ {
+ s = smp + i - order;
+ int pred = 0;
+ int* co = coefs + order - 1;
+ int* c7 = coefs + 7;
+ while (co > c7)
+ pred += *(co--) * *(s++);
+ pred += coefs[7] * *(s++);
+ pred += coefs[6] * *(s++);
+ pred += coefs[5] * *(s++);
+ pred += coefs[4] * *(s++);
+ pred += coefs[3] * *(s++);
+ pred += coefs[2] * *(s++);
+ pred += c1 * *(s++);
+ pred += c0 * *(s++);
+ *s = *(r++) + (pred >> shift);
+ }
+ break;
+ }
+ }
+ public static unsafe void
+ decode_residual_long(int* res, int* smp, int n, int order,
+ int* coefs, int shift)
+ {
+ for (int i = 0; i < order; i++)
+ smp[i] = res[i];
+
+ int* s = smp;
+ int* r = res + order;
+ int c0 = coefs[0];
+ int c1 = coefs[1];
+ switch (order)
+ {
+ case 1:
+ for (int i = n - order; i > 0; i--)
+ {
+ long pred = c0 * (long)*(s++);
+ *s = *(r++) + (int)(pred >> shift);
+ }
+ break;
+ case 2:
+ for (int i = n - order; i > 0; i--)
+ {
+ long pred = c1 * (long)*(s++);
+ pred += c0 * (long)*(s++);
+ *(s--) = *(r++) + (int)(pred >> shift);
+ }
+ break;
+ case 3:
+ for (int i = n - order; i > 0; i--)
+ {
+ long pred = coefs[2] * (long)*(s++);
+ pred += c1 * (long)*(s++);
+ pred += c0 * (long)*(s++);
+ *s = *(r++) + (int)(pred >> shift);
+ s -= 2;
+ }
+ break;
+ case 4:
+ for (int i = n - order; i > 0; i--)
+ {
+ long pred = coefs[3] * (long)*(s++);
+ pred += coefs[2] * (long)*(s++);
+ pred += c1 * (long)*(s++);
+ pred += c0 * (long)*(s++);
+ *s = *(r++) + (int)(pred >> shift);
+ s -= 3;
+ }
+ break;
+ case 5:
+ for (int i = n - order; i > 0; i--)
+ {
+ long pred = coefs[4] * (long)*(s++);
+ pred += coefs[3] * (long)*(s++);
+ pred += coefs[2] * (long)*(s++);
+ pred += c1 * (long)*(s++);
+ pred += c0 * (long)*(s++);
+ *s = *(r++) + (int)(pred >> shift);
+ s -= 4;
+ }
+ break;
+ case 6:
+ for (int i = n - order; i > 0; i--)
+ {
+ long pred = coefs[5] * (long)*(s++);
+ pred += coefs[4] * (long)*(s++);
+ pred += coefs[3] * (long)*(s++);
+ pred += coefs[2] * (long)*(s++);
+ pred += c1 * (long)*(s++);
+ pred += c0 * (long)*(s++);
+ *s = *(r++) + (int)(pred >> shift);
+ s -= 5;
+ }
+ break;
+ case 7:
+ for (int i = n - order; i > 0; i--)
+ {
+ long pred = coefs[6] * (long)*(s++);
+ pred += coefs[5] * (long)*(s++);
+ pred += coefs[4] * (long)*(s++);
+ pred += coefs[3] * (long)*(s++);
+ pred += coefs[2] * (long)*(s++);
+ pred += c1 * (long)*(s++);
+ pred += c0 * (long)*(s++);
+ *s = *(r++) + (int)(pred >> shift);
+ s -= 6;
+ }
+ break;
+ case 8:
+ for (int i = n - order; i > 0; i--)
+ {
+ long pred = coefs[7] * (long)*(s++);
+ pred += coefs[6] * (long)*(s++);
+ pred += coefs[5] * (long)*(s++);
+ pred += coefs[4] * (long)*(s++);
+ pred += coefs[3] * (long)*(s++);
+ pred += coefs[2] * (long)*(s++);
+ pred += c1 * (long)*(s++);
+ pred += c0 * (long)*(s++);
+ *s = *(r++) + (int)(pred >> shift);
+ s -= 7;
+ }
+ break;
+ default:
+ for (int i = order; i < n; i++)
+ {
+ s = smp + i - order;
+ long pred = 0;
+ int* co = coefs + order - 1;
+ int* c7 = coefs + 7;
+ while (co > c7)
+ pred += *(co--) * (long)*(s++);
+ pred += coefs[7] * (long)*(s++);
+ pred += coefs[6] * (long)*(s++);
+ pred += coefs[5] * (long)*(s++);
+ pred += coefs[4] * (long)*(s++);
+ pred += coefs[3] * (long)*(s++);
+ pred += coefs[2] * (long)*(s++);
+ pred += c1 * (long)*(s++);
+ pred += c0 * (long)*(s++);
+ *s = *(r++) + (int)(pred >> shift);
+ }
+ break;
+ }
+ }
+ }
+}