private int AccumulateFit(float[] flr, float[] mdct, int x0, int x1, ref FitAccumulation fits, int n) { int xa = 0, ya = 0, x2a = 0, xya = 0, na = 0, xb = 0, yb = 0, x2b = 0, xyb = 0, nb = 0; fits.x0 = x0; fits.x1 = x1; if (x1 >= n) { x1 = n - 1; } for (var i = x0; i <= x1; i++) { var quantized = DecibelQuant(flr[i]); if (quantized != 0) { if (mdct[i] + _floor.TwoFitAtten >= flr[i]) { xa += i; ya += quantized; x2a += i * i; xya += i * quantized; na++; } else { xb += i; yb += quantized; x2b += i * i; xyb += i * quantized; nb++; } } } fits.xa = xa; fits.ya = ya; fits.x2a = x2a; fits.xya = xya; fits.an = na; fits.xb = xb; fits.yb = yb; fits.x2b = x2b; fits.xyb = xyb; fits.bn = nb; return(na); }
public int[] Fit(IList <float> logmdct, IList <float> logmask) { var n = _n; var nonzero = 0; var fits = new FitAccumulation[Posit + 1]; var fitValueA = new int[Posit + 2]; // index by range list position var fitValueB = new int[Posit + 2]; // index by range list position var loneighbor = new int[Posit + 2]; // sorted index of range list position (+2) var hineighbor = new int[Posit + 2]; var memo = new int[Posit + 2]; for (var i = 0; i < _posts; i++) { fitValueA[i] = -200; // mark all unused } for (var i = 0; i < _posts; i++) { fitValueB[i] = -200; // mark all unused } for (var i = 0; i < _posts; i++) { loneighbor[i] = 0; // 0 for the implicit 0 post } for (var i = 0; i < _posts; i++) { hineighbor[i] = 1; // 1 for the implicit post at n } for (var i = 0; i < _posts; i++) { memo[i] = -1; // no neighbor yet } // quantize the relevant floor points and collect them into line fit // structures (one per minimal division) at the same time if (_posts == 0) { nonzero = AccumulateFit(logmask, logmdct, 0, n, ref fits[0], n); } else { for (var i = 0; i < _posts - 1; i++) { nonzero += AccumulateFit(logmask, logmdct, _sortedIndex[i], _sortedIndex[i + 1], ref fits[i], n); } } if (nonzero != 0) { // start by fitting the implicit base case.... int y0, y1; FitLine(fits, 0, _posts - 1, out y0, out y1); fitValueA[0] = y0; fitValueB[0] = y0; fitValueB[1] = y1; fitValueA[1] = y1; // Non degenerate case // start progressive splitting. This is a greedy, non-optimal // algorithm, but simple and close enough to the best // answer. for (var i = 2; i < _posts; i++) { var sortPosition = _reverseIndex[i]; var ln = loneighbor[sortPosition]; var hn = hineighbor[sortPosition]; // eliminate repeat searches of a particular range with a memo if (memo[ln] != hn) { // haven't performed this error search yet var lowSortPosition = _reverseIndex[ln]; var highSortPosition = _reverseIndex[hn]; memo[ln] = hn; { // A note: we want to bound/minimize *local*, not global, error var lx = _floor.PostList[ln]; var hx = _floor.PostList[hn]; var ly = PostY(new List <int>(fitValueA), new List <int>(fitValueB), ln); var hy = PostY(new List <int>(fitValueA), new List <int>(fitValueB), hn); if ((ly == -1) || (hy == -1)) { throw new InvalidOperationException("An error occurred during minimization"); } if (InspectError(lx, hx, ly, hy, logmask, logmdct)) { // outside error bounds/begin search area. Split it. int ly0, ly1, hy0, hy1; var ret0 = FitLine(fits, lowSortPosition, sortPosition - lowSortPosition, out ly0, out ly1); var ret1 = FitLine(fits, sortPosition, highSortPosition - sortPosition, out hy0, out hy1); if (ret0 != 0) { ly0 = ly; ly1 = hy0; } if (ret1 != 0) { hy0 = ly1; hy1 = hy; } if ((ret0 != 0) && (ret1 != 0)) { fitValueA[i] = -200; fitValueB[i] = -200; } else { // store new edge values fitValueB[ln] = ly0; if (ln == 0) { fitValueA[ln] = ly0; } fitValueA[i] = ly1; fitValueB[i] = hy0; fitValueA[hn] = hy1; if (hn == 1) { fitValueB[hn] = hy1; } if ((ly1 >= 0) || (hy0 >= 0)) { // store new neighbor values for (var j = sortPosition - 1; j >= 0; j--) { if (hineighbor[j] == hn) { hineighbor[j] = i; } else { break; } } for (var j = sortPosition + 1; j < _posts; j++) { if (loneighbor[j] == ln) { loneighbor[j] = i; } else { break; } } } } } else { fitValueA[i] = -200; fitValueB[i] = -200; } } } } var output = new int[_posts]; output[0] = PostY(new List <int>(fitValueA), new List <int>(fitValueB), 0); output[1] = PostY(new List <int>(fitValueA), new List <int>(fitValueB), 1); // fill in posts marked as not using a fit; we will zero // back out to 'unused' when encoding them so int as curve // interpolation doesn't force them into use for (var i = 2; i < _posts; i++) { var ln = _lowNeighbor[i - 2]; var hn = _highNeighbor[i - 2]; var x0 = _floor.PostList[ln]; var x1 = _floor.PostList[hn]; y0 = output[ln]; y1 = output[hn]; var predicted = RenderPoint(x0, x1, y0, y1, _floor.PostList[i]); var vx = PostY(new List <int>(fitValueA), new List <int>(fitValueB), i); if ((vx >= 0) && (predicted != vx)) { output[i] = vx; } else { output[i] = predicted | 0x8000; } } return(output); } return(null); }