diff --git a/CUETools.Codecs.ALAC/ALACWriter.cs b/CUETools.Codecs.ALAC/ALACWriter.cs index e167f43..7c9e8b2 100644 --- a/CUETools.Codecs.ALAC/ALACWriter.cs +++ b/CUETools.Codecs.ALAC/ALACWriter.cs @@ -91,6 +91,7 @@ namespace CUETools.Codecs.ALAC int[] verifyBuffer; int[] residualBuffer; float[] windowBuffer; + WindowFunction[] windowType; LpcWindowSection[,] windowSections; int samplesInBuffer = 0; @@ -124,6 +125,7 @@ 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 * lpc.MAX_LPC_WINDOWS]; + windowType = new WindowFunction[lpc.MAX_LPC_WINDOWS]; windowSections = new LpcWindowSection[lpc.MAX_LPC_WINDOWS, lpc.MAX_LPC_SECTIONS]; eparams.set_defaults(m_settings.EncoderModeIndex); @@ -836,7 +838,7 @@ namespace CUETools.Codecs.ALAC } } - unsafe void encode_residual(ALACFrame frame, int ch, int pass, int best_window) + unsafe void encode_residual(ALACFrame frame, int ch, int pass, int best_windows) { int* smp = frame.subframes[ch].samples; int i, n = frame.blocksize; @@ -872,16 +874,15 @@ namespace CUETools.Codecs.ALAC for (int iWindow = 0; iWindow < _windowcount; iWindow++) { - if (best_window != -1 && iWindow != best_window) + if (0 == (best_windows & (1 << iWindow))) continue; LpcContext lpc_ctx = frame.subframes[ch].lpc_ctx[iWindow]; 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); + frame.subframes[ch].sf, max_order, frame.blocksize, smp, + frame.window_buffer + iWindow * Alac.MAX_BLOCKSIZE * 2, sections); 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++) @@ -921,7 +922,7 @@ namespace CUETools.Codecs.ALAC bitwriter.flush(); } - unsafe void encode_residual_pass1(ALACFrame frame, int ch, int best_window) + unsafe void encode_residual_pass1(ALACFrame frame, int ch, int best_windows) { int max_prediction_order = eparams.max_prediction_order; int estimation_depth = eparams.estimation_depth; @@ -931,7 +932,7 @@ namespace CUETools.Codecs.ALAC eparams.estimation_depth = 1; eparams.min_modifier = eparams.max_modifier; eparams.adaptive_passes = 0; - encode_residual(frame, ch, 1, best_window); + encode_residual(frame, ch, 1, best_windows); eparams.max_prediction_order = max_prediction_order; eparams.estimation_depth = estimation_depth; eparams.min_modifier = min_modifier; @@ -943,48 +944,89 @@ namespace CUETools.Codecs.ALAC encode_residual(frame, ch, 2, estimate_best_window(frame, ch)); } + unsafe int estimate_best_windows_akaike(ALACFrame 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]++; + } + + int order = Math.Min(4, eparams.max_prediction_order); + float* err = stackalloc float[lpc.MAX_LPC_ORDER]; + for (int i = 0; i < _windowcount; i++) + { + LpcContext lpc_ctx = frame.subframes[ch].lpc_ctx[i]; + fixed (LpcWindowSection* sections = &windowSections[i, 0]) + lpc_ctx.GetReflection( + frame.subframes[ch].sf, order, frame.blocksize, + frame.subframes[ch].samples, + frame.window_buffer + i * Alac.MAX_BLOCKSIZE * 2, sections); + 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); + } + 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_window(ALACFrame frame, int ch) { if (_windowcount == 1) - return 0; + return 1; switch (eparams.window_method) { case WindowMethod.Estimate: - { - int order = 4; - float* err = stackalloc float[lpc.MAX_LPC_ORDER]; - for (int i = 0; i < _windowcount; i++) - { - 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); - } - 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: + return estimate_best_windows_akaike(frame, ch, 1, false); + case WindowMethod.EstimateN: + return estimate_best_windows_akaike(frame, ch, 1, true); + case WindowMethod.EvaluateN: + encode_residual_pass1(frame, ch, estimate_best_windows_akaike(frame, ch, 1, true)); + return 1 << frame.subframes[ch].best.window; + case WindowMethod.Evaluate: encode_residual_pass1(frame, ch, -1); - return frame.subframes[ch].best.window; + return 1 << frame.subframes[ch].best.window; case WindowMethod.Search: return -1; } @@ -1006,17 +1048,16 @@ namespace CUETools.Codecs.ALAC double alpha = 1.5; // 4.5 + eparams.max_prediction_order / 10.0; fixed (LpcWindowSection* sections = &windowSections[iWindow, 0]) lpc_ctx.GetReflection( - frame.subframes[ch].sf, stereo_order, + frame.subframes[ch].sf, stereo_order, frame.blocksize, frame.subframes[ch].samples, - frame.window_buffer + iWindow * Alac.MAX_BLOCKSIZE * 2, sections, - Settings.PCM.BitsPerSample * 2 + BitReader.log2i(frame.blocksize) >= 61); + frame.window_buffer + iWindow * Alac.MAX_BLOCKSIZE * 2, sections); 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)); } break; 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++) @@ -1104,7 +1145,8 @@ namespace CUETools.Codecs.ALAC pos += sz; sz >>= 1; } while (sz >= 32); - _windowcount++; + windowType[_windowcount] = flag; + _windowcount++; } unsafe int encode_frame(ref int size) @@ -1148,7 +1190,7 @@ namespace CUETools.Codecs.ALAC if (_windowcount == 0) throw new Exception("invalid windowfunction"); fixed (LpcWindowSection* sections = &windowSections[0, 0]) - LpcWindowSection.Detect(_windowcount, window, Alac.MAX_BLOCKSIZE * 2, _windowsize, sections); + LpcWindowSection.Detect(_windowcount, window, Alac.MAX_BLOCKSIZE * 2, _windowsize, Settings.PCM.BitsPerSample, sections); } frame.window_buffer = window; @@ -1809,10 +1851,10 @@ namespace CUETools.Codecs.ALAC } // default to level 5 params - window_function = WindowFunction.Flattop | WindowFunction.Tukey; + window_function = WindowFunction.PartialTukey | WindowFunction.PunchoutTukey; order_method = OrderMethod.Estimate; - stereo_method = StereoMethod.Evaluate; - window_method = WindowMethod.Evaluate; + stereo_method = StereoMethod.Estimate; + window_method = WindowMethod.Estimate; block_time_ms = 105; min_modifier = 4; max_modifier = 4; @@ -1828,53 +1870,50 @@ namespace CUETools.Codecs.ALAC { case 0: stereo_method = StereoMethod.Independent; - window_function = WindowFunction.Hann; max_prediction_order = 6; break; case 1: stereo_method = StereoMethod.Independent; - window_function = WindowFunction.Hann; max_prediction_order = 8; break; case 2: - stereo_method = StereoMethod.Estimate; - window_function = WindowFunction.Hann; max_prediction_order = 6; break; case 3: - stereo_method = StereoMethod.Estimate; - window_function = WindowFunction.Hann; + window_function = WindowFunction.PartialTukey; max_prediction_order = 8; break; case 4: - stereo_method = StereoMethod.Estimate; - window_method = WindowMethod.Estimate; - window_function = WindowFunction.PartialTukey | WindowFunction.PunchoutTukey; + window_function = WindowFunction.PunchoutTukey; max_prediction_order = 8; break; case 5: - stereo_method = StereoMethod.Estimate; - window_method = WindowMethod.Estimate; - window_function = WindowFunction.PartialTukey | WindowFunction.PunchoutTukey; + window_function = WindowFunction.PunchoutTukey; break; case 6: - stereo_method = StereoMethod.Estimate; + window_method = WindowMethod.EvaluateN; break; case 7: - stereo_method = StereoMethod.Estimate; + window_method = WindowMethod.EvaluateN; adaptive_passes = 1; min_modifier = 2; break; case 8: + stereo_method = StereoMethod.Evaluate; + window_method = WindowMethod.EvaluateN; adaptive_passes = 1; min_modifier = 2; break; case 9: + stereo_method = StereoMethod.Evaluate; + window_method = WindowMethod.EvaluateN; adaptive_passes = 1; max_prediction_order = 30; min_modifier = 2; break; case 10: + stereo_method = StereoMethod.Evaluate; + window_method = WindowMethod.EvaluateN; estimation_depth = 2; adaptive_passes = 2; max_prediction_order = 30; diff --git a/CUETools.Codecs.ALAC/WindowMethod.cs b/CUETools.Codecs.ALAC/WindowMethod.cs index 549fba8..0d4e468 100644 --- a/CUETools.Codecs.ALAC/WindowMethod.cs +++ b/CUETools.Codecs.ALAC/WindowMethod.cs @@ -4,6 +4,8 @@ { Estimate = 0, Evaluate = 1, - Search = 2 + Search = 2, + EstimateN = 3, + EvaluateN = 4, } } diff --git a/CUETools.Codecs.FLAKE/FlakeWriter.cs b/CUETools.Codecs.FLAKE/FlakeWriter.cs index 7a3a58a..d8d1486 100644 --- a/CUETools.Codecs.FLAKE/FlakeWriter.cs +++ b/CUETools.Codecs.FLAKE/FlakeWriter.cs @@ -1399,7 +1399,7 @@ new int[] { // 30 //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) { @@ -1464,8 +1464,8 @@ new int[] { // 30 LpcContext lpc_ctx = frame.subframes[ch].lpc_ctx[iWindow]; 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); + frame.subframes[ch].sf, max_order, frame.blocksize, + smp, frame.window_buffer + iWindow * Flake.MAX_BLOCKSIZE * 2, sections); lpc_ctx.ComputeLPC(lpcs); //int frameSize = n; @@ -1671,7 +1671,7 @@ new int[] { // 30 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); } @@ -1777,9 +1777,8 @@ new int[] { // 30 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); + frame.subframes[ch].sf, estimate_order, frame.blocksize, + frame.subframes[ch].samples, frame.window_buffer + i * Flake.MAX_BLOCKSIZE * 2, sections); 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); @@ -1895,37 +1894,65 @@ new int[] { // 30 switch (eparams.stereo_method) { - case StereoMethod.Estimate: + case StereoMethod.Estimate: for (int ch = 0; ch < subframes; ch++) { -#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); + frame.subframes[ch].sf, estimate_order, frame.blocksize, + frame.subframes[ch].samples, frame.window_buffer + iWindow * Flake.MAX_BLOCKSIZE * 2, sections); 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; +#if XXX + case StereoMethod.EstimateFixed: + for (int ch = 0; ch < subframes; ch++) + { + 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]; + } + break; +#endif + case StereoMethod.EstimateX: + for (int ch = 0; ch < subframes; ch++) + for (int iWindow = 0; iWindow < _windowcount; iWindow++) + { + LpcContext lpc_ctx = frame.subframes[ch].lpc_ctx[iWindow]; + int estimate_order = 4; + fixed (LpcWindowSection* sections = &windowSections[frame.nSeg, iWindow, 0]) + lpc_ctx.GetReflection( + frame.subframes[ch].sf, estimate_order, frame.blocksize, + frame.subframes[ch].samples, frame.window_buffer + iWindow * Flake.MAX_BLOCKSIZE * 2, sections); + lpc_ctx.SortOrdersAkaike(frame.blocksize, 1, 1, estimate_order, 4.5, 0.0); + uint estimate + = (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); + if (iWindow == 0 || frame.subframes[ch].best.size > estimate) + frame.subframes[ch].best.size = estimate; + } + break; case StereoMethod.Evaluate: for (int ch = 0; ch < subframes; ch++) encode_residual_pass1(frame, ch, 1); break; + case StereoMethod.EvaluateX: + for (int ch = 0; ch < subframes; ch++) + encode_residual_pass1(frame, ch, + estimate_best_windows_akaike(frame, ch, 1, false)); + break; case StereoMethod.Search: for (int ch = 0; ch < subframes; ch++) encode_residual_pass2(frame, ch); @@ -1977,14 +2004,16 @@ new int[] { // 30 switch (eparams.stereo_method) { case StereoMethod.Estimate: - for (int ch = 0; ch < channels; ch++) + case StereoMethod.EstimateX: + for (int ch = 0; ch < channels; ch++) { frame.subframes[ch].best.size = AudioSamples.UINT32_MAX; encode_residual_pass2(frame, ch); } break; case StereoMethod.Evaluate: - for (int ch = 0; ch < channels; ch++) + case StereoMethod.EvaluateX: + for (int ch = 0; ch < channels; ch++) encode_residual_pass2(frame, ch); break; case StereoMethod.Search: @@ -2070,7 +2099,7 @@ new int[] { // 30 do { fixed (LpcWindowSection* sections = &windowSections[nSeg, 0, 0]) - LpcWindowSection.Detect(_windowcount, window_segment, Flake.MAX_BLOCKSIZE * 2, sz, sections); + LpcWindowSection.Detect(_windowcount, window_segment, Flake.MAX_BLOCKSIZE * 2, sz, Settings.PCM.BitsPerSample, sections); if ((sz & 1) != 0) break; window_segment += sz; @@ -2087,7 +2116,7 @@ new int[] { // 30 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); + tx.WriteLine("{0}\t{1}\t{2}\t{3}", windowSections[0, i, sec].m_start, windowSections[0, i, sec].m_end, windowSections[0, i, sec].m_type, windowSections[0, i, sec].m_id); if (windowSections[0, i, sec].m_type != LpcWindowSection.SectionType.One) total += windowSections[0, i, sec].m_end - windowSections[0, i, sec].m_start; } diff --git a/CUETools.Codecs.FLAKE/StereoMethod.cs b/CUETools.Codecs.FLAKE/StereoMethod.cs index 7a5987d..9e963f2 100644 --- a/CUETools.Codecs.FLAKE/StereoMethod.cs +++ b/CUETools.Codecs.FLAKE/StereoMethod.cs @@ -7,5 +7,7 @@ namespace CUETools.Codecs.FLAKE Estimate = 1, Evaluate = 2, Search = 3, - } + EstimateX = 4, + EvaluateX = 5, + } } diff --git a/CUETools.Codecs/LPC.cs b/CUETools.Codecs/LPC.cs index 4ad1168..d5975b9 100644 --- a/CUETools.Codecs/LPC.cs +++ b/CUETools.Codecs/LPC.cs @@ -131,7 +131,7 @@ namespace CUETools.Codecs * A window function is applied before calculation. */ static public unsafe void - compute_autocorr(/*const*/ int* data, float* window, int len, int min, int lag, double* autoc, int prev, int next) + compute_autocorr(/*const*/ int* data, float* window, int len, int min, int lag, double* autoc) { #if FPAC short* data1 = stackalloc short[len + 1]; @@ -220,22 +220,18 @@ namespace CUETools.Codecs if (lag < min) return; } #endif - double* data1 = stackalloc double[lag + len + lag]; + double* data1 = stackalloc double[len]; 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; + data1[i] = data[i] * window[i]; for (i = min; i <= lag; ++i) { double temp = 0; double temp2 = 0; - double* pdata = data1 + lag - i; - double* finish = data1 + lag + len - 1; + double* pdata = data1; + double* finish = data1 + len - 1 - i; while (pdata < finish) { @@ -361,6 +357,23 @@ namespace CUETools.Codecs } } + static public unsafe void + compute_autocorr_glue(/*const*/ int* data, float* window, int offs, int sz, int min, int lag, double* autoc) + { + double* data1 = stackalloc double[lag + lag]; + for (int i = -lag; i < lag; i++) + data1[i + lag] = offs + i >= 0 && offs + i < sz ? data[offs + i] * window[offs + i] : 0; + for (int i = min; i <= lag; ++i) + { + double temp = 0; + double* pdata = data1 + lag - i; + double* finish = data1 + lag; + while (pdata < finish) + temp += pdata[i] * (*pdata++); + autoc[i] += temp; + } + } + static public unsafe void compute_autocorr_glue(/*const*/ int* data, int min, int lag, double* autoc) { diff --git a/CUETools.Codecs/LpcContext.cs b/CUETools.Codecs/LpcContext.cs index 6f15073..13436b8 100644 --- a/CUETools.Codecs/LpcContext.cs +++ b/CUETools.Codecs/LpcContext.cs @@ -29,7 +29,9 @@ namespace CUETools.Codecs { Zero, One, + OneLarge, Data, + OneGlue, Glue }; public int m_start; @@ -72,22 +74,48 @@ namespace CUETools.Codecs m_type = SectionType.Zero; } - unsafe public static void Detect(int _windowcount, float* window_segment, int stride, int sz, LpcWindowSection* sections) + unsafe public void compute_autocorr(/*const*/ int* data, float* window, int min_order, int order, int blocksize, double* autoc) + { + if (m_type == SectionType.OneLarge) + lpc.compute_autocorr_windowless_large(data + m_start, m_end - m_start, min_order, order, autoc); + else if (m_type == SectionType.One) + lpc.compute_autocorr_windowless(data + m_start, m_end - m_start, min_order, order, autoc); + else if (m_type == SectionType.Data) + lpc.compute_autocorr(data + m_start, window + m_start, m_end - m_start, min_order, order, autoc); + else if (m_type == SectionType.Glue) + lpc.compute_autocorr_glue(data, window, m_start, blocksize, min_order, order, autoc); + else if (m_type == SectionType.OneGlue) + lpc.compute_autocorr_glue(data + m_start, min_order, order, autoc); + } + + unsafe public static void Detect(int _windowcount, float* window_segment, int stride, int sz, int bps, LpcWindowSection* sections) { int section_id = 0; var boundaries = new List(); var types = new LpcWindowSection.SectionType[_windowcount, lpc.MAX_LPC_SECTIONS * 2]; + var alias = new int[_windowcount, lpc.MAX_LPC_SECTIONS * 2]; + var alias_set = new int[_windowcount, lpc.MAX_LPC_SECTIONS * 2]; for (int x = 0; x < sz; x++) { for (int i = 0; i < _windowcount; i++) { + int a = alias[i, boundaries.Count]; float w = window_segment[i * stride + x]; + float wa = window_segment[a * stride + x]; + if (wa != w) + { + for (int i1 = i; i1 < _windowcount; i1++) + if (alias[i1, boundaries.Count] == a + && w == window_segment[i1 * stride + x]) + alias[i1, boundaries.Count] = i; + } 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; + LpcWindowSection.SectionType.Zero : w != 1.0 ? + LpcWindowSection.SectionType.Data : bps * 2 + BitReader.log2i(sz) >= 61 ? + LpcWindowSection.SectionType.OneLarge : + LpcWindowSection.SectionType.One ; } bool isBoundary = false; for (int i = 0; i < _windowcount; i++) @@ -95,74 +123,90 @@ namespace CUETools.Codecs isBoundary |= boundaries.Count == 0 || types[i, boundaries.Count - 1] != types[i, boundaries.Count]; } - if (isBoundary) boundaries.Add(x); + if (isBoundary) + { + for (int i = 0; i < _windowcount; i++) + for (int i1 = 0; i1 < _windowcount; i1++) + if (i != i1 && alias[i, boundaries.Count] == alias[i1, boundaries.Count]) + alias_set[i, boundaries.Count] |= 1 << i1; + boundaries.Add(x); + } } boundaries.Add(sz); - var ones = new int[boundaries.Count - 1]; + var secs = new int[_windowcount]; // 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 i = 0; i < _windowcount; i++) + LpcWindowSection* window_sections = sections + i * lpc.MAX_LPC_SECTIONS; + // leave room for glue + if (secs[i] >= lpc.MAX_LPC_SECTIONS - 1) { - 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]; - } + window_sections[secs[i] - 1].m_type = LpcWindowSection.SectionType.Data; + window_sections[secs[i] - 1].m_end = boundaries[j + 1]; + continue; } - continue; + window_sections[secs[i]].setData(boundaries[j], boundaries[j + 1]); + window_sections[secs[i]++].m_type = types[i, j]; } - if ((ones[j] & (ones[j] - 1)) != 0 && section_id < lpc.MAX_LPC_SECTIONS) + for (int i = 0; i < _windowcount; i++) { - 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++; + LpcWindowSection* window_sections = sections + i * lpc.MAX_LPC_SECTIONS; + int sec = secs[i] - 1; + if (sec > 0 + && j > 0 && (alias_set[i, j] == alias_set[i, j - 1] || window_sections[sec].m_type == SectionType.Zero) + && window_sections[sec].m_start == boundaries[j] + && window_sections[sec].m_end == boundaries[j + 1] + && window_sections[sec - 1].m_end == boundaries[j] + && window_sections[sec - 1].m_type == window_sections[sec].m_type) + { + window_sections[sec - 1].m_end = window_sections[sec].m_end; + secs[i]--; + continue; + } + if (alias_set[i, j] != 0 + && types[i, j] != SectionType.Zero + && section_id < lpc.MAX_LPC_SECTIONS) + { + for (int i1 = i; i1 < _windowcount; i1++) + if (alias[i1, j] == i && secs[i1] > 0) + sections[i1 * lpc.MAX_LPC_SECTIONS + secs[i1] - 1].m_id = section_id; + section_id++; + } + // TODO: section_id for glue? + if (sec > 0 + && (window_sections[sec].m_type == SectionType.One || window_sections[sec].m_type == SectionType.OneLarge) + && window_sections[sec].m_end - window_sections[sec].m_start >= lpc.MAX_LPC_ORDER + && (window_sections[sec - 1].m_type == SectionType.One || window_sections[sec - 1].m_type == SectionType.OneLarge) + && window_sections[sec - 1].m_end - window_sections[sec - 1].m_start >= lpc.MAX_LPC_ORDER) + { + window_sections[sec + 1] = window_sections[sec]; + window_sections[sec].m_end = window_sections[sec].m_start; + window_sections[sec].m_type = SectionType.OneGlue; + window_sections[sec].m_id = -1; + secs[i]++; + continue; + } + if (sec > 0 + && window_sections[sec].m_type != SectionType.Zero + && window_sections[sec - 1].m_type != SectionType.Zero) + { + window_sections[sec + 1] = window_sections[sec]; + window_sections[sec].m_end = window_sections[sec].m_start; + window_sections[sec].m_type = SectionType.Glue; + window_sections[sec].m_id = -1; + secs[i]++; + continue; + } + } + } + for (int i = 0; i < _windowcount; i++) + { + while (secs[i] < lpc.MAX_LPC_SECTIONS) + { + LpcWindowSection* window_sections = sections + i * lpc.MAX_LPC_SECTIONS; + window_sections[secs[i]++].setZero(sz, sz); } } } @@ -202,55 +246,37 @@ namespace CUETools.Codecs /// Samples pointer /// Block size /// Window function - public void GetReflection(LpcSubframeInfo subframe, int order, int* samples, float* window, LpcWindowSection* sections, bool large) + public void GetReflection(LpcSubframeInfo subframe, int order, int blocksize, int* samples, float* window, LpcWindowSection* sections) { if (autocorr_order > order) return; fixed (double* reff = reflection_coeffs, autoc = autocorr_values, err = prediction_error) { 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) + if (sections[section].m_id >= 0) { - 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) { - if (subframe.autocorr_section_orders[sections[section].m_id] <= order) + fixed (double* autocsec = &subframe.autocorr_section_values[sections[section].m_id, 0]) { - 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; + int min_order = subframe.autocorr_section_orders[sections[section].m_id]; + for (int i = min_order; i <= order; i++) autocsec[i] = 0; + sections[section].compute_autocorr(samples, window, min_order, order, blocksize, autocsec); } - for (int i = autocorr_order; i <= order; i++) - autoc[i] += subframe.autocorr_section_values[sections[section].m_id, i]; + subframe.autocorr_section_orders[sections[section].m_id] = order + 1; } - 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; + for (int i = autocorr_order; i <= order; i++) + autoc[i] += subframe.autocorr_section_values[sections[section].m_id, i]; + } + else + { + sections[section].compute_autocorr(samples, window, autocorr_order, order, blocksize, autoc); } } lpc.compute_schur_reflection(autoc, (uint)order, reff, err); diff --git a/CUETools.Flake/Program.cs b/CUETools.Flake/Program.cs index 86c5b24..e405085 100644 --- a/CUETools.Flake/Program.cs +++ b/CUETools.Flake/Program.cs @@ -100,7 +100,7 @@ namespace CUETools.FlakeExe Console.WriteLine(" --verify Verify during encoding."); Console.WriteLine(" --no-md5 Don't compute MD5 hash."); Console.WriteLine(" --no-seektable Don't generate a seektable."); - Console.WriteLine(" --ignore-chunk-sizes Ignore WAV length (for pipe input)"); + Console.WriteLine(" --ignore-chunk-sizes Ignore WAV length (for pipe input)"); Console.WriteLine(); Console.WriteLine("Advanced Options:"); Console.WriteLine(); @@ -114,10 +114,12 @@ namespace CUETools.FlakeExe Console.WriteLine(); Console.WriteLine(" -m Prediction order search (akaike)."); Console.WriteLine(" -e # Prediction order search depth (1..32)."); - Console.WriteLine(" -w [,] One or more window functions (bartlett,welch,hann,flattop,tukey)."); + Console.WriteLine(" -w [,] One or more window functions (tukey,partialtukey,"); + Console.WriteLine(" punchouttukey,bartlett,welch,hann,flattop)."); Console.WriteLine(" -l #[,#] Prediction order {max} or {min},{max} (1..32)."); - Console.WriteLine(" --window-method Window selection method (estimate,evaluate,search)."); - Console.WriteLine(" --max-precision Coefficients precision search (0..1)."); + Console.WriteLine(" --window-method Window selection method (estimate,estimateN,"); + Console.WriteLine(" evaluate,evaluateN,search)."); + Console.WriteLine(" --max-precision #,# Coefficients precision search (0..1)."); Console.WriteLine(); Console.WriteLine("Fixed prediction options:"); Console.WriteLine();