/** * CUETools.Codecs: common audio encoder/decoder routines * Copyright (c) 2009 Gregory S. Chudov * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ using System; using System.Collections.Generic; using System.Text; namespace CUETools.Codecs { public class lpc { public const int MAX_LPC_ORDER = 32; public const int MAX_LPC_WINDOWS = 4; public const int MAX_LPC_PRECISIONS = 4; /** * Apply Welch window function to audio block */ static unsafe void apply_welch_window(/*const*/int* data, uint len, double* w_data) { double c = (2.0 / (len - 1.0)) - 1.0; for (uint i = 0; i < (len >> 1); i++) { double w = 1.0 - ((c - i) * (c - i)); w_data[i] = data[i] * w; w_data[len - 1 - i] = data[len - 1 - i] * w; } } /** * Calculates autocorrelation data from audio samples * A Welch window function is applied before calculation. */ static public unsafe void compute_autocorr(/*const*/ int* data, uint len, uint min, uint lag, double* autoc, double* window) { double* data1 = stackalloc double[(int)len + 16]; uint i, j; double temp, temp2; if (window == null) apply_welch_window(data, len, data1); else { for (i = 0; i < len; i++) data1[i] = data[i] * window[i]; } data1[len] = 0; for (i = min; i <= lag; ++i) { temp = 1.0; temp2 = 1.0; for (j = 0; j <= lag - i; ++j) temp += data1[j + i] * data1[j]; double* finish = data1 + len - i; for (double* pdata = data1 + lag + 1 - i; pdata < finish; pdata += 2) { temp += pdata[i] * pdata[0]; temp2 += pdata[i + 1] * pdata[1]; } autoc[i] = temp + temp2; } //int sample, coeff; //for (coeff = 0; coeff <= lag; coeff++) // autoc[coeff] = 0.0; //int data_len = (int)len; //int limit = data_len - (int)lag - 1; //for (sample = 0; sample <= limit; sample++) //{ // double d = data1[sample]; // for (coeff = 0; coeff <= lag; coeff++) // autoc[coeff] += d * data1[sample + coeff]; //} //for (; sample < data_len; sample++) //{ // double d = data1[sample]; // for (coeff = 0; coeff < data_len - sample; coeff++) // autoc[coeff] += d * data1[sample + coeff]; //} } /** * Levinson-Durbin recursion. * Produces LPC coefficients from autocorrelation data. */ public static unsafe void compute_lpc_coefs(/*const*/ double* autoc, uint max_order, double* reff, double* lpc/*[][MAX_LPC_ORDER]*/) { double* lpc_tmp = stackalloc double[MAX_LPC_ORDER]; int i, j, i2; double r, err, tmp; if (max_order > MAX_LPC_ORDER) throw new Exception("wierd"); for (i = 0; i < max_order; i++) lpc_tmp[i] = 0; err = 1.0; if (autoc != null) err = autoc[0]; for (i = 0; i < max_order; i++) { if (reff != null) { r = reff[i]; } else { r = -autoc[i + 1]; for (j = 0; j < i; j++) { r -= lpc_tmp[j] * autoc[i - j]; } r /= err; err *= 1.0 - (r * r); } i2 = (i >> 1); lpc_tmp[i] = r; for (j = 0; j < i2; j++) { tmp = lpc_tmp[j]; lpc_tmp[j] += r * lpc_tmp[i - 1 - j]; lpc_tmp[i - 1 - j] += r * tmp; } if (0 != (i & 1)) { lpc_tmp[j] += lpc_tmp[j] * r; } for (j = 0; j <= i; j++) { lpc[i * MAX_LPC_ORDER + j] = -lpc_tmp[j]; } } } public static unsafe void compute_schur_reflection(/*const*/ double* autoc, uint max_order, double* reff/*[][MAX_LPC_ORDER]*/) { double* gen0 = stackalloc double[MAX_LPC_ORDER]; double* gen1 = stackalloc double[MAX_LPC_ORDER]; // Schur recursion for (uint i = 0; i < max_order; i++) gen0[i] = gen1[i] = autoc[i + 1]; double error = autoc[0]; reff[0] = -gen1[0] / error; error += gen1[0] * reff[0]; for (uint i = 1; i < max_order; i++) { for (uint j = 0; j < max_order - i; j++) { gen1[j] = gen1[j + 1] + reff[i - 1] * gen0[j]; gen0[j] = gen1[j + 1] * reff[i - 1] + gen0[j]; } reff[i] = -gen1[0] / error; error += gen1[0] * reff[i]; } } /** * Compute LPC coefs for Flake.OrderMethod._EST * Faster LPC coeff computation by first calculating the reflection coefficients * using Schur recursion. That allows for estimating the optimal order before * running Levinson recursion. */ public static unsafe uint compute_lpc_coefs_est(/*const*/ double* autoc, uint max_order, double* lpc/*[][MAX_LPC_ORDER]*/) { double* reff = stackalloc double[MAX_LPC_ORDER]; // Schur recursion compute_schur_reflection(autoc, max_order, reff); // Estimate optimal order using reflection coefficients uint order_est = 1; for (int i = (int)max_order - 1; i >= 0; i--) { if (Math.Abs(reff[i]) > 0.10) { order_est = (uint)i + 1; break; } } // Levinson recursion compute_lpc_coefs(null, order_est, reff, lpc); return order_est; } /** * Quantize LPC coefficients */ public static unsafe void quantize_lpc_coefs(double* lpc_in, int order, uint precision, int* lpc_out, out int shift, int max_shift, int zero_shift) { int i; double d, cmax, error; int qmax; int sh, q; // define maximum levels qmax = (1 << ((int)precision - 1)) - 1; // find maximum coefficient value cmax = 0.0; for (i = 0; i < order; i++) { d = Math.Abs(lpc_in[i]); if (d > cmax) cmax = d; } // if maximum value quantizes to zero, return all zeros if (cmax * (1 << max_shift) < 1.0) { shift = zero_shift; for (i = 0; i < order; i++) lpc_out[i] = 0; return; } // calculate level shift which scales max coeff to available bits sh = max_shift; while ((cmax * (1 << sh) > qmax) && (sh > 0)) { sh--; } // since negative shift values are unsupported in decoder, scale down // coefficients instead if (sh == 0 && cmax > qmax) { double scale = ((double)qmax) / cmax; for (i = 0; i < order; i++) { lpc_in[i] *= scale; } } // output quantized coefficients and level shift error = 0; for (i = 0; i < order; i++) { error += lpc_in[i] * (1 << sh); q = (int)(error + 0.5); if (q <= -qmax) q = -qmax + 1; if (q > qmax) q = qmax; error -= q; lpc_out[i] = q; } shift = sh; } public static unsafe void encode_residual(int* res, int* smp, int n, int order, int* coefs, int shift) { for (int i = 0; i < order; i++) res[i] = smp[i]; int* s = smp; int* r = res + order; int c0 = coefs[0]; int c1 = coefs[1]; switch (order) { case 1: for (int i = n - order; i > 0; i--) { int pred = c0 * *(s++); *(r++) = *s - (pred >> shift); } break; case 2: for (int i = n - order; i > 0; i--) { int pred = c1 * *(s++); pred += c0 * *(s++); *(r++) = *(s--) - (pred >> shift); } break; case 3: for (int i = n - order; i > 0; i--) { int pred = coefs[2] * *(s++); pred += c1 * *(s++); pred += c0 * *(s++); *(r++) = *s - (pred >> shift); s -= 2; } break; case 4: for (int i = n - order; i > 0; i--) { int pred = coefs[3] * *(s++); pred += coefs[2] * *(s++); pred += c1 * *(s++); pred += c0 * *(s++); *(r++) = *s - (pred >> shift); s -= 3; } break; case 5: for (int i = n - order; i > 0; i--) { int pred = coefs[4] * *(s++); pred += coefs[3] * *(s++); pred += coefs[2] * *(s++); pred += c1 * *(s++); pred += c0 * *(s++); *(r++) = *s - (pred >> shift); s -= 4; } break; case 6: for (int i = n - order; i > 0; i--) { int pred = coefs[5] * *(s++); pred += coefs[4] * *(s++); pred += coefs[3] * *(s++); pred += coefs[2] * *(s++); pred += c1 * *(s++); pred += c0 * *(s++); *(r++) = *s - (pred >> shift); s -= 5; } break; case 7: for (int i = n - order; i > 0; i--) { int pred = coefs[6] * *(s++); pred += coefs[5] * *(s++); pred += coefs[4] * *(s++); pred += coefs[3] * *(s++); pred += coefs[2] * *(s++); pred += c1 * *(s++); pred += c0 * *(s++); *(r++) = *s - (pred >> shift); s -= 6; } break; case 8: for (int i = n - order; i > 0; i--) { int pred = coefs[7] * *(s++); pred += coefs[6] * *(s++); pred += coefs[5] * *(s++); pred += coefs[4] * *(s++); pred += coefs[3] * *(s++); pred += coefs[2] * *(s++); pred += c1 * *(s++); pred += c0 * *(s++); *(r++) = *s - (pred >> shift); s -= 7; } break; default: for (int i = order; i < n; i++) { s = smp + i - order; int pred = 0; int* co = coefs + order - 1; int* c7 = coefs + 7; while (co > c7) pred += *(co--) * *(s++); pred += coefs[7] * *(s++); pred += coefs[6] * *(s++); pred += coefs[5] * *(s++); pred += coefs[4] * *(s++); pred += coefs[3] * *(s++); pred += coefs[2] * *(s++); pred += c1 * *(s++); pred += c0 * *(s++); *(r++) = *s - (pred >> shift); } break; } } public static unsafe void encode_residual_long(int* res, int* smp, int n, int order, int* coefs, int shift) { for (int i = 0; i < order; i++) res[i] = smp[i]; int* s = smp; int* r = res + order; int c0 = coefs[0]; int c1 = coefs[1]; switch (order) { case 1: for (int i = n - order; i > 0; i--) { long pred = c0 * (long)*(s++); *(r++) = *s - (int)(pred >> shift); } break; case 2: for (int i = n - order; i > 0; i--) { long pred = c1 * (long)*(s++); pred += c0 * (long)*(s++); *(r++) = *(s--) - (int)(pred >> shift); } break; case 3: for (int i = n - order; i > 0; i--) { long pred = coefs[2] * (long)*(s++); pred += c1 * (long)*(s++); pred += c0 * (long)*(s++); *(r++) = *s - (int)(pred >> shift); s -= 2; } break; case 4: for (int i = n - order; i > 0; i--) { long pred = coefs[3] * (long)*(s++); pred += coefs[2] * (long)*(s++); pred += c1 * (long)*(s++); pred += c0 * (long)*(s++); *(r++) = *s - (int)(pred >> shift); s -= 3; } break; case 5: for (int i = n - order; i > 0; i--) { long pred = coefs[4] * (long)*(s++); pred += coefs[3] * (long)*(s++); pred += coefs[2] * (long)*(s++); pred += c1 * (long)*(s++); pred += c0 * (long)*(s++); *(r++) = *s - (int)(pred >> shift); s -= 4; } break; case 6: for (int i = n - order; i > 0; i--) { long pred = coefs[5] * (long)*(s++); pred += coefs[4] * (long)*(s++); pred += coefs[3] * (long)*(s++); pred += coefs[2] * (long)*(s++); pred += c1 * (long)*(s++); pred += c0 * (long)*(s++); *(r++) = *s - (int)(pred >> shift); s -= 5; } break; case 7: for (int i = n - order; i > 0; i--) { long pred = coefs[6] * (long)*(s++); pred += coefs[5] * (long)*(s++); pred += coefs[4] * (long)*(s++); pred += coefs[3] * (long)*(s++); pred += coefs[2] * (long)*(s++); pred += c1 * (long)*(s++); pred += c0 * (long)*(s++); *(r++) = *s - (int)(pred >> shift); s -= 6; } break; case 8: for (int i = n - order; i > 0; i--) { long pred = coefs[7] * (long)*(s++); pred += coefs[6] * (long)*(s++); pred += coefs[5] * (long)*(s++); pred += coefs[4] * (long)*(s++); pred += coefs[3] * (long)*(s++); pred += coefs[2] * (long)*(s++); pred += c1 * (long)*(s++); pred += c0 * (long)*(s++); *(r++) = *s - (int)(pred >> shift); s -= 7; } break; default: for (int i = order; i < n; i++) { s = smp + i - order; long pred = 0; int* co = coefs + order - 1; int* c7 = coefs + 7; while (co > c7) pred += *(co--) * (long)*(s++); pred += coefs[7] * (long)*(s++); pred += coefs[6] * (long)*(s++); pred += coefs[5] * (long)*(s++); pred += coefs[4] * (long)*(s++); pred += coefs[3] * (long)*(s++); pred += coefs[2] * (long)*(s++); pred += c1 * (long)*(s++); pred += c0 * (long)*(s++); *(r++) = *s - (int)(pred >> shift); } break; } } public static unsafe void decode_residual(int* res, int* smp, int n, int order, int* coefs, int shift) { for (int i = 0; i < order; i++) smp[i] = res[i]; int* s = smp; int* r = res + order; int c0 = coefs[0]; int c1 = coefs[1]; switch (order) { case 1: for (int i = n - order; i > 0; i--) { int pred = c0 * *(s++); *s = *(r++) + (pred >> shift); } break; case 2: for (int i = n - order; i > 0; i--) { int pred = c1 * *(s++); pred += c0 * *(s++); *(s--) = *(r++) + (pred >> shift); } break; case 3: for (int i = n - order; i > 0; i--) { int pred = coefs[2] * *(s++); pred += c1 * *(s++); pred += c0 * *(s++); *s = *(r++) + (pred >> shift); s -= 2; } break; case 4: for (int i = n - order; i > 0; i--) { int pred = coefs[3] * *(s++); pred += coefs[2] * *(s++); pred += c1 * *(s++); pred += c0 * *(s++); *s = *(r++) + (pred >> shift); s -= 3; } break; case 5: for (int i = n - order; i > 0; i--) { int pred = coefs[4] * *(s++); pred += coefs[3] * *(s++); pred += coefs[2] * *(s++); pred += c1 * *(s++); pred += c0 * *(s++); *s = *(r++) + (pred >> shift); s -= 4; } break; case 6: for (int i = n - order; i > 0; i--) { int pred = coefs[5] * *(s++); pred += coefs[4] * *(s++); pred += coefs[3] * *(s++); pred += coefs[2] * *(s++); pred += c1 * *(s++); pred += c0 * *(s++); *s = *(r++) + (pred >> shift); s -= 5; } break; case 7: for (int i = n - order; i > 0; i--) { int pred = coefs[6] * *(s++); pred += coefs[5] * *(s++); pred += coefs[4] * *(s++); pred += coefs[3] * *(s++); pred += coefs[2] * *(s++); pred += c1 * *(s++); pred += c0 * *(s++); *s = *(r++) + (pred >> shift); s -= 6; } break; case 8: for (int i = n - order; i > 0; i--) { int pred = coefs[7] * *(s++); pred += coefs[6] * *(s++); pred += coefs[5] * *(s++); pred += coefs[4] * *(s++); pred += coefs[3] * *(s++); pred += coefs[2] * *(s++); pred += c1 * *(s++); pred += c0 * *(s++); *s = *(r++) + (pred >> shift); s -= 7; } break; default: for (int i = order; i < n; i++) { s = smp + i - order; int pred = 0; int* co = coefs + order - 1; int* c7 = coefs + 7; while (co > c7) pred += *(co--) * *(s++); pred += coefs[7] * *(s++); pred += coefs[6] * *(s++); pred += coefs[5] * *(s++); pred += coefs[4] * *(s++); pred += coefs[3] * *(s++); pred += coefs[2] * *(s++); pred += c1 * *(s++); pred += c0 * *(s++); *s = *(r++) + (pred >> shift); } break; } } public static unsafe void decode_residual_long(int* res, int* smp, int n, int order, int* coefs, int shift) { for (int i = 0; i < order; i++) smp[i] = res[i]; int* s = smp; int* r = res + order; int c0 = coefs[0]; int c1 = coefs[1]; switch (order) { case 1: for (int i = n - order; i > 0; i--) { long pred = c0 * (long)*(s++); *s = *(r++) + (int)(pred >> shift); } break; case 2: for (int i = n - order; i > 0; i--) { long pred = c1 * (long)*(s++); pred += c0 * (long)*(s++); *(s--) = *(r++) + (int)(pred >> shift); } break; case 3: for (int i = n - order; i > 0; i--) { long pred = coefs[2] * (long)*(s++); pred += c1 * (long)*(s++); pred += c0 * (long)*(s++); *s = *(r++) + (int)(pred >> shift); s -= 2; } break; case 4: for (int i = n - order; i > 0; i--) { long pred = coefs[3] * (long)*(s++); pred += coefs[2] * (long)*(s++); pred += c1 * (long)*(s++); pred += c0 * (long)*(s++); *s = *(r++) + (int)(pred >> shift); s -= 3; } break; case 5: for (int i = n - order; i > 0; i--) { long pred = coefs[4] * (long)*(s++); pred += coefs[3] * (long)*(s++); pred += coefs[2] * (long)*(s++); pred += c1 * (long)*(s++); pred += c0 * (long)*(s++); *s = *(r++) + (int)(pred >> shift); s -= 4; } break; case 6: for (int i = n - order; i > 0; i--) { long pred = coefs[5] * (long)*(s++); pred += coefs[4] * (long)*(s++); pred += coefs[3] * (long)*(s++); pred += coefs[2] * (long)*(s++); pred += c1 * (long)*(s++); pred += c0 * (long)*(s++); *s = *(r++) + (int)(pred >> shift); s -= 5; } break; case 7: for (int i = n - order; i > 0; i--) { long pred = coefs[6] * (long)*(s++); pred += coefs[5] * (long)*(s++); pred += coefs[4] * (long)*(s++); pred += coefs[3] * (long)*(s++); pred += coefs[2] * (long)*(s++); pred += c1 * (long)*(s++); pred += c0 * (long)*(s++); *s = *(r++) + (int)(pred >> shift); s -= 6; } break; case 8: for (int i = n - order; i > 0; i--) { long pred = coefs[7] * (long)*(s++); pred += coefs[6] * (long)*(s++); pred += coefs[5] * (long)*(s++); pred += coefs[4] * (long)*(s++); pred += coefs[3] * (long)*(s++); pred += coefs[2] * (long)*(s++); pred += c1 * (long)*(s++); pred += c0 * (long)*(s++); *s = *(r++) + (int)(pred >> shift); s -= 7; } break; default: for (int i = order; i < n; i++) { s = smp + i - order; long pred = 0; int* co = coefs + order - 1; int* c7 = coefs + 7; while (co > c7) pred += *(co--) * (long)*(s++); pred += coefs[7] * (long)*(s++); pred += coefs[6] * (long)*(s++); pred += coefs[5] * (long)*(s++); pred += coefs[4] * (long)*(s++); pred += coefs[3] * (long)*(s++); pred += coefs[2] * (long)*(s++); pred += c1 * (long)*(s++); pred += c0 * (long)*(s++); *s = *(r++) + (int)(pred >> shift); } break; } } } /// /// Context for LPC coefficients calculation and order estimation /// unsafe public class LpcContext { public LpcContext() { reflection_coeffs = new double[lpc.MAX_LPC_ORDER]; autocorr_values = new double[lpc.MAX_LPC_ORDER + 1]; done_lpcs = new uint[lpc.MAX_LPC_PRECISIONS]; } /// /// Reset to initial (blank) state /// public void Reset() { autocorr_order = 0; for (int iPrecision = 0; iPrecision < lpc.MAX_LPC_PRECISIONS; iPrecision++) done_lpcs[iPrecision] = 0; } /// /// Calculate autocorrelation data and reflection coefficients. /// Can be used to incrementaly compute coefficients for higher orders, /// because it caches them. /// /// Maximum order /// Samples pointer /// Block size /// Window function public void GetReflection(int order, int* samples, int blocksize, double* window) { if (autocorr_order > order) return; fixed (double* reff = reflection_coeffs, autoc = autocorr_values) { lpc.compute_autocorr(samples, (uint)blocksize, (uint)autocorr_order, (uint)order, autoc, window); lpc.compute_schur_reflection(autoc, (uint)order, reff); autocorr_order = order + 1; } } /// /// Produces LPC coefficients from autocorrelation data. /// /// LPC coefficients buffer (for all orders) public void ComputeLPC(double* lpcs) { fixed (double* reff = reflection_coeffs) lpc.compute_lpc_coefs(null, (uint)autocorr_order - 1, reff, lpcs); } /// /// Checks if an order is 'interesting'. /// Lower orders are likely to be useful if next two reflection coefficients are small, /// Higher orders are likely to be useful if reflection coefficient for that order is greater than 0.1 and the next one is not. /// /// /// public bool IsInterestingOrder(int order) { return (order > 4 && Math.Abs(reflection_coeffs[order - 1]) >= 0.10 && (order == autocorr_order - 1 || Math.Abs(reflection_coeffs[order]) < 0.10)) || (order < 6 && order < autocorr_order - 2 && reflection_coeffs[order + 1] * reflection_coeffs[order + 1] + reflection_coeffs[order] * reflection_coeffs[order] < 0.1); } double[] autocorr_values; double[] reflection_coeffs; int autocorr_order; public double[] Reflection { get { return reflection_coeffs; } } public uint[] done_lpcs; } }