eaglercraft-1.8/sources/teavm/java/com/jcraft/jorbis/Lpc.java

186 lines
4.6 KiB
Java

/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */
/* JOrbis
* Copyright (C) 2000 ymnk, JCraft,Inc.
*
* Written by: 2000 ymnk<ymnk@jcraft.com>
*
* Many thanks to
* Monty <monty@xiph.org> and
* The XIPHOPHORUS Company http://www.xiph.org/ .
* JOrbis has been based on their awesome works, Vorbis codec.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public License
* as published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
* This program 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 Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
package com.jcraft.jorbis;
class Lpc {
// en/decode lookups
Drft fft = new Drft();;
int ln;
int m;
// Autocorrelation LPC coeff generation algorithm invented by
// N. Levinson in 1947, modified by J. Durbin in 1959.
// Input : n elements of time doamin data
// Output: m lpc coefficients, excitation energy
static float lpc_from_data(float[] data, float[] lpc, int n, int m) {
float[] aut = new float[m + 1];
float error;
int i, j;
// autocorrelation, p+1 lag coefficients
j = m + 1;
while (j-- != 0) {
float d = 0;
for (i = j; i < n; i++)
d += data[i] * data[i - j];
aut[j] = d;
}
// Generate lpc coefficients from autocorr values
error = aut[0];
/*
* if(error==0){ for(int k=0; k<m; k++) lpc[k]=0.0f; return 0; }
*/
for (i = 0; i < m; i++) {
float r = -aut[i + 1];
if (error == 0) {
for (int k = 0; k < m; k++)
lpc[k] = 0.0f;
return 0;
}
// Sum up this iteration's reflection coefficient; note that in
// Vorbis we don't save it. If anyone wants to recycle this code
// and needs reflection coefficients, save the results of 'r' from
// each iteration.
for (j = 0; j < i; j++)
r -= lpc[j] * aut[i - j];
r /= error;
// Update LPC coefficients and total error
lpc[i] = r;
for (j = 0; j < i / 2; j++) {
float tmp = lpc[j];
lpc[j] += r * lpc[i - 1 - j];
lpc[i - 1 - j] += r * tmp;
}
if (i % 2 != 0)
lpc[j] += lpc[j] * r;
error *= 1.0 - r * r;
}
// we need the error value to know how big an impulse to hit the
// filter with later
return error;
}
// Input : n element envelope spectral curve
// Output: m lpc coefficients, excitation energy
float lpc_from_curve(float[] curve, float[] lpc) {
int n = ln;
float[] work = new float[n + n];
float fscale = (float) (.5 / n);
int i, j;
// input is a real curve. make it complex-real
// This mixes phase, but the LPC generation doesn't care.
for (i = 0; i < n; i++) {
work[i * 2] = curve[i] * fscale;
work[i * 2 + 1] = 0;
}
work[n * 2 - 1] = curve[n - 1] * fscale;
n *= 2;
fft.backward(work);
// The autocorrelation will not be circular. Shift, else we lose
// most of the power in the edges.
for (i = 0, j = n / 2; i < n / 2;) {
float temp = work[i];
work[i++] = work[j];
work[j++] = temp;
}
return (lpc_from_data(work, lpc, n, m));
}
void init(int mapped, int m) {
ln = mapped;
this.m = m;
// we cheat decoding the LPC spectrum via FFTs
fft.init(mapped * 2);
}
void clear() {
fft.clear();
}
static float FAST_HYPOT(float a, float b) {
return (float) Math.sqrt((a) * (a) + (b) * (b));
}
// One can do this the long way by generating the transfer function in
// the time domain and taking the forward FFT of the result. The
// results from direct calculation are cleaner and faster.
//
// This version does a linear curve generation and then later
// interpolates the log curve from the linear curve.
void lpc_to_curve(float[] curve, float[] lpc, float amp) {
for (int i = 0; i < ln * 2; i++)
curve[i] = 0.0f;
if (amp == 0)
return;
for (int i = 0; i < m; i++) {
curve[i * 2 + 1] = lpc[i] / (4 * amp);
curve[i * 2 + 2] = -lpc[i] / (4 * amp);
}
fft.backward(curve);
{
int l2 = ln * 2;
float unit = (float) (1. / amp);
curve[0] = (float) (1. / (curve[0] * 2 + unit));
for (int i = 1; i < ln; i++) {
float real = (curve[i] + curve[l2 - i]);
float imag = (curve[i] - curve[l2 - i]);
float a = real + unit;
curve[i] = (float) (1.0 / FAST_HYPOT(a, imag));
}
}
}
}