From 7b9609d0d0abe64da1baa24c4701a4e49839b575 Mon Sep 17 00:00:00 2001 From: chudov Date: Thu, 24 Dec 2009 16:14:41 +0000 Subject: [PATCH] optimizations --- CUETools.Codecs.ALAC/ALAC.cs | 36 +- CUETools.Codecs.ALAC/ALACWriter.cs | 637 +++++++++--------- .../CUETools.Codecs.ALAC.csproj | 2 +- 3 files changed, 328 insertions(+), 347 deletions(-) diff --git a/CUETools.Codecs.ALAC/ALAC.cs b/CUETools.Codecs.ALAC/ALAC.cs index e22feeb..3c33d82 100644 --- a/CUETools.Codecs.ALAC/ALAC.cs +++ b/CUETools.Codecs.ALAC/ALAC.cs @@ -31,6 +31,7 @@ namespace CUETools.Codecs.ALAC public const int MAX_RICE_PARAM = 14; public const int MAX_PARTITION_ORDER = 8; public const int MAX_PARTITIONS = 1 << MAX_PARTITION_ORDER; + public const int MAX_LPC_WINDOWS = 4; public const uint UINT32_MAX = 0xffffffff; @@ -49,6 +50,10 @@ namespace CUETools.Codecs.ALAC return (WindowFunction)(Enum.Parse(typeof(WindowFunction), name, true)); } + public static WindowMethod LookupWindowMethod(string name) + { + return (WindowMethod)(Enum.Parse(typeof(WindowMethod), name, true)); + } } unsafe class RiceContext @@ -80,6 +85,7 @@ namespace CUETools.Codecs.ALAC { rc = new RiceContext(); coefs = new int[lpc.MAX_LPC_ORDER]; + coefs_adapted = new int[lpc.MAX_LPC_ORDER]; } public int order; public int* residual; @@ -90,6 +96,7 @@ namespace CUETools.Codecs.ALAC public int cbits; public int shift; public int[] coefs; + public int[] coefs_adapted; public int window; }; @@ -98,8 +105,8 @@ namespace CUETools.Codecs.ALAC public ALACSubframeInfo() { best = new ALACSubframe(); - lpc_ctx = new LpcContext[lpc.MAX_LPC_WINDOWS]; - for (int i = 0; i < lpc.MAX_LPC_WINDOWS; i++) + lpc_ctx = new LpcContext[Alac.MAX_LPC_WINDOWS]; + for (int i = 0; i < Alac.MAX_LPC_WINDOWS; i++) lpc_ctx[i] = new LpcContext(); } @@ -108,7 +115,8 @@ namespace CUETools.Codecs.ALAC samples = s; best.residual = r; best.size = AudioSamples.UINT32_MAX; - for (int iWindow = 0; iWindow < lpc.MAX_LPC_WINDOWS; iWindow++) + best.order = 0; + for (int iWindow = 0; iWindow < Alac.MAX_LPC_WINDOWS; iWindow++) lpc_ctx[iWindow].Reset(); done_fixed = 0; } @@ -181,26 +189,27 @@ namespace CUETools.Codecs.ALAC public int interlacing_shift, interlacing_leftweight; public ALACSubframeInfo[] subframes; public ALACSubframe current; - public double* window_buffer; + public float* window_buffer; } public enum OrderMethod { - Max = 0, - Estimate = 1, - LogFast = 2, - LogSearch = 3, - EstSearch2 = 4, - Search = 5 + Estimate = 0 } public enum StereoMethod { Independent = 0, Estimate = 1, - Estimate2 = 2, - Evaluate = 3, - Search = 4 + Evaluate = 2, + Search = 3, + } + + public enum WindowMethod + { + Estimate = 0, + Evaluate = 1, + Search = 2 } public enum FrameType @@ -224,6 +233,7 @@ namespace CUETools.Codecs.ALAC Tukey = 2, Hann = 4, Flattop = 8, + Bartlett = 16, TukFlat = 10 } } diff --git a/CUETools.Codecs.ALAC/ALACWriter.cs b/CUETools.Codecs.ALAC/ALACWriter.cs index 0db3e55..d036251 100644 --- a/CUETools.Codecs.ALAC/ALACWriter.cs +++ b/CUETools.Codecs.ALAC/ALACWriter.cs @@ -19,12 +19,15 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ +#define INTEROP + using System; using System.Text; using System.IO; using System.Collections.Generic; -using System.Collections.Specialized; -//using System.Runtime.InteropServices; +#if INTEROP +using System.Runtime.InteropServices; +#endif using CUETools.Codecs; namespace CUETools.Codecs.ALAC @@ -56,7 +59,7 @@ namespace CUETools.Codecs.ALAC // this can be used to allocate memory for output int max_frame_size; - int initial_history, history_mult, k_modifier; + int initial_history = 10, history_mult = 40, k_modifier = 14; byte[] frame_buffer = null; @@ -64,7 +67,9 @@ namespace CUETools.Codecs.ALAC long first_frame_offset = 0; +#if INTEROP TimeSpan _userProcessorTime; +#endif // header bytes byte[] header; @@ -73,10 +78,10 @@ namespace CUETools.Codecs.ALAC int[] samplesBuffer; int[] verifyBuffer; int[] residualBuffer; - double[] windowBuffer; + float[] windowBuffer; int samplesInBuffer = 0; - int _compressionLevel = 4; + int _compressionLevel = 5; int _blocksize = 0; int _totalSize = 0; int _windowsize = 0, _windowcount = 0; @@ -108,7 +113,7 @@ namespace CUETools.Codecs.ALAC samplesBuffer = new int[Alac.MAX_BLOCKSIZE * (channels == 2 ? 5 : channels)]; residualBuffer = new int[Alac.MAX_BLOCKSIZE * (channels == 2 ? 6 : channels + 1)]; - windowBuffer = new double[Alac.MAX_BLOCKSIZE * 2 * lpc.MAX_LPC_WINDOWS]; + windowBuffer = new float[Alac.MAX_BLOCKSIZE * 2 * Alac.MAX_LPC_WINDOWS]; eparams.set_defaults(_compressionLevel); eparams.padding_size = 8192; @@ -147,17 +152,19 @@ namespace CUETools.Codecs.ALAC } set { - if (value < 0 || value > 11) + if (value < 0 || value > 10) throw new Exception("unsupported compression level"); _compressionLevel = value; eparams.set_defaults(_compressionLevel); } } - //[DllImport("kernel32.dll")] - //static extern bool GetThreadTimes(IntPtr hThread, out long lpCreationTime, out long lpExitTime, out long lpKernelTime, out long lpUserTime); - //[DllImport("kernel32.dll")] - //static extern IntPtr GetCurrentThread(); +#if INTEROP + [DllImport("kernel32.dll")] + static extern bool GetThreadTimes(IntPtr hThread, out long lpCreationTime, out long lpExitTime, out long lpKernelTime, out long lpUserTime); + [DllImport("kernel32.dll")] + static extern IntPtr GetCurrentThread(); +#endif void chunk_start(BitWriter bitwriter) { @@ -201,9 +208,11 @@ namespace CUETools.Codecs.ALAC inited = false; } - //long fake, KernelStart, UserStart; - //GetThreadTimes(GetCurrentThread(), out fake, out fake, out KernelStart, out UserStart); - //_userProcessorTime = new TimeSpan(UserStart); +#if INTEROP + long fake, KernelStart, UserStart; + GetThreadTimes(GetCurrentThread(), out fake, out fake, out KernelStart, out UserStart); + _userProcessorTime = new TimeSpan(UserStart); +#endif } public void Close() @@ -256,6 +265,12 @@ namespace CUETools.Codecs.ALAC set { eparams.stereo_method = value; } } + public WindowMethod WindowMethod + { + get { return eparams.window_method; } + set { eparams.window_method = value; } + } + public WindowFunction WindowFunction { get { return eparams.window_function; } @@ -330,7 +345,7 @@ namespace CUETools.Codecs.ALAC } set { - if (value > 100) + if (value > 7) throw new Exception("invalid MaxHistoryModifier " + value.ToString()); eparams.max_modifier = value; if (eparams.min_modifier > value) @@ -338,6 +353,34 @@ namespace CUETools.Codecs.ALAC } } + public int HistoryMult + { + get + { + return history_mult; + } + set + { + if (value < 1 || value > 255) + throw new Exception("invalid history_mult"); + history_mult = value; + } + } + + public int InitialHistory + { + get + { + return initial_history; + } + set + { + if (value < 1 || value > 255) + throw new Exception("invalid initial_history"); + initial_history = value; + } + } + public int EstimationDepth { get @@ -352,9 +395,30 @@ namespace CUETools.Codecs.ALAC } } + public int AdaptivePasses + { + get + { + return eparams.adaptive_passes; + } + set + { + if (value >= lpc.MAX_LPC_PRECISIONS || value < 0) + throw new Exception("invalid adaptive_passes " + value.ToString()); + eparams.adaptive_passes = value; + } + } + public TimeSpan UserProcessorTime { - get { return _userProcessorTime; } + get + { +#if INTEROP + return _userProcessorTime; +#else + return TimeSpan(0); +#endif + } } public int BitsPerSample @@ -385,15 +449,6 @@ namespace CUETools.Codecs.ALAC samplesInBuffer += block; } - unsafe static void channel_decorrelation(int* leftS, int* rightS, int *leftM, int *rightM, int blocksize) - { - for (int i = 0; i < blocksize; i++) - { - leftM[i] = (leftS[i] + rightS[i]) >> 1; - rightM[i] = leftS[i] - rightS[i]; - } - } - unsafe static void channel_decorrelation(int* leftS, int* rightS, int* leftM, int* rightM, int blocksize, int leftweight, int shift) { for (int i = 0; i < blocksize; i++) @@ -408,9 +463,9 @@ namespace CUETools.Codecs.ALAC return (val << (32 - bits)) >> (32 - bits); } - private static short sign_only(int val) + private static int sign_only(int val) { - return (short)((val >> 31) + ((val - 1) >> 31) + 1); + return (val >> 31) + ((val - 1) >> 31) + 1; } unsafe static void alac_encode_residual_31(int* res, int* smp, int n) @@ -428,7 +483,7 @@ namespace CUETools.Codecs.ALAC unsafe static void alac_encode_residual(int* res, int* smp, int n, int order, int* coefs, int shift, int bps) { int csum = 0; - + for (int i = order - 1; i >= 0; i--) csum += coefs[i]; @@ -444,24 +499,31 @@ namespace CUETools.Codecs.ALAC for (int i = order + 1; i < n; i++) { int sample = *(smp++); - int/*long*/ sum = (1 << (shift - 1)) -csum * sample; - int sum2 = 0; - for (int j = 0; j < order; j+= 2) - { - sum += smp[j] * coefs[j]; - sum2 += smp[j+1] * coefs[j+1]; - } - int resval = extend_sign32(smp[order] - (int)((sum + sum2) >> shift) - sample, bps); + int/*long*/ sum = (1 << (shift - 1)); + for (int j = 0; j < order; j++) + sum += (smp[j] - sample) * coefs[j]; + int resval = extend_sign32(smp[order] - sample - (int)(sum >> shift), bps); res[i] = resval; - int error_sign = sign_only(resval); - for (int j = 0; j < order && resval * error_sign > 0; j++) + + if (resval > 0) { - int val = sample - smp[j]; - int sign = error_sign * sign_only(val); - coefs[j] -= sign; - csum -= sign; - resval -= ((val * sign) >> shift) * (j + 1); - //error_sign = (error_sign + sign_only(resval)) / 2; + for (int j = 0; j < order && resval > 0; j++) + { + int val = smp[j] - sample; + int sign = sign_only(val); + coefs[j] += sign; + resval -= (val * sign >> shift) * (j + 1); + } + } + else + { + for (int j = 0; j < order && resval < 0; j++) + { + int val = smp[j] - sample; + int sign = -sign_only(val); + coefs[j] += sign; + resval -= (val * sign >> shift) * (j + 1); + } } } res[n] = 1; // Stop byte to help alac_entropy_coder; @@ -507,7 +569,7 @@ namespace CUETools.Codecs.ALAC modifier = eparams.min_modifier; for (int i = eparams.min_modifier; i <= eparams.max_modifier; i++) { - int newsize = alac_entropy_coder(res, n, bps, i); + int newsize = alac_entropy_estimate(res, n, bps, i); if (size > newsize) { size = newsize; @@ -523,15 +585,13 @@ namespace CUETools.Codecs.ALAC int sign_modifier = 0; int rice_historymult = modifier * history_mult / 4; int size = 0; + int* fin = res + n; - for (int i = 0; i < n; ) + while (res < fin) { int k = BitReader.log2i((history >> 9) + 3); - int x = -2 * (*res) - 1; - x ^= (x >> 31); - - res++; - i++; + int x = *(res++); + x = (x << 1) ^ (x >> 31); size += encode_scalar(x - sign_modifier, Math.Min(k, k_modifier), bps); @@ -541,14 +601,13 @@ namespace CUETools.Codecs.ALAC if (x > 0xFFFF) history = 0xFFFF; - if (history < 128 && i < n) + if (history < 128 && res < fin) { k = 7 - BitReader.log2i(history) + ((history + 16) >> 6); - int block_size = 0; - while (res[block_size] == 0) // we have a stop byte, so need not check if i + blocksize < n - block_size++; - res += block_size; - i += block_size; + int* res1 = res; + while (*res == 0) // we have a stop byte, so need not check if res < fin + res++; + int block_size = (int)(res - res1); size += encode_scalar(block_size, Math.Min(k, k_modifier), 16); //sign_modifier = (block_size <= 0xFFFF) ? 1 : 0; //never happens sign_modifier = 1; @@ -558,20 +617,45 @@ namespace CUETools.Codecs.ALAC return size; } + /// + /// Crude estimation of entropy length + /// + /// + /// + /// + /// + /// + unsafe int alac_entropy_estimate(int* res, int n, int bps, int modifier) + { + int history = initial_history; + int rice_historymult = modifier * history_mult / 4; + int size = 0; + int* fin = res + n; + + while (res < fin) + { + int x = *(res++); + x = (x << 1) ^ (x >> 31); + int k = BitReader.log2i((history >> 9) + 3); + k = k > k_modifier ? k_modifier : k; + size += (x >> k) > 8 ? 9 + bps : (x >> k) + k + 1; + history += x * rice_historymult - ((history * rice_historymult) >> 9); + } + return size; + } + unsafe void alac_entropy_coder(BitWriter bitwriter, int* res, int n, int bps, int modifier) { int history = initial_history; int sign_modifier = 0; int rice_historymult = modifier * history_mult / 4; + int* fin = res + n; - for (int i = 0; i < n; ) + while (res < fin) { int k = BitReader.log2i((history >> 9) + 3); - int x = -2 * (*res) - 1; - x ^= (x >> 31); - - res++; - i++; + int x = *(res++); + x = (x << 1) ^ (x >> 31); encode_scalar(bitwriter, x - sign_modifier, k, bps); @@ -581,14 +665,13 @@ namespace CUETools.Codecs.ALAC if (x > 0xFFFF) history = 0xFFFF; - if (history < 128 && i < n) + if (history < 128 && res < fin) { k = 7 - BitReader.log2i(history) + ((history + 16) >> 6); - int block_size = 0; - while (res[block_size] == 0) // we have a stop byte, so need not check if i + blocksize < n - block_size++; - res += block_size; - i += block_size; + int* res1 = res; + while (*res == 0) // we have a stop byte, so need not check if res < fin + res++; + int block_size = (int)(res - res1); encode_scalar(bitwriter, block_size, k, 16); sign_modifier = (block_size <= 0xFFFF) ? 1 : 0; history = 0; @@ -596,35 +679,30 @@ namespace CUETools.Codecs.ALAC } } - unsafe void encode_residual_lpc_sub(ALACFrame frame, double * lpcs, int iWindow, int order, int ch) + unsafe void encode_residual_lpc_sub(ALACFrame frame, float* lpcs, int iWindow, int order, int ch) { - // select LPC precision based on block size - uint lpc_precision = 15; - int i_precision = 0; - //if (frame.blocksize <= 192) lpc_precision = 7U; - //else if (frame.blocksize <= 384) lpc_precision = 8U; - //else if (frame.blocksize <= 576) lpc_precision = 9U; - //else if (frame.blocksize <= 1152) lpc_precision = 10U; - //else if (frame.blocksize <= 2304) lpc_precision = 11U; - //else if (frame.blocksize <= 4608) lpc_precision = 12U; - //else if (frame.blocksize <= 8192) lpc_precision = 13U; - //else if (frame.blocksize <= 16384) lpc_precision = 14U; + // check if we already calculated with this order, window and precision + if ((frame.subframes[ch].lpc_ctx[iWindow].done_lpcs[eparams.adaptive_passes] & (1U << (order - 1))) == 0) + { + frame.subframes[ch].lpc_ctx[iWindow].done_lpcs[eparams.adaptive_passes] |= (1U << (order - 1)); - //for (int i_precision = eparams.lpc_min_precision_search; i_precision <= eparams.lpc_max_precision_search && 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) + uint cbits = 15U; + + frame.current.order = order; + frame.current.window = iWindow; + + int bps = (int)bits_per_sample + channels - 1; + + int* coefs = stackalloc int[lpc.MAX_LPC_ORDER]; + + //if (frame.subframes[ch].best.order == order && frame.subframes[ch].best.window == iWindow) + //{ + // frame.current.shift = frame.subframes[ch].best.shift; + // for (int i = 0; i < frame.current.order; i++) + // frame.current.coefs[i] = frame.subframes[ch].best.coefs_adapted[i]; + //} + //else { - frame.subframes[ch].lpc_ctx[iWindow].done_lpcs[i_precision] |= (1U << (order - 1)); - - uint cbits = lpc_precision + (uint)i_precision; - - frame.current.order = order; - frame.current.window = iWindow; - - int bps = (int)bits_per_sample + channels - 1; - - int* coefs = stackalloc int[lpc.MAX_LPC_ORDER]; - lpc.quantize_lpc_coefs(lpcs + (frame.current.order - 1) * lpc.MAX_LPC_ORDER, frame.current.order, cbits, coefs, out frame.current.shift, 15, 1); @@ -633,25 +711,41 @@ namespace CUETools.Codecs.ALAC for (int i = 0; i < frame.current.order; i++) frame.current.coefs[i] = coefs[i]; + } + for (int i = 0; i < frame.current.order; i++) + coefs[i] = frame.current.coefs[frame.current.order - 1 - i]; + for (int i = frame.current.order; i < lpc.MAX_LPC_ORDER; i++) + coefs[i] = 0; + + alac_encode_residual(frame.current.residual, frame.subframes[ch].samples, frame.blocksize, + frame.current.order, coefs, frame.current.shift, bps); + + for (int i = 0; i < frame.current.order; i++) + frame.current.coefs_adapted[i] = coefs[frame.current.order - 1 - i]; + + for (int adaptive_pass = 0; adaptive_pass < eparams.adaptive_passes; adaptive_pass++) + { for (int i = 0; i < frame.current.order; i++) - coefs[i] = frame.current.coefs[frame.current.order - 1 - i]; - coefs[frame.current.order] = 0; + frame.current.coefs[i] = frame.current.coefs_adapted[i]; alac_encode_residual(frame.current.residual, frame.subframes[ch].samples, frame.blocksize, frame.current.order, coefs, frame.current.shift, bps); - frame.current.size = (uint)(alac_entropy_coder(frame.current.residual, frame.blocksize, bps, out frame.current.ricemodifier) + 16 + 16 * order); - - frame.ChooseBestSubframe(ch); + for (int i = 0; i < frame.current.order; i++) + frame.current.coefs_adapted[i] = coefs[frame.current.order - 1 - i]; } + + frame.current.size = (uint)(alac_entropy_estimate(frame.current.residual, frame.blocksize, bps, eparams.max_modifier) + 16 + 16 * order); + + frame.ChooseBestSubframe(ch); + } } - unsafe void encode_residual(ALACFrame frame, int ch, OrderMethod omethod, int pass) + unsafe void encode_residual(ALACFrame frame, int ch, int pass, int best_window) { int* smp = frame.subframes[ch].samples; int i, n = frame.blocksize; - int best_window = frame.subframes[ch].best.window; int bps = (int)bits_per_sample + channels - 1; // FIXED @@ -678,70 +772,22 @@ namespace CUETools.Codecs.ALAC if (n < eparams.max_prediction_order) return; - double* lpcs = stackalloc double[lpc.MAX_LPC_ORDER * lpc.MAX_LPC_ORDER]; + float* lpcs = stackalloc float[lpc.MAX_LPC_ORDER * lpc.MAX_LPC_ORDER]; int min_order = eparams.min_prediction_order; int max_order = eparams.max_prediction_order; 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]; lpc_ctx.GetReflection(max_order, smp, n, frame.window_buffer + iWindow * Alac.MAX_BLOCKSIZE * 2); lpc_ctx.ComputeLPC(lpcs); - - switch (omethod) - { - case OrderMethod.Max: - // always use maximum order - encode_residual_lpc_sub(frame, lpcs, iWindow, max_order, ch); - break; - case OrderMethod.Estimate: - // estimated orders - // Search at reflection coeff thresholds (where they cross 0.10) - { - int found = 0; - for (i = max_order; i >= min_order && found < eparams.estimation_depth; i--) - if (lpc_ctx.IsInterestingOrder(i)) - { - encode_residual_lpc_sub(frame, lpcs, iWindow, i, ch); - found++; - } - if (0 == found) - encode_residual_lpc_sub(frame, lpcs, iWindow, min_order, ch); - } - break; - case OrderMethod.EstSearch2: - // Search at reflection coeff thresholds (where they cross 0.10) - { - int found = 0; - for (i = min_order; i <= max_order && found < eparams.estimation_depth; i++) - if (lpc_ctx.IsInterestingOrder(i)) - { - encode_residual_lpc_sub(frame, lpcs, iWindow, i, ch); - found++; - } - if (0 == found) - encode_residual_lpc_sub(frame, lpcs, iWindow, min_order, ch); - } - break; - case OrderMethod.Search: - // brute-force optimal order search - for (i = max_order; i >= min_order; i--) - encode_residual_lpc_sub(frame, lpcs, iWindow, i, ch); - break; - case OrderMethod.LogFast: - // Try max, est, 32,16,8,4,2,1 - encode_residual_lpc_sub(frame, lpcs, iWindow, max_order, ch); - for (i = lpc.MAX_LPC_ORDER; i >= min_order; i >>= 1) - if (i < max_order) - encode_residual_lpc_sub(frame, lpcs, iWindow, i, ch); - break; - default: - throw new Exception("unknown ordermethod"); - } + lpc_ctx.SortOrdersAkaike(frame.blocksize, eparams.estimation_depth, max_order, 5.0, 1.0/18); + for (i = 0; i < eparams.estimation_depth && i < max_order; i++) + encode_residual_lpc_sub(frame, lpcs, iWindow, lpc_ctx.best_orders[i], ch); } } @@ -777,82 +823,58 @@ namespace CUETools.Codecs.ALAC bitwriter.flush(); } - unsafe void window_welch(double* window, int L) - { - int N = L - 1; - double N2 = (double)N / 2.0; - - for (int n = 0; n <= N; n++) - { - double k = 1 / N2 - 1.0 - Math.Min(n, N - n); - //double k = ((double)n - N2) / N2; - window[n] = 1.0 - k * k; - } - } - - unsafe void window_rectangle(double* window, int L) - { - for (int n = 0; n < L; n++) - window[n] = 1.0; - } - - unsafe void window_flattop(double* window, int L) - { - int N = L - 1; - for (int n = 0; n < L; n++) - window[n] = 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); - } - - unsafe void window_tukey(double* window, int L) - { - window_rectangle(window, L); - double p = 0.5; - int Np = (int)(p / 2.0 * L) - 1; - if (Np > 0) - { - for (int n = 0; n <= Np; n++) - { - window[n] = 0.5 - 0.5 * Math.Cos(Math.PI * n / Np); - window[L - Np - 1 + n] = 0.5 - 0.5 * Math.Cos(Math.PI * (n + Np) / Np); - } - } - } - - unsafe void window_hann(double* window, int L) - { - int N = L - 1; - for (int n = 0; n < L; n++) - window[n] = 0.5 - 0.5 * Math.Cos(2.0 * Math.PI * n / N); - } - - unsafe void encode_residual_pass1(ALACFrame frame, int ch) + unsafe void encode_residual_pass1(ALACFrame frame, int ch, int best_window) { int max_prediction_order = eparams.max_prediction_order; int estimation_depth = eparams.estimation_depth; int min_modifier = eparams.min_modifier; + int adaptive_passes = eparams.adaptive_passes; eparams.max_prediction_order = Math.Min(8,eparams.max_prediction_order); eparams.estimation_depth = 1; eparams.min_modifier = eparams.max_modifier; - encode_residual(frame, ch, OrderMethod.Estimate, 1); + eparams.adaptive_passes = 0; + encode_residual(frame, ch, 1, best_window); eparams.max_prediction_order = max_prediction_order; eparams.estimation_depth = estimation_depth; eparams.min_modifier = min_modifier; + eparams.adaptive_passes = adaptive_passes; } unsafe void encode_residual_pass2(ALACFrame frame, int ch) { - encode_residual(frame, ch, eparams.order_method, 2); + encode_residual(frame, ch, 2, estimate_best_window(frame, ch)); } - unsafe void encode_residual_onepass(ALACFrame frame, int ch) + unsafe int estimate_best_window(ALACFrame 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); + 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 * Alac.MAX_BLOCKSIZE * 2); + double err = frame.subframes[ch].lpc_ctx[i].prediction_error[order - 1] / frame.subframes[ch].lpc_ctx[i].autocorr_values[0]; + if (best_window == -1 || best_error > err) + { + best_window = i; + best_error = err; + } + } + return best_window; + } + case WindowMethod.Evaluate: + encode_residual_pass1(frame, ch, -1); + return frame.subframes[ch].best.window; + case WindowMethod.Search: + return -1; } - else - encode_residual(frame, ch, eparams.order_method, 0); + return -1; } unsafe void estimate_frame(ALACFrame frame, bool do_midside) @@ -861,18 +883,24 @@ namespace CUETools.Codecs.ALAC switch (eparams.stereo_method) { - case StereoMethod.Evaluate: + case StereoMethod.Estimate: for (int ch = 0; ch < subframes; ch++) { - int windowcount = _windowcount; - _windowcount = 1; - encode_residual_pass1(frame, ch); - _windowcount = windowcount; + LpcContext lpc_ctx = frame.subframes[ch].lpc_ctx[0]; + int stereo_order = Math.Min(8, eparams.max_prediction_order); + double alpha = 1.5; // 4.5 + eparams.max_prediction_order / 10.0; + lpc_ctx.GetReflection(stereo_order, frame.subframes[ch].samples, frame.blocksize, frame.window_buffer); + lpc_ctx.SortOrdersAkaike(frame.blocksize, 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); + break; case StereoMethod.Search: for (int ch = 0; ch < subframes; ch++) - encode_residual_onepass(frame, ch); + encode_residual_pass2(frame, ch); break; } } @@ -920,27 +948,30 @@ namespace CUETools.Codecs.ALAC { switch (eparams.stereo_method) { - case StereoMethod.Evaluate: + case StereoMethod.Estimate: for (int ch = 0; ch < channels; ch++) { - if (_windowcount > 1) - encode_residual_pass1(frame, 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++) + encode_residual_pass2(frame, ch); + break; case StereoMethod.Search: break; } } - unsafe delegate void window_function(double* window, int size); + unsafe delegate void window_function(float* window, int size); - unsafe void calculate_window(double* window, window_function func, WindowFunction flag) + unsafe void calculate_window(float * window, window_function func, WindowFunction flag) { - if ((eparams.window_function & flag) == 0 || _windowcount == lpc.MAX_LPC_WINDOWS) + if ((eparams.window_function & flag) == 0 || _windowcount == Alac.MAX_LPC_WINDOWS) return; int sz = _windowsize; - double* pos = window + _windowcount * Alac.MAX_BLOCKSIZE * 2; + float* pos = window + _windowcount * Alac.MAX_BLOCKSIZE * 2; do { func(pos, sz); @@ -955,7 +986,7 @@ namespace CUETools.Codecs.ALAC unsafe int encode_frame(ref int size) { fixed (int* s = samplesBuffer, r = residualBuffer) - fixed (double* window = windowBuffer) + fixed (float * window = windowBuffer) { frame.InitSize(size); @@ -963,10 +994,11 @@ namespace CUETools.Codecs.ALAC { _windowsize = frame.blocksize; _windowcount = 0; - calculate_window(window, window_welch, WindowFunction.Welch); - calculate_window(window, window_tukey, WindowFunction.Tukey); - calculate_window(window, window_hann, WindowFunction.Hann); - calculate_window(window, window_flattop, WindowFunction.Flattop); + 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, lpc.window_hann, WindowFunction.Hann); + calculate_window(window, lpc.window_flattop, WindowFunction.Flattop); if (_windowcount == 0) throw new Exception("invalid windowfunction"); } @@ -981,93 +1013,7 @@ namespace CUETools.Codecs.ALAC frame.subframes[ch].Init(s + ch * Alac.MAX_BLOCKSIZE, r + ch * Alac.MAX_BLOCKSIZE); for (int ch = 0; ch < channels; ch++) - encode_residual_onepass(frame, ch); - } - else if (eparams.stereo_method == StereoMethod.Estimate || eparams.stereo_method == StereoMethod.Estimate2) - { - int* sl = s; - int* sr = s + Alac.MAX_BLOCKSIZE; - int n = frame.blocksize; - ulong lsum = 0, rsum = 0, dsum = 0, s31 = 0, s1 = 0, s2 = 0, s3 = 0; - if (eparams.stereo_method == StereoMethod.Estimate) - for (int i = 2; i < n; i++) - { - int lt = sl[i] - 2 * sl[i - 1] + sl[i - 2]; - int rt = sr[i] - 2 * sr[i - 1] + sr[i - 2]; - int df = lt - rt; - lsum += (ulong)Math.Abs(lt); - rsum += (ulong)Math.Abs(rt); - dsum += (ulong)Math.Abs(df); - s1 += (ulong)Math.Abs(rt + (df >> 1)); - s31 += (ulong)Math.Abs(rt + (df >> 31)); - } - else - for (int i = 2; i < n; i++) - { - int lt = sl[i] - 2 * sl[i - 1] + sl[i - 2]; - int rt = sr[i] - 2 * sr[i - 1] + sr[i - 2]; - int df = lt - rt; - lsum += (ulong)Math.Abs(lt); - rsum += (ulong)Math.Abs(rt); - dsum += (ulong)Math.Abs(df); - s1 += (ulong)Math.Abs(rt + (df >> 1)); - s2 += (ulong)Math.Abs(rt + (df >> 2)); - s3 += (ulong)Math.Abs(rt + (df * 3 >> 2)); - s31 += (ulong)Math.Abs(rt + (df >> 31)); - } - frame.interlacing_leftweight = 0; - frame.interlacing_shift = 0; - ulong score = lsum + rsum; - if (lsum + dsum < score) //leftside - { - frame.interlacing_leftweight = 1; - frame.interlacing_shift = 0; - score = lsum + dsum; - } - if (s1 + dsum < score) // midside - { - frame.interlacing_leftweight = 1; - frame.interlacing_shift = 1; - score = s1 + dsum; - } - if (s31 + dsum < score) // rightside - { - frame.interlacing_leftweight = 1; - frame.interlacing_shift = 31; - score = s31 + dsum; - } - if (eparams.stereo_method == StereoMethod.Estimate2) - { - if (s2 + dsum < score) // close to rightside - { - frame.interlacing_leftweight = 1; - frame.interlacing_shift = 2; - score = s2 + dsum; - } - if (s3 + dsum < score) // close to leftside - { - frame.interlacing_leftweight = 3; - frame.interlacing_shift = 2; - score = s3 + dsum; - } - } - if (frame.interlacing_leftweight == 0) - { - frame.current.residual = r + channels * Alac.MAX_BLOCKSIZE; - for (int ch = 0; ch < channels; ch++) - frame.subframes[ch].Init(s + ch * Alac.MAX_BLOCKSIZE, r + ch * Alac.MAX_BLOCKSIZE); - } - else - { - frame.current.residual = r + 2 * channels * Alac.MAX_BLOCKSIZE; - channel_decorrelation(s, s + Alac.MAX_BLOCKSIZE, s + 2 * Alac.MAX_BLOCKSIZE, s + 3 * Alac.MAX_BLOCKSIZE, frame.blocksize, - frame.interlacing_leftweight, frame.interlacing_shift); - for (int ch = 0; ch < channels; ch++) - frame.subframes[ch].Init(s + (channels + ch) * Alac.MAX_BLOCKSIZE, r + (channels + ch) * Alac.MAX_BLOCKSIZE); - } - - for (int ch = 0; ch < channels; ch++) - encode_residual_onepass(frame, ch); + encode_residual_pass2(frame, ch); } else { @@ -1081,6 +1027,16 @@ namespace CUETools.Codecs.ALAC frame.ChooseSubframes(); encode_estimated_frame(frame); } + + for (int ch = 0; ch < channels; ch++) + { + if (eparams.min_modifier == eparams.max_modifier) + frame.subframes[ch].best.ricemodifier = eparams.max_modifier; + else + /*frame.subframes[ch].best.size = 16 + 16 * order + */ + alac_entropy_coder(frame.subframes[ch].best.residual, frame.blocksize, bps, out frame.subframes[ch].best.ricemodifier); + } + uint fs = measure_frame_size(frame, false); frame.type = ((int)fs > frame.blocksize * channels * bps) ? FrameType.Verbatim : FrameType.Compressed; BitWriter bitwriter = new BitWriter(frame_buffer, 0, max_frame_size); @@ -1563,10 +1519,6 @@ namespace CUETools.Codecs.ALAC frame_buffer = new byte[max_frame_size]; _sample_byte_size = new uint[sample_count / eparams.block_size + 1]; - initial_history = 10; - history_mult = 40; - k_modifier = 14; - if (eparams.do_verify) { verify = new ALACReader(channels, (int)bits_per_sample, history_mult, initial_history, k_modifier, eparams.block_size); @@ -1610,6 +1562,8 @@ namespace CUETools.Codecs.ALAC // 1 = mid-side encoding public StereoMethod stereo_method; + public WindowMethod window_method; + // block size in samples // set by the user prior to calling encode_init // if set to 0, a block size is chosen based on block_time_ms @@ -1645,6 +1599,8 @@ namespace CUETools.Codecs.ALAC // valid values are 1 to 32 public int estimation_depth; + public int adaptive_passes; + public int min_modifier, max_modifier; public WindowFunction window_function; @@ -1665,6 +1621,7 @@ namespace CUETools.Codecs.ALAC window_function = WindowFunction.Flattop | WindowFunction.Tukey; order_method = OrderMethod.Estimate; stereo_method = StereoMethod.Evaluate; + window_method = WindowMethod.Evaluate; block_size = 0; block_time_ms = 105; min_modifier = 4; @@ -1672,6 +1629,7 @@ namespace CUETools.Codecs.ALAC min_prediction_order = 1; max_prediction_order = 12; estimation_depth = 1; + adaptive_passes = 0; do_verify = false; do_seektable = false; @@ -1679,41 +1637,54 @@ namespace CUETools.Codecs.ALAC switch (lvl) { case 0: - block_time_ms = 53; stereo_method = StereoMethod.Independent; - window_function = WindowFunction.Welch; + window_function = WindowFunction.Hann; max_prediction_order = 6; break; case 1: stereo_method = StereoMethod.Independent; - window_function = WindowFunction.Welch; + window_function = WindowFunction.Hann; max_prediction_order = 8; break; case 2: stereo_method = StereoMethod.Estimate; - window_function = WindowFunction.Welch; + window_function = WindowFunction.Hann; max_prediction_order = 6; break; case 3: stereo_method = StereoMethod.Estimate; - window_function = WindowFunction.Welch; + window_function = WindowFunction.Hann; max_prediction_order = 8; break; case 4: - stereo_method = StereoMethod.Estimate2; - window_function = WindowFunction.Welch; + stereo_method = StereoMethod.Estimate; + window_method = WindowMethod.Estimate; + max_prediction_order = 8; break; case 5: - stereo_method = StereoMethod.Estimate2; + stereo_method = StereoMethod.Estimate; + window_method = WindowMethod.Estimate; break; - case 6: + case 6: + stereo_method = StereoMethod.Estimate; break; case 7: - estimation_depth = 3; - min_modifier = 3; + stereo_method = StereoMethod.Estimate; + adaptive_passes = 1; + min_modifier = 2; break; case 8: - estimation_depth = 5; + adaptive_passes = 1; + min_modifier = 2; + break; + case 9: + adaptive_passes = 1; + max_prediction_order = 30; + min_modifier = 2; + break; + case 10: + estimation_depth = 2; + adaptive_passes = 2; max_prediction_order = 30; min_modifier = 2; break; diff --git a/CUETools.Codecs.ALAC/CUETools.Codecs.ALAC.csproj b/CUETools.Codecs.ALAC/CUETools.Codecs.ALAC.csproj index 8c93164..4c3d264 100644 --- a/CUETools.Codecs.ALAC/CUETools.Codecs.ALAC.csproj +++ b/CUETools.Codecs.ALAC/CUETools.Codecs.ALAC.csproj @@ -23,7 +23,7 @@ pdbonly true - bin\Release\ + ..\bin\x64\Release\ TRACE prompt 4