diff --git a/CUETools.Codecs.FLAKE/BitWriter.cs b/CUETools.Codecs.FLAKE/BitWriter.cs index 0f727b7..27f8a41 100644 --- a/CUETools.Codecs.FLAKE/BitWriter.cs +++ b/CUETools.Codecs.FLAKE/BitWriter.cs @@ -38,6 +38,17 @@ namespace CUETools.Codecs.FLAKE writebits(bits, (uint)val); } + public void writebits64(int bits, ulong val) + { + if (bits > 32) + { + writebits(bits - 32, (uint)(val >> 32)); + val &= 0xffffffffL; + bits = 32; + } + writebits(bits, (uint)val); + } + public void writebits(int bits, uint val) { //assert(bits == 32 || val < (1U << bits)); @@ -100,12 +111,26 @@ namespace CUETools.Codecs.FLAKE } } + public void write_unary_signed(int val) + { + // convert signed to unsigned + int v = -2 * val - 1; + v ^= (v >> 31); + + // write quotient in unary + int q = v + 1; + while (q > 31) + { + writebits(31, 0); + q -= 31; + } + writebits(q, 1); + } + 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); diff --git a/CUETools.Codecs.FLAKE/Flake.cs b/CUETools.Codecs.FLAKE/Flake.cs index 4de4f95..2237636 100644 --- a/CUETools.Codecs.FLAKE/Flake.cs +++ b/CUETools.Codecs.FLAKE/Flake.cs @@ -11,6 +11,10 @@ namespace CUETools.Codecs.FLAKE public const int MAX_PARTITION_ORDER = 8; public const int MAX_PARTITIONS = 1 << MAX_PARTITION_ORDER; + public const int FLAC__STREAM_METADATA_SEEKPOINT_SAMPLE_NUMBER_LEN = 64; /* bits */ + public const int FLAC__STREAM_METADATA_SEEKPOINT_STREAM_OFFSET_LEN = 64; /* bits */ + public const int FLAC__STREAM_METADATA_SEEKPOINT_FRAME_SAMPLES_LEN = 16; /* bits */ + public static readonly int[] flac_samplerates = new int[16] { 0, 0, 0, 0, 8000, 16000, 22050, 24000, 32000, 44100, 48000, 96000, @@ -39,59 +43,22 @@ namespace CUETools.Codecs.FLAKE 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); + return (PredictionType)(Enum.Parse(typeof(PredictionType), name, true)); } 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); + return (StereoMethod)(Enum.Parse(typeof(StereoMethod), name, true)); } 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); + return (OrderMethod)(Enum.Parse(typeof(OrderMethod), name, true)); } 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; + return (WindowFunction)(Enum.Parse(typeof(WindowFunction), name, true)); } unsafe public static bool memcmp(int* res, int* smp, int n) @@ -124,6 +91,14 @@ namespace CUETools.Codecs.FLAKE *(res++) = *(src2++); } } + unsafe public static void deinterlace(int* dst1, int* dst2, int* src, int n) + { + for (int i = n; i > 0; i--) + { + *(dst1++) = *(src++); + *(dst2++) = *(src++); + } + } } unsafe struct RiceContext @@ -137,20 +112,28 @@ namespace CUETools.Codecs.FLAKE { 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 cbits; + public int shift; + public fixed int coefs[lpc.MAX_LPC_ORDER]; public int window; }; + unsafe struct FlacSubframeInfo + { + public FlacSubframe best; + public uint obits; + public uint wbits; + public int* samples; + public fixed uint done_lpcs[lpc.MAX_LPC_WINDOWS * 2]; + public uint done_fixed; + public fixed double lpcs_reff[lpc.MAX_LPC_ORDER * lpc.MAX_LPC_WINDOWS]; + public fixed int lpcs_order[lpc.MAX_LPC_WINDOWS]; + }; + unsafe struct FlacFrame { public int blocksize; @@ -158,7 +141,7 @@ namespace CUETools.Codecs.FLAKE public ChannelMode ch_mode; public int ch_order0, ch_order1; public byte crc8; - public FlacSubframe* subframes; + public FlacSubframeInfo* subframes; public uint frame_count; public FlacSubframe current; } @@ -170,6 +153,7 @@ namespace CUETools.Codecs.FLAKE LogFast = 2, LogSearch = 3, EstSearch = 4, + Estimate8 = 6, Search = 5 } @@ -204,11 +188,8 @@ namespace CUETools.Codecs.FLAKE { Independent = 0, Estimate = 1, - Estimate2 = 2, - Estimate3 = 3, - Estimate4 = 4, - Estimate5 = 5, - Search = 9 + Evaluate = 2, + Search = 3 } public enum SubframeType @@ -233,7 +214,8 @@ namespace CUETools.Codecs.FLAKE Welch = 1, Tukey = 2, Hann = 4, - Flattop = 8 + Flattop = 8, + TukeyFlattop = 10 } public struct SeekPoint diff --git a/CUETools.Codecs.FLAKE/FlakeReader.cs b/CUETools.Codecs.FLAKE/FlakeReader.cs index 6269a28..d39065e 100644 --- a/CUETools.Codecs.FLAKE/FlakeReader.cs +++ b/CUETools.Codecs.FLAKE/FlakeReader.cs @@ -118,13 +118,28 @@ namespace CUETools.Codecs.FLAKE { if (value > _sampleCount) throw new Exception("seeking past end of stream"); - if (value < Position) - throw new Exception("backwards seeking not yet supported"); - if (seek_table != null) + if (value < Position || value > _sampleOffset) { - + if (seek_table != null && _IO.CanSeek) + { + int best_st = -1; + for (int st = 0; st < seek_table.Length; st++) + { + if (seek_table[st].number <= value && + (best_st == -1 || seek_table[st].number > seek_table[best_st].number)) + best_st = st; + } + if (best_st != -1) + { + _framesBufferLength = 0; + _samplesInBuffer = 0; + _IO.Position = (long)seek_table[best_st].offset + first_frame_offset; + _sampleOffset = seek_table[best_st].number; + } + } + if (value < Position) + throw new Exception("cannot seek backwards without seek table"); } - while (value > _sampleOffset) { _samplesInBuffer = 0; @@ -313,14 +328,14 @@ namespace CUETools.Codecs.FLAKE 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); + frame->subframes[ch].best.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); + frame->subframes[ch].best.residual[i] = bitreader.readbits_signed(obits); } unsafe void decode_residual(BitReader bitreader, FlacFrame* frame, int ch) @@ -330,25 +345,25 @@ namespace CUETools.Codecs.FLAKE if (coding_method != 0 && coding_method != 1) // 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) + frame->subframes[ch].best.rc.porder = (int)bitreader.readbits(4); + if (frame->subframes[ch].best.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; + int psize = frame->blocksize >> frame->subframes[ch].best.rc.porder; + int res_cnt = psize - frame->subframes[ch].best.order; int rice_len = 4 + (int)coding_method; // 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++) + int j = frame->subframes[ch].best.order; + int* r = frame->subframes[ch].best.residual + j; + for (int p = 0; p < (1 << frame->subframes[ch].best.rc.porder); p++) { if (p == 1) res_cnt = psize; int n = Math.Min(res_cnt, frame->blocksize - j); - int k = frame->subframes[ch].rc.rparams[p] = (int)bitreader.readbits(rice_len); + int k = frame->subframes[ch].best.rc.rparams[p] = (int)bitreader.readbits(rice_len); if (k == (1 << rice_len) - 1) { - k = frame->subframes[ch].rc.esc_bps[p] = (int)bitreader.readbits(5); + k = frame->subframes[ch].best.rc.esc_bps[p] = (int)bitreader.readbits(5); for (int i = n; i > 0; i--) *(r++) = bitreader.readbits_signed((int)k); } @@ -365,8 +380,8 @@ namespace CUETools.Codecs.FLAKE { // 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); + for (int i = 0; i < frame->subframes[ch].best.order; i++) + frame->subframes[ch].best.residual[i] = bitreader.readbits_signed(obits); // residual decode_residual(bitreader, frame, ch); @@ -376,14 +391,14 @@ namespace CUETools.Codecs.FLAKE { // 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); + for (int i = 0; i < frame->subframes[ch].best.order; i++) + frame->subframes[ch].best.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); + frame->subframes[ch].best.cbits = (int)bitreader.readbits(4) + 1; // lpc_precision + frame->subframes[ch].best.shift = bitreader.readbits_signed(5); + for (int i = 0; i < frame->subframes[ch].best.order; i++) + frame->subframes[ch].best.coefs[i] = bitreader.readbits_signed(frame->subframes[ch].best.cbits); // residual decode_residual(bitreader, frame, ch); @@ -411,25 +426,25 @@ namespace CUETools.Codecs.FLAKE case ChannelMode.RightSide: frame->subframes[ch].obits += 1 - (uint)ch; break; } - frame->subframes[ch].type = (SubframeType)type_code; - frame->subframes[ch].order = 0; + frame->subframes[ch].best.type = (SubframeType)type_code; + frame->subframes[ch].best.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; + frame->subframes[ch].best.order = (type_code - (int)SubframeType.LPC) + 1; + frame->subframes[ch].best.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].best.order = (type_code - (int)SubframeType.Fixed); + frame->subframes[ch].best.type = SubframeType.Fixed; } - frame->subframes[ch].residual = r + ch * Flake.MAX_BLOCKSIZE; + frame->subframes[ch].best.residual = r + ch * Flake.MAX_BLOCKSIZE; frame->subframes[ch].samples = s + ch * Flake.MAX_BLOCKSIZE; // subframe - switch (frame->subframes[ch].type) + switch (frame->subframes[ch].best.type) { case SubframeType.Constant: decode_subframe_constant(bitreader, frame, ch); @@ -451,14 +466,14 @@ namespace CUETools.Codecs.FLAKE unsafe void restore_samples_fixed(FlacFrame* frame, int ch) { - FlacSubframe* sub = frame->subframes + ch; + FlacSubframeInfo* 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; + Flake.memcpy(sub->samples, sub->best.residual, sub->best.order); + int* data = sub->samples + sub->best.order; + int* residual = sub->best.residual + sub->best.order; + int data_len = frame->blocksize - sub->best.order; int s0, s1, s2; - switch (sub->order) + switch (sub->best.order) { case 0: Flake.memcpy(data, residual, data_len); @@ -497,27 +512,27 @@ namespace CUETools.Codecs.FLAKE unsafe void restore_samples_lpc(FlacFrame* frame, int ch) { - FlacSubframe* sub = frame->subframes + ch; + FlacSubframeInfo* sub = frame->subframes + ch; ulong csum = 0; - for (int i = sub->order; i > 0; i--) - csum += (ulong)Math.Abs(sub->coefs[i - 1]); + for (int i = sub->best.order; i > 0; i--) + csum += (ulong)Math.Abs(sub->best.coefs[i - 1]); if ((csum << (int)sub->obits) >= 1UL << 32) - lpc.decode_residual_long(sub->residual, sub->samples, frame->blocksize, sub->order, sub->coefs, sub->shift); + lpc.decode_residual_long(sub->best.residual, sub->samples, frame->blocksize, sub->best.order, sub->best.coefs, sub->best.shift); else - lpc.decode_residual(sub->residual, sub->samples, frame->blocksize, sub->order, sub->coefs, sub->shift); + lpc.decode_residual(sub->best.residual, sub->samples, frame->blocksize, sub->best.order, sub->best.coefs, sub->best.shift); } unsafe void restore_samples(FlacFrame* frame) { for (int ch = 0; ch < channels; ch++) { - switch (frame->subframes[ch].type) + switch (frame->subframes[ch].best.type) { case SubframeType.Constant: - Flake.memset(frame->subframes[ch].samples, frame->subframes[ch].residual[0], frame->blocksize); + Flake.memset(frame->subframes[ch].samples, frame->subframes[ch].best.residual[0], frame->blocksize); break; case SubframeType.Verbatim: - Flake.memcpy(frame->subframes[ch].samples, frame->subframes[ch].residual, frame->blocksize); + Flake.memcpy(frame->subframes[ch].samples, frame->subframes[ch].best.residual, frame->blocksize); break; case SubframeType.Fixed: restore_samples_fixed(frame, ch); @@ -574,12 +589,12 @@ namespace CUETools.Codecs.FLAKE { BitReader bitreader = new BitReader(buf, pos, len); FlacFrame frame; - FlacSubframe* subframes = stackalloc FlacSubframe[channels]; + FlacSubframeInfo* subframes = stackalloc FlacSubframeInfo[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); + ushort crc = crc16.ComputeChecksum(bitreader.Buffer + pos, bitreader.Position - pos); if (crc != bitreader.readbits(16)) throw new Exception("frame crc mismatch"); restore_samples(&frame); @@ -691,17 +706,13 @@ namespace CUETools.Codecs.FLAKE } else if (type == MetadataType.FLAC__METADATA_TYPE_SEEKTABLE) { - const int FLAC__STREAM_METADATA_SEEKPOINT_SAMPLE_NUMBER_LEN = 64; /* bits */ - const int FLAC__STREAM_METADATA_SEEKPOINT_STREAM_OFFSET_LEN = 64; /* bits */ - const int FLAC__STREAM_METADATA_SEEKPOINT_FRAME_SAMPLES_LEN = 16; /* bits */ - int num_entries = len / 18; seek_table = new SeekPoint[num_entries]; for (int e = 0; e < num_entries; e++) { - seek_table[e].number = bitreader.readbits64(FLAC__STREAM_METADATA_SEEKPOINT_SAMPLE_NUMBER_LEN); - seek_table[e].offset = bitreader.readbits64(FLAC__STREAM_METADATA_SEEKPOINT_STREAM_OFFSET_LEN); - seek_table[e].framesize = bitreader.readbits24(FLAC__STREAM_METADATA_SEEKPOINT_FRAME_SAMPLES_LEN); + seek_table[e].number = bitreader.readbits64(Flake.FLAC__STREAM_METADATA_SEEKPOINT_SAMPLE_NUMBER_LEN); + seek_table[e].offset = bitreader.readbits64(Flake.FLAC__STREAM_METADATA_SEEKPOINT_STREAM_OFFSET_LEN); + seek_table[e].framesize = bitreader.readbits24(Flake.FLAC__STREAM_METADATA_SEEKPOINT_FRAME_SAMPLES_LEN); } } if (_framesBufferLength < 4 + len) diff --git a/CUETools.Codecs.FLAKE/FlakeWriter.cs b/CUETools.Codecs.FLAKE/FlakeWriter.cs index 70f7818..31a29aa 100644 --- a/CUETools.Codecs.FLAKE/FlakeWriter.cs +++ b/CUETools.Codecs.FLAKE/FlakeWriter.cs @@ -48,6 +48,8 @@ namespace CUETools.Codecs.FLAKE int frame_count = 0; + long first_frame_offset = 0; + TimeSpan _userProcessorTime; // header bytes @@ -71,6 +73,9 @@ namespace CUETools.Codecs.FLAKE FlakeReader verify; + SeekPoint[] seek_table; + int seek_table_offset = -1; + bool inited = false; public FlakeWriter(string path, int bitsPerSample, int channelCount, int sampleRate, Stream IO) @@ -89,20 +94,15 @@ namespace CUETools.Codecs.FLAKE _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 @@ -145,23 +145,37 @@ namespace CUETools.Codecs.FLAKE void DoClose() { - if (samplesInBuffer > 0) + if (inited) { - eparams.block_size = samplesInBuffer; - output_frame(); + if (samplesInBuffer > 0) + { + eparams.block_size = samplesInBuffer; + output_frame(); + } + + if (_IO.CanSeek) + { + if (md5 != null) + { + md5.TransformFinalBlock(frame_buffer, 0, 0); + _IO.Position = 26; + _IO.Write(md5.Hash, 0, md5.Hash.Length); + } + + if (seek_table != null) + { + _IO.Position = seek_table_offset; + int len = write_seekpoints(header, 0, 0); + _IO.Write(header, 4, len - 4); + } + } + _IO.Close(); + inited = false; } 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() @@ -174,7 +188,8 @@ namespace CUETools.Codecs.FLAKE public void Delete() { DoClose(); - File.Delete(_path); + if (_path != "") + File.Delete(_path); } public long Position @@ -193,6 +208,7 @@ namespace CUETools.Codecs.FLAKE public long BlockSize { set { _blocksize = (int)value; } + get { return _blocksize == 0 ? eparams.block_size : _blocksize; } } public OrderMethod OrderMethod @@ -213,31 +229,54 @@ namespace CUETools.Codecs.FLAKE set { eparams.stereo_method = value; } } + public int MaxPrecisionSearch + { + get { return eparams.lpc_precision_search; } + set + { + if (value < 0 || value > 1) + throw new Exception("unsupported MaxPrecisionSearch value"); + eparams.lpc_precision_search = value; + } + } + public WindowFunction WindowFunction { get { return eparams.window_function; } set { eparams.window_function = value; } } + public bool DoMD5 + { + get { return eparams.do_md5; } + set { eparams.do_md5 = value; } + } + + public bool DoVerify + { + get { return eparams.do_verify; } + set { eparams.do_verify = value; } + } + + public bool DoSeekTable + { + get { return eparams.do_seektable; } + set { eparams.do_seektable = value; } + } + public int MinPredictionOrder { get { - return eparams.prediction_type == PredictionType.Fixed ? - eparams.min_fixed_order : eparams.min_prediction_order; + return PredictionType == PredictionType.Fixed ? + MinFixedOrder : MinLPCOrder; } 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; + if (PredictionType == PredictionType.Fixed) + MinFixedOrder = value; + else + MinLPCOrder = value; } } @@ -245,31 +284,92 @@ namespace CUETools.Codecs.FLAKE { get { - return eparams.prediction_type == PredictionType.Fixed ? - eparams.max_fixed_order : eparams.max_prediction_order; + return PredictionType == PredictionType.Fixed ? + MaxFixedOrder : MaxLPCOrder; + } + set + { + if (PredictionType == PredictionType.Fixed) + MaxFixedOrder = value; + else + MaxLPCOrder = value; + } + } + + public int MinLPCOrder + { + get + { + return eparams.min_prediction_order; + } + set + { + if (value < 1 || value > eparams.max_prediction_order) + throw new Exception("invalid MinLPCOrder " + value.ToString()); + eparams.min_prediction_order = value; + } + } + + public int MaxLPCOrder + { + get + { + return 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"); + throw new Exception("invalid MaxLPCOrder " + value.ToString()); eparams.max_prediction_order = value; } } + public int MinFixedOrder + { + get + { + return eparams.min_fixed_order; + } + set + { + if (value < 0 || value > eparams.max_fixed_order) + throw new Exception("invalid MinFixedOrder " + value.ToString()); + eparams.min_fixed_order = value; + } + } + + public int MaxFixedOrder + { + get + { + return eparams.max_fixed_order; + } + set + { + if (value > 4 || value < eparams.min_fixed_order) + throw new Exception("invalid MaxFixedOrder " + value.ToString()); + eparams.max_fixed_order = value; + } + } + + public int MinPartitionOrder + { + get { return eparams.min_partition_order; } + set + { + if (value < 0 || value > eparams.max_partition_order) + throw new Exception("invalid MinPartitionOrder " + value.ToString()); + eparams.min_partition_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"); + throw new Exception("invalid MaxPartitionOrder " + value.ToString()); eparams.max_partition_order = value; } } @@ -351,18 +451,21 @@ namespace CUETools.Codecs.FLAKE } } - /** - * Copy channel-interleaved input samples into separate subframes - */ + /// 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]; - } + fixed (int* fsamples = samplesBuffer, src = &samples[pos, 0]) + { + if (channels == 2) + Flake.deinterlace(fsamples + samplesInBuffer, fsamples + Flake.MAX_BLOCKSIZE + samplesInBuffer, src, block); + else + for (int ch = 0; ch < channels; ch++) + { + int* psamples = fsamples + ch * Flake.MAX_BLOCKSIZE + samplesInBuffer; + for (int i = 0; i < block; i++) + psamples[i] = src[i * channels + ch]; + } + } samplesInBuffer += block; } @@ -407,9 +510,9 @@ namespace CUETools.Codecs.FLAKE return k_opt; } - unsafe uint calc_decorr_score(FlacFrame* frame, int ch, FlacSubframe* sub) + unsafe uint calc_decorr_score(FlacFrame* frame, int ch) { - int* s = sub->samples; + int* s = frame->subframes[ch].samples; int n = frame->blocksize; ulong sum = 0; for (int i = 2; i < n; i++) @@ -425,14 +528,16 @@ namespace CUETools.Codecs.FLAKE 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].best.residual = r; + frame->subframes[ch].best.type = SubframeType.Verbatim; + frame->subframes[ch].best.size = UINT32_MAX; + for (int iWindow = 0; iWindow < 2 * lpc.MAX_LPC_WINDOWS; iWindow++) frame->subframes[ch].done_lpcs[iWindow] = 0; frame->subframes[ch].done_fixed = 0; + for (int iWindow = 0; iWindow < lpc.MAX_LPC_WINDOWS; iWindow++) + frame->subframes[ch].lpcs_order[iWindow] = 0; } unsafe static void channel_decorrelation(int* leftS, int* rightS, int *leftM, int *rightM, int blocksize) @@ -613,37 +718,45 @@ namespace CUETools.Codecs.FLAKE unsafe void choose_best_subframe(FlacFrame* frame, int ch) { - if (frame->current.size < frame->subframes[ch].size) + if (frame->current.size < frame->subframes[ch].best.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; + FlacSubframe tmp = frame->subframes[ch].best; + frame->subframes[ch].best = frame->current; frame->current = tmp; } } - unsafe void encode_residual_lpc_sub(FlacFrame* frame, double* lpcs, int iWindow, int order, int ch) + 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; + for (uint i_precision = 0; i_precision <= eparams.lpc_precision_search && lpc_precision + i_precision < 16; i_precision++) + // check if we already calculated with this order, window and precision + if ((frame->subframes[ch].done_lpcs[iWindow + i_precision * lpc.MAX_LPC_WINDOWS] & (1U << (order - 1))) == 0) + { + frame->subframes[ch].done_lpcs[iWindow + i_precision * lpc.MAX_LPC_WINDOWS] |= (1U << (order - 1)); - frame->subframes[ch].done_lpcs[iWindow] |= (1U << (order - 1)); + uint cbits = lpc_precision + i_precision; - frame->current.type = SubframeType.LPC; - frame->current.order = order; - frame->current.window = iWindow; + 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.quantize_lpc_coefs(lpcs + (frame->current.order - 1) * lpc.MAX_LPC_ORDER, + frame->current.order, cbits, 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); + ulong csum = 0; + for (int i = frame->current.order; i > 0; i--) + csum += (ulong)Math.Abs(frame->current.coefs[i - 1]); - 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); + if ((csum << (int)frame->subframes[ch].obits) >= 1UL << 32) + lpc.encode_residual_long(frame->current.residual, frame->subframes[ch].samples, frame->blocksize, frame->current.order, frame->current.coefs, frame->current.shift); + else + lpc.encode_residual(frame->current.residual, frame->subframes[ch].samples, frame->blocksize, frame->current.order, frame->current.coefs, frame->current.shift); - choose_best_subframe(frame, ch); + 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->subframes[ch].obits, cbits); + + choose_best_subframe(frame, ch); + } } unsafe void encode_residual_fixed_sub(FlacFrame* frame, int order, int ch) @@ -654,10 +767,10 @@ namespace CUETools.Codecs.FLAKE frame->current.order = order; frame->current.type = SubframeType.Fixed; - encode_residual_fixed(frame->current.residual, frame->current.samples, frame->blocksize, frame->current.order); + encode_residual_fixed(frame->current.residual, frame->subframes[ch].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->current.residual, frame->blocksize, frame->current.order, frame->subframes[ch].obits); frame->subframes[ch].done_fixed |= (1U << order); @@ -666,15 +779,9 @@ namespace CUETools.Codecs.FLAKE 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; + int* smp = frame->subframes[ch].samples; + int i, 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++) { @@ -682,15 +789,15 @@ namespace CUETools.Codecs.FLAKE } if (i == n) { - sub->type = SubframeType.Constant; - sub->residual[0] = smp[0]; - sub->size = sub->obits; + frame->subframes[ch].best.type = SubframeType.Constant; + frame->subframes[ch].best.residual[0] = smp[0]; + frame->subframes[ch].best.size = frame->subframes[ch].obits; return; } // VERBATIM frame->current.type = SubframeType.Verbatim; - frame->current.size = frame->current.obits * (uint)frame->blocksize; + frame->current.size = frame->subframes[ch].obits * (uint)frame->blocksize; choose_best_subframe(frame, ch); if (n < 5 || predict == PredictionType.None) @@ -699,7 +806,7 @@ namespace CUETools.Codecs.FLAKE // FIXED if (predict == PredictionType.Fixed || predict == PredictionType.Search || - (predict == PredictionType.Estimated && sub->type == SubframeType.Fixed) || + (predict == PredictionType.Estimated && frame->subframes[ch].best.type == SubframeType.Fixed) || n <= eparams.max_prediction_order) { int max_fixed_order = Math.Min(eparams.max_fixed_order, 4); @@ -713,20 +820,51 @@ namespace CUETools.Codecs.FLAKE if (n > eparams.max_prediction_order && (predict == PredictionType.Levinson || predict == PredictionType.Search || - (predict == PredictionType.Estimated && sub->type == SubframeType.LPC))) + (predict == PredictionType.Estimated && frame->subframes[ch].best.type == SubframeType.LPC))) { - double* lpcs = stackalloc double[lpc.MAX_LPC_ORDER * lpc.MAX_LPC_ORDER]; + //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) + if (predict == PredictionType.Estimated && frame->subframes[ch].best.window != iWindow) continue; - - int est_order = (int)lpc.calc_coefs(smp, (uint)n, (uint)max_order, omethod, lpcs, - window + iWindow * _windowsize); + + double* reff = frame->subframes[ch].lpcs_reff + iWindow * lpc.MAX_LPC_ORDER; + if (frame->subframes[ch].lpcs_order[iWindow] != max_order) + { + double* autoc = stackalloc double[lpc.MAX_LPC_ORDER + 1]; + lpc.compute_autocorr(smp, (uint)n, (uint)max_order + 1, autoc, + window + iWindow * _windowsize); + lpc.compute_schur_reflection(autoc, (uint)max_order, reff); + frame->subframes[ch].lpcs_order[iWindow] = max_order; + } + + int est_order = 1; + int est_order2 = 1; + if (omethod == OrderMethod.Estimate || omethod == OrderMethod.Estimate8 || omethod == OrderMethod.EstSearch) + { + // Estimate optimal order using reflection coefficients + for (int r = max_order - 1; r >= 0; r--) + if (Math.Abs(reff[r]) > 0.1) + { + est_order = r + 1; + break; + } + for (int r = Math.Min(max_order, 2 * eparams.max_fixed_order) - 1; r >= 0; r--) + if (Math.Abs(reff[r]) > 0.1) + { + est_order2 = r + 1; + break; + } + } + else + est_order = max_order; + + double* lpcs = stackalloc double[lpc.MAX_LPC_ORDER * lpc.MAX_LPC_ORDER]; + lpc.compute_lpc_coefs(null, (uint)est_order, reff, lpcs); switch (omethod) { @@ -738,21 +876,31 @@ namespace CUETools.Codecs.FLAKE // 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); + case OrderMethod.Estimate8: + // estimated order + encode_residual_lpc_sub(frame, lpcs, iWindow, est_order2, ch); break; + //case OrderMethod.EstSearch: + // brute-force search starting from estimate + //encode_residual_lpc_sub(frame, lpcs, iWindow, est_order, ch); + //encode_residual_lpc_sub(frame, lpcs, iWindow, est_order2, ch); + //break; case OrderMethod.EstSearch: // brute-force search starting from estimate - for (i = est_order; i > 0; i--) + for (i = est_order; i >= min_order; i--) + if (i == est_order || Math.Abs(reff[i - 1]) > 0.10) + encode_residual_lpc_sub(frame, lpcs, iWindow, i, ch); + break; + case OrderMethod.Search: + // brute-force optimal order search + for (i = max_order; i >= min_order; 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) + for (i = lpc.MAX_LPC_ORDER; i >= min_order; i >>= 1) if (i < max_order) encode_residual_lpc_sub(frame, lpcs, iWindow, i, ch); break; @@ -760,16 +908,16 @@ namespace CUETools.Codecs.FLAKE // 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) + for (i = lpc.MAX_LPC_ORDER; i >= min_order; 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) + if (frame->subframes[ch].best.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; + int last = frame->subframes[ch].best.order; if (step <= (last + 1) / 2) for (i = last - step; i <= last + step; i += step) if (i >= min_order && i <= max_order) @@ -821,40 +969,42 @@ namespace CUETools.Codecs.FLAKE bitwriter.writebits(8, crc); } - unsafe void output_residual(FlacFrame* frame, BitWriter bitwriter, FlacSubframe* sub) + unsafe void output_residual(FlacFrame* frame, BitWriter bitwriter, FlacSubframeInfo* sub) { // rice-encoded block bitwriter.writebits(2, 0); // partition order - int porder = sub->rc.porder; + int porder = sub->best.rc.porder; int psize = frame->blocksize >> porder; //assert(porder >= 0); bitwriter.writebits(4, porder); - int res_cnt = psize - sub->order; + int res_cnt = psize - sub->best.order; // residual - int j = sub->order; + int j = sub->best.order; for (int p = 0; p < (1 << porder); p++) { - int k = sub->rc.rparams[p]; + int k = sub->best.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]); - } + if (k == 0) + for (int i = 0; i < res_cnt && j < frame->blocksize; i++, j++) + bitwriter.write_unary_signed(sub->best.residual[j]); + else + for (int i = 0; i < res_cnt && j < frame->blocksize; i++, j++) + bitwriter.write_rice_signed(k, sub->best.residual[j]); } } unsafe void - output_subframe_constant(FlacFrame* frame, BitWriter bitwriter, FlacSubframe* sub) + output_subframe_constant(FlacFrame* frame, BitWriter bitwriter, FlacSubframeInfo* sub) { - bitwriter.writebits_signed(sub->obits, sub->residual[0]); + bitwriter.writebits_signed(sub->obits, sub->best.residual[0]); } unsafe void - output_subframe_verbatim(FlacFrame* frame, BitWriter bitwriter, FlacSubframe* sub) + output_subframe_verbatim(FlacFrame* frame, BitWriter bitwriter, FlacSubframeInfo* sub) { int n = frame->blocksize; for (int i = 0; i < n; i++) @@ -863,29 +1013,32 @@ namespace CUETools.Codecs.FLAKE } unsafe void - output_subframe_fixed(FlacFrame* frame, BitWriter bitwriter, FlacSubframe* sub) + output_subframe_fixed(FlacFrame* frame, BitWriter bitwriter, FlacSubframeInfo* sub) { // warm-up samples - for (int i = 0; i < sub->order; i++) - bitwriter.writebits_signed(sub->obits, sub->residual[i]); + for (int i = 0; i < sub->best.order; i++) + bitwriter.writebits_signed(sub->obits, sub->best.residual[i]); // residual output_residual(frame, bitwriter, sub); } unsafe void - output_subframe_lpc(FlacFrame* frame, BitWriter bitwriter, FlacSubframe* sub) + output_subframe_lpc(FlacFrame* frame, BitWriter bitwriter, FlacSubframeInfo* sub) { // warm-up samples - for (int i = 0; i < sub->order; i++) - bitwriter.writebits_signed(sub->obits, sub->residual[i]); + for (int i = 0; i < sub->best.order; i++) + bitwriter.writebits_signed(sub->obits, sub->best.residual[i]); // LPC coefficients - uint cbits = lpc_precision; + int cbits = 1; + for (int i = 0; i < sub->best.order; i++) + while (cbits < 16 && sub->best.coefs[i] != (sub->best.coefs[i] << (32 - cbits)) >> (32 - cbits)) + cbits++; 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]); + bitwriter.writebits_signed(5, sub->best.shift); + for (int i = 0; i < sub->best.order; i++) + bitwriter.writebits_signed(cbits, sub->best.coefs[i]); // residual output_residual(frame, bitwriter, sub); @@ -895,13 +1048,13 @@ namespace CUETools.Codecs.FLAKE { for (int ch = 0; ch < channels; ch++) { - FlacSubframe* sub = frame->subframes + ch; + FlacSubframeInfo* 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; + int type_code = (int) sub->best.type; + if (sub->best.type == SubframeType.Fixed) + type_code |= sub->best.order; + if (sub->best.type == SubframeType.LPC) + type_code |= sub->best.order - 1; bitwriter.writebits(1, 0); bitwriter.writebits(6, type_code); bitwriter.writebits(1, sub->wbits != 0 ? 1 : 0); @@ -909,7 +1062,7 @@ namespace CUETools.Codecs.FLAKE bitwriter.writebits((int)sub->wbits, 1); // subframe - switch (sub->type) + switch (sub->best.type) { case SubframeType.Constant: output_subframe_constant(frame, bitwriter, sub); @@ -986,7 +1139,7 @@ namespace CUETools.Codecs.FLAKE unsafe int encode_frame() { FlacFrame* frame = stackalloc FlacFrame[1]; - FlacSubframe* sf = stackalloc FlacSubframe[channels * 3]; + FlacSubframeInfo* sf = stackalloc FlacSubframeInfo[channels * 3]; fixed (int* s = samplesBuffer, r = residualBuffer) { @@ -1044,65 +1197,31 @@ namespace CUETools.Codecs.FLAKE 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); + frameM->subframes[0].best.size = (uint)frame->blocksize * 32 + calc_decorr_score(frameM, 0); + frameM->subframes[1].best.size = (uint)frame->blocksize * 32 + calc_decorr_score(frameM, 1); + frameS->subframes[0].best.size = (uint)frame->blocksize * 32 + calc_decorr_score(frameS, 0); + frameS->subframes[1].best.size = (uint)frame->blocksize * 32 + calc_decorr_score(frameS, 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) + else if (eparams.stereo_method == StereoMethod.Evaluate) { int max_prediction_order = eparams.max_prediction_order; int max_fixed_order = eparams.max_fixed_order; int min_fixed_order = eparams.min_fixed_order; + int lpc_precision_search = eparams.lpc_precision_search; + OrderMethod omethod = OrderMethod.Estimate8; 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.lpc_precision_search = 0; + if (eparams.max_prediction_order > 12) + eparams.max_prediction_order = 8; + encode_residual(frameM, 0, eparams.prediction_type, omethod); + encode_residual(frameM, 1, eparams.prediction_type, omethod); + encode_residual(frameS, 0, eparams.prediction_type, omethod); + encode_residual(frameS, 1, eparams.prediction_type, omethod); eparams.min_fixed_order = min_fixed_order; eparams.max_fixed_order = max_fixed_order; eparams.max_prediction_order = max_prediction_order; + eparams.lpc_precision_search = lpc_precision_search; } else // StereoMethod.Search { @@ -1113,37 +1232,37 @@ namespace CUETools.Codecs.FLAKE alreadyEncoded = true; } - if (bitsBest > frameM->subframes[0].size + frameM->subframes[1].size) + if (bitsBest > frameM->subframes[0].best.size + frameM->subframes[1].best.size) { - bitsBest = frameM->subframes[0].size + frameM->subframes[1].size; + bitsBest = frameM->subframes[0].best.size + frameM->subframes[1].best.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) + if (bitsBest > frameM->subframes[1].best.size + frameS->subframes[1].best.size) { - bitsBest = frameM->subframes[1].size + frameS->subframes[1].size; + bitsBest = frameM->subframes[1].best.size + frameS->subframes[1].best.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) + if (bitsBest > frameM->subframes[1].best.size + frameS->subframes[0].best.size) { - bitsBest = frameM->subframes[1].size + frameS->subframes[0].size; + bitsBest = frameM->subframes[1].best.size + frameS->subframes[0].best.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) + if (bitsBest > frameS->subframes[0].best.size + frameS->subframes[1].best.size) { - bitsBest = frameS->subframes[0].size + frameS->subframes[1].size; + bitsBest = frameS->subframes[0].best.size + frameS->subframes[1].best.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) + if (eparams.stereo_method == StereoMethod.Evaluate) { encode_residual(frame, 0, PredictionType.Estimated, eparams.order_method); encode_residual(frame, 1, PredictionType.Estimated, eparams.order_method); @@ -1187,6 +1306,23 @@ namespace CUETools.Codecs.FLAKE else fs = encode_frame(); + if (seek_table != null && _IO.CanSeek) + { + for (int sp = 0; sp < seek_table.Length; sp++) + { + if (seek_table[sp].framesize != 0) + continue; + if (seek_table[sp].number > (ulong)_position + (ulong)eparams.block_size) + break; + if (seek_table[sp].number >= (ulong)_position) + { + seek_table[sp].number = (ulong)_position; + seek_table[sp].offset = (ulong)(_IO.Position - first_frame_offset); + seek_table[sp].framesize = (uint)eparams.block_size; + } + } + } + _position += eparams.block_size; _IO.Write(frame_buffer, 0, fs); _totalSize += fs; @@ -1209,10 +1345,12 @@ namespace CUETools.Codecs.FLAKE { if (!inited) { - int header_size = flake_encode_init(); if (_IO == null) _IO = new FileStream(_path, FileMode.Create, FileAccess.Write, FileShare.Read); + int header_size = flake_encode_init(); _IO.Write(header, 0, header_size); + if (_IO.CanSeek) + first_frame_offset = _IO.Position; inited = true; } @@ -1223,8 +1361,11 @@ namespace CUETools.Codecs.FLAKE 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 (md5 != null) + { + 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; @@ -1316,6 +1457,26 @@ namespace CUETools.Codecs.FLAKE return vendor_len + 12; } + int write_seekpoints(byte[] header, int pos, int last) + { + seek_table_offset = pos + 4; + + BitWriter bitwriter = new BitWriter(header, pos, 4 + 18 * seek_table.Length); + + // metadata header + bitwriter.writebits(1, last); + bitwriter.writebits(7, (int)MetadataType.FLAC__METADATA_TYPE_SEEKTABLE); + bitwriter.writebits(24, 18 * seek_table.Length); + for (int i = 0; i < seek_table.Length; i++) + { + bitwriter.writebits64(Flake.FLAC__STREAM_METADATA_SEEKPOINT_SAMPLE_NUMBER_LEN, seek_table[i].number); + bitwriter.writebits64(Flake.FLAC__STREAM_METADATA_SEEKPOINT_STREAM_OFFSET_LEN, seek_table[i].offset); + bitwriter.writebits(Flake.FLAC__STREAM_METADATA_SEEKPOINT_FRAME_SAMPLES_LEN, seek_table[i].framesize); + } + bitwriter.flush(); + return 4 + 18 * seek_table.Length; + } + /** * Write padding metadata block to byte array. */ @@ -1348,6 +1509,10 @@ namespace CUETools.Codecs.FLAKE write_streaminfo(header, header_size, last); header_size += 38; + // seek table + if (_IO.CanSeek && seek_table != null) + header_size += write_seekpoints(header, header_size, last); + // vorbis comment if (eparams.padding_size == 0) last = 1; header_size += write_vorbis_comment(header, header_size, last); @@ -1421,13 +1586,34 @@ namespace CUETools.Codecs.FLAKE else max_frame_size = 16 + ((eparams.block_size * channels * (int)bits_per_sample + 7) >> 3); + if (_IO.CanSeek && eparams.do_seektable) + { + int seek_points_distance = sample_rate * 10; + int num_seek_points = 1 + sample_count / seek_points_distance; // 1 seek point per 10 seconds + if (sample_count % seek_points_distance == 0) + num_seek_points--; + seek_table = new SeekPoint[num_seek_points]; + for (int sp = 0; sp < num_seek_points; sp++) + { + seek_table[sp].framesize = 0; + seek_table[sp].offset = 0; + seek_table[sp].number = (ulong)(sp * seek_points_distance); + } + } + // output header bytes - header = new byte[eparams.padding_size + 1024]; + header = new byte[eparams.padding_size + 1024 + (seek_table == null ? 0 : seek_table.Length * 18)]; header_len = write_headers(); // initialize CRC & MD5 - //crc_init(); - //md5_init(&ctx->md5ctx); + if (_IO.CanSeek && eparams.do_md5) + md5 = new MD5CryptoServiceProvider(); + + if (eparams.do_verify) + { + verify = new FlakeReader(channels, bits_per_sample); + verifyBuffer = new int[Flake.MAX_BLOCKSIZE * channels]; + } frame_buffer = new byte[max_frame_size]; @@ -1536,9 +1722,17 @@ namespace CUETools.Codecs.FLAKE // 1 = variable block size public int variable_block_size; + // whether to try various lpc_precisions + // 0 - use only one precision + // 1 - try two precisions + public int lpc_precision_search; public WindowFunction window_function; + public bool do_md5; + public bool do_verify; + public bool do_seektable; + public int flake_set_defaults(int lvl) { compression = lvl; @@ -1562,6 +1756,10 @@ namespace CUETools.Codecs.FLAKE min_partition_order = 0; max_partition_order = 6; variable_block_size = 0; + lpc_precision_search = 0; + do_md5 = true; + do_verify = true; + do_seektable = true; // differences from level 5 switch (lvl) @@ -1605,28 +1803,28 @@ namespace CUETools.Codecs.FLAKE prediction_type = PredictionType.Levinson; break; case 6: - stereo_method = StereoMethod.Estimate4; + stereo_method = StereoMethod.Evaluate; break; case 7: order_method = OrderMethod.LogSearch; - stereo_method = StereoMethod.Estimate4; + stereo_method = StereoMethod.Evaluate; break; case 8: order_method = OrderMethod.Search; - stereo_method = StereoMethod.Estimate4; + stereo_method = StereoMethod.Evaluate; break; case 9: - stereo_method = StereoMethod.Estimate4; + stereo_method = StereoMethod.Evaluate; max_prediction_order = 32; break; case 10: order_method = OrderMethod.LogFast; - stereo_method = StereoMethod.Estimate5; + stereo_method = StereoMethod.Evaluate; max_prediction_order = 32; break; case 11: order_method = OrderMethod.LogSearch; - stereo_method = StereoMethod.Estimate5; + stereo_method = StereoMethod.Evaluate; max_prediction_order = 32; max_partition_order = 8; break; diff --git a/CUETools.Codecs.FLAKE/lpc.cs b/CUETools.Codecs.FLAKE/lpc.cs index 6e8e01e..21030a9 100644 --- a/CUETools.Codecs.FLAKE/lpc.cs +++ b/CUETools.Codecs.FLAKE/lpc.cs @@ -28,7 +28,7 @@ namespace CUETools.Codecs.FLAKE * Calculates autocorrelation data from audio samples * A Welch window function is applied before calculation. */ - static unsafe void + static public unsafe void compute_autocorr(/*const*/ int* data, uint len, uint lag, double* autoc, double* window) { double* data1 = stackalloc double[(int)len + 16]; @@ -83,7 +83,7 @@ namespace CUETools.Codecs.FLAKE * Levinson-Durbin recursion. * Produces LPC coefficients from autocorrelation data. */ - static unsafe void + public static unsafe void compute_lpc_coefs(/*const*/ double* autoc, uint max_order, double* reff, double* lpc/*[][MAX_LPC_ORDER]*/) { @@ -139,19 +139,12 @@ namespace CUETools.Codecs.FLAKE } } - /** - * 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]*/) + public static unsafe void + compute_schur_reflection(/*const*/ double* autoc, uint max_order, + double* reff/*[][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++) @@ -170,6 +163,22 @@ namespace CUETools.Codecs.FLAKE reff[i] = -gen1[0] / error; error += gen1[0] * reff[i]; } + } + + /** + * 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. + */ + public static unsafe uint + compute_lpc_coefs_est(/*const*/ double* autoc, uint max_order, + double* lpc/*[][MAX_LPC_ORDER]*/) + { + double* reff = stackalloc double[MAX_LPC_ORDER]; + + // Schur recursion + compute_schur_reflection(autoc, max_order, reff); // Estimate optimal order using reflection coefficients uint order_est = 1; @@ -396,6 +405,132 @@ namespace CUETools.Codecs.FLAKE } } + public static unsafe void + encode_residual_long(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--) + { + long pred = c0 * (long)*(s++); + *(r++) = *s - (int)(pred >> shift); + } + break; + case 2: + for (int i = n - order; i > 0; i--) + { + long pred = c1 * (long)*(s++); + pred += c0 * (long)*(s++); + *(r++) = *(s--) - (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++); + *(r++) = *s - (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++); + *(r++) = *s - (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++); + *(r++) = *s - (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++); + *(r++) = *s - (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++); + *(r++) = *s - (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++); + *(r++) = *s - (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++); + *(r++) = *s - (int)(pred >> shift); + } + break; + } + } + public static unsafe void decode_residual(int* res, int* smp, int n, int order, int* coefs, int shift)