diff --git a/CUETools.Codecs.FLAKE/Flake.cs b/CUETools.Codecs.FLAKE/Flake.cs index a0cf831..38db01c 100644 --- a/CUETools.Codecs.FLAKE/Flake.cs +++ b/CUETools.Codecs.FLAKE/Flake.cs @@ -55,6 +55,11 @@ namespace CUETools.Codecs.FLAKE return (StereoMethod)(Enum.Parse(typeof(StereoMethod), name, true)); } + public static WindowMethod LookupWindowMethod(string name) + { + return (WindowMethod)(Enum.Parse(typeof(WindowMethod), name, true)); + } + public static OrderMethod LookupOrderMethod(string name) { return (OrderMethod)(Enum.Parse(typeof(OrderMethod), name, true)); @@ -226,9 +231,12 @@ namespace CUETools.Codecs.FLAKE //public int ch_order0, ch_order1; public byte crc8; public FlacSubframeInfo[] subframes; - public uint frame_count; + public int frame_number; public FlacSubframe current; public float* window_buffer; + + public BitWriter writer = null; + public int writer_offset = 0; } public enum OrderMethod @@ -270,6 +278,13 @@ namespace CUETools.Codecs.FLAKE Search = 3 } + public enum WindowMethod + { + Estimate = 0, + Evaluate = 1, + Search = 2 + } + public enum SubframeType { Constant = 0, diff --git a/CUETools.Codecs.FLAKE/FlakeReader.cs b/CUETools.Codecs.FLAKE/FlakeReader.cs index 7fb6ada..ee61090 100644 --- a/CUETools.Codecs.FLAKE/FlakeReader.cs +++ b/CUETools.Codecs.FLAKE/FlakeReader.cs @@ -325,7 +325,7 @@ namespace CUETools.Codecs.FLAKE uint t1 = bitreader.readbit(); // == 0????? if (t1 != 0) throw new Exception("unsupported frame coding"); - frame.frame_count = bitreader.read_utf8(); + frame.frame_number = (int)bitreader.read_utf8(); // custom block size if (frame.bs_code0 == 6) diff --git a/CUETools.Codecs.FLAKE/FlakeWriter.cs b/CUETools.Codecs.FLAKE/FlakeWriter.cs index 7897433..c497e0a 100644 --- a/CUETools.Codecs.FLAKE/FlakeWriter.cs +++ b/CUETools.Codecs.FLAKE/FlakeWriter.cs @@ -85,6 +85,7 @@ namespace CUETools.Codecs.FLAKE int[] verifyBuffer; int[] residualBuffer; float[] windowBuffer; + double[] windowScale; int samplesInBuffer = 0; int _compressionLevel = 7; @@ -123,6 +124,7 @@ 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]; eparams.flake_set_defaults(_compressionLevel); eparams.padding_size = 8192; @@ -267,6 +269,12 @@ namespace CUETools.Codecs.FLAKE set { eparams.stereo_method = value; } } + public WindowMethod WindowMethod + { + get { return eparams.window_method; } + set { eparams.window_method = value; } + } + public int MinPrecisionSearch { get { return eparams.lpc_min_precision_search; } @@ -823,12 +831,11 @@ namespace CUETools.Codecs.FLAKE frame.ChooseBestSubframe(ch); } - unsafe void encode_residual(FlacFrame frame, int ch, PredictionType predict, OrderMethod omethod, int pass) + unsafe void encode_residual(FlacFrame frame, int ch, PredictionType predict, OrderMethod omethod, int pass, int best_window) { int* smp = frame.subframes[ch].samples; int i, n = frame.blocksize; // save best.window, because we can overwrite it later with fixed frame - int best_window = frame.subframes[ch].best.type == SubframeType.LPC ? frame.subframes[ch].best.window : -1; // CONSTANT for (i = 1; i < n; i++) @@ -879,7 +886,7 @@ namespace CUETools.Codecs.FLAKE for (int iWindow = 0; iWindow < _windowcount; iWindow++) { - if (pass == 2 && iWindow != best_window) + if (best_window != -1 && iWindow != best_window) continue; LpcContext lpc_ctx = frame.subframes[ch].lpc_ctx[iWindow]; @@ -940,7 +947,8 @@ namespace CUETools.Codecs.FLAKE switch (omethod) { case OrderMethod.Akaike: - lpc_ctx.SortOrdersAkaike(frame.blocksize, eparams.estimation_depth); + //lpc_ctx.SortOrdersAkaike(frame.blocksize, eparams.estimation_depth, max_order, 7.1, 0.0); + lpc_ctx.SortOrdersAkaike(frame.blocksize, eparams.estimation_depth, max_order, 4.5, 0.0); break; default: throw new Exception("unknown order method"); @@ -1107,7 +1115,7 @@ namespace CUETools.Codecs.FLAKE bitwriter.flush(); } - unsafe void encode_residual_pass1(FlacFrame frame, int ch) + unsafe void encode_residual_pass1(FlacFrame frame, int ch, int best_window) { int max_prediction_order = eparams.max_prediction_order; int max_fixed_order = eparams.max_fixed_order; @@ -1121,7 +1129,7 @@ namespace CUETools.Codecs.FLAKE eparams.lpc_min_precision_search = eparams.lpc_max_precision_search; eparams.max_prediction_order = 8; eparams.estimation_depth = 1; - encode_residual(frame, ch, eparams.prediction_type, OrderMethod.Akaike, 1); + encode_residual(frame, ch, eparams.prediction_type, OrderMethod.Akaike, 1, best_window); eparams.min_fixed_order = min_fixed_order; eparams.max_fixed_order = max_fixed_order; eparams.max_prediction_order = max_prediction_order; @@ -1133,17 +1141,40 @@ namespace CUETools.Codecs.FLAKE unsafe void encode_residual_pass2(FlacFrame frame, int ch) { - encode_residual(frame, ch, eparams.prediction_type, eparams.order_method, 2); + encode_residual(frame, ch, eparams.prediction_type, eparams.order_method, 2, estimate_best_window(frame, ch)); } - unsafe void encode_residual_onepass(FlacFrame frame, int ch) + unsafe int estimate_best_window(FlacFrame frame, int ch) { - if (_windowcount > 1) + if (_windowcount == 1) + return 0; + switch (eparams.window_method) { - encode_residual_pass1(frame, ch); - encode_residual_pass2(frame, ch); - } else - encode_residual(frame, ch, eparams.prediction_type, eparams.order_method, 0); + 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: + encode_residual_pass1(frame, ch, -1); + return frame.subframes[ch].best.type == SubframeType.LPC ? frame.subframes[ch].best.window : -1; + case WindowMethod.Search: + return -1; + } + return -1; } unsafe void estimate_frame(FlacFrame frame, bool do_midside) @@ -1157,22 +1188,17 @@ namespace CUETools.Codecs.FLAKE { 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); - frame.subframes[ch].best.size = Math.Max(0U, (uint)lpc_ctx.Akaike(frame.blocksize, lpc_ctx.best_orders[0])); + lpc_ctx.SortOrdersAkaike(frame.blocksize, 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 * eparams.max_prediction_order); } break; case StereoMethod.Evaluate: for (int ch = 0; ch < subframes; ch++) - { - int windowcount = _windowcount; - _windowcount = 1; - encode_residual_pass1(frame, ch); - _windowcount = windowcount; - } + encode_residual_pass1(frame, ch, 0); break; case StereoMethod.Search: for (int ch = 0; ch < subframes; ch++) - encode_residual_onepass(frame, ch); + encode_residual_pass2(frame, ch); break; } } @@ -1224,16 +1250,12 @@ namespace CUETools.Codecs.FLAKE for (int ch = 0; ch < channels; ch++) { frame.subframes[ch].best.size = AudioSamples.UINT32_MAX; - encode_residual_onepass(frame, ch); + encode_residual_pass2(frame, ch); } break; case StereoMethod.Evaluate: for (int ch = 0; ch < channels; ch++) - { - if (_windowcount > 1) - encode_residual_pass1(frame, ch); encode_residual_pass2(frame, ch); - } break; case StereoMethod.Search: break; @@ -1247,7 +1269,8 @@ namespace CUETools.Codecs.FLAKE if ((eparams.window_function & flag) == 0 || _windowcount == lpc.MAX_LPC_WINDOWS) return; int sz = _windowsize; - float* pos = window + _windowcount * Flake.MAX_BLOCKSIZE * 2; + float* pos1 = window + _windowcount * Flake.MAX_BLOCKSIZE * 2; + float* pos = pos1; do { func(pos, sz); @@ -1256,6 +1279,10 @@ namespace CUETools.Codecs.FLAKE 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; _windowcount++; } @@ -1272,8 +1299,8 @@ namespace CUETools.Codecs.FLAKE _windowcount = 0; calculate_window(window, lpc.window_welch, WindowFunction.Welch); calculate_window(window, lpc.window_tukey, WindowFunction.Tukey); - calculate_window(window, lpc.window_hann, WindowFunction.Hann); 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"); @@ -1289,7 +1316,7 @@ namespace CUETools.Codecs.FLAKE bits_per_sample, get_wasted_bits(s + ch * Flake.MAX_BLOCKSIZE, frame.blocksize)); for (int ch = 0; ch < channels; ch++) - encode_residual_onepass(frame, ch); + encode_residual_pass2(frame, ch); } else { @@ -1739,6 +1766,8 @@ namespace CUETools.Codecs.FLAKE // 1 = mid-side encoding public StereoMethod stereo_method; + public WindowMethod window_method; + // block size in samples // set by the user prior to calling flake_encode_init // if set to 0, a block size is chosen based on block_time_ms @@ -1834,6 +1863,7 @@ namespace CUETools.Codecs.FLAKE window_function = WindowFunction.Flattop | WindowFunction.Tukey; order_method = OrderMethod.Akaike; stereo_method = StereoMethod.Evaluate; + window_method = WindowMethod.Evaluate; block_size = 0; block_time_ms = 105; prediction_type = PredictionType.Search; @@ -1882,7 +1912,8 @@ namespace CUETools.Codecs.FLAKE window_function = WindowFunction.Bartlett; break; case 5: - window_function = WindowFunction.Bartlett; + stereo_method = StereoMethod.Estimate; + window_method = WindowMethod.Estimate; break; case 6: stereo_method = StereoMethod.Estimate;