diff --git a/CUETools.Codecs.ALAC/ALACSubframeInfo.cs b/CUETools.Codecs.ALAC/ALACSubframeInfo.cs index 148d394..f5ba459 100644 --- a/CUETools.Codecs.ALAC/ALACSubframeInfo.cs +++ b/CUETools.Codecs.ALAC/ALACSubframeInfo.cs @@ -6,12 +6,14 @@ public int* samples; public uint done_fixed; public LpcContext[] lpc_ctx; + public LpcSubframeInfo sf; public ALACSubframeInfo() { best = new ALACSubframe(); - lpc_ctx = new LpcContext[Alac.MAX_LPC_WINDOWS]; - for (int i = 0; i < Alac.MAX_LPC_WINDOWS; i++) + sf = new LpcSubframeInfo(); + lpc_ctx = new LpcContext[lpc.MAX_LPC_WINDOWS]; + for (int i = 0; i < lpc.MAX_LPC_WINDOWS; i++) lpc_ctx[i] = new LpcContext(); } @@ -21,7 +23,8 @@ best.residual = r; best.size = AudioSamples.UINT32_MAX; best.order = 0; - for (int iWindow = 0; iWindow < Alac.MAX_LPC_WINDOWS; iWindow++) + sf.Reset(); + for (int iWindow = 0; iWindow < lpc.MAX_LPC_WINDOWS; iWindow++) lpc_ctx[iWindow].Reset(); done_fixed = 0; } diff --git a/CUETools.Codecs.ALAC/ALACWriter.cs b/CUETools.Codecs.ALAC/ALACWriter.cs index c572287..e167f43 100644 --- a/CUETools.Codecs.ALAC/ALACWriter.cs +++ b/CUETools.Codecs.ALAC/ALACWriter.cs @@ -36,7 +36,7 @@ namespace CUETools.Codecs.ALAC public class ALACWriterSettings: AudioEncoderSettings { public ALACWriterSettings() - : base("0 1 2 3 4 5 6 7 8 9 10", "3") + : base("0 1 2 3 4 5 6 7 8 9 10", "5") { } @@ -91,7 +91,8 @@ namespace CUETools.Codecs.ALAC int[] verifyBuffer; int[] residualBuffer; float[] windowBuffer; - int samplesInBuffer = 0; + LpcWindowSection[,] windowSections; + int samplesInBuffer = 0; int m_blockSize = 0; int _totalSize = 0; @@ -122,7 +123,8 @@ namespace CUETools.Codecs.ALAC samplesBuffer = new int[Alac.MAX_BLOCKSIZE * (Settings.PCM.ChannelCount == 2 ? 5 : Settings.PCM.ChannelCount)]; residualBuffer = new int[Alac.MAX_BLOCKSIZE * (Settings.PCM.ChannelCount == 2 ? 6 : Settings.PCM.ChannelCount + 1)]; - windowBuffer = new float[Alac.MAX_BLOCKSIZE * 2 * Alac.MAX_LPC_WINDOWS]; + windowBuffer = new float[Alac.MAX_BLOCKSIZE * 2 * lpc.MAX_LPC_WINDOWS]; + windowSections = new LpcWindowSection[lpc.MAX_LPC_WINDOWS, lpc.MAX_LPC_SECTIONS]; eparams.set_defaults(m_settings.EncoderModeIndex); @@ -875,7 +877,11 @@ namespace CUETools.Codecs.ALAC LpcContext lpc_ctx = frame.subframes[ch].lpc_ctx[iWindow]; - lpc_ctx.GetReflection(max_order, smp, n, frame.window_buffer + iWindow * Alac.MAX_BLOCKSIZE * 2); + fixed (LpcWindowSection* sections = &windowSections[iWindow, 0]) + lpc_ctx.GetReflection( + frame.subframes[ch].sf, max_order, smp, + frame.window_buffer + iWindow * Alac.MAX_BLOCKSIZE * 2, sections, + Settings.PCM.BitsPerSample * 2 + BitReader.log2i(frame.blocksize) >= 61); lpc_ctx.ComputeLPC(lpcs); lpc_ctx.SortOrdersAkaike(frame.blocksize, eparams.estimation_depth, min_order, max_order, 5.0, 1.0/18); for (i = 0; i < eparams.estimation_depth && i < max_order; i++) @@ -945,20 +951,36 @@ namespace CUETools.Codecs.ALAC { case WindowMethod.Estimate: { - int best_window = -1; - double best_error = 0; - int order = 2; - for (int i = 0; i < _windowcount; i++) + int order = 4; + float* err = stackalloc float[lpc.MAX_LPC_ORDER]; + for (int i = 0; i < _windowcount; i++) { - frame.subframes[ch].lpc_ctx[i].GetReflection(order, frame.subframes[ch].samples, frame.blocksize, frame.window_buffer + i * Alac.MAX_BLOCKSIZE * 2); - double err = frame.subframes[ch].lpc_ctx[i].prediction_error[order - 1] / frame.subframes[ch].lpc_ctx[i].autocorr_values[0]; - if (best_window == -1 || best_error > err) - { - best_window = i; - best_error = err; - } + LpcContext lpc_ctx = frame.subframes[ch].lpc_ctx[i]; + fixed (LpcWindowSection* sections = &windowSections[i, 0]) + lpc_ctx.GetReflection( + frame.subframes[ch].sf, order, + frame.subframes[ch].samples, + frame.window_buffer + i * Alac.MAX_BLOCKSIZE * 2, sections, + Settings.PCM.BitsPerSample * 2 + BitReader.log2i(frame.blocksize) >= 61); + lpc_ctx.SortOrdersAkaike(frame.blocksize, 1, 1, order, 4.5, 0.0); + err[i] = (float)(lpc_ctx.Akaike(frame.blocksize, lpc_ctx.best_orders[0], 4.5, 0.0) - frame.blocksize * Math.Log(lpc_ctx.autocorr_values[0]) / 2); } - return best_window; + int* best_windows = stackalloc int[lpc.MAX_LPC_ORDER]; + for (int i = 0; i < _windowcount; i++) + best_windows[i] = i; + for (int i = 0; i < _windowcount; i++) + { + for (int j = i + 1; j < _windowcount; j++) + { + if (err[best_windows[i]] > err[best_windows[j]]) + { + int tmp = best_windows[j]; + best_windows[j] = best_windows[i]; + best_windows[i] = tmp; + } + } + } + return best_windows[0]; } case WindowMethod.Evaluate: encode_residual_pass1(frame, ch, -1); @@ -978,10 +1000,16 @@ namespace CUETools.Codecs.ALAC case StereoMethod.Estimate: for (int ch = 0; ch < subframes; ch++) { - LpcContext lpc_ctx = frame.subframes[ch].lpc_ctx[0]; - int stereo_order = Math.Min(8, eparams.max_prediction_order); + int iWindow = 0; + LpcContext lpc_ctx = frame.subframes[ch].lpc_ctx[iWindow]; + int stereo_order = Math.Min(8, eparams.max_prediction_order); double alpha = 1.5; // 4.5 + eparams.max_prediction_order / 10.0; - lpc_ctx.GetReflection(stereo_order, frame.subframes[ch].samples, frame.blocksize, frame.window_buffer); + fixed (LpcWindowSection* sections = &windowSections[iWindow, 0]) + lpc_ctx.GetReflection( + frame.subframes[ch].sf, stereo_order, + frame.subframes[ch].samples, + frame.window_buffer + iWindow * Alac.MAX_BLOCKSIZE * 2, sections, + Settings.PCM.BitsPerSample * 2 + BitReader.log2i(frame.blocksize) >= 61); lpc_ctx.SortOrdersAkaike(frame.blocksize, 1, 1, stereo_order, alpha, 0); frame.subframes[ch].best.size = (uint)Math.Max(0, lpc_ctx.Akaike(frame.blocksize, lpc_ctx.best_orders[0], alpha, 0)); } @@ -1060,13 +1088,17 @@ namespace CUETools.Codecs.ALAC unsafe void calculate_window(float * window, window_function func, WindowFunction flag) { - if ((eparams.window_function & flag) == 0 || _windowcount == Alac.MAX_LPC_WINDOWS) + if ((eparams.window_function & flag) == 0 || _windowcount == lpc.MAX_LPC_WINDOWS) return; int sz = _windowsize; float* pos = window + _windowcount * Alac.MAX_BLOCKSIZE * 2; do { - func(pos, sz); + windowSections[_windowcount, 0].setData(0, sz); + for (int j = 1; j < lpc.MAX_LPC_SECTIONS; j++) + windowSections[_windowcount, j].setZero(sz, sz); + func(pos, sz); + break; if ((sz & 1) != 0) break; pos += sz; @@ -1091,9 +1123,33 @@ namespace CUETools.Codecs.ALAC calculate_window(window, lpc.window_tukey, WindowFunction.Tukey); calculate_window(window, lpc.window_hann, WindowFunction.Hann); calculate_window(window, lpc.window_flattop, WindowFunction.Flattop); - if (_windowcount == 0) + int tukey_parts = 2; + double overlap = -0.3; + double overlap_units = overlap / (1.0 - overlap); + for (int m = 0; m < tukey_parts; m++) + calculate_window(window, (w, wsz) => + { + lpc.window_punchout_tukey(w, wsz, 0.1, + m / (tukey_parts + overlap_units), + (m + 1 + overlap_units) / (tukey_parts + overlap_units)); + }, WindowFunction.PartialTukey); + + tukey_parts = 3; + overlap = -0.1; + //overlap = 0.1; + overlap_units = overlap / (1.0 - overlap); + for (int m = 0; m < tukey_parts; m++) + calculate_window(window, (w, wsz) => + { + lpc.window_punchout_tukey(w, wsz, 0.1, + m / (tukey_parts + overlap_units), + (m + 1 + overlap_units) / (tukey_parts + overlap_units)); + }, WindowFunction.PunchoutTukey); + if (_windowcount == 0) throw new Exception("invalid windowfunction"); - } + fixed (LpcWindowSection* sections = &windowSections[0, 0]) + LpcWindowSection.Detect(_windowcount, window, Alac.MAX_BLOCKSIZE * 2, _windowsize, sections); + } frame.window_buffer = window; int bps = Settings.PCM.BitsPerSample + Settings.PCM.ChannelCount - 1; @@ -1793,11 +1849,13 @@ namespace CUETools.Codecs.ALAC case 4: stereo_method = StereoMethod.Estimate; window_method = WindowMethod.Estimate; + window_function = WindowFunction.PartialTukey | WindowFunction.PunchoutTukey; max_prediction_order = 8; break; case 5: stereo_method = StereoMethod.Estimate; window_method = WindowMethod.Estimate; + window_function = WindowFunction.PartialTukey | WindowFunction.PunchoutTukey; break; case 6: stereo_method = StereoMethod.Estimate; diff --git a/CUETools.Codecs.ALAC/Alac.cs b/CUETools.Codecs.ALAC/Alac.cs index 07b63ad..d8d989b 100644 --- a/CUETools.Codecs.ALAC/Alac.cs +++ b/CUETools.Codecs.ALAC/Alac.cs @@ -28,7 +28,6 @@ namespace CUETools.Codecs.ALAC public const int MAX_RICE_PARAM = 14; public const int MAX_PARTITION_ORDER = 8; public const int MAX_PARTITIONS = 1 << MAX_PARTITION_ORDER; - public const int MAX_LPC_WINDOWS = 4; public const uint UINT32_MAX = 0xffffffff; diff --git a/CUETools.Codecs.ALAC/WindowFunction.cs b/CUETools.Codecs.ALAC/WindowFunction.cs index 694b0a1..bbc628b 100644 --- a/CUETools.Codecs.ALAC/WindowFunction.cs +++ b/CUETools.Codecs.ALAC/WindowFunction.cs @@ -7,6 +7,8 @@ Hann = 4, Flattop = 8, Bartlett = 16, - TukFlat = 10 + TukFlat = 10, + PartialTukey = 32, + PunchoutTukey = 64, } } diff --git a/CUETools.Codecs.FLACCL/FLACCLWriter.cs b/CUETools.Codecs.FLACCL/FLACCLWriter.cs index 5fcdf84..0de51f0 100644 --- a/CUETools.Codecs.FLACCL/FLACCLWriter.cs +++ b/CUETools.Codecs.FLACCL/FLACCLWriter.cs @@ -270,6 +270,8 @@ namespace CUETools.Codecs.FLACCL string _path; long _position; + public const int MAX_LPC_WINDOWS = 2; + // number of audio channels // valid values are 1 to 8 int channels, ch_code; @@ -1090,7 +1092,7 @@ namespace CUETools.Codecs.FLACCL unsafe void calculate_window(FLACCLTask task, window_function func, WindowFunction flag) { - if ((eparams.window_function & flag) == 0 || task.nWindowFunctions == lpc.MAX_LPC_WINDOWS) + if ((eparams.window_function & flag) == 0 || task.nWindowFunctions == MAX_LPC_WINDOWS) return; func(((float*)task.clWindowFunctionsPtr) + task.nWindowFunctions * task.frameSize, task.frameSize); @@ -2508,10 +2510,10 @@ namespace CUETools.Codecs.FLACCL int residualBufferLen = sizeof(int) * MAX_CHANNELSIZE * channels; // need to adjust residualOffset? int partitionsLen = sizeof(int) * ((writer.Settings.PCM.BitsPerSample > 16 ? 31 : 15) * 2 << 8) * channels * MAX_FRAMES; int riceParamsLen = sizeof(int) * (4 << 8) * channels * MAX_FRAMES; - int autocorLen = sizeof(float) * (MAX_ORDER + 1) * lpc.MAX_LPC_WINDOWS * channelsCount * MAX_FRAMES; + int autocorLen = sizeof(float) * (MAX_ORDER + 1) * FLACCLWriter.MAX_LPC_WINDOWS * channelsCount * MAX_FRAMES; int lpcDataLen = autocorLen * 32; - int resOutLen = sizeof(int) * channelsCount * (lpc.MAX_LPC_WINDOWS * lpc.MAX_LPC_ORDER + 8) * MAX_FRAMES; - int wndLen = sizeof(float) * MAX_CHANNELSIZE /** 2*/ * lpc.MAX_LPC_WINDOWS; + int resOutLen = sizeof(int) * channelsCount * (FLACCLWriter.MAX_LPC_WINDOWS * lpc.MAX_LPC_ORDER + 8) * MAX_FRAMES; + int wndLen = sizeof(float) * MAX_CHANNELSIZE /** 2*/ * FLACCLWriter.MAX_LPC_WINDOWS; int selectedLen = sizeof(int) * 32 * channelsCount * MAX_FRAMES; int riceLen = sizeof(int) * channels * MAX_CHANNELSIZE; diff --git a/CUETools.Codecs.FLAKE/FlacFrame.cs b/CUETools.Codecs.FLAKE/FlacFrame.cs index a30b72a..c9741f4 100644 --- a/CUETools.Codecs.FLAKE/FlacFrame.cs +++ b/CUETools.Codecs.FLAKE/FlacFrame.cs @@ -11,6 +11,7 @@ public int frame_number; public FlacSubframe current; public float* window_buffer; + public int nSeg = 0; public BitWriter writer = null; public int writer_offset = 0; diff --git a/CUETools.Codecs.FLAKE/FlacSubframeInfo.cs b/CUETools.Codecs.FLAKE/FlacSubframeInfo.cs index 264092a..0c285dc 100644 --- a/CUETools.Codecs.FLAKE/FlacSubframeInfo.cs +++ b/CUETools.Codecs.FLAKE/FlacSubframeInfo.cs @@ -7,6 +7,7 @@ namespace CUETools.Codecs.FLAKE public FlacSubframeInfo() { best = new FlacSubframe(); + sf = new LpcSubframeInfo(); lpc_ctx = new LpcContext[lpc.MAX_LPC_WINDOWS]; for (int i = 0; i < lpc.MAX_LPC_WINDOWS; i++) lpc_ctx[i] = new LpcContext(); @@ -22,8 +23,10 @@ namespace CUETools.Codecs.FLAKE best.residual = r; best.type = SubframeType.Verbatim; best.size = AudioSamples.UINT32_MAX; + sf.Reset(); for (int iWindow = 0; iWindow < lpc.MAX_LPC_WINDOWS; iWindow++) lpc_ctx[iWindow].Reset(); + //sf.obits = obits; done_fixed = 0; } @@ -33,5 +36,6 @@ namespace CUETools.Codecs.FLAKE public int* samples; public uint done_fixed; public LpcContext[] lpc_ctx; + public LpcSubframeInfo sf; }; } diff --git a/CUETools.Codecs.FLAKE/FlakeWriter.cs b/CUETools.Codecs.FLAKE/FlakeWriter.cs index a1938af..7a3a58a 100644 --- a/CUETools.Codecs.FLAKE/FlakeWriter.cs +++ b/CUETools.Codecs.FLAKE/FlakeWriter.cs @@ -20,6 +20,7 @@ */ #define NOINTEROP +#define VARIANT1 using System; using System.ComponentModel; @@ -84,14 +85,14 @@ namespace CUETools.Codecs.FLAKE } [DefaultValue(-1)] - [DefaultValueForMode(3, 2, 2, 2, 2, 2, 2, 2, 0, 2, 0, 0)] + [DefaultValueForMode(2, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0)] [Browsable(false)] [DisplayName("MinFixedOrder")] [SRDescription(typeof(Properties.Resources), "MinFixedOrderDescription")] public int MinFixedOrder { get; set; } [DefaultValue(-1)] - [DefaultValueForMode(2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 4, 4)] + [DefaultValueForMode(2, 4, 4, 4, 4, 4, 4, 4, 4, 2, 4, 4)] [Browsable(false)] [DisplayName("MaxFixedOrder")] [SRDescription(typeof(Properties.Resources), "MaxFixedOrderDescription")] @@ -104,7 +105,7 @@ namespace CUETools.Codecs.FLAKE public int MinLPCOrder { get; set; } [DefaultValue(-1)] - [DefaultValueForMode(6, 8, 12, 8, 12, 12, 12, 12, 12, 32, 32, 32)] + [DefaultValueForMode(8, 8, 8, 12, 12, 12, 12, 12, 12, 32, 32, 32)] [Browsable(false)] [DisplayName("MaxLPCOrder")] [SRDescription(typeof(Properties.Resources), "MaxLPCOrderDescription")] @@ -117,7 +118,7 @@ namespace CUETools.Codecs.FLAKE public int MinPartitionOrder { get; set; } [DefaultValue(-1)] - [DefaultValueForMode(6, 6, 6, 8, 8, 8, 8, 8, 8, 8, 8, 8)] + [DefaultValueForMode(6, 8, 6, 8, 8, 8, 8, 8, 8, 8, 8, 8)] [DisplayName("MaxPartitionOrder")] [Browsable(false)] [SRDescription(typeof(Properties.Resources), "MaxPartitionOrderDescription")] @@ -192,6 +193,9 @@ namespace CUETools.Codecs.FLAKE int[] residualBuffer; float[] windowBuffer; double[] windowScale; + LpcWindowSection[, ,] windowSections; + + WindowFunction[] windowType; int samplesInBuffer = 0; int m_blockSize = 0; @@ -229,7 +233,9 @@ namespace CUETools.Codecs.FLAKE samplesBuffer = new int[Flake.MAX_BLOCKSIZE * (channels == 2 ? 4 : channels)]; residualBuffer = new int[Flake.MAX_BLOCKSIZE * (channels == 2 ? 10 : channels + 1)]; windowBuffer = new float[Flake.MAX_BLOCKSIZE * 2 * lpc.MAX_LPC_WINDOWS]; - windowScale = new double[lpc.MAX_LPC_WINDOWS]; + windowScale = new double[lpc.MAX_LPC_WINDOWS]; + windowType = new WindowFunction[lpc.MAX_LPC_WINDOWS]; + windowSections = new LpcWindowSection[12, lpc.MAX_LPC_WINDOWS, lpc.MAX_LPC_SECTIONS]; eparams.flake_set_defaults(m_settings); @@ -524,59 +530,293 @@ namespace CUETools.Codecs.FLAKE AudioSamples.MemCpy(res, smp, (int) n); } - unsafe static void encode_residual_fixed(int* res, int* smp, int n, int order) - { - int i; - int s0, s1, s2; - switch (order) - { - case 0: - AudioSamples.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; - } - } + unsafe static void encode_residual_fixed(int* res, int* smp, int n, int order) + { + int next_error_0, next_error_1, next_error_2, next_error_3, next_error_4; + int last_error_0, last_error_1, last_error_2, last_error_3; + int* end = smp + n; + if (order == 0) + { + AudioSamples.MemCpy(res, smp, n); + return; + } + + next_error_0 = *(res++) = *(smp++); + last_error_0 = next_error_0; + + if (order == 1) + { + while (smp < end) + { +#if VARIANT1 + int error, save; + error = *(smp++); save = error; + error -= last_error_0; *(res++) = error; last_error_0 = save; +#else + next_error_0 = *(smp++); + next_error_1 = next_error_0 - last_error_0; + + last_error_0 = next_error_0; + + *(res++) = (int)next_error_1; +#endif + } + return; + } + + next_error_0 = *(res++) = *(smp++); + next_error_1 = next_error_0 - last_error_0; + last_error_0 = next_error_0; + last_error_1 = next_error_1; + + if (order == 2) + { + while (smp < end) + { +#if VARIANT1 + int error, save; + error = *(smp++); save = error; + error -= last_error_0; last_error_0 = save; save = error; + error -= last_error_1; *(res++) = error; last_error_1 = save; +#else + next_error_0 = *(smp++); + next_error_1 = next_error_0 - last_error_0; + next_error_2 = next_error_1 - last_error_1; + + last_error_0 = next_error_0; + last_error_1 = next_error_1; + + *(res++) = (int)next_error_2; +#endif + } + return; + } + + next_error_0 = *(res++) = *(smp++); + next_error_1 = next_error_0 - last_error_0; + next_error_2 = next_error_1 - last_error_1; + last_error_0 = next_error_0; + last_error_1 = next_error_1; + last_error_2 = next_error_2; + + if (order == 3) + { + while (smp < end) + { +#if VARIANT1 + int error, save; + error = *(smp++); save = error; + error -= last_error_0; last_error_0 = save; save = error; + error -= last_error_1; last_error_1 = save; save = error; + error -= last_error_2; *(res++) = error; last_error_2 = save; +#else + next_error_0 = *(smp++); + next_error_1 = next_error_0 - last_error_0; + next_error_2 = next_error_1 - last_error_1; + next_error_3 = next_error_2 - last_error_2; + + last_error_0 = next_error_0; + last_error_1 = next_error_1; + last_error_2 = next_error_2; + + *(res++) = (int)next_error_3; +#endif + } + return; + } + + next_error_0 = *(res++) = *(smp++); + next_error_1 = next_error_0 - last_error_0; + next_error_2 = next_error_1 - last_error_1; + next_error_3 = next_error_2 - last_error_2; + last_error_0 = next_error_0; + last_error_1 = next_error_1; + last_error_2 = next_error_2; + last_error_3 = next_error_3; + + if (order == 4) + { + while (smp < end) + { +#if VARIANT1 + int error, save; + error = *(smp++); save = error; + error -= last_error_0; last_error_0 = save; save = error; + error -= last_error_1; last_error_1 = save; save = error; + error -= last_error_2; last_error_2 = save; save = error; + error -= last_error_3; *(res++) = error; last_error_3 = save; +#else + next_error_0 = *(smp++); + next_error_1 = next_error_0 - last_error_0; + next_error_2 = next_error_1 - last_error_1; + next_error_3 = next_error_2 - last_error_2; + next_error_4 = next_error_3 - last_error_3; + + last_error_0 = next_error_0; + last_error_1 = next_error_1; + last_error_2 = next_error_2; + last_error_3 = next_error_3; + + *(res++) = (int)next_error_4; +#endif + } + return; + } + + throw new ArgumentOutOfRangeException(); + } +#if XXX + unsafe static int encode_residual_fixed_estimate_best_order(int* res, int* smp, int n, int order) + { + int next_error_0, next_error_1, next_error_2, next_error_3, next_error_4; + int last_error_0, last_error_1, last_error_2, last_error_3; + int* end = smp + n; + ulong total_error_0 = 0, total_error_1 = 0, total_error_2 = 0, total_error_3 = 0, total_error_4 = 0; + + if (order == 0) + { + AudioSamples.MemCpy(res, smp, n); + return 0; + } + + next_error_0 = *(res++) = *(smp++); + last_error_0 = next_error_0; + + if (order == 1) + { + while (smp < end) + { + next_error_0 = *(smp++); + next_error_1 = next_error_0 - last_error_0; + + last_error_0 = next_error_0; + + total_error_0 += (ulong)((next_error_0 << 1) ^ (next_error_0 >> 31)); + total_error_1 += (ulong)((next_error_1 << 1) ^ (next_error_1 >> 31)); + + *(res++) = (int)next_error_1; + } + + if ((total_error_0 < total_error_1)) + return 0; + return 1; + } + + next_error_0 = *(res++) = *(smp++); + next_error_1 = next_error_0 - last_error_0; + last_error_0 = next_error_0; + last_error_1 = next_error_1; + + if (order == 2) + { + while (smp < end) + { + next_error_0 = *(smp++); + next_error_1 = next_error_0 - last_error_0; + next_error_2 = next_error_1 - last_error_1; + + last_error_0 = next_error_0; + last_error_1 = next_error_1; + + total_error_0 += (ulong)((next_error_0 << 1) ^ (next_error_0 >> 31)); + total_error_1 += (ulong)((next_error_1 << 1) ^ (next_error_1 >> 31)); + total_error_2 += (ulong)((next_error_2 << 1) ^ (next_error_2 >> 31)); + + *(res++) = (int)next_error_2; + } + + if ((total_error_0 < total_error_1) & (total_error_0 < total_error_2)) + return 0; + else if ((total_error_1 < total_error_2)) + return 1; + return 2; + } + + next_error_0 = *(res++) = *(smp++); + next_error_1 = next_error_0 - last_error_0; + next_error_2 = next_error_1 - last_error_1; + last_error_0 = next_error_0; + last_error_1 = next_error_1; + last_error_2 = next_error_2; + + if (order == 3) + { + while (smp < end) + { + next_error_0 = *(smp++); + next_error_1 = next_error_0 - last_error_0; + next_error_2 = next_error_1 - last_error_1; + next_error_3 = next_error_2 - last_error_2; + + last_error_0 = next_error_0; + last_error_1 = next_error_1; + last_error_2 = next_error_2; + + total_error_0 += (ulong)((next_error_0 << 1) ^ (next_error_0 >> 31)); + total_error_1 += (ulong)((next_error_1 << 1) ^ (next_error_1 >> 31)); + total_error_2 += (ulong)((next_error_2 << 1) ^ (next_error_2 >> 31)); + total_error_3 += (ulong)((next_error_3 << 1) ^ (next_error_3 >> 31)); + + *(res++) = (int)next_error_3; + } + + if ((total_error_0 < total_error_1) & (total_error_0 < total_error_2) & (total_error_0 < total_error_3)) + return 0; + else if ((total_error_1 < total_error_2) & (total_error_1 < total_error_3)) + return 1; + else if ((total_error_2 < total_error_3)) + return 2; + return 3; + } + + next_error_0 = *(res++) = *(smp++); + next_error_1 = next_error_0 - last_error_0; + next_error_2 = next_error_1 - last_error_1; + next_error_3 = next_error_2 - last_error_2; + last_error_0 = next_error_0; + last_error_1 = next_error_1; + last_error_2 = next_error_2; + last_error_3 = next_error_3; + + if (order == 4) + { + while (smp < end) + { + next_error_0 = *(smp++); + next_error_1 = next_error_0 - last_error_0; + next_error_2 = next_error_1 - last_error_1; + next_error_3 = next_error_2 - last_error_2; + next_error_4 = next_error_3 - last_error_3; + + last_error_0 = next_error_0; + last_error_1 = next_error_1; + last_error_2 = next_error_2; + last_error_3 = next_error_3; + + total_error_0 += (ulong)((next_error_0 << 1) ^ (next_error_0 >> 31)); + total_error_1 += (ulong)((next_error_1 << 1) ^ (next_error_1 >> 31)); + total_error_2 += (ulong)((next_error_2 << 1) ^ (next_error_2 >> 31)); + total_error_3 += (ulong)((next_error_3 << 1) ^ (next_error_3 >> 31)); + total_error_4 += (ulong)((next_error_4 << 1) ^ (next_error_4 >> 31)); + + *(res++) = (int)next_error_4; + } + + if ((total_error_0 < total_error_1) & (total_error_0 < total_error_2) & (total_error_0 < total_error_3) & (total_error_0 < total_error_4)) + return 0; + else if ((total_error_1 < total_error_2) & (total_error_1 < total_error_3) & (total_error_1 < total_error_4)) + return 1; + else if ((total_error_2 < total_error_3) & (total_error_2 < total_error_4)) + return 2; + else if (total_error_3 < total_error_4) + return 3; + return 4; + } + + throw new ArgumentOutOfRangeException(); + } +#endif static unsafe uint calc_optimal_rice_params(int porder, int* parm, ulong* sums, uint n, uint pred_order, ref int method) { uint part = (1U << porder); @@ -1079,7 +1319,22 @@ new int[] { // 30 frame.current.order = order; frame.current.type = SubframeType.Fixed; - encode_residual_fixed(frame.current.residual, frame.subframes[ch].samples, frame.blocksize, frame.current.order); +#if XXX + int best_order = order; + if (frame.subframes[ch].done_fixed == 0) + { + best_order = encode_residual_fixed_estimate_best_order(frame.current.residual, frame.subframes[ch].samples, frame.blocksize, frame.current.order); + if (best_order != order) + { + //frame.subframes[ch].done_fixed |= (1U << order); + order = best_order; + frame.current.order = order; + encode_residual_fixed(frame.current.residual, frame.subframes[ch].samples, frame.blocksize, frame.current.order); + } + } + else +#endif + encode_residual_fixed(frame.current.residual, frame.subframes[ch].samples, frame.blocksize, frame.current.order); int pmax = get_max_p_order(m_settings.MaxPartitionOrder, frame.blocksize, frame.current.order); int pmin = Math.Min(m_settings.MinPartitionOrder, pmax); @@ -1091,7 +1346,78 @@ new int[] { // 30 frame.ChooseBestSubframe(ch); } - unsafe void encode_residual(FlacFrame frame, int ch, PredictionType predict, OrderMethod omethod, int pass, int best_window) + unsafe void fixed_compute_best_predictor(int* data, uint data_len, ulong* errors)//, float* residual_bits_per_sample) + { + long last_error_0 = data[-1]; + long last_error_1 = data[-1] - data[-2]; + long last_error_2 = last_error_1 - (data[-2] - data[-3]); + long last_error_3 = last_error_2 - (data[-2] - 2 * data[-3] + data[-4]); + ulong total_error_0 = 0, total_error_1 = 0, total_error_2 = 0, total_error_3 = 0, total_error_4 = 0; + +#if VARIANT1 + long error, save; + int* finish = data + data_len; + while (data < finish) + { + error = *(data++); total_error_0 += (ulong)((error << 1) ^ (error >> 63)); save = error; + error -= last_error_0; total_error_1 += (ulong)((error << 1) ^ (error >> 63)); last_error_0 = save; save = error; + error -= last_error_1; total_error_2 += (ulong)((error << 1) ^ (error >> 63)); last_error_1 = save; save = error; + error -= last_error_2; total_error_3 += (ulong)((error << 1) ^ (error >> 63)); last_error_2 = save; save = error; + error -= last_error_3; total_error_4 += (ulong)((error << 1) ^ (error >> 63)); last_error_3 = save; + } +#else + int* finish = data + data_len; + while (data < finish) + { + long next_error_0 = *(data++); + long next_error_1 = next_error_0 - last_error_0; + long next_error_2 = next_error_1 - last_error_1; + long next_error_3 = next_error_2 - last_error_2; + long next_error_4 = next_error_3 - last_error_3; + + last_error_0 = next_error_0; + last_error_1 = next_error_1; + last_error_2 = next_error_2; + last_error_3 = next_error_3; + + total_error_0 += (ulong)((last_error_0 << 1) ^ (last_error_0 >> 63)); + total_error_1 += (ulong)((last_error_1 << 1) ^ (last_error_1 >> 63)); + total_error_2 += (ulong)((last_error_2 << 1) ^ (last_error_2 >> 63)); + total_error_3 += (ulong)((last_error_3 << 1) ^ (last_error_3 >> 63)); + total_error_4 += (ulong)((next_error_4 << 1) ^ (next_error_4 >> 63)); + } +#endif + + errors[0] = total_error_0; + errors[1] = total_error_1; + errors[2] = total_error_2; + errors[3] = total_error_3; + errors[4] = total_error_4; + + //residual_bits_per_sample[0] = (float)((total_error_0 > 0) ? log(M_LN2 * (FLAC__double)total_error_0 / (FLAC__double)data_len) / M_LN2 : 0.0); + //residual_bits_per_sample[1] = (float)((total_error_1 > 0) ? log(M_LN2 * (FLAC__double)total_error_1 / (FLAC__double)data_len) / M_LN2 : 0.0); + //residual_bits_per_sample[2] = (float)((total_error_2 > 0) ? log(M_LN2 * (FLAC__double)total_error_2 / (FLAC__double)data_len) / M_LN2 : 0.0); + //residual_bits_per_sample[3] = (float)((total_error_3 > 0) ? log(M_LN2 * (FLAC__double)total_error_3 / (FLAC__double)data_len) / M_LN2 : 0.0); + //residual_bits_per_sample[4] = (float)((total_error_4 > 0) ? log(M_LN2 * (FLAC__double)total_error_4 / (FLAC__double)data_len) / M_LN2 : 0.0); + } + + unsafe int fixed_compute_best_predictor_order(ulong* error) + { + int order; + if ((error[0] < error[1]) & (error[0] < error[2]) & (error[0] < error[3]) & (error[0] < error[4])) + order = 0; + else if ((error[1] < error[2]) & (error[1] < error[3]) & (error[1] < error[4])) + order = 1; + else if ((error[2] < error[3]) & (error[2] < error[4])) + order = 2; + else if (error[3] < error[4]) + order = 3; + else + order = 4; + return order; + } + + unsafe void encode_residual(FlacFrame frame, int ch, PredictionType predict, OrderMethod omethod, int pass, int windows_mask) { int* smp = frame.subframes[ch].samples; int i, n = frame.blocksize; @@ -1132,13 +1458,15 @@ new int[] { // 30 for (int iWindow = 0; iWindow < _windowcount; iWindow++) { - if (best_window != -1 && iWindow != best_window) + if ((windows_mask & (1 << iWindow)) == 0) continue; LpcContext lpc_ctx = frame.subframes[ch].lpc_ctx[iWindow]; - - lpc_ctx.GetReflection(max_order, smp, n, frame.window_buffer + iWindow * Flake.MAX_BLOCKSIZE * 2); - lpc_ctx.ComputeLPC(lpcs); + fixed (LpcWindowSection* sections = &windowSections[frame.nSeg, iWindow, 0]) + lpc_ctx.GetReflection( + frame.subframes[ch].sf, max_order, smp, frame.window_buffer + iWindow * Flake.MAX_BLOCKSIZE * 2, sections, + frame.subframes[ch].obits * 2 + BitReader.log2i(frame.blocksize) >= 61); + lpc_ctx.ComputeLPC(lpcs); //int frameSize = n; //float* F = stackalloc float[frameSize]; @@ -1217,8 +1545,21 @@ new int[] { // 30 int max_fixed_order = Math.Min(m_settings.MaxFixedOrder, 4); int min_fixed_order = Math.Min(m_settings.MinFixedOrder, max_fixed_order); - for (i = min_fixed_order; i <= max_fixed_order; i++) - encode_residual_fixed_sub(frame, i, ch); + if (min_fixed_order == 0 && max_fixed_order == 4) + { + if (frame.subframes[ch].done_fixed == 0) + { + ulong* fixed_errors = stackalloc ulong[5]; + fixed_compute_best_predictor(smp + 4, (uint)n - 4, fixed_errors); + i = fixed_compute_best_predictor_order(fixed_errors); + encode_residual_fixed_sub(frame, i, ch); + } + } + else + { + for (i = max_fixed_order; i >= min_fixed_order; i--) + encode_residual_fixed_sub(frame, i, ch); + } } } @@ -1379,25 +1720,25 @@ new int[] { // 30 bitwriter.flush(); } - unsafe void encode_residual_pass1(FlacFrame frame, int ch, int best_window) + unsafe void encode_residual_pass1(FlacFrame frame, int ch, int windows_mask) { int max_prediction_order = m_settings.MaxLPCOrder; - int max_fixed_order = m_settings.MaxFixedOrder; - int min_fixed_order = m_settings.MinFixedOrder; + //int max_fixed_order = m_settings.MaxFixedOrder; + //int min_fixed_order = m_settings.MinFixedOrder; int lpc_min_precision_search = eparams.lpc_min_precision_search; int lpc_max_precision_search = eparams.lpc_max_precision_search; int max_partition_order = m_settings.MaxPartitionOrder; int estimation_depth = eparams.estimation_depth; var development_mode = eparams.development_mode; - m_settings.MinFixedOrder = 2; - m_settings.MaxFixedOrder = 2; + //m_settings.MinFixedOrder = 2; + //m_settings.MaxFixedOrder = 2; eparams.lpc_min_precision_search = eparams.lpc_max_precision_search; m_settings.MaxLPCOrder = Math.Min(m_settings.MaxLPCOrder, Math.Max(m_settings.MinLPCOrder, 8)); eparams.estimation_depth = 1; eparams.development_mode = Math.Min(eparams.development_mode, -1); - encode_residual(frame, ch, eparams.prediction_type, OrderMethod.Akaike, 1, best_window); - m_settings.MinFixedOrder = min_fixed_order; - m_settings.MaxFixedOrder = max_fixed_order; + encode_residual(frame, ch, eparams.prediction_type, OrderMethod.Akaike, 1, windows_mask); + //m_settings.MinFixedOrder = min_fixed_order; + //m_settings.MaxFixedOrder = max_fixed_order; m_settings.MaxLPCOrder = max_prediction_order; eparams.lpc_min_precision_search = lpc_min_precision_search; eparams.lpc_max_precision_search = lpc_max_precision_search; @@ -1408,37 +1749,141 @@ new int[] { // 30 unsafe void encode_residual_pass2(FlacFrame frame, int ch) { - encode_residual(frame, ch, eparams.prediction_type, eparams.order_method, 2, estimate_best_window(frame, ch)); + encode_residual(frame, ch, eparams.prediction_type, eparams.order_method, 2, estimate_best_windows(frame, ch)); } - unsafe int estimate_best_window(FlacFrame frame, int ch) + unsafe int estimate_best_windows_akaike(FlacFrame frame, int ch, int count, bool onePerType) + { + int* windows_present = stackalloc int[_windowcount]; + for (int i = 0; i < _windowcount; i++) + windows_present[i] = 0; + if (onePerType) + { + for (int i = 0; i < _windowcount; i++) + for (int j = 0; j < _windowcount; j++) + if (windowType[j] == windowType[i]) + windows_present[j]++; + } + + float* err = stackalloc float[lpc.MAX_LPC_ORDER]; + for (int i = 0; i < _windowcount; i++) + { + LpcContext lpc_ctx = frame.subframes[ch].lpc_ctx[i]; + if (onePerType && windows_present[i] <= count) + { + err[i] = 0; + continue; + } + int estimate_order = 4; + fixed (LpcWindowSection* sections = &windowSections[frame.nSeg, i, 0]) + lpc_ctx.GetReflection( + frame.subframes[ch].sf, estimate_order, + frame.subframes[ch].samples, frame.window_buffer + i * Flake.MAX_BLOCKSIZE * 2, sections, + frame.subframes[ch].obits * 2 + BitReader.log2i(frame.blocksize) >= 61); + lpc_ctx.SortOrdersAkaike(frame.blocksize, 1, 1, estimate_order, 4.5, 0.0); + //err[i] = (float)(lpc_ctx.Akaike(frame.blocksize, lpc_ctx.best_orders[0], 4.5, 0.0)); + //err[i] = (float)((frame.blocksize * lpc_ctx.prediction_error[lpc_ctx.best_orders[0] - 1] / windowScale[i]) + lpc_ctx.best_orders[0] * 4.5); + //err[i] = (float)((frame.blocksize * lpc_ctx.prediction_error[lpc_ctx.best_orders[0] - 1] / windowScale[i]) + lpc_ctx.best_orders[0] * frame.subframes[ch].obits); + + // realistic + //err[i] = (float)(frame.blocksize * Math.Log(lpc_ctx.prediction_error[lpc_ctx.best_orders[0] - 1]) / Math.Log(2) / 2.5 + //- windowScale[i] / 2 + lpc_ctx.best_orders[0] * frame.subframes[ch].obits / 2); + + //err[i] = (float)(frame.blocksize * Math.Log(lpc_ctx.prediction_error[lpc_ctx.best_orders[0] - 1]) / Math.Log(2) / 2.5 + //- frame.blocksize * Math.Log(lpc_ctx.autocorr_values[0]) / 2.1 + //+ Math.Log(frame.blocksize) * lpc_ctx.best_orders[0] * 4.5 / 2.5 / Math.Log(2)); + + // Akaike + //err[i] = (float)(frame.blocksize * (Math.Log(lpc_ctx.prediction_error[lpc_ctx.best_orders[0] - 1])) + Math.Log(frame.blocksize) * lpc_ctx.best_orders[0] * 4.5); + + //err[i] = (float)(lpc_ctx.Akaike(frame.blocksize, lpc_ctx.best_orders[0], 4.5, 0.0) - frame.blocksize * (frame.subframes[ch].obits + Math.Log(windowScale[i] / frame.blocksize) / 2)); + + // tested good + err[i] = (float)(lpc_ctx.Akaike(frame.blocksize, lpc_ctx.best_orders[0], 4.5, 0.0) - frame.blocksize * Math.Log(lpc_ctx.autocorr_values[0]) / 2); + } + int* best_windows = stackalloc int[lpc.MAX_LPC_ORDER]; + for (int i = 0; i < _windowcount; i++) + best_windows[i] = i; + for (int i = 0; i < _windowcount; i++) + { + for (int j = i + 1; j < _windowcount; j++) + { + if (err[best_windows[i]] > err[best_windows[j]]) + { + int tmp = best_windows[j]; + best_windows[j] = best_windows[i]; + best_windows[i] = tmp; + } + } + } + int window_mask = 0; + if (onePerType) + { + for (int i = 0; i < _windowcount; i++) + windows_present[i] = count; + for (int i = 0; i < _windowcount; i++) + { + int w = best_windows[i]; + if (windows_present[w] > 0) + { + for (int j = 0; j < _windowcount; j++) + if (windowType[j] == windowType[w]) + windows_present[j]--; + window_mask |= 1 << w; + } + } + } + else + { + for (int i = 0; i < _windowcount && i < count; i++) + window_mask |= 1 << best_windows[i]; + } + return window_mask; + } + + unsafe int estimate_best_windows(FlacFrame frame, int ch) { - if (_windowcount == 1) - return 0; + if (_windowcount == 1 || eparams.prediction_type == PredictionType.Fixed) + return 1; switch (eparams.window_method) { - case WindowMethod.Estimate: - { - int best_window = -1; - double best_error = 0; - int order = 2; - for (int i = 0; i < _windowcount; i++) - { - frame.subframes[ch].lpc_ctx[i].GetReflection(order, frame.subframes[ch].samples, frame.blocksize, frame.window_buffer + i * Flake.MAX_BLOCKSIZE * 2); - double err = frame.subframes[ch].lpc_ctx[i].prediction_error[order - 1] / frame.subframes[ch].lpc_ctx[i].autocorr_values[0]; - //double err = frame.subframes[ch].lpc_ctx[i].autocorr_values[0] / frame.subframes[ch].lpc_ctx[i].autocorr_values[2]; - if (best_window == -1 || best_error > err) - { - best_window = i; - best_error = err; - } - } - return best_window; - } - case WindowMethod.Evaluate: + case WindowMethod.Estimate: + return estimate_best_windows_akaike(frame, ch, 1, false); + case WindowMethod.Estimate2: + return estimate_best_windows_akaike(frame, ch, 2, false); + case WindowMethod.Estimate3: + return estimate_best_windows_akaike(frame, ch, 3, false); + case WindowMethod.EstimateN: + return estimate_best_windows_akaike(frame, ch, 1, true); + case WindowMethod.Evaluate2: + encode_residual_pass1(frame, ch, estimate_best_windows_akaike(frame, ch, 2, false)); + return frame.subframes[ch].best.type == SubframeType.LPC ? 1 << frame.subframes[ch].best.window : 0; + case WindowMethod.Evaluate3: + encode_residual_pass1(frame, ch, estimate_best_windows_akaike(frame, ch, 3, false)); + return frame.subframes[ch].best.type == SubframeType.LPC ? 1 << frame.subframes[ch].best.window : 0; + case WindowMethod.EvaluateN: + encode_residual_pass1(frame, ch, estimate_best_windows_akaike(frame, ch, 1, true)); +#if XXX + if (frame.subframes[ch].best.type == SubframeType.LPC && frame.subframes[ch].best.order <= 4) + { + LpcContext lpc_ctx = frame.subframes[ch].lpc_ctx[frame.subframes[ch].best.window]; + double err = lpc_ctx.prediction_error[frame.subframes[ch].best.order - 1] / lpc_ctx.autocorr_values[0]; + double est = frame.blocksize * (frame.subframes[ch].obits * (1 - err)); + double est1 = frame.blocksize * (frame.subframes[ch].obits * (err)); + if (est < 0 || est1 < 0) return -1; + } +#endif + return frame.subframes[ch].best.type == SubframeType.LPC ? 1 << frame.subframes[ch].best.window : 0; + case WindowMethod.Evaluate2N: + encode_residual_pass1(frame, ch, estimate_best_windows_akaike(frame, ch, 2, true)); + return frame.subframes[ch].best.type == SubframeType.LPC ? 1 << frame.subframes[ch].best.window : 0; + case WindowMethod.Evaluate3N: + encode_residual_pass1(frame, ch, estimate_best_windows_akaike(frame, ch, 3, true)); + return frame.subframes[ch].best.type == SubframeType.LPC ? 1 << frame.subframes[ch].best.window : 0; + case WindowMethod.Evaluate: encode_residual_pass1(frame, ch, -1); - return frame.subframes[ch].best.type == SubframeType.LPC ? frame.subframes[ch].best.window : -1; - case WindowMethod.Search: + return frame.subframes[ch].best.type == SubframeType.LPC ? 1 << frame.subframes[ch].best.window : 0; + case WindowMethod.Search: return -1; } return -1; @@ -1450,18 +1895,36 @@ new int[] { // 30 switch (eparams.stereo_method) { - case StereoMethod.Estimate: + case StereoMethod.Estimate: for (int ch = 0; ch < subframes; ch++) { - LpcContext lpc_ctx = frame.subframes[ch].lpc_ctx[0]; - lpc_ctx.GetReflection(4, frame.subframes[ch].samples, frame.blocksize, frame.window_buffer); - lpc_ctx.SortOrdersAkaike(frame.blocksize, 1, 1, 4, 4.5, 0.0); - frame.subframes[ch].best.size = (uint)Math.Max(0, lpc_ctx.Akaike(frame.blocksize, lpc_ctx.best_orders[0], 4.5, 0.0) + 7.1 * frame.subframes[ch].obits * m_settings.MaxLPCOrder); - } +#if XXX + ulong* fixed_errors = stackalloc ulong[5]; + fixed_compute_best_predictor(frame.subframes[ch].samples + 4, (uint)frame.blocksize - 4, fixed_errors); + int best_order = fixed_compute_best_predictor_order(fixed_errors); + //residual_bits_per_sample[0] = (float)((total_error_0 > 0) ? log(M_LN2 * (FLAC__double)total_error_0 / (FLAC__double)data_len) / M_LN2 : 0.0); + frame.subframes[ch].best.size = (uint)fixed_errors[best_order]; +#else + LpcContext lpc_ctx = frame.subframes[ch].lpc_ctx[0]; + int estimate_order = 4; + int iWindow = 0; + fixed (LpcWindowSection* sections = &windowSections[frame.nSeg, iWindow, 0]) + lpc_ctx.GetReflection( + frame.subframes[ch].sf, estimate_order, + frame.subframes[ch].samples, frame.window_buffer + iWindow * Flake.MAX_BLOCKSIZE * 2, sections, + frame.subframes[ch].obits * 2 + BitReader.log2i(frame.blocksize) >= 61); + lpc_ctx.SortOrdersAkaike(frame.blocksize, 1, 1, estimate_order, 4.5, 0.0); + frame.subframes[ch].best.size + = (uint)Math.Max(0, frame.blocksize * (Math.Log(lpc_ctx.prediction_error[lpc_ctx.best_orders[0] - 1])) + Math.Log(frame.blocksize) * lpc_ctx.best_orders[0] * 4.5 + //= (uint)Math.Max(0, lpc_ctx.Akaike(frame.blocksize, lpc_ctx.best_orders[0], 4.5, 0.0) + //* 2.0 / Math.Log(windowScale[0] / frame.blocksize) + + 7.1 * frame.subframes[ch].obits * m_settings.MaxLPCOrder); +#endif + } break; - case StereoMethod.Evaluate: + case StereoMethod.Evaluate: for (int ch = 0; ch < subframes; ch++) - encode_residual_pass1(frame, ch, 0); + encode_residual_pass1(frame, ch, 1); break; case StereoMethod.Search: for (int ch = 0; ch < subframes; ch++) @@ -1520,7 +1983,7 @@ new int[] { // 30 encode_residual_pass2(frame, ch); } break; - case StereoMethod.Evaluate: + case StereoMethod.Evaluate: for (int ch = 0; ch < channels; ch++) encode_residual_pass2(frame, ch); break; @@ -1538,18 +2001,26 @@ new int[] { // 30 int sz = _windowsize; float* pos1 = window + _windowcount * Flake.MAX_BLOCKSIZE * 2; float* pos = pos1; + int nSeg = 0; do { + windowSections[nSeg, _windowcount, 0].setData(0, sz); + for (int j = 1; j < lpc.MAX_LPC_SECTIONS; j++) + windowSections[nSeg, _windowcount, j].setZero(sz, sz); + + fixed (LpcWindowSection* sections = &windowSections[nSeg, _windowcount, 0]) func(pos, sz); - if ((sz & 1) != 0) + if ((sz & 1) != 0) break; - pos += sz; + nSeg++; + pos += sz; sz >>= 1; } while (sz >= 32); double scale = 0.0; for (int i = 0; i < _windowsize; i++) scale += pos1[i] * pos1[i]; windowScale[_windowcount] = scale; + windowType[_windowcount] = flag; _windowcount++; } @@ -1560,22 +2031,86 @@ new int[] { // 30 { frame.InitSize(m_blockSize, eparams.variable_block_size != 0); - if (frame.blocksize != _windowsize && frame.blocksize > 4) - { - _windowsize = frame.blocksize; - _windowcount = 0; - calculate_window(window, lpc.window_welch, WindowFunction.Welch); - calculate_window(window, lpc.window_tukey, WindowFunction.Tukey); - calculate_window(window, lpc.window_flattop, WindowFunction.Flattop); - calculate_window(window, lpc.window_hann, WindowFunction.Hann); - calculate_window(window, lpc.window_bartlett, WindowFunction.Bartlett); - if (_windowcount == 0) - throw new Exception("invalid windowfunction"); - } + if (frame.blocksize != _windowsize && frame.blocksize > 4) + { + _windowsize = frame.blocksize; + _windowcount = 0; + calculate_window(window, lpc.window_welch, WindowFunction.Welch); + calculate_window(window, lpc.window_tukey, WindowFunction.Tukey); + calculate_window(window, lpc.window_flattop, WindowFunction.Flattop); + calculate_window(window, lpc.window_hann, WindowFunction.Hann); + calculate_window(window, lpc.window_bartlett, WindowFunction.Bartlett); + int tukey_parts = 2; + double overlap = -0.3; + double overlap_units = overlap / (1.0 - overlap); + for (int m = 0; m < tukey_parts; m++) + calculate_window(window, (w, wsz) => + { + lpc.window_punchout_tukey(w, wsz, 0.1, + m / (tukey_parts + overlap_units), + (m + 1 + overlap_units) / (tukey_parts + overlap_units)); + }, WindowFunction.PartialTukey); + + tukey_parts = 3; + overlap = -0.1; + //overlap = 0.1; + overlap_units = overlap / (1.0 - overlap); + for (int m = 0; m < tukey_parts; m++) + calculate_window(window, (w, wsz) => + { + lpc.window_punchout_tukey(w, wsz, 0.1, + m / (tukey_parts + overlap_units), + (m + 1 + overlap_units) / (tukey_parts + overlap_units)); + }, WindowFunction.PunchoutTukey); + if (_windowcount == 0) + throw new Exception("invalid windowfunction"); + int nSeg = 0; + int sz = _windowsize; + float* window_segment = window; + do + { + fixed (LpcWindowSection* sections = &windowSections[nSeg, 0, 0]) + LpcWindowSection.Detect(_windowcount, window_segment, Flake.MAX_BLOCKSIZE * 2, sz, sections); + if ((sz & 1) != 0) + break; + window_segment += sz; + nSeg++; + sz >>= 1; + } while (sz >= 32); +#if NONONO + using (TextWriter tx = File.CreateText("C:\\Temp\\w.csv")) + { + int totaltotal = 0; + for (int i = 0; i < _windowcount; i++) + { + int total = 0; + for (int sec = 0; sec < lpc.MAX_LPC_SECTIONS; sec++) + if (windowSections[0, i, sec].m_type != LpcWindowSection.SectionType.Zero || windowSections[0, i, sec].m_start != windowSections[0, i, sec].m_end) + { + tx.WriteLine("{0}\t{1}\t{2}", windowSections[0, i, sec].m_start, windowSections[0, i, sec].m_end, windowSections[0, i, sec].m_type); + if (windowSections[0, i, sec].m_type != LpcWindowSection.SectionType.One) + total += windowSections[0, i, sec].m_end - windowSections[0, i, sec].m_start; + } + totaltotal += total; + tx.WriteLine("{0} total window data", total); + } + tx.WriteLine("{0} grand total window data", totaltotal); + for (int x = 0; x < frame.blocksize; x++) + { + for (int i = 0; i < _windowcount; i++) + { + tx.Write("{0}\t", window[i * Flake.MAX_BLOCKSIZE * 2 + x]); + } + tx.WriteLine(); + } + } +#endif + } if (channels != 2 || frame.blocksize <= 32 || eparams.stereo_method == StereoMethod.Independent) { frame.window_buffer = window; + frame.nSeg = 0; frame.current.residual = r + channels * Flake.MAX_BLOCKSIZE; frame.ch_mode = channels != 2 ? ChannelMode.NotStereo : ChannelMode.LeftRight; for (int ch = 0; ch < channels; ch++) @@ -1589,6 +2124,7 @@ new int[] { // 30 { //channel_decorrelation(s, s + Flake.MAX_BLOCKSIZE, s + 2 * Flake.MAX_BLOCKSIZE, s + 3 * Flake.MAX_BLOCKSIZE, frame.blocksize); frame.window_buffer = window; + frame.nSeg = 0; frame.current.residual = r + 4 * Flake.MAX_BLOCKSIZE; for (int ch = 0; ch < 4; ch++) frame.subframes[ch].Init(s + ch * Flake.MAX_BLOCKSIZE, r + ch * Flake.MAX_BLOCKSIZE, @@ -1610,6 +2146,7 @@ new int[] { // 30 { frame2.InitSize(frame.blocksize / 2, true); frame2.window_buffer = frame.window_buffer + frame.blocksize; + frame2.nSeg++; frame2.current.residual = r + tumbler * 5 * Flake.MAX_BLOCKSIZE; for (int ch = 0; ch < 4; ch++) frame2.subframes[ch].Init(frame.subframes[ch].samples, frame2.current.residual + (ch + 1) * frame2.blocksize, @@ -1621,6 +2158,7 @@ new int[] { // 30 { frame3.InitSize(frame2.blocksize, true); frame3.window_buffer = frame2.window_buffer; + frame3.nSeg = frame2.nSeg; frame3.current.residual = frame2.current.residual + 5 * frame2.blocksize; for (int ch = 0; ch < 4; ch++) frame3.subframes[ch].Init(frame2.subframes[ch].samples + frame2.blocksize, frame3.current.residual + (ch + 1) * frame3.blocksize, @@ -2071,12 +2609,12 @@ new int[] { // 30 public int flake_set_defaults(FlakeWriterSettings settings) { // default to level 7 params - window_function = WindowFunction.Flattop | WindowFunction.Tukey; + window_function = WindowFunction.PunchoutTukey | WindowFunction.PartialTukey | WindowFunction.Tukey; order_method = OrderMethod.Akaike; stereo_method = StereoMethod.Evaluate; - window_method = WindowMethod.Evaluate; + window_method = WindowMethod.EvaluateN; block_time_ms = 105; - prediction_type = PredictionType.Search; + prediction_type = PredictionType.Levinson; estimation_depth = 1; variable_block_size = 0; lpc_min_precision_search = 1; @@ -2088,37 +2626,45 @@ new int[] { // 30 switch (settings.EncoderModeIndex) { case 0: - block_time_ms = 53; prediction_type = PredictionType.Fixed; stereo_method = StereoMethod.Independent; + window_method = WindowMethod.Estimate; + window_function = WindowFunction.Tukey; break; case 1: - prediction_type = PredictionType.Levinson; + prediction_type = PredictionType.Fixed; stereo_method = StereoMethod.Independent; - window_function = WindowFunction.Bartlett; + window_method = WindowMethod.Estimate; + window_function = WindowFunction.Tukey; break; case 2: - stereo_method = StereoMethod.Independent; - window_function = WindowFunction.Bartlett; + stereo_method = StereoMethod.Estimate; + window_method = WindowMethod.Estimate; + window_function = WindowFunction.PartialTukey; break; case 3: stereo_method = StereoMethod.Estimate; - window_function = WindowFunction.Bartlett; + window_method = WindowMethod.Estimate; + window_function = WindowFunction.PunchoutTukey; break; case 4: stereo_method = StereoMethod.Estimate; - window_function = WindowFunction.Bartlett; + window_method = WindowMethod.Estimate; + window_function = WindowFunction.PunchoutTukey; + estimation_depth = 2; break; case 5: - stereo_method = StereoMethod.Estimate; window_method = WindowMethod.Estimate; + window_function = WindowFunction.PunchoutTukey; break; case 6: - stereo_method = StereoMethod.Estimate; + window_method = WindowMethod.EstimateN; + window_function = WindowFunction.PunchoutTukey | WindowFunction.PartialTukey; break; case 7: break; case 8: + prediction_type = PredictionType.Search; estimation_depth = 2; lpc_min_precision_search = 0; break; diff --git a/CUETools.Codecs.FLAKE/StereoMethod.cs b/CUETools.Codecs.FLAKE/StereoMethod.cs index fd511e3..7a5987d 100644 --- a/CUETools.Codecs.FLAKE/StereoMethod.cs +++ b/CUETools.Codecs.FLAKE/StereoMethod.cs @@ -6,6 +6,6 @@ namespace CUETools.Codecs.FLAKE Independent = 0, Estimate = 1, Evaluate = 2, - Search = 3 + Search = 3, } } diff --git a/CUETools.Codecs.FLAKE/WindowFunction.cs b/CUETools.Codecs.FLAKE/WindowFunction.cs index af5fb31..2d8b25c 100644 --- a/CUETools.Codecs.FLAKE/WindowFunction.cs +++ b/CUETools.Codecs.FLAKE/WindowFunction.cs @@ -2,11 +2,17 @@ { public enum WindowFunction { + None = 0, Welch = 1, Tukey = 2, Hann = 4, Flattop = 8, Bartlett = 16, - TukeyFlattop = 10 + TukeyFlattop = 10, + PartialTukey = 32, + TukeyPartialTukey = 34, + TukeyFlattopPartialTukey = 42, + PunchoutTukey = 64, + TukeyFlattopPartialTukeyPunchoutTukey = 106, } } diff --git a/CUETools.Codecs.FLAKE/WindowMethod.cs b/CUETools.Codecs.FLAKE/WindowMethod.cs index f05c8e1..e8e7e90 100644 --- a/CUETools.Codecs.FLAKE/WindowMethod.cs +++ b/CUETools.Codecs.FLAKE/WindowMethod.cs @@ -2,8 +2,16 @@ { public enum WindowMethod { - Estimate = 0, - Evaluate = 1, - Search = 2 + Evaluate, + Search, + Estimate, + Estimate2, + Estimate3, + EstimateN, + Evaluate2, + Evaluate2N, + Evaluate3, + Evaluate3N, + EvaluateN, } } diff --git a/CUETools.Codecs/AudioEncoderSettings.cs b/CUETools.Codecs/AudioEncoderSettings.cs index ed4360c..a342012 100644 --- a/CUETools.Codecs/AudioEncoderSettings.cs +++ b/CUETools.Codecs/AudioEncoderSettings.cs @@ -73,6 +73,19 @@ namespace CUETools.Codecs } } + public bool HasDefaultValuesForMode(int index) + { + bool res = true; + foreach (PropertyDescriptor property in TypeDescriptor.GetProperties(this)) + foreach (var attribute in property.Attributes) + if (attribute is DefaultValueForModeAttribute) + { + var defaultValueForMode = attribute as DefaultValueForModeAttribute; + res &= (int)property.GetValue(this) == defaultValueForMode.m_values[index]; + } + return res; + } + [Browsable(false)] [XmlIgnore] public AudioPCMConfig PCM diff --git a/CUETools.Codecs/LPC.cs b/CUETools.Codecs/LPC.cs index c457ed6..4ad1168 100644 --- a/CUETools.Codecs/LPC.cs +++ b/CUETools.Codecs/LPC.cs @@ -5,8 +5,9 @@ namespace CUETools.Codecs public class lpc { public const int MAX_LPC_ORDER = 32; - public const int MAX_LPC_WINDOWS = 2; + public const int MAX_LPC_WINDOWS = 8; public const int MAX_LPC_PRECISIONS = 4; + public const int MAX_LPC_SECTIONS = 32; public unsafe static void window_welch(float* window, int L) { @@ -21,7 +22,7 @@ namespace CUETools.Codecs } } - public unsafe static void window_bartlett(float* window, int L) + public unsafe static void window_bartlett(float* window, int L) { int N = L - 1; double N2 = (double)N / 2.0; @@ -33,35 +34,65 @@ namespace CUETools.Codecs } } - public unsafe static void window_rectangle(float* window, int L) + public unsafe static void window_rectangle(float* window, int L) { for (int n = 0; n < L; n++) window[n] = 1.0F; } - public unsafe static void window_flattop(float* window, int L) + public unsafe static void window_flattop(float* window, int L) { int N = L - 1; for (int n = 0; n < L; n++) window[n] = (float)(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)); } - public unsafe static void window_tukey(float* window, int L) + public unsafe static void window_tukey(float* window, int L) { - window_rectangle(window, L); double p = 0.5; - int Np = (int)(p / 2.0 * L) - 1; + int z = 0; + int Np = (int)(p / 2.0 * L) - z; if (Np > 0) { - for (int n = 0; n <= Np; n++) - { - window[n] = (float)(0.5 - 0.5 * Math.Cos(Math.PI * n / Np)); - window[L - Np - 1 + n] = (float)(0.5 - 0.5 * Math.Cos(Math.PI * (n + Np) / Np)); - } + for (int n = 0; n < z; n++) + window[n] = window[L - n - 1] = 0; + for (int n = 0; n < Np - 1; n++) + window[n + z] = window[L - n - 1 - z] = (float)(0.5 - 0.5 * Math.Cos(Math.PI * (n + 1) / Np)); + for (int n = z + Np - 1; n < L - z - Np + 1; n++) + window[n] = 1.0F; } } - public unsafe static void window_hann(float* window, int L) + public unsafe static void window_punchout_tukey(float* window, int L, double p, double start, double end) + { + int start_n = (int)(start * L); + int end_n = (int)(end * L); + int Np = (int)(p / 2.0 * L); + int i, n = 0; + + if (start_n != 0) + { + for (i = 1; n < Np; n++, i++) + window[n] = (float)(0.5 - 0.5 * Math.Cos(Math.PI * i / Np)); + for (; n < start_n - Np; n++) + window[n] = 1.0f; + for (i = Np; n < start_n; n++, i--) + window[n] = (float)(0.5 - 0.5 * Math.Cos(Math.PI * i / Np)); + } + for (; n < end_n; n++) + window[n] = 0.0f; + if (end_n != L) + { + for (i = 1; n < end_n + Np; n++, i++) + window[n] = (float)(0.5 - 0.5 * Math.Cos(Math.PI * i / Np)); + for (; n < L - Np; n++) + window[n] = 1.0f; + for (i = Np; n < L; n++, i--) + window[n] = (float)(0.5 - 0.5 * Math.Cos(Math.PI * i / Np)); + } + } + + public unsafe static void window_hann(float* window, int L) { int N = L - 1; for (int n = 0; n < L; n++) @@ -73,6 +104,7 @@ namespace CUETools.Codecs return (short)((val >> 31) + ((val - 1) >> 31) + 1); } +#if XXX static public unsafe void compute_corr_int(/*const*/ short* data1, short* data2, int len, int min, int lag, int* autoc) { @@ -92,13 +124,14 @@ namespace CUETools.Codecs autoc[i] = temp + temp2; } } +#endif /** * Calculates autocorrelation data from audio samples * A window function is applied before calculation. */ static public unsafe void - compute_autocorr(/*const*/ int* data, int len, int min, int lag, double* autoc, float* window) + compute_autocorr(/*const*/ int* data, float* window, int len, int min, int lag, double* autoc, int prev, int next) { #if FPAC short* data1 = stackalloc short[len + 1]; @@ -125,30 +158,224 @@ namespace CUETools.Codecs for (int coeff = min; coeff <= lag; coeff++) autoc[coeff] = (c1[coeff] * (double)(1 << 18) + (c2[coeff] + c3[coeff]) * (double)(1 << 9) + c4[coeff]); #else - double* data1 = stackalloc double[(int)len + 16]; - int i; +#if XXX + if (min == 0 && lag >= 4) + { + int* pdata = data; + float* pwindow = window; - for (i = 0; i < len; i++) - data1[i] = data[i] * window[i]; - data1[len] = 0; + double temp0 = 1.0; + double temp1 = 1.0; + double temp2 = 1.0; + double temp3 = 1.0; + double temp4 = 1.0; - for (i = min; i <= lag; ++i) + double c0 = *(pdata++) * *(pwindow++); + float c1 = *(pdata++) * *(pwindow++); + float c2 = *(pdata++) * *(pwindow++); + float c3 = *(pdata++) * *(pwindow++); + float c4 = *(pdata++) * *(pwindow++); + + int* finish = data + len; + + while (pdata <= finish) + { + temp0 += c0 * c0; + temp1 += c0 * c1; + temp2 += c0 * c2; + temp3 += c0 * c3; + temp4 += c0 * c4; + + c0 = c1; + c1 = c2; + c2 = c3; + c3 = c4; + c4 = *(pdata++) * *(pwindow++); + } + + temp0 += c0 * c0; + temp1 += c0 * c1; + temp2 += c0 * c2; + temp3 += c0 * c3; + c0 = c1; + c1 = c2; + c2 = c3; + temp0 += c0 * c0; + temp1 += c0 * c1; + temp2 += c0 * c2; + c0 = c1; + c1 = c2; + temp0 += c0 * c0; + temp1 += c0 * c1; + c0 = c1; + temp0 += c0 * c0; + + autoc[0] += temp0; + autoc[1] += temp1; + autoc[2] += temp2; + autoc[3] += temp3; + autoc[4] += temp4; + min = 5; + + if (lag < min) return; + } +#endif + double* data1 = stackalloc double[lag + len + lag]; + int i; + + for (i = 0; i < lag; i++) + data1[i] = prev != 0 ? data[i - lag] : 0; + for (i = 0; i < len; i++) + data1[lag + i] = data[i] * window[i]; + for (i = 0; i < lag; i++) + data1[lag + len + i] = next != 0 ? data[len + i] : 0; + + for (i = min; i <= lag; ++i) { - double temp = 1.0; - double temp2 = 1.0; - double* finish = data1 + len - i; + double temp = 0; + double temp2 = 0; + double* pdata = data1 + lag - i; + double* finish = data1 + lag + len - 1; - for (double* pdata = data1; pdata < finish; pdata += 2) - { - temp += pdata[i] * pdata[0]; - temp2 += pdata[i + 1] * pdata[1]; - } - autoc[i] = temp + temp2; + while (pdata < finish) + { + temp += pdata[i] * (*pdata++); + temp2 += pdata[i] * (*pdata++); + } + if (pdata <= finish) + temp += pdata[i] * (*pdata++); + + autoc[i] += temp + temp2; } #endif } - /** + static public unsafe void + compute_autocorr_windowless(/*const*/ int* data, int len, int min, int lag, double* autoc) + { + // if databits*2 + log2(len) <= 64 +#if !XXX +#if XXX + if (min == 0 && lag >= 4) + { + long temp0 = 0; + long temp1 = 0; + long temp2 = 0; + long temp3 = 0; + long temp4 = 0; + int* pdata = data; + int* finish = data + len - 4; + while (pdata < finish) + { + long c0 = *(pdata++); + temp0 += c0 * c0; + temp1 += c0 * pdata[0]; + temp2 += c0 * pdata[1]; + temp3 += c0 * pdata[2]; + temp4 += c0 * pdata[3]; + } + { + long c0 = *(pdata++); + temp0 += c0 * c0; + temp1 += c0 * pdata[0]; + temp2 += c0 * pdata[1]; + temp3 += c0 * pdata[2]; + } + { + long c0 = *(pdata++); + temp0 += c0 * c0; + temp1 += c0 * pdata[0]; + temp2 += c0 * pdata[1]; + } + { + long c0 = *(pdata++); + temp0 += c0 * c0; + temp1 += c0 * pdata[0]; + } + { + long c0 = *(pdata++); + temp0 += c0 * c0; + } + autoc[0] += temp0; + autoc[1] += temp1; + autoc[2] += temp2; + autoc[3] += temp3; + autoc[4] += temp4; + min = 5; + + if (lag < min) return; + } +#endif + for (int i = min; i <= lag; ++i) + { + long temp = 0; + long temp2 = 0; + int* pdata = data; + int* finish = data + len - i - 1; + while (pdata < finish) + { + temp += (long)pdata[i] * (*pdata++); + temp2 += (long)pdata[i] * (*pdata++); + } + if (pdata <= finish) + temp += (long)pdata[i] * (*pdata++); + autoc[i] += temp + temp2; + } +#else + for (int i = min; i <= lag; ++i) + { + double temp = 0; + double temp2 = 0; + int* pdata = data; + int* finish = data + len - i - 1; + + while (pdata < finish) + { + temp += (double)pdata[i] * (double)(*pdata++); + temp2 += (double)pdata[i] * (double)(*pdata++); + } + if (pdata <= finish) + temp += (double)pdata[i] * (double)(*pdata++); + autoc[i] += temp + temp2; + } +#endif + } + + static public unsafe void + compute_autocorr_windowless_large(/*const*/ int* data, int len, int min, int lag, double* autoc) + { + for (int i = min; i <= lag; ++i) + { + double temp = 0; + double temp2 = 0; + int* pdata = data; + int* finish = data + len - i - 1; + while (pdata < finish) + { + temp += (long)pdata[i] * (*pdata++); + temp2 += (long)pdata[i] * (*pdata++); + } + if (pdata <= finish) + temp += (long)pdata[i] * (*pdata++); + autoc[i] += temp + temp2; + } + } + + static public unsafe void + compute_autocorr_glue(/*const*/ int* data, int min, int lag, double* autoc) + { + for (int i = min; i <= lag; ++i) + { + long temp = 0; + int* pdata = data - i; + int* finish = data; + while (pdata < finish) + temp += (long)pdata[i] * (*pdata++); + autoc[i] += temp; + } + } + + /** * Levinson-Durbin recursion. * Produces LPC coefficients from autocorrelation data. */ diff --git a/CUETools.Codecs/LpcContext.cs b/CUETools.Codecs/LpcContext.cs index fc6b970..6f15073 100644 --- a/CUETools.Codecs/LpcContext.cs +++ b/CUETools.Codecs/LpcContext.cs @@ -1,7 +1,173 @@ using System; +using System.Collections.Generic; namespace CUETools.Codecs { + unsafe public class LpcSubframeInfo + { + public LpcSubframeInfo() + { + autocorr_section_values = new double[lpc.MAX_LPC_SECTIONS, lpc.MAX_LPC_ORDER + 1]; + autocorr_section_orders = new int[lpc.MAX_LPC_SECTIONS]; + } + + // public LpcContext[] lpc_ctx; + public double[,] autocorr_section_values; + public int[] autocorr_section_orders; + //public int obits; + + public void Reset() + { + for (int sec = 0; sec < autocorr_section_orders.Length; sec++) + autocorr_section_orders[sec] = 0; + } + } + + unsafe public struct LpcWindowSection + { + public enum SectionType + { + Zero, + One, + Data, + Glue + }; + public int m_start; + public int m_end; + public SectionType m_type; + public int m_id; + public LpcWindowSection(int end) + { + m_id = -1; + m_start = 0; + m_end = end; + m_type = SectionType.Data; + } + public void setData(int start, int end) + { + m_id = -1; + m_start = start; + m_end = end; + m_type = SectionType.Data; + } + public void setOne(int start, int end) + { + m_id = -1; + m_start = start; + m_end = end; + m_type = SectionType.One; + } + public void setGlue(int start) + { + m_id = -1; + m_start = start; + m_end = start; + m_type = SectionType.Glue; + } + public void setZero(int start, int end) + { + m_id = -1; + m_start = start; + m_end = end; + m_type = SectionType.Zero; + } + + unsafe public static void Detect(int _windowcount, float* window_segment, int stride, int sz, LpcWindowSection* sections) + { + int section_id = 0; + var boundaries = new List(); + var types = new LpcWindowSection.SectionType[_windowcount, lpc.MAX_LPC_SECTIONS * 2]; + for (int x = 0; x < sz; x++) + { + for (int i = 0; i < _windowcount; i++) + { + float w = window_segment[i * stride + x]; + types[i, boundaries.Count] = + boundaries.Count >= lpc.MAX_LPC_SECTIONS * 2 - 2 ? + LpcWindowSection.SectionType.Data : w == 0.0 ? + LpcWindowSection.SectionType.Zero : w == 1.0 ? + LpcWindowSection.SectionType.One : + LpcWindowSection.SectionType.Data; + } + bool isBoundary = false; + for (int i = 0; i < _windowcount; i++) + { + isBoundary |= boundaries.Count == 0 || + types[i, boundaries.Count - 1] != types[i, boundaries.Count]; + } + if (isBoundary) boundaries.Add(x); + } + boundaries.Add(sz); + var ones = new int[boundaries.Count - 1]; + // Reconstruct segments list. + for (int i = 0; i < _windowcount; i++) + { + int secs = 0; + for (int j = 0; j < boundaries.Count - 1; j++) + { + if (types[i, j] == LpcWindowSection.SectionType.Zero) + { + if (secs > 0 && sections[i * lpc.MAX_LPC_SECTIONS + secs - 1].m_end == boundaries[j] && sections[i * lpc.MAX_LPC_SECTIONS + secs - 1].m_type == LpcWindowSection.SectionType.Zero) + { + sections[i * lpc.MAX_LPC_SECTIONS + secs - 1].m_end = boundaries[j + 1]; + continue; + } + sections[i * lpc.MAX_LPC_SECTIONS + secs++].setZero(boundaries[j], boundaries[j + 1]); + continue; + } + if (types[i, j] == LpcWindowSection.SectionType.Data + || secs + 1 >= lpc.MAX_LPC_SECTIONS + || (boundaries[j + 1] - boundaries[j] < lpc.MAX_LPC_ORDER)) + { + if (secs > 0 && sections[i * lpc.MAX_LPC_SECTIONS + secs - 1].m_end == boundaries[j] && sections[i * lpc.MAX_LPC_SECTIONS + secs - 1].m_type == LpcWindowSection.SectionType.Data) + { + sections[i * lpc.MAX_LPC_SECTIONS + secs - 1].m_end = boundaries[j + 1]; + continue; + } + sections[i * lpc.MAX_LPC_SECTIONS + secs++].setData(boundaries[j], boundaries[j + 1]); + continue; + } + + if (secs > 0 && sections[i * lpc.MAX_LPC_SECTIONS + secs - 1].m_end == boundaries[j] && sections[i * lpc.MAX_LPC_SECTIONS + secs - 1].m_type == LpcWindowSection.SectionType.One) + sections[i * lpc.MAX_LPC_SECTIONS + secs++].setGlue(boundaries[j]); + sections[i * lpc.MAX_LPC_SECTIONS + secs++].setOne(boundaries[j], boundaries[j + 1]); + ones[j] |= 1 << i; + } + while (secs < lpc.MAX_LPC_SECTIONS) + sections[i * lpc.MAX_LPC_SECTIONS + secs++].setZero(sz, sz); + } + for (int j = 0; j < boundaries.Count - 1; j++) + { + if (j > 0 && ones[j - 1] == ones[j]) + { + for (int i = 0; i < _windowcount; i++) + { + for (int sec = 0; sec < lpc.MAX_LPC_SECTIONS; sec++) + if (sections[i * lpc.MAX_LPC_SECTIONS + sec].m_type == LpcWindowSection.SectionType.Glue && + sections[i * lpc.MAX_LPC_SECTIONS + sec].m_start == boundaries[j]) + { + sections[i * lpc.MAX_LPC_SECTIONS + sec - 1].m_end = sections[i * lpc.MAX_LPC_SECTIONS + sec + 1].m_end; + for (int sec1 = sec; sec1 + 2 < lpc.MAX_LPC_SECTIONS; sec1++) + sections[i * lpc.MAX_LPC_SECTIONS + sec1] = sections[i * lpc.MAX_LPC_SECTIONS + sec1 + 2]; + } + } + continue; + } + if ((ones[j] & (ones[j] - 1)) != 0 && section_id < lpc.MAX_LPC_SECTIONS) + { + for (int i = 0; i < _windowcount; i++) + for (int sec = 0; sec < lpc.MAX_LPC_SECTIONS; sec++) + if (sections[i * lpc.MAX_LPC_SECTIONS + sec].m_type == LpcWindowSection.SectionType.One && + sections[i * lpc.MAX_LPC_SECTIONS + sec].m_start == boundaries[j]) + { + sections[i * lpc.MAX_LPC_SECTIONS + sec].m_id = section_id; + } + section_id++; + } + } + } + } + /// /// Context for LPC coefficients calculation and order estimation /// @@ -36,18 +202,62 @@ namespace CUETools.Codecs /// Samples pointer /// Block size /// Window function - public void GetReflection(int order, int* samples, int blocksize, float* window) + public void GetReflection(LpcSubframeInfo subframe, int order, int* samples, float* window, LpcWindowSection* sections, bool large) { if (autocorr_order > order) return; fixed (double* reff = reflection_coeffs, autoc = autocorr_values, err = prediction_error) { - lpc.compute_autocorr(samples, blocksize, autocorr_order, order, autoc, window); + for (int i = autocorr_order; i <= order; i++) autoc[i] = 0; + int prev = 0; + for (int section = 0; section < lpc.MAX_LPC_SECTIONS; section++) + { + if (sections[section].m_type == LpcWindowSection.SectionType.Zero) + { + prev = 0; + continue; + } + if (sections[section].m_type == LpcWindowSection.SectionType.Data) + { + int next = section + 1 < lpc.MAX_LPC_SECTIONS && sections[section + 1].m_type == LpcWindowSection.SectionType.One ? 1 : 0; + lpc.compute_autocorr(samples + sections[section].m_start, window + sections[section].m_start, sections[section].m_end - sections[section].m_start, autocorr_order, order, autoc, prev, next); + } + else if (sections[section].m_type == LpcWindowSection.SectionType.Glue) + lpc.compute_autocorr_glue(samples + sections[section].m_start, autocorr_order, order, autoc); + else if (sections[section].m_type == LpcWindowSection.SectionType.One) + { + if (sections[section].m_id >= 0) + { + if (subframe.autocorr_section_orders[sections[section].m_id] <= order) + { + fixed (double* autocsec = &subframe.autocorr_section_values[sections[section].m_id, 0]) + { + for (int i = subframe.autocorr_section_orders[sections[section].m_id]; i <= order; i++) autocsec[i] = 0; + if (large) + lpc.compute_autocorr_windowless_large(samples + sections[section].m_start, sections[section].m_end - sections[section].m_start, subframe.autocorr_section_orders[sections[section].m_id], order, autocsec); + else + lpc.compute_autocorr_windowless(samples + sections[section].m_start, sections[section].m_end - sections[section].m_start, subframe.autocorr_section_orders[sections[section].m_id], order, autocsec); + } + subframe.autocorr_section_orders[sections[section].m_id] = order + 1; + } + for (int i = autocorr_order; i <= order; i++) + autoc[i] += subframe.autocorr_section_values[sections[section].m_id, i]; + } + else + { + if (large) + lpc.compute_autocorr_windowless_large(samples + sections[section].m_start, sections[section].m_end - sections[section].m_start, autocorr_order, order, autoc); + else + lpc.compute_autocorr_windowless(samples + sections[section].m_start, sections[section].m_end - sections[section].m_start, autocorr_order, order, autoc); + } + prev = 1; + } + } lpc.compute_schur_reflection(autoc, (uint)order, reff, err); autocorr_order = order + 1; } } - +#if XXX public void GetReflection1(int order, int* samples, int blocksize, float* window) { if (autocorr_order > order) @@ -83,11 +293,12 @@ namespace CUETools.Codecs autocorr_order = order + 1; } } - +#endif public double Akaike(int blocksize, int order, double alpha, double beta) { //return (blocksize - order) * (Math.Log(prediction_error[order - 1]) - Math.Log(1.0)) + Math.Log(blocksize) * order * (alpha + beta * order); - return blocksize * Math.Log(prediction_error[order - 1]) + Math.Log(blocksize) * order * (alpha + beta * order); + //return blocksize * (Math.Log(prediction_error[order - 1]) - Math.Log(autocorr_values[0]) / 2) + Math.Log(blocksize) * order * (alpha + beta * order); + return blocksize * (Math.Log(prediction_error[order - 1])) + Math.Log(blocksize) * order * (alpha + beta * order); } /// diff --git a/CUETools.Flake/Program.cs b/CUETools.Flake/Program.cs index cec90f6..86c5b24 100644 --- a/CUETools.Flake/Program.cs +++ b/CUETools.Flake/Program.cs @@ -475,25 +475,45 @@ namespace CUETools.FlakeExe if (debug) { + settings = flake.Settings as FlakeWriterSettings; Console.SetOut(stdout); - Console.Out.WriteLine("{0}\t{1:0.00}\t{2}\t{3}\t{4}\t{5}\t{6}..{7}\t{8}..{9}\t{10}..{11}\t{12}..{13}\t{14}\t{15}\t{16}", + Console.Out.WriteLine("{17}\t{0}\t{1:0.00}\t{2}\t{3}\t{4}\t{5}\t{6}..{7}\t{8}..{9}\t{10}..{11}\t{12}..{13}\t{14}\t{15}\t{16}\t{18}", flake.TotalSize, flake.UserProcessorTime.TotalSeconds > 0 ? flake.UserProcessorTime.TotalSeconds : totalElapsed.TotalSeconds, flake.PredictionType.ToString().PadRight(15), flake.StereoMethod.ToString().PadRight(15), (flake.OrderMethod.ToString() + "(" + flake.EstimationDepth.ToString() + ")").PadRight(15), - flake.WindowFunction, - (flake.Settings as FlakeWriterSettings).MinPartitionOrder, - (flake.Settings as FlakeWriterSettings).MaxPartitionOrder, - (flake.Settings as FlakeWriterSettings).MinLPCOrder, - (flake.Settings as FlakeWriterSettings).MaxLPCOrder, - (flake.Settings as FlakeWriterSettings).MinFixedOrder, - (flake.Settings as FlakeWriterSettings).MaxFixedOrder, + (flake.WindowMethod.ToString().PadRight(10) + "(" + + ((flake.WindowFunction & WindowFunction.Tukey) != 0 ? "T" : " ") + + ((flake.WindowFunction & WindowFunction.PartialTukey) != 0 ? "P" : " ") + + (((flake.WindowFunction & WindowFunction.PunchoutTukey) != 0 ? "O" : "") + + ((flake.WindowFunction & WindowFunction.Welch) == 0 ? "" : "W") + + ((flake.WindowFunction & WindowFunction.Hann) == 0 ? "" : "H") + + ((flake.WindowFunction & WindowFunction.Flattop) == 0 ? "" : "F") + + ((flake.WindowFunction & WindowFunction.Bartlett) == 0 ? "" : "B")).PadRight(1)) + +")", + settings.MinPartitionOrder, + settings.MaxPartitionOrder, + settings.MinLPCOrder, + settings.MaxLPCOrder, + settings.MinFixedOrder, + settings.MaxFixedOrder, flake.MinPrecisionSearch, flake.MaxPrecisionSearch, flake.Settings.BlockSize, flake.VBRMode, - coeffs ?? "" + coeffs ?? "", + audioSource.Position * audioSource.PCM.BlockAlign, + (flake.PredictionType == PredictionType.Search && flake.StereoMethod == StereoMethod.Evaluate && flake.WindowMethod == WindowMethod.EvaluateN && flake.WindowFunction == (WindowFunction.PunchoutTukey | WindowFunction.PartialTukey | WindowFunction.Tukey) && flake.EstimationDepth == 2 && flake.MinPrecisionSearch == 0 && settings.HasDefaultValuesForMode(8)) ? "8" : + (flake.PredictionType == PredictionType.Levinson && flake.StereoMethod == StereoMethod.Evaluate && flake.WindowMethod == WindowMethod.EvaluateN && flake.WindowFunction == (WindowFunction.PunchoutTukey | WindowFunction.PartialTukey | WindowFunction.Tukey) && flake.EstimationDepth == 1 && flake.MinPrecisionSearch == 1 && settings.HasDefaultValuesForMode(7)) ? "7" : + (flake.PredictionType == PredictionType.Levinson && flake.StereoMethod == StereoMethod.Evaluate && flake.WindowMethod == WindowMethod.EstimateN && flake.WindowFunction == (WindowFunction.PunchoutTukey | WindowFunction.PartialTukey) && flake.EstimationDepth == 1 && flake.MinPrecisionSearch == 1 && settings.HasDefaultValuesForMode(6)) ? "6" : + (flake.PredictionType == PredictionType.Levinson && flake.StereoMethod == StereoMethod.Evaluate && flake.WindowMethod == WindowMethod.Estimate && flake.WindowFunction == WindowFunction.PunchoutTukey && flake.EstimationDepth == 1 && flake.MinPrecisionSearch == 1 && settings.HasDefaultValuesForMode(5)) ? "5" : + (flake.PredictionType == PredictionType.Levinson && flake.StereoMethod == StereoMethod.Estimate && flake.WindowMethod == WindowMethod.Estimate && flake.WindowFunction == WindowFunction.PunchoutTukey && flake.EstimationDepth == 2 && flake.MinPrecisionSearch == 1 && settings.HasDefaultValuesForMode(4)) ? "4" : + (flake.PredictionType == PredictionType.Levinson && flake.StereoMethod == StereoMethod.Estimate && flake.WindowMethod == WindowMethod.Estimate && flake.WindowFunction == WindowFunction.PunchoutTukey && flake.EstimationDepth == 1 && flake.MinPrecisionSearch == 1 && settings.HasDefaultValuesForMode(3)) ? "3" : + (flake.PredictionType == PredictionType.Levinson && flake.StereoMethod == StereoMethod.Estimate && flake.WindowMethod == WindowMethod.Estimate && flake.WindowFunction == WindowFunction.PartialTukey && flake.EstimationDepth == 1 && flake.MinPrecisionSearch == 1 && settings.HasDefaultValuesForMode(2)) ? "2" : + (flake.PredictionType == PredictionType.Fixed && flake.StereoMethod == StereoMethod.Independent && flake.WindowMethod == WindowMethod.Estimate && flake.MinPrecisionSearch == 1 && flake.WindowFunction == WindowFunction.Tukey && settings.HasDefaultValuesForMode(1)) ? "1" : + (flake.PredictionType == PredictionType.Fixed && flake.StereoMethod == StereoMethod.Independent && flake.WindowMethod == WindowMethod.Estimate && flake.MinPrecisionSearch == 1 && flake.WindowFunction == WindowFunction.Tukey && settings.HasDefaultValuesForMode(0)) ? "0" : + "?" ); } #endif