From 6201101ccf2aeac984e57a75d17b0706c89ad3fc Mon Sep 17 00:00:00 2001 From: Grigory Chudov Date: Mon, 8 Dec 2014 22:18:34 -0500 Subject: [PATCH] CUETools.Flake: better use of new window functions --- CUETools.Codecs.ALAC/ALACWriter.cs | 9 +- CUETools.Codecs.FLACCL/FLACCLWriter.cs | 5 +- CUETools.Codecs.FLAKE/FlacSubframeInfo.cs | 4 + CUETools.Codecs.FLAKE/FlakeWriter.cs | 906 ++++++++++------------ CUETools.Codecs.FLAKE/StereoMethod.cs | 14 +- CUETools.Codecs.FLAKE/WindowFunction.cs | 21 +- CUETools.Codecs.FLAKE/WindowMethod.cs | 1 + CUETools.Codecs/AudioEncoderSettings.cs | 14 +- CUETools.Codecs/BitReader.cs | 49 +- CUETools.Codecs/BitWriter.cs | 6 + CUETools.Codecs/DefaultValueForMode.cs | 4 +- CUETools.Codecs/LPC.cs | 24 +- CUETools.Codecs/LpcContext.cs | 16 +- CUETools.Flake/Program.cs | 313 ++++---- 14 files changed, 679 insertions(+), 707 deletions(-) diff --git a/CUETools.Codecs.ALAC/ALACWriter.cs b/CUETools.Codecs.ALAC/ALACWriter.cs index 7c9e8b2..70d476b 100644 --- a/CUETools.Codecs.ALAC/ALACWriter.cs +++ b/CUETools.Codecs.ALAC/ALACWriter.cs @@ -1162,7 +1162,10 @@ namespace CUETools.Codecs.ALAC _windowcount = 0; calculate_window(window, lpc.window_welch, WindowFunction.Welch); calculate_window(window, lpc.window_bartlett, WindowFunction.Bartlett); - calculate_window(window, lpc.window_tukey, WindowFunction.Tukey); + calculate_window(window, (w, wsz) => + { + lpc.window_tukey(w, wsz, 0.5); + }, WindowFunction.Tukey); calculate_window(window, lpc.window_hann, WindowFunction.Hann); calculate_window(window, lpc.window_flattop, WindowFunction.Flattop); int tukey_parts = 2; @@ -1171,7 +1174,7 @@ namespace CUETools.Codecs.ALAC for (int m = 0; m < tukey_parts; m++) calculate_window(window, (w, wsz) => { - lpc.window_punchout_tukey(w, wsz, 0.1, + lpc.window_punchout_tukey(w, wsz, 0.1, 0.1, m / (tukey_parts + overlap_units), (m + 1 + overlap_units) / (tukey_parts + overlap_units)); }, WindowFunction.PartialTukey); @@ -1183,7 +1186,7 @@ namespace CUETools.Codecs.ALAC for (int m = 0; m < tukey_parts; m++) calculate_window(window, (w, wsz) => { - lpc.window_punchout_tukey(w, wsz, 0.1, + lpc.window_punchout_tukey(w, wsz, 0.1, 0.1, m / (tukey_parts + overlap_units), (m + 1 + overlap_units) / (tukey_parts + overlap_units)); }, WindowFunction.PunchoutTukey); diff --git a/CUETools.Codecs.FLACCL/FLACCLWriter.cs b/CUETools.Codecs.FLACCL/FLACCLWriter.cs index 2826aae..93e16e9 100644 --- a/CUETools.Codecs.FLACCL/FLACCLWriter.cs +++ b/CUETools.Codecs.FLACCL/FLACCLWriter.cs @@ -1118,7 +1118,10 @@ namespace CUETools.Codecs.FLACCL { calculate_window(task, lpc.window_welch, WindowFunction.Welch); calculate_window(task, lpc.window_flattop, WindowFunction.Flattop); - calculate_window(task, lpc.window_tukey, WindowFunction.Tukey); + calculate_window(task, (w, wsz) => + { + lpc.window_tukey(w, wsz, 0.5); + }, WindowFunction.Tukey); calculate_window(task, lpc.window_hann, WindowFunction.Hann); calculate_window(task, lpc.window_bartlett, WindowFunction.Bartlett); if (task.nWindowFunctions == 0) diff --git a/CUETools.Codecs.FLAKE/FlacSubframeInfo.cs b/CUETools.Codecs.FLAKE/FlacSubframeInfo.cs index 0c285dc..441b9d0 100644 --- a/CUETools.Codecs.FLAKE/FlacSubframeInfo.cs +++ b/CUETools.Codecs.FLAKE/FlacSubframeInfo.cs @@ -8,6 +8,7 @@ namespace CUETools.Codecs.FLAKE { best = new FlacSubframe(); sf = new LpcSubframeInfo(); + best_fixed = new ulong[5]; lpc_ctx = new LpcContext[lpc.MAX_LPC_WINDOWS]; for (int i = 0; i < lpc.MAX_LPC_WINDOWS; i++) lpc_ctx[i] = new LpcContext(); @@ -20,6 +21,8 @@ namespace CUETools.Codecs.FLAKE samples = s; obits = bps - w; wbits = w; + for (int o = 0; o <= 4; o++) + best_fixed[o] = 0; best.residual = r; best.type = SubframeType.Verbatim; best.size = AudioSamples.UINT32_MAX; @@ -35,6 +38,7 @@ namespace CUETools.Codecs.FLAKE public int wbits; public int* samples; public uint done_fixed; + public ulong[] best_fixed; public LpcContext[] lpc_ctx; public LpcSubframeInfo sf; }; diff --git a/CUETools.Codecs.FLAKE/FlakeWriter.cs b/CUETools.Codecs.FLAKE/FlakeWriter.cs index 8145bef..4d2e21d 100644 --- a/CUETools.Codecs.FLAKE/FlakeWriter.cs +++ b/CUETools.Codecs.FLAKE/FlakeWriter.cs @@ -80,19 +80,36 @@ namespace CUETools.Codecs.FLAKE throw new Exception("invalid MinPartitionOrder " + MinPartitionOrder.ToString()); if (MinPartitionOrder > MaxPartitionOrder || MaxPartitionOrder > 8) throw new Exception("invalid MaxPartitionOrder " + MaxPartitionOrder.ToString()); + if (PredictionType == PredictionType.None) + throw new Exception("invalid PredictionType " + PredictionType.ToString()); + if (PredictionType != PredictionType.Fixed) + { + if (WindowMethod == WindowMethod.Invalid) + throw new InvalidOperationException("invalid WindowMethod " + WindowMethod.ToString()); + if (WindowFunctions == WindowFunction.None) + throw new InvalidOperationException("invalid WindowFunctions " + WindowFunctions.ToString()); + if (EstimationDepth > 32 || EstimationDepth < 1) + throw new InvalidOperationException("invalid EstimationDepth " + EstimationDepth.ToString()); + if (MinPrecisionSearch < 0 || MinPrecisionSearch >= lpc.MAX_LPC_PRECISIONS) + throw new Exception("unsupported MinPrecisionSearch value"); + if (MaxPrecisionSearch < 0 || MaxPrecisionSearch >= lpc.MAX_LPC_PRECISIONS) + throw new Exception("unsupported MaxPrecisionSearch value"); + if (MaxPrecisionSearch < MinPrecisionSearch) + throw new Exception("unsupported MaxPrecisionSearch value"); + } if (!AllowNonSubset && !IsSubset()) throw new Exception("the encoding parameters specified do not conform to the FLAC Subset"); } [DefaultValue(-1)] - [DefaultValueForMode(2, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0)] + [DefaultValueForMode(2, 0, 0, 0, 2, 2, 0, 0, 0, 0, 0, 0)] [Browsable(false)] [DisplayName("MinFixedOrder")] [SRDescription(typeof(Properties.Resources), "MinFixedOrderDescription")] public int MinFixedOrder { get; set; } [DefaultValue(-1)] - [DefaultValueForMode(2, 4, 4, 4, 4, 4, 4, 4, 4, 2, 4, 4)] + [DefaultValueForMode(2, 4, 4, 4, 2, 2, 4, 4, 4, 4, 4, 4)] [Browsable(false)] [DisplayName("MaxFixedOrder")] [SRDescription(typeof(Properties.Resources), "MaxFixedOrderDescription")] @@ -118,7 +135,7 @@ namespace CUETools.Codecs.FLAKE public int MinPartitionOrder { get; set; } [DefaultValue(-1)] - [DefaultValueForMode(6, 8, 6, 8, 8, 8, 8, 8, 8, 8, 8, 8)] + [DefaultValueForMode(6, 6, 6, 6, 6, 6, 6, 6, 7, 6, 6, 8)] [DisplayName("MaxPartitionOrder")] [Browsable(false)] [SRDescription(typeof(Properties.Resources), "MaxPartitionOrderDescription")] @@ -138,6 +155,106 @@ namespace CUETools.Codecs.FLAKE [DisplayName("Allow Non-subset")] [SRDescription(typeof(Properties.Resources), "AllowNonSubsetDescription")] public bool AllowNonSubset { get; set; } + + [DefaultValue(StereoMethod.Invalid)] + [DefaultValueForMode( + /* 0 */ StereoMethod.Independent, + /* 1 */ StereoMethod.EstimateFixed, + /* 2 */ StereoMethod.Estimate, + /* 3 */ StereoMethod.Estimate, + /* 4 */ StereoMethod.Evaluate, + /* 5 */ StereoMethod.Evaluate, + /* 6 */ StereoMethod.Evaluate, + /* 7 */ StereoMethod.Evaluate, + /* 8 */ StereoMethod.Evaluate, + /* 9 */ StereoMethod.Evaluate, + /* 10 */ StereoMethod.Evaluate, + /* 11 */ StereoMethod.Evaluate)] + [Browsable(false)] + public StereoMethod StereoMethod { get; set; } + + [DefaultValue(PredictionType.None)] + [DefaultValueForMode( + /* 0 */ PredictionType.Fixed, + /* 1 */ PredictionType.Fixed, + /* 2 */ PredictionType.Levinson, + /* 3 */ PredictionType.Levinson, + /* 4 */ PredictionType.Search, + /* 5 */ PredictionType.Search, + /* 6 */ PredictionType.Search, + /* 7 */ PredictionType.Search, + /* 8 */ PredictionType.Search, + /* 9 */ PredictionType.Levinson, + /* 10 */ PredictionType.Search, + /* 11 */ PredictionType.Search)] + [Browsable(false)] + public PredictionType PredictionType { get; set; } + + [DefaultValue(WindowMethod.Invalid)] + [DefaultValueForMode( + /* 0 */ WindowMethod.Invalid, + /* 1 */ WindowMethod.Invalid, + /* 2 */ WindowMethod.Estimate, + /* 3 */ WindowMethod.Estimate, + /* 4 */ WindowMethod.Estimate, + /* 5 */ WindowMethod.EvaluateN, + /* 6 */ WindowMethod.EvaluateN, + /* 7 */ WindowMethod.EvaluateN, + /* 8 */ WindowMethod.EvaluateN, + /* 9 */ WindowMethod.EvaluateN, + /* 10 */ WindowMethod.EvaluateN, + /* 11 */ WindowMethod.EvaluateN)] + [Browsable(false)] + public WindowMethod WindowMethod { get; set; } + + [DefaultValue(WindowFunction.None)] + [DefaultValueForMode( + /* 0 */ WindowFunction.None, + /* 1 */ WindowFunction.None, + /* 2 */ WindowFunction.Tukey3, + /* 3 */ WindowFunction.Tukey4, + /* 4 */ WindowFunction.Tukey4, + /* 5 */ WindowFunction.Tukey4 | WindowFunction.Tukey3, + /* 6 */ WindowFunction.Tukey4 | WindowFunction.Tukey3 | WindowFunction.Tukey, + /* 7 */ WindowFunction.Tukey4 | WindowFunction.Tukey3 | WindowFunction.Tukey2 | WindowFunction.Tukey, + /* 8 */ WindowFunction.Tukey4 | WindowFunction.Tukey3 | WindowFunction.Tukey2 | WindowFunction.Tukey, + /* 9 */ WindowFunction.Tukey3 | WindowFunction.Tukey2 | WindowFunction.Tukey, + /* 10 */ WindowFunction.Tukey3 | WindowFunction.Tukey2 | WindowFunction.Tukey, + /* 11 */ WindowFunction.Tukey3 | WindowFunction.Tukey2 | WindowFunction.Tukey)] + [Browsable(false)] + [DisplayName("WindowFunctions")] + [SRDescription(typeof(Properties.Resources), "WindowFunctionsDescription")] + public WindowFunction WindowFunctions { get; set; } + + [DefaultValue(0)] + [DefaultValueForMode(0, 0, 1, 1, 1, 1, 1, 1, 3, 1, 1, 5)] + [Browsable(false)] + public int EstimationDepth { get; set; } + + [DefaultValue(-1)] + [DefaultValueForMode(1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 0, 1)] + [Browsable(false)] + public int MinPrecisionSearch { get; set; } + + [DefaultValue(-1)] + [DefaultValueForMode(1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1)] + [Browsable(false)] + public int MaxPrecisionSearch { get; set; } + + [DefaultValue(0)] + [Browsable(false)] + public int TukeyParts { get; set; } + + [DefaultValue(1.0)] + [Browsable(false)] + public double TukeyOverlap { get; set; } + + [DefaultValue(1.0)] + [Browsable(false)] + public double TukeyP { get; set; } + + [Browsable(false)] + public string[] Tags { get; set; } } [AudioEncoderClass("cuetools", "flac", true, 4, typeof(FlakeWriterSettings))] @@ -357,58 +474,12 @@ namespace CUETools.Codecs.FLAKE set { eparams.order_method = value; } } - public PredictionType PredictionType - { - get { return eparams.prediction_type; } - set { eparams.prediction_type = value; } - } - - public StereoMethod StereoMethod - { - get { return eparams.stereo_method; } - set { eparams.stereo_method = value; } - } - - public WindowMethod WindowMethod - { - get { return eparams.window_method; } - set { eparams.window_method = value; } - } - public int DevelopmentMode { get { return eparams.development_mode; } set { eparams.development_mode = value; } } - public int MinPrecisionSearch - { - get { return eparams.lpc_min_precision_search; } - set - { - if (value < 0 || value > eparams.lpc_max_precision_search) - throw new Exception("unsupported MinPrecisionSearch value"); - eparams.lpc_min_precision_search = value; - } - } - - public int MaxPrecisionSearch - { - get { return eparams.lpc_max_precision_search; } - set - { - if (value < eparams.lpc_min_precision_search || value >= lpc.MAX_LPC_PRECISIONS) - throw new Exception("unsupported MaxPrecisionSearch value"); - eparams.lpc_max_precision_search = value; - } - } - - public WindowFunction WindowFunction - { - get { return eparams.window_function; } - set { eparams.window_function = value; } - } - public bool DoSeekTable { get { return eparams.do_seektable; } @@ -421,20 +492,6 @@ namespace CUETools.Codecs.FLAKE set { eparams.variable_block_size = value; } } - public int EstimationDepth - { - get - { - return eparams.estimation_depth; - } - set - { - if (value > 32 || value < 1) - throw new Exception("invalid estimation_depth " + value.ToString()); - eparams.estimation_depth = value; - } - } - public TimeSpan UserProcessorTime { get @@ -486,7 +543,7 @@ namespace CUETools.Codecs.FLAKE { if (channels == 2) { - if (eparams.stereo_method == StereoMethod.Independent) + if (m_settings.StereoMethod == StereoMethod.Independent) AudioSamples.Deinterlace(fsamples + samplesInBuffer, fsamples + Flake.MAX_BLOCKSIZE + samplesInBuffer, src, block); else { @@ -530,142 +587,112 @@ namespace CUETools.Codecs.FLAKE AudioSamples.MemCpy(res, smp, (int) n); } - unsafe static void encode_residual_fixed(int* res, int* smp, int n, int order) + unsafe static ulong encode_residual_fixed_partition(int* res, int* smp, int* end, int order, int* last_errors) { - 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) + ulong sum = 0UL; + switch (order) { - AudioSamples.MemCpy(res, smp, n); - return; + case 0: + { + while (smp < end) + { + int error = *(res++) = *(smp++); + sum += (uint)((error << 1) ^ (error >> 31)); + } + break; + } + case 1: + { + int last_error_0 = last_errors[0]; + while (smp < end) + { + int error, save; + error = *(smp++); save = error; + error -= last_error_0; *(res++) = error; last_error_0 = save; + sum += (uint)((error << 1) ^ (error >> 31)); + } + last_errors[0] = last_error_0; + break; + } + case 2: + { + int last_error_0 = last_errors[0], last_error_1 = last_errors[1]; + while (smp < end) + { + 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; + sum += (uint)((error << 1) ^ (error >> 31)); + } + last_errors[0] = last_error_0; last_errors[1] = last_error_1; ; + break; + } + case 3: + { + int last_error_0 = last_errors[0], last_error_1 = last_errors[1], last_error_2 = last_errors[2]; + while (smp < end) + { + 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; + sum += (uint)((error << 1) ^ (error >> 31)); + } + last_errors[0] = last_error_0; last_errors[1] = last_error_1; last_errors[2] = last_error_2; + break; + } + case 4: + { + int last_error_0 = last_errors[0], last_error_1 = last_errors[1], last_error_2 = last_errors[2], last_error_3 = last_errors[3]; + while (smp < end) + { + 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; + sum += (uint)((error << 1) ^ (error >> 31)); + } + last_errors[0] = last_error_0; last_errors[1] = last_error_1; last_errors[2] = last_error_2; last_errors[3] = last_error_3; + break; + } + default: + throw new ArgumentOutOfRangeException(); } - - 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(); + return sum; } + + unsafe static void encode_residual_fixed(int* res, int* smp, int n, int order, ulong* sums, int pmax) + { + int* last_errors = stackalloc int[4]; + int* end = smp + n; + int* seg_end = smp + (n >> pmax); + + if (order > 4) + throw new ArgumentOutOfRangeException(); + + for (int i = 0; i < order; i++) + { + int* next_errors = stackalloc int[4]; + next_errors[0] = *(res++) = *(smp++); + for (int j = 0; j < i; j++) + next_errors[j + 1] = next_errors[j] - last_errors[j]; + for (int j = 0; j <= i; j++) + last_errors[j] = next_errors[j]; + } + + while (smp < end) + { + *(sums++) = encode_residual_fixed_partition(res, smp, seg_end, order, last_errors); + res += seg_end - smp; + smp = seg_end; + seg_end += n >> pmax; + } + } + #if XXX unsafe static int encode_residual_fixed_estimate_best_order(int* res, int* smp, int n, int order) { @@ -827,14 +854,33 @@ namespace CUETools.Codecs.FLAKE ulong all_bits = cnt * ((uint)k + 1U) + (sums[0] >> k); parm[0] = k; cnt = (n >> porder); - for (uint i = 1; i < part; i++) - { - k = Math.Min(maxK, BitReader.log2i(sums[i] / cnt)); - realMaxK0 = Math.Max(realMaxK0, k); - all_bits += cnt * ((uint)k + 1U) + (sums[i] >> k); - parm[i] = k; - } - method = realMaxK0 > Flake.MAX_RICE_PARAM ? 1 : 0; + int logcnt = BitReader.log2i(cnt); + if (cnt == 1 << logcnt) + { + for (uint i = 1; i < part; i++) + { + ulong s = sums[i]; + ulong u = s >> logcnt; + k = u >> maxK != 0 ? maxK : BitReader.log2i((uint)u); + realMaxK0 = Math.Max(realMaxK0, k); + all_bits += ((uint)k << logcnt) + (s >> k); + parm[i] = k; + } + } + else + { + for (uint i = 1; i < part; i++) + { + ulong s = sums[i]; + ulong u = s / cnt; + k = u >> maxK != 0 ? maxK : BitReader.log2i((uint)u); + realMaxK0 = Math.Max(realMaxK0, k); + all_bits += cnt * (uint)k + (s >> k); + parm[i] = k; + } + } + all_bits += cnt * (part - 1U); + method = realMaxK0 > Flake.MAX_RICE_PARAM ? 1 : 0; return (uint)all_bits + ((4U + (uint)method) * part); } @@ -851,82 +897,6 @@ namespace CUETools.Codecs.FLAKE } } - static unsafe void calc_sums(int pmin, int pmax, uint* data, uint n, uint pred_order, ulong* sums) - { - int parts = (1 << pmax); - uint* res = data + pred_order; - uint cnt = (n >> pmax) - pred_order; - ulong sum = 0; - for (uint j = cnt; j > 0; j--) - sum += *(res++); - sums[0] = sum; - cnt = (n >> pmax); - for (int i = 1; i < parts; i++) - { - sum = 0; - for (uint j = cnt; j > 0; j--) - sum += *(res++); - sums[i] = sum; - } - } - - /// - /// Special case when (n >> pmax) == 18 - /// - /// - /// - /// - /// - /// - /// - static unsafe void calc_sums18(int pmin, int pmax, uint* data, uint n, uint pred_order, ulong* sums) - { - int parts = (1 << pmax); - uint* res = data + pred_order; - uint cnt = 18 - pred_order; - ulong sum = 0; - for (uint j = cnt; j > 0; j--) - sum += *(res++); - sums[0] = sum; - for (int i = 1; i < parts; i++) - { - sums[i] = 0UL + - *(res++) + *(res++) + *(res++) + *(res++) + - *(res++) + *(res++) + *(res++) + *(res++) + - *(res++) + *(res++) + *(res++) + *(res++) + - *(res++) + *(res++) + *(res++) + *(res++) + - *(res++) + *(res++); - } - } - - /// - /// Special case when (n >> pmax) == 18 - /// - /// - /// - /// - /// - /// - /// - static unsafe void calc_sums16(int pmin, int pmax, uint* data, uint n, uint pred_order, ulong* sums) - { - int parts = (1 << pmax); - uint* res = data + pred_order; - uint cnt = 16 - pred_order; - ulong sum = 0; - for (uint j = cnt; j > 0; j--) - sum += *(res++); - sums[0] = sum; - for (int i = 1; i < parts; i++) - { - sums[i] = 0UL + - *(res++) + *(res++) + *(res++) + *(res++) + - *(res++) + *(res++) + *(res++) + *(res++) + - *(res++) + *(res++) + *(res++) + *(res++) + - *(res++) + *(res++) + *(res++) + *(res++); - } - } - static unsafe uint calc_rice_params_sums(RiceContext rc, int pmin, int pmax, ulong* sums, uint n, uint pred_order, int bps) { int* parm = stackalloc int[(pmax + 1) * Flake.MAX_PARTITIONS]; @@ -962,29 +932,6 @@ namespace CUETools.Codecs.FLAKE return opt_bits; } - static unsafe uint calc_rice_params(RiceContext rc, int pmin, int pmax, int* data, uint n, uint pred_order, int bps) - { - uint* udata = stackalloc uint[(int)n]; - ulong* sums = stackalloc ulong[(pmax + 1) * Flake.MAX_PARTITIONS]; - - //assert(pmin >= 0 && pmin <= Flake.MAX_PARTITION_ORDER); - //assert(pmax >= 0 && pmax <= Flake.MAX_PARTITION_ORDER); - //assert(pmin <= pmax); - - for (uint i = 0; i < n; i++) - udata[i] = (uint) ((data[i] << 1) ^ (data[i] >> 31)); - - // sums for highest level - if ((n >> pmax) == 18) - calc_sums18(pmin, pmax, udata, n, pred_order, sums + pmax * Flake.MAX_PARTITIONS); - else if ((n >> pmax) == 16) - calc_sums16(pmin, pmax, udata, n, pred_order, sums + pmax * Flake.MAX_PARTITIONS); - else - calc_sums(pmin, pmax, udata, n, pred_order, sums + pmax * Flake.MAX_PARTITIONS); - - return calc_rice_params_sums(rc, pmin, pmax, sums, n, pred_order, bps); - } - static int get_max_p_order(int max_porder, int n, int order) { int porder = Math.Min(max_porder, BitReader.log2i(n ^ (n - 1))); @@ -1248,7 +1195,7 @@ new int[] { // 30 else if (frame.blocksize <= 16384) lpc_precision = 14U; else lpc_precision = 15; - for (int i_precision = eparams.lpc_min_precision_search; i_precision <= eparams.lpc_max_precision_search && lpc_precision + i_precision < 16; i_precision++) + for (int i_precision = m_settings.MinPrecisionSearch; i_precision <= m_settings.MaxPrecisionSearch && lpc_precision + i_precision < 16; i_precision++) // check if we already calculated with this order, window and precision if ((frame.subframes[ch].lpc_ctx[iWindow].done_lpcs[i_precision] & (1U << (order - 1))) == 0) { @@ -1295,13 +1242,13 @@ new int[] { // 30 } } - unsafe void encode_residual_fixed_sub(FlacFrame frame, int order, int ch) - { - if ((frame.subframes[ch].done_fixed & (1U << order)) != 0) - return; // already calculated; + unsafe void encode_residual_fixed_sub(FlacFrame frame, int order, int ch) + { + if ((frame.subframes[ch].done_fixed & (1U << order)) != 0) + return; // already calculated; - frame.current.order = order; - frame.current.type = SubframeType.Fixed; + frame.current.order = order; + frame.current.type = SubframeType.Fixed; #if XXX int best_order = order; @@ -1318,17 +1265,18 @@ new int[] { // 30 } 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); - frame.current.size = (uint)(frame.current.order * frame.subframes[ch].obits) + 6 - + calc_rice_params(frame.current.rc, pmin, pmax, frame.current.residual, (uint)frame.blocksize, (uint)frame.current.order, Settings.PCM.BitsPerSample); + ulong* sums = stackalloc ulong[(pmax + 1) * Flake.MAX_PARTITIONS]; + encode_residual_fixed(frame.current.residual, frame.subframes[ch].samples, frame.blocksize, frame.current.order, sums + pmax * Flake.MAX_PARTITIONS, pmax); - frame.subframes[ch].done_fixed |= (1U << order); + frame.current.size = (uint)(frame.current.order * frame.subframes[ch].obits) + 6 + + calc_rice_params_sums(frame.current.rc, pmin, pmax, sums, (uint)frame.blocksize, (uint)frame.current.order, Settings.PCM.BitsPerSample); - frame.ChooseBestSubframe(ch); - } + frame.subframes[ch].done_fixed |= (1U << order); + + frame.ChooseBestSubframe(ch); + } unsafe void fixed_compute_best_predictor(int* data, uint data_len, ulong* errors)//, float* residual_bits_per_sample) { @@ -1505,14 +1453,14 @@ new int[] { // 30 switch (omethod) { case OrderMethod.Akaike: - //lpc_ctx.SortOrdersAkaike(frame.blocksize, eparams.estimation_depth, max_order, 7.1, 0.0); - lpc_ctx.SortOrdersAkaike(frame.blocksize, eparams.estimation_depth, min_order, max_order, 4.5, 0); + //lpc_ctx.SortOrdersAkaike(frame.blocksize, m_settings.EstimationDepth, max_order, 7.1, 0.0); + lpc_ctx.SortOrdersAkaike(frame.blocksize, m_settings.EstimationDepth, min_order, max_order, 4.5, 0); break; default: throw new Exception("unknown order method"); } - for (i = 0; i < eparams.estimation_depth && i < max_order; i++) + for (i = 0; i < m_settings.EstimationDepth && i < max_order; i++) encode_residual_lpc_sub(frame, lpcs, iWindow, lpc_ctx.best_orders[i], ch); } @@ -1531,10 +1479,13 @@ new int[] { // 30 if (min_fixed_order == 0 && max_fixed_order == 4) { - if (frame.subframes[ch].done_fixed == 0) + fixed (ulong* fixed_errors = frame.subframes[ch].best_fixed) { - ulong* fixed_errors = stackalloc ulong[5]; - fixed_compute_best_predictor(smp + 4, (uint)n - 4, fixed_errors); + if ((frame.subframes[ch].done_fixed & (1U << 5)) == 0) + { + fixed_compute_best_predictor(smp + 4, (uint)n - 4, fixed_errors); + frame.subframes[ch].done_fixed |= (1U << 5); + } i = fixed_compute_best_predictor_order(fixed_errors); encode_residual_fixed_sub(frame, i, ch); } @@ -1709,31 +1660,31 @@ new int[] { // 30 int max_prediction_order = m_settings.MaxLPCOrder; //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 lpc_min_precision_search = m_settings.MinPrecisionSearch; + int lpc_max_precision_search = m_settings.MaxPrecisionSearch; int max_partition_order = m_settings.MaxPartitionOrder; - int estimation_depth = eparams.estimation_depth; + int estimation_depth = m_settings.EstimationDepth; var development_mode = eparams.development_mode; //m_settings.MinFixedOrder = 2; //m_settings.MaxFixedOrder = 2; - eparams.lpc_min_precision_search = eparams.lpc_max_precision_search; + m_settings.MinPrecisionSearch = m_settings.MaxPrecisionSearch; m_settings.MaxLPCOrder = Math.Min(m_settings.MaxLPCOrder, Math.Max(m_settings.MinLPCOrder, 8)); - eparams.estimation_depth = 1; + m_settings.EstimationDepth = 1; eparams.development_mode = Math.Min(eparams.development_mode, -1); - encode_residual(frame, ch, eparams.prediction_type, OrderMethod.Akaike, 1, windows_mask); + encode_residual(frame, ch, m_settings.PredictionType, 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; + m_settings.MinPrecisionSearch = lpc_min_precision_search; + m_settings.MaxPrecisionSearch = lpc_max_precision_search; m_settings.MaxPartitionOrder = max_partition_order; - eparams.estimation_depth = estimation_depth; + m_settings.EstimationDepth = estimation_depth; eparams.development_mode = development_mode; } unsafe void encode_residual_pass2(FlacFrame frame, int ch) { - encode_residual(frame, ch, eparams.prediction_type, eparams.order_method, 2, estimate_best_windows(frame, ch)); + encode_residual(frame, ch, m_settings.PredictionType, eparams.order_method, 2, estimate_best_windows(frame, ch)); } unsafe int estimate_best_windows_akaike(FlacFrame frame, int ch, int count, bool onePerType) @@ -1826,9 +1777,9 @@ new int[] { // 30 unsafe int estimate_best_windows(FlacFrame frame, int ch) { - if (_windowcount == 1 || eparams.prediction_type == PredictionType.Fixed) + if (_windowcount == 1 || m_settings.PredictionType == PredictionType.Fixed) return 1; - switch (eparams.window_method) + switch (m_settings.WindowMethod) { case WindowMethod.Estimate: return estimate_best_windows_akaike(frame, ch, 1, false); @@ -1876,7 +1827,7 @@ new int[] { // 30 { int subframes = do_midside ? channels * 2 : channels; - switch (eparams.stereo_method) + switch (m_settings.StereoMethod) { case StereoMethod.Estimate: for (int ch = 0; ch < subframes; ch++) @@ -1896,20 +1847,25 @@ new int[] { // 30 + 7.1 * frame.subframes[ch].obits * m_settings.MaxLPCOrder); } 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]; + fixed (ulong* fixed_errors = frame.subframes[ch].best_fixed) + { + if ((frame.subframes[ch].done_fixed & (1U << 5)) == 0) + { + fixed_compute_best_predictor(frame.subframes[ch].samples + 4, (uint)frame.blocksize - 4, fixed_errors); + frame.subframes[ch].done_fixed |= (1U << 5); + } + 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]; @@ -1927,6 +1883,7 @@ new int[] { // 30 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++) @@ -1985,10 +1942,11 @@ new int[] { // 30 unsafe void encode_estimated_frame(FlacFrame frame) { - switch (eparams.stereo_method) + switch (m_settings.StereoMethod) { case StereoMethod.Estimate: case StereoMethod.EstimateX: + case StereoMethod.EstimateFixed: for (int ch = 0; ch < channels; ch++) { frame.subframes[ch].best.size = AudioSamples.UINT32_MAX; @@ -2009,7 +1967,7 @@ new int[] { // 30 unsafe void calculate_window(float* window, window_function func, WindowFunction flag) { - if ((eparams.window_function & flag) == 0 || _windowcount == lpc.MAX_LPC_WINDOWS) + if ((m_settings.WindowFunctions & flag) == 0 || _windowcount == lpc.MAX_LPC_WINDOWS) return; int sz = _windowsize; float* pos1 = window + _windowcount * Flake.MAX_BLOCKSIZE * 2; @@ -2037,6 +1995,23 @@ new int[] { // 30 _windowcount++; } + class PunchoutTukeyVariant + { + public PunchoutTukeyVariant( + WindowFunction _type, + int _parts, double _overlap, double _p) + { + parts = _parts; + type = _type; + overlap = _overlap; + p = _p; + } + public WindowFunction type; + public int parts; + public double overlap; + public double p; + }; + unsafe int encode_frame(out int size) { fixed (int* s = samplesBuffer, r = residualBuffer) @@ -2044,37 +2019,56 @@ new int[] { // 30 { frame.InitSize(m_blockSize, eparams.variable_block_size != 0); - if (frame.blocksize != _windowsize && frame.blocksize > 4) + if (frame.blocksize != _windowsize && frame.blocksize > 4 && m_settings.PredictionType != PredictionType.Fixed) { _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); + var tukeys = new PunchoutTukeyVariant[] + { + new PunchoutTukeyVariant(WindowFunction.Tukey4, 4, 0, 0.03), + new PunchoutTukeyVariant(WindowFunction.Tukey4A, 4, 0, 0.03), + new PunchoutTukeyVariant(WindowFunction.Tukey4B, 4, 0, 0.03), + new PunchoutTukeyVariant(WindowFunction.Tukey4X, 4, m_settings.TukeyOverlap, m_settings.TukeyP), + new PunchoutTukeyVariant(WindowFunction.Tukey3, 3, 1.0/3, 0.03), + new PunchoutTukeyVariant(WindowFunction.Tukey3A, 3, 1.0/3, 0.03), + new PunchoutTukeyVariant(WindowFunction.Tukey3B, 3, 1.0/3, 0.03), + new PunchoutTukeyVariant(WindowFunction.Tukey3X, 3, m_settings.TukeyOverlap, m_settings.TukeyP), + new PunchoutTukeyVariant(WindowFunction.Tukey2, 2, 0.25, 0.03), + new PunchoutTukeyVariant(WindowFunction.Tukey2A, 2, 0.25, 0.03), + new PunchoutTukeyVariant(WindowFunction.Tukey2B, 2, 0.25, 0.03), + new PunchoutTukeyVariant(WindowFunction.Tukey2X, 2, m_settings.TukeyOverlap, m_settings.TukeyP), + new PunchoutTukeyVariant(WindowFunction.Tukey, 1, 0.0, 0.03), + new PunchoutTukeyVariant(WindowFunction.Tukey1A, 1, 0.0, 0.03), + new PunchoutTukeyVariant(WindowFunction.Tukey1B, 1, 0.0, 0.03), + new PunchoutTukeyVariant(WindowFunction.Tukey1X, 1, m_settings.TukeyOverlap, m_settings.TukeyP), + }; - 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) => + foreach (var tukey in tukeys) + { + if (tukey.parts == 0 || (m_settings.WindowFunctions & tukey.type) == 0) continue; + if (tukey.parts == 1) { - lpc.window_punchout_tukey(w, wsz, 0.1, - m / (tukey_parts + overlap_units), - (m + 1 + overlap_units) / (tukey_parts + overlap_units)); - }, WindowFunction.PunchoutTukey); + calculate_window(window, (w, wsz) => + { + lpc.window_tukey(w, wsz, tukey.p); + }, tukey.type); + continue; + } + double overlap = tukey.overlap; + 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, tukey.p, tukey.p, + m / (tukey.parts + overlap_units), + (m + 1 + overlap_units) / (tukey.parts + overlap_units)); + }, tukey.type); + } + if (_windowcount == 0) throw new Exception("invalid windowfunction"); int nSeg = 0; @@ -2091,8 +2085,9 @@ new int[] { // 30 sz >>= 1; } while (sz >= 32); #if NONONO - using (TextWriter tx = File.CreateText("C:\\Temp\\w.csv")) + using (TextWriter tx = File.CreateText(@"H:\ubuntu\flac\w.txt")) { +#if !NONONO int totaltotal = 0; for (int i = 0; i < _windowcount; i++) { @@ -2108,19 +2103,19 @@ new int[] { // 30 tx.WriteLine("{0} total window data", total); } tx.WriteLine("{0} grand total window data", totaltotal); - for (int x = 0; x < frame.blocksize; x++) +#endif + for (int x = 0; x < frame.blocksize; x++) { + tx.Write("{0}", x); for (int i = 0; i < _windowcount; i++) - { - tx.Write("{0}\t", window[i * Flake.MAX_BLOCKSIZE * 2 + x]); - } + tx.Write("\t{0}", window[i * Flake.MAX_BLOCKSIZE * 2 + x]); tx.WriteLine(); } } #endif } - if (channels != 2 || frame.blocksize <= 32 || eparams.stereo_method == StereoMethod.Independent) + if (channels != 2 || frame.blocksize <= 32 || m_settings.StereoMethod == StereoMethod.Independent) { frame.window_buffer = window; frame.nSeg = 0; @@ -2304,7 +2299,7 @@ new int[] { // 30 if (!inited) { if (_IO == null) - _IO = new FileStream(_path, FileMode.Create, FileAccess.Write, FileShare.Read); + _IO = new FileStream(_path, FileMode.Create, FileAccess.Write, FileShare.Read, 0x10000); inited = true; int header_size = flake_encode_init(); _IO.Write(header, 0, header_size); @@ -2408,27 +2403,36 @@ new int[] { // 30 * Write vorbis comment metadata block to byte array. * Just writes the vendor string for now. */ - int write_vorbis_comment(byte[] comment, int pos, int last) + int write_vorbis_comment(byte[] comment, int pos, int len, int last) { - BitWriter bitwriter = new BitWriter(comment, pos, 4); - Encoding enc = new ASCIIEncoding(); - int vendor_len = enc.GetBytes(Vendor, 0, Vendor.Length, comment, pos + 8); + BitWriter bitwriter = new BitWriter(comment, pos, len); + Encoding enc = new UTF8Encoding(); + byte[] str = enc.GetBytes(Vendor); // metadata header bitwriter.writebits(1, last); bitwriter.writebits(7, (int)MetadataType.VorbisComment); - bitwriter.writebits(24, vendor_len + 8); - - comment[pos + 4] = (byte)(vendor_len & 0xFF); - comment[pos + 5] = (byte)((vendor_len >> 8) & 0xFF); - comment[pos + 6] = (byte)((vendor_len >> 16) & 0xFF); - comment[pos + 7] = (byte)((vendor_len >> 24) & 0xFF); - comment[pos + 8 + vendor_len] = 0; - comment[pos + 9 + vendor_len] = 0; - comment[pos + 10 + vendor_len] = 0; - comment[pos + 11 + vendor_len] = 0; + int tagsLen = 0; + if (m_settings.Tags != null) + foreach (var t in m_settings.Tags) + tagsLen += 4 + enc.GetByteCount(t); + bitwriter.writebits(24, 8 + str.Length + tagsLen); + for (int i = 0; i < 4; i++) + bitwriter.writebits(8, (str.Length >> (i * 8)) & 0xff); + bitwriter.write(str); + int nTags = m_settings.Tags != null ? m_settings.Tags.Length : 0; + for (int i = 0; i < 4; i++) + bitwriter.writebits(8, (nTags >> (i * 8)) & 0xff); + if (m_settings.Tags != null) + foreach (var tag in m_settings.Tags) + { + str = enc.GetBytes(tag); + for (int i = 0; i < 4; i++) + bitwriter.writebits(8, (str.Length >> (i * 8)) & 0xff); + bitwriter.write(str); + } bitwriter.flush(); - return vendor_len + 12; + return bitwriter.Length; } int write_seekpoints(byte[] header, int pos, int last) @@ -2488,9 +2492,9 @@ new int[] { // 30 if (_IO.CanSeek && seek_table != null) header_size += write_seekpoints(header, header_size, last); - // vorbis comment - if (m_settings.Padding == 0) last = 1; - header_size += write_vorbis_comment(header, header_size, last); + // vorbis comments + if (m_settings.Padding == 0) last = 1; + header_size += write_vorbis_comment(header, header_size, header.Length - header_size, last); // padding if (m_settings.Padding > 0) @@ -2560,7 +2564,12 @@ new int[] { // 30 } // output header bytes - header = new byte[m_settings.Padding + 1024 + (seek_table == null ? 0 : seek_table.Length * 18)]; + int tagsLen = 0; + Encoding enc = new UTF8Encoding(); + if (m_settings.Tags != null) + foreach (var t in m_settings.Tags) + tagsLen += 4 + enc.GetByteCount(t); + header = new byte[m_settings.Padding + 1024 + (seek_table == null ? 0 : seek_table.Length * 18) + tagsLen]; header_len = write_headers(); // initialize CRC & MD5 @@ -2594,127 +2603,34 @@ new int[] { // 30 // 6 = log search public OrderMethod order_method; - // stereo decorrelation method - // set by user prior to calling flake_encode_init - // if set to less than 0, it is chosen based on compression. - // valid values are 0 to 2 - // 0 = independent L+R channels - // 1 = mid-side encoding - public StereoMethod stereo_method; - - public WindowMethod window_method; - // block time in milliseconds // set by the user prior to calling flake_encode_init // used to calculate block_size based on sample rate // can also be changed by user before encoding a frame public int block_time_ms; - // Number of LPC orders to try (for estimate mode) - // set by user prior to calling flake_encode_init - // if set to less than 0, it is chosen based on compression. - // valid values are 1 to 32 - public int estimation_depth; - - // type of linear prediction - // set by user prior to calling flake_encode_init - public PredictionType prediction_type; - // whether to use variable block sizes // set by user prior to calling flake_encode_init // 0 = fixed block size // 1 = variable block size public int variable_block_size; - // whether to try various lpc_precisions - // 0 - use only one precision - // 1 - try two precisions - public int lpc_max_precision_search; - - public int lpc_min_precision_search; - - public WindowFunction window_function; - public bool do_seektable; public int development_mode; - public int flake_set_defaults(FlakeWriterSettings settings) - { - // default to level 7 params - window_function = WindowFunction.PunchoutTukey | WindowFunction.PartialTukey | WindowFunction.Tukey; - order_method = OrderMethod.Akaike; - stereo_method = StereoMethod.Evaluate; - window_method = WindowMethod.EvaluateN; - block_time_ms = 105; - prediction_type = PredictionType.Levinson; - estimation_depth = 1; + public int flake_set_defaults(FlakeWriterSettings settings) + { + order_method = OrderMethod.Akaike; + block_time_ms = 105; variable_block_size = 0; - lpc_min_precision_search = 1; - lpc_max_precision_search = 1; - do_seektable = true; + do_seektable = true; development_mode = -1; - // differences from level 7 - switch (settings.EncoderModeIndex) - { - case 0: - prediction_type = PredictionType.Fixed; - stereo_method = StereoMethod.Independent; - window_method = WindowMethod.Estimate; - window_function = WindowFunction.Tukey; - break; - case 1: - prediction_type = PredictionType.Fixed; - stereo_method = StereoMethod.Independent; - window_method = WindowMethod.Estimate; - window_function = WindowFunction.Tukey; - break; - case 2: - stereo_method = StereoMethod.Estimate; - window_method = WindowMethod.Estimate; - window_function = WindowFunction.PartialTukey; - break; - case 3: - stereo_method = StereoMethod.Estimate; - window_method = WindowMethod.Estimate; - window_function = WindowFunction.PunchoutTukey; - break; - case 4: - stereo_method = StereoMethod.Estimate; - window_method = WindowMethod.Estimate; - window_function = WindowFunction.PunchoutTukey; - estimation_depth = 2; - break; - case 5: - window_method = WindowMethod.Estimate; - window_function = WindowFunction.PunchoutTukey; - break; - case 6: - 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; - case 9: - window_function = WindowFunction.Bartlett; - break; - case 10: - //lpc_max_precision_search = 2; - break; - case 11: - estimation_depth = 5; - //lpc_max_precision_search = 2; - variable_block_size = 4; - break; - } + if (settings.EncoderModeIndex == 11) + variable_block_size = 4; - return 0; - } + return 0; + } } } diff --git a/CUETools.Codecs.FLAKE/StereoMethod.cs b/CUETools.Codecs.FLAKE/StereoMethod.cs index 9e963f2..3e42574 100644 --- a/CUETools.Codecs.FLAKE/StereoMethod.cs +++ b/CUETools.Codecs.FLAKE/StereoMethod.cs @@ -3,11 +3,13 @@ namespace CUETools.Codecs.FLAKE { public enum StereoMethod { - Independent = 0, - Estimate = 1, - Evaluate = 2, - Search = 3, - EstimateX = 4, - EvaluateX = 5, + Invalid, + Independent, + Estimate, + Evaluate, + Search, + EstimateX, + EvaluateX, + EstimateFixed, } } diff --git a/CUETools.Codecs.FLAKE/WindowFunction.cs b/CUETools.Codecs.FLAKE/WindowFunction.cs index 2d8b25c..ff6deeb 100644 --- a/CUETools.Codecs.FLAKE/WindowFunction.cs +++ b/CUETools.Codecs.FLAKE/WindowFunction.cs @@ -8,11 +8,20 @@ Hann = 4, Flattop = 8, Bartlett = 16, - TukeyFlattop = 10, - PartialTukey = 32, - TukeyPartialTukey = 34, - TukeyFlattopPartialTukey = 42, - PunchoutTukey = 64, - TukeyFlattopPartialTukeyPunchoutTukey = 106, + Tukey2 = 32, + Tukey3 = 64, + Tukey4 = (1 << 7), + Tukey2A = (1 << 9), + Tukey2B = (1 << 10), + Tukey3A = (1 << 11), + Tukey3B = (1 << 12), + Tukey4A = (1 << 13), + Tukey4B = (1 << 14), + Tukey1A = (1 << 15), + Tukey1B = (1 << 16), + Tukey1X = (1 << 17), + Tukey2X = (1 << 18), + Tukey3X = (1 << 19), + Tukey4X = (1 << 20), } } diff --git a/CUETools.Codecs.FLAKE/WindowMethod.cs b/CUETools.Codecs.FLAKE/WindowMethod.cs index e8e7e90..45c3bd1 100644 --- a/CUETools.Codecs.FLAKE/WindowMethod.cs +++ b/CUETools.Codecs.FLAKE/WindowMethod.cs @@ -2,6 +2,7 @@ { public enum WindowMethod { + Invalid, Evaluate, Search, Estimate, diff --git a/CUETools.Codecs/AudioEncoderSettings.cs b/CUETools.Codecs/AudioEncoderSettings.cs index a342012..72f009d 100644 --- a/CUETools.Codecs/AudioEncoderSettings.cs +++ b/CUETools.Codecs/AudioEncoderSettings.cs @@ -81,11 +81,23 @@ namespace CUETools.Codecs if (attribute is DefaultValueForModeAttribute) { var defaultValueForMode = attribute as DefaultValueForModeAttribute; - res &= (int)property.GetValue(this) == defaultValueForMode.m_values[index]; + res &= property.GetValue(this).Equals(defaultValueForMode.m_values[index]); } return res; } + public int GuessEncoderMode() + { + string defaultMode; + string[] modes = this.GetSupportedModes(out defaultMode).Split(' '); + if (modes == null || modes.Length < 1) + return -1; + for (int i = 0; i < modes.Length; i++) + if (HasDefaultValuesForMode(i)) + return i; + return -1; + } + [Browsable(false)] [XmlIgnore] public AudioPCMConfig PCM diff --git a/CUETools.Codecs/BitReader.cs b/CUETools.Codecs/BitReader.cs index 6dc2c38..b7a3ac0 100644 --- a/CUETools.Codecs/BitReader.cs +++ b/CUETools.Codecs/BitReader.cs @@ -11,21 +11,32 @@ namespace CUETools.Codecs return log2i((uint)v); } + public static readonly byte[] MultiplyDeBruijnBitPosition = new byte[32] + { + 0, 9, 1, 10, 13, 21, 2, 29, 11, 14, 16, 18, 22, 25, 3, 30, + 8, 12, 20, 28, 15, 17, 24, 7, 19, 27, 23, 6, 26, 5, 4, 31 + }; + public static int log2i(ulong v) { - int n = 0; - if (0 != (v & 0xffffffff00000000)) { v >>= 32; n += 32; } - if (0 != (v & 0xffff0000)) { v >>= 16; n += 16; } - if (0 != (v & 0xff00)) { v >>= 8; n += 8; } - return n + byte_to_log2_table[v]; + v |= v >> 1; // first round down to one less than a power of 2 + v |= v >> 2; + v |= v >> 4; + v |= v >> 8; + v |= v >> 16; + if (v >> 32 == 0) + return MultiplyDeBruijnBitPosition[(uint)((uint)v * 0x07C4ACDDU) >> 27]; + return 32 + MultiplyDeBruijnBitPosition[(uint)((uint)(v >> 32) * 0x07C4ACDDU) >> 27]; } public static int log2i(uint v) { - int n = 0; - if (0 != (v & 0xffff0000)) { v >>= 16; n += 16; } - if (0 != (v & 0xff00)) { v >>= 8; n += 8; } - return n + byte_to_log2_table[v]; + v |= v >> 1; // first round down to one less than a power of 2 + v |= v >> 2; + v |= v >> 4; + v |= v >> 8; + v |= v >> 16; + return MultiplyDeBruijnBitPosition[(uint)(v * 0x07C4ACDDU) >> 27]; } public static readonly byte[] byte_to_unary_table = new byte[] @@ -48,26 +59,6 @@ namespace CUETools.Codecs 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; - public static readonly byte[] byte_to_log2_table = new byte[] - { - 0,0,1,1,2,2,2,2,3,3,3,3,3,3,3,3, - 4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4, - 5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5, - 5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5, - 6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6, - 6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6, - 6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6, - 6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6, - 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, - 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, - 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, - 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, - 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, - 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, - 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, - 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7 - }; - #endregion private byte* buffer_m; diff --git a/CUETools.Codecs/BitWriter.cs b/CUETools.Codecs/BitWriter.cs index 518110d..f8c8253 100644 --- a/CUETools.Codecs/BitWriter.cs +++ b/CUETools.Codecs/BitWriter.cs @@ -108,6 +108,12 @@ namespace CUETools.Codecs writebits(8, (byte)s[i]); } + public void write(byte[] s) + { + for (int i = 0; i < s.Length; i++) + writebits(8, s[i]); + } + public void writebits_signed(int bits, int val) { writebits(bits, val & ((1 << bits) - 1)); diff --git a/CUETools.Codecs/DefaultValueForMode.cs b/CUETools.Codecs/DefaultValueForMode.cs index c3af11e..a03c8ec 100644 --- a/CUETools.Codecs/DefaultValueForMode.cs +++ b/CUETools.Codecs/DefaultValueForMode.cs @@ -13,13 +13,13 @@ namespace CUETools.Codecs /// /// Resource manager to use; /// - public int[] m_values; + public object[] m_values; /// /// Construct the description attribute /// /// - public DefaultValueForModeAttribute(params int[] values) + public DefaultValueForModeAttribute(params object[] values) { this.m_values = values; } diff --git a/CUETools.Codecs/LPC.cs b/CUETools.Codecs/LPC.cs index 35ba011..e710a09 100644 --- a/CUETools.Codecs/LPC.cs +++ b/CUETools.Codecs/LPC.cs @@ -5,9 +5,9 @@ namespace CUETools.Codecs public class lpc { public const int MAX_LPC_ORDER = 32; - public const int MAX_LPC_WINDOWS = 8; + public const int MAX_LPC_WINDOWS = 16; public const int MAX_LPC_PRECISIONS = 4; - public const int MAX_LPC_SECTIONS = 32; + public const int MAX_LPC_SECTIONS = 128; public unsafe static void window_welch(float* window, int L) { @@ -47,9 +47,8 @@ namespace CUETools.Codecs 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, double p) { - double p = 0.5; int z = 0; int Np = (int)(p / 2.0 * L) - z; if (Np > 0) @@ -63,28 +62,29 @@ namespace CUETools.Codecs } } - public unsafe static void window_punchout_tukey(float* window, int L, double p, double start, double end) + public unsafe static void window_punchout_tukey(float* window, int L, double p, double p1, double start, double end) { int start_n = (int)(start * L); int end_n = (int)(end * L); int Np = (int)(p / 2.0 * L); + int Np1 = (int)(p1 / 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++) + for (; n < start_n - Np1; 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 (i = Np1; n < start_n; n++, i--) + window[n] = (float)(0.5 - 0.5 * Math.Cos(Math.PI * i / Np1)); } 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 (i = 1; n < end_n + Np1; n++, i++) + window[n] = (float)(0.5 - 0.5 * Math.Cos(Math.PI * i / Np1)); for (; n < L - Np; n++) window[n] = 1.0f; for (i = Np; n < L; n++, i--) @@ -358,11 +358,11 @@ 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) + compute_autocorr_glue(/*const*/ int* data, float* window, int offs, int offs1, 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; + data1[i + lag] = offs + i >= 0 && offs + i < offs1 ? data[offs + i] * window[offs + i] : 0; for (int i = min; i <= lag; ++i) { double temp = 0; diff --git a/CUETools.Codecs/LpcContext.cs b/CUETools.Codecs/LpcContext.cs index 13436b8..2337b24 100644 --- a/CUETools.Codecs/LpcContext.cs +++ b/CUETools.Codecs/LpcContext.cs @@ -83,7 +83,7 @@ namespace CUETools.Codecs 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); + lpc.compute_autocorr_glue(data, window, m_start, m_end, min_order, order, autoc); else if (m_type == SectionType.OneGlue) lpc.compute_autocorr_glue(data + m_start, min_order, order, autoc); } @@ -109,6 +109,7 @@ namespace CUETools.Codecs && w == window_segment[i1 * stride + x]) alias[i1, boundaries.Count] = i; } + if (boundaries.Count >= lpc.MAX_LPC_SECTIONS * 2) throw new IndexOutOfRangeException(); types[i, boundaries.Count] = boundaries.Count >= lpc.MAX_LPC_SECTIONS * 2 - 2 ? LpcWindowSection.SectionType.Data : w == 0.0 ? @@ -143,6 +144,7 @@ namespace CUETools.Codecs // leave room for glue if (secs[i] >= lpc.MAX_LPC_SECTIONS - 1) { + throw new IndexOutOfRangeException(); window_sections[secs[i] - 1].m_type = LpcWindowSection.SectionType.Data; window_sections[secs[i] - 1].m_end = boundaries[j + 1]; continue; @@ -165,6 +167,7 @@ namespace CUETools.Codecs secs[i]--; continue; } + if (section_id >= lpc.MAX_LPC_SECTIONS) throw new IndexOutOfRangeException(); if (alias_set[i, j] != 0 && types[i, j] != SectionType.Zero && section_id < lpc.MAX_LPC_SECTIONS) @@ -174,7 +177,7 @@ namespace CUETools.Codecs sections[i1 * lpc.MAX_LPC_SECTIONS + secs[i1] - 1].m_id = section_id; section_id++; } - // TODO: section_id for glue? + // TODO: section_id for glue? nontrivial, must be sure next sections are the same size 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 @@ -203,6 +206,15 @@ namespace CUETools.Codecs } for (int i = 0; i < _windowcount; i++) { + for (int s = 0; s < secs[i]; s++) + { + LpcWindowSection* window_sections = sections + i * lpc.MAX_LPC_SECTIONS; + if (window_sections[s].m_type == SectionType.Glue + || window_sections[s].m_type == SectionType.OneGlue) + { + window_sections[s].m_end = window_sections[s + 1].m_end; + } + } while (secs[i] < lpc.MAX_LPC_SECTIONS) { LpcWindowSection* window_sections = sections + i * lpc.MAX_LPC_SECTIONS; diff --git a/CUETools.Flake/Program.cs b/CUETools.Flake/Program.cs index e405085..d9f54cb 100644 --- a/CUETools.Flake/Program.cs +++ b/CUETools.Flake/Program.cs @@ -94,21 +94,23 @@ namespace CUETools.FlakeExe Console.WriteLine(); Console.WriteLine(" -0 .. -11 Compression level, default 7; 9..11 require --lax"); Console.WriteLine(" -o Output filename, or \"-\" for stdout, or nul."); - Console.WriteLine(" -p # Padding bytes."); - Console.WriteLine(" -q --quiet Quiet mode."); - Console.WriteLine(" --lax Allow non-subset modes"); - Console.WriteLine(" --verify Verify during encoding."); + Console.WriteLine(" -P, --padding # Padding bytes."); + Console.WriteLine(" -q, --quiet Quiet mode."); + Console.WriteLine(" -f, --force Overwrite existing files."); + Console.WriteLine(" -V, --verify Verify a correct encoding."); + Console.WriteLine(" -T, --tag FIELD=VAL Add a FLAC tag; may appear multiple times"); + Console.WriteLine(" --lax Allow encoder to generate non-Subset files"); 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 data chunk sizes in WAVE files (for pipe input)"); Console.WriteLine(); Console.WriteLine("Advanced Options:"); Console.WriteLine(); Console.WriteLine(" -b # Block size."); - Console.WriteLine(" -v # Variable block size mode (0,4)."); Console.WriteLine(" -t Prediction type (fixed,levinson,search)."); Console.WriteLine(" -s Stereo decorrelation (independent,estimate,evaluate,search)."); Console.WriteLine(" -r #[,#] Rice partition order {max} or {min},{max} (0..8)."); + Console.WriteLine(" --vbr # Variable block size mode (0,4)."); Console.WriteLine(); Console.WriteLine("LPC options:"); Console.WriteLine(); @@ -134,15 +136,9 @@ namespace CUETools.FlakeExe DateTime start = DateTime.Now; TimeSpan lastPrint = TimeSpan.FromMilliseconds(0); bool debug = false, quiet = false; - string prediction_type = null; - string stereo_method = null; - string window_method = null; string order_method = null; - string window_function = null; string input_file = null; string output_file = null; - int min_precision = -1, max_precision = -1, - estimation_depth = -1; int skip_a = 0, skip_b = 0; int intarg = -1, vbr_mode = -1, magic = -1; bool do_seektable = true; @@ -151,6 +147,7 @@ namespace CUETools.FlakeExe var settings = new FlakeWriterSettings() { AllowNonSubset = true }; bool allowNonSubset = false; bool ignore_chunk_sizes = false; + bool force = false; #if FINETUNE int finetune_depth = -1; #endif @@ -158,112 +155,141 @@ namespace CUETools.FlakeExe for (int arg = 0; arg < args.Length; arg++) { bool ok = true; - if (args[arg].Length == 0) - ok = false; - else if (args[arg] == "--debug") - debug = true; - else if ((args[arg] == "-q" || args[arg] == "--quiet")) - quiet = true; - else if (args[arg] == "--verify") - settings.DoVerify = true; - else if (args[arg] == "--no-seektable") - do_seektable = false; - else if (args[arg] == "--ignore-chunk-sizes") - ignore_chunk_sizes = true; - else if (args[arg] == "--no-md5") - settings.DoMD5 = false; - else if (args[arg] == "--lax") - allowNonSubset = true; - else if (args[arg] == "--buffered") - buffered = true; - else if ((args[arg] == "-o" || args[arg] == "--output") && ++arg < args.Length) - output_file = args[arg]; - else if ((args[arg] == "-t" || args[arg] == "--prediction-type") && ++arg < args.Length) - prediction_type = args[arg]; - else if ((args[arg] == "-s" || args[arg] == "--stereo") && ++arg < args.Length) - stereo_method = args[arg]; - else if ((args[arg] == "-m" || args[arg] == "--order-method") && ++arg < args.Length) - order_method = args[arg]; - else if ((args[arg] == "-w" || args[arg] == "--window") && ++arg < args.Length) - window_function = args[arg]; - else if (args[arg] == "--window-method" && ++arg < args.Length) - window_method = args[arg]; - else if ((args[arg] == "-r" || args[arg] == "--partition-order") && ++arg < args.Length) + try { - int min_partition_order, max_partition_order; - ok = (args[arg].Split(',').Length == 2 - && int.TryParse(args[arg].Split(',')[0], out min_partition_order) - && (settings.MinPartitionOrder = min_partition_order) != -1 - && int.TryParse(args[arg].Split(',')[1], out max_partition_order) - && (settings.MaxPartitionOrder = max_partition_order) != -1) - || (int.TryParse(args[arg], out max_partition_order) - && (settings.MaxPartitionOrder = max_partition_order) != -1); - } - else if ((args[arg] == "-l" || args[arg] == "--lpc-order") && ++arg < args.Length) - { - int min_lpc_order, max_lpc_order; - ok = (args[arg].Split(',').Length == 2 - && int.TryParse(args[arg].Split(',')[0], out min_lpc_order) - && (settings.MinLPCOrder = min_lpc_order) != -1 - && int.TryParse(args[arg].Split(',')[1], out max_lpc_order) - && (settings.MaxLPCOrder = max_lpc_order) != -1) - || (int.TryParse(args[arg], out max_lpc_order) - && (settings.MaxLPCOrder = max_lpc_order) != -1); - } - else if ((args[arg] == "-f" || args[arg] == "--fixed-order") && ++arg < args.Length) - { - int min_fixed_order, max_fixed_order; - ok = (args[arg].Split(',').Length == 2 - && int.TryParse(args[arg].Split(',')[0], out min_fixed_order) - && (settings.MinFixedOrder = min_fixed_order) != -1 - && int.TryParse(args[arg].Split(',')[1], out max_fixed_order) - && (settings.MaxFixedOrder = max_fixed_order) != -1) - || (int.TryParse(args[arg], out max_fixed_order) - && (settings.MaxFixedOrder = max_fixed_order) != -1); - } - else if (args[arg] == "--skip" && ++arg < args.Length) - { - ok = (args[arg].Split(',').Length == 2 && - int.TryParse(args[arg].Split(',')[0], out skip_a) && - int.TryParse(args[arg].Split(',')[1], out skip_b)) || - int.TryParse(args[arg], out skip_a); - } - else if ((args[arg] == "-e" || args[arg] == "--estimation-depth") && ++arg < args.Length) - ok = int.TryParse(args[arg], out estimation_depth); - else if ((args[arg] == "-c" || args[arg] == "--max-precision") && ++arg < args.Length) - { - ok = (args[arg].Split(',').Length == 2 && - int.TryParse(args[arg].Split(',')[0], out min_precision) && - int.TryParse(args[arg].Split(',')[1], out max_precision)) || - int.TryParse(args[arg], out max_precision); - } - else if ((args[arg] == "-v" || args[arg] == "--vbr")) - ok = (++arg < args.Length) && int.TryParse(args[arg], out vbr_mode); - else if ((args[arg] == "-b" || args[arg] == "--blocksize") && ++arg < args.Length && int.TryParse(args[arg], out intarg)) - settings.BlockSize = intarg; - else if ((args[arg] == "-p" || args[arg] == "--padding") && ++arg < args.Length && int.TryParse(args[arg], out intarg)) - settings.Padding = intarg; - else if (args[arg] == "--magic" && ++arg < args.Length) - ok = int.TryParse(args[arg], out magic); + if (args[arg].Length == 0) + ok = false; + else if (args[arg] == "--debug") + debug = true; + else if ((args[arg] == "-f" || args[arg] == "--force")) + force = true; + else if ((args[arg] == "-q" || args[arg] == "--quiet")) + quiet = true; + else if ((args[arg] == "-V" || args[arg] == "--verify")) + settings.DoVerify = true; + else if (args[arg] == "--no-seektable") + do_seektable = false; + else if (args[arg] == "--ignore-chunk-sizes") + ignore_chunk_sizes = true; + else if (args[arg] == "--no-md5") + settings.DoMD5 = false; + else if (args[arg] == "--lax") + allowNonSubset = true; + else if (args[arg] == "--buffered") + buffered = true; + else if ((args[arg] == "-o" || args[arg] == "--output") && ++arg < args.Length) + output_file = args[arg]; + else if ((args[arg] == "-T" || args[arg] == "--tag") && ++arg < args.Length) + { + var tags = settings.Tags != null ? new List(settings.Tags) : new List(); + tags.Add(args[arg]); + settings.Tags = tags.ToArray(); + } + else if ((args[arg] == "-t" || args[arg] == "--prediction-type") && ++arg < args.Length) + settings.PredictionType = Flake.LookupPredictionType(args[arg]); + else if ((args[arg] == "-s" || args[arg] == "--stereo") && ++arg < args.Length) + settings.StereoMethod = Flake.LookupStereoMethod(args[arg]); + else if ((args[arg] == "-m" || args[arg] == "--order-method") && ++arg < args.Length) + order_method = args[arg]; + else if ((args[arg] == "-w" || args[arg] == "--window") && ++arg < args.Length) + settings.WindowFunctions = Flake.LookupWindowFunction(args[arg]); + else if (args[arg] == "--window-method" && ++arg < args.Length) + settings.WindowMethod = Flake.LookupWindowMethod(args[arg]); + else if ((args[arg] == "-r" || args[arg] == "--partition-order") && ++arg < args.Length) + { + int min_partition_order, max_partition_order; + ok = (args[arg].Split(',').Length == 2 + && int.TryParse(args[arg].Split(',')[0], out min_partition_order) + && (settings.MinPartitionOrder = min_partition_order) != -1 + && int.TryParse(args[arg].Split(',')[1], out max_partition_order) + && (settings.MaxPartitionOrder = max_partition_order) != -1) + || (int.TryParse(args[arg], out max_partition_order) + && (settings.MaxPartitionOrder = max_partition_order) != -1); + } + else if ((args[arg] == "-l" || args[arg] == "--lpc-order") && ++arg < args.Length) + { + int min_lpc_order, max_lpc_order; + ok = (args[arg].Split(',').Length == 2 + && int.TryParse(args[arg].Split(',')[0], out min_lpc_order) + && (settings.MinLPCOrder = min_lpc_order) != -1 + && int.TryParse(args[arg].Split(',')[1], out max_lpc_order) + && (settings.MaxLPCOrder = max_lpc_order) != -1) + || (int.TryParse(args[arg], out max_lpc_order) + && (settings.MaxLPCOrder = max_lpc_order) != -1); + } + else if ((args[arg] == "--fixed-order") && ++arg < args.Length) + { + int min_fixed_order, max_fixed_order; + ok = (args[arg].Split(',').Length == 2 + && int.TryParse(args[arg].Split(',')[0], out min_fixed_order) + && (settings.MinFixedOrder = min_fixed_order) != -1 + && int.TryParse(args[arg].Split(',')[1], out max_fixed_order) + && (settings.MaxFixedOrder = max_fixed_order) != -1) + || (int.TryParse(args[arg], out max_fixed_order) + && (settings.MaxFixedOrder = max_fixed_order) != -1); + } + else if (args[arg] == "--skip" && ++arg < args.Length) + { + ok = (args[arg].Split(',').Length == 2 && + int.TryParse(args[arg].Split(',')[0], out skip_a) && + int.TryParse(args[arg].Split(',')[1], out skip_b)) || + int.TryParse(args[arg], out skip_a); + } + else if ((args[arg] == "-e" || args[arg] == "--estimation-depth") && ++arg < args.Length) + { + settings.EstimationDepth = int.Parse(args[arg]); + } + else if (args[arg] == "--tukey-parts" && ++arg < args.Length) + settings.TukeyParts = int.Parse(args[arg]); + else if (args[arg] == "--tukey-overlap" && ++arg < args.Length) + settings.TukeyOverlap = double.Parse(args[arg]); + else if (args[arg] == "--tukey-p" && ++arg < args.Length) + settings.TukeyP = double.Parse(args[arg]); + else if ((args[arg] == "-c" || args[arg] == "--max-precision") && ++arg < args.Length) + { + int min_precision = -1, max_precision = -1; + ok = (args[arg].Split(',').Length == 2 && + int.TryParse(args[arg].Split(',')[0], out min_precision) && + int.TryParse(args[arg].Split(',')[1], out max_precision)) || + int.TryParse(args[arg], out max_precision); + settings.MinPrecisionSearch = min_precision; + settings.MaxPrecisionSearch = max_precision; + } + else if (args[arg] == "--vbr") + ok = (++arg < args.Length) && int.TryParse(args[arg], out vbr_mode); + else if ((args[arg] == "-b" || args[arg] == "--blocksize") && ++arg < args.Length && int.TryParse(args[arg], out intarg)) + settings.BlockSize = intarg; + else if ((args[arg] == "-P" || args[arg] == "--padding") && ++arg < args.Length && int.TryParse(args[arg], out intarg)) + settings.Padding = intarg; + else if (args[arg] == "--magic" && ++arg < args.Length) + ok = int.TryParse(args[arg], out magic); #if FINETUNE else if (args[arg] == "--depth" && ++arg < args.Length) ok = int.TryParse(args[arg], out finetune_depth); #endif - else if (args[arg] == "--coefs" && ++arg < args.Length) - coeffs = args[arg]; - else if (args[arg] != "-" && args[arg][0] == '-' && int.TryParse(args[arg].Substring(1), out intarg)) - { - ok = intarg >= 0 && intarg <= 11; - settings.EncoderModeIndex = intarg; + else if (args[arg] == "--coefs" && ++arg < args.Length) + coeffs = args[arg]; + else if (args[arg] != "-" && args[arg][0] == '-' && int.TryParse(args[arg].Substring(1), out intarg)) + { + ok = intarg >= 0 && intarg <= 11; + settings.EncoderModeIndex = intarg; + } + else if ((args[arg][0] != '-' || args[arg] == "-") && input_file == null) + input_file = args[arg]; + else + ok = false; + if (!ok) + { + Usage(); + return 1; + } } - else if ((args[arg][0] != '-' || args[arg] == "-") && input_file == null) - input_file = args[arg]; - else - ok = false; - if (!ok) + catch (Exception ex) { Usage(); - return 1; + Console.WriteLine(""); + Console.WriteLine("{0}: {1}", args[arg - 1], ex.Message); + return 5; } } if (input_file == null || ((input_file == "-" || Path.GetExtension(input_file) == ".flac") && output_file == null)) @@ -274,8 +300,8 @@ namespace CUETools.FlakeExe if (!quiet) { - Console.WriteLine("CUETools.Flake, Copyright (C) 2009-2013 Grigory Chudov."); - Console.WriteLine("Based on Flake encoder by Justin Ruggles, ."); + Console.WriteLine("CUETools.Flake, Copyright (C) 2009-2014 Grigory Chudov."); + Console.WriteLine("Initially based on Flake encoder by Justin Ruggles."); Console.WriteLine("This is free software under the GNU GPLv3+ license; There is NO WARRANTY, to"); Console.WriteLine("the extent permitted by law. for details."); } @@ -342,6 +368,13 @@ namespace CUETools.FlakeExe audioSource = new AudioPipe(audioSource, 0x10000); if (output_file == null) output_file = Path.ChangeExtension(input_file, "flac"); + if (File.Exists(output_file) && !force) + { + Usage(); + Console.WriteLine(); + Console.WriteLine("File '{0}' already exists.", output_file); + return 9; + } settings.PCM = audioSource.PCM; settings.AllowNonSubset = allowNonSubset; FlakeWriter flake; @@ -354,22 +387,8 @@ namespace CUETools.FlakeExe settings); flake.FinalSampleCount = audioSource.Length < 0 ? -1 : audioSource.Length - skip_a - skip_b; - if (prediction_type != null) - flake.PredictionType = Flake.LookupPredictionType(prediction_type); - if (stereo_method != null) - flake.StereoMethod = Flake.LookupStereoMethod(stereo_method); - if (window_method != null) - flake.WindowMethod = Flake.LookupWindowMethod(window_method); if (order_method != null) flake.OrderMethod = Flake.LookupOrderMethod(order_method); - if (window_function != null) - flake.WindowFunction = Flake.LookupWindowFunction(window_function); - if (min_precision >= 0) - flake.MinPrecisionSearch = min_precision; - if (max_precision >= 0) - flake.MaxPrecisionSearch = max_precision; - if (estimation_depth >= 0) - flake.EstimationDepth = estimation_depth; if (vbr_mode >= 0) flake.VBRMode = vbr_mode; if (magic >= 0) @@ -479,20 +498,23 @@ namespace CUETools.FlakeExe { settings = flake.Settings as FlakeWriterSettings; Console.SetOut(stdout); - 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}", + Console.Out.WriteLine("{17}\t{0}\t{1:0.000}\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.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.PredictionType.ToString().PadRight(15), + settings.StereoMethod.ToString().PadRight(15), + (flake.OrderMethod.ToString() + "(" + settings.EstimationDepth.ToString() + ")").PadRight(15), + (settings.WindowMethod.ToString().PadRight(10) + "(" + + ((settings.WindowFunctions & WindowFunction.Tukey1X) != 0 ? "X" : (settings.WindowFunctions & WindowFunction.Tukey) != 0 ? "T" : (settings.WindowFunctions & WindowFunction.Tukey1A) != 0 ? "A" : (settings.WindowFunctions & WindowFunction.Tukey1B) != 0 ? "B" : " ") + + ((settings.WindowFunctions & WindowFunction.Tukey2X) != 0 ? "X" : (settings.WindowFunctions & WindowFunction.Tukey2) != 0 ? "2" : (settings.WindowFunctions & WindowFunction.Tukey2A) != 0 ? "A" : (settings.WindowFunctions & WindowFunction.Tukey2B) != 0 ? "B" : " ") + + ((settings.WindowFunctions & WindowFunction.Tukey3X) != 0 ? "X" : (settings.WindowFunctions & WindowFunction.Tukey3) != 0 ? "3" : (settings.WindowFunctions & WindowFunction.Tukey3A) != 0 ? "A" : (settings.WindowFunctions & WindowFunction.Tukey3B) != 0 ? "B" : " ") + + ((settings.WindowFunctions & WindowFunction.Tukey4X) != 0 ? "X" : ((settings.WindowFunctions & WindowFunction.Tukey4) != 0 ? "4" : (settings.WindowFunctions & WindowFunction.Tukey4A) != 0 ? "A" : (settings.WindowFunctions & WindowFunction.Tukey4B) != 0 ? "B" : " ") + + ((settings.WindowFunctions & WindowFunction.Welch) == 0 ? "" : "W") + + ((settings.WindowFunctions & WindowFunction.Hann) == 0 ? "" : "H") + + ((settings.WindowFunctions & WindowFunction.Flattop) == 0 ? "" : "F") + + ((settings.WindowFunctions & WindowFunction.Bartlett) == 0 ? "" : "B")).PadRight(1)) + + ((settings.WindowFunctions & (WindowFunction.Tukey1X|WindowFunction.Tukey2X|WindowFunction.Tukey2X|WindowFunction.Tukey3X|WindowFunction.Tukey4X)) != 0 ? + ("," + settings.TukeyOverlap.ToString("F3").PadLeft(6) + "," + settings.TukeyP.ToString("F3")) : "").PadRight(13) +")", settings.MinPartitionOrder, settings.MaxPartitionOrder, @@ -500,22 +522,13 @@ namespace CUETools.FlakeExe settings.MaxLPCOrder, settings.MinFixedOrder, settings.MaxFixedOrder, - flake.MinPrecisionSearch, - flake.MaxPrecisionSearch, + settings.MinPrecisionSearch, + settings.MaxPrecisionSearch, flake.Settings.BlockSize, flake.VBRMode, 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" : - "?" + settings.GuessEncoderMode().ToString().Replace("-1","?") ); } #endif